glueviz-1.0.1+dfsg.orig/0000755000175000017500000000000013752535025014413 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/tox.ini0000644000175000017500000000306013752534526015732 0ustar noahfxnoahfx[tox] envlist = py{36,37,38}-{codestyle,test,docs}-{pyqt57,pyqt58,pyqt59,pyqt510,pyqt511,pyqt512,pyqt513,pyside511,pyside512,pyside513}-all-{dev,legacy} requires = pip >= 18.0 setuptools >= 30.3.0 [testenv] passenv = DISPLAY HOME whitelist_externals = find rm sed make changedir = test: .tmp/{envname} docs: doc deps = pyqt59: PyQt5==5.9.* pyqt510: PyQt5==5.10.* pyqt511: PyQt5==5.11.* pyqt512: PyQt5==5.12.* pyqt513: PyQt5==5.13.* pyqt514: PyQt5==5.14.* pyside511: PySide2==5.11.* pyside512: PySide2==5.12.* pyside513: PySide2==5.13.* pyside514: PySide2==5.14.* dev: git+https://github.com/numpy/numpy dev: git+https://github.com/astropy/astropy legacy: numpy==1.16.* legacy: matplotlib==3.2.* legacy: scipy==1.0.* legacy: pandas==1.0.* legacy: echo==0.5.* legacy: astropy==4.0.* legacy: setuptools==30.3.* legacy: qtpy==1.9.* legacy: ipython==7.16.* legacy: ipykernel==5.3.* legacy: qtconsole==4.3.* legacy: dill==0.2.* legacy: xlrd==1.2.* legacy: h5py==2.10.* legacy: mpl-scatter-density==0.7.* legacy: bottleneck==1.2.* all: pytest-qt extras = test all: all docs: docs commands = test: pip freeze test: pytest --pyargs glue --cov glue --cov-config={toxinidir}/setup.cfg {posargs} docs: sphinx-build -W -n -b html -d _build/doctrees . _build/html [testenv:codestyle] deps = flake8 skipsdist = true skip_install = true commands = flake8 --max-line-length=100 --exclude=external glue doc glueviz-1.0.1+dfsg.orig/glueviz.png0000644000175000017500000004634613502206677016624 0ustar noahfxnoahfxPNG  IHDR\rfiCCPICC Profile(c``RH,(a``+) rwRR`B > @Qk .,Ly+%8H䂢 [E vЁ@;>Va r?|I`6.t[Ć )I kZZZh$D;Teg(8C*U3/YOGЌ՟(v!9 KX Lz00OE2030웓\ZT5ɘR?Jq1 pHYsnu>YiTXtXML:com.adobe.xmp 1 L'Y@IDATx}űw])r@YBY('D D ylـL4$m@"$B O _kfvvfvggP]]U].&#< PSSȩ#Á@a߾}:uD}Y wDUXXH׿s=N>djlh uEm3~pQ64G%?wGˉnҲ+..mF]v}Q!H֭(N;:(72|ʁC `̙t%RCXl(ݻSEEt@aC M4!%/4kG|K\voOy(z:6V:PYI|UUUvU]?z(e{2 &qU ]&s+c0AZ}D^y*#Q1cŜ`7r l`Ϭw _[@}q b*"ͧ־ߞB0jy*1зV\_)-VBa6HqPvV$-LP~a_v-J4zɗ h_3ؾ#\0\~ﯰ*Mg݇s #J8A{裧Co\S{t5-C)իViࡴq˦Kh֤=oћ` Jf| <ʿoT{/u;|$GHaaQV%_nqX]cRǎd ~?,l@?| 1J$+ЗSq}~Lmsz?S^Rj߻'soHqwzf**nO}gL'O}{n|U{>ĥ$4uhY|B]yu(,ܜ'x 0- *ynهTXRBlǤ[kVR[4 hԫ{7ja17, "t3K[o5\^4jq~ g҈07u06:OqU߶c=ȣD+}œϖ*V^S1|2`+zÏk&ݥ丙̐oU $?%1͗r O(,[_z%:TYf-+m_QAq ×N;:w(-!{#I@ ?و&/-ōwޡ%K@糲ǭ㏧c~LJQ km.T^Ra@89qO>O0m\F9ɃWќ .SH{d0 syբ|^z: a-"~k6l&>~iy|{ &Kf2 L7dt#F/@)[loDBt[ɦGneyodYNJ7bl/2#F֬'XJF!hqztrE^6 U]C'\ye@fk pf.1n?W$ xMַi֜>N(rg1c~ŪU$j}5SٳioFFrS h|W-[gao<#$B7hY,s<1 @9♛C-uoK*>rG^~oFOECnm4+4m޸Jzkq0'ZC<8ؑL>#.=IFdXnnJcM<׭.x~?|/-h⑳{YY|YOgFsSvԧ(k]^};wA_Xg4:X 9QCJ@q&D>\63M6[f)pn Bnj[-ld}",py/OoF;zCZ#5~/iQ1< in,EqKb!,8^qU=n}CM㴞j+߼ֿqRmu+aL /~RO从k7"R _tN9cu.JW+hWèyyl H>܃:tb <3@R-m' 3{=t yw }yXlj'^^$Pʭ&v|a-Utȧ2_gxnc!@)-?2iu4 ̋XP*m_wlD;RА~}iٻWYܮD7 V.q>=pO c皻QqJ<w1 6q\>n.|+NTԅ\V V/oܰ4355M>Oi!T4^6=ѽ U['<` ?𚪤7[DڕB*^[噎?*nEŷ  ݻ`-|{Dw6ߑ?TɷJ^2L`@ p‚?,7o@Tkyqתhݺb>}zӰi„ lt-Ӑأ!U|뗴š~2Pvjyy8~ {1y-+.jǧ8!\0ש]lLdO.n=uTFMjV@t&8PIA+EKXÚ-#GCx nj:>y)ૼ;j0Ayt}G왳ߌi鋍[ͅoaчFKSv{ VQfN8 cPד&!}[Ņ^H:tȨpUi?u p2A"<\ϭ2&D7ߠ4xMkN27I$JyH|;vq3]t3g?/1y%+xgY ;xAO$!C#tCoޑ_> CtnuU5}Pެ3u|9rͳDs+' dC: حPMMq'<ȃB=_\}B|At$W>3QFnT0&O-Ac,@BE:Ho8iG; <|87i͚5x p;=3Ϥ'|n*@&OE; =5V勧ҭ8~z@4af_(@amߩ[ۜW@@kcN|F_1OӑsY2^xAk[nY ;YfQ'<mOEp_~t\-zm6u*ӱWݽL߽҅ 8T)eHrfm\<>C%9ĭ˼yWn{-@\dn Ke&zǩ}޾FPA? 3"-]wMgs{vxw2'v~oԻSUUq-D'N/ZnM2#oOeg֟Jw/Mo.nS [Ȣt))5rDr#Ka8|-A'X%[@/ם"Jt3ŸrF4O u/f5-A!Иho*;Lt@> zE0 ”(8m)?taܥ݄ىk>p ͩH!O9mSeaPS%ς!els?%_k-%ʡkpR~0p;/>xtQ] ,XR cF$l#J9i[R|yЊ~@G"xȂVx)QߖV{R8o04PtPD]@>BK%*fy5  `@x< ԠLpڢ|WhC|RR 07K/eZz{5H^$?p5W_-7&c)1t]0n8:ߤ}$~|>EB7?(l: TgE>S $vzQݹ{#Nƛn#FOyy%W3YBe#T;y ok0+0w?]1SݤAqu ?SKWXA?яFcJ^S%*[XCna=ly)#gr'lټ{]z̳~#}jMe=K|L9`&L -je -_(qèGң=Fq i/=hد?}Q|A _,]w|JOx!_t1|7OO ^wp}\, `۶mÃ?(uAөP뮻NptaġUUbC,G|16vy y6 .߿y+8JCXNPڻv-I347,G:%J4W/YADr ;n}:GdN!Æ˚~k KcU/(U1~_s~<*ܘ" x-4!;u'PvN{2T[lUˡ .He8~J+t=>܉ЈGV1 ꫉~P@JDwb@ ]ەb¹}Ia h4g9oҮͤxTĻGq?#X TAbGr s޹Bw*Y`cQJh Azl{kkrGlN)37MSR0V_k7-{k4?S%oUfޡ F4"ɷe,k$McӞ*MXC|" 23_UqBUi2i^JbUU`iWpPR)GԔ}<72O*jCI5L߼Inn 7Gk\'+*o@pSv ["ICM*:͇5*hgt0N"|rKrDY/*Z椗*Z,Jnxbl|x@P8% !Ǭ|ۣѻpHHJM9@N&'Ix1ix@78pBzoF:|8w`:@[IA()y5G0j`ܾǑ|UHYĦl\ K(u&+xwhahDmJ~[E EJHQ)˱cRCb/^V(f( %Ix^}:I:f FLh۪ybNt.ȀS0!S{y,S#Ne|L^| P І<ْfx>쯏=*GdpC1}\3xrSs9yn +4f 8h>O>gk^ ,]B}\'t7 @s3e͑tJD0]Tq}^|Q.̗y\4 TQ))Gx,s OGH.NgSO;M9?~?24#op ]H!O901$Вc3ϟg: kq"Ǚ9wC5O?*8F.2}@D ?ׅv{l+(H=taQLLd%EqB!Zz:t~8 a"@m/~s:Ӆs,t ׅ :522WQ˗|D^Sŗ%Hw]|~x&2gBd%`q%9L݋/WƝ+΂"◿WW/MOJwicΰxFLyK*`[qg%ڜZjveW!z>9-[^~%z%dr!~0v=/tUYi{3ϋMgN ? /.A>=."{8nZZzgmN4Iqq<~=s=w 6,c-f`Xv52QS#+3iK>\"OblEXjcYnE?C}L y)$nyR VfPg JL0}71&پ[Bcd?VwıUlM`vkh?|Y@-Qܒ=ͯ*ic>q-;FiR[ij>Kgq=ڟ>$(ThDh9ckXVNeܽ{7}gTe3q{#ETs'KGU]iJY 5I΀C40[2*UN-(h={%5lihTh B p+*W_})#6§T+qwV F s5픯锹siڴi[o/*Z5uhW*n(f%#;ץ< Z Ђ`>ǝe~L@Z9iLwƑ,7ӪNgΠOctx @p J݊y\!+WfGT'g$a>0{LAo89u Z6/iBB+-F>7!48@ЈM|VZOW"8}ꥋRٗ)NGqJ9V0 im54_@OB0cVYWxX?TUT5k]T[D!iyZ!.CH}g<Ƴ h}!i%)cR0kwӚ_NfLȨ>F&ny:"%ʲ/RZFU\֌EMU?E @\[ ͼ]oI;kTh_S/s0[9Y7Tx5jWmJFzH|-7.W]K54lx@dk~Jwz-W=vjjAjybQScێ/6 eR;_|Iȣg2|n" ׭*aw]yiB*6 *^ˁݴZqJ\u8^Jj˖h[2(`3"$~*6_%~.#y7; (/tƒ`cO 9GLba_)a(R*Il@Q!u5څX)uƽeGQ#[E 2>%WuN9n-tgwb0{)+i"Nԭ5[)Ja/RiT V{eVEQyZH/6mՂzo.JĈ K jPPءѶ)Wqn*?z}|q);'dBuBah.rOvxnlR/ fJ|%^]+p8 GYCt_FGuuTԹSs_ΤN"@e"yt\ e VLЧ2/i/|BH;Ax5aO(g%W%!g(fUC^oi#;LL}=[V%`ٳg=sEr sΡ!CXC&PvYKL{Z9T̳Ž pxu+~iY :ڶ/zr.~XQ" AԞW"4l]#rzgԆϧ+VИc;qf;s!Oua4 uAl-uQb:J1,m&5sYjч:"ʀ/XK˳$X!Խm^y/|GD=ڷg(Ν;Ӣwަ_?4rKGWnaP(y0P9J^v0O啶GռdA*u\&|O0 K>F#X6=${2P#JKF&]z-^ G IN|JOc^}*VkFp{G*8>^Jwyc@é_lƒo9U\,^=3ߠ#FPSqV~AE&ađ͛FTc-[~u<0w_*:5?U"Q Q \ҳ'<ؘ.]Jyb F=?.<|,5عc>:Q+.eI"5 ҡ˩^ 邋.ٱ};5rjW^ kҫ,ֆ@ 5,|xCYGb9µCkIr2?8m?;n8"n_a>gzi.}'?|̊g@8_81j ]H˧s14 pswMC]jqzԅ?iѦR)h|qZVJ4\Z4qY9̎a2rdX_p 8]{?'d*(~''?yhA V7yl6P\-+鷍c;!5<Unr6-{VN+.ZɒQ~SUii,PsN9uo(c) 4I}$?3]Kq XUf/񙒊|'9j^O&>܉Q|DyB94!].Hl!\AldhQ7oyW^IyWѼg`ҤItW;#2Epz-Lf[gd)mݻ]q%h֌H|!=ԥKwfcPPЮ\ JZ/-\({C;v_}5zwO)1" Fƭ$ 8;KN* t,6p#Ckdua 6pyf' h&ˁK 3*pV*%\  j ?48րo޴N8q, ,֓iӦINkY,))L⛜f@wz; yS1dNȂxiUWمfjS;mm^Zp> v~{kx@ o(U< bZ` 8 8 k"68H DNvhQ_+W1;]N3K5K/|/BXٻw/#y'!@etpq5ǍKEa$fWzs05v?kPjemeKl_sW-xY>bʦ3h*+fۮe]i9Ogu^[ءuϼɈ3C|5U 딟a4.be_Do[}?2ѾZ֣ 2q q@,‹ @4 plOj pQ'y_Kp?W쳂(p ^W17WCԫzAs.l.r-u&DYTcx2րEpxMun i.xǝVJ>!(ƑGГ=Au6Py:ʽ>u*͎QgKCYg1tl@An3(mFX/}aj"2M~6ťhYȋy.zᱧ~,<%R&+qy :`A+>^4\~l5&iҬAG-x5~Xe˖հqϸVvοf:$VZkF+صk]uK.,}GSNA>;iۍ4ۗ*MY |#YˋyK7gwdʑf;K.X@33%[K'kJϱT3JWqRlL<9)tLSb[*̰x~;z }wEGg~~Ѐ=J ;RE+Ei mE 8%z㯜PAs+(\4Js cVtO;d0uCDqF՜<54AT~}.W>>z?[puYu&+@* k1p˒1dt`aFm>ۼ7'i˙L™L;.S2 8O}|>_%Q-3#լHqfdک2xֺ~|'+ʞ|{NB$3 @\ТH0 @qI1} f_93g;27:5qLFĶFby)RBo+3o?+eDoPVq9M/QMY}zi8 rFDT  "QmdLn)3 @ A㊮'`y0F*ynٺP‹GPYY) ~/J޿8sFÝ%,+Ёng ;GX٭SAe O9eͱJ&żEQECmڸ=p5C} Nj V@ C.rR:wm2ض\nqpY'3Y C&s(/?z^re2eұpHXD,{z9`@zZe`4ȑl @9aQ07Y70 ԁ010v(qr4+C%2p>Gg Ȑg8%9` (pb3)3Eme֭|9ƔήQ9Vf%`x+5 ͻ5 ( WmP؉@zS4Lq2VnP1 Q3mgKjO&8Pi Z2AI[hٖE8]) F7l@}%|kFɜ|lxiG[[>۶mkRCCҋQHbG3߮s{)׭['q>X34= LooseVersion(version) installed = True except (ImportError, AssertionError): installed = False return installed, pytest.mark.skipif(str(not installed), reason='Requires %s' % label) ASTROPY_INSTALLED, requires_astropy = make_skipper('astropy', label='Astropy') MATPLOTLIB_GE_22, requires_matplotlib_ge_22 = make_skipper('matplotlib', version='2.2') ASTRODENDRO_INSTALLED, requires_astrodendro = make_skipper('astrodendro') SCIPY_INSTALLED, requires_scipy = make_skipper('scipy', label='SciPy') PIL_INSTALLED, requires_pil = make_skipper('PIL', label='PIL') SKIMAGE_INSTALLED, requires_skimage = make_skipper('skimage', label='scikit-image') XLRD_INSTALLED, requires_xlrd = make_skipper('xlrd') PYAVM_INSTALLED, requires_pyavm = make_skipper('pyavm') PLOTLY_INSTALLED, requires_plotly = make_skipper('plotly') IPYTHON_INSTALLED, requires_ipython = make_skipper('IPython') requires_pil_or_skimage = pytest.mark.skipif(str(not SKIMAGE_INSTALLED and not PIL_INSTALLED), reason='Requires PIL or scikit-image') PLOTLY_INSTALLED, requires_plotly = make_skipper('plotly') H5PY_INSTALLED, requires_h5py = make_skipper('h5py') PYQT5_INSTALLED, requires_pyqt5 = make_skipper('PyQt5') PYSIDE2_INSTALLED, requires_pyside2 = make_skipper('PySide2') HYPOTHESIS_INSTALLED, requires_hypothesis = make_skipper('hypothesis') QT_INSTALLED = PYQT5_INSTALLED or PYSIDE2_INSTALLED SPECTRAL_CUBE_INSTALLED, requires_spectral_cube = make_skipper('spectral_cube', label='spectral-cube') requires_qt = pytest.mark.skipif(str(not QT_INSTALLED), reason='An installation of Qt is required') @contextmanager def make_file(contents, suffix, decompress=False): """Context manager to write data to a temporary file, and delete on exit :param contents: Data to write. string :param suffix: File suffix. string """ if decompress: contents = zlib.decompress(contents) try: _, fname = tempfile.mkstemp(suffix=suffix) with open(fname, 'wb') as outfile: outfile.write(contents) yield fname finally: try: os.unlink(fname) except WindowsError: # on Windows the unlink can fail pass glueviz-1.0.1+dfsg.orig/glue/tests/data/0000755000175000017500000000000014001427230017404 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/tests/data/simple_table_resaved.glu0000644000175000017500000000641513610101121024265 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "log": "LoadLog", "log_item": 2 }, "Component_0": { "_type": "glue.core.component.Component", "log": "LoadLog", "log_item": 3 }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 4, "_type": "glue.core.data_collection.DataCollection", "cids": [ "Pixel Axis 0 [x]", "World 0", "a", "b" ], "components": [ "CoordinateComponent", "CoordinateComponent_0", "Component", "Component_0" ], "data": [ "single_table[HDU1]" ], "groups": [], "links": [], "subset_group_count": 0 }, "LoadLog": { "_protocol": 2, "_type": "glue.core.data_factories.helpers.LoadLog", "factory": { "_type": "types.FunctionType", "function": "glue.core.data_factories.fits.fits_reader" }, "kwargs": [ [] ], "path": "{DATA_PATH}/single_table.fits" }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "label": "Pixel Axis 0 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.viewers.scatter.qt", "glue.plugins.data_factories.spectral_cube", "glue.core.data_exporters", "glue.viewers.image.qt", "glue.plugins.coordinate_helpers", "glue.plugins.tools", "glue.viewers.table.qt", "glue.plugins.dendro_viewer.qt", "glue.viewers.histogram.qt", "glue.plugins.wcs_autolinking", "glue.io.formats.fits", "glue.plugins.tools.pv_slicer", "glue.plugins.export_d3po", "glue.viewers.profile.qt" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "label": "b" }, "single_table[HDU1]": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "a", "Component" ], [ "b", "Component_0" ] ], "coords": "Coordinates", "label": "single_table[HDU1]", "meta": { "_type": "collections.OrderedDict", "contents": {} }, "primary_owner": [ "Pixel Axis 0 [x]", "World 0", "a", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "#595959", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "d804c55e-ebee-4850-a0f1-3b854bda62b4" } } glueviz-1.0.1+dfsg.orig/glue/tests/data/glue_v0.7_pixel_roi_selection.glu0000644000175000017500000002002613455362716025765 0ustar noahfxnoahfx{ "CoordinateComponent_2": { "world": true, "_type": "glue.core.component.CoordinateComponent", "axis": 1 }, "CoordinateComponent_1": { "world": true, "_type": "glue.core.component.CoordinateComponent", "axis": 0 }, "CoordinateComponent_0": { "world": false, "_type": "glue.core.component.CoordinateComponent", "axis": 1 }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "session": "Session", "data": "DataCollection", "viewers": [ [ "ImageWidget" ] ], "plugins": [ "glue.plugins.tools.pv_slicer", "glue.viewers.histogram", "glue.plugins.export_plotly", "glue.plugins.export_d3po", "glue.viewers.image", "glue.plugins.tools.spectrum_tool", "glue.viewers.scatter", "glue.plugins.coordinate_helpers" ] }, "1_0": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#e31a1c", "markersize": 7, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "group": "1", "_type": "glue.core.subset_group.GroupedSubset" }, "World 1": { "hidden": true, "_type": "glue.core.component_id.ComponentID", "label": "World 1" }, "image_0": { "hidden": false, "_type": "glue.core.component_id.ComponentID", "label": "image" }, "CoordinateComponent": { "world": false, "_type": "glue.core.component.CoordinateComponent", "axis": 0 }, "Component": { "units": "None", "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDIpLCB9ICAgICAgICAgIAoAAAAAAADwPwAAAAAAAPA/AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/AAAAAAAA8D8=" } }, "1": { "_type": "glue.core.subset_group.SubsetGroup", "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#e31a1c", "markersize": 7, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "state": "RoiSubsetState", "subsets": [ "1_0" ], "label": "1" }, "ImageWidget": { "layers": [ { "visible": true, "layer": "image", "zorder": 1, "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "norm": "DS9Normalize" }, { "visible": true, "layer": "1_0", "zorder": 2, "_type": "glue.viewers.image.layer_artist.SubsetImageLayerArtist" } ], "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", "pos": [ 0, 0 ], "session": "Session", "properties": { "rgb_viz": [ true, true, true ], "ratt": null, "rgb_mode": false, "gatt": null, "attribute": "image_0", "batt": null, "slice": [ "y", "x" ], "data": "image" }, "size": [ 600, 400 ] }, "CoordinateComponentLink_2": { "index": 1, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel y", "Pixel x" ], "to": [ "World 1" ], "pix2world": true, "coords": "Coordinates" }, "CoordinateComponentLink": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0", "World 1" ], "to": [ "Pixel y" ], "pix2world": false, "coords": "Coordinates" }, "image": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#7f7f7f", "markersize": 3, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [ "1_0" ], "_type": "glue.core.data.Data", "label": "image", "coords": "Coordinates", "components": [ [ "image_0", "Component" ], [ "Pixel y", "CoordinateComponent" ], [ "Pixel x", "CoordinateComponent_0" ], [ "World 0", "CoordinateComponent_1" ], [ "World 1", "CoordinateComponent_2" ] ], "_key_joins": [] }, "World 0": { "hidden": true, "_type": "glue.core.component_id.ComponentID", "label": "World 0" }, "RoiSubsetState": { "roi": "PolygonalROI", "_type": "glue.core.subset.RoiSubsetState", "xatt": "Pixel x", "yatt": "Pixel y" }, "Pixel y": { "hidden": true, "_type": "glue.core.component_id.ComponentID", "label": "Pixel y" }, "Pixel x": { "hidden": true, "_type": "glue.core.component_id.ComponentID", "label": "Pixel x" }, "PolygonalROI": { "vx": [ 0.523465703971119, 0.523465703971119, 0.523465703971119, 0.523465703971119, 0.5451263537906135, 0.5776173285198554, 0.6642599277978336, 0.8158844765342956, 0.9675090252707577, 1.1841155234657035, 1.335740072202166, 1.4765342960288805, 1.5631768953068592, 1.628158844765343, 1.7039711191335734, 1.7039711191335734, 1.7039711191335734, 1.6931407942238268, 1.628158844765343, 1.5415162454873643, 1.454873646209386, 1.3574007220216604, 1.2599277978339347, 1.162454873646209, 1.0649819494584833, 0.9675090252707577, 0.8808664259927794, 0.7942238267148012, 0.707581227436823, 0.6425992779783392, 0.5992779783393498 ], "vy": [ 1.9765342960288808, 1.9548736462093863, 1.868231046931408, 1.7382671480144405, 1.5108303249097472, 1.4133574007220215, 1.2942238267148014, 1.2075812274368232, 1.1859205776173285, 1.1859205776173285, 1.1859205776173285, 1.2075812274368232, 1.2184115523465704, 1.2509025270758123, 1.3375451263537905, 1.4891696750902528, 1.7707581227436824, 2.0090252707581224, 2.279783393501805, 2.4530685920577615, 2.5288808664259927, 2.583032490974729, 2.6263537906137184, 2.6696750902527073, 2.723826714801444, 2.734657039711191, 2.734657039711191, 2.6805054151624548, 2.6046931407942235, 2.5288808664259927, 2.4422382671480145 ], "_type": "glue.core.roi.PolygonalROI" }, "CoordinateComponentLink_1": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel y", "Pixel x" ], "to": [ "World 0" ], "pix2world": true, "coords": "Coordinates" }, "CoordinateComponentLink_0": { "index": 1, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0", "World 1" ], "to": [ "Pixel x" ], "pix2world": false, "coords": "Coordinates" }, "DS9Normalize": { "vmax": 1.0, "_type": "glue.viewers.image.ds9norm.DS9Normalize", "bias": 0.5, "vmin": 1.0, "clip_hi": 95.0, "stretch": "arcsinh", "clip_lo": 5.0, "contrast": 1.0 }, "DataCollection": { "_type": "glue.core.data_collection.DataCollection", "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "CoordinateComponent_1", "CoordinateComponent_2" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2" ], "_protocol": 2, "groups": [ "1" ], "cids": [ "image_0", "Pixel y", "Pixel x", "World 0", "World 1" ], "data": [ "image" ] }, "Session": { "_type": "glue.core.session.Session" } }glueviz-1.0.1+dfsg.orig/glue/tests/data/simple_table_015.glu0000644000175000017500000000641513610101121023141 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "log": "LoadLog", "log_item": 2 }, "Component_0": { "_type": "glue.core.component.Component", "log": "LoadLog", "log_item": 3 }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 4, "_type": "glue.core.data_collection.DataCollection", "cids": [ "Pixel Axis 0 [x]", "World 0", "a", "b" ], "components": [ "CoordinateComponent", "CoordinateComponent_0", "Component", "Component_0" ], "data": [ "single_table[HDU1]" ], "groups": [], "links": [], "subset_group_count": 0 }, "LoadLog": { "_protocol": 1, "_type": "glue.core.data_factories.helpers.LoadLog", "factory": { "_type": "types.FunctionType", "function": "glue.core.data_factories.fits.fits_reader" }, "kwargs": [ [] ], "path": "{DATA_PATH}/single_table.fits" }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "label": "Pixel Axis 0 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.viewers.profile.qt", "glue.core.data_exporters", "glue.io.formats.fits", "glue.plugins.data_factories.spectral_cube", "glue.plugins.tools.pv_slicer", "glue.viewers.histogram.qt", "glue.plugins.coordinate_helpers", "glue.viewers.image.qt", "glue.plugins.export_d3po", "glue.plugins.tools", "glue.viewers.scatter.qt", "glue.plugins.wcs_autolinking", "glue.plugins.dendro_viewer.qt", "glue.viewers.table.qt" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "label": "b" }, "single_table[HDU1]": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "a", "Component" ], [ "b", "Component_0" ] ], "coords": "Coordinates", "label": "single_table[HDU1]", "meta": { "_type": "collections.OrderedDict", "contents": {} }, "primary_owner": [ "Pixel Axis 0 [x]", "World 0", "a", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "#595959", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "d804c55e-ebee-4850-a0f1-3b854bda62b4" } } glueviz-1.0.1+dfsg.orig/glue/tests/data/session_coordinate_links_013.glu0000644000175000017500000001700413503203504025576 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" }, "units": "" }, "ComponentLink": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "x_0", "y_0" ], "inverse": null, "to": [ "x" ], "using": { "_type": "glue.core.link_helpers.PartialResult", "func": { "_type": "types.MethodType", "instance": "Galactic_to_FK5", "method": "forward" }, "index": 0 } }, "ComponentLink_0": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "x", "y" ], "inverse": null, "to": [ "x_0" ], "using": { "_type": "glue.core.link_helpers.PartialResult", "func": { "_type": "types.MethodType", "instance": "Galactic_to_FK5", "method": "backward" }, "index": 0 } }, "ComponentLink_1": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "x_0", "y_0" ], "inverse": null, "to": [ "y" ], "using": { "_type": "glue.core.link_helpers.PartialResult", "func": { "_type": "types.MethodType", "instance": "Galactic_to_FK5", "method": "forward" }, "index": 1 } }, "ComponentLink_2": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "x", "y" ], "inverse": null, "to": [ "y_0" ], "using": { "_type": "glue.core.link_helpers.PartialResult", "func": { "_type": "types.MethodType", "instance": "Galactic_to_FK5", "method": "backward" }, "index": 1 } }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoCAAAAAAAAAAMAAAAAAAAABAAAAAAAAAA=" }, "units": "" }, "Component_1": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" }, "units": "" }, "Component_2": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoCAAAAAAAAAAMAAAAAAAAABAAAAAAAAAA=" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "CoordinateComponent_1": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponent_2": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "Coordinates_0": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 4, "_type": "glue.core.data_collection.DataCollection", "cids": [ "Pixel Axis 0 [x]", "World 0", "x", "y", "Pixel Axis 0 [x]_0", "World 0_0", "x_0", "y_0" ], "components": [ "CoordinateComponent", "CoordinateComponent_0", "Component", "Component_0", "CoordinateComponent_1", "CoordinateComponent_2", "Component_1", "Component_2" ], "data": [ "data1", "data2" ], "groups": [], "links": [ "ComponentLink", "ComponentLink_0", "ComponentLink_1", "ComponentLink_2" ], "subset_group_count": 0 }, "Galactic_to_FK5": { "_type": "glue.plugins.coordinate_helpers.link_helpers.Galactic_to_FK5", "cids": [ "x_0", "y_0", "x", "y" ] }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "label": "Pixel Axis 0 [x]" }, "Pixel Axis 0 [x]_0": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "label": "Pixel Axis 0 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "label": "World 0" }, "World 0_0": { "_type": "glue.core.component_id.ComponentID", "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.plugins.tools", "glue.plugins.export_d3po", "glue.core.data_exporters", "mosviz", "glue.io.formats.fits", "glue.plugins.tools.pv_slicer", "glue.plugins.coordinate_helpers", "glue.plugins.data_factories.spectral_cube" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [] ] }, "data1": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "x", "Component" ], [ "y", "Component_0" ] ], "coords": "Coordinates", "label": "data1", "meta": { "_type": "collections.OrderedDict", "contents": {} }, "primary_owner": [ "Pixel Axis 0 [x]", "World 0", "x", "y" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "#919191", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "4eb0477a-dc4c-4062-8245-4249124f66b0" }, "data2": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "Pixel Axis 0 [x]_0", "CoordinateComponent_1" ], [ "World 0_0", "CoordinateComponent_2" ], [ "x_0", "Component_1" ], [ "y_0", "Component_2" ] ], "coords": "Coordinates_0", "label": "data2", "meta": { "_type": "collections.OrderedDict", "contents": {} }, "primary_owner": [ "Pixel Axis 0 [x]_0", "World 0_0", "x_0", "y_0" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "#919191", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "a0d5cba0-9770-4b38-8682-82c80854b109" }, "x": { "_type": "glue.core.component_id.ComponentID", "label": "x" }, "x_0": { "_type": "glue.core.component_id.ComponentID", "label": "x" }, "y": { "_type": "glue.core.component_id.ComponentID", "label": "y" }, "y_0": { "_type": "glue.core.component_id.ComponentID", "label": "y" } }glueviz-1.0.1+dfsg.orig/glue/tests/data/__init__.py0000644000175000017500000000000013455362716021526 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/tests/data/load_log_1.glu0000644000175000017500000000645113502206677022142 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "log": "LoadLog", "log_item": 2 }, "Component_0": { "_type": "glue.core.component.Component", "log": "LoadLog", "log_item": 3 }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "Pixel Axis 0 [x]", "World 0", "a", "b" ], "components": [ "CoordinateComponent", "CoordinateComponent_0", "Component", "Component_0" ], "data": [ "simple" ], "groups": [], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0" ], "subset_group_count": 0 }, "LoadLog": { "_protocol": 1, "_type": "glue.core.data_factories.helpers.LoadLog", "factory": { "_type": "types.FunctionType", "function": "glue.core.data_factories.helpers.auto_data" }, "kwargs": [ [] ], "path": "{DATA_PATH}simple.csv" }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "simple": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "a", "Component" ], [ "b", "Component_0" ] ], "coords": "Coordinates", "label": "simple", "primary_owner": [ "Pixel Axis 0 [x]", "World 0", "a", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "741368cc-0826-4e8e-aa11-9e8ac0a093cd" } } glueviz-1.0.1+dfsg.orig/glue/tests/data/simple_hdf5_grid.glu0000644000175000017500000002511613455362716023351 0ustar noahfxnoahfx{ "CoordinateComponent_9": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 1 }, "CoordinateComponent_8": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "World 0_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "World 2": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 2" }, "World 1": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 1" }, "CoordinateComponent_0": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 1 }, "single_grid": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "single_grid", "coords": "Coordinates", "components": [ [ "/array1_0", "Component_0" ], [ "Pixel z_0", "CoordinateComponent_5" ], [ "Pixel y_0", "CoordinateComponent_6" ], [ "Pixel x_0", "CoordinateComponent_7" ], [ "World 0_0", "CoordinateComponent_8" ], [ "World 1_0", "CoordinateComponent_9" ], [ "World 2_0", "CoordinateComponent_10" ] ], "_key_joins": [] }, "CoordinateComponent_6": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 1 }, "CoordinateComponent_5": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponent_4": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 2 }, "World 1_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 1" }, "CoordinateComponentLink_6": { "index": 2, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0", "World 1", "World 2" ], "to": [ "Pixel x" ], "pix2world": false, "coords": "Coordinates_0" }, "CoordinateComponentLink_10": { "index": 2, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0_0", "World 1_0", "World 2_0" ], "to": [ "Pixel x_0" ], "pix2world": false, "coords": "Coordinates" }, "__main__": { "_type": "glue.qt.glue_application.GlueApplication", "session": "Session", "data": "DataCollection", "viewers": [ [] ] }, "CoordinateComponentLink_2": { "index": 1, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0", "World 1", "World 2" ], "to": [ "Pixel y" ], "pix2world": false, "coords": "Coordinates_0" }, "Pixel x_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel x" }, "World 2_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 2" }, "Component": { "log_item": 0, "_type": "glue.core.data.Component", "log": "LoadLog" }, "Pixel y_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel y" }, "CoordinateComponent_3": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 1 }, "Pixel z_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel z" }, "CoordinateComponentLink": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0", "World 1", "World 2" ], "to": [ "Pixel z" ], "pix2world": false, "coords": "Coordinates_0" }, "/array1_0": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "/array1" }, "CoordinateComponent_2": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponent_10": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 2 }, "CoordinateComponent_1": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 2 }, "CoordinateComponentLink_9": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel z", "Pixel y", "Pixel x" ], "to": [ "World 0" ], "pix2world": true, "coords": "Coordinates_0" }, "World 0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "Pixel z": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel z" }, "Pixel y": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel y" }, "Pixel x": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel x" }, "CoordinateComponentLink_3": { "index": 2, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel z_0", "Pixel y_0", "Pixel x_0" ], "to": [ "World 2_0" ], "pix2world": true, "coords": "Coordinates" }, "CoordinateComponent_7": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 2 }, "CoordinateComponentLink_1": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0_0", "World 1_0", "World 2_0" ], "to": [ "Pixel z_0" ], "pix2world": false, "coords": "Coordinates" }, "CoordinateComponentLink_0": { "index": 1, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel z", "Pixel y", "Pixel x" ], "to": [ "World 1" ], "pix2world": true, "coords": "Coordinates_0" }, "CoordinateComponentLink_7": { "index": 1, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel z_0", "Pixel y_0", "Pixel x_0" ], "to": [ "World 1_0" ], "pix2world": true, "coords": "Coordinates" }, "LoadLog": { "path": "{DATA_PATH}single_grid.hdf5", "_type": "glue.core.data_factories.LoadLog", "factory": { "function": "glue.core.data_factories.auto_data", "_type": "types.FunctionType" }, "kwargs": [ [] ] }, "CoordinateComponentLink_5": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel z_0", "Pixel y_0", "Pixel x_0" ], "to": [ "World 0_0" ], "pix2world": true, "coords": "Coordinates" }, "Coordinates_0": { "_type": "glue.core.coordinates.Coordinates" }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "CoordinateComponent": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponentLink_4": { "index": 2, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel z", "Pixel y", "Pixel x" ], "to": [ "World 2" ], "pix2world": true, "coords": "Coordinates_0" }, "CoordinateComponentLink_8": { "index": 1, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0_0", "World 1_0", "World 2_0" ], "to": [ "Pixel y_0" ], "pix2world": false, "coords": "Coordinates" }, "/array1": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "/array1" }, "Component_0": { "log_item": 0, "_type": "glue.core.data.Component", "log": "LoadLog_0" }, "DataCollection": { "_type": "glue.core.data_collection.DataCollection", "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "CoordinateComponent_1", "CoordinateComponent_2", "CoordinateComponent_3", "CoordinateComponent_4", "Component_0", "CoordinateComponent_5", "CoordinateComponent_6", "CoordinateComponent_7", "CoordinateComponent_8", "CoordinateComponent_9", "CoordinateComponent_10" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2", "CoordinateComponentLink_3", "CoordinateComponentLink_4", "CoordinateComponentLink_5", "CoordinateComponentLink_6", "CoordinateComponentLink_7", "CoordinateComponentLink_8", "CoordinateComponentLink_9", "CoordinateComponentLink_10" ], "_protocol": 2, "groups": [], "cids": [ "/array1", "Pixel z", "Pixel y", "Pixel x", "World 0", "World 1", "World 2", "/array1_0", "Pixel z_0", "Pixel y_0", "Pixel x_0", "World 0_0", "World 1_0", "World 2_0" ], "data": [ "single_grid_auto", "single_grid" ] }, "Session": { "_type": "glue.core.session.Session" }, "LoadLog_0": { "path": "{DATA_PATH}single_grid.hdf5", "_type": "glue.core.data_factories.LoadLog", "factory": { "function": "glue.core.data_factories.gridded_data", "_type": "types.FunctionType" }, "kwargs": [ [] ] }, "single_grid_auto": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5019607843137255, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "single_grid_auto", "coords": "Coordinates_0", "components": [ [ "/array1", "Component" ], [ "Pixel z", "CoordinateComponent" ], [ "Pixel y", "CoordinateComponent_0" ], [ "Pixel x", "CoordinateComponent_1" ], [ "World 0", "CoordinateComponent_2" ], [ "World 1", "CoordinateComponent_3" ], [ "World 2", "CoordinateComponent_4" ] ], "_key_joins": [] } }glueviz-1.0.1+dfsg.orig/glue/tests/data/simple_tables.glu0000644000175000017500000002615413455362716022773 0ustar noahfxnoahfx{ "LoadLog": { "path": "{DATA_PATH}single_table.fits", "_type": "glue.core.data_factories.LoadLog", "factory": { "function": "glue.core.data_factories.auto_data", "_type": "types.FunctionType" }, "kwargs": [ [] ] }, "a_2": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "a" }, "a_1": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "a" }, "a_0": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "a" }, "World 0_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "World 0_1": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "World 0_2": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "World 0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "Coordinates_1": { "_type": "glue.core.coordinates.Coordinates" }, "CoordinateComponent_6": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponent_5": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponent_4": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "__main__": { "_type": "glue.qt.glue_application.GlueApplication", "session": "Session", "data": "DataCollection", "viewers": [ [] ] }, "b_0": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "b" }, "b_1": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "b" }, "b_2": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "b" }, "double_tables": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "double_tables", "coords": "Coordinates", "components": [ [ "a_2", "Component_5" ], [ "Pixel Axis 0_2", "CoordinateComponent_5" ], [ "World 0_2", "CoordinateComponent_6" ], [ "b_2", "Component_6" ] ], "_key_joins": [] }, "Component": { "log_item": 0, "_type": "glue.core.data.Component", "log": "LoadLog" }, "single_table": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "single_table", "coords": "Coordinates_0", "components": [ [ "a_0", "Component_1" ], [ "Pixel Axis 0_0", "CoordinateComponent_1" ], [ "World 0_0", "CoordinateComponent_2" ], [ "b_0", "Component_2" ] ], "_key_joins": [] }, "CoordinateComponent_3": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponentLink": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0_2" ], "to": [ "Pixel Axis 0_2" ], "pix2world": false, "coords": "Coordinates" }, "CoordinateComponent_2": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponent_1": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "single_table_auto": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5019607843137255, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "single_table_auto", "coords": "Coordinates_1", "components": [ [ "a", "Component" ], [ "Pixel Axis 0", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "b", "Component_0" ] ], "_key_joins": [] }, "CoordinateComponent_0": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "Pixel Axis 0_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel Axis 0" }, "Pixel Axis 0_1": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel Axis 0" }, "Pixel Axis 0_2": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel Axis 0" }, "Pixel Axis 0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel Axis 0" }, "CoordinateComponentLink_3": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0" ], "to": [ "Pixel Axis 0" ], "pix2world": false, "coords": "Coordinates_1" }, "CoordinateComponentLink_2": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel Axis 0" ], "to": [ "World 0" ], "pix2world": true, "coords": "Coordinates_1" }, "CoordinateComponentLink_1": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel Axis 0_0" ], "to": [ "World 0_0" ], "pix2world": true, "coords": "Coordinates_0" }, "CoordinateComponentLink_0": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel Axis 0_1" ], "to": [ "World 0_1" ], "pix2world": true, "coords": "Coordinates_2" }, "CoordinateComponentLink_6": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0_1" ], "to": [ "Pixel Axis 0_1" ], "pix2world": false, "coords": "Coordinates_2" }, "CoordinateComponentLink_5": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0_0" ], "to": [ "Pixel Axis 0_0" ], "pix2world": false, "coords": "Coordinates_0" }, "Coordinates_0": { "_type": "glue.core.coordinates.Coordinates" }, "Coordinates_2": { "_type": "glue.core.coordinates.Coordinates" }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "CoordinateComponent": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponentLink_4": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel Axis 0_2" ], "to": [ "World 0_2" ], "pix2world": true, "coords": "Coordinates" }, "a": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "a" }, "b": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "b" }, "double_tables_auto": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5019607843137255, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "double_tables_auto", "coords": "Coordinates_2", "components": [ [ "a_1", "Component_3" ], [ "Pixel Axis 0_1", "CoordinateComponent_3" ], [ "World 0_1", "CoordinateComponent_4" ], [ "b_1", "Component_4" ] ], "_key_joins": [] }, "Component_3": { "log_item": 0, "_type": "glue.core.data.Component", "log": "LoadLog_0" }, "Component_2": { "log_item": 3, "_type": "glue.core.data.Component", "log": "LoadLog_1" }, "Component_1": { "log_item": 0, "_type": "glue.core.data.Component", "log": "LoadLog_1" }, "Component_0": { "log_item": 3, "_type": "glue.core.data.Component", "log": "LoadLog" }, "Component_6": { "log_item": 3, "_type": "glue.core.data.Component", "log": "LoadLog_2" }, "Component_5": { "log_item": 0, "_type": "glue.core.data.Component", "log": "LoadLog_2" }, "Component_4": { "log_item": 3, "_type": "glue.core.data.Component", "log": "LoadLog_0" }, "DataCollection": { "_type": "glue.core.data_collection.DataCollection", "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0", "Component_1", "CoordinateComponent_1", "CoordinateComponent_2", "Component_2", "Component_3", "CoordinateComponent_3", "CoordinateComponent_4", "Component_4", "Component_5", "CoordinateComponent_5", "CoordinateComponent_6", "Component_6" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2", "CoordinateComponentLink_3", "CoordinateComponentLink_4", "CoordinateComponentLink_5", "CoordinateComponentLink_6" ], "_protocol": 2, "groups": [], "cids": [ "a", "Pixel Axis 0", "World 0", "b", "a_0", "Pixel Axis 0_0", "World 0_0", "b_0", "a_1", "Pixel Axis 0_1", "World 0_1", "b_1", "a_2", "Pixel Axis 0_2", "World 0_2", "b_2" ], "data": [ "single_table_auto", "single_table", "double_tables_auto", "double_tables" ] }, "Session": { "_type": "glue.core.session.Session" }, "LoadLog_2": { "path": "{DATA_PATH}double_tables.fits", "_type": "glue.core.data_factories.LoadLog", "factory": { "function": "glue.core.data_factories.astropy_tabular_data", "_type": "types.FunctionType" }, "kwargs": [ [] ] }, "LoadLog_0": { "path": "{DATA_PATH}double_tables.fits", "_type": "glue.core.data_factories.LoadLog", "factory": { "function": "glue.core.data_factories.auto_data", "_type": "types.FunctionType" }, "kwargs": [ [] ] }, "LoadLog_1": { "path": "{DATA_PATH}single_table.fits", "_type": "glue.core.data_factories.LoadLog", "factory": { "function": "glue.core.data_factories.astropy_tabular_data", "_type": "types.FunctionType" }, "kwargs": [ [] ] } }glueviz-1.0.1+dfsg.orig/glue/tests/data/simple.csv0000644000175000017500000000002013502206677021421 0ustar noahfxnoahfxa,b 1,2 3,2 5,3 glueviz-1.0.1+dfsg.orig/glue/tests/data/session_links.glu0000644000175000017500000002335313455362716023031 0ustar noahfxnoahfx{ "World 0_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "CoordinateComponent_2": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponent_1": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "CoordinateComponent_0": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "Coordinates_0": { "_type": "glue.core.coordinates.Coordinates" }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DerivedComponent": { "_type": "glue.core.data.DerivedComponent", "link": "ComponentLink" }, "__main__": { "_type": "glue.qt.glue_application.GlueApplication", "session": "Session", "data": "DataCollection", "viewers": [ [] ] }, "DerivedComponent_2": { "_type": "glue.core.data.DerivedComponent", "link": "ComponentLink_2" }, "DerivedComponent_1": { "_type": "glue.core.data.DerivedComponent", "link": "ComponentLink_0" }, "DerivedComponent_0": { "_type": "glue.core.data.DerivedComponent", "link": "ComponentLink_1" }, "CoordinateComponent": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "ComponentLink": { "_type": "glue.core.component_link.ComponentLink", "inverse": null, "frm": [ "l", "b" ], "to": [ "ra" ], "using": { "index": 0, "_type": "glue.core.link_helpers.PartialResult", "func": { "function": "glue.external.aplpy.gal2fk5", "_type": "types.FunctionType" } }, "hidden": false }, "CoordinateComponentLink": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel Axis 0_0" ], "to": [ "World 0_0" ], "pix2world": true, "coords": "Coordinates" }, "ra": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "ra" }, "ComponentLink_2": { "_type": "glue.core.component_link.ComponentLink", "inverse": null, "frm": [ "ra", "dec" ], "to": [ "b" ], "using": { "function": "glue.core.link_helpers.radec2glat", "_type": "types.FunctionType" }, "hidden": false }, "ComponentLink_1": { "_type": "glue.core.component_link.ComponentLink", "inverse": null, "frm": [ "l", "b" ], "to": [ "dec" ], "using": { "index": 1, "_type": "glue.core.link_helpers.PartialResult", "func": { "function": "glue.external.aplpy.gal2fk5", "_type": "types.FunctionType" } }, "hidden": false }, "World 0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "Pixel Axis 0_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel Axis 0" }, "Pixel Axis 0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel Axis 0" }, "ComponentLink_3": { "_type": "glue.core.component_link.ComponentLink", "inverse": null, "frm": [ "l", "b" ], "to": [ "dec" ], "using": { "function": "glue.core.link_helpers.lb2dec", "_type": "types.FunctionType" }, "hidden": false }, "CoordinateComponentLink_2": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel Axis 0" ], "to": [ "World 0" ], "pix2world": true, "coords": "Coordinates_0" }, "CoordinateComponentLink_1": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0" ], "to": [ "Pixel Axis 0" ], "pix2world": false, "coords": "Coordinates_0" }, "Component": { "units": null, "_type": "glue.core.data.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" } }, "ComponentLink_6": { "_type": "glue.core.component_link.ComponentLink", "inverse": null, "frm": [ "l", "b" ], "to": [ "ra" ], "using": { "function": "glue.core.link_helpers.lb2ra", "_type": "types.FunctionType" }, "hidden": false }, "ComponentLink_5": { "_type": "glue.core.component_link.ComponentLink", "inverse": null, "frm": [ "ra", "dec" ], "to": [ "b" ], "using": { "index": 1, "_type": "glue.core.link_helpers.PartialResult", "func": { "function": "glue.external.aplpy.fk52gal", "_type": "types.FunctionType" } }, "hidden": false }, "ComponentLink_4": { "_type": "glue.core.component_link.ComponentLink", "inverse": null, "frm": [ "ra", "dec" ], "to": [ "l" ], "using": { "index": 0, "_type": "glue.core.link_helpers.PartialResult", "func": { "function": "glue.external.aplpy.fk52gal", "_type": "types.FunctionType" } }, "hidden": false }, "CoordinateComponentLink_0": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0_0" ], "to": [ "Pixel Axis 0_0" ], "pix2world": false, "coords": "Coordinates" }, "b": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "b" }, "Component_2": { "units": null, "_type": "glue.core.data.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" } }, "Component_1": { "units": null, "_type": "glue.core.data.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" } }, "Component_0": { "units": null, "_type": "glue.core.data.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" } }, "t2": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "t2", "coords": "Coordinates_0", "components": [ [ "b", "Component" ], [ "Pixel Axis 0", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "l", "Component_0" ], [ "ra", "DerivedComponent" ], [ "dec", "DerivedComponent_0" ] ], "_key_joins": [] }, "l": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "l" }, "t1": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "t1", "coords": "Coordinates", "components": [ [ "dec", "Component_1" ], [ "Pixel Axis 0_0", "CoordinateComponent_1" ], [ "World 0_0", "CoordinateComponent_2" ], [ "ra", "Component_2" ], [ "l", "DerivedComponent_1" ], [ "b", "DerivedComponent_2" ] ], "_key_joins": [] }, "DataCollection": { "_type": "glue.core.data_collection.DataCollection", "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0", "DerivedComponent", "DerivedComponent_0", "Component_1", "CoordinateComponent_1", "CoordinateComponent_2", "Component_2", "DerivedComponent_1", "DerivedComponent_2" ], "links": [ "ComponentLink", "ComponentLink_0", "ComponentLink_1", "CoordinateComponentLink", "ComponentLink_2", "CoordinateComponentLink_0", "ComponentLink_3", "ComponentLink_4", "ComponentLink_5", "CoordinateComponentLink_1", "ComponentLink_6", "CoordinateComponentLink_2" ], "_protocol": 2, "groups": [], "cids": [ "b", "Pixel Axis 0", "World 0", "l", "ra", "dec", "dec", "Pixel Axis 0_0", "World 0_0", "ra", "l", "b" ], "data": [ "t2", "t1" ] }, "Session": { "_type": "glue.core.session.Session" }, "dec": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "dec" }, "ComponentLink_0": { "_type": "glue.core.component_link.ComponentLink", "inverse": null, "frm": [ "ra", "dec" ], "to": [ "l" ], "using": { "function": "glue.core.link_helpers.radec2glon", "_type": "types.FunctionType" }, "hidden": false } }glueviz-1.0.1+dfsg.orig/glue/tests/data/load_log_0.glu0000644000175000017500000000642513502206677022142 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "log": "LoadLog", "log_item": 0 }, "Component_0": { "_type": "glue.core.component.Component", "log": "LoadLog", "log_item": 3 }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "a", "Pixel Axis 0 [x]", "World 0", "b" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0" ], "data": [ "simple" ], "groups": [], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0" ], "subset_group_count": 0 }, "LoadLog": { "_type": "glue.core.data_factories.helpers.LoadLog", "factory": { "_type": "types.FunctionType", "function": "glue.core.data_factories.helpers.auto_data" }, "kwargs": [ [] ], "path": "{DATA_PATH}simple.csv" }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "simple": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component" ], [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "b", "Component_0" ] ], "coords": "Coordinates", "label": "simple", "primary_owner": [ "a", "Pixel Axis 0 [x]", "World 0", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "fd317c94-7a6a-4ac2-b024-17e44bde3cb8" } } glueviz-1.0.1+dfsg.orig/glue/tests/data/simple_viewers.glu0000644000175000017500000002217713455362716023206 0ustar noahfxnoahfx{ "ScatterWidget": { "layers": [ { "_type": "glue.clients.layer_artist.ScatterLayerArtist", "layer": "table", "yatt": "a", "visible": true, "zorder": 1, "xatt": "b" } ], "_type": "glue.qt.widgets.scatter_widget.ScatterWidget", "pos": [ 0, 0 ], "session": "Session", "properties": { "xflip": false, "ymax": 3.04, "xlog": true, "yflip": true, "yatt": "a", "ylog": false, "xmax": 4.36, "xmin": 1.24, "hidden": false, "ymin": 0.96, "xatt": "b" }, "size": [ 670, 512 ] }, "World 0_0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "CoordinateComponent_2": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 1 }, "image": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "image", "coords": "Coordinates", "components": [ [ "image_0", "Component_1" ], [ "Pixel y", "CoordinateComponent_1" ], [ "Pixel x", "CoordinateComponent_2" ], [ "World 0_0", "CoordinateComponent_3" ], [ "World 1", "CoordinateComponent_4" ] ], "_key_joins": [] }, "World 0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 0" }, "Coordinates_0": { "_type": "glue.core.coordinates.Coordinates" }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "CoordinateComponent_4": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 1 }, "__main__": { "_type": "glue.qt.glue_application.GlueApplication", "session": "Session", "data": "DataCollection", "viewers": [ [ "ScatterWidget", "ImageWidget", "HistogramWidget" ] ] }, "table": { "style": { "_type": "glue.core.visual.VisualAttributes", "color": "#373737", "markersize": 3, "marker": "o", "alpha": 0.5, "linewidth": 1, "linestyle": "solid" }, "_protocol": 3, "subsets": [], "_type": "glue.core.data.Data", "label": "table", "coords": "Coordinates_0", "components": [ [ "a", "Component" ], [ "Pixel Axis 0", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "b", "Component_0" ] ], "_key_joins": [] }, "image_0": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "image" }, "CoordinateComponent": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "Pixel x": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel x" }, "Component": { "units": null, "_type": "glue.core.data.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" } }, "CoordinateComponent_3": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "ImageWidget": { "layers": [ { "visible": true, "layer": "image", "zorder": 1, "_type": "glue.clients.layer_artist.ImageLayerArtist", "norm": "DS9Normalize" } ], "_type": "glue.qt.widgets.image_widget.ImageWidget", "pos": [ 672, 0 ], "session": "Session", "properties": { "rgb_viz": [ true, true, true ], "ratt": null, "rgb_mode": false, "gatt": null, "attribute": "image_0", "batt": null, "slice": [ "y", "x" ], "data": "image" }, "size": [ 562, 513 ] }, "CoordinateComponentLink": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0_0", "World 1" ], "to": [ "Pixel y" ], "pix2world": false, "coords": "Coordinates" }, "World 1": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "World 1" }, "CoordinateComponent_0": { "world": true, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "HistogramWidget": { "layers": [ { "nbins": 5.0, "layer": "table", "lo": 1.3, "xlog": false, "_type": "glue.clients.layer_artist.HistogramLayerArtist", "visible": true, "hi": 4.2999999999999998, "zorder": 1 } ], "_type": "glue.qt.widgets.histogram_widget.HistogramWidget", "pos": [ 0, 535 ], "session": "Session", "properties": { "nbins": 5.0, "normed": false, "autoscale": true, "xlog": false, "cumulative": false, "component": "b", "ylog": false, "xmax": 4.3, "xmin": 1.3 }, "size": [ 1235, 531 ] }, "Pixel y": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel y" }, "Pixel Axis 0": { "hidden": true, "_type": "glue.core.data.ComponentID", "label": "Pixel Axis 0" }, "CoordinateComponentLink_3": { "index": 1, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel y", "Pixel x" ], "to": [ "World 1" ], "pix2world": true, "coords": "Coordinates" }, "CoordinateComponentLink_2": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0" ], "to": [ "Pixel Axis 0" ], "pix2world": false, "coords": "Coordinates_0" }, "CoordinateComponentLink_1": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel y", "Pixel x" ], "to": [ "World 0_0" ], "pix2world": true, "coords": "Coordinates" }, "CoordinateComponentLink_0": { "index": 0, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "Pixel Axis 0" ], "to": [ "World 0" ], "pix2world": true, "coords": "Coordinates_0" }, "CoordinateComponentLink_4": { "index": 1, "_type": "glue.core.component_link.CoordinateComponentLink", "frm": [ "World 0_0", "World 1" ], "to": [ "Pixel x" ], "pix2world": false, "coords": "Coordinates" }, "a": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "a" }, "b": { "hidden": false, "_type": "glue.core.data.ComponentID", "label": "b" }, "CoordinateComponent_1": { "world": false, "_type": "glue.core.data.CoordinateComponent", "axis": 0 }, "Component_1": { "units": null, "_type": "glue.core.data.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDMpLCB9ICAgICAgICAgIAooXatr9N7uP2BEXHn4oZo/A7ENS98v7D9I5JtTwmHEP3ajNj9M198/ZE5h+Z+Nzz9wEO/ZkuqlP94MlPisMuQ/G94dgwMP6j8=" } }, "Component_0": { "units": null, "_type": "glue.core.data.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIArNzMzMzMz0P83MzMzMzARAMzMzMzMzEUA=" } }, "DS9Normalize": { "vmax": 0.93116720658359486, "_type": "glue.clients.ds9norm.DS9Normalize", "bias": 0.5, "vmin": 0.032727208219196571, "clip_hi": 95.0, "stretch": "arcsinh", "clip_lo": 5.0, "contrast": 1.0 }, "DataCollection": { "_type": "glue.core.data_collection.DataCollection", "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0", "Component_1", "CoordinateComponent_1", "CoordinateComponent_2", "CoordinateComponent_3", "CoordinateComponent_4" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2", "CoordinateComponentLink_3", "CoordinateComponentLink_4" ], "_protocol": 2, "groups": [], "cids": [ "a", "Pixel Axis 0", "World 0", "b", "image_0", "Pixel y", "Pixel x", "World 0_0", "World 1" ], "data": [ "table", "image" ] }, "Session": { "_type": "glue.core.session.Session" } }glueviz-1.0.1+dfsg.orig/glue/tests/data/glue_v0.10_table.glu0000644000175000017500000002007513502206677023067 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoEAAAAAAAAAAUAAAAAAAAABgAAAAAAAAA=" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates_0", "frm": [ "Pixel Axis 0 [x]_0" ], "index": 0, "pix2world": true, "to": [ "World 0_0" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates_0", "frm": [ "World 0_0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]_0" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_3": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]_1" ], "index": 0, "pix2world": true, "to": [ "World 0_1" ] }, "CoordinateComponentLink_4": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0_1" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]_1" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "Coordinates_0": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "x", "Pixel Axis 0 [x]", "World 0", "y" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0" ], "data": [ "data" ], "groups": [ "Subset 1_0", "Subset 2_0" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2", "CoordinateComponentLink_3", "CoordinateComponentLink_4" ], "subset_group_count": 2 }, "ElementSubsetState": { "_type": "glue.core.subset.ElementSubsetState", "data_uuid": "ca8ae9d2-170d-482a-bb93-4f8e1b4b1ddd", "indices": [ 0, 1 ] }, "ElementSubsetState_0": { "_type": "glue.core.subset.ElementSubsetState", "data_uuid": "ca8ae9d2-170d-482a-bb93-4f8e1b4b1ddd", "indices": [ 1 ] }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "Pixel Axis 0 [x]_0": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "Pixel Axis 0 [x]_1": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "Subset 1_0": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "ElementSubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1" ] }, "Subset 2": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 2_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#33a02c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "Subset 2_0": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 2", "state": "ElementSubsetState_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#33a02c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 2" ] }, "TableWidget": { "_type": "glue.viewers.table.qt.viewer_widget.TableWidget", "layers": [ { "_type": "glue.viewers.table.qt.viewer_widget.TableLayerArtist", "layer": "data", "visible": true, "zorder": 1 }, { "_type": "glue.viewers.table.qt.viewer_widget.TableLayerArtist", "layer": "Subset 1", "visible": true, "zorder": 2 }, { "_type": "glue.viewers.table.qt.viewer_widget.TableLayerArtist", "layer": "Subset 2", "visible": true, "zorder": 3 } ], "pos": [ 0, 0 ], "properties": {}, "session": "Session", "size": [ 640, 480 ] }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 0_0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 0_1": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.plugins.tools.pv_slicer", "glue_vispy_viewers.scatter", "glue_vispy_viewers.volume", "glue.core.data_exporters", "glue.plugins.tools.spectrum_tool", "glue.viewers.table", "glue.plugins.coordinate_helpers", "glue.plugins.exporters.plotly", "glue.viewers.image", "glue.viewers.histogram", "glue.viewers.scatter", "glue_ginga", "glue.plugins.export_d3po" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "TableWidget" ] ] }, "data": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "x", "Component" ], [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "y", "Component_0" ] ], "coords": "Coordinates", "label": "data", "primary_owner": [ "x", "Pixel Axis 0 [x]", "World 0", "y" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "#595959", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1", "Subset 2" ], "uuid": "ca8ae9d2-170d-482a-bb93-4f8e1b4b1ddd" }, "x": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "x" }, "y": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "y" } }glueviz-1.0.1+dfsg.orig/glue/tests/test_config.py0000644000175000017500000000161513605357235021374 0ustar noahfxnoahfxfrom ..config import qt_client, link_function, data_factory from glue.tests.helpers import requires_qt @requires_qt def test_default_clients(): from glue.viewers.image.qt import ImageViewer from glue.viewers.scatter.qt import ScatterViewer from glue.viewers.histogram.qt import HistogramViewer assert ImageViewer in qt_client assert ScatterViewer in qt_client assert HistogramViewer in qt_client def test_add_client(): @qt_client class TestClient(object): pass assert TestClient in qt_client def test_add_link_default(): @link_function(info='maps x to y', output_labels=['y']) def foo(x): return 3 val = (foo, 'maps x to y', ['y'], 'General') assert val in link_function def test_add_data_factory(): @data_factory('XYZ file', "*txt") def foo(x): pass assert (foo, 'XYZ file', '*txt', 0, False) in data_factory glueviz-1.0.1+dfsg.orig/glue/tests/test_settings_helpers.py0000644000175000017500000000341013605357235023504 0ustar noahfxnoahfxfrom unittest.mock import patch import os from glue.config import SettingRegistry from glue._settings_helpers import load_settings, save_settings def test_roundtrip(tmpdir): settings = SettingRegistry() settings.add('STRING', 'green', str) settings.add('INT', 3, int) settings.add('FLOAT', 5.5, float) settings.add('LIST', [1, 2, 3], list) with patch('glue.config.settings', settings): with patch('glue.config.CFG_DIR', tmpdir.strpath): settings.STRING = 'blue' settings.INT = 4 settings.FLOAT = 3.5 settings.LIST = ['A', 'BB', 'CCC'] settings.reset_defaults() assert settings.STRING == 'green' assert settings.INT == 3 assert settings.FLOAT == 5.5 assert settings.LIST == [1, 2, 3] settings.STRING = 'blue' settings.INT = 4 settings.FLOAT = 3.5 settings.LIST = ['A', 'BB', 'CCC'] save_settings() assert os.path.exists(os.path.join(tmpdir.strpath, 'settings.cfg')) settings.reset_defaults() settings.STRING = 'red' settings.INT = 5 # Loading settings will only change settings that have not been # changed from the defaults... load_settings() assert settings.STRING == 'red' assert settings.INT == 5 assert settings.FLOAT == 3.5 assert settings.LIST == ['A', 'BB', 'CCC'] # ... unless the ``force=True`` option is passed load_settings(force=True) assert settings.STRING == 'blue' assert settings.INT == 4 assert settings.FLOAT == 3.5 assert settings.LIST == ['A', 'BB', 'CCC'] glueviz-1.0.1+dfsg.orig/glue/tests/test_qglue.py0000644000175000017500000001764513605357235021256 0ustar noahfxnoahfximport pytest import numpy as np import pandas as pd from unittest.mock import patch from .. import qglue from ..core import BaseCartesianData, Data from ..core.exceptions import IncompatibleAttribute from ..core.registry import Registry from .helpers import requires_astropy, requires_qt @requires_qt @requires_astropy class TestQGlue(object): def setup_method(self, method): from astropy.table import Table from astropy.io.fits import HDUList, ImageHDU Registry().clear() x = [1, 2, 3] y = [2, 3, 4] u = [10, 20, 30, 40] v = [20, 40, 60, 80] self.xy = {'x': x, 'y': y} self.dict_data = {'u': u, 'v': v} self.recarray_data = np.rec.array([(0, 1), (2, 3)], dtype=[(str('a'), int), (str('b'), int)]) self.astropy_table = Table({'x': x, 'y': y}) self.bad_data = {'x': x, 'u': u} self.hdulist = HDUList([ImageHDU(x, name='PRIMARY')]) self.x = np.array(x) self.y = np.array(y) self.u = np.array(u) self.v = np.array(v) def check_setup(self, dc, expected): # assert that the assembled data collection returned # form qglue matches expected structure # test for expected data, components for data in dc: components = set(c.label for c in data.components) e = expected.pop(data.label) for component in e: assert component in components assert len(expected) == 0 def test_qglue_starts_application(self): pandas_data = pd.DataFrame(self.xy) with patch('glue.app.qt.GlueApplication') as ga: qglue(data1=pandas_data) ga.assert_called_once() def test_single_pandas(self): with patch('glue.app.qt.GlueApplication') as ga: qglue(data1=self.xy) dc = ga.call_args[0][0] self.check_setup(dc, {'data1': ['x', 'y']}) def test_single_pandas_nonstring_column(self): with patch('glue.app.qt.GlueApplication') as ga: qglue(data1=pd.DataFrame({1: [1, 2, 3]})) dc = ga.call_args[0][0] self.check_setup(dc, {'data1': ['1']}) def test_single_numpy(self): with patch('glue.app.qt.GlueApplication') as ga: qglue(data1=np.array([1, 2, 3])) dc = ga.call_args[0][0] self.check_setup(dc, {'data1': ['data1']}) def test_single_list(self): with patch('glue.app.qt.GlueApplication') as ga: qglue(data1=[1, 2, 3]) dc = ga.call_args[0][0] self.check_setup(dc, {'data1': ['data1']}) def test_single_dict(self): with patch('glue.app.qt.GlueApplication') as ga: qglue(data2=self.dict_data) dc = ga.call_args[0][0] self.check_setup(dc, {'data2': ['u', 'v']}) def test_recarray(self): with patch('glue.app.qt.GlueApplication') as ga: qglue(data3=self.recarray_data) dc = ga.call_args[0][0] self.check_setup(dc, {'data3': ['a', 'b']}) def test_astropy_table(self): with patch('glue.app.qt.GlueApplication') as ga: qglue(data4=self.astropy_table) dc = ga.call_args[0][0] self.check_setup(dc, {'data4': ['x', 'y']}) def test_multi_data(self): with patch('glue.app.qt.GlueApplication') as ga: qglue(data1=self.dict_data, data2=self.xy) dc = ga.call_args[0][0] self.check_setup(dc, {'data1': ['u', 'v'], 'data2': ['x', 'y']}) def test_hdulist(self): with patch('glue.app.qt.GlueApplication') as ga: qglue(data1=self.hdulist).data_collection dc = ga.call_args[0][0] self.check_setup(dc, {'data1': ['PRIMARY']}) def test_glue_data(self): d = Data(x=[1, 2, 3]) with patch('glue.app.qt.GlueApplication') as ga: qglue(x=d) dc = ga.call_args[0][0] assert d.label == 'x' def test_base_data(self): class CustomData(BaseCartesianData): def get_kind(self): pass def compute_histogram(self): pass def compute_statistic(self): pass def get_mask(self): pass @property def shape(self): pass @property def main_components(self): return [] d = CustomData() with patch('glue.app.qt.GlueApplication') as ga: qglue(x=d) dc = ga.call_args[0][0] assert dc[0] is d def test_simple_link(self): using = lambda x: x * 2 links = [['data1.x', 'data2.u', using]] with patch('glue.app.qt.GlueApplication') as ga: qglue(data1=self.xy, data2=self.dict_data, links=links) dc = ga.call_args[0][0] links = [[['x'], 'u', using]] self.check_setup(dc, {'data1': ['x', 'y'], 'data2': ['u', 'v']}) d = dc[0] if dc[0].label == 'data1' else dc[1] np.testing.assert_array_equal(d['x'], self.x) np.testing.assert_array_equal(d['u'], self.x * 2) d = dc[0] if dc[0].label == 'data2' else dc[1] with pytest.raises(IncompatibleAttribute) as exc: d['x'] def test_multi_link(self): def forwards(x, y): return x * 2, y * 3 def backwards(x, y): return x / 2, y / 3 links = [[['Data1.x', 'Data1.y'], ['Data2.u', 'Data2.v'], forwards, backwards]] with patch('glue.app.qt.GlueApplication') as ga: qglue(Data1=self.xy, Data2=self.dict_data, links=links) dc = ga.call_args[0][0] self.check_setup(dc, {'Data1': ['x', 'y'], 'Data2': ['u', 'v']}) for d in dc: if d.label == 'Data1': np.testing.assert_array_equal(d['x'], self.x) np.testing.assert_array_equal(d['y'], self.y) np.testing.assert_array_equal(d['u'], self.x * 2) np.testing.assert_array_equal(d['v'], self.y * 3) else: np.testing.assert_array_equal(d['x'], self.u / 2) np.testing.assert_array_equal(d['y'], self.v / 3) np.testing.assert_array_equal(d['u'], self.u) np.testing.assert_array_equal(d['v'], self.v) def test_implicit_identity_link(self): links = [('Data1.x', 'Data2.v'), ('Data1.y', 'Data2.u')] with patch('glue.app.qt.GlueApplication') as ga: qglue(Data1=self.xy, Data2=self.dict_data, links=links) dc = ga.call_args[0][0] # currently, identity links rename the second link to first, # so u/v disappear for d in dc: if d.label == 'Data1': np.testing.assert_array_equal(d['x'], self.x) np.testing.assert_array_equal(d['y'], self.y) else: np.testing.assert_array_equal(d['y'], self.u) np.testing.assert_array_equal(d['x'], self.v) def test_bad_link(self): forwards = lambda *args: args links = [(['Data1.a'], ['Data2.b'], forwards)] with pytest.raises(ValueError) as exc: qglue(Data1=self.xy, Data2=self.dict_data, links=links) assert exc.value.args[0] == "Invalid link (no component named Data1.a)" def test_bad_data_shape(self): with pytest.raises(ValueError) as exc: qglue(d=self.bad_data) assert exc.value.args[0].startswith("Invalid format for data 'd'") def test_bad_data_format(self): with pytest.raises(TypeError) as exc: qglue(d=5) assert exc.value.args[0].startswith("Invalid data description") def test_malformed_data_dict(self): with pytest.raises(ValueError) as exc: qglue(d={'x': 'bad'}) assert exc.value.args[0].startswith("Invalid format for data 'd'") glueviz-1.0.1+dfsg.orig/glue/tests/example_data.py0000644000175000017500000000327713605357235021522 0ustar noahfxnoahfximport numpy as np from glue.core.component import Component, CategoricalComponent from glue.core.data import Data def test_histogram_data(): data = Data(label="Test Data") comp_a = Component(np.random.uniform(size=500)) comp_b = Component(np.random.normal(size=500)) data.add_component(comp_a, 'uniform') data.add_component(comp_b, 'normal') return data def test_data(): data = Data(label="Test Data 1") data2 = Data(label="Teset Data 2") comp_a = Component(np.array([1, 2, 3])) comp_b = Component(np.array([1, 2, 3])) comp_c = Component(np.array([2, 4, 6])) comp_d = Component(np.array([1, 3, 5])) data.add_component(comp_a, 'a') data.add_component(comp_b, 'b') data2.add_component(comp_c, 'c') data2.add_component(comp_d, 'd') return data, data2 def test_categorical_data(): data = Data(label="Test Cat Data 1") data2 = Data(label="Teset Cat Data 2") comp_x1 = CategoricalComponent(np.array(['a', 'a', 'b'])) comp_y1 = Component(np.array([1, 2, 3])) comp_x2 = CategoricalComponent(np.array(['c', 'a', 'b'])) comp_y2 = Component(np.array([1, 3, 5])) data.add_component(comp_x1, 'x1') data.add_component(comp_y1, 'y1') data2.add_component(comp_x2, 'x2') data2.add_component(comp_y2, 'y2') return data, data2 def test_image(): data = Data(label="Test Image") comp_a = Component(np.ones((25, 25))) data.add_component(comp_a, 'test_1') comp_b = Component(np.zeros((25, 25))) data.add_component(comp_b, 'test_2') return data def test_cube(): data = Data(label="Test Cube") comp_a = Component(np.ones((16, 16, 16))) data.add_component(comp_a, 'test_3') return data glueviz-1.0.1+dfsg.orig/glue/config.py0000644000175000017500000010450413661230662017170 0ustar noahfxnoahfximport os import imp import sys import warnings from collections import namedtuple from glue.utils import format_choices """ Objects used to configure Glue at runtime. """ __all__ = ['Registry', 'SettingRegistry', 'ExporterRegistry', 'ColormapRegistry', 'DataFactoryRegistry', 'QtClientRegistry', 'LinkFunctionRegistry', 'LinkHelperRegistry', 'ViewerToolRegistry', 'LayerActionRegistry', 'ProfileFitterRegistry', 'qt_client', 'data_factory', 'link_function', 'link_helper', 'colormaps', 'exporters', 'settings', 'fit_plugin', 'auto_refresh', 'importer', 'DictRegistry', 'preference_panes', 'PreferencePanesRegistry', 'DataExporterRegistry', 'data_exporter', 'layer_action', 'SubsetMaskExporterRegistry', 'SubsetMaskImporterRegistry', 'StartupActionRegistry', 'startup_action', 'QtFixedLayoutTabRegistry', 'qt_fixed_layout_tab', 'KeyboardShortcut', 'keyboard_shortcut', 'LayerArtistMakerRegistry', 'layer_artist_maker', 'SessionPatchRegistry', 'session_patch', 'AutoLinkerRegistry', 'autolinker', 'DataTranslatorRegistry', 'data_translator', 'SubsetDefinitionTranslatorRegistry', 'subset_state_translator'] CFG_DIR = os.path.join(os.path.expanduser('~'), '.glue') class Registry(object): """Container to hold groups of objects or settings. Registry instances are used by Glue to track objects used for various tasks like data linking, widget creation, etc. They have the following properties: - A `members` property, which lists each item in the registry - A `default_members` function, which can be overridden to lazily initialize the members list - A call interface, allowing the instance to be used as a decorator for users to add new items to the registry in their config files """ def __init__(self): self._members = [] self._lazy_members = [] self._loaded = False @property def members(self): """ A list of the members in the registry. The return value is a list. The contents of the list are specified in each subclass""" self._load_lazy_members() if not self._loaded: self._members = self.default_members() + self._members self._loaded = True return self._members def default_members(self): """The member items provided by default. These are put in this method so that code is only imported when needed""" return [] def add(self, value): """ Add a new item to the registry. """ self._members.append(value) def lazy_add(self, value): """ Add a reference to a plugin which will be loaded when needed. """ self._lazy_members.append(value) def _load_lazy_members(self): from glue.plugins import load_plugin while self._lazy_members: plugin = self._lazy_members.pop() load_plugin(plugin) def __iter__(self): return iter(self.members) def __len__(self): return len(self.members) def __contains__(self, value): return value in self.members def __call__(self, arg): """This is provided so that registry instances can be used as decorators. The decorators should add the decorated code object to the registry, and return the original function""" self.add(arg) return arg class DictRegistry(Registry): """ Base class for registries that are based on dictionaries instead of lists of objects. """ def __init__(self): self._members = {} self._lazy_members = [] self._loaded = False @property def members(self): self._load_lazy_members() if not self._loaded: defaults = self.default_members() for key in defaults: if key in self._members: self._members[key].extend(defaults[key]) else: self._members[key] = defaults[key] self._loaded = True return self._members def default_members(self): return {} class SettingRegistry(DictRegistry): """Stores key/value settings that code can use to customize Glue Each member is a tuple of 3 items: - key: the setting name [str] - value: the default setting [object] - validator: A function which tests whether the input is a valid value, and raises a ValueError if invalid. On valid input, returns the (possibly sanitized) setting value. """ def __init__(self): super(SettingRegistry, self).__init__() self._validators = {} self._defaults = {} def add(self, key, default=None, validator=None): if validator is None: validator = lambda x: x self._defaults[key] = validator(default) self._validators[key] = validator def __getattr__(self, attr): if attr.startswith('_'): raise AttributeError("No such setting: {0}".format(attr)) else: if attr in self._members: return self._members[attr] elif attr in self._defaults: return self._defaults[attr] else: raise AttributeError("No such setting: {0}".format(attr)) def __setattr__(self, attr, value): if attr.startswith('_'): object.__setattr__(self, attr, value) elif attr in self: self._members[attr] = self._validators[attr](value) else: raise AttributeError("No such setting: {0}".format(attr)) def __dir__(self): return sorted(self._members.keys()) def __contains__(self, setting): return setting in self._defaults def __iter__(self): for key in self._defaults: value = self._members.get(key, self._defaults[key]) yield key, value, self._validators[key] def __str__(self): s = "" for name, value, validator in self: s += "{0}: {1}\n".format(name, value) return s def reset_defaults(self): self._members.clear() def is_default(self, setting): return setting in self._defaults and setting not in self._members class QGlueParserRegistry(Registry): """ Registry for parsers that can be used to interpret arguments to the :func:`~glue.qglue` function. The members property is a list of parsers, each represented as a named tuple with ``data_class``, ``parser`` and ``priority`` attributes, where ``class`` defines the class for which to use the parser, and ``parser`` is a function that takes the input data and returns a list of glue :class:`~glue.core.data.Data` objects. The ``parser`` functions should take two arguments: the variable containing the data being parsed, and a label. In addition, the priority (defaulting to 0) can be specified in case one wants to make sure sub-classes get tested before more general classes. The priority should be a numerical value, and the larger it is the higher the priority. """ item = namedtuple('DataFactory', 'data_class parser priority') def add(self, data_class, parser, priority=0): """ Add a new parser Parameters ---------- data_class : class The type of of data for which to use the specified parser parser : func The function to use to parse the input data priority : int, optional The priority, which is used to determine the order in which to check the parsers. """ self.members.append(self.item(data_class, parser, priority)) def __call__(self, data_class, priority=0): def adder(func): if isinstance(data_class, tuple): for dc in data_class: self.add(dc, func, priority=priority) else: self.add(data_class, func, priority=priority) return func return adder def __iter__(self): for member in sorted(self.members, key=lambda x: -x.priority): yield member class DataImportRegistry(Registry): """ Stores functions which can import data. The members property is a list of importers, each represented as a ``(label, load_function)`` tuple. The ``load_function`` should take no arguments and return a list of :class:`~glue.core.data.Data` objects. """ def add(self, label, importer): """ Add a new importer :param label: Short label for the importer :type label: str :param importer: importer function :type importer: function() """ self.members.append((label, importer)) def __call__(self, label): def adder(func): self.add(label, func) return func return adder class MenubarPluginRegistry(Registry): """ Stores menubar plugins. The members property is a list of menubar plugins, each represented as a ``(label, function)`` tuple. The ``function`` should take two items which are a reference to the session and to the data collection respectively. """ def add(self, label, function): """ Add a new menubar plugin :param label: Short label for the plugin :type label: str :param function: function :type function: function() """ self.members.append((label, function)) def __call__(self, label): def adder(func): self.add(label, func) return func return adder class PreferencePanesRegistry(DictRegistry): """ Stores preference panes The members property is a list of tuples of Qt widget classes that can have their own tab in the preferences window. """ def add(self, label, widget_cls): self._members[label] = widget_cls def __iter__(self): for label in self._members: yield label, self._members[label] class AutoLinkerRegistry(Registry): """ Registry for auto-linking functions that given a data collection can suggest links. The members property is a list of auto-linking plugins, each represented as a ``(label, function)`` tuple. The ``function`` should take a reference to the data collection. """ def add(self, label, function): """ Add a new auto-linking function. Parameters ---------- label : str Short label for the plugin function : func The plugin function """ self.members.append((label, function)) def __call__(self, label): def adder(func): self.add(label, func) return func return adder class ExporterRegistry(Registry): """Stores functions which can export an application to an output file The members property is a list of exporters, each represented as a (label, save_function, can_save_function, outmode) tuple. save_function takes an (application, path) as input, and saves the session can_save_function takes an application as input, and raises an exception if saving this session is not possible outmode is a string, with one of 3 values: 'file': indicates that exporter creates a file 'directory': exporter creates a directory 'label': exporter doesn't write to disk, but needs a label """ def add(self, label, exporter, checker, outmode=None): """ Add a new exporter Parameters ---------- label : str Short label for the exporter exporter : func Exporter function which takes two arguments: the application and optionally the path or label to create. This function should raise an exception if export isn't possible. checker : func Function that checks if saving is possible, which takes one argument: the application. outmode : str or `None` Indicates what kind of output is created. This can be either set to ``'file'``, ``'directory'``, ``'label'``, or `None`. """ self.members.append((label, exporter, checker, outmode)) class ColormapRegistry(Registry): """Stores colormaps for the Image Viewer. The members property is a list of colormaps, each represented as a [name,cmap] pair. """ def default_members(self): import matplotlib.cm as cm members = [] members.append(['Gray', cm.gray]) members.append(['Viridis', cm.viridis]) members.append(['Plasma', cm.plasma]) members.append(['Inferno', cm.inferno]) members.append(['Magma', cm.magma]) members.append(['Purple-Blue', cm.PuBu]) members.append(['Yellow-Green-Blue', cm.YlGnBu]) members.append(['Yellow-Orange-Red', cm.YlOrRd]) members.append(['Red-Purple', cm.RdPu]) members.append(['Blue-Green', cm.BuGn]) members.append(['Hot', cm.hot]) members.append(['Red-Blue', cm.RdBu]) members.append(['Red-Yellow-Blue', cm.RdYlBu]) members.append(['Purple-Orange', cm.PuOr]) members.append(['Purple-Green', cm.PRGn]) return members def add(self, label, cmap): """ Add colormap *cmap* with label *label*. """ self.members.append([label, cmap]) def __getitem__(self, cmap_name): for name, cmap in self.members: if name == cmap_name: return cmap raise KeyError(cmap_name) def name_from_cmap(self, cmap_desired): for name, cmap in self.members: if cmap is cmap_desired: return name raise ValueError("Could not find name for colormap") class DataFactoryRegistry(Registry): """Stores data factories. Data factories take filenames as input, and return :class:`~glue.core.data.Data` instances The members property returns a list of (function, label, identifier, priority) namedtuples: - Function is the factory that creates the data object - label is a short human-readable description of the factory - identifier is a function that takes ``(filename, **kwargs)`` as input and returns True if the factory can open the file - priority is a numerical value that indicates how confident the data factory is that it should read the data, relative to other data factories. For example, a highly specialized FITS reader for specific FITS file types can be given a higher priority than the generic FITS reader in order to take precedence over it. New data factories can be registered via:: @data_factory('label_name', identifier=identifier, priority=10) def new_factory(file_name): ... If not specified, the priority defaults to 0. """ item = namedtuple('DataFactory', 'function label identifier priority deprecated') def __call__(self, label, identifier=None, priority=None, default='', deprecated=False): if identifier is None: identifier = lambda *a, **k: False if priority is None: if deprecated: priority = -1000 else: priority = 0 def adder(func): self.add(self.item(func, label, identifier, priority, deprecated)) return func return adder def __iter__(self): for member in sorted(self.members, key=lambda x: (-x.priority, x.label)): yield member class DataExporterRegistry(Registry): """ Stores data exporters. Data exporters take a data/subset object as input followed by a filename. """ item = namedtuple('DataFactory', 'function label extension') def __call__(self, label, extension=[]): def adder(func): self.add(self.item(func, label, extension)) return func return adder def __iter__(self): for member in sorted(self.members, key=lambda x: x.label): yield member class SubsetMaskExporterRegistry(DataExporterRegistry): """ Stores mask exporters. Mask exporters should take a filename followed by a dictionary of Numpy boolean arrays all with the same dimensions. """ item = namedtuple('SubsetMaskExporter', 'function label extension') class SubsetMaskImporterRegistry(DataExporterRegistry): """ Stores mask importers. Mask importers should take a filename and return a dictionary of Numpy boolean arrays. """ item = namedtuple('SubsetMaskImporter', 'function label extension') class DataTranslatorRegistry(Registry): """ Stores data translators, which are classes that define methods to translate between :class:`~glue.core.data.Data` objects and other kinds of data containers. """ item = namedtuple('DataTranslator', 'target_cls handler priority') def __call__(self, target_cls, priority=0): def adder(handler_cls): self.add(self.item(target_cls, handler_cls(), priority)) return handler_cls return adder def __iter__(self): for member in sorted(self.members, key=lambda x: -x.priority): yield member def remove(self, target_cls): for member in self.members[:]: if member.target_cls is target_cls: self.members.remove(member) @property def supported_classes(self): return [tr.target_cls for tr in self] def get_handler_for(self, data_or_class): for translator in self: if isinstance(data_or_class, translator.target_cls) or data_or_class is translator.target_cls: handler = translator.handler preferred = translator.target_cls break else: if isinstance(data_or_class, type): raise TypeError("Could not find a class to translate objects of " "type Data to {0}".format(data_or_class.__name__)) else: raise TypeError("Could not find a class to translate objects of " "type {0} to Data".format(data_or_class.__class__.__name__)) return handler, preferred class SubsetDefinitionTranslatorRegistry(Registry): """ Stores subset state translators, which are classes that define methods to translate between :class:`~glue.core.subset.SubsetState` objects and other kinds of selection representations. """ item = namedtuple('SubsetDefinitionTranslator', 'format handler priority') def __call__(self, format, priority=0): def adder(handler_cls): self.add(self.item(format, handler_cls(), priority)) return handler_cls return adder def __iter__(self): for member in sorted(self.members, key=lambda x: -x.priority): yield member def remove(self, format): for member in self.members[:]: if member.format is format: self.members.remove(member) @property def supported_classes(self): return [tr.target_cls for tr in self] def get_handler_for(self, format): for translator in self: if translator.format == format: return translator.handler all_formats = [translator.format for translator in self] if format is None: raise ValueError("Subset state handler format not set - should be one of:" + format_choices(all_formats)) else: raise ValueError("Invalid subset state handler format '{0}' - should be one of:".format(format) + format_choices(all_formats)) class QtClientRegistry(Registry): """ Stores QT widgets to visualize data. The members property is a list of Qt widget classes New widgets can be registered via:: @qt_client class CustomWidget(QMainWindow): ... """ class QtFixedLayoutTabRegistry(Registry): """ Stores Qt pre-defined tabs (non-MDI) New widgets can be registered via:: @qt_fixed_layout_tab class CustomTab(QWidget): ... """ class ViewerToolRegistry(DictRegistry): def add(self, tool_cls): """ Add a tool class to the registry. The the ``tool_id`` attribute on the tool_cls should be set, and is used by the viewers to indicate which tools they want to """ if tool_cls.tool_id in self.members: raise ValueError("Tool ID '{0}' already registered".format(tool_cls.tool_id)) else: self.members[tool_cls.tool_id] = tool_cls def __call__(self, tool_cls): self.add(tool_cls) return tool_cls class StartupActionRegistry(DictRegistry): def add(self, startup_name, startup_function): """ Add a startup function to the registry. This is a function that will get called once glue has been started and any data loaded, and can be used to set up specific layouts and create links. Startup actions are triggered by either specifying comma-separated names of actions on the command-line:: glue --startup=mystartupaction or by passing an iterable of startup action names to the ``startup`` keyword of ``GlueApplication``. The startup function will be given the session object and the data collection object. """ if startup_name in self.members: raise ValueError("A startup action with the name '{0}' already exists".format(startup_name)) else: self.members[startup_name] = startup_function def __call__(self, name): def adder(func): self.add(name, func) return func return adder class LinkFunctionRegistry(Registry): """Stores functions to convert between quantities The members property is a list of (function, info_string, output_labels) namedtuples. ``info_string`` describes what the function does. ``output_labels`` is a list of names for each output. ``category`` is a category in which the link function will appear (defaults to 'General'). New link functions can be registered via @link_function(info="maps degrees to arcseconds", output_labels=['arcsec']) def degrees2arcsec(degrees): return degrees * 3600 Link functions are expected to receive and return numpy arrays """ item = namedtuple('LinkFunction', 'function info output_labels category') def __call__(self, info="", output_labels=None, category='General'): out = output_labels or [] def adder(func): self.add(self.item(func, info, out, category)) return func return adder class LayerActionRegistry(Registry): """ Stores custom menu actions available when the user select one or more datasets, subset group, or subset in the data collection view. This members property is a list of named tuples with the following attributes: * ``label``: the user-facing name of the action * ``tooltip``: the text that appears when hovering with the mouse over the action * ``callback``: the function to call when the action is triggered * ``icon``: an icon image to use for the layer action * ``single``: whether to show this action only when selecting single layers (default: `False`) * ``data``: if ``single`` is `True` whether to only show the action when selecting a dataset * ``subset_group``: if ``single`` is `True` whether to only show the action when selecting a subset group * ``subset``: if ``single`` is `True` whether to only show the action when selecting a subset The callback function is called with two arguments. If ``single`` is `True`, the first argument is the selected layer, otherwise it is the list of selected layers. The second argument is the `~glue.core.data_collection.DataCollection` object. """ item = namedtuple('LayerAction', 'label tooltip callback icon single data subset_group, subset') def __call__(self, label, callback=None, tooltip=None, icon=None, single=False, data=False, subset_group=False, subset=False): # Backward-compatibility if callback is not None: self.add(self.item(label, tooltip, callback, icon, True, False, False, True)) return True def adder(func): self.add(self.item(label, tooltip, func, icon, single, data, subset_group, subset)) return func return adder class LinkHelperRegistry(Registry): """ Stores helper objects that compute many ComponentLinks at once Link helpers can either be functions or subclasses of `~glue.core.link_helpers.LinkCollection`. If a function, it should take a list of `~glue.core.component_id.ComponentIDs` as inputs, and returns an iterable of `~glue.core.component_link.ComponentLink` objects. A link helper should only link components between two datasets, and the order of the inputs to the function should be the `~glue.core.component_id.ComponentIDs` of the first dataset, followed by the ones for the second dataset. Human-readable names for the input and output components should be given using ``input_labels`` and ``output_labels`` New link helpers can be registered with e.g.:: @link_helper('Links degrees and arcseconds in both directions', input_labels=['degree'], output_labels=['arcsecond']) def new_helper(degree, arcsecond): return [ComponentLink([degree], arcsecond, using=lambda d: d*3600), ComponentLink([arcsecond], degree, using=lambda a: a/3600)] """ item = namedtuple('LinkHelper', 'helper category') def __call__(self, info=None, input_labels=None, output_labels=None, category='General'): if input_labels is not None and output_labels is None: warnings.warn('Specifying @link_helper without giving output_labels is ' 'deprecated and will be removed in future. See the ' 'documentation about how to specify output_labels', UserWarning) def adder(func): from glue.core.link_helpers import LinkCollection, functional_link_collection if not issubclass(func, LinkCollection): func = functional_link_collection(func, description=info, labels1=input_labels or [], labels2=output_labels or []) self.add(self.item(func, category)) return func return adder class ProfileFitterRegistry(Registry): item = namedtuple('ProfileFitter', 'cls') def add(self, cls): """ Add colormap *cmap* with label *label*. """ self.members.append(cls) def default_members(self): from glue.core.fitters import __FITTERS__ return list(__FITTERS__) class BooleanSetting(object): def __init__(self, default=True): self.state = default def __call__(self, state=None): if state not in [None, True, False]: raise ValueError("Invalid True/False setting: %s" % state) if state is not None: self.state = state return self.state class KeyboardShortcut(DictRegistry): """ Stores keyboard shortcuts. The members property is a dictionary within a dictionary of keyboard shortcuts, which is represented as (viewer,(keybind,function)). The ``function`` should take one item, which is a reference to the session. """ def add(self, valid_viewers, keybind, function): """ Add a new keyboard shortcut Parameters ---------- arg1: list list of viewers where event can be fired arg2: Qt.Key type of key event arg3: function() function to be run that corresponds with key """ if valid_viewers: for viewer in valid_viewers: if viewer in self.members: if keybind in self.members[viewer]: raise ValueError("Keyboard shortcut '{0}' already registered in {1}".format(keybind, viewer)) else: self.members[viewer][keybind] = function else: self.members[viewer] = {keybind: function} else: if None in self.members: if keybind in self.members[None]: raise ValueError("Keyboard shortcut '{0}' already registered in {1}".format(keybind, None)) else: self.members[None][keybind] = function else: self.members[None] = {keybind: function} def __call__(self, keybind, valid_viewers): def adder(func): self.add(valid_viewers, keybind, func) return func return adder class LayerArtistMakerRegistry(Registry): """ A registry that allows customization of layer artists based on the data and viewer type. """ item = namedtuple('LayerArtistMaker', 'label function priority') def add(self, label, function, priority=0): """ Add a new plugin for providing custom layer artists. Plugins take the form of functions that take two arguments - the data or subset being added, and the viewer. The function should either return a `~glue.viewers.common.layer_artist.LayerArtist` sub-class object or `None`. Parameters ---------- label : str Name for the plugin function : callable The function that returns layer artists priority : int, optional Set this to a higher number if multiple plugins are present and you want your plugin to take precedence over another. """ self.members.append(self.item(label, function, priority)) def __call__(self, label, priority=0): def adder(func): self.add(label, func, priority=priority) return func return adder def __iter__(self): for member in sorted(self.members, key=lambda x: -x.priority): yield member class SessionPatchRegistry(Registry): """A registry that allows in-place patch of the session file""" item = namedtuple('SessionPatch', 'function priority') def add(self, function, priority=0): """ Add a new plugin for providing custom in-place session patch. Plugins take the form of functions that take one argument: the session object (a dictionary). It should returns `None`. Parameters ---------- function : callable The function apply the patch *in-place* priority : int, optional Set this to a higher number if multiple plugins are present and you want your plugin to take precedence over another. """ self.members.append(self.item(function, priority)) def __call__(self, priority=0): def adder(func): self.add(func, priority=priority) return func return adder def __iter__(self): for member in sorted(self.members, key=lambda x: -x.priority): yield member layer_artist_maker = LayerArtistMakerRegistry() qt_client = QtClientRegistry() qt_fixed_layout_tab = QtFixedLayoutTabRegistry() viewer_tool = ViewerToolRegistry() link_function = LinkFunctionRegistry() link_helper = LinkHelperRegistry() colormaps = ColormapRegistry() importer = DataImportRegistry() exporters = ExporterRegistry() settings = SettingRegistry() fit_plugin = ProfileFitterRegistry() layer_action = LayerActionRegistry() menubar_plugin = MenubarPluginRegistry() preference_panes = PreferencePanesRegistry() qglue_parser = QGlueParserRegistry() startup_action = StartupActionRegistry() keyboard_shortcut = KeyboardShortcut() autolinker = AutoLinkerRegistry() session_patch = SessionPatchRegistry() # watch loaded data files for changes? auto_refresh = BooleanSetting(False) enable_contracts = BooleanSetting(False) # Data and subset I/O data_factory = DataFactoryRegistry() data_exporter = DataExporterRegistry() subset_mask_exporter = SubsetMaskExporterRegistry() subset_mask_importer = SubsetMaskImporterRegistry() data_translator = DataTranslatorRegistry() subset_state_translator = SubsetDefinitionTranslatorRegistry() # Backward-compatibility single_subset_action = layer_action def load_configuration(search_path=None): """ Find and import a config.py file Returns: The module object Raises: Exception, if no module was found """ search_order = search_path or _default_search_order() result = imp.new_module('config') for config_file in search_order: dir = os.path.dirname(config_file) try: sys.path.append(dir) config = imp.load_source('config', config_file) result = config except IOError: pass except Exception as e: raise type(e)("Error loading config file %s:\n%s" % (config_file, e), sys.exc_info()[2]) finally: sys.path.remove(dir) return result def _default_search_order(): """ The default configuration file search order: * current working directory * environ var GLUERC * HOME/.glue/config.py * Glue's own default config """ search_order = [os.path.join(os.getcwd(), 'config.py')] if 'GLUERC' in os.environ: search_order.append(os.environ['GLUERC']) search_order.append(os.path.join(CFG_DIR, 'config.py')) return search_order[::-1] # ##### Now define global settings ###### GRAY = '#7F7F7F' BLUE = "#1F78B4" GREEN = "#33A02C" RED = "#E31A1C" ORANGE = "#FF7F00" PURPLE = "#6A3D9A" YELLOW = "#FFFF99" BROWN = "#8C510A" PINK = "#FB9A99" LIGHT_BLUE = "#A6CEE3" LIGHT_GREEN = "#B2DF8A" LIGHT_RED = "#FB9A99" LIGHT_ORANGE = "#FDBF6F" LIGHT_PURPLE = "#CAB2D6" settings.add('SUBSET_COLORS', [RED, GREEN, BLUE, BROWN, ORANGE, PURPLE, PINK], validator=list) settings.add('DATA_COLOR', '#595959') settings.add('DATA_ALPHA', 0.8, validator=float) settings.add('BACKGROUND_COLOR', '#FFFFFF') settings.add('FOREGROUND_COLOR', '#000000') settings.add('SHOW_LARGE_DATA_WARNING', True, validator=bool) settings.add('SHOW_INFO_PROFILE_OPEN', True, validator=bool) settings.add('SHOW_WARN_PROFILE_DUPLICATE', True, validator=bool) settings.add('FONT_SIZE', -1.0, validator=float) settings.add('AUTOLINK', {}, validator=dict) glueviz-1.0.1+dfsg.orig/glue/conftest.py0000644000175000017500000000664513752534424017563 0ustar noahfxnoahfximport os import sys import warnings import pytest try: from qtpy import PYSIDE2 except Exception: PYSIDE2 = False from glue.config import CFG_DIR as CFG_DIR_ORIG try: import objgraph except ImportError: OBJGRAPH_INSTALLED = False else: OBJGRAPH_INSTALLED = True STDERR_ORIGINAL = sys.stderr ON_APPVEYOR = os.environ.get('APPVEYOR', 'False') == 'True' def pytest_runtest_teardown(item, nextitem): sys.stderr = STDERR_ORIGINAL global start_dir os.chdir(start_dir) def pytest_addoption(parser): parser.addoption("--no-optional-skip", action="store_true", default=False, help="don't skip any tests with optional dependencies") start_dir = None def pytest_configure(config): global start_dir start_dir = os.path.abspath('.') os.environ['GLUE_TESTING'] = 'True' if config.getoption('no_optional_skip'): from glue.tests import helpers for attr in helpers.__dict__: if attr.startswith('requires_'): # The following line replaces the decorators with a function # that does noting, effectively disabling it. setattr(helpers, attr, lambda f: f) # Make sure we don't affect the real glue config dir import tempfile from glue import config config.CFG_DIR = tempfile.mkdtemp() # Start up QApplication, if the Qt code is present try: from glue.utils.qt import get_qapp except Exception: # Note that we catch any exception, not just ImportError, because # QtPy can raise a PythonQtError. pass else: get_qapp() # Force loading of plugins from glue.main import load_plugins load_plugins() def pytest_report_header(config): from glue import __version__ glue_version = "%20s:\t%s" % ("glue", __version__) from glue._deps import get_status return os.linesep + glue_version + os.linesep + os.linesep + get_status() def pytest_unconfigure(config): os.environ.pop('GLUE_TESTING') # Reset configuration directory to original one from glue import config config.CFG_DIR = CFG_DIR_ORIG # Remove reference to QApplication to prevent segmentation fault on PySide try: from glue.utils.qt import app app.qapp = None except Exception: # for when we run the tests without the qt directories # Note that we catch any exception, not just ImportError, because # QtPy can raise a PythonQtError. pass if OBJGRAPH_INSTALLED and not ON_APPVEYOR: # Make sure there are no lingering references to GlueApplication obj = objgraph.by_type('GlueApplication') if len(obj) > 0: objgraph.show_backrefs(objgraph.by_type('GlueApplication')) warnings.warn('There are {0} remaining references to GlueApplication'.format(len(obj))) # Uncomment when checking for memory leaks # objgraph.show_most_common_types(limit=100) # With PySide2, tests can fail in a non-deterministic way with the following # error: # # AttributeError: 'PySide2.QtGui.QStandardItem' object has no attribute 'connect' # # Until this can be properly debugged and fixed, we xfail any test that fails # with this exception if PYSIDE2: def pytest_runtest_call(__multicall__): try: __multicall__.execute() except AttributeError as exc: if 'PySide2.QtGui.QStandardItem' in str(exc): pytest.xfail() glueviz-1.0.1+dfsg.orig/glue/logger.py0000644000175000017500000000026213605357235017202 0ustar noahfxnoahfxfrom logging import getLogger, basicConfig, NullHandler basicConfig() logger = getLogger("glue") # Default to Null unless we override this later logger.addHandler(NullHandler()) glueviz-1.0.1+dfsg.orig/glue/viewers/0000755000175000017500000000000013752535025017033 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/0000755000175000017500000000000013752535025021202 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/tests/0000755000175000017500000000000013752535025022344 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/tests/test_mouse_mode.py0000644000175000017500000001345113605357235026117 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 from unittest.mock import MagicMock # from glue.utils.qt import process_dialog from ..mouse_mode import MouseMode from ..toolbar_mode import RectangleMode, CircleMode, PolyMode class Event(object): def __init__(self, x, y, button=3, key='a'): self.x = x self.y = y self.xdata = x self.ydata = y self.button = button self.key = key self.inaxes = True def viewer(): result = MagicMock() result.axes.figure.canvas.get_width_height.return_value = (640, 480) return result class TestMouseMode(object): def setup_method(self, method): self.mode = self.mode_factory()(viewer()) self.axes = self.mode._axes self.attach_callbacks() def attach_callbacks(self): self.press = self.mode._press_callback = MagicMock() self.move = self.mode._move_callback = MagicMock() self.release = self.mode._release_callback = MagicMock() def mode_factory(self): return MouseMode def test_press_callback(self): e = Event(1, 2) self.mode.press(e) self.press.assert_called_once_with(self.mode) assert self.move.call_count == 0 assert self.release.call_count == 0 # def test_log_null_event(self): # """ Should exit quietly if event is None """ # self.mode._log_position(None) def test_move_callback(self): e = Event(1, 2) self.mode.move(e) self.move.assert_called_once_with(self.mode) assert self.press.call_count == 0 assert self.release.call_count == 0 def test_release_callback(self): e = Event(1, 2) self.mode.release(e) self.release.assert_called_once_with(self.mode) assert self.press.call_count == 0 assert self.move.call_count == 0 def test_press_log(self): e = Event(1, 2) self.mode.press(e) assert self.mode._event_x == 1 assert self.mode._event_y == 2 def test_move_log(self): e = Event(1, 2) self.mode.move(e) assert self.mode._event_x == 1 assert self.mode._event_y == 2 def test_release_log(self): e = Event(1, 2) self.mode.release(e) assert self.mode._event_x == 1 assert self.mode._event_y == 2 class TestRoiMode(TestMouseMode): def setup_method(self, method): TestMouseMode.setup_method(self, method) self.mode._roi_tool = MagicMock() def mode_factory(self): raise NotImplementedError() def test_roi_not_called_on_press(self): e = Event(1, 2) self.mode.press(e) assert self.mode._roi_tool.start_selection.call_count == 0 def test_roi_called_on_drag(self): e = Event(1, 2) e2 = Event(10, 200) self.mode.press(e) self.mode.move(e2) self.mode._roi_tool.start_selection.assert_called_once_with(e) self.mode._roi_tool.update_selection.assert_called_once_with(e2) # def test_roi_ignores_small_drags(self): # e = Event(1, 2) # e2 = Event(1, 3) # self.mode.press(e) # self.mode.move(e2) # assert self.mode._roi_tool.start_selection.call_count == 0 # assert self.mode._roi_tool.update_selection.call_count == 0 def test_roi_called_on_release(self): e = Event(1, 2) e2 = Event(10, 20) self.mode.press(e) self.mode.move(e2) self.mode.release(e2) self.mode._roi_tool.finalize_selection.assert_called_once_with(e2) def test_roi(self): self.mode.roi() self.mode._roi_tool.roi.assert_called_once_with() def test_roi_resets_on_escape(self): e = Event(1, 2) e2 = Event(1, 30, key='escape') self.mode.press(e) self.mode.key(e2) self.mode.press(e) assert self.mode._roi_tool.abort_selection.call_count == 1 class TestClickRoiMode(TestMouseMode): def setup_method(self, method): TestMouseMode.setup_method(self, method) self.mode._roi_tool = MagicMock() self.mode._roi_tool.active.return_value = False def mode_factory(self): raise NotImplementedError() def test_roi_started_on_press(self): e = Event(1, 2) self.mode.press(e) assert self.mode._roi_tool.start_selection.call_count == 1 def test_roi_updates_on_subsequent_presses(self): e = Event(1, 2) e2 = Event(1, 30) self.mode.press(e) self.mode._roi_tool.active.return_value = True self.mode.press(e2) assert self.mode._roi_tool.start_selection.call_count == 1 assert self.mode._roi_tool.update_selection.call_count == 1 def test_roi_finalizes_on_enter(self): e = Event(1, 2) e2 = Event(1, 20) e3 = Event(1, 30, key='enter') self.mode.press(e) self.mode._roi_tool.active.return_value = True self.mode.press(e2) self.mode.key(e3) self.mode._roi_tool.start_selection.assert_called_once_with(e) self.mode._roi_tool.update_selection.assert_called_once_with(e2) self.mode._roi_tool.finalize_selection.assert_called_once_with(e2) def test_roi_resets_on_escape(self): e = Event(1, 2) e2 = Event(1, 30, key='escape') self.mode.press(e) self.mode.key(e2) self.mode.press(e) assert self.mode._roi_tool.abort_selection.call_count == 1 assert self.mode._roi_tool.start_selection.call_count == 2 class TestRectangleMode(TestRoiMode): def mode_factory(self): return RectangleMode class TestCircleMode(TestRoiMode): def mode_factory(self): return CircleMode class TestPolyMode(TestClickRoiMode): def mode_factory(self): return PolyMode del TestRoiMode # prevents test discovery from running abstract test del TestClickRoiMode glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/tests/test_viewer.py0000644000175000017500000000454113502206677025263 0ustar noahfxnoahfxfrom numpy.testing import assert_allclose import matplotlib.pyplot as plt from glue.core.application_base import Application from glue.viewers.common.viewer import Viewer from glue.viewers.matplotlib.viewer import MatplotlibViewerMixin from glue.viewers.matplotlib.state import MatplotlibDataViewerState def assert_limits(viewer, x_min, x_max, y_min, y_max): # Convenience to check both state and matplotlib assert_allclose(viewer.state.x_min, x_min) assert_allclose(viewer.state.x_max, x_max) assert_allclose(viewer.state.y_min, y_min) assert_allclose(viewer.state.y_max, y_max) assert_allclose(viewer.axes.get_xlim(), (x_min, x_max)) assert_allclose(viewer.axes.get_ylim(), (y_min, y_max)) def test_aspect_ratio(): # Test of the aspect ratio infrastructure class CustomViewer(MatplotlibViewerMixin, Viewer): _state_cls = MatplotlibDataViewerState def __init__(self, *args, **kwargs): Viewer.__init__(self, *args, **kwargs) self.figure = plt.figure(figsize=(12, 6)) self.axes = self.figure.add_axes([0, 0, 1, 1]) MatplotlibViewerMixin.setup_callbacks(self) def show(self): pass class CustomApplication(Application): def add_widget(self, *args, **kwargs): pass app = CustomApplication() viewer = app.new_data_viewer(CustomViewer) viewer.state.aspect = 'equal' assert_limits(viewer, -0.5, 1.5, 0., 1.) # Test changing x limits in state, which should just change the y limits viewer.state.x_min = -2.5 assert_limits(viewer, -2.5, 1.5, -0.5, 1.5) viewer.state.x_max = -1.5 assert_limits(viewer, -2.5, -1.5, 0.25, 0.75) # Test changing y limits in state, which should just change the x limits viewer.state.y_max = 1.25 assert_limits(viewer, -3.0, -1.0, 0.25, 1.25) viewer.state.y_min = 0.75 assert_limits(viewer, -2.5, -1.5, 0.75, 1.25) # Test changing x limits in Matplotlib, which should just change the x limits viewer.axes.set_xlim(1., 3.) assert_limits(viewer, 1.0, 3.0, 0.5, 1.5) # Test changing x limits in Matplotlib, which should just change the x limits viewer.axes.set_ylim(0., 2.) assert_limits(viewer, 0.0, 4.0, 0.0, 2.0) # We include tests for resizing inside the Qt folder (since this doesn't # work correctly with the Agg backend) glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/tests/__init__.py0000644000175000017500000000000013502206677024444 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/tests/test_state.py0000755000175000017500000000154213752534424025104 0ustar noahfxnoahfxfrom ..state import MatplotlibLegendState from glue.config import settings from matplotlib.colors import to_rgba class TestMatplotlibLegendState: def setup_method(self, method): self.state = MatplotlibLegendState() def test_draggable(self): self.state.location = 'draggable' assert self.state.draggable assert self.state.mpl_location == 'best' def test_no_draggable(self): self.state.location = 'lower left' assert not self.state.draggable assert self.state.mpl_location == 'lower left' def test_no_edge(self): self.state.show_edge = False assert self.state.edge_color is None def test_default_color(self): assert self.state.frame_color == settings.BACKGROUND_COLOR assert self.state.edge_color == to_rgba(settings.FOREGROUND_COLOR, self.state.alpha) glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/0000755000175000017500000000000013752535025021626 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/tests/0000755000175000017500000000000013752535025022770 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/tests/test_toolbar.py0000644000175000017500000000525213605357235026051 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase from glue.core.tests.util import simple_session class ToolbarModeTest(ToolbarModeBase): tool_id = 'test' tool_tip = 'just testing' icon = 'glue_square' def __init__(self, axes, release_callback=None): super(ToolbarModeTest, self).__init__(axes, release_callback=release_callback) self.action_text = 'test text' self.last_mode = None def press(self, event): self.last_mode = 'PRESS' def move(self, event): self.last_mode = 'MOVE' class ExampleViewer(MatplotlibDataViewer): def __init__(self, session, parent=None): super(ExampleViewer, self).__init__(session, parent=parent) self.axes.plot([1, 2, 3])[0] def initialize_toolbar(self): super(ExampleViewer, self).initialize_toolbar() self.tool = ToolbarModeTest(self, release_callback=self.callback) self.toolbar.add_tool(self.tool) def callback(self, mode): self._called_back = True class TestToolbar(object): def setup_method(self, method): self.session = simple_session() self.viewer = ExampleViewer(self.session) self._called_back = False def teardown_method(self, method): self.viewer.close() def assert_valid_mode_state(self, target_mode): for tool_id in self.viewer.toolbar.actions: if tool_id == target_mode and self.viewer.toolbar.actions[tool_id].isCheckable(): assert self.viewer.toolbar.actions[tool_id].isChecked() self.viewer.toolbar._active == target_mode else: assert not self.viewer.toolbar.actions[tool_id].isChecked() def test_callback(self): self.viewer.toolbar.actions['mpl:home'].trigger() self.viewer.tool.release(None) assert self.viewer._called_back def test_change_mode(self): self.viewer.toolbar.actions['mpl:pan'].toggle() assert self.viewer.toolbar.active_tool.tool_id == 'mpl:pan' assert self.viewer._mpl_nav.mode == 'pan/zoom' self.viewer.toolbar.actions['mpl:pan'].toggle() assert self.viewer.toolbar.active_tool is None assert self.viewer._mpl_nav.mode == '' self.viewer.toolbar.actions['mpl:zoom'].trigger() assert self.viewer.toolbar.active_tool.tool_id == 'mpl:zoom' assert self.viewer._mpl_nav.mode == 'zoom rect' self.viewer.toolbar.actions['test'].trigger() assert self.viewer.toolbar.active_tool.tool_id == 'test' assert self.viewer._mpl_nav.mode == '' glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/tests/test_python_export.py0000644000175000017500000000232113657331477027332 0ustar noahfxnoahfximport os import sys import pytest import subprocess import numpy as np from matplotlib.testing.compare import compare_images __all__ = ['random_with_nan', 'BaseTestExportPython'] def random_with_nan(nsamples, nan_index): x = np.random.random(nsamples) x[nan_index] = np.nan return x class BaseTestExportPython: def assert_same(self, tmpdir, tol=0.1): os.chdir(tmpdir.strpath) expected = tmpdir.join('expected.png').strpath script = tmpdir.join('actual.py').strpath actual = tmpdir.join('glue_plot.png').strpath self.viewer.axes.figure.savefig(expected) self.viewer.export_as_script(script) subprocess.call([sys.executable, script]) msg = compare_images(expected, actual, tol=tol) if msg: from base64 import b64encode print("SCRIPT:") with open(script, 'r') as f: print(f.read()) print("EXPECTED:") with open(expected, 'rb') as f: print(b64encode(f.read()).decode()) print("ACTUAL:") with open(actual, 'rb') as f: print(b64encode(f.read()).decode()) pytest.fail(msg, pytrace=False) glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/tests/test_toolbar_mode.py0000644000175000017500000000251713507416115027050 0ustar noahfxnoahfxfrom glue.viewers.matplotlib.tests.test_mouse_mode import TestMouseMode, Event from ..toolbar_mode import ContrastMode class TestContrastMode(TestMouseMode): def mode_factory(self): return ContrastMode def test_move_ignored_if_not_right_drag(self): e = Event(1, 2, button=1) self.mode.move(e) count = self.mode._axes.figure.canvas.get_width_height.call_count assert count == 0 def test_clip_percentile(self): assert self.mode.get_clip_percentile() == (1, 99) self.mode.set_clip_percentile(2, 33) assert self.mode.get_clip_percentile() == (2, 33) def test_vmin_vmax(self): assert self.mode.get_vmin_vmax() == (None, None) self.mode.set_vmin_vmax(3, 4) assert self.mode.get_vmin_vmax() == (3, 4) assert self.mode.get_clip_percentile() == (None, None) # TODO: at the moment, this doesn't work because the dialog is non-modal # assert self.mode.get_vmin_vmax() == (5, 7) # def test_choose_vmin_vmax(self): # # assert self.mode.get_vmin_vmax() == (None, None) # # def fill_apply(dialog): # dialog.vmin.setText('5') # dialog.vmax.setText('7') # dialog.accept() # # with process_dialog(delay=500, function=fill_apply): # self.mode.choose_vmin_vmax() glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/tests/__init__.py0000644000175000017500000000000013502206677025070 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/tests/test_data_viewer.py0000644000175000017500000005450013752534424026701 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import pytest from numpy.testing import assert_allclose try: import objgraph except ImportError: OBJGRAPH_INSTALLED = False else: OBJGRAPH_INSTALLED = True from glue.core import Data from glue.core.exceptions import IncompatibleDataException from glue.app.qt.application import GlueApplication from glue.core.roi import XRangeROI from glue.utils.qt import process_events from glue.tests.helpers import requires_matplotlib_ge_22 class MatplotlibDrawCounter(object): def __init__(self, figure): self.figure = figure # For recent versions of Matplotlib it seems that we need # to process events at least twice to really flush out any # unprocessed events process_events() process_events() self.start = self.figure.canvas._draw_count @property def draw_count(self): process_events() process_events() return self.figure.canvas._draw_count - self.start class BaseTestMatplotlibDataViewer(object): """ Base class to test viewers based on MatplotlibDataViewer. This only runs a subset of tests that relate to functionality implemented in MatplotlibDataViewer and specific viewers are responsible for implementing a more complete test suite. Viewers based on this should inherit from this test class and define the following attributes: * ``data``: an instance of a data object that works by default in the viewer * ``viewer_cls``: the viewer class It is then safe to assume that ``data_collection``, ``viewer``, and ``hub`` are defined when writing tests. """ def setup_method(self, method): if OBJGRAPH_INSTALLED: self.viewer_count_start = self.viewer_count self.data = self.init_data() self.application = GlueApplication() self.session = self.application.session self.hub = self.session.hub self.data_collection = self.session.data_collection self.data_collection.append(self.data) self.viewer = self.viewer_cls(self.session) self.data_collection.register_to_hub(self.hub) self.viewer.register_to_hub(self.hub) def init_subset(self): cid = self.data.main_components[0] self.data_collection.new_subset_group('subset 1', cid > 0) @property def viewer_count(self): process_events() obj = objgraph.by_type(self.viewer_cls.__name__) return len(obj) def teardown_method(self, method): if self.viewer is not None: self.viewer.close() self.application.close() # The following is a check to make sure that once the viewer and # application have been closed, there are no leftover references to # the data viewer. This was introduced because there were previously # circular references that meant that viewer instances were not # properly garbage collected, which in turn meant they still reacted # in some cases to events. if OBJGRAPH_INSTALLED: self.viewer = None self.application = None if self.viewer_count > self.viewer_count_start: objgraph.show_backrefs(objgraph.by_type(self.viewer_cls.__name__)) raise ValueError("No net viewers should be created in tests") def test_add_data(self): # Add a dataset with no subsets and make sure the appropriate layer # state and layer artists are created self.viewer.add_data(self.data) assert len(self.viewer.layers) == 1 assert self.viewer.layers[0].layer is self.data assert len(self.viewer.state.layers) == 1 assert self.viewer.state.layers[0].layer is self.data def test_add_data_with_subset(self): # Make sure that if subsets are present in the data, they are added # automatically self.init_subset() self.viewer.add_data(self.data) assert len(self.viewer.layers) == 2 assert self.viewer.layers[0].layer is self.data assert self.viewer.layers[1].layer is self.data.subsets[0] assert len(self.viewer.state.layers) == 2 assert self.viewer.state.layers[0].layer is self.data assert self.viewer.state.layers[1].layer is self.data.subsets[0] def test_add_data_then_subset(self): # Make sure that if a subset is created in a dataset that has already # been added to a viewer, the subset gets added self.viewer.add_data(self.data) assert len(self.viewer.layers) == 1 assert self.viewer.layers[0].layer is self.data assert len(self.viewer.state.layers) == 1 assert self.viewer.state.layers[0].layer is self.data self.init_subset() assert len(self.viewer.layers) == 2 assert self.viewer.layers[0].layer is self.data assert self.viewer.layers[1].layer is self.data.subsets[0] assert len(self.viewer.state.layers) == 2 assert self.viewer.state.layers[0].layer is self.data assert self.viewer.state.layers[1].layer is self.data.subsets[0] def init_draw_count(self): self.mpl_counter = MatplotlibDrawCounter(self.viewer.axes.figure) @property def draw_count(self): return self.mpl_counter.draw_count def test_single_draw(self): # Make sure that the number of draws is kept to a minimum self.init_draw_count() self.init_subset() assert self.draw_count == 0 self.viewer.add_data(self.data) assert self.draw_count == 1 def test_update_subset(self): self.init_draw_count() # Check that updating a subset causes the plot to be updated self.init_subset() assert self.draw_count == 0 self.viewer.add_data(self.data) count_before = self.draw_count # Change the subset cid = self.data.main_components[0] self.data.subsets[0].subset_state = cid > 1 # Make sure the figure has been redrawn assert self.draw_count - count_before > 0 def test_double_add_ignored(self): self.viewer.add_data(self.data) assert len(self.viewer.state.layers) == 1 self.viewer.add_data(self.data) assert len(self.viewer.state.layers) == 1 def test_removing_data_removes_layer_state(self): # Removing data from data collection should remove data from viewer self.viewer.add_data(self.data) assert len(self.viewer.state.layers) == 1 self.data_collection.remove(self.data) assert len(self.viewer.state.layers) == 0 def test_removing_data_removes_subsets(self): # Removing data from data collection should remove subsets from viewer self.init_subset() self.viewer.add_data(self.data) assert len(self.viewer.state.layers) == 2 self.data_collection.remove(self.data) assert len(self.viewer.state.layers) == 0 def test_removing_subset_removes_layers(self): # Removing a layer artist removes the corresponding layer state. We need # to do this with a subset otherwise the viewer is closed self.init_subset() self.viewer.add_data(self.data) assert len(self.viewer.layers) == 2 assert len(self.viewer.state.layers) == 2 self.data_collection.remove_subset_group(self.data_collection.subset_groups[0]) assert len(self.viewer.layers) == 1 assert self.viewer.layers[0].layer is self.data assert len(self.viewer.state.layers) == 1 assert self.viewer.state.layers[0].layer is self.data def test_removing_layer_artist_removes_layer_state(self): # Removing a layer artist removes the corresponding layer state. We need # to do this with a subset otherwise the viewer is closed self.init_subset() self.viewer.add_data(self.data) assert len(self.viewer.layers) == 2 assert len(self.viewer.state.layers) == 2 # self.layers is a copy so we need to remove from the original list self.viewer._layer_artist_container.remove(self.viewer.layers[1]) assert len(self.viewer.layers) == 1 assert self.viewer.layers[0].layer is self.data assert len(self.viewer.state.layers) == 1 assert self.viewer.state.layers[0].layer is self.data def test_removing_layer_state_removes_layer_artist(self): # Removing a layer artist removes the corresponding layer state. We need # to do this with a subset otherwise the viewer is closed self.init_subset() self.viewer.add_data(self.data) assert len(self.viewer.layers) == 2 assert len(self.viewer.state.layers) == 2 # self.layers is a copy so we need to remove from the original list self.viewer.state.layers.pop(1) assert len(self.viewer.layers) == 1 assert self.viewer.layers[0].layer is self.data assert len(self.viewer.state.layers) == 1 assert self.viewer.state.layers[0].layer is self.data def test_new_subset_after_remove_data(self): # Once we remove a dataset, if we make a new subset, it will not be # added to the viewer self.init_subset() self.viewer.add_data(self.data) assert len(self.viewer.layers) == 2 assert len(self.viewer.state.layers) == 2 self.viewer.state.layers.pop(0) self.init_subset() # makes a new subset assert len(self.data.subsets) == 2 assert len(self.viewer.layers) == 1 assert self.viewer.layers[0].layer is self.data.subsets[0] assert len(self.viewer.state.layers) == 1 assert self.viewer.state.layers[0].layer is self.data.subsets[0] def test_remove_not_present_ignored(self): data = Data(label='not in viewer') self.viewer.remove_data(data) def test_limits_sync(self): viewer_state = self.viewer.state axes = self.viewer.axes if axes.get_adjustable() == 'datalim': pytest.xfail() # Make sure that the viewer state and matplotlib viewer limits and log # settings are in sync. We start by modifying the state and making sure # that the axes follow. viewer_state.x_min = 3 viewer_state.x_max = 9 viewer_state.y_min = -2 viewer_state.y_max = 3 assert axes.get_xlim() == (3, 9) assert axes.get_ylim() == (-2, 3) assert axes.get_xscale() == 'linear' assert axes.get_yscale() == 'linear' viewer_state.x_log = True assert axes.get_xlim() == (3, 9) assert axes.get_ylim() == (-2, 3) assert axes.get_xscale() == 'log' assert axes.get_yscale() == 'linear' viewer_state.y_log = True # FIXME: the limits for y don't seem right, should be adjusted because of log? assert axes.get_xlim() == (3, 9) assert axes.get_ylim() == (-2, 3) assert axes.get_xscale() == 'log' assert axes.get_yscale() == 'log' # Check that changing the axes changes the state # NOTE: at the moment this doesn't work because Matplotlib doesn't # emit events for changing xscale/yscale. This isn't crucial anyway for # glue, but leaving the tests below in case this is fixed some day. The # Matplotlib issue is https://github.com/matplotlib/matplotlib/issues/8439 # axes.set_xscale('linear') # # assert viewer_state.x_min == 3 # assert viewer_state.x_max == 9 # assert viewer_state.y_min == -2 # assert viewer_state.y_max == 3 # assert not viewer_state.x_log # assert viewer_state.y_log # # axes.set_yscale('linear') # # assert viewer_state.x_min == 3 # assert viewer_state.x_max == 9 # assert viewer_state.y_min == -2 # assert viewer_state.y_max == 3 # assert not viewer_state.x_log # assert not viewer_state.y_log viewer_state.x_log = False viewer_state.y_log = False axes.set_xlim(-1, 4) assert viewer_state.x_min == -1 assert viewer_state.x_max == 4 assert viewer_state.y_min == -2 assert viewer_state.y_max == 3 # assert not viewer_state.x_log # assert not viewer_state.y_log axes.set_ylim(5, 6) assert viewer_state.x_min == -1 assert viewer_state.x_max == 4 assert viewer_state.y_min == 5 assert viewer_state.y_max == 6 # assert not viewer_state.x_log # assert not viewer_state.y_log # TODO: the following test should deal gracefully with the fact that # some viewers will want to show a Qt error for IncompatibleDataException def test_add_invalid_data(self): data2 = Data() with pytest.raises(IncompatibleDataException): self.viewer.add_data(data2) # Communication tests def test_ignore_data_add_message(self): self.data_collection.append(self.data) assert len(self.viewer.layers) == 0 def test_update_data_ignored_if_data_not_present(self): self.init_draw_count() self.data_collection.append(self.data) ct0 = self.draw_count self.data.style.color = 'blue' assert self.draw_count == ct0 def test_update_data_processed_if_data_present(self): self.init_draw_count() self.data_collection.append(self.data) self.viewer.add_data(self.data) ct0 = self.draw_count self.data.style.color = 'blue' assert self.draw_count > ct0 def test_add_subset_ignored_if_data_not_present(self): self.data_collection.append(self.data) sub = self.data.new_subset() assert sub not in self.viewer._layer_artist_container def test_add_subset_processed_if_data_present(self): self.data_collection.append(self.data) self.viewer.add_data(self.data) sub = self.data.new_subset() assert sub in self.viewer._layer_artist_container def test_update_subset_ignored_if_not_present(self): # This can be quite a difficult test to pass because it makes sure that # there are absolutely no references to the layer state left over once # a subset is removed - when originally written this identified quite # a few places where references were being accidentally kept, and # resulted in weakref being needed in a number of places. But ultimately # this test should pass! No cheating :) self.init_draw_count() self.data_collection.append(self.data) self.viewer.add_data(self.data) sub = self.data.new_subset() self.viewer.remove_subset(sub) ct0 = self.draw_count sub.style.color = 'blue' assert self.draw_count == ct0 def test_update_subset_processed_if_present(self): self.init_draw_count() self.data_collection.append(self.data) self.viewer.add_data(self.data) sub = self.data.new_subset() ct0 = self.draw_count sub.style.color = 'blue' assert self.draw_count > ct0 def test_data_remove_message(self): self.data_collection.append(self.data) self.viewer.add_data(self.data) self.data_collection.remove(self.data) assert self.data not in self.viewer._layer_artist_container def test_subset_remove_message(self): self.data_collection.append(self.data) self.viewer.add_data(self.data) sub = self.data.new_subset() assert sub in self.viewer._layer_artist_container sub.delete() assert sub not in self.viewer._layer_artist_container def test_session_round_trip(self, tmpdir): self.init_subset() ga = GlueApplication(self.data_collection) ga.show() viewer = ga.new_data_viewer(self.viewer_cls) viewer.add_data(self.data) session_file = tmpdir.join('test_session_round_trip.glu').strpath ga.save_session(session_file) ga.close() ga2 = GlueApplication.restore_session(session_file) ga2.show() viewer2 = ga2.viewers[0][0] data2 = ga2.data_collection[0] assert viewer2.layers[0].layer is data2 assert viewer2.layers[1].layer is data2.subsets[0] ga2.close() def test_apply_roi_undo(self): self.data_collection.append(self.data) self.viewer.add_data(self.data) roi = XRangeROI(1, 2) self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 lo1 = self.data.subsets[0].subset_state.lo hi1 = self.data.subsets[0].subset_state.hi roi = XRangeROI(0, 3) self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 lo2 = self.data.subsets[0].subset_state.lo hi2 = self.data.subsets[0].subset_state.hi assert lo2 != lo1 assert hi2 != hi1 self.application.undo() assert len(self.data.subsets) == 1 assert self.data.subsets[0].subset_state.lo == lo1 assert self.data.subsets[0].subset_state.hi == hi1 self.application.redo() assert len(self.data.subsets) == 1 assert self.data.subsets[0].subset_state.lo == lo2 assert self.data.subsets[0].subset_state.hi == hi2 def test_numerical_data_changed(self): self.init_draw_count() self.init_subset() assert self.draw_count == 0 self.viewer.add_data(self.data) assert self.draw_count == 1 data = Data(label=self.data.label) data.coords = self.data.coords for cid in self.data.main_components: if self.data.get_kind(cid) == 'numerical': data.add_component(self.data[cid] * 2, cid.label) else: data.add_component(self.data[cid], cid.label) self.data.update_values_from_data(data) assert self.draw_count == 2 @requires_matplotlib_ge_22 def test_aspect_resize(self): # Make sure that the limits are adjusted appropriately when resizing # depending on the aspect ratio mode. Note that we don't add any data # here since it isn't needed for this test. # This test works with Matplotlib 2.0 and 2.2 but not 2.1, hence we # skip it with Matplotlib 2.1 above. # Note that we need to explicitly call draw() below because otherwise # draw_idle is used, which has no guarantee of being effective. # Set initial limits to deterministic values self.viewer.state.aspect = 'auto' self.viewer.state.x_min = 0. self.viewer.state.x_max = 1. self.viewer.state.y_min = 0. self.viewer.state.y_max = 1. self.viewer.state.aspect = 'equal' # Resize events only work if widget is visible self.viewer.show() self.viewer.figure.canvas.draw() process_events(wait=0.1) def limits(viewer): return (viewer.state.x_min, viewer.state.x_max, viewer.state.y_min, viewer.state.y_max) # Set viewer to an initial size and save limits self.viewer.viewer_size = (800, 400) self.viewer.figure.canvas.draw() process_events(wait=0.1) initial_limits = limits(self.viewer) # Change the viewer size, and make sure the limits are adjusted self.viewer.viewer_size = (400, 400) self.viewer.figure.canvas.draw() process_events(wait=0.1) with pytest.raises(AssertionError): assert_allclose(limits(self.viewer), initial_limits) # Now change the viewer size a number of times and make sure if we # return to the original size, the limits match the initial ones. self.viewer.viewer_size = (350, 800) self.viewer.figure.canvas.draw() process_events(wait=0.1) self.viewer.viewer_size = (900, 300) self.viewer.figure.canvas.draw() process_events(wait=0.1) self.viewer.viewer_size = (600, 600) self.viewer.figure.canvas.draw() process_events(wait=0.1) self.viewer.viewer_size = (800, 400) self.viewer.figure.canvas.draw() process_events(wait=0.1) assert_allclose(limits(self.viewer), initial_limits) # Now check that the limits don't change in 'auto' mode self.viewer.state.aspect = 'auto' self.viewer.viewer_size = (900, 300) self.viewer.figure.canvas.draw() process_events(wait=0.1) assert_allclose(limits(self.viewer), initial_limits) def test_update_data_values(self): # Regression test for a bug that caused some viewers to not behave # correctly if the data values were updated. self.viewer.add_data(self.data) data = self.init_data() self.data_collection.append(data) self.data.update_values_from_data(data) def test_legend(self): viewer_state = self.viewer.state self.viewer.add_data(self.data) # no legend by default assert self.viewer.axes.get_legend() is None self.viewer.state.legend.visible = True # a legend appears legend = self.viewer.axes.get_legend() assert not (legend is None) handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 1 assert labels[0] == self.data.label self.init_subset() assert len(viewer_state.layers) == 2 handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 2 assert labels[1] == 'subset 1' # The next set of test check that the legend does not create extra draws ! def test_legend_single_draw(self): # Make sure that the number of draws is kept to a minimum self.viewer.state.legend.visible = True self.init_draw_count() self.init_subset() assert self.draw_count == 0 self.viewer.add_data(self.data) assert self.draw_count == 1 def test_legend_numerical_data_changed(self): self.viewer.state.legend.visible = True self.init_draw_count() self.init_subset() assert self.draw_count == 0 self.viewer.add_data(self.data) assert self.draw_count == 1 data = Data(label=self.data.label) data.coords = self.data.coords for cid in self.data.main_components: if self.data.get_kind(cid) == 'numerical': data.add_component(self.data[cid] * 2, cid.label) else: data.add_component(self.data[cid], cid.label) self.data.update_values_from_data(data) assert self.draw_count == 2 glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/legend_editor.py0000755000175000017500000000056313661230662025011 0ustar noahfxnoahfximport os from qtpy import QtWidgets from glue.utils.qt import load_ui __all__ = ['LegendEditorWidget'] class LegendEditorWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(LegendEditorWidget, self).__init__(parent=parent) self.ui = load_ui('legend_editor.ui', self, directory=os.path.dirname(__file__)) glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/toolbar.py0000644000175000017500000000524313752534424023650 0ustar noahfxnoahfxfrom matplotlib.backends.backend_qt5 import NavigationToolbar2QT from glue.config import viewer_tool from glue.viewers.common.tool import CheckableTool, Tool __all__ = ['MatplotlibTool', 'MatplotlibCheckableTool', 'HomeTool', 'SaveTool', 'PanTool', 'ZoomTool'] def _ensure_mpl_nav(viewer): # Set up virtual Matplotlib navigation toolbar (don't show it) if not hasattr(viewer, '_mpl_nav'): viewer._mpl_nav = NavigationToolbar2QT(viewer.central_widget.canvas, viewer) viewer._mpl_nav.hide() def _cleanup_mpl_nav(viewer): if getattr(viewer, '_mpl_nav', None) is not None: viewer._mpl_nav.setParent(None) try: viewer._mpl_nav.parent = None except AttributeError: pass viewer._mpl_nav = None class MatplotlibTool(Tool): def __init__(self, viewer=None): super(MatplotlibTool, self).__init__(viewer=viewer) _ensure_mpl_nav(viewer) def close(self): _cleanup_mpl_nav(self.viewer) super(MatplotlibTool, self).close() class MatplotlibCheckableTool(CheckableTool): def __init__(self, viewer=None): super(MatplotlibCheckableTool, self).__init__(viewer=viewer) _ensure_mpl_nav(viewer) def close(self): _cleanup_mpl_nav(self.viewer) super(MatplotlibCheckableTool, self).close() @viewer_tool class HomeTool(MatplotlibTool): tool_id = 'mpl:home' icon = 'glue_home' action_text = 'Home' tool_tip = 'Reset original zoom' shortcut = 'H' def activate(self): if hasattr(self.viewer, 'state') and hasattr(self.viewer.state, 'reset_limits'): self.viewer.state.reset_limits() else: self.viewer._mpl_nav.home() @viewer_tool class SaveTool(MatplotlibTool): tool_id = 'mpl:save' icon = 'glue_filesave' action_text = 'Save plot to file' tool_tip = 'Save the figure' def activate(self): self.viewer._mpl_nav.save_figure() @viewer_tool class PanTool(MatplotlibCheckableTool): tool_id = 'mpl:pan' icon = 'glue_move' action_text = 'Pan' tool_tip = 'Pan axes with left mouse, zoom with right' shortcut = 'M' def activate(self): self.viewer._mpl_nav.pan() def deactivate(self): if hasattr(self.viewer, '_mpl_nav'): self.viewer._mpl_nav.pan() @viewer_tool class ZoomTool(MatplotlibCheckableTool): tool_id = 'mpl:zoom' icon = 'glue_zoom_to_rect' action_text = 'Zoom' tool_tip = 'Zoom to rectangle' shortcut = 'Z' def activate(self): self.viewer._mpl_nav.zoom() def deactivate(self): if hasattr(self.viewer, '_mpl_nav'): self.viewer._mpl_nav.zoom() glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/data_viewer.py0000644000175000017500000000554213752534424024502 0ustar noahfxnoahfxfrom qtpy.QtCore import QTimer from glue.core.message import ComputationStartedMessage from glue.viewers.common.qt.data_viewer import DataViewer from glue.viewers.matplotlib.qt.widget import MplWidget from glue.viewers.matplotlib.mpl_axes import init_mpl from glue.utils import defer_draw, decorate_all_methods from glue.viewers.matplotlib.state import MatplotlibDataViewerState from glue.viewers.matplotlib.viewer import MatplotlibViewerMixin # The following import is required to register the viewer tools from glue.viewers.matplotlib.qt import toolbar # noqa __all__ = ['MatplotlibDataViewer'] @decorate_all_methods(defer_draw) class MatplotlibDataViewer(MatplotlibViewerMixin, DataViewer): _state_cls = MatplotlibDataViewerState tools = ['mpl:home', 'mpl:pan', 'mpl:zoom'] subtools = {'save': ['mpl:save']} def __init__(self, session, parent=None, wcs=None, state=None, projection=None): super(MatplotlibDataViewer, self).__init__(session, parent=parent, state=state) # Use MplWidget to set up a Matplotlib canvas inside the Qt window self.mpl_widget = MplWidget() self.setCentralWidget(self.mpl_widget) # TODO: shouldn't have to do this self.central_widget = self.mpl_widget self.figure, self.axes = init_mpl(self.mpl_widget.canvas.fig, wcs=wcs, projection=projection) MatplotlibViewerMixin.setup_callbacks(self) self.central_widget.resize(600, 400) self.resize(self.central_widget.size()) self._monitor_computation = QTimer() self._monitor_computation.setInterval(500) self._monitor_computation.timeout.connect(self._update_computation) def _update_computation(self, message=None): # If we get a ComputationStartedMessage and the timer isn't currently # active, then we start the timer but we then return straight away. # This is to avoid showing the 'Computing' message straight away in the # case of reasonably fast operations. if isinstance(message, ComputationStartedMessage): if not self._monitor_computation.isActive(): self._monitor_computation.start() return for layer_artist in self.layers: if layer_artist.is_computing: self.loading_rectangle.set_visible(True) text = self.loading_text.get_text() if text.count('.') > 2: text = 'Computing' else: text += '.' self.loading_text.set_text(text) self.loading_text.set_visible(True) self.redraw() return self.loading_rectangle.set_visible(False) self.loading_text.set_visible(False) self.redraw() # If we get here, the computation has stopped so we can stop the timer self._monitor_computation.stop() glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/compute_worker.py0000644000175000017500000000322713605357235025253 0ustar noahfxnoahfximport sys import time import queue from glue.utils import queue_to_list from qtpy.QtCore import Signal, QThread # For some viewers, we make use of a thread that continuously listens for # requests to update the profile and we run these as needed. In future, # we should add the ability to interrupt compute jobs if a newer compute # job is requested. class ComputeWorker(QThread): compute_start = Signal() compute_end = Signal() compute_error = Signal(object) def __init__(self, function): super(ComputeWorker, self).__init__() self.function = function self.running = False self.work_queue = queue.Queue() def run(self): error = None while True: time.sleep(1 / 25) msgs = queue_to_list(self.work_queue) if 'stop' in msgs: return elif len(msgs) == 0: # We change this here rather than in the try...except below # to avoid stopping and starting in quick succession. if self.running: self.running = False if error is None: self.compute_end.emit() else: self.compute_error.emit(error) error = None continue # If any resets were requested, honor this reset = any(msgs) try: self.running = True self.compute_start.emit() self.function(reset=reset) except Exception: error = sys.exc_info() else: error = None glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/toolbar_mode.py0000644000175000017500000001356613605357235024663 0ustar noahfxnoahfximport os from qtpy import QtGui, QtWidgets from glue.utils import nonpartial from glue.utils.qt import load_ui, cmap2pixmap from glue.viewers.common.tool import Tool from glue.config import viewer_tool from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase __all__ = ['ContrastMode', 'ColormapMode'] @viewer_tool class ContrastMode(ToolbarModeBase): """ Uses right mouse button drags to set bias and contrast, DS9-style. The horizontal position of the mouse sets the bias, the vertical position sets the contrast. The move_callback defaults to calling _set_norm on the viewer with the instance of ConstrastMode as the sole argument. """ icon = 'glue_contrast' tool_id = 'image:contrast' action_text = 'Contrast' tool_tip = 'Adjust the bias/contrast' shortcut = 'B' def __init__(self, viewer, **kwargs): super(ContrastMode, self).__init__(viewer, **kwargs) self.bias = 0.5 self.contrast = 1.0 self._last = None self._result = None self._percent_lo = 1. self._percent_hi = 99. self.stretch = 'linear' self._vmin = None self._vmax = None def set_clip_percentile(self, lo, hi): """Percentiles at which to clip the data at black/white""" if lo == self._percent_lo and hi == self._percent_hi: return self._percent_lo = lo self._percent_hi = hi self._vmin = None self._vmax = None def get_clip_percentile(self): if self._vmin is None and self._vmax is None: return self._percent_lo, self._percent_hi return None, None def get_vmin_vmax(self): if self._percent_lo is None or self._percent_hi is None: return self._vmin, self._vmax return None, None def set_vmin_vmax(self, vmin, vmax): if vmin == self._vmin and vmax == self._vmax: return self._percent_hi = self._percent_lo = None self._vmin = vmin self._vmax = vmax def choose_vmin_vmax(self): dialog = load_ui('contrastlimits.ui', None, directory=os.path.dirname(__file__)) v = QtGui.QDoubleValidator() dialog.vmin.setValidator(v) dialog.vmax.setValidator(v) vmin, vmax = self.get_vmin_vmax() if vmin is not None: dialog.vmin.setText(str(vmin)) if vmax is not None: dialog.vmax.setText(str(vmax)) def _apply(): try: vmin = float(dialog.vmin.text()) vmax = float(dialog.vmax.text()) self.set_vmin_vmax(vmin, vmax) if self._move_callback is not None: self._move_callback(self) except ValueError: pass bb = dialog.buttonBox bb.button(bb.Apply).clicked.connect(_apply) dialog.accepted.connect(_apply) dialog.show() dialog.raise_() dialog.exec_() def move(self, event): """ MoveEvent. Update bias and contrast on Right Mouse button drag """ if event.button != 3: # RMB drag only return x, y = event.x, event.y dx, dy = self._axes.figure.canvas.get_width_height() x = 1.0 * x / dx y = 1.0 * y / dy self.bias = x self.contrast = (1 - y) * 10 super(ContrastMode, self).move(event) def menu_actions(self): result = [] a = QtWidgets.QAction("minmax", None) a.triggered.connect(nonpartial(self.set_clip_percentile, 0, 100)) result.append(a) a = QtWidgets.QAction("99%", None) a.triggered.connect(nonpartial(self.set_clip_percentile, 1, 99)) result.append(a) a = QtWidgets.QAction("95%", None) a.triggered.connect(nonpartial(self.set_clip_percentile, 5, 95)) result.append(a) a = QtWidgets.QAction("90%", None) a.triggered.connect(nonpartial(self.set_clip_percentile, 10, 90)) result.append(a) rng = QtWidgets.QAction("Set range...", None) rng.triggered.connect(nonpartial(self.choose_vmin_vmax)) result.append(rng) a = QtWidgets.QAction("", None) a.setSeparator(True) result.append(a) a = QtWidgets.QAction("linear", None) a.triggered.connect(nonpartial(setattr, self, 'stretch', 'linear')) result.append(a) a = QtWidgets.QAction("log", None) a.triggered.connect(nonpartial(setattr, self, 'stretch', 'log')) result.append(a) a = QtWidgets.QAction("sqrt", None) a.triggered.connect(nonpartial(setattr, self, 'stretch', 'sqrt')) result.append(a) a = QtWidgets.QAction("asinh", None) a.triggered.connect(nonpartial(setattr, self, 'stretch', 'arcsinh')) result.append(a) for r in result: if r is rng: continue if self._move_callback is not None: r.triggered.connect(nonpartial(self._move_callback, self)) return result class ColormapAction(QtWidgets.QAction): def __init__(self, label, cmap, parent): super(ColormapAction, self).__init__(label, parent) self.cmap = cmap pm = cmap2pixmap(cmap) self.setIcon(QtGui.QIcon(pm)) @viewer_tool class ColormapMode(Tool): """ A tool to change the colormap used in a viewer. This calls a ``set_cmap`` method on the viewer, which should take the name of the colormap as the sole argument. """ icon = 'glue_rainbow' tool_id = 'image:colormap' action_text = 'Set color scale' tool_tip = 'Set color scale' def menu_actions(self): from glue import config acts = [] for label, cmap in config.colormaps: a = ColormapAction(label, cmap, self.viewer) a.triggered.connect(nonpartial(self.viewer.set_cmap, cmap)) acts.append(a) return acts glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/__init__.py0000644000175000017500000000004313507416115023730 0ustar noahfxnoahfxfrom . import toolbar_mode # noqa glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/axes_editor.ui0000644000175000017500000001276713661230662024506 0ustar noahfxnoahfx Form 0 0 272 286 Form 5 5 5 5 5 y label: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 70 0 x label: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter tick label size Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 70 0 axis label size Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter axis label weight Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 50 false x Qt::AlignCenter Qt::Horizontal 40 20 Apply to all plots Qt::Horizontal 40 20 Qt::Vertical 20 0 50 false y Qt::AlignCenter glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/widget.py0000755000175000017500000001224113605357235023470 0ustar noahfxnoahfx#!/usr/bin/env python import warnings import matplotlib from matplotlib.figure import Figure from qtpy import QtCore, QtGui, QtWidgets from qtpy.QtCore import Qt from glue.config import settings from glue.utils.matplotlib import DEFER_DRAW_BACKENDS from matplotlib.backends.backend_qt5 import FigureManagerQT from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg __all__ = ['MplCanvas', 'MplWidget'] # Register the Qt backend with defer_draw DEFER_DRAW_BACKENDS.append(FigureCanvasQTAgg) # We want to ignore warnings about left==right and bottom==top since these are # not critical and the default behavior makes sense. warnings.filterwarnings('ignore', '.*Attempting to set identical left==right', UserWarning) warnings.filterwarnings('ignore', '.*Attempting to set identical bottom==top', UserWarning) class MplCanvas(FigureCanvasQTAgg): """Class to represent the FigureCanvas widget""" rightDrag = QtCore.Signal(float, float) leftDrag = QtCore.Signal(float, float) homeButton = QtCore.Signal() resize_begin = QtCore.Signal() resize_end = QtCore.Signal() def __init__(self): self._draw_count = 0 interactive = matplotlib.is_interactive() matplotlib.interactive(False) self.roi_callback = None self._draw_zoom_rect = None self.fig = Figure(facecolor=settings.BACKGROUND_COLOR) FigureCanvasQTAgg.__init__(self, self.fig) FigureCanvasQTAgg.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) FigureCanvasQTAgg.updateGeometry(self) self.manager = FigureManagerQT(self, 0) matplotlib.interactive(interactive) self._resize_timer = QtCore.QTimer() self._resize_timer.setInterval(250) self._resize_timer.setSingleShot(True) self._resize_timer.timeout.connect(self._on_timeout) self.renderer = None def _on_timeout(self): buttons = QtWidgets.QApplication.instance().mouseButtons() if buttons != Qt.NoButton: self._resize_timer.start() else: self.resize_end.emit() def paintEvent(self, event): # super needs this if self.renderer is None: self.renderer = self.get_renderer() super(MplCanvas, self).paintEvent(event) # See drawRectangle for what _draw_zoom_rect is if self._draw_zoom_rect is not None: painter = QtGui.QPainter(self) self._draw_zoom_rect(painter) painter.end() if self.roi_callback is not None: self.roi_callback(self) def drawRectangle(self, rect): # The default zoom rectangle in Matplotlib is quite faint, and there is # no easy mechanism for changing the default appearance. However, the # drawing of the zoom rectangle is done in the public method # drawRectangle on the canvas. This method sets up a callback function # that is then called during paintEvent. However, we shouldn't use the # same private attribute since this might break, so we use a private # attribute with a different name, which means the one matplotlib uses # will remain empty and not plot anything. if rect is None: _draw_zoom_rect = None else: def _draw_zoom_rect(painter): pen = QtGui.QPen(QtGui.QPen(Qt.red, 2, Qt.DotLine)) painter.setPen(pen) try: dpi_ratio = self.devicePixelRatio() or 1 except AttributeError: # Matplotlib <2 dpi_ratio = 1 painter.drawRect(*(pt / dpi_ratio for pt in rect)) # This function will be called at the end of the paintEvent self._draw_zoom_rect = _draw_zoom_rect # We need to call update to force the canvas to be painted again self.update() def resizeEvent(self, event): if not self._resize_timer.isActive(): self.resize_begin.emit() self._resize_timer.start() super(MplCanvas, self).resizeEvent(event) def draw(self, *args, **kwargs): self._draw_count += 1 return super(MplCanvas, self).draw(*args, **kwargs) def keyPressEvent(self, event): event.setAccepted(False) super(MplCanvas, self).keyPressEvent(event) class MplWidget(QtWidgets.QWidget): """Widget defined in Qt Designer""" # signals rightDrag = QtCore.Signal(float, float) leftDrag = QtCore.Signal(float, float) def __init__(self, parent=None): # initialization of Qt MainWindow widget QtWidgets.QWidget.__init__(self, parent) # set the canvas to the Matplotlib widget self.canvas = MplCanvas() # create a vertical box layout self.vbl = QtWidgets.QVBoxLayout() self.vbl.setContentsMargins(0, 0, 0, 0) self.vbl.setSpacing(0) # add mpl widget to the vertical box self.vbl.addWidget(self.canvas) # set the layout to the vertical box self.setLayout(self.vbl) self.canvas.rightDrag.connect(self.rightDrag) self.canvas.leftDrag.connect(self.leftDrag) glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/axes_editor.py0000644000175000017500000000055313605357235024513 0ustar noahfxnoahfximport os from qtpy import QtWidgets from glue.utils.qt import load_ui __all__ = ['AxesEditorWidget'] class AxesEditorWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(AxesEditorWidget, self).__init__(parent=parent) self.ui = load_ui('axes_editor.ui', self, directory=os.path.dirname(__file__)) glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/qt/legend_editor.ui0000644000175000017500000001134413752534424024776 0ustar noahfxnoahfx Form 0 0 272 286 Form Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing 5 5 5 5 0 0 enable Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter location Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter opacity Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 0 0 title Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter label size Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter box color 0 0 text color box edge QColorBox QLabel
glue.utils.qt.colors
glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/state.py0000644000175000017500000002603313752534424022702 0ustar noahfxnoahfxfrom echo import CallbackProperty, SelectionCallbackProperty, keep_in_sync, delay_callback from matplotlib.colors import to_rgba from glue.core.message import LayerArtistUpdatedMessage from glue.core.state_objects import State from glue.viewers.common.state import ViewerState, LayerState from glue.utils import defer_draw, avoid_circular __all__ = ['DeferredDrawSelectionCallbackProperty', 'DeferredDrawCallbackProperty', 'MatplotlibDataViewerState', 'MatplotlibLayerState'] class DeferredDrawCallbackProperty(CallbackProperty): """ A callback property where drawing is deferred until after notify has called all callback functions. """ @defer_draw def notify(self, *args, **kwargs): super(DeferredDrawCallbackProperty, self).notify(*args, **kwargs) class DeferredDrawSelectionCallbackProperty(SelectionCallbackProperty): """ A callback property where drawing is deferred until after notify has called all callback functions. """ @defer_draw def notify(self, *args, **kwargs): super(DeferredDrawSelectionCallbackProperty, self).notify(*args, **kwargs) VALID_WEIGHTS = ['light', 'normal', 'medium', 'semibold', 'bold', 'heavy', 'black'] VALID_LOCATIONS = ['draggable', 'best', 'upper right', 'upper left', 'lower left', 'lower right', 'center left', 'center right', 'lower center', 'upper center'] class MatplotlibLegendState(State): """The legend state""" visible = DeferredDrawCallbackProperty(False, docstring="Whether to show the legend") location = DeferredDrawSelectionCallbackProperty(0, docstring="The location of the legend in the axis") title = DeferredDrawCallbackProperty("", docstring='The title of the legend') fontsize = DeferredDrawCallbackProperty(10, docstring='The font size of the title') alpha = DeferredDrawCallbackProperty(0.6, docstring='Transparency of the legend frame') frame_color = DeferredDrawCallbackProperty("#ffffff", docstring='Frame color of the legend') show_edge = DeferredDrawCallbackProperty(True, docstring="Whether to show the edge of the frame ") text_color = DeferredDrawCallbackProperty("#000000", docstring='Text color of the legend') def __init__(self, *args, **kwargs): MatplotlibLegendState.location.set_choices(self, VALID_LOCATIONS) super().__init__(*args, **kwargs) self._set_color_choices() def _set_color_choices(self): from glue.config import settings self.frame_color = settings.BACKGROUND_COLOR self.text_color = settings.FOREGROUND_COLOR @property def edge_color(self): if self.show_edge: return to_rgba(self.text_color, self.alpha) else: return None @property def draggable(self): return self.location == 'draggable' @property def mpl_location(self): if self.location == 'draggable': return 'best' else: return self.location def update_axes_settings_from(self, state): self.visible = state.show_legend self.loc_and_drag = state.loc_and_drag self.alpha = state.alpha self.title = state.title self.fontsize = state.fontsize self.frame_color = state.frame_color self.show_edge = state.show_edge self.text_color = state.text_color class MatplotlibDataViewerState(ViewerState): """ A base class that includes common attributes for viewers based on Matplotlib. """ x_min = DeferredDrawCallbackProperty(docstring='Lower limit of the visible x range') x_max = DeferredDrawCallbackProperty(docstring='Upper limit of the visible x range') y_min = DeferredDrawCallbackProperty(docstring='Lower limit of the visible y range') y_max = DeferredDrawCallbackProperty(docstring='Upper limit of the visible y range') x_log = DeferredDrawCallbackProperty(False, docstring='Whether the x axis is logarithmic') y_log = DeferredDrawCallbackProperty(False, docstring='Whether the y axis is logarithmic') aspect = DeferredDrawCallbackProperty('auto', docstring='Aspect ratio for the axes') show_axes = DeferredDrawCallbackProperty(True, docstring='Whether the axes are shown') x_axislabel = DeferredDrawCallbackProperty('', docstring='Label for the x-axis') y_axislabel = DeferredDrawCallbackProperty('', docstring='Label for the y-axis') x_axislabel_size = DeferredDrawCallbackProperty(10, docstring='Size of the x-axis label') y_axislabel_size = DeferredDrawCallbackProperty(10, docstring='Size of the y-axis label') x_axislabel_weight = DeferredDrawSelectionCallbackProperty(1, docstring='Weight of the x-axis label') y_axislabel_weight = DeferredDrawSelectionCallbackProperty(1, docstring='Weight of the y-axis label') x_ticklabel_size = DeferredDrawCallbackProperty(8, docstring='Size of the x-axis tick labels') y_ticklabel_size = DeferredDrawCallbackProperty(8, docstring='Size of the y-axis tick labels') def __init__(self, *args, **kwargs): self._axes_aspect_ratio = None MatplotlibDataViewerState.x_axislabel_weight.set_choices(self, VALID_WEIGHTS) MatplotlibDataViewerState.y_axislabel_weight.set_choices(self, VALID_WEIGHTS) super(MatplotlibDataViewerState, self).__init__(*args, **kwargs) self.legend = MatplotlibLegendState(*args, **kwargs) self.add_callback('aspect', self._adjust_limits_aspect, priority=10000) self.add_callback('x_min', self._adjust_limits_aspect_x, priority=10000) self.add_callback('x_max', self._adjust_limits_aspect_x, priority=10000) self.add_callback('y_min', self._adjust_limits_aspect_y, priority=10000) self.add_callback('y_max', self._adjust_limits_aspect_y, priority=10000) def _set_axes_aspect_ratio(self, value): """ Set the aspect ratio of the axes in which the visualization is shown. This is a private method that is intended only for internal use, and it allows this viewer state class to adjust the limits accordingly when the aspect callback property is set to 'equal' """ self._axes_aspect_ratio = value self._adjust_limits_aspect(aspect_adjustable='both') def _adjust_limits_aspect_x(self, *args): self._adjust_limits_aspect(aspect_adjustable='y') def _adjust_limits_aspect_y(self, *args): self._adjust_limits_aspect(aspect_adjustable='x') @avoid_circular def _adjust_limits_aspect(self, *args, **kwargs): """ Adjust the limits of the visualization to take into account the aspect ratio. This only works if `_set_axes_aspect_ratio` has been called previously. """ if self.aspect == 'auto' or self._axes_aspect_ratio is None: return if self.x_min is None or self.x_max is None or self.y_min is None or self.y_max is None: return aspect_adjustable = kwargs.pop('aspect_adjustable', 'auto') changed = None # Find axes aspect ratio axes_ratio = self._axes_aspect_ratio # Put the limits in temporary variables so that we only actually change # them in one go at the end. x_min, x_max = self.x_min, self.x_max y_min, y_max = self.y_min, self.y_max # Find current data ratio data_ratio = abs(y_max - y_min) / abs(x_max - x_min) # Only do something if the data ratio is sufficiently different # from the axes ratio. if abs(data_ratio - axes_ratio) / (0.5 * (data_ratio + axes_ratio)) > 0.01: # We now adjust the limits - which ones we adjust depends on # the adjust keyword. We also make sure we preserve the # mid-point of the current coordinates. if aspect_adjustable == 'both': # We need to adjust both at the same time x_mid = 0.5 * (x_min + x_max) x_width = abs(x_max - x_min) * (data_ratio / axes_ratio) ** 0.5 y_mid = 0.5 * (y_min + y_max) y_width = abs(y_max - y_min) / (data_ratio / axes_ratio) ** 0.5 x_min = x_mid - x_width / 2. x_max = x_mid + x_width / 2. y_min = y_mid - y_width / 2. y_max = y_mid + y_width / 2. elif (aspect_adjustable == 'auto' and data_ratio > axes_ratio) or aspect_adjustable == 'x': x_mid = 0.5 * (x_min + x_max) x_width = abs(y_max - y_min) / axes_ratio x_min = x_mid - x_width / 2. x_max = x_mid + x_width / 2. else: y_mid = 0.5 * (y_min + y_max) y_width = abs(x_max - x_min) * axes_ratio y_min = y_mid - y_width / 2. y_max = y_mid + y_width / 2. with delay_callback(self, 'x_min', 'x_max', 'y_min', 'y_max'): self.x_min = x_min self.x_max = x_max self.y_min = y_min self.y_max = y_max def update_axes_settings_from(self, state): # axis self.x_axislabel_size = state.x_axislabel_size self.y_axislabel_size = state.y_axislabel_size self.x_axislabel_weight = state.x_axislabel_weight self.y_axislabel_weight = state.y_axislabel_weight self.x_ticklabel_size = state.x_ticklabel_size self.y_ticklabel_size = state.y_ticklabel_size # legend self.legend.update_axes_settings_from(state.legend) @defer_draw def _notify_global(self, *args, **kwargs): super(MatplotlibDataViewerState, self)._notify_global(*args, **kwargs) def _update_priority(self, name): if name == 'layers': return 2 elif name.endswith('_log'): return 0.5 elif name.endswith(('_min', '_max')): return 0 else: return 1 class MatplotlibLayerState(LayerState): """ A base class that includes common attributes for all layers in viewers based on Matplotlib. """ color = DeferredDrawCallbackProperty(docstring='The color used to display ' 'the data') alpha = DeferredDrawCallbackProperty(docstring='The transparency used to ' 'display the data') def __init__(self, viewer_state=None, **kwargs): super(MatplotlibLayerState, self).__init__(viewer_state=viewer_state, **kwargs) self.color = self.layer.style.color self.alpha = self.layer.style.alpha self._sync_color = keep_in_sync(self, 'color', self.layer.style, 'color') self._sync_alpha = keep_in_sync(self, 'alpha', self.layer.style, 'alpha') self.add_global_callback(self._notify_layer_update) def _notify_layer_update(self, **kwargs): message = LayerArtistUpdatedMessage(self) if self.layer is not None and self.layer.hub is not None: self.layer.hub.broadcast(message) @defer_draw def _notify_global(self, *args, **kwargs): super(MatplotlibLayerState, self)._notify_global(*args, **kwargs) glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/toolbar_mode.py0000644000175000017500000002611013752534424024224 0ustar noahfxnoahfx""" ToolbarModes are CheckableTools that enable various MouseModes. The toolbar maintains a list of MouseModes from the visualization it is assigned to, and sees to it that only one MouseMode is active at a time. Each ToolbarMode appears as an Icon in the toolbar. Classes can assign methods to the press_callback, move_callback, and release_callback methods of each Mouse Mode, to implement custom functionality The basic usage pattern is thus: * visualization object instantiates the MouseModes it wants * each of these is passed to the add_tool method of the toolbar * visualization object optionally attaches methods to the 3 _callback methods in a MouseMode, for additional behavior All the mouse modes and tools in this file are Matplotlib-specific. """ from glue.core import roi from glue.viewers.common.tool import CheckableTool from glue.config import viewer_tool from .mouse_mode import MouseMode __all__ = ['ToolbarModeBase', 'RoiModeBase', 'RoiMode', 'PersistentRoiMode', 'ClickRoiMode', 'RectangleMode', 'PathMode', 'CircleMode', 'PolyMode', 'HRangeMode', 'VRangeMode', 'PickMode'] class ToolbarModeBase(MouseMode, CheckableTool): """ All ToolbarModes are both MouseModes and CheckableTools """ def __init__(self, viewer, **kwargs): MouseMode.__init__(self, viewer, **kwargs) CheckableTool.__init__(self, viewer=viewer) class RoiModeBase(ToolbarModeBase): """ Base class for defining ROIs. ROIs accessible via the roi() method See RoiMode and ClickRoiMode subclasses for interaction details An roi_callback function can be provided. When ROIs are finalized (i.e. fully defined), this function will be called with the RoiMode object as the argument. Clients can use RoiMode.roi() to retrieve the new ROI, and take the appropriate action. By default, roi_callback will default to calling an ``apply_roi`` method on the data viewer. """ persistent = False # clear the shape when drawing completes? disable_on_finalize = True def __init__(self, viewer, **kwargs): """ Parameters ---------- roi_callback : `func` Function that will be called when the ROI is finished being defined. """ def apply_mode(mode): self.viewer.apply_roi(self.roi()) self._roi_callback = kwargs.pop('roi_callback', apply_mode) super(RoiModeBase, self).__init__(viewer, **kwargs) self._roi_tool = None def close(self, *args): self._roi_callback = None super(RoiModeBase, self).close() def activate(self): # For persistent ROIs, the user might e.g. pan and zoom around before # the selection is finalized. The Matplotlib ROIs cache the image # background to make things more efficient, but if the user pans/zooms # we need to make sure we reset the background. if getattr(self._roi_tool, '_mid_selection', False): self._roi_tool._reset_background() self._roi_tool._sync_patch() super(RoiModeBase, self).activate() def roi(self): """ The ROI defined by this mouse mode Returns ------- roi : :class:`~glue.core.roi.Roi` """ return self._roi_tool.roi() def _finish_roi(self, event): """ Called by subclasses when ROI is fully defined """ if not self.persistent: self._roi_tool.finalize_selection(event) if self._roi_callback is not None: self._roi_callback(self) if self.disable_on_finalize: self.viewer.toolbar.active_tool = None def clear(self): self._roi_tool.reset() class RoiMode(RoiModeBase): """ Define Roi Modes via click+drag events. ROIs are updated continuously on click+drag events, and finalized on each mouse release """ status_tip = "CLICK and DRAG to define selection, CTRL-CLICK and DRAG to move selection" def __init__(self, viewer, **kwargs): super(RoiMode, self).__init__(viewer, **kwargs) self._start_event = None self._drag = False def _update_drag(self, event): if self._drag or self._start_event is None: return dx = abs(event.x - self._start_event.x) dy = abs(event.y - self._start_event.y) status = self._roi_tool.start_selection(self._start_event) # If start_selection returns False, the selection has not been # started and we should abort, so we set self._drag to False in # this case. self._drag = True if status is None else status def press(self, event): self._start_event = event super(RoiMode, self).press(event) def move(self, event): self._update_drag(event) if self._drag: self._roi_tool.update_selection(event) super(RoiMode, self).move(event) def release(self, event): if self._drag: self._finish_roi(event) self._drag = False self._start_event = None super(RoiMode, self).release(event) def key(self, event): if event.key == 'escape': self._roi_tool.abort_selection(event) self._drag = False self._drawing = False self._start_event = None super(RoiMode, self).key(event) class PersistentRoiMode(RoiMode): """ Same functionality as RoiMode, but the Roi is never finalized, and remains rendered after mouse gestures """ def _finish_roi(self, event): if self._roi_callback is not None: self._roi_callback(self) class ClickRoiMode(RoiModeBase): """ Generate ROIs using clicks and click+drags. ROIs updated on each click, and each click+drag. ROIs are finalized on enter press, and reset on escape press. """ def __init__(self, viewer, **kwargs): super(ClickRoiMode, self).__init__(viewer, **kwargs) self._last_event = None self._drawing = False def press(self, event): if not self._roi_tool.active() or not self._drawing: self._roi_tool.start_selection(event) self._drawing = True else: self._roi_tool.update_selection(event) self._last_event = event super(ClickRoiMode, self).press(event) def move(self, event): if event.button is not None and self._roi_tool.active(): self._roi_tool.update_selection(event) self._last_event = event super(ClickRoiMode, self).move(event) def key(self, event): if event.key == 'enter': self._finish_roi(self._last_event) self._drawing = False elif event.key == 'escape': self._roi_tool.abort_selection(event) self._drawing = False super(ClickRoiMode, self).key(event) def release(self, event): if getattr(self._roi_tool, '_scrubbing', False): self._finish_roi(event) self._start_event = None super(ClickRoiMode, self).release(event) @viewer_tool class RectangleMode(RoiMode): """ Defines a Rectangular ROI, accessible via the :meth:`~RectangleMode.roi` method """ icon = 'glue_square' tool_id = 'select:rectangle' action_text = 'Rectangular ROI' tool_tip = 'Define a rectangular region of interest' shortcut = 'R' def __init__(self, viewer, **kwargs): super(RectangleMode, self).__init__(viewer, **kwargs) data_space = not hasattr(viewer.state, 'plot_mode') or viewer.state.plot_mode == 'rectilinear' self._roi_tool = roi.MplRectangularROI(self._axes, data_space=data_space) class PathMode(ClickRoiMode): persistent = True def __init__(self, viewer, **kwargs): super(PathMode, self).__init__(viewer, **kwargs) self._roi_tool = roi.MplPathROI(self._axes) self._roi_tool.plot_opts.update(color='#de2d26', fill=False, linewidth=3, alpha=0.4) @viewer_tool class CircleMode(RoiMode): """ Defines a Circular ROI, accessible via the :meth:`~CircleMode.roi` method """ icon = 'glue_circle' tool_id = 'select:circle' action_text = 'Circular ROI' tool_tip = 'Define a circular region of interest' shortcut = 'C' def __init__(self, viewer, **kwargs): super(CircleMode, self).__init__(viewer, **kwargs) data_space = not hasattr(viewer.state, 'plot_mode') or viewer.state.plot_mode == 'rectilinear' self._roi_tool = roi.MplCircularROI(self._axes, data_space=data_space) @viewer_tool class PolyMode(ClickRoiMode): """ Defines a Polygonal ROI, accessible via the :meth:`~PolyMode.roi` method """ icon = 'glue_lasso' tool_id = 'select:polygon' action_text = 'Polygonal ROI' tool_tip = ('Lasso a region of interest\n' ' ENTER accepts the path\n' ' ESCAPE clears the path') status_tip = ('CLICK and DRAG (or CLICK multiple times) to define selection,' ' ENTER to finalize, ESC to cancel, CTRL-CLICK and DRAG to move selection') shortcut = 'G' def __init__(self, viewer, **kwargs): super(PolyMode, self).__init__(viewer, **kwargs) data_space = not hasattr(viewer.state, 'plot_mode') or viewer.state.plot_mode == 'rectilinear' self._roi_tool = roi.MplPolygonalROI(self._axes, data_space=data_space) @viewer_tool class HRangeMode(RoiMode): """ Defines a Range ROI, accessible via the :meth:`~HRangeMode.roi` method. This class defines horizontal ranges """ icon = 'glue_xrange_select' tool_id = 'select:xrange' action_text = 'X range' tool_tip = 'Select a range of x values' shortcut = 'X' def __init__(self, viewer, **kwargs): super(HRangeMode, self).__init__(viewer, **kwargs) data_space = not hasattr(viewer.state, 'plot_mode') or viewer.state.plot_mode == 'rectilinear' self._roi_tool = roi.MplXRangeROI(self._axes, data_space=data_space) @viewer_tool class VRangeMode(RoiMode): """ Defines a Range ROI, accessible via the :meth:`~VRangeMode.roi` method. This class defines vertical ranges. """ icon = 'glue_yrange_select' tool_id = 'select:yrange' action_text = 'Y range' tool_tip = 'Select a range of y values' shortcut = 'Y' def __init__(self, viewer, **kwargs): super(VRangeMode, self).__init__(viewer, **kwargs) data_space = not hasattr(viewer.state, 'plot_mode') or viewer.state.plot_mode == 'rectilinear' self._roi_tool = roi.MplYRangeROI(self._axes, data_space=data_space) @viewer_tool class PickMode(RoiMode): """ Defines a PointROI. Defines single point selections. """ icon = 'glue_point' tool_id = 'select:pick' action_text = 'Click on the item to select' tool_tip = 'Select a single item' shortcut = 'K' def __init__(self, viewer, **kwargs): super(PickMode, self).__init__(viewer, **kwargs) self._roi_tool = roi.MplPickROI(self._axes) def press(self, event): super(PickMode, self).press(event) self._drag = True glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/__init__.py0000644000175000017500000000004313507416115023304 0ustar noahfxnoahfxfrom . import toolbar_mode # noqa glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/layer_artist.py0000644000175000017500000000510113661230662024251 0ustar noahfxnoahfximport matplotlib.patches as mpatches from glue.viewers.matplotlib.state import DeferredDrawCallbackProperty from glue.core.message import ComputationStartedMessage, ComputationEndedMessage from glue.viewers.common.layer_artist import LayerArtist __all__ = ['MatplotlibLayerArtist'] class MatplotlibLayerArtist(LayerArtist): zorder = DeferredDrawCallbackProperty() visible = DeferredDrawCallbackProperty() def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(MatplotlibLayerArtist, self).__init__(viewer_state, layer_state=layer_state, layer=layer) self.axes = axes self.mpl_artists = [] def notify_start_computation(self): """ Broadcast a message to indicate that this layer artist has started a computation (typically used in conjunction with asynchronous operations). """ if self.state.layer is not None and self.state.layer.hub is not None: self.state.layer.hub.broadcast(ComputationStartedMessage(self)) def notify_end_computation(self): """ Broadcast a message to indicate that this layer artist has ended a computation (typically used in conjunction with asynchronous operations). """ if self.state.layer is not None and self.state.layer.hub is not None: self.state.layer.hub.broadcast(ComputationEndedMessage(self)) def clear(self): for artist in self.mpl_artists: try: artist.set_visible(False) except AttributeError: # can happen for e.g. errorbars pass def remove(self): for artist in self.mpl_artists: try: artist.remove() except ValueError: # already removed pass except TypeError: # can happen for e.g. errorbars pass except AttributeError: # can happen for Matplotlib 1.4 pass self.mpl_artists[:] = [] self._notify_start = None def get_layer_color(self): return self.state.color def get_handle_legend(self): # The default legend handle for matplotlib viewer if self.enabled and self.state.visible: handle = mpatches.Patch(color=self.get_layer_color(), alpha=self.layer.style.alpha) return handle, self.layer.label, None else: return None, None, None def redraw(self): self.axes.figure.canvas.draw_idle() glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/mouse_mode.py0000644000175000017500000001054613630201003023673 0ustar noahfxnoahfx""" MouseModes define various mouse gestures. MouseModes are generally activated and deactivated by toolbar buttons, although not necessarily so. The toolbar maintains a list of MouseModes from the visualization it is assigned to, and sees to it that only one MouseMode is active at a time. Each MouseMode appears as an Icon in the toolbar. Classes can assign methods to the press_callback, move_callback, and release_callback methods of each Mouse Mode, to implement custom functionality The basic usage pattern is thus: * visualization object instantiates the MouseModes it wants * each of these is passed to the add_tool method of the toolbar * visualization object optionally attaches methods to the 3 _callback methods in a MouseMode, for additional behavior """ __all__ = ['MouseMode'] class MouseMode(object): """ The base class for all MouseModes. MouseModes have the following attributes: * press_callback : Callback method that will be called whenever a MouseMode processes a mouse press event * move_callback : Same as above, for move events * release_callback : Same as above, for release events * key_callback : Same as above, for release events The _callback hooks are called with the MouseMode as its only argument """ def __init__(self, viewer, press_callback=None, move_callback=None, release_callback=None, key_callback=None): self._axes = getattr(viewer, 'axes', None) self._canvas = viewer.central_widget.canvas self._press_callback = press_callback self._move_callback = move_callback self._release_callback = release_callback self._key_callback = key_callback self._event_x = None self._event_y = None self._event_xdata = None self._event_ydata = None self._connections = [] def _log_position(self, event): if event is None: return self._event_x, self._event_y = event.x, event.y self._event_xdata, self._event_ydata = event.xdata, event.ydata def press(self, event): """ Handles mouse presses. Logs mouse position and calls press_callback method. Parameters ---------- event : :class:`~matplotlib.backend_bases.MouseEvent` The event that was triggered """ self._log_position(event) if self._press_callback is not None: self._press_callback(self) def move(self, event): """ Handles mouse move events. Logs mouse position and calls move_callback method. Parameters ---------- event : :class:`~matplotlib.backend_bases.MouseEvent` The event that was triggered """ self._log_position(event) if self._move_callback is not None: self._move_callback(self) def release(self, event): """ Handles mouse release events. Logs mouse position and calls release_callback method. Parameters ---------- event : :class:`~matplotlib.backend_bases.MouseEvent` The event that was triggered """ self._log_position(event) if self._release_callback is not None: self._release_callback(self) def key(self, event): """ Handles key press events. Calls key_callback method. Parameters ---------- event : :class:`~matplotlib.backend_bases.KeyEvent` The event that was triggered """ if self._key_callback is not None: self._key_callback(self) def activate(self): """ Activates all MPL event handlers associated with this mouse mode. """ self._connections.append(self._canvas.mpl_connect('button_press_event', self.press)) self._connections.append(self._canvas.mpl_connect('motion_notify_event', self.move)) self._connections.append(self._canvas.mpl_connect('button_release_event', self.release)) self._connections.append(self._canvas.mpl_connect('key_press_event', self.key)) def deactivate(self): """ Deactivates all MPL event handlers associated with this mouse mode. """ for connection in self._connections: self._canvas.mpl_disconnect(connection) self._connections = [] glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/mpl_axes.py0000644000175000017500000000367613752534424023402 0ustar noahfxnoahfximport matplotlib.pyplot as plt from glue.utils.matplotlib import freeze_margins __all__ = ['update_appearance_from_settings', 'init_mpl'] def set_background_color(axes, color): axes.figure.set_facecolor(color) axes.patch.set_facecolor(color) def set_foreground_color(axes, color): if hasattr(axes, 'coords'): axes.coords.frame.set_color(color) axes.coords.frame.set_linewidth(1) for coord in axes.coords: coord.set_ticks(color=color) coord.set_ticklabel(color=color) coord.axislabels.set_color(color) else: for spine in axes.spines.values(): spine.set_color(color) axes.tick_params(color=color, labelcolor=color) axes.xaxis.label.set_color(color) axes.yaxis.label.set_color(color) def update_appearance_from_settings(axes): from glue.config import settings set_background_color(axes, settings.BACKGROUND_COLOR) set_foreground_color(axes, settings.FOREGROUND_COLOR) def init_mpl(figure=None, axes=None, wcs=False, axes_factory=None, projection=None): if (axes is not None and figure is not None and axes.figure is not figure): raise ValueError("Axes and figure are incompatible") try: from astropy.visualization.wcsaxes import WCSAxesSubplot except ImportError: WCSAxesSubplot = None if axes is not None: _axes = axes _figure = axes.figure else: _figure = figure or plt.figure() if wcs and WCSAxesSubplot is not None: _axes = WCSAxesSubplot(_figure, 111) _figure.add_axes(_axes) else: if axes_factory is None: _axes = _figure.add_subplot(1, 1, 1, projection=projection) else: _axes = axes_factory(_figure) freeze_margins(_axes, margins=[1, 0.25, 0.50, 0.25]) update_appearance_from_settings(_axes) return _figure, _axes glueviz-1.0.1+dfsg.orig/glue/viewers/matplotlib/viewer.py0000644000175000017500000003071213752534424023062 0ustar noahfxnoahfxfrom textwrap import indent import numpy as np from matplotlib.patches import Rectangle from matplotlib.artist import setp as msetp from glue.viewers.matplotlib.mpl_axes import update_appearance_from_settings from echo import delay_callback from glue.utils import mpl_to_datetime64 __all__ = ['MatplotlibViewerMixin'] SCRIPT_HEADER = """ # Initialize figure fig = plt.figure() ax = fig.add_subplot(1, 1, 1, aspect='{aspect}', projection={projection}) # for the legend legend_handles = [] legend_labels = [] legend_handler_dict = dict() """.strip() LIMIT_SCRIPT = """ # Set limits ax.set_xlim(left={x_min}, right={x_max}) ax.set_ylim(bottom={y_min}, top={y_max}) """ SCRIPT_FOOTER = """ {limit_script} {scale_script} # Set axis label properties ax.set_xlabel('{x_axislabel}', weight='{x_axislabel_weight}', size={x_axislabel_size}) ax.set_ylabel('{y_axislabel}', weight='{y_axislabel_weight}', size={y_axislabel_size}) # Set tick label properties ax.tick_params('x', labelsize={x_ticklabel_size}) ax.tick_params('y', labelsize={x_ticklabel_size}) # For manual edition of the plot # - Uncomment the next code line (plt.show) # - Also change the matplotlib backend to qt5Agg # - And comment the "plt.close" line # plt.show() # Save figure fig.savefig('glue_plot.png') plt.close(fig) """.strip() SCRIPT_LEGEND = """ ax.legend(legend_handles, legend_labels, handler_map=legend_handler_dict, loc='{location}', # location framealpha={alpha:.2f}, # opacity of the frame title='{title}', # title of the legend title_fontsize={fontsize}, # fontsize of the title fontsize={fontsize}, # fontsize of the labels facecolor='{frame_color}', edgecolor={edge_color} ) """.strip() ZORDER_MAX = 100000 class MatplotlibViewerMixin(object): def setup_callbacks(self): for spine in self.axes.spines.values(): spine.set_zorder(ZORDER_MAX) self.loading_rectangle = Rectangle((0, 0), 1, 1, color='0.9', alpha=0.9, zorder=ZORDER_MAX - 1, transform=self.axes.transAxes) self.loading_rectangle.set_visible(False) self.axes.add_patch(self.loading_rectangle) self.loading_text = self.axes.text(0.4, 0.5, 'Computing', color='k', zorder=self.loading_rectangle.get_zorder() + 1, ha='left', va='center', transform=self.axes.transAxes) self.loading_text.set_visible(False) self.state.add_callback('x_min', self.limits_to_mpl) self.state.add_callback('x_max', self.limits_to_mpl) self.state.add_callback('y_min', self.limits_to_mpl) self.state.add_callback('y_max', self.limits_to_mpl) if (self.state.x_min or self.state.x_max or self.state.y_min or self.state.y_max) is None: self.limits_from_mpl() else: self.limits_to_mpl() self.state.add_callback('x_log', self.update_x_log, priority=1000) self.state.add_callback('y_log', self.update_y_log, priority=1000) self.update_x_log() self.axes.callbacks.connect('xlim_changed', self.limits_from_mpl) self.axes.callbacks.connect('ylim_changed', self.limits_from_mpl) self.figure.canvas.mpl_connect('resize_event', self._on_resize) self._on_resize() self.axes.set_autoscale_on(False) self.state.add_callback('x_axislabel', self.update_x_axislabel) self.state.add_callback('x_axislabel_weight', self.update_x_axislabel) self.state.add_callback('x_axislabel_size', self.update_x_axislabel) self.state.add_callback('y_axislabel', self.update_y_axislabel) self.state.add_callback('y_axislabel_weight', self.update_y_axislabel) self.state.add_callback('y_axislabel_size', self.update_y_axislabel) self.state.add_callback('x_ticklabel_size', self.update_x_ticklabel) self.state.add_callback('y_ticklabel_size', self.update_y_ticklabel) self.state.legend.add_callback('visible', self.draw_legend) self.state.legend.add_callback('location', self.draw_legend) self.state.legend.add_callback('alpha', self.update_legend) self.state.legend.add_callback('title', self.draw_legend) self.state.legend.add_callback('fontsize', self.draw_legend) self.state.legend.add_callback('frame_color', self.update_legend) self.state.legend.add_callback('show_edge', self.update_legend) self.state.legend.add_callback('text_color', self.update_legend) self.update_x_axislabel() self.update_y_axislabel() self.update_x_ticklabel() self.update_y_ticklabel() self.draw_legend() def _update_computation(self, message=None): pass def update_x_axislabel(self, *event): self.axes.set_xlabel(self.state.x_axislabel, weight=self.state.x_axislabel_weight, size=self.state.x_axislabel_size) self.redraw() def update_y_axislabel(self, *event): self.axes.set_ylabel(self.state.y_axislabel, weight=self.state.y_axislabel_weight, size=self.state.y_axislabel_size) self.redraw() def update_x_ticklabel(self, *event): self.axes.tick_params(axis='x', labelsize=self.state.x_ticklabel_size) self.axes.xaxis.get_offset_text().set_fontsize(self.state.x_ticklabel_size) self.redraw() def update_y_ticklabel(self, *event): self.axes.tick_params(axis='y', labelsize=self.state.y_ticklabel_size) self.axes.yaxis.get_offset_text().set_fontsize(self.state.y_ticklabel_size) self.redraw() def get_handles_legend(self): """Collect the handles and labels from each layer artist.""" handles = [] labels = [] handler_dict = {} for layer_artist in self._layer_artist_container: handle, label, handler = layer_artist.get_handle_legend() if handle is not None: handles.append(handle) labels.append(label) if handler is not None: handler_dict[handle] = handler return handles, labels, handler_dict def _update_legend_visual(self, legend): """Update the legend colors and opacity. No redraw.""" msetp(legend.get_title(), color=self.state.legend.text_color) msetp(legend.get_texts(), color=self.state.legend.text_color) msetp(legend.get_frame(), alpha=self.state.legend.alpha, facecolor=self.state.legend.frame_color, edgecolor=self.state.legend.edge_color ) def update_legend(self, *args): """Update the legend colors and opacity.""" legend = self.axes.get_legend() if legend is not None: self._update_legend_visual(legend) self.redraw() def draw_legend(self, *args): if self.state.legend.visible: handles, labels, handler_map = self.get_handles_legend() kwargs = dict(loc=self.state.legend.mpl_location, title=self.state.legend.title, title_fontsize=self.state.legend.fontsize, fontsize=self.state.legend.fontsize) if handler_map is not None: kwargs["handler_map"] = handler_map legend = self.axes.legend(handles, labels, **kwargs) self._update_legend_visual(legend) legend.set_draggable(self.state.legend.draggable) else: legend = self.axes.get_legend() if legend is not None: legend.remove() self.redraw() def redraw(self): self.figure.canvas.draw_idle() def update_x_log(self, *args): self.axes.set_xscale('log' if self.state.x_log else 'linear') self.redraw() def update_y_log(self, *args): self.axes.set_yscale('log' if self.state.y_log else 'linear') self.redraw() def limits_from_mpl(self, *args, **kwargs): if getattr(self, '_skip_limits_from_mpl', False): return if isinstance(self.state.x_min, np.datetime64): x_min, x_max = [mpl_to_datetime64(x) for x in self.axes.get_xlim()] else: x_min, x_max = self.axes.get_xlim() if isinstance(self.state.y_min, np.datetime64): y_min, y_max = [mpl_to_datetime64(y) for y in self.axes.get_ylim()] else: y_min, y_max = self.axes.get_ylim() with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max'): self.state.x_min = x_min self.state.x_max = x_max self.state.y_min = y_min self.state.y_max = y_max def limits_to_mpl(self, *args): if (self.state.x_min is None or self.state.x_max is None or self.state.y_min is None or self.state.y_max is None): return x_min, x_max = self.state.x_min, self.state.x_max if self.state.x_log: if self.state.x_max <= 0: x_min, x_max = 0.1, 1 elif self.state.x_min <= 0: x_min = x_max / 10 y_min, y_max = self.state.y_min, self.state.y_max if self.state.y_log: if self.state.y_max <= 0: y_min, y_max = 0.1, 1 elif self.state.y_min <= 0: y_min = y_max / 10 # Since we deal with aspect ratio internally, there are no conditions # under which we would want to immediately change the state once the # limits are set in Matplotlib, so we avoid this by setting the # _skip_limits_from_mpl attribute which is then recognized inside # limits_from_mpl self._skip_limits_from_mpl = True try: self.axes.set_xlim(x_min, x_max) self.axes.set_ylim(y_min, y_max) self.axes.figure.canvas.draw_idle() finally: self._skip_limits_from_mpl = False @property def axes_ratio(self): # Get figure aspect ratio width, height = self.figure.get_size_inches() fig_aspect = height / width # Get axes aspect ratio l, b, w, h = self.axes.get_position().bounds axes_ratio = fig_aspect * (h / w) return axes_ratio def _on_resize(self, *args): self.state._set_axes_aspect_ratio(self.axes_ratio) def _update_appearance_from_settings(self, message=None): update_appearance_from_settings(self.axes) self.redraw() def get_layer_artist(self, cls, layer=None, layer_state=None): return cls(self.axes, self.state, layer=layer, layer_state=layer_state) def apply_roi(self, roi, override_mode=None): """ This method must be implemented by subclasses """ raise NotImplementedError def _script_header(self): state_dict = self.state.as_dict() proj = "'" + self.state.plot_mode + "'" if hasattr(self.state, 'plot_mode') else "None" state_dict['projection'] = proj return ['import matplotlib', "matplotlib.use('Agg')", "# matplotlib.use('qt5Agg')", 'import matplotlib.pyplot as plt'], SCRIPT_HEADER.format(**state_dict) def _script_footer(self): mode = 'rectilinear' if not hasattr(self.state, 'plot_mode') else self.state.plot_mode state_dict = self.state.as_dict() temp_str = '' if mode not in ['aitoff', 'hammer', 'lambert', 'mollweide', 'polar']: x_log_str = 'log' if self.state.x_log else 'linear' temp_str += "ax.set_xscale('" + x_log_str + "')\n" if mode not in ['aitoff', 'hammer', 'lambert', 'mollweide']: y_log_str = 'log' if self.state.y_log else 'linear' temp_str += "ax.set_yscale('" + y_log_str + "')\n" state_dict['scale_script'] = "# Set scale (log or linear)\n" + temp_str if temp_str else '' full_sphere = ['aitoff', 'hammer', 'lambert', 'mollweide'] state_dict['limit_script'] = '' if mode in full_sphere else LIMIT_SCRIPT.format(**state_dict) return [], SCRIPT_FOOTER.format(**state_dict) def _script_legend(self): state_dict = self.state.legend.as_dict() state_dict['location'] = self.state.legend.mpl_location state_dict['edge_color'] = self.state.legend.edge_color legend_str = SCRIPT_LEGEND.format(**state_dict) if not self.state.legend.visible: legend_str = indent(legend_str, "# ") return [], legend_str glueviz-1.0.1+dfsg.orig/glue/viewers/table/0000755000175000017500000000000013752535025020122 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/table/tests/0000755000175000017500000000000013752535025021264 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/table/tests/__init__.py0000644000175000017500000000000013455362716023370 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/table/qt/0000755000175000017500000000000013752535025020546 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/table/qt/tests/0000755000175000017500000000000013752535025021710 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/table/qt/tests/__init__.py0000644000175000017500000000000013455362716024014 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/table/qt/tests/test_data_viewer.py0000644000175000017500000003121613660522006025607 0ustar noahfxnoahfximport pytest import numpy as np from unittest.mock import MagicMock, patch from qtpy import QtCore, QtGui from qtpy.QtCore import Qt from glue.utils.qt import get_qapp, process_events from glue.core import Data, DataCollection from glue.utils.qt import qt_to_mpl_color from glue.app.qt import GlueApplication from ..data_viewer import DataTableModel, TableViewer from glue.core.edit_subset_mode import AndNotMode, OrMode, ReplaceMode class TestDataTableModel(): def setup_method(self, method): self.gapp = GlueApplication() self.viewer = self.gapp.new_data_viewer(TableViewer) self.data = Data(x=[1, 2, 3, 4], y=[2, 3, 4, 5]) self.gapp.data_collection.append(self.data) self.viewer.add_data(self.data) self.model = DataTableModel(self.viewer) def teardown_method(self, method): self.gapp.close() self.gapp = None def test_column_count(self): assert self.model.columnCount() == 2 def test_column_count_hidden(self): self.model.show_coords = True assert self.model.columnCount() == 3 def test_header_data(self): for i, c in enumerate(self.data.main_components): result = self.model.headerData(i, Qt.Horizontal, Qt.DisplayRole) assert result == c.label for i in range(self.data.size): result = self.model.headerData(i, Qt.Vertical, Qt.DisplayRole) assert result == str(i) def test_row_count(self): assert self.model.rowCount() == 4 def test_data(self): for i, c in enumerate(self.data.main_components): for j in range(self.data.size): idx = self.model.index(j, i) result = self.model.data(idx, Qt.DisplayRole) assert float(result) == self.data[c, j] @pytest.mark.xfail def test_data_2d(self): self.data = Data(x=[[1, 2], [3, 4]], y=[[2, 3], [4, 5]]) self.model = DataTableModel(self.data) for i, c in enumerate(self.data.main_components): for j in range(self.data.size): idx = self.model.index(j, i) result = self.model.data(idx, Qt.DisplayRole) assert float(result) == self.data[c].ravel()[j] def check_values_and_color(model, data, colors): for i in range(len(colors)): for j, colname in enumerate(sorted(data)): # Get index of cell idx = model.index(i, j) # Check values value = model.data(idx, Qt.DisplayRole) assert value == str(data[colname][i]) # Check colors brush = model.data(idx, Qt.BackgroundRole) if colors[i] is None: assert brush is None else: assert qt_to_mpl_color(brush.color()) == colors[i] def test_table_widget(tmpdir): # Start off by creating a glue application instance with a table viewer and # some data pre-loaded. app = get_qapp() d = Data(a=[1, 2, 3, 4, 5], b=[3.2, 1.2, 4.5, 3.3, 2.2], c=['e', 'b', 'c', 'a', 'f']) dc = DataCollection([d]) gapp = GlueApplication(dc) widget = gapp.new_data_viewer(TableViewer) widget.add_data(d) subset_mode = gapp._session.edit_subset_mode # Create two subsets sg1 = dc.new_subset_group('D <= 3', d.id['a'] <= 3) sg1.style.color = '#aa0000' sg2 = dc.new_subset_group('1 < D < 4', (d.id['a'] > 1) & (d.id['a'] < 4)) sg2.style.color = '#0000cc' model = widget.ui.table.model() # We now check what the data and colors of the table are, and try various # sorting methods to make sure that things are still correct. data = {'a': [1, 2, 3, 4, 5], 'b': [3.2, 1.2, 4.5, 3.3, 2.2], 'c': ['e', 'b', 'c', 'a', 'f']} colors = ['#aa0000', '#380088', '#380088', None, None] check_values_and_color(model, data, colors) model.sort(1, Qt.AscendingOrder) data = {'a': [2, 5, 1, 4, 3], 'b': [1.2, 2.2, 3.2, 3.3, 4.5], 'c': ['b', 'f', 'e', 'a', 'c']} colors = ['#380088', None, '#aa0000', None, '#380088'] check_values_and_color(model, data, colors) model.sort(2, Qt.AscendingOrder) data = {'a': [4, 2, 3, 1, 5], 'b': [3.3, 1.2, 4.5, 3.2, 2.2], 'c': ['a', 'b', 'c', 'e', 'f']} colors = [None, '#380088', '#380088', '#aa0000', None] check_values_and_color(model, data, colors) model.sort(0, Qt.DescendingOrder) data = {'a': [5, 4, 3, 2, 1], 'b': [2.2, 3.3, 4.5, 1.2, 3.2], 'c': ['f', 'a', 'c', 'b', 'e']} colors = [None, None, '#380088', '#380088', '#aa0000'] check_values_and_color(model, data, colors) model.sort(0, Qt.AscendingOrder) # We now modify the subsets using the table. selection = widget.ui.table.selectionModel() widget.toolbar.actions['table:rowselect'].toggle() def press_key(key): event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, key, Qt.NoModifier) app.postEvent(widget.ui.table, event) app.processEvents() process_events() # We now use key presses to navigate down to the third row press_key(Qt.Key_Tab) press_key(Qt.Key_Down) press_key(Qt.Key_Down) process_events() indices = selection.selectedRows() # We make sure that the third row is selected assert len(indices) == 1 assert indices[0].row() == 2 # At this point, the subsets haven't changed yet np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 0, 0]) np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 1, 0, 0]) # We specify that we are editing the second subset, and use a 'not' logical # operation to remove the currently selected line from the second subset. subset_mode.edit_subset = [d.subsets[1]] subset_mode.mode = AndNotMode press_key(Qt.Key_Enter) np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 0, 0]) np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0]) # At this point, the selection should be cleared indices = selection.selectedRows() assert len(indices) == 0 # We move to the fourth row and now do an 'or' selection with the first # subset. press_key(Qt.Key_Down) subset_mode.mode = OrMode subset_mode.edit_subset = [d.subsets[0]] press_key(Qt.Key_Enter) np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 1, 0]) np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0]) # Finally we move to the fifth row and deselect all subsets so that # pressing enter now creates a new subset. press_key(Qt.Key_Down) subset_mode.mode = ReplaceMode subset_mode.edit_subset = None press_key(Qt.Key_Enter) np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 1, 0]) np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0]) np.testing.assert_equal(d.subsets[2].to_mask(), [0, 0, 0, 0, 1]) # Make the color for the new subset deterministic dc.subset_groups[2].style.color = '#bababa' # Now finally check saving and restoring session session_file = tmpdir.join('table.glu').strpath gapp.save_session(session_file) gapp2 = GlueApplication.restore_session(session_file) gapp2.show() d = gapp2.data_collection[0] widget2 = gapp2.viewers[0][0] model2 = widget2.ui.table.model() data = {'a': [1, 2, 3, 4, 5], 'b': [3.2, 1.2, 4.5, 3.3, 2.2], 'c': ['e', 'b', 'c', 'a', 'f']} # Need to take into account new selections above colors = ['#aa0000', '#380088', '#aa0000', "#aa0000", "#bababa"] check_values_and_color(model2, data, colors) def test_table_widget_session_no_subset(tmpdir): # Regression test for a bug that caused table viewers with no subsets to # not be restored correctly and instead raise an exception. app = get_qapp() # noqa d = Data(a=[1, 2, 3, 4, 5], b=[3.2, 1.2, 4.5, 3.3, 2.2], c=['e', 'b', 'c', 'a', 'f'], label='test') dc = DataCollection([d]) gapp = GlueApplication(dc) widget = gapp.new_data_viewer(TableViewer) widget.add_data(d) session_file = tmpdir.join('table.glu').strpath gapp.save_session(session_file) gapp2 = GlueApplication.restore_session(session_file) gapp2.show() gapp2.data_collection[0] gapp2.viewers[0][0] def test_change_components(): # Regression test for a bug that caused table viewers to not update when # adding/removing components. app = get_qapp() # noqa d = Data(a=[1, 2, 3, 4, 5], b=[3.2, 1.2, 4.5, 3.3, 2.2], c=['e', 'b', 'c', 'a', 'f'], label='test') dc = DataCollection([d]) gapp = GlueApplication(dc) viewer = gapp.new_data_viewer(TableViewer) viewer.add_data(d) data_changed = MagicMock() viewer.model.dataChanged.connect(data_changed) # layoutChanged needs to be emitted for the new/removed columns to be # registered (dataChanged is not enough) layout_changed = MagicMock() viewer.model.layoutChanged.connect(layout_changed) assert data_changed.call_count == 0 assert layout_changed.call_count == 0 viewer.model.columnCount() == 2 d.add_component([3, 4, 5, 6, 2], 'z') assert data_changed.call_count == 1 assert layout_changed.call_count == 1 viewer.model.columnCount() == 3 d.remove_component(d.id['z']) assert data_changed.call_count == 2 assert layout_changed.call_count == 2 viewer.model.columnCount() == 2 def test_table_title(): app = get_qapp() # noqa data1 = Data(a=[1, 2, 3, 4, 5], label='test1') data2 = Data(a=[1, 2, 3, 4, 5], label='test2') dc = DataCollection([data1, data2]) gapp = GlueApplication(dc) viewer = gapp.new_data_viewer(TableViewer) assert viewer.windowTitle() == 'Table' viewer.add_data(data1) assert viewer.windowTitle() == 'Table: test1' viewer.add_data(data2) assert viewer.windowTitle() == 'Table: test2' def test_add_subset(): # Regression test for a bug that occurred when adding a subset # directly to the table viewer. data1 = Data(a=[1, 2, 3, 4, 5], label='test1') data2 = Data(a=[1, 2, 3, 4, 5], label='test2') dc = DataCollection([data1, data2]) dc.new_subset_group('test subset 1', data1.id['a'] > 2) gapp = GlueApplication(dc) viewer = gapp.new_data_viewer(TableViewer) viewer.add_subset(data1.subsets[0]) assert len(viewer.state.layers) == 2 assert not viewer.state.layers[0].visible assert viewer.state.layers[1].visible dc.new_subset_group('test subset 2', data1.id['a'] <= 2) assert len(viewer.state.layers) == 3 assert not viewer.state.layers[0].visible assert viewer.state.layers[1].visible assert viewer.state.layers[2].visible viewer.remove_subset(data1.subsets[1]) assert len(viewer.state.layers) == 2 assert not viewer.state.layers[0].visible assert viewer.state.layers[1].visible viewer.add_subset(data1.subsets[1]) assert len(viewer.state.layers) == 3 assert not viewer.state.layers[0].visible assert viewer.state.layers[1].visible assert viewer.state.layers[2].visible with pytest.raises(ValueError) as exc: viewer.add_subset(data2.subsets[1]) assert exc.value.args[0] == 'subset parent data does not match existing table data' def test_graceful_close_after_invalid(capsys): # Regression test for a bug that caused an error if an invalid dataset # was added to the viewer after the user had acknowledged the error. d = Data(a=[[1, 2], [3, 4]], label='test') dc = DataCollection([d]) gapp = GlueApplication(dc) viewer = gapp.new_data_viewer(TableViewer) gapp.show() process_events() with pytest.raises(ValueError, match='Can only use Table widget for 1D data'): viewer.add_data(d) viewer.close() process_events() # We use capsys here because the # error is otherwise only apparent in stderr. out, err = capsys.readouterr() assert out.strip() == "" assert err.strip() == "" def test_incompatible_subset(): # Regression test for a bug that caused the table to be refreshed in an # infinite loop if incompatible subsets were present. data1 = Data(a=[1, 2, 3, 4, 5], label='test1') data2 = Data(a=[1, 2, 3, 4, 5], label='test2') dc = DataCollection([data1, data2]) gapp = GlueApplication(dc) viewer = gapp.new_data_viewer(TableViewer) viewer.add_data(data1) dc.new_subset_group('test subset', data2.id['a'] > 2) gapp.show() process_events(0.5) with patch.object(viewer.layers[0], '_refresh') as refresh1: with patch.object(viewer.layers[1], '_refresh') as refresh2: process_events(0.5) assert refresh1.call_count == 0 assert refresh2.call_count == 0 glueviz-1.0.1+dfsg.orig/glue/viewers/table/qt/data_viewer.py0000644000175000017500000002534013657331477023427 0ustar noahfxnoahfximport os import numpy as np from qtpy.QtCore import Qt from qtpy import QtCore, QtGui, QtWidgets from matplotlib.colors import ColorConverter from glue.utils.qt import get_qapp from glue.config import viewer_tool from glue.core import BaseData, Data from glue.utils.qt import load_ui from glue.viewers.common.qt.data_viewer import DataViewer from glue.viewers.common.qt.toolbar import BasicToolbar from glue.viewers.common.tool import CheckableTool from glue.viewers.common.layer_artist import LayerArtist from glue.core.subset import ElementSubsetState from glue.utils.colors import alpha_blend_colors from glue.utils.qt import mpl_to_qt_color, messagebox_on_error from glue.core.exceptions import IncompatibleAttribute from glue.viewers.table.compat import update_table_viewer_state __all__ = ['TableViewer', 'TableLayerArtist'] COLOR_CONVERTER = ColorConverter() class DataTableModel(QtCore.QAbstractTableModel): def __init__(self, table_viewer): super(DataTableModel, self).__init__() if table_viewer.data.ndim != 1: raise ValueError("Can only use Table widget for 1D data") self._table_viewer = table_viewer self._data = table_viewer.data self.show_coords = False self.order = np.arange(self._data.shape[0]) self._update_visible() def data_changed(self): top_left = self.index(0, 0) bottom_right = self.index(self.columnCount(), self.rowCount()) self._update_visible() self.dataChanged.emit(top_left, bottom_right) self.layoutChanged.emit() @property def columns(self): if self.show_coords: return self._data.components else: return self._data.main_components + self._data.derived_components def columnCount(self, index=None): return len(self.columns) def rowCount(self, index=None): # Qt bug: Crashes on tables bigger than this return min(self.order_visible.size, 71582788) def headerData(self, section, orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: column_name = self.columns[section].label units = self._data.get_component(self.columns[section]).units if units != '': column_name += "\n{0}".format(units) return column_name elif orientation == Qt.Vertical: return str(self.order_visible[section]) def data(self, index, role): if not index.isValid(): return None if role == Qt.DisplayRole: c = self.columns[index.column()] idx = self.order_visible[index.row()] comp = self._data[c] if isinstance(comp[idx], bytes): return comp[idx].decode('ascii') else: return str(comp[idx]) elif role == Qt.BackgroundRole: idx = self.order_visible[index.row()] # Find all subsets that this index is part of colors = [] for layer_artist in self._table_viewer.layers[::-1]: if isinstance(layer_artist.layer, BaseData): continue if layer_artist.visible: subset = layer_artist.layer try: if subset.to_mask(view=slice(idx, idx + 1))[0]: colors.append(subset.style.color) except IncompatibleAttribute as exc: # Only disable the layer if enabled, as otherwise we # will recursively call clear and _refresh, causing # an infinite loop and performance issues. if layer_artist.enabled: layer_artist.disable_invalid_attributes(*exc.args) else: layer_artist.enabled = True # Blend the colors using alpha blending if len(colors) > 0: color = alpha_blend_colors(colors, additional_alpha=0.5) color = mpl_to_qt_color(color) return QtGui.QBrush(color) def sort(self, column, ascending): c = self.columns[column] comp = self._data.get_component(c) self.order = np.argsort(comp.data) if ascending == Qt.DescendingOrder: self.order = self.order[::-1] self._update_visible() self.layoutChanged.emit() def _update_visible(self): """ Given which layers are visible or not, convert order to order_visible. """ # First, if the data layer is visible, show all rows for layer_artist in self._table_viewer.layers: if layer_artist.visible and isinstance(layer_artist.layer, BaseData): self.order_visible = self.order return # If not then we need to show only the rows with visible subsets visible = np.zeros(self.order.shape, dtype=bool) for layer_artist in self._table_viewer.layers: if layer_artist.visible: visible |= layer_artist.layer.to_mask() self.order_visible = self.order[visible] class TableLayerArtist(LayerArtist): def __init__(self, table_viewer, viewer_state, layer_state=None, layer=None): self._table_viewer = table_viewer super(TableLayerArtist, self).__init__(viewer_state, layer_state=layer_state, layer=layer) self.redraw() def _refresh(self): self._table_viewer.model.data_changed() def redraw(self): self._refresh() def update(self): self._refresh() def clear(self): self._refresh() @viewer_tool class RowSelectTool(CheckableTool): tool_id = 'table:rowselect' icon = 'glue_row_select' action_text = 'Select row(s)' tool_tip = ('Select rows by clicking on rows and pressing enter ' 'once the selection is ready to be applied') status_tip = ('CLICK to select, press ENTER to finalize selection, ' 'ALT+CLICK or ALT+UP/DOWN to apply selection immediately') def __init__(self, viewer): super(RowSelectTool, self).__init__(viewer) self.deactivate() def activate(self): self.viewer.ui.table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) def deactivate(self): # Don't do anything if the viewer has already been closed if self.viewer is None: return self.viewer.ui.table.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.viewer.ui.table.clearSelection() class TableViewWithSelectionSignal(QtWidgets.QTableView): selection_changed = QtCore.Signal() def selectionChanged(self, *args, **kwargs): self.selection_changed.emit() super(TableViewWithSelectionSignal, self).selectionChanged(*args, **kwargs) class TableViewer(DataViewer): LABEL = "Table Viewer" _toolbar_cls = BasicToolbar _data_artist_cls = TableLayerArtist _subset_artist_cls = TableLayerArtist inherit_tools = False tools = ['table:rowselect'] def __init__(self, session, state=None, parent=None, widget=None): super(TableViewer, self).__init__(session, state=state, parent=parent) self.ui = load_ui('data_viewer.ui', directory=os.path.dirname(__file__)) self.setCentralWidget(self.ui) hdr = self.ui.table.horizontalHeader() hdr.setStretchLastSection(True) hdr.setSectionResizeMode(hdr.Interactive) hdr = self.ui.table.verticalHeader() hdr.setSectionResizeMode(hdr.Interactive) self.data = None self.model = None self.ui.table.selection_changed.connect(self.selection_changed) self.state.add_callback('layers', self._on_layers_changed) self._on_layers_changed() def selection_changed(self): app = get_qapp() if app.queryKeyboardModifiers() == Qt.AltModifier: self.finalize_selection(clear=False) def keyPressEvent(self, event): if self.toolbar.active_tool is self.toolbar.tools['table:rowselect']: if event.key() in [Qt.Key_Enter, Qt.Key_Return]: self.finalize_selection() super(TableViewer, self).keyPressEvent(event) def finalize_selection(self, clear=True): model = self.ui.table.selectionModel() selected_rows = [self.model.order_visible[x.row()] for x in model.selectedRows()] subset_state = ElementSubsetState(indices=selected_rows, data=self.data) mode = self.session.edit_subset_mode mode.update(self._data, subset_state, focus_data=self.data) if clear: # We block the signals here to make sure that we don't update # the subset again once the selection is cleared. self.ui.table.blockSignals(True) self.ui.table.clearSelection() self.ui.table.blockSignals(False) def _on_layers_changed(self, *args): for layer_state in self.state.layers: if isinstance(layer_state.layer, BaseData): break else: return self.data = layer_state.layer self.setUpdatesEnabled(False) self.model = DataTableModel(self) self.ui.table.setModel(self.model) self.setUpdatesEnabled(True) @messagebox_on_error("Failed to add data") def add_data(self, data): with self._layer_artist_container.ignore_empty(): self.state.layers[:] = [] return super(TableViewer, self).add_data(data) @messagebox_on_error("Failed to add subset") def add_subset(self, subset): if self.data is None: self.add_data(subset.data) self.state.layers[0].visible = False elif subset.data != self.data: raise ValueError("subset parent data does not match existing table data") return super(TableViewer, self).add_subset(subset) @property def window_title(self): if len(self.state.layers) > 0: return 'Table: ' + self.state.layers[0].layer.label else: return 'Table' def closeEvent(self, event): """ On close, Qt seems to scan through the entire model if the data set is big. To sidestep that, we swap out with a tiny data set before closing """ super(TableViewer, self).closeEvent(event) if self.model is not None: self.model._data = Data(x=[0]) event.accept() def get_layer_artist(self, cls, layer=None, layer_state=None): return cls(self, self.state, layer=layer, layer_state=layer_state) @staticmethod def update_viewer_state(rec, context): return update_table_viewer_state(rec, context) glueviz-1.0.1+dfsg.orig/glue/viewers/table/qt/__init__.py0000644000175000017500000000022313502206677022655 0ustar noahfxnoahfxfrom .data_viewer import TableViewer, TableLayerArtist # noqa def setup(): from glue.config import qt_client qt_client.add(TableViewer) glueviz-1.0.1+dfsg.orig/glue/viewers/table/qt/data_viewer.ui0000644000175000017500000000327713502206677023411 0ustar noahfxnoahfx Form 0 0 742 612 Form QAbstractItemView::NoEditTriggers QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows true 107 false true false TableViewWithSelectionSignal QTableView
glue.viewers.table.qt.data_viewer
glueviz-1.0.1+dfsg.orig/glue/viewers/table/__init__.py0000644000175000017500000000000013502206677022222 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/table/compat.py0000644000175000017500000000257213605357235021767 0ustar noahfxnoahfximport uuid from glue.viewers.common.state import LayerState STATE_CLASS = {} STATE_CLASS['TableLayerArtist'] = LayerState def update_table_viewer_state(rec, context): """ Given viewer session information, make sure the session information is compatible with the current version of the viewers, and if not, update the session information in-place. """ if '_protocol' not in rec: # Note that files saved with protocol < 1 have bin settings saved per # layer but they were always restricted to be the same, so we can just # use the settings from the first layer rec['state'] = {} rec['state']['values'] = {} rec.pop('properties') layer_states = [] for layer in rec['layers']: state_id = str(uuid.uuid4()) state_cls = STATE_CLASS[layer['_type'].split('.')[-1]] state = state_cls(layer=context.object(layer.pop('layer'))) for prop in ('visible', 'zorder'): value = layer.pop(prop) value = context.object(value) setattr(state, prop, value) context.register_object(state_id, state) layer['state'] = state_id layer_states.append(state) list_id = str(uuid.uuid4()) context.register_object(list_id, layer_states) rec['state']['values']['layers'] = list_id glueviz-1.0.1+dfsg.orig/glue/viewers/image/0000755000175000017500000000000013752535025020115 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/image/tests/0000755000175000017500000000000013752535025021257 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/image/tests/test_pixel_selection_subset_state.py0000644000175000017500000000575513605357235030661 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import pytest import numpy as np from numpy.testing import assert_array_equal from glue.core.data import Data from glue.core.data_collection import DataCollection from glue.core.link_helpers import LinkSame, MultiLink from glue.core.exceptions import IncompatibleAttribute from ..pixel_selection_subset_state import PixelSubsetState def test_pixel_selection_subset_state(): data1 = Data(x=np.ones((2, 4, 3))) data2 = Data(y=np.ones((4, 3, 2))) data3 = Data(z=np.ones((2, 3))) y_id = data2.main_components[0] z_id = data3.main_components[0] slice_1d = [slice(1, 2), slice(None), slice(None)] slice_2d = [slice(None), slice(2, 3), slice(1, 2)] slice_3d = [slice(1, 2), slice(2, 3), slice(1, 2)] state_1d = PixelSubsetState(data1, slice_1d) state_2d = PixelSubsetState(data1, slice_2d) state_3d = PixelSubsetState(data1, slice_3d) states = [state_1d, state_2d, state_3d] dc = DataCollection([data1, data2, data3]) # Calling to_array with reference data should work by default, and not work # with unlinked datasets. for data in dc: for state in states: cid = data.main_components[0] if data is data1: assert_array_equal(state.to_array(data, cid), data[cid][state.slices]) else: with pytest.raises(IncompatibleAttribute): state.to_array(data, cid) # Add one set of links dc.add_link(LinkSame(data1.pixel_component_ids[0], data2.pixel_component_ids[2])) dc.add_link(LinkSame(data1.pixel_component_ids[0], data3.pixel_component_ids[0])) assert_array_equal(state_1d.to_array(data2, y_id), data2[y_id][:, :, 1:2]) with pytest.raises(IncompatibleAttribute): state_2d.to_array(data2, y_id) with pytest.raises(IncompatibleAttribute): state_3d.to_array(data2, y_id) assert_array_equal(state_1d.to_array(data3, z_id), data3[z_id][1:2]) with pytest.raises(IncompatibleAttribute): state_2d.to_array(data3, z_id) with pytest.raises(IncompatibleAttribute): state_3d.to_array(data3, z_id) # Add links with multiple components, in this case linking two cids with two cids def forwards(x, y): return x + y, x - y def backwards(x, y): return 0.5 * (x + y), 0.5 * (x - y) dc.add_link(MultiLink(data1.pixel_component_ids[1:], data2.pixel_component_ids[:2], forwards=forwards, backwards=backwards)) assert_array_equal(state_1d.to_array(data2, y_id), data2[y_id][:, :, 1:2]) assert_array_equal(state_2d.to_array(data2, y_id), data2[y_id][2:3, 1:2, :]) assert_array_equal(state_3d.to_array(data2, y_id), data2[y_id][2:3, 1:2, 1:2]) assert_array_equal(state_1d.to_array(data3, z_id), data3[z_id][1:2]) with pytest.raises(IncompatibleAttribute): state_2d.to_array(data3, z_id) with pytest.raises(IncompatibleAttribute): state_3d.to_array(data3, z_id) glueviz-1.0.1+dfsg.orig/glue/viewers/image/tests/__init__.py0000644000175000017500000000000013455362716023363 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/image/tests/test_composite_array.py0000644000175000017500000001157513605357235026103 0ustar noahfxnoahfxfrom unittest.mock import MagicMock import numpy as np from numpy.testing import assert_allclose from matplotlib.pyplot import cm from ..composite_array import CompositeArray class TestCompositeArray(object): def setup_method(self, method): self.array1 = np.array([[2.0, 1.0], [0.0, 0.0]]) self.array2 = np.array([[np.nan, 1.0], [0.0, 0.0]]) self.array3 = np.array([[0.0, 0.0], [1.0, 0.0]]) self.array4 = np.array([[0.0, 0.0], [0.0, 1.0]]) self.composite = CompositeArray() self.default_bounds = [(0, 1, 2), (0, 1, 2)] def test_shape_size_ndim_dtype(self): assert self.composite.shape is None assert self.composite.size is None assert self.composite.ndim == 2 # for now, this is hard-coded assert self.composite.dtype is np.float # for now, this is hard-coded self.composite.allocate('a') self.composite.set('a', array=self.array1) assert self.composite.shape == (2, 2) assert self.composite.size == 4 assert self.composite.ndim == 2 assert self.composite.dtype is np.float def test_shape_function(self): array_func = MagicMock() array_func.return_value = None assert self.composite.shape is None self.composite.allocate('a') self.composite.set('a', array=array_func) assert self.composite.shape is None array_func.return_value = self.array1 assert self.composite.shape == (2, 2) def test_cmap_blending(self): self.composite.allocate('a') self.composite.allocate('b') self.composite.set('a', zorder=0, visible=True, array=self.array1, color=cm.Blues, clim=(0, 2)) self.composite.set('b', zorder=1, visible=True, array=self.array2, color=cm.Reds, clim=(0, 1)) # Determine expected result for each layer individually in the absence # of transparency expected_a = np.array([[cm.Blues(1.), cm.Blues(0.5)], [cm.Blues(0.), cm.Blues(0.)]]) expected_b = np.array([[cm.Reds(0.), cm.Reds(1.)], [cm.Reds(0.), cm.Reds(0.)]]) # If both layers have alpha=1, the top layer should be the only one visible assert_allclose(self.composite(bounds=self.default_bounds), expected_b) # If the top layer has alpha=0, the bottom layer should be the only one visible self.composite.set('b', alpha=0.) assert_allclose(self.composite(bounds=self.default_bounds), expected_a) # If the top layer has alpha=0.5, the result should be an equal blend of each self.composite.set('b', alpha=0.5) assert_allclose(self.composite(bounds=self.default_bounds), 0.5 * (expected_b + expected_a)) def test_color_blending(self): self.composite.allocate('a') self.composite.allocate('b') self.composite.set('a', zorder=0, visible=True, array=self.array1, color=(0, 0, 1, 1), clim=(0, 2)) self.composite.set('b', zorder=1, visible=True, array=self.array2, color=(1, 0, 0, 1), clim=(0, 1)) # Determine expected result for each layer individually in the absence # of transparency expected_a = np.array([[(0, 0, 1, 1), (0, 0, 0.5, 1)], [(0, 0, 0, 1), (0, 0, 0, 1)]]) expected_b = np.array([[(0, 0, 0, 1), (1, 0, 0, 1)], [(0, 0, 0, 1), (0, 0, 0, 1)]]) # In this mode, the zorder shouldn't matter, and if both layers have # alpha=1, we should see a normal blend of the colors assert_allclose(self.composite(bounds=self.default_bounds), np.maximum(expected_a, expected_b)) # If the top layer has alpha=0, the bottom layer should be the only one visible self.composite.set('b', alpha=0.) assert_allclose(self.composite(bounds=self.default_bounds), expected_a) # If the top layer has alpha=0.5, the result should have self.composite.set('b', alpha=0.5) assert_allclose(self.composite(bounds=self.default_bounds), np.maximum(expected_a, expected_b * 0.5)) def test_deallocate(self): self.composite.allocate('a') self.composite.set('a', array=self.array1, color='1.0', clim=(0, 2)) assert self.composite.shape == (2, 2) expected = np.ones(4) * self.array1[:, :, np.newaxis] / 2. expected[:, :, 3] = 1 assert_allclose(self.composite(bounds=self.default_bounds), expected) self.composite.deallocate('a') assert self.composite.shape is None assert self.composite(bounds=self.default_bounds) is None def test_noactive(self): array = np.random.random((100, 100)) self.composite.allocate('a') self.composite.set('a', array=array, visible=False) assert self.composite() is None glueviz-1.0.1+dfsg.orig/glue/viewers/image/tests/test_state.py0000644000175000017500000003700213661230662024010 0ustar noahfxnoahfximport pytest import numpy as np from numpy.testing import assert_equal from glue.core import Data, DataCollection from glue.core.coordinates import Coordinates, IdentityCoordinates from glue.core.link_helpers import LinkSame from glue.core.exceptions import IncompatibleDataException, IncompatibleAttribute from ..state import ImageViewerState, ImageLayerState, AggregateSlice class SimpleCoordinates(Coordinates): def __init__(self): super().__init__(pixel_n_dim=3, world_n_dim=3) def pixel_to_world_values(self, *args): return tuple([2.5 * p for p in args]) def world_to_pixel_values(self, *args): return tuple([0.4 * w for w in args]) @property def axis_correlation_matrix(self): matrix = np.zeros((self.world_n_dim, self.pixel_n_dim), dtype=bool) matrix[2, 2] = True matrix[0:2, 0:2] = True return matrix class TestImageViewerState(object): def setup_method(self, method): self.state = ImageViewerState() def test_pixel_world_linking(self): data = Data(label='data', x=[[1, 2], [3, 4]], y=[[5, 6], [7, 8]], coords=IdentityCoordinates(n_dim=2)) layer_state = ImageLayerState(layer=data, viewer_state=self.state) self.state.layers.append(layer_state) w1, w2 = data.world_component_ids p1, p2 = data.pixel_component_ids self.state.reference_data = data # Setting world components should set the pixel ones self.state.x_att_world = w1 self.state.y_att_world = w2 assert self.state.x_att is p1 assert self.state.y_att is p2 # Setting one component to the same as the other should trigger the other # to flip to prevent them from both being the same self.state.x_att_world = w2 assert self.state.x_att is p2 assert self.state.y_att is p1 assert self.state.y_att_world is w1 self.state.y_att_world = w2 assert self.state.x_att is p1 assert self.state.x_att_world is w1 assert self.state.y_att is p2 # Changing x_att and y_att should change the world equivalents self.state.x_att = p2 assert self.state.x_att_world is w2 assert self.state.y_att is p1 assert self.state.y_att_world is w1 self.state.y_att = p2 assert self.state.y_att_world is w2 assert self.state.x_att is p1 assert self.state.x_att_world is w1 class TestSlicingAggregation(): def setup_method(self, method): self.viewer_state = ImageViewerState() self.data = Data(x=np.ones((3, 4, 5, 6, 7))) self.layer_state = ImageLayerState(layer=self.data, viewer_state=self.viewer_state) self.viewer_state.layers.append(self.layer_state) self.p = self.data.pixel_component_ids def test_default(self): # Check default settings assert self.viewer_state.x_att == self.p[4] assert self.viewer_state.y_att == self.p[3] assert self.viewer_state.slices == (0, 0, 0, 0, 0) assert self.layer_state.get_sliced_data().shape == (6, 7) def test_flipped(self): # Make sure slice is transposed if needed self.viewer_state.x_att = self.p[3] self.viewer_state.y_att = self.p[4] assert self.viewer_state.slices == (0, 0, 0, 0, 0) assert self.layer_state.get_sliced_data().shape == (7, 6) def test_slice_preserved(self): # Make sure slice stays the same if changing attributes self.viewer_state.slices = (1, 3, 2, 5, 4) self.viewer_state.x_att = self.p[2] self.viewer_state.y_att = self.p[4] assert self.viewer_state.slices == (1, 3, 2, 5, 4) assert self.viewer_state.wcsaxes_slice == ['y', 5, 'x', 3, 1] assert self.layer_state.get_sliced_data().shape == (7, 5) self.viewer_state.x_att = self.p[2] self.viewer_state.y_att = self.p[1] assert self.viewer_state.slices == (1, 3, 2, 5, 4) assert self.viewer_state.wcsaxes_slice == [4, 5, 'x', 'y', 1] assert self.layer_state.get_sliced_data().shape == (4, 5) self.viewer_state.x_att = self.p[0] self.viewer_state.y_att = self.p[4] assert self.viewer_state.slices == (1, 3, 2, 5, 4) assert self.viewer_state.wcsaxes_slice == ['y', 5, 2, 3, 'x'] assert self.layer_state.get_sliced_data().shape == (7, 3) def test_aggregation(self): # Check whether using AggregateSlice works slc1 = AggregateSlice(slice(None), 0, np.mean) slc2 = AggregateSlice(slice(2, 5), 3, np.sum) self.viewer_state.slices = (slc1, 3, 2, slc2, 4) self.viewer_state.x_att = self.p[2] self.viewer_state.y_att = self.p[4] assert self.viewer_state.slices == (slc1, 3, 2, slc2, 4) assert self.viewer_state.wcsaxes_slice == ['y', 3, 'x', 3, 0] result = self.layer_state.get_sliced_data() assert result.shape == (7, 5) assert_equal(result, 3) # sum along 3 indices in one of the dimensions class TestReprojection(): def setup_method(self, method): self.data_collection = DataCollection() self.array = np.arange(3024).reshape((6, 7, 8, 9)) # The reference dataset. Shape is (6, 7, 8, 9). self.data1 = Data(x=self.array, coords=IdentityCoordinates(n_dim=4)) self.data_collection.append(self.data1) # A dataset with the same shape but not linked. Shape is (6, 7, 8, 9). self.data2 = Data(x=self.array) self.data_collection.append(self.data2) # A dataset with the same number of dimesnions but in a different # order, linked to the first. Shape is (9, 7, 6, 8). self.data3 = Data(x=np.moveaxis(self.array, (3, 1, 0, 2), (0, 1, 2, 3))) self.data_collection.append(self.data3) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[0], self.data3.pixel_component_ids[2])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[1], self.data3.pixel_component_ids[1])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[2], self.data3.pixel_component_ids[3])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[3], self.data3.pixel_component_ids[0])) # A dataset with fewer dimensions, linked to the first one. Shape is # (8, 7, 6) self.data4 = Data(x=self.array[:, :, :, 0].transpose()) self.data_collection.append(self.data4) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[0], self.data4.pixel_component_ids[2])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[1], self.data4.pixel_component_ids[1])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[2], self.data4.pixel_component_ids[0])) # A dataset with even fewer dimensions, linked to the first one. Shape # is (8, 6) self.data5 = Data(x=self.array[:, 0, :, 0].transpose()) self.data_collection.append(self.data5) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[0], self.data5.pixel_component_ids[1])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[2], self.data5.pixel_component_ids[0])) # A dataset that is not on the same pixel grid and requires reprojection self.data6 = Data() self.data6.coords = SimpleCoordinates() self.array_nonaligned = np.arange(60).reshape((5, 3, 4)) self.data6['x'] = np.array(self.array_nonaligned) self.data_collection.append(self.data6) self.data_collection.add_link(LinkSame(self.data1.world_component_ids[0], self.data6.world_component_ids[1])) self.data_collection.add_link(LinkSame(self.data1.world_component_ids[1], self.data6.world_component_ids[2])) self.data_collection.add_link(LinkSame(self.data1.world_component_ids[2], self.data6.world_component_ids[0])) self.viewer_state = ImageViewerState() self.viewer_state.layers.append(ImageLayerState(viewer_state=self.viewer_state, layer=self.data1)) self.viewer_state.layers.append(ImageLayerState(viewer_state=self.viewer_state, layer=self.data2)) self.viewer_state.layers.append(ImageLayerState(viewer_state=self.viewer_state, layer=self.data3)) self.viewer_state.layers.append(ImageLayerState(viewer_state=self.viewer_state, layer=self.data4)) self.viewer_state.layers.append(ImageLayerState(viewer_state=self.viewer_state, layer=self.data5)) self.viewer_state.layers.append(ImageLayerState(viewer_state=self.viewer_state, layer=self.data6)) self.viewer_state.reference_data = self.data1 def test_default_axis_order(self): # Start off with a combination of x/y that means that only one of the # other datasets will be matched. self.viewer_state.x_att = self.data1.pixel_component_ids[3] self.viewer_state.y_att = self.data1.pixel_component_ids[2] self.viewer_state.slices = (3, 2, 4, 1) image = self.viewer_state.layers[0].get_sliced_data() assert_equal(image, self.array[3, 2, :, :]) with pytest.raises(IncompatibleAttribute): self.viewer_state.layers[1].get_sliced_data() image = self.viewer_state.layers[2].get_sliced_data() assert_equal(image, self.array[3, 2, :, :]) with pytest.raises(IncompatibleDataException): self.viewer_state.layers[3].get_sliced_data() with pytest.raises(IncompatibleDataException): self.viewer_state.layers[4].get_sliced_data() def test_transpose_axis_order(self): # Next make it so the x/y axes correspond to the dimensions with length # 6 and 8 which most datasets will be compatible with, and this also # requires a tranposition. self.viewer_state.x_att = self.data1.pixel_component_ids[0] self.viewer_state.y_att = self.data1.pixel_component_ids[2] self.viewer_state.slices = (3, 2, 4, 1) image = self.viewer_state.layers[0].get_sliced_data() print(image.shape) assert_equal(image, self.array[:, 2, :, 1].transpose()) with pytest.raises(IncompatibleAttribute): self.viewer_state.layers[1].get_sliced_data() image = self.viewer_state.layers[2].get_sliced_data() print(image.shape) assert_equal(image, self.array[:, 2, :, 1].transpose()) image = self.viewer_state.layers[3].get_sliced_data() assert_equal(image, self.array[:, 2, :, 0].transpose()) image = self.viewer_state.layers[4].get_sliced_data() assert_equal(image, self.array[:, 0, :, 0].transpose()) def test_transpose_axis_order_view(self): # As for the previous test, but this time with a view applied self.viewer_state.x_att = self.data1.pixel_component_ids[0] self.viewer_state.y_att = self.data1.pixel_component_ids[2] self.viewer_state.slices = (3, 2, 4, 1) view = [slice(1, None, 2), slice(None, None, 3)] image = self.viewer_state.layers[0].get_sliced_data(view=view) assert_equal(image, self.array[::3, 2, 1::2, 1].transpose()) with pytest.raises(IncompatibleAttribute): self.viewer_state.layers[1].get_sliced_data(view=view) image = self.viewer_state.layers[2].get_sliced_data(view=view) print(image.shape) assert_equal(image, self.array[::3, 2, 1::2, 1].transpose()) image = self.viewer_state.layers[3].get_sliced_data(view=view) assert_equal(image, self.array[::3, 2, 1::2, 0].transpose()) image = self.viewer_state.layers[4].get_sliced_data(view=view) assert_equal(image, self.array[::3, 0, 1::2, 0].transpose()) def test_reproject(self): # Test a case where the data needs to actually be reprojected # As for the previous test, but this time with a view applied self.viewer_state.x_att = self.data1.pixel_component_ids[0] self.viewer_state.y_att = self.data1.pixel_component_ids[2] self.viewer_state.slices = (3, 2, 4, 1) view = [slice(1, None, 2), slice(None, None, 3)] actual = self.viewer_state.layers[5].get_sliced_data(view=view) # The data to be reprojected is 3-dimensional. The axes we have set # correspond to 1 (for x) and 0 (for y). The third dimension of the # data to be reprojected should be sliced. This is linked with the # second dimension of the original data, for which the slice index is # 2. Since the data to be reprojected has coordinates that are 2.5 times # those of the reference data, this means the slice index should be 0.8, # which rounded corresponds to 1. expected = self.array_nonaligned[:, :, 1] # Now in the frame of the reference data, the data to show are indices # [0, 3] along x and [1, 3, 5, 7] along y. Applying the transformation, # this gives values of [0, 1.2] and [0.4, 1.2, 2, 2.8] for x and y, # and rounded, this gives [0, 1] and [0, 1, 2, 3]. As a reminder, in the # data to reproject, dimension 0 is y and dimension 1 is x expected = expected[:4, :2] # Let's make sure this works! assert_equal(actual, expected) def test_too_many_dimensions(self): # If we change the reference data, then the first dataset won't be # visible anymore because it has too many dimensions self.viewer_state.reference_data = self.data4 with pytest.raises(IncompatibleAttribute): self.viewer_state.layers[0].get_sliced_data() self.viewer_state.reference_data = self.data6 with pytest.raises(IncompatibleAttribute): self.viewer_state.layers[0].get_sliced_data() def test_update_x_att_and_y_att(): # Regression test for a bug that caused y_att to not be updated before # events were sent out about x_att changing. viewer_state = ImageViewerState() data1 = Data(x=np.ones((3, 4, 5))) layer_state1 = ImageLayerState(layer=data1, viewer_state=viewer_state) viewer_state.layers.append(layer_state1) data2 = Data(x=np.ones((3, 4, 5))) layer_state2 = ImageLayerState(layer=data2, viewer_state=viewer_state) viewer_state.layers.append(layer_state2) def check_consistency(*args, **kwargs): # Make sure that x_att and y_att are always for same dataset assert viewer_state.x_att.parent is viewer_state.y_att.parent viewer_state.add_global_callback(check_consistency) viewer_state.add_callback('x_att', check_consistency) viewer_state.add_callback('y_att', check_consistency) viewer_state.add_callback('x_att_world', check_consistency) viewer_state.add_callback('y_att_world', check_consistency) viewer_state.add_callback('slices', check_consistency) viewer_state.reference_data = data1 assert viewer_state.x_att is data1.pixel_component_ids[2] assert viewer_state.y_att is data1.pixel_component_ids[1] viewer_state.reference_data = data2 assert viewer_state.x_att is data2.pixel_component_ids[2] assert viewer_state.y_att is data2.pixel_component_ids[1] glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/0000755000175000017500000000000013752535025020541 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/0000755000175000017500000000000013752535025021703 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/test_python_export.py0000644000175000017500000000651213752534424026244 0ustar noahfxnoahfximport numpy as np import matplotlib.pyplot as plt from astropy.utils import NumpyRNGContext from glue.core import Data, DataCollection from glue.app.qt.application import GlueApplication from glue.viewers.image.qt import ImageViewer from glue.viewers.matplotlib.qt.tests.test_python_export import BaseTestExportPython class TestExportPython(BaseTestExportPython): def setup_method(self, method): with NumpyRNGContext(12345): self.data = Data(cube=np.random.random((30, 50, 20))) self.data_collection = DataCollection([self.data]) self.app = GlueApplication(self.data_collection) self.viewer = self.app.new_data_viewer(ImageViewer) self.viewer.add_data(self.data) # FIXME: On some platforms, using an integer label size # causes some of the labels to be non-deterministically # shifted by one pixel, so we pick a non-round font size # to avoid this. self.viewer.state.x_ticklabel_size = 8.21334111 self.viewer.state.y_ticklabel_size = 8.21334111 def teardown_method(self, method): self.viewer.close() self.viewer = None self.app.close() self.app = None def assert_same(self, tmpdir, tol=0.1): BaseTestExportPython.assert_same(self, tmpdir, tol=tol) def test_simple(self, tmpdir): self.assert_same(tmpdir) def test_simple_legend(self, tmpdir): self.viewer.state.show_legend = True self.assert_same(tmpdir) def test_simple_att(self, tmpdir): self.viewer.state.x_att = self.data.pixel_component_ids[1] self.viewer.state.y_att = self.data.pixel_component_ids[0] self.assert_same(tmpdir) def test_simple_visual(self, tmpdir): self.viewer.state.legend.visible = True self.viewer.state.layers[0].cmap = plt.cm.RdBu self.viewer.state.layers[0].v_min = 0.2 self.viewer.state.layers[0].v_max = 0.8 self.viewer.state.layers[0].stretch = 'sqrt' self.viewer.state.layers[0].stretch = 'sqrt' self.viewer.state.layers[0].contrast = 0.9 self.viewer.state.layers[0].bias = 0.6 self.assert_same(tmpdir) def test_slice(self, tmpdir): self.viewer.state.x_att = self.data.pixel_component_ids[1] self.viewer.state.y_att = self.data.pixel_component_ids[0] self.viewer.state.slices = (2, 3, 4) self.assert_same(tmpdir) def test_aspect(self, tmpdir): self.viewer.state.aspect = 'auto' self.assert_same(tmpdir) def test_subset(self, tmpdir): self.data_collection.new_subset_group('mysubset', self.data.id['cube'] > 0.5) self.assert_same(tmpdir) def test_subset_legend(self, tmpdir): self.viewer.state.legend.visible = True self.data_collection.new_subset_group('mysubset', self.data.id['cube'] > 0.5) self.assert_same(tmpdir, tol=0.15) # transparency and such def test_subset_slice(self, tmpdir): self.data_collection.new_subset_group('mysubset', self.data.id['cube'] > 0.5) self.test_slice(tmpdir) def test_subset_transposed(self, tmpdir): self.viewer.state.x_att = self.data.pixel_component_ids[0] self.viewer.state.y_att = self.data.pixel_component_ids[1] self.data_collection.new_subset_group('mysubset', self.data.id['cube'] > 0.5) self.assert_same(tmpdir) glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/__init__.py0000644000175000017500000000000013455362716024007 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/baseline/0000755000175000017500000000000013752535025023465 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/baseline/test_resample_on_zoom.png0000644000175000017500000051364613502206677030622 0ustar noahfxnoahfxPNG  IHDRt sBIT|d pHYsԂb IDATxgSU[׭}Q2 "APPq$Q欘@1gEE(؂F@QI$gH())ѣGPSSeee )**А줡yyys@߿ؘ_"!!^OOFCOOO|O?Ȉvˑ###ѣAee%())a``@~~>]]](((BUU477#Ԥtuu'BmmmZZZ]WWGss3]]]PZZ߾}_~GKK Eyy9zUUU$$$hkkC$Ԅhkkjjj***PTT}}}Z[[caaAaa!z~dgg#++>tuuQ\\H$BWW2(.. ~HIIASS"P2JJJBCC}E$FYY괴AII YYY*++ꢱ6z쉒455!--gΜ!##-ZӧOYh;v7o'XXXpA&MDgg'}!77 &qFrssٿ?477# =z4zbʔ)ȰuV-[FII fĉ+ ܜaÆkf͚Eqq1AAA}ٳg&ոN~~>_̷o1b())~z  E(2dۙ9s&zzzl۶G1m4tuu9s >>>SQQ-wwwY`fʔ)l߾eeeƏC ɉ72sLnܸܹsqvv֖w1vXvލ%^^^dffݻѡ---ƍGPP[n///~Aee%ΝCMMFVXʕ+QWWۛ|9B^|$^999-[Fvv6"ԩSlٲ)))~zpqq‚aÆQXXHmm-sΥ;v,VB ;v@VVƍɇXb#))7k֬!66.=JQQJJJ(++={DQQf̘Aff&&&&Y{{{87o eԩtuuqJKK777JKKY|9Gfȑ < X~=>}pvvf,];w3 ߿ޞիW`:;;)++ڵkHIIի ͛7imm%##דƩSX`RRRsa>} 8iiiqssctttzjZZZ"<<<8|0ՋR?>[nsssvhooGYYE/fΜAp߿)S>|8>>>8;;cbbBmm-qqqHJJRUU^^^R]]͋/HKKC!!![hllDMM/˗/CZZdee ::<&L@ee%fffZxLLL}6hjjeoN{{;ӧOG(riV^IMMp֬YCKK QQQ455˵k ŋaccC{{;222 <߿3w\222!==555*++9~8fffߟ6޽1у}ٳ{{{.\@kk+3p@RRRX|96l`ҥ҂;DEEC߾}s---J^^ܸq###رc***hhh ޽aڵDEE舉 %%%xzzrA֯_ϴi ̝;t$%%?Ǐlذ+Wra;7=B m6jkkٸq#Ϟ=Ǐr8q"L8W^k._>>|'O'yرcdԨQL8k׮affF>}pwwߟ`fΜɀx9666x񂨨( ľ}pqqܹsx{{3ydzL8rJrss̜9G"//ϩS';;ׯ_3x`jjjxǏc޽r%-[O>%..L6oׯ&))`jkkׯfff477ͽ{֭[!C÷o033Nyy9<@__}# BYYC >999سg ._R\\7\vl055eȑL45kP\\L\\k׮ø@rr2Bdeey&F ޽| F`` =z֭[lܸ^zN^^UUU 4O1b-OZZZ III8s Æ CJJ1cưa:t(ZZZꢫ-B$/^[BNN|{FVVK,᯿bXYYq}#y:O>eŊ|6n܈:u {{{jkkQWWg\~PϟL8#G?Chh(=Ϗ3gcKhh(,Y֭[ NNN\vm۶1m4CMMlllhjj 06oLHH~~~tttp5&''fy%gҥKgή]x5144DGG---xo&55IJJbĉh֮]=())Lmm-R\\>>>ܿB!accCgg'RRR 2$$ !CBFFRRR;;;6l222|___߿Ͼ}>ꢺ|9{,Ϟ=cٲecffÆ cΝ.7bccQTTׯ_D\\ αc000@[[1o<455=SSS틧'455zjҰ۷bhhHjj**=zĤI000`ԩ455accCmm-Gʕ+;}ѣϬ3f`mmMBBYYY,ӻ1cֆ:::l۶ HDFFKQQŤJZZRRR1h ?Α#Gċjjj\z߿s޿7"@˗/ ׯ_S^^.$ $--J<==)++А>}0`6nȶm˗/7gϞDFF2x`JKK8p ̞=[?| &ѣG9<<[[[dС<\  alݺ'N0n8N8&^^^̙3777>|yQQQL4 ''':::b֬YDFFrYqϥ@@EEJ5)===&N… QQQ!>>[nALL qqq$&&b``͛gڴih"fϞ&Mk2}t,--ill$..sss 0220ٽ{7hkkׯYhϟ',,zyׯ_޽{+++:::ظq#6mӓ4㱲"..Çsaptt$%%}}}c#++K`` _F$xbvIUUΝ#33{& tfΜߞ:u*NbСRSSbҤIX0`혚RPPP($77)SpYϟOCC...DDD!nEDD؏nk׮ԩSݻw=₩)wF ۷x"ⅸ#FPTT>K,aҥ,Z &ʼy6mAAA|$={Wddds455Ņ'OsIx=VBUUVN>۷3gcʕHJJ=$''cjjJ\\fffhkkՅ [[[3f ZZZajj4###Ï?Ɔdill˗/p`` XXXPRR€& @l)1zh|`޼yxyy̹sXr%***ܽ{ DL2:xB:QVV7 '3f̠?~0vkv葑L<ɓ'SPP@޽ݻ7";vrJTUUg̘1XsqvvFUUxW^%((|bbbX`#G۷oƊG1eIIIAWWD޽?y!'OԩS̝;PH$">>''' ** MMMHFFǏG(ԩSٰa HJJruә;w. 0'Ni&zIJJ BBB?d֬Y`lly|ppp| HLLd̘1 > Μ9)))7o`ll̷oXx1DCCCСCIHH`ɒ%_EӣCˉ'zx!y= SUUŝ;w8x ܹ< 6l999BUU111<~}}}郅 HJJbɒ%|bbbҥK˓:::X[[c4hΝݝ$$$ 7nԔ,V^XZZ"!!i8<Ϟ=Ǐ`bb7X|9"++Kuu5~~~)NU}/_HSS {{{m֭[G__Ǐ#8p Ʉ `ȑ :777U9ՋG3ؾ};|CCCكV˗/ׯ_`kkoDGGChh(JJJx{{}vݙ8q"\|ɓ'sN>|~`bbԩSѣ ߿KNNwˋ x%)))y& Ɔsαxb122֖GƍeȑL0N>y& ڵk,XjjjhjjyyyXbFFFzZYh<\͛7̜9aÆILL3rHjjj&<<\ܠÇW#SL///OliiItt4 133@1x"+Vŋb˖-0|pd̙b833u֡ɓ'pww֖>}pHLL[YYYBƏϖ-[x)ׯg̙߿A! ߿̙3l۶zf͚ř3g5k蠬ɓ' ARR#G l̙Cff&WqݻwoZZZ#::---͛ñ-[;bܹxC{{;1~xrssYlFFF燡xGg ʕ+#;;@ӧ&L͛:t133ܹs>}---rrr?>Æ #>>llla…DKBB̟?<i-[HYYCx ,tuuq"##/:dgg#))IBB9997EEEIKKC___HIIq=v `_ >[laٲeS[[ܸq???󣶶{{{ ӧOӧqAPSSɉߓBPB D\]]s>d x)ϟyyy;h IDATׯ666j'--}FFF(((ix1&&&̙3UUUrssbժUKzz:ZZZl2QPPA\U-FFF  g֬Y}:SNEUUӧettt3^3g0c V^MFFKKK\\\GSSp177'%%CCCy3f̠gϞ␕%((###ŋYz5顮ׯիL>cdddHJJɉׯ_ٳF<==dȐ!ǜ9sx!?DUU=z0i$qww$$$pttzIEEqqq\|*_n̙33p@._̮]HOO֖. <{޼yC^^eee9R=z4 <SSSbcc2e (++s=tttصkL6Ml622]vH||~L4]vQSS)**bС0almmbԩS{GGGΝӧ6m`ffF޽gҤIo>Ο?) ѣG?> :rrr`رl۶ ]]]ݻ(**ۛ?NA?qsttt999ܾ}s璕şYv-cƌ!$$4||| ʹiӰȑ#"())AEEEHHHی7@TUU>|8߹~:3c KvvyX\\ 666{n222>'N$55###͛GZZē'O(--%>>_qssX[[_qux'̚5 6l@zz:7 Gӧ3f \pbٻw/cǎE$ʵk9r$Q__Opp0Jcc#rrrtvvr/_2h N</͛70j(|}}D[[RzIqq1***lٲ### @ `֬YHHHPRR7oޤ7np-RRRHJJB$IYY˗/А3gpi֬Yógϸz*fgg' ,@QQ :;;y1yyy\p4/^H$b˖-_AA ^zEQQx{{3uTqnzر:ur6lpo޼͍{qx ? &p!l‰'000WWWL:C}v={6|?~Nuu8LУGH߾}ӧϞ=Q\U\z%%%233Yr%K.%""|۷3zhٳ'GGGq˗/Z.dee4h˗/'//Ǐ+;OOO`III}eeeF%jg˗/TTTп9{,{ ''HĬY033#%%|sM ˋ0f̘A>}غu+6mBSSZOϞ=y1G&??kkk233ҥK 6 ---Xt)1~xallLDD())쌜"7ncK,2J 5h߿O=ӧl޼9sPZZRLLLhjjǏa&?~3***QZZJLL؃aРA:u y&ʶm8tϟ?ߟV\ɥKHNNȔ)Sӧ0p@\8Z[[X[[SSS/_GAKK 2k,~IAAٳ'ܸq߿ܺuϟWpppԔѣGwjjj=z͛7s Xn;wСC\|PPPÃu֡ǏYf ',,s֭[hkkA||<ǏחDdeeIMM%773fgۇ7˗/gӦM1_d۩dԩ,_`֬Y۷oĄǏcooODDǏlll8{ⶴ Ă ?u?~ ??///)**ܹs,[ jkk͍ϟ?C[[+WQ| 7III# qttd۶m\tT6nȆ ppp@QQ'Oڵk`p]ӧO˗ΎÇ{={ƈ#PQQxyyOLLL~:SN͛7̤I߿?dddKR***"--@HMME[["2{l<==y[( m۶cFСC9}4`hhHϞ=9y1b&&& <}ѣG~@ ޞBJWWʔKBBdeeѫW/Yn8Ydd$---"xx qnʕXXXPYYɘ1cgTTTpan޼IJJ YYY̘1bV\I=x-2221=zp58q"vZuܽ{޽{ӳgO֭[AggW^aee+.]Ãŋɓc$***?rР ~ׯ_qqq Y`YYYp=ddd Ę1cĶ4ܾ}CCCxxǏDFFՅ*߿gxyyD}}=gf…xb'1|Bvv6 w݋|`VZEDDLvə3gՕ?߿O۷ <ȫWHJJ"99jkk?~>bf͚ȑ#illd޽|?2|pvލ?~ӧǗ/_4hvvv{N|8wD, ,Z7o ##CVVahlld˖-ߟ{Sjkkɡ߿MAA999 ##27oΎW^̐!C1b"ߋG\oH(KGG111XZZAgg'渻F}}8*۷oqqq!--m۶qi  Bqaii.JJJӧORSSy=mmmΜ9' Q555,--ɓ 8)))~ͻw ޼y/ό;ߋu1cXZZboo8H`ggGCCYYY<}v;pm"##yUUUܽ{sUb ܹeee>}ٺu+nnnOfС<|&  BAA000<==1b 3j(ܜ;wDVV3g$33X֮]K^^o޼AGGDlmmΎaÆQ^^NXXxn9r$zgϞ߿DLLL͍`>|Htt4%%%DDDnѣ˅\uĈBI,twwgtttׯQWWgj****pzzz\pÇ IIIؠǙ3gadffbhh>O}:222y_tRÃXHPPO>EWWL\\V N{ܹsHHHP]]MHH&::Zk/{/hkk666ٱsNrrr8}4Ϟ=ʕ+磧GEEK,IHH+VtR1ի쌫+<;G.tQUU,ڐ*U IDAT&..uuux$$$ƍIKKcÆ lݺccc ±c8q0tPRSSҥK*FFFjooGNN߿3yd9y$MMM8;;;222~{rY6o 8撒y%7QQQR)))aÆ HHHMrr2>}gϞƜ9s甕j*o… ߿se@MM LLLsɼ{ 9|0燏?~ <Ǐ ztt4ϟ?dӫW/"## :N>6}JKKАvlmmqww'66333tttקUESRRBVVaÆɂ 1c߿,Z???ZXXczPVV'NkɓIKKcʔ)>|߿o^x8p@~III9]v7nVVVɓ'ǚ5kԩSb9p@;&pmmmhiiaƌzݻw#!!`Ќ1 &P[[ׯ_PpqttwԄk֬ӧɛ7oTTTceeo^JSS-i&8{,ҸR]]M~~>;w̌BTUU}6BvqhnnҒ{1i$޽KϞ=Y~=K,AJJ$,,,CMM۷oSWWyjhh0oq ϟϵkPVVȑ#dgg3c ..\@RR___jkkill3grqQVVL8l ֭ӧOٙ/"//֭[y9˗/'''&L+W]+(--e۶m}5l޼Y:vvv --M@@H:u*=b㫧'555L0bii)=bڴitttp-455ҥ ***EѣG7o$''ѣG5jUUUƌCAA>}ԔC ҵkɨw\\\x=-BRR\\\x9:::ⴡS-XK.ѣGLBXX1c 9GEEq%n߾rrrKjjjߟ/_ү_?vAEEMMM!---vMsMMMl޼ǏS^^ɓ3f 7oߗ oiرcիWSUUٳ9s&,Z''',-->}:[n?!CSĈ/9" ݻwܹs)**bH}6ܿ9~8Oرclذ jjj())fʠA֭k׮eǎ)ڲe ttt˾6{ȑ#lݺlllHMMe֬Yz kkk>?&3386mDaaX .]TࢣٳgC(((ݝ'Oyf\BEE~wn˜1c055%;;ccc>|=")) IOOݝ GRR]v Dii){nY`#F ;;ׯ_ӥKdذa|[nK}}=^^^,\G2aݱTDur=ƍo޼ǏL0pʘ2e  s7o:::xyydy%#F &&srJˣK. 06055E]]]Ґ~%ϟ?g̘1|򅚚礧s%&Oƍ"IcnnRRR۷#FK 9}>}N|>}x`AJJ޽{sU޽{ƍ߿?VbBellѣׯ\zUpݻʕ+)//)Q塧Gnc޼yZJıߟ%KPZZʈ#رc+WΝ;ݛ?~Bxx8sE]]7np98pvvvܿsssʸwJJJHKKS^^N]] 4Ç{n̙悜w!gӓL;eee\\\(++J@L:!C ''*}ӧOtRMǏ7~x;ѯ_?V3m4>̏?pppZ;w)Bܹs߿xqd&33CENN"'K,̌/_bkkK@@&&&dggs1ƌ#gN||<\reeev˗/=z$ ~<ܼy7oPYY͛APt"9x }Xȝ;wGFF>̺uDe~ >$''^zORRTTT̆ 1bё߿ӽ{wZZZD ҥKCUU $%%ƍqe<==YjК/XիWy}I&n:|||w111̘1|}}100\lIII8p RRR 7nDt׮]ȈdٳgX[[yfַo_9Bee%]t᯿"--Yf닭-ddd"˖-cƍ'rssYbǏ^;;; ׯӧ{졾ӭ[76l@Ϟ=QTTӯ_?y ...ܹ7o2zhJJJ(++ȑ#Y[n1m4@MM @CCC8;||||2SLٳg888`ccÎ;W^䄟;v GGGV---֖}ĉy1#!!.>|?]&66XN~bذa̙3۷=z>},Dii)uuuTUU+RRRL4REA߻w/V0#ϑ#G ٙBCCINNG1|7ѣGrM4i={{{{t邷7?P122?HAAָW޽Khh(ϟ>|sy/^Lvv6_X(FŸq8|0<}#GPTT ߑ.6///;v,GMMvN82 @OOUUU ƤIDԙ?УG~͡C ?S%tttHOOgšƍAIIӧOSTTĐ!Cׯw ovƔ`kkˡChmm)@999+TVV ʕ+2d;v ;;;FW^k׮q!xM8x XZZb ޽b݋ 3gӧO?~p5j6l%%%ǏӧO<{ 555~7<ȑ#",9--_lܸse(**"33ѣGiӈ@^^x>~H^ĉD^ꎎ,xyyѻwo]XرG ȑ# رc?ϟOGGB|ǏsQ|}}f֬Y:twwwRRR8p mmm<|YYY}6QQQDGGӻwo$%%ի***,]Ll€4ifԩɓ'OD"K.ɉfHII[n(++cdd͛7fNjHHwޥW^qg{UVakkǎC]]#GN^^=z 99"9u$&&rJX|=z4ZZZ,\{{{.^h@~MLL ӧOGEE999֬Y#={˗qss_~yr"""ͥH\ 1/^Օ z职 'ODIIׯ_3sL޼y#B=&O̥KPUUeԨQڵPnܸAxx8vvv#''Nj/3gׯ'55---ٷoC^^ׯ_#++Khh(AAA|ٳgA޽y=Z,_xڵkIJJBEE s8|p8wHHH0sL>|߯w^{A߽{Iw'O燼<?~vZ[[:t@͝;?|IAA&L`ɒ%ٳGt`}}}ݻ7ųgԩSB@ZϟOZZlٲ[[[illȑ#,]@Yz5?˗/jt֍]XB͚5K}đ#G1c߿GNN*MFTTs}\Hkk+ǎ#88-[ ++ jjj 0@}044$11+WS_{ۗE.,,d̘1w^tttxY=zjZ[[kx IDATGGG}իWٳ'ڵkIIIaȐ!"JEEK.@VV ^[[̞=W^ѭ[7ٽ{7999}v Fss3NB[[%K0e틃9998;;Dyy9{x{{sN ͛#~z>~… EĜ9sgҤI^~M^^%%%455QZZ~~~?~K.Dź hkk yp'ڵk###=ʩSHIIA8ÇN,'O˗/lذG1x`155qqN͟qۇÆ cǎ8::rI"""BWW߿ODD <4f̘3gXbSLӧO$B.\( ,@AA{1rH^zŵkprr\zÇw^3//￿l 1#ZvJmm55g٥Jn8{,O>ܜݻ=cǎ qqqlذHr f̘!:&;Fqq1cǎ%>>Caeeży022bͬ['R{.x{{ʋ/5kNwﰳCZZ{{{lllҥ EEEb+...ddd F˗/3h ʗ/_}6:u eeeQQQXXX%flܸQ,8q"9e899 ~vvP7\|cǎѧOGRR $%%3f 477DNN>ӧOpUn޼Iaa!|ltuut455#޿;vϟIIIASS999:qvvA !KܹkYx1>|+WPYYĉihh`…L>TUUQWWݻw̞=._޽{~Ŋdgg3m4<<iooׄ0bg}ĠA(++`Æ љ;w.s1VPP`3sL.\@ii)222TTTũ֯_:fffDuu5퍹9zzzl2ʄzʸv7n!:R aѢETTTz\\$%%abb z"'''O74448;;En8~8>>>8;; XQee%xjMF݉GyuVpuueܹ";;wpEҩ7*QTTϟ Ο?ό38|0~tBRRuuuacc1b_t ]]]/MLMM)//]]ZZ*7n@VV}!++<ݺu#44;333zC1czzz466ϠAPQQׯ@KK ddd ''̞=2Q ݙ3gn ##e˖ :V(((K.l2~իW3//ht|}}QQQAAABqRRRx)YNNcccرc)))֭[GBB%%%lڴcNJ`AQQQb޽;׮]#$$bΟ?uXYYqU<<|gΜAFF(y9k׮%88 ).. 0ҥKٳG޼y#~_QQ^zq%Qknnfĉ|EȎsssڵ+riFJJJ9 *I7KMMG9r???"$I/3f TVVzj>Ν;j*#lFjj*+W%K˗-44>:ZVVPp})((`޼yLX]]Aq%>&LSSS=zDbb"l2,--ѣG3rHIMMKKKkwwwٳٳg>^ɓ>>Ell,TVV믿7o EEE푔DKK,8twFFFaÆ1rH;w.'Oɼyƍhkk3|pJJJ6!!-[Frr2➩ A#{tt4uuu n穩aܹhhhヹ9/_dРA <333aSQQaڵMnx->R^^N\\,\ooowxxx ++K׮]yׯ_1󣤤D444hIII#88XufꊐŋLTTag͍_܌# s8q;;;a`_~Gqq1zzzӇ|6mD``vtt{4447vvvSyyy$$$[ڈd{Ԉ]pss3&&&HHHF``𗤥1i$PSS#77W6v:JII1}t2d>}bʕDDDtR<{ IIIXv-gݺu b.M?{u]>/^Ú5kPUUɓHHHhMM 888>8piӦq޽{GKK w念xzIii0iiiQTTDPPjjj"$ݝy1j(ك7%%%X[[78+ǏO]]Æ CVVvŏ?8q" 4O>CmFDDlڴ Μ9[ڵ+n[[[BCCFFF߿QPP@FFB޽{Gdd$]t|ݹs'FFF̘1'OO,--ٳgQQQxxxc:::PUU<֯_O>}(..ֵk%NNN<TĔ)Shii5PPP 77+++.\@FFC aС)))!??_ۉՕm۶amm5C *3gOܹ×/_000`…={r;w.wp Ғsmz-^ | 997bllL`` W\Ύ_~qQ:t(tڕxѡlll˗(((0j('W\֖9s0a ŋ())!++Kvv06EFF2c ;v,^BMMM#555S^^߿155_~1}tVZEϞ=9tHb̟?eeeRRR8{ o兏/,?\Ri.MQR(d ɐydȜ).C%C(L2RII4x6չֽ7eÆv~z3f@AA###߿QPP@x@ٱcSN͛7(**2zh,--y:gff wx*bܼy IHHQ777Ξ=˦M055eʔ)7LMM}:ѣٳ'''vͬY0223 &Э[7aÆ 2L;wHOO'$$={`bb+/^6455qpp`˖-|]ރؽ{7555 6߿s޼y-Ǐ'++K$B~I׮]122b"ѱ#GbhhHuu50`rss###lBbb":::Mee%K,!33%Kl2GQQ#f湹eܹsgdeeʢϓ'Oسg yyyUUU#JJJ|KKKYz5/ BTTgf̘1;v iii<==9s&-Ѫ*VZ%Mƽ{(..FBBm۶LBB ,@UU$jkk2e ϟ? uuuxyyÁ>^^^1tP]ѣGlڵl߾:*++y-se<}˗#%%ڵkٴiO>EOOOĉIKKٳL>>p u111ԠƪU6l(R|R~477si͑qeɓ <-[PTT5yyy4uTΝKii)\r[[[RSSF__K.1n8QTT$!!'''ikkΎJPP{gϞQSS#Bבi e…hjj|rmϟ޽;/_ݻOONll,DGGcbb³gXz5.]ERSSYp!у$=<|ܹsQQQ!$$}}}w}ϟyyyh-**b޽uh*{Ill,_& ۷ӷo_щҹsgy%SQQXߟk2n8Φ^2336l&M3|pgԨQx@9x Ofݺur!6n(#uuu"m0rH?κu?~`e?uuu;w8v턄`kk2Bzꅌ &&&!))@(߿WWW:z( -ϟ?vܾ}v055ٳg~EEEx"sѣG<#F-4>%%%w0qD?~CΝ; N*'O&55"Hvv6FFFhkkaLMMINN`ԨQoooXr%***"nccÅ ̟????`dddɥKd͚5bjjJMM`y'%%GNZZ߾}VtTGϟ?:u $%% (**bgg' `ݺuWp[Ο?Occ#EEEݻwL3ڸq#Myy9,Z___|8{+++Н;wժпq 733CWWƏOBB... ,oƍtڕ\fϞÇݻlܸJ^+˖-ӓbp IDAT昇 Ȏ899S"""ܹ3=ɉΝ;SXX"ǏG__w߿Ͽ:zbhhhpe233)**BOOIIIn޼)h{{{Չŋlذr?.Th&&&Z)SPVVFkk+b vICC@̛7 /_?p1ԩiii 6lmm9pcǎɓ'$''[vɭ[gyVXgHIIaȑ())QVVǏn޼yTVV¹s/_ԄKZZfffhii؈ ;vي \]]x"?˗HHHIPP>>> 'fff&3fܸq?c~~>ֆ555b[n{Nxc=<<Ǐ ߿߿QSScРAdeeORVֻwo۷/Ν#;;ccc޼yիW;v,[lؘ;wĠA8<!##۷yӇ'rA*++'nBUU+W}v4JJJKGi)//:&O5555Jؚ:j*'-lܸQ=<fff1cyI&ɓ'E:555|||ooo~Arr2~~~ձgPQQ۷?sȚ5kCSSSn݊gΜTuPWWHYBB%%% 8EEElllx9̟?T >\$*jjjРʙ3g)7oޞ5k֠5888&MW^DDDP]]MΝYx1***DFFo]FccKKK ߿ +++^|)x<JZFŵkݻwx{{EBBb19qDL"RVVǹy&yyy& 04ۙ?>- >5kBFFbV+b111HHH`hhH.]x)))8qooo\]] wwwuƜ9s=z4aaalggǒ%K:t([jjjhllE$(**r)g„ <}*  }rm޾}Kn(,, ֮]KBBEEEGGG֮]Kaa! [r MF^^L6 sss1b3vXy%?~`kӧO{]LN|߾9Я]F{{;fÇsIF͙3gDNNݻwGƍÇqttΝ;{nz*8;;gؼy3HKK#!! ֭_~ OOOΞ=)NBMM ddd8p ꤦCՕ=z0tP1005۷oKhh(K,ӧ.//g˖-}!)) .0a>vss4|sҧO뉎,ٿ?TTTpE?~E 07n򪤤$&Lۛ!CP__/4Neoo/`j&L ~߷o_ɓ',YYYY233E)&N(׺N[[:::<}/_gΜQ$$$Xn6mٳgӥK>~ȤI&==]#]vѭ[7jjjpttDWW>}Jrr2ׯB\__%{Q骪 ٙe˖DCCC abyM._~{W ՑFEEŋ3p@ BZZ^ܹsPWWǕ+WSN\rEڵy˖-W^(**rE'11A{z޽{̛7Ǐ#%%Ş={x9TWWcccCFFo޼JA 'OǏׯWԩS g̘1lڴ {{{K`Ĉ1gJKKhŋ ]H(9r޽{p@.]į_رc899͛aΜ9$$$еkW.\%7nb(9{,MMMXYY̙3qpp={r1444011ɓ ̥K={6;wϟ?ڲetBqq1%%%lڴ '''HNNl޼Ν;3k,LMM mmm%)""B7oތ sҥKTWWݛSbff=/_̙3~Zcbb6l/_:unݺahhvח߿kט7o qK~~>֤3ڵkL2;v ))XXX`mmϟYj,_7bhhS[[ӧׯ_`///v% .888Hll,s爉a}*/eee#//ٳgɓ'9999-ZĶm9o///} ߾}~!%%%[6l`„ ˗/ѣ,\_~Q[[˲e#552}t011AFF 0blDFFRVVFee%dffr]߿?6m`(**ׯ_ 4{޽;O<ϏD,,,PSScܹTVV0?Nzz:GfPTT'YYY|ݻwH׮]9wDTT/.\@[[̚5bLMM۷oannϟE.ѣGcǎǏܸqB°VZJKKGZZ###>~pB ƍĄ{+йnbΜ9xzz|rv튦&477܌>2aԨQ,^X0پ};cʔ) 4 &MDEEe! | N8 8q=ȑ#WV1#=z4444p~㩬$00Zo񡶶iӦ1n8.\Yz5vޞlll کS2j(K=cڵOt}a :ѣGc.]F]] /ݻ@ԏL6 yyymƯ_믿8zʼnPZZJaa!:::pa9Buu5BLbb" ͍fb?^M;0񃔔022ô333ZZZ߿?gϞptuuy)MMMڼy}.^Hyy9)))L6h޾}K]],YDTSRRX[[*2޽.]k.A[3 .ݻw\UUU į_Wؙ:nmjjjo>:۷QSScʕ՘F@@O y!萖FAAwP\\̡CHHH`(++ׯ_2e RRR8pJ?~,,AϞ=cʕ8::gŋ)//_~9s///inn Xz5:uJ(//СCrJJJx=555۷ٷo7ofݺuݛÇGVX# DEEMMMjkk௿ʕ+477@~ѣّLmm-***"}}>xhiiѥKpWUU֭[0vX>|*۳ePRRvlѢEqM6l@YY_|̌KLFFݻ}}}ƏOhh(FFFՋ~*4hӧ!!!X@ prrrm޽KBBҧO9+`hhWTTīW߿?QQQHff& ,X/^瑔dJH___Z[[2e 'Oggg8 999HKKS\\Lkk+VVVlڴ`Mׯ_;v,ׯ_,D9 IDATK4fΜƌCSS߿'22o"%%ży󈎎&//g"++K\\k>~֭[QUUnݺ ^zSUUE^^?9sPXXȳgϘ1c-" f&O:Ǐ3G.hhhO~X|X>JIIei˗/C U}:RXXgϞҥKtܙKȅ x 1vI2d%%%XYY!%%Ekk04i>DZZ)))>})--QȧNJbbȬw͖/_x())Izz:6lȑ#Gr1ZZZD&_KK xk 4K.ɇ{ܹsQOwss*:999>|HYyyyonn v܉lܸQ>;hӦMʊ/_ìYgҥ477-_766ׯ_E$Ԕz222%##',QQQxyyBTT>}ˋ .sjjj3g`ooϙ3g>}: .gϞ\p?(**)+KKKyb֭[XXXPZZСCѣG"ڑQRR"00CCCzMyy9&&&\k׮accøqHNNܹs899q!>̗/_HNNf1999$%%9pݻϟ?ٰa?f߾}i&N`` XZZOll,\p___sJ~Fɓ'\~+W0on+44||}} '""iii~Itt4%G~~>ޢ؈.GeҤI ÇHHH&f҉^@ƍ:III<{ cccZ[[첼BN>˗/IOOluFss3Z,]Fll,x{{HSHKK3aZZZ5jeee477 ݯ_!&&ׯ_S[[?_ڵr.]51w\޽{GKK bkk˖-SN2yd;4773~x֭[ƍ~:))) &L`Сlٲ ~JFFR__ϩSPVVׯ ǰhiiDTTͨc?4mۆ=ǎ466sNʍ7x uuu466ׯ_gӦMHIIɳgϨb„ 񃶶6ڵ+ָ [g'OD[[7"##EWWIKKӓ#Gҹsgtɓ8YYYJ\\˜>fjjj1c s\\]G~.K,aܸqO[[?DǏGWWZIJJb4m4AР{ GԔ_~!%%Exx8wpB:t(UUUҽ{wV\2X[[d̙CEEjjj RVVc|"##)))ԔvСC1{lZ[[8q"EEEqMh(**bkkKhh(܌&2|nܸ݊q\***1113EzjBCCٿ?QSSCss3޽ɉUVacc۷o3O>݆F*++Y~=?T_SSSqww۷ưahkkӧOsI2g?|6m}wGyy9Cppp 233ٵksΥwܹs*Kb]]]ƍٳWWW9y$;v젦{{{BCC3|k /jHH}e۶makkȑ#e…ܼyF>ĉ166],{I~~>{x-NNN;v LMMݻw_?#%%źu$00I&켮.#F%%%DCC>}Gaa!7nӧbާOE|uɑ&MMMtܙx^ʶmȠpvv&66;bee#qqqT222())ݻw 4CCC***ŋR---͍/HYY899ʞ={bҥrfƌ<{@tԩ唔ꊏDEEDZc5jXXX'^zGaĉfƦMÃiӦ~z) c``ĉD__yÉ'^KKO>ѯ_?a>|8tPSS[n-0l0^u߿Oll,888ǏILLD[[_qF._VVV?| vJTT8Ԏ9ƒ?~<䠬L^x899qqd̞=[,/gʔ)ܻw}}}-ZرcQUU%..N0`ƌѣ3f Ǐ͛77oDKKv fܸqGL¡CGX[[GHH"ebbXΝ;yfƏǏ޽;>|&HNڶK.477K@@&Mbܸq@CC_]777RSS (((0k,.]ѣE^ښo߾%cUUU8̈#(**5kф&o޼]v.,\PfÇ ͥ;Gܹs$%%1ʕ+qqqCޛFcC)s JFiPhT WiU(uE$MT $\h Rf"X{~~<~|ޯ7GO`׮]qiS)++#996n͛IHHXOiiizIBBӦMCNNN@ٰa+WҥKrZZZ[[[ ϟ? |999hjjRYYݻwfɒ%t999\œ9sHMMeԩ ~MDD#F~gggTUU?2dDDD0`y-sAII ???-ZD`` aoo/x3gԩSݻPPTT$// >}BNNdfϞͱcǐFFFwޱe(--ŋtttpUxTw6PWWWt̤o߾dddpuVZř3gPVVZqXx1ߔDss8+V`)..---Ο?*֭wl޼OOO=} *>yccc222uVb3i$}&81c n޼)ǏǏ466 ddd[nȗ/_pvvŅSN{233OAA/^NTTUUU###CCCƸ;VXAVV8;;Guu5g&""Bb:O.ܺu7n~4446l>|A|HIIѣy_~ tuu رc<\:I*/_ !!;8}z*ӓO2~x4441c;wѣGwLMM144dժUS]]-Ν;'5\pk׮gߏ[ >'Oh" ٳghhh.h?~"̙3L2o߾q Ο?ϹsHJJbժU?,^zŋ/cԩHIIN]]CLMM!??_ԪF̽Ϙ1mmmqL͜9;wEMM }tڕ۷SVVYnq`gg''N$331cƠ466ҭ[7߿8t ߽{.XXXK @[[+WDJJ/^{7#@BBgggJJJ… peϟϻwXYYaff&:EEE >YYYxxxPTTDss3֨`bbBkk B]Vr7?ڿWPP III9s&|4&MDxx~QUUEEEEٴiϟ?իWHKKs 6l؀D,zzz 0MMM)--eϞ=dff GׯpǏϵm6ܘ;w.C̭[HMMeYOSS :QRtxbs---ƍBڞׯ_9r$222ܸq޽{tRϟO~5ksA $  ֭[(**wPUUϬ^z<Ȅ ܹs%iƔӽ{wlق1 _1c ŋٓ$HPP8Ϙ1)S,7nDVV;vθqpppp'RɉoryZ[[QTTdРAx)W&NH~~>阚Mnn.---lٲWWWY`L2͛7pq]FϞ=t=:k˹|2p}ʆ ͛7b VZ  Cmm-s444СC9p-_˗/gϞ!--̀f1ɰi&!=t_fΟIQQ)jiiɱc0553qqqxyyѭ[7^x)SXv-rrr <}7oܜqq5FAxx8tޝQFDxx8***~UUU-[xxx0`Q 9r$ "))E͝;wPQQ"""x ,Y333\)))A__o߾fݻwGGG 6#SN%44ٳgW"##̝;=zpN:šCx% 2SNqQ144d}0gFɗ/_#llݺU/۩gȑS\\111Օ I܏M>B433c„  GK4'Nȱc % r300 ''lmmillݻ,Yv&O,&ˍ74i***888vZ044$66ggg;30l0444(--ٳgx{{O׮]ioogĉx{{ȷoXn?f߾}2}tFA{{;ݻwgʔ)``` EYYmmm>| j۷nֲ`|رc GGG>|ׯHKK'UUUDGGsmarssCMM3gN޽ٳ';whkk#44Xۇ.mbbBLLH˙1|p ;vcӧDydرJTT-bѢE̘1k׮g7449s戒 a]tȈwމ(:fb޽pB!pss#44E燼|yyy Yx0UEDD`ooׯ_qss… Q__ϙ3g033CMM.].fԈmmm066kkklBbbQRRB`` Ą'ORTT$^ݻwt)))y\xb>|@TTJJJRQQAll, ,ɓ'իW&ӧOHII1j(TFAA?o߾1w\'//@Qb ښT婫CKK .\(,Q:::L:sM)//gԩs)ȑ#0dddd011ARRƏ/c/^d̘1رccc|?777޼y#s!66 |}}իWwa988P__/)gչf@SS :yyyϚ5khkk֭[|UVf(--ݻdXl'Of۶mBRJRR .$!!H޿OJJ Wf̘1`LLLt999HJJCAA'&&eee2dz"// .ƍQVVf֭(**s  [la޼yL:;w͛7qttdܹep-ZJ$+j---bìYصkӓ#GrA"##)++YC&L D :::deeCvv6]vO>Ą?~rJ$zDFFBzz:W\A]]h޼y*G˗/xxxpY^ȑ#9}4zzzӯ_?233ShxzzdױO>9rϓHEEHII N^^ݻw^zl2z%]F˗₣#IIID@@vvv<} z*'B-" ooohkkCWW===PQQACC^~-R vݛ 6m˖-?ɓ9d߾}IIIjkk=z_"@gƜ={V$|->}̰b͚5$'' ŋ\ LPR9~8;SNQVVƘ1chiiAFFlͥ%%%V^-VVV=SSShnnÇL:i&~ݻw ŋ,\wwwMFEEVB__@>>t҅~OGGvvv4iڊQBBiii ۷oy=...̟?lǃxGܿrrrӓ'OA=ߟ**++KKKHII!EEEؐ?À'00KKK(++?@SS޽{s%022'113f̳gϰcĉ,[v(..&//KKKy%k֬aΝHHH0ydnݺgǎ 4I&QUU#}gϞ"99IIIDK]]+Wŋ.(w˗())ɓ'LLL &Lŋutt###,YZ߿wwwtuuեW"--Myy9EEEpB>}ȑ#IIICȋ[ZZ9s&ŢvZ a|///Ǝ+Ѐヴ4׏Vjkk9x yyy߿8\]]%!!AYbϟTLYeee}v9wK.ERREEE?~̘1chllׯ1|poߎ9>}bԨQ:t7o2{l$$$ddd`llӧӧP!;" 8s lܸ'O2zhZ|9o޼aذa<~5k[RSS#\8|ٱcÆ #99r] K<:eIII ƍɉOӧOgƍгgOPPP` > (//gٴRPP)FFFdddɷo8pQfggS&Ő/x%qqqK?/譭t .]Ċ+(++xxxYC߼y3s5)))IpYVXAMM ݺuCKK ˍ7x/mmm޽{ vĄ$L.ڼzYfc;vH-_Ç(]vEFFϟ?舻;ݻߋfٹs'\t iiiQٴilFF,_\QRR"88GѳgOGVV'''qpp sss\>Ό?j8p ۶md-ZxzzeGBBulܸ˗/3j(DoT]]MXXׯ_СC444yG3}tJHH0tPKee%߿ׯ_111"''GDD_&%%(_΍7PPP@__J&Mb̙3̙3-[˗/5>>###ٳg$%%|||8q"۷ogʕڵk׮!--MLL 888 jڵ+yyyX[[.:ݟiii,[Vlll?Ffhkkӭ[7O.]Ǐ_^߾}I&CXXܹs'N %%ETT%%%2l0x Yv`믿XjSLŋɏ?شi1qDvڅ:t֯_:dٲe444P^^ׯ_ijj:nܸA||}? !FFF!fddP__/Rޝ9 ={hkkYfOAASNEII=zܿDZZt֍}q.\֭[166Ԕr qrr( 9toF!J-y9|||433<)((@]]RMѣGx"---sifϞMll,FFFbggǹs簵eΝlٲEz ILLd„ XXXц ĩ CͥCCC,,,5kH͛7EEE222011ӧO&l+g Mu&N>}DQQEEE?J/_ǏРW^DGGm6~Jaa!8;;_;ɜ:uD.\'O022ɓ蠪DGG3w\1qzj '--M`(,,ŋs\\\HLLBϟښ?ҧO"""bȐ! >\jBCC ddd $22Ç3fݻѣGˋ_~!C}uqv͞={R`O>P at֍D Ғׯ_`Ǐ3gݺuݝOr}})/^޽{XXX YYYhii L۶mCBB/_l2$$$X`̞=[gΜImm-fر񃤤$pttƍ_RRRPTTPh=z4_~%00///Ξ=OpCNN?#233IJJ ___\Bee%{!99lٿ?昘Ijj*׿x)...XXX*1BX~=;w$88_~}pss#::TƎK[[?~d̘1l޼YLǣG>}ϟdffbee֭[ì@SSgFCCx&N ;7n ̙âEXt)͙3%K0zh#Gdҥ̛7\;ioo'&&ѣGfúu}6C۷osq FGG[lǏ̞=[Od(ՉXQQ***꒐Irr2Æ Ց %%~;w5%88_䄌 {fرCX_zŨQ gϞݠ)cƌAIIm۶"P'Or-(..X[[iJ?%%sMMƝ;wqiͣG3b,r5wξ}ׯ_$%%/X`Ɍ36mCEJJ~b ٙ;wGՐ#..ɓ'SSSÕ+W%++y%%%$$$GSS^^^ߟǏchhȩSXhvٓPUUeb<))۷o(,,3;wӧHII兏RZZ,︜+jjj455/0p\]]Xh=B]](F 111} 򐑑B삂lmm100"޿ϤIׯ_X|5ӧOD44ܹÆ pww deeի:u46mĹsBАw^222ҒBԘ?>HJJbnnNkk+%%%9s!C͛ٱc+V ""Bjˣɓ'0aO<ڵkStvINN6lわ؈ w料͛7-ژ.с6l`޽$%%ѵkWrrr7 ń/lݺ#G6x]]nnn$&&beeŇd`` 27oӧOHHH֭[W^﹢ ŋ(++ȯ_Dcذas%lll(,,dĉ9uֱ~BBB"ӧ^[namm̓իЏ7;wP^^#ZZZl޼B޽+hW^˗/m6tuu)--o߾aeehrvۛϳm6jkk"''Kرc9w~z^~MVV:::cbbB@@GӧODGG/и&L₢""""___sLQ6n(Mk׮%<}BSS3>|x455155 `:D\\ҴÇsvZzͨQg͚5ҽ{w(,,,$++ÇD||<bvժU/V\7BMHH7n@YY͛7{ݻ :Yf9^ ~-vjjj¡C:u*=z@BB ~ʇ:t(/_‚3fp%$%%qvvFQQooo֯_O\\?~o466ȷoߒȂ طo...}iӦ&L 88SSSQL>b IDAT(Ը{.vׯ444֬Y)(( --gϞaffܻwÇBYY'QQQ477cnnǏ9s9;;gtttcܸq1uT*++IJJb֭PWWG>}pqqa̛75k֐%&M@LL 999ٳgYt)~BII ---E:FA7p@N())ח02w***:t(Kͪ^HHzzzٓ"q-ttthkkٙbv ֯_ϠA6l899N3f nϟtޝgϢLbb"ϟ?G㎎-Z$jk֬ŋ/^̠A5j86?49ti3{lO.,+,˖-#66ݻwcffFYY}ABB[[[ܜ0uT ܽ{cjjʣG%""ׯd._̙3g6miiihhh򢙙?fĈL<3fEvv6?$''G.]*L8:: '6m 2Rvڅ:gΜ9TVVʒ%K3f }ANNP;k׮"h{.)))dgg3i$… ahh޽ёfN< ۷o'00 ϟϢE?>\r???\]]QUUEQQQG\VPPojBVVbcc),,~q5._,%3x`IIIݻ|8߾}W^۷UUUϧYYYpwwɓ;w7oϟ"==5kPTT˗/?>Ϟ=#:: mۆx{{3n8077̌&??={bmmMqq1渺r6mڄiii\iiiؼy3>0`'==}:deepB>|ڵkygϞ˗TUU!))ϟ?';;aˣHHH%wޥ"֬Y?;;IIIM+**ԩSIMMҒ(޾}ˈ#ׯ_L81c B;e*++'**_~ѣGIMM!'O2p@3>>>zѣGi&jjjPRRB__DV\͛7 I%%%DJr̘1bF]^^===!8p !!=777?ΠA֭?~`֬YdeeQVV4h{[4 p2c BBBXz5'N$88X mmmIOO_~ܾ}[QVVիW3e&NȤIPSS̛7yyy.]ѣGǁre̙39s LKK 'O 뇶6"|rڰ֖Nzz:RVV$:;-ZĎ;022bʔ)chhHUU塨H@@EEE̛7O 7nF… HJJ TJg966AqAYА_Μ9Cjj*PTTĹs𠭭 ---Q޽INN&''W鉼|ȇxe˖Q[[Yp!JJJKbb"fSÜ8qÇg&L #GDAAA<7111,Xgggé &ē'OXv-K,a֭TUU˖----|,]CCCOUUtttclllHHH@RR7n233f̙jjj߿?t ߿Ϙ1c ۛ7ox<UUULLL!66B||<:::1zh?~̗/_eՋ222֭| YYYΟ?ORR0}Fr ͣ#6QL!B$PDHWyNy4jUp5)TR2E$q~k^y}Z-v}?HsΔ1k,޽{LJ'N޽{)..fС?r9Ν;'SSLǏ\?舷7>|`ڵŋϯ_իFFF':Ddd$L0k׮1k,+++ɉ85jCe˖-#HW쨨@*bmmڵq' PRR 066gϞ_Ǐ3{lTUUN<ɤIf޾}; b1|pz*˖-А >|ӧ6ltЁǏrJpuuرc={V7oY3x`J\#""Ғo߾'8033DC022ӧO,]o߾3{vU1cb ȑ#X[[ccc#iΝ#99?NN:t(2223uTvEEE)))n:كڵ'Oxb6lѣٳg0rH6nHNN<@VVggg9rӧOG]]B={Y`X={6B@Evv6)))̛7|BBB8s L6 mmmmۆ&&M*֬Y9fff"++KII9Ϝ9Caa!jjjzDPSSWpdcɒ%\xOŴSNhjj?pEFѣɴ㩮֖z $<<{{{s"ٔHssXٳsaii;ܾ};vs:t考 ԐC[[MMM~ĉqqq)S0m4uЭ[7ػw/EEEbmmϟ?122,F%HRCAA/_о}{?.j1}ts:`FǏԩo߾ӧbccCˣo߾߿_С|ÇG>} ̙3Gh2228x hkk455IHH`ڴi?`jjJSS]ťkӧOE%++ߧK.-٧OÇ8p@gQWWgϞ<}Tpd<<}:y󆂂._)|tĉ=z?ruv͚5kׯ_n[ѣBp|%a*RPP ,,߿SWWg˖-pm=z4>cdeeILLdQ[[Kcc~^uuucСbhhҥK޽{HZZ8p@Φ`cc uuuDGGȰaϧGDHHK._~piZZZJͪUXdtgddpY.]+&MBFFqM0m۶ѣGe…<}7nݻ7k֬kײcә2e III444stBjj*:::鑝͚5kx#G$ M6O}}=-bƌ1bN8P[[˂ 8z(}eʔ)\vM իWy.]b߾}"xyyԩSbܹTVVr=222:u*qqqtЁI&qiB^^}zjkhh0uTHKKC]]qơٓRΞ=,MMMܻwm۶7ESSV***gϞ’3|pƌáC{./^}ENN |k2qD+++DOSٙj?Nmm-f„ :,011!99:{.tԉF8pZZZbQ&..h:uDll,{ÃTW<==VUhh(SLA^^"""C'/111lڴC2ydݻ7eeedgg`~!޽{ƩSHJJ1ccx" lڴI\\~gϞGNDDeee477S__O'99x~EKK ~~~tؑ xfff=zJJJ۷/Ѽ}9̂~ >| _~o"HԩSPP@\\---<~*++)++Dh}Ɗ+`̙3???033cĉĈ3D|@MM tؑPWWΝ;inn[n[999"""SNFyy9yyyTUUahh+7n`֬YH$aqU\\\PVVF*ܹhI333BCC111a۶m߿_ 322%44iӦG_ӧOijj͛7r5ټy3 Jd{Mvv6---L0={%nkHHHsӇHVXAnnk#ٳ'dffr%dee;;;ؾ};/^OD^^III,Y%%%177baaӧOfԨQ$$$peLLLcǎ|;v{nMD"LRSSĉ*|Y ===F__wMVV˳g"""%++ ޾}+bcc'o߾C_<|P(~AǎJhiiQVVFbb"TTTyfTFMRR[ne…$''3n8ڷoݻ7;wH=zѣG133͛(**%9z(_FGGG'޽{iiiٳh (,,$&&uuug̘1x ݻ'ffff >Tʎ; .iӦ}vZ[[ٱcyyy477vZ<==ѣ_"Ξ=ӦMcѢEz&ڵkGjj*lذd:v숟~ݻ7---ӽ{w_zɝ;wĎUUUדϟpYYf ޽{9r }899 U^^O4*xW^%//ر#!Hˋ۷Ӯ];ddd=;;7o؞R\tuu1b|:0p@\BUU;wXYiwwwdee7nA-rrrakk˖-[pvv̌}biiIRR&L梤DAAbiiɤI011AEE%%%&Nӧ|2---8rrr۷ >nݺqE!(߿?7nd;w2?έ[֦ s"J9}4o޼AUA IDATU'NAZZ{ϟ?Ԥ#Gp)&L |=͛7ۗ߿ၾ>Ӈү_?8鸺ի ,`bffƎ;ѣܜ.\ŋի,\$V^Mrr2ǎۛ5kзo_rssECǏ|:c4i{ŋx{{›7oژ-ZD>}~: J~:weƍ:t,"""|H׮])..ۛZnݺE~:t(X[[I׮]ٽ{7>|`l޼{{{8w=ȑ#coo/666š5""??fA_nK,XT*eH$as3޽;GANN7obnn-yyyL>f͚EΝ)--{tޝ /_NZZWǏSZ|,Xݻw1bX5rrrرc<`ajj*ಲ(**+---]va?@SSܿu놦X(((Ν;722sss&O +V`ƍZ 999a!J#Qnkk˗/"Hprr1i$4559r$_~eܹp%?~̴iصkNNN"=pZZZGYY?M[[(++sm ܹssΝCEE `ccûwX~=ZZZgfΜI\\xzz ={ [nb}„ tܙe˖퍒ׯ_ׯdgg1|p.]˗9t{=nnnfzz::::H$IOOYիWdzInݐߔ\Ϟ=144MMM9vzzzcccnܸAjj*$%%m???),,$11$n߾-֮]˸q8w> 0o<Ǝˉ'0iРA?<___>|("oۜ?@ %!!޼yCVV@ ߼ySSS,YBXX BOOOԵ&MDcc#+Wˋ/NXXyyyҷo_455e˖qI޼yϟ)//gѬ[^xA]]׮]cذaԠ˗/144+++XH$)ollLJձj*n߾Mjj*>|$Qח]2`t邝۷o͛7899ѵkW '/_dߘƌ#5+˖-իWQPP#G7oуN:pxyyqF߿Yd ƍӓ+WPZZ=zϓP͙3PV^zk׮ܾ}GGGȸq8q"YYY'/Æ GGGUVi&ݹqR[Ν;y=/2zhllleeenʓ'O֭(++cffFKK _~(ׯ_3uT0`}E,,,/EAA߿S^^NNNoߞWÇy53pƼ{?2h >|H}}=߿͛7L:Gq}M\cccqqqѣwwwD[G Tq.]DQQ3gΰi&֦@IJJYE*re<}ѣGS]]Ϟ=c%Hhg***/^!Ci& -- lllx5rrr$''ė/_D>800vډxpp0mmmѥKLLLH$8::Ç)..&88GGGܹCFFO<WWW7uVDmz+ի\vbRRR߿?˖-CSS7MKK ^^^ZJD 8p k׮!//ILLDVVɓ'chhȌ31co߾XXX0uT^zEnn.yyy(**Ejj`شիܺucǎ1yd5JJJ;9wzgϞrJrrr+&L **JQTT KKKx왈LB^^Rŋٳg9vfff<|Lz聻;׮]CAA###dee9}4|uuu0`SYYI[[!!!hjj2l0Ԉcʔ)1d|}}qwwɓt9uخZPP@pp0\]]qvvFOOׯ_SXX=?ɓ3dIJJ {{{޽KPPH񄇇 ihh`ӦM۷3gP\\̋/fݺu?222|2o߾EWWWH7nȘ1cHKKcܸq\t=z?~<.]ܜo2p@FO> UUU ޞUVモ"w;wPUU3(((0~x((( //jLMM100`ݺu:w̋/y gHR:w̸qذa0߽{>0c BBB9s&MMMPRR@ǎw$$$f'>| &&F #իNNN|?r) )((@WWp444C"PVV`ȟ:uwމiMM `ii"MVVs )++ܜZZZ߿?йsg͛GZZʄ`bbBCC...1vX߿OϞ=ٶm`ehhhc[[IIIߟs)"p 100 ??;w0|݋5^bӇv 2KPRRիWx{{sAڱcPJp/_ .[[[pqqaСTTTʕ+ZDv* !~e˖q!/_7n@UUWWW֯_ݻEȠW^lܸSr%v͛7y8466r=0?=0|_ztt46mBQQ]]]455EԩSձh"JKK ZZZIUUK.EAA*nʳgׯ0|kkk6oތǎ$MMMz͗/_a,Zhmm'''EA  SSS ={6>>>\v#GAll,EEE\zEEEQPPݻ9>ğy``hR ԣGhmmE__/_2~xN<)w%rrrhkkӽ{wRSSgʕaddD"!<<"##7nӦMŋ>|W^J<<<1b儅)?&((H,n.]"%%"baaٳgٸq# PWWٳFPPƜ?gggϟO~hkkCOO+<Tڢ+d*666$%%QVVʕ+E ݻwTWWӹsgn߾D"˗x͛7SWWGrr2b{6l%%%tjkk;wڵ9[nECC'Op%Ərrrbx"""w;v$44VXAff&!!!'11ٳg@ss3"̡C8s cǎٙCB@@nݢ>}Jpp0ƍ#>>^(444D_Oj3|p>~(۷iEE>}{ܾ}p~3f`Ν*ԭ[7H߿͢EPWWÇ«WD,!![[[<==innfу$󧐑tؑ!C H',,LWKJJx)˗/3ydJJJhllwA_x1C #<Μ9!߿CCCN:EJJ ?~`)**BAARdddpww wȑ#pwޥ͛7 []]%%%bdd4hP~uڕ3fЩS'aÆ cbbBSS̘1#G%W^%## w^>6m"44gggGׯhhhPWW 999hii1vX"""tGAEEmmm(,,߸!H}ߟbeҥqßaD"CRVV5EEE̘1Jz,Y->|fA?tϹpg|,\www9T* 122TT @`` EEE򌈈)Slܸ\9zϟ?_DƌC!%%D"`hhȾ}ؽ{7cƌAQQ'Op<3uT233111ׯ۶mc޼yL4 Μ9#cYYY$??KPRRBZZ =z.]vښu1{llll "55MMMw>|chll̟͛?K.ѵkWOimmgϞDdd$SNeՔuVdee4̙3Gpp0ajjÇYx1#F`Ϟ= 0@8M VVV5߿n:9uyxzz2|IMM%$$O~r SL!&& FӧO9w_f5J@tuu@ vo޼,/^˗/ƒĉYd cƌյk7*++qC Glݺw2{lquuEEE0aO΅ ,^]r9ƌ#T .$!!ÇS[[KNN0sY[[cggGTT:::ۗWr-JKKy߿g´4|pڵkGll,gϞ1FFFDcc#111l۶MlIwN>M\\555R^^΢E8s ӹsgzMmm8|¤Igȑ萜Lyy9dee,>>>"//x1k֬~ www.\Ȉ#ر#ÇСC8q={ رc9pxc?/^L\\>>>H$Ç;w*>޽{s9,,,Dŋ8;;PQQ!:::닛|!CJ߾}y9:::booOdd$ 0#Gbff֭[@D GWWWB=z k׮EOOcbb"⌧N"44FT*%##}}}===ٺu+]tښjEeܜ\_.ֿvqv˗/ZLMMx";vH0EE娤C1ewNTT={ܒ"N8p,]DBzz:|/ٳg RRRիL>](Ǝ˭[HMMɉ?EII ;wüy:`llL||}HKKCCC ƙ3g% |ppp`ǎCkk+G˗/XXX'Ϟ=888pIgkkk:u$,IxzzR]]ӧDYYN:ѿo=ÇElf̘VVV̚5=zرc/X__CϟIHH`= cǒ󏸒WUU?] eee>LJJ(}ѻwo̙C144$33ݻcffFiiΜ9#F0n8!? $;;>}p}:wL\\F… '''6l@aa! Ge̘1444f.]8}(ٳ111hkkꊂDFFrYƏ/\%%%_ܼyeeeAGGT9DFFyfu&L@FF|޽{˗y9zzz? B]]۷G*r=Zns%mmmݝVٶmё9pBIn׮]b 1*))iӦ*޾}cǎE^^y摝H$^~Myy9=bĈ`ii-)))PQQABB---{.\Ȇ 033#--߿xba!<<ŋ+QVVؘ`8z(ٜ>}EEE;F]]3qDֽ{2bFD"^zѥKCpp0JJJXÇ `PXx1 ,'iӦq={Fpp0Ϟ=LGr غu+6l jkkҥ ?$66;wԄ3_R3f ={={`ddD^^[0`p1~ͭ[͍m۶ʍ7x 2D+Hkk+˗/Æ044ӓ(>}#~~~ :fFԩSuuu%%%Tʇ044޽{bll̄ 2e rrr~7 6888dѣy%%%XCCCoN~毸X`A֬Y/_fܽ{rf͚E߾}s۷ktTUUs]EEXkii˸qسgaaahhhPUUECC?F^^OOO:u?~zӧOQUUǸ!+++Rv؁nnn,ZSRRBmm- 555ӧOTUUɓ'РW^E= yyy %555 <0rH222HLLd۶mTVV2m4ˊ+}Jxx8'Ojx~~~3w\;FVVMMMDDD`dd5&L@VVuuu1SSSKVVDEEݻwZfONcc7ǏsqܹàAǏK=$$D-F 2PVVFǎҧOx1<~>}..._SSS.]Eݛ}}dlmm111SEEHII1m4~HII iii899qmvʜ9sXf Fb|޽{s ,--4i$$$5rJV\ @UUJJJ(++SYY~?[eee7oRPP@SSޤQ__OLL ʼz۷oSSS2))):tWWW $ׯgΝl߾EܹsKyfOIIfǎ\vM|;v,CeCN "$$ϟ϶m۸vOFCCdmkkcʔ)lڴ wwwRSS?~`D3}tnݺ~!C޽;666((( Wf…HJJһwoÇuC444pi\BLL ߿l2󉊊b˖-8;;ӧOlllpuuȑ#8::.GgΜ=&I}6&&&>}ϟ?Cg0k,̙SkÆ 0l0TTTEEEEěXSS#F祬LEEvBNNEEpp0mmmdeeՕ ͛7 А8zݻwcccNHH邯EFFݻwG^^ES)55ϟ?3k,QSS‚/_:u*ZZZܺu *++ }K.ESS;;;<==y<ߟ+VPVVF\\YYYrIZ[[)))aԨQz+Wxb&$$,>| Nkk+$&& PR^^+W:tرcY` ,ח:prrb,^=FTTFƆN:WKKߟ,͛C auM/^̗/_>}:...yfĄRܽ{'''***hjj{9x9-ZDxx:;;ӷo_v]XbgСC9s%%%jkkԤ;Djj*rrrՉ&qZZ=ӧ|1c/1JLL V˗HII!##Cbb"Ƣbnnϟ?y NNN8::2ydΝ;۷ر#ΝÇ X݁ppp@WWҥ zӧ466C||<^^^´iΎ  //ϑ#Gѣ"Z^^Nvv6=SN\xWZʊS իW111O>|3f`" uqϟ?… C]]OOObbbPPPDDD|r1vZ><`~.T֡CΝΝ;l2ڸt#G$00PԲL?][%'Oko.>_\\Lqq1l޼ ҽ{wv̙3tJJJi }޽`ggGHHׯ_Ύ 222166nݺDT元QQQ,X+++NLL w˗4662|p&Oĉɓ<ӧchh(f}||4iMMM,_{φ d޼yTTTɓ'lNʼys|rL,--p?>}9w{sαpBzeРV:w̃ػwk|A Sϟ?&wرcE fϞMjj*C !!!)׮]COON:̆ @WWW*++5jHIIqEիWtsssݻ FBBuVϒ%KXt)RYY TTTHOO&LMMquuG@ΝY~=MMML>Ǐ{Q>=%K$AAA\p<==IOOg͚5?~F4...HIIq)~JPPakkKee%'NӧOkdeeٳ'O '''v؁$%% ۑƍӧOメ!VVVرkkkqR:t(555b HOOW^+++QVVShԸz*iee*ӇL޽{1TUUƱcx SNEMMM{#Ϟ=[n̝;"tuu̙ѿ8 ,?w"// 󱳳cݺu899yfΝ۷[n9sF2duuuՋO>qQf͚1bׯ_gҤIxzz"%%Eyy9aaaHKK܌!Ϟ=eK }DEEqaoٵk111̛7GMzz:<@BBӱcGquwwo߾l۶Qs|?JN׏eҤI(**̓'OcСTWWI&Gzꅢ"}G^^NNNܼyuuu*++ikk#==}RRR*gϞ"͛p&^tz!Xr%~~~9rŋcooOee%]tAJJǏh"6oތ?oԔ ;/_۷oÇGWW۷oFyy9---B}}=...ݻ/_бcG>|˗y߿'==:Qׯ9:t:v숁tޝ 4Ollh >|Ȏ;իqqqraf̘MPSScB~wނCuu5c̘1 \ry)RDD_P^^Nxx8zzz1sÇi&^zի~:GĄd'44sEPdɤ0b}Jjj*SNL>SN)222lڴdwM@^xAFF>>>`jjʶmܹۨs̚5 ؾ};}ח+Wn:HJJѣhjj"#//w"### NGUUU1S.cǎeĈ,]Yzg׮]STTTXbAAALHHHʕ+wʔ) 6?~Cnn.۶m\v8"""gϞ=z_ҵkWa 6o+++Ǐ#))III yyyx{{'%%ENkk+?D[[;;;rvvYfccr1:uرceUUULMM6l3f@WWٳg3ydlmm#&&x>nٲOOO=*4;w$55/RYY?;w$==Ǐcmm5JSqqq1555tR>~ȵkx}!,,Xٰak֬#ʕ+())ŋ6lݺuŅJF˗cҥXdvɘ1c:u*"N@^^q}=z4ݺuMƍ3Yfg222x%DFF_pssܹs444woqrssGaiiIdlݺyyy˅dܜ/Fff&^^^^]]]TUUqtt܋/dggcddD~~:ÇY~z֬Y= )9s044$""EQXXqssCGGb:wLVV'ND__=zp)FǏ9uG_~MMM }st֍Khnn{{{ZZZpvvriƎ=;vWWWRSS1b?f„ 477#++˜9sx%UUU`bbq%W^舼,v۷oy);:s +W$::ZWXƍ9u?~`8::~~~"mddĬY0a3sLq"r fff$''4gϞёByEEE{,swwŅ'Obff [ne٘ceeEmm-&''+WR[[Ǐlܸ^x_Ǹ@>}hii`N:򢡡g"j~!!Vرc0X2;vdΜ9$''#))IUUTWW3tP6m$ TUUAjj*)))3߿ϲe8puuu|ϟ?3`:"X:veeeիO<ƍ$&& 3={puueΝ,YtR8@CCG\\\(((lڴ ۷o9~8gfƍ),, ܹ3ǎŋptttϏZPPP`DDD0a***xjjjуϟ?CKK :=zW$%%CYY}}}nܸYHHHl;r=111!((8jkk;v,|NDٳ={?>ӧO9!C޽{3sLbbbDAA$~:jjjhiiqAEɓ'\~?VVVry444}kkڧaѧNR,,,ׯ(/y5LƎKYY;|}}#..kkk$$$ eȐ!| ҥ4',,.]P]]-6:tڵk7ٳgɥK=zNN< 7cǎ̞=$lmmBRRR jjjӓ˗3|p?"V|M̞]qƱh"뇁jjj2{lj絴v_x]`ll,\uuu2bΟ?OݹvH >>>x5^z%jo޼ϟ/ҩS''vxϞ=ijjܜPg̚5 _@\\=7nɓhii3]v%;;~xbN7;wd"))̌$Ύfx-$%%_o>vAee%˗/gÆ ~%%%())ܜXNJ@@[<}J Dxx8rrrQVV͍ G~z~?$-->}0h =zD||<}e$&&R__/]rrr.]">>cccѣG<{bOLL >>>477߿4i999~<== <<ߓƫWHKK#,,-[0eTUU}v;w# )Sp ޽{'gΜaӦMOQWW̌7`aa ڵkS\\80`8{,<}TIIIAMM ###ޞ|TTTcoիW|rrr8u---S6662h \]]ٶm.\`ʔ)\|߳[XX`nnNXX7n<Vtt4AAA())gD7,, WWWrrrFCCtttѣ666 >\JƌCFFw;wЭ[7v܉RRRΡѣ~0Ҹ~:ܺuRTTę3g(++c׮]ٳ!C0sL>̂ 8s :tmmmFׯw/Cݻw ">o<9BII ۷ovnٳg166Ã8o ==)Sȷo߸x"W\B>}ΠAx%hjjҳgOϧUUUJKK100ΎG{ٽ{7} bz聗Blncbb A;w.111,] BMM өkCddddǏՕ 7nٔQ]]555\z<}FNN{vv+ϟӓ[ngʢ[nGĠJ}}='99???Ə'Nf OOOV^*<@^^ccc%TQRRݝ&FAMM ̚5{QPP@@@ ȑ#FZZ\ƏOUUyyy477#%%ڊ "yڹsg^JII W^k׮z+W2eNMMM >:̺u߿?ر#ҴpmQTTdРAݛ߿M``:e{CR}hkkS\\,hkIIIHJJ޽{y~bС">{lԄY~ȑ9QFƍH磥˗?999VZ>iii"qͽ{y&ϟŋL>sMn .\ X|9޽cʔ)DFFh":v(׏=zPYYǏIHH@RRyyy&OweȐ!3i$Μ9GXQWW'--QF1ydEVVVܦŋ9<222ܺu 055%)) >} 'deequuENN:u*ݺu#99,--155L0(ټy3'OǏhkkQڻw/vvv/;v} "jjjK|ܸq[N<,.\@mm-nnn\z/^ɱk׮˗/ʊ'5L8'O thh(Geԩ|˗/BRRSNgϞ 8l>}*d%$&&ңG""">EEEDGG3bQswwGEEF/**===jkk),,|7ΩS;w.;uuu"""8p ...,__~1h ?~,(ϧpaqtt:0~xvIss3ܺu7o %%E}}=L0777x:u" $a׮]L>%%%6l BINE圝ҥ :ubҤI~HvЁx֬YCϞ=꿺˃/oYXXX>}ׯ2e :uJ`i?}G{urrȑ#}0I]xuֱf:'OÄÒ%KΝ;=}t6oLdd$b5fOqq1=֖cffF]]@ [k֮]˦M())ۂ~UK022Ύ'S"##ٰaS]]ȑ# ΎsW~ 6>7c eС!!!AHH D}}=s%;;III&::TN8A>}$:: 6!IIIell̐!CpaaaлwoUݺu+RRR 4xܨZa,#44:`ggԩSill$^|Iff&,^*>~۷ocٲe/jjj{8p _|o߾;wYYY\ 'Nӷo_>,~^w&***޽<ϟOyy9߿lvi7L!?a555M:vvvXYY!%%`R\c2n8Μ9#0ܸq}777M9l0찶6ؐ۷oٽ{7fذa 6 {{{:tmHIIAOO6oL~~RSSի"!! |rJKKٴi]t<$$$&**7oްn:Ҹwyyy¯_hmm[nTuV|||xo޼ǏL>ӹt qMlllfÆ ܹMMMY`())1gg֬Yή]000}6ӦM\r&={&NVVVӵkWlق)))Z*=zw^444uJOOMMMAv={6t҅˗&hq >|@n݄eKQQk2{lXǣKii) JJJ1}tt邦&,[xl 033K.wHpuuƍDDD0rH?FCCC:5449s&O|Hee%{조gK&NW\8˗"XYYqI.]yyyߞk֬&ƌCKK 555ܸq---:vH~xnbƍ$%% ϗ/_=z4Z/^PYYIAAglܸiӦ1|A'O2'NrJ+:w_|ĄyЀ HHH`gg6,]T4J8|0uuu())1n8RRRbϞ=3h r ߿?{h Cf2P.R4F[yu54B)Ѩm2F)?gZsiNy|}|| ׏`1׌ٙNLLLy&\p!}3fp}޿/„?}ġC777222޽{䠭M]]9::;.]$\ۇ,s%$$W^S%115Q艗/_Nqq1=zE!##öm RUU)KBBvvv(K.rJLŸIKK ر&1;ijj";;&>~HQQXZZ>>>ҫW/RRR((($$$e9ݣMč꒗ÇIIIiiibccٷo(++3zh޿ɓMII!CPRRرc{L ڵkSUUźu޽{鑞۷od,,,DQQdVXshѢE:tyfy=---YFK.%11Yf7oވ\PaڵL:e˖SMNN\|6/CCC:::XYYq%θq㈌ܜ۷cccɓ6l fff 4F={ԩS.ӷo_"//Oxx8~~~ܹskkkXt),]/^PSS <<~͛7cffƧO077ԩSۋuBw/ϟ?Ϙ1cPPP ''pһr ?~$55""":ux!}6ނi&+WbŊK[.???&M9˗/g̙x{{AKK $&& o߾GX$$$|ׯL0֬YիWcaa@^NѸ8ttt(..&88 djjjDׯQVVFMMFz)TvvvѣG9s Ҽ}޽{H~~>\x;vPPP ř3g'DZZt$$$D6l ))#Fp ttt2e cǎ,Ųn|(,,,u555d ƍ3f O|8vKϜ9#HҞ={MXXUUUա%899憼;vʑ#Gprr_* >\,.~'} wJKK_biiIMM ?f̙HKKcaa9sHJJ͍/2j(ŤI矱X۷of~')++F$$$ۛHKKgϞfٽ{7MMM<|M6aoo/;;wy ɓ!/mkk+d{0 0bxTVV"##@}}=Çˋ#GiUwFbll,,[=zPWWo߾yyy444xFFF"ELTT444؈Ҩ Ycnn.=_ɓ'3m4444pww'99h۷/?~ٳ8;;w&L@dd$ .ȑ#>}'''PQQAAAkHHHBqq1̟?x֭[Gnn. 3zh}&4ƍCGGGtIII_̙ñcǀ3111ű`1c;wN\v?ty {zѣ <ϳk.ƏOXXGII͛7ӷo_~bΝ޽{O>(**ҿjkk4iϞ=9sv횈NNNʢ~~~$%%aaaG(//gÆ ۷[ɖ-[8p 888pQˊ+8<_fʔ)(Wq>!!!l߾Y|9ܹ555ILLLJɓ'SXX$?~ܜ}aff&Kvvv8qbvZ"##5j˗񡹹Ǐ~z>LyyT%YUUEqq1 _ݼ_ɒ%K{.bҥg)**k׮Vnݺ7ׯgĈ\v pQaǎ԰pBPUU;;;2339s&~͛7rrr/%%ŋCZZZReeeYnݻ>%f$&&7뉈رc߿˗Drr2yyyB~~>aaap:::DS^^fXt)aaaL2EEE&$$`'OEݻGOOZz聾>[nVduuusiV^MFF&&&"Օz*:?V3||||bfؾ};nτ~زe NBOO5k //+EGG/FEEEPsrrNҤ$֭[ڵk?d֬Ycbb瑑ĉ$޽cEHH鸺"++K[[%((eee"""2e ">mǎ!//ϐ!C\|||ёNv-F'NĄϩ#11%%%mF^^iiiܻw---yՋG"//OEE0qߟLRRRsDܸq .\@TTٓhR|2 tttw^ӧ$$$`hhH{{;1<쨨˗իy5Ņ͛77.^HNNcƌaǎ >;wpAw۶mHKKSWWGJJ 6l̜9XRRRPWW޽{]cE3ѭ4440|pԐ!11o߾!''3ZZZ8;;#''G}}=466***'552(// @ٵkiii_O>1tP(--ޞӧӯ_?6nѣ4h>|_ LO2yd 0 ?~̝;wxHJJ'Xv-1118;;g^J`` '555clÇcРAxzzһwo˂ (((Nee%EEEb$---mmm~͛ccmmȚ]x1{n?ȑ#,[SN!)) /^$""$hmmA񥥥ܼy%%%NvgϞǏ0o<GZZr 666|kkk?~UUUL8QP Dzz:///as999Lj#}„ /ڵ^zKHKKݻwQWWGAA^z`e˖gjjjAfbر(((/ R^^%ϟ{{{JJJ`ʔ)tvv2zh^zE{{;/  }eaaݻ1RRRcbbBaa!W\ puueժUl޼Y,lll ?'O0n8TTT [nQYYɪU:u*W\O>ő2k׮%<<\t7nܠyyyNJll,cƌ᧟~"??///޼yC^^899ѣG)++Jx222o߾XZZ2o<9r$봷4=z@UUYYY1OIIaoV^͓'ODI|ذa2tPpssݝ 6]]]dddpQx|Bp5ƌÚ5kX|9bq/))ə3gDh7o@AA ڄ;ؘϟ СCp+--/^pY? ?h郾>"ml߾}>|MMMfϞMLL 'O6C@AA :;;;mmm,X_.pf͚ERRǏgƍakkKnn.2n8&L!C'"..999䋏GZZooo )((Ғ˗###CDDxS WꢣIII߿+;v_?~ zŇx hiiQWWLJhoog?~@AAKf͚&]]]l۶SRR"_݋%---fرX[[3tP2d4551l0^~MKK :tΝHș9s,--QWWSү_?zIOOg֭lٲ#G7CFFo߾eΜ9LXX8;;SXX fff|EqiV\I޽Y`>ƍGFFFFF" رc2tPΜ9Á;;;޼ygΜh D~~>...Q__͛74h rO>Q]]-vyyy9s[[[^|I~~>q,Y,۷^zaddD^^ٳg|L֯_OJJ 8::uuuakkP rd~fѴr)֯_Occ#lݺӧOb &L#^^^;k׮TFGG3{l455IIIȈ%N؈) D{pttC޽qssc͢EhkkcԨQF}}=XSN₏RRRTUU`QFDff& ...Ӈ?n޼ԿLee%8;;z+9ɓ'QSScݨPTT%L]XܹCVV_~3댌 O~o߾EQQ Vb $--Skkk3>}Ks>|SSSf͚EXX;v`ݺu|={///زe b ߧSNaee% KKK9s deeQSS=GGG.\Ș1ce˖-ر۷~/_ۛٳgӻwooߎL:o߾xb1w8s 6m̜9YYt[n [NN&M"88L6l@rr2K<6 Zkk+"FѣG444ׯ>ŋ8::"--Mdd$s 1^~fff2vX-[FWWg˖- ''2m4?'ODQQ(÷oߨ… 5S[[Kxx8{eС<~mmmFAaa!~~~dggs뉉;(#BEESիWFVVVW Dss3HKK!PZZիWijjBJJ3fPQQ7PVVFxx8۷o >L^^SZZJcc`1aXlaff&1vȑ#!++wrqWWW000_~ԠHjj9UWWՕÇs==z4?SNݻ˗E௿ X%%%]ƧODNzz:}Օ0i$tuu7npas&M"99;;;>}jjj]===x-{Ln޼رc믿-Аsabb2...hiiiHԔ(9viiitvv2dƏ; cccJKKExFZZ}022ҥK%Еnb̙APP|&GEdd$yyy,[H>|H~~>lݺ}}}Ν;;۷h233ikkƆ3azIRRkwԩxBgϞ́TYYuuucĉ˗/9}4TWWcaa-gΜASS#GriO̙3qqq!>> ׯW?Β%KDallLZZ>}BBBVZE||<{ݻwhiiCpp0&&&?)[nϏNj"l2455)++CFFvn޼S\\:u YYY&MCGGؽ{X bdcc#aaa\|s1c |||pqqeLL 3f̠kkk\\\HMMܜp4i"!!innٳg#?~,^@ӣG Ǐ())L͞={JϞ=={6EEEUVVFpp0+W"ŋxyy1w\7nb„ HHHpEjjj>|8>>>l޼#--MNN477133n۝;wXfд755QVVɓcѸCPPqqqÇ~:"thjj͛7(((رchii@ss3d֭l޼}!'''8{,())[nC<ÇGCCϸq? GԔ0-Zի)..&(({Ō3CYYA!++KMM "gϞ,X@8-[HMMESSccc:::PQQʊ$=}޽ 8gggvڅm1tEEEDAWRR>~)֮] UUU#--ͤIٳ,Y?̙3xzz{n޽{5`̙ܹsYYYptt/2o<9{,|R铐=SWW )))DGG3w\~ٳg n={#;VP)p^bΜ9Æ 7o}۶m#??/2c ^ʽ{ӧO,;v`֭Ğ={'++SgϞȑ#ݻ7?t矸 ''Gvv6߿S,dddx]t۷os1 8wnnnhlaa!L"SmƘ1c@EEo߾aee/5L8w&O:Ŝ9sx-fffxyysNNܻwuQSSCUU^BCCŋ3k,qDCCZ022bӦM|R*++t6|=/^ --Llllؾ};III׏nݺ%LC]>򨨨'OٳEgvMD*۷oYf ׯ'00&^|6l"|ѣG wprr2GKK jjje֬YsN~i$**JѣGSpc޿$̞=E._LYYݻWx(5jQQQFAA"iV޿Ott4Æ Hnid\\._̈́Q\\L~~>TWWގ'NdѢEHKKʕ+̌+V0n8133ʕ+X[[s=L®]Xh#G={E||455@SS+WMPPhjj; 9nիW#ى!iiiTWW{yKUUFswuuc՝ ֭[EX\\l۶H""";w1|pHNN&##5kPZZJWWzzz?;;;V\I{{;cƍ/ruLMM144ĉ"ŋI]]3fܜYfavIII ?~dժUbpLMMQVVo߾߿ߣ#|cƌ‚P~7(++CMMSSSäI͛7ƻwﰷgСL<``РA|EΟ?Ǐ$ƍ'uuu<}ag~ȑ#),,kג {ZZZ8qBBB_EgooOKK $$$dJJJ3f k׮%--Mt>ٳg{޼yCff&Cv3KCC ,`РAK̰ѣG466m6N>իW7o~~~bggǥKBUU[[[갰`1'''4iOwyy9f͢cNjj*ׯԔ)S.]bҤI3s]HLL$88_2uTTT`ffƁ@OO'OFXX ܹs <\={&BΞ=… 4i7nDNNNϹ|2nnn<*444xW^#Gr444(** |}}8q":c///"##_ BMMj8<>>>XZZ΋/q444;QTTLJR"##]]]N: 6իWSVV&ܹsv `'OdժUtttIHH`Ȑ![6>}* _,^???YMM|vcQ{Eii)NNN$(([[[| eee122Ȉ@nݺEFF_J999@AAmmm ¹sPVVTUUѷo_FMAA_~ 8HCChhh`mm̓x ,R!>>pvƍ#O2j(8^B`` :t ~'';;?aÆ &&&ѻwoHNNf׮]N޽)++̌s1|pLMMٰa޽C[[9sܸs899ajj*,¸3rH000ӓgϞqe>|555shnnw1m4&N(KKKy3fԝ;w@FFMMMTSSK.%-- mmm?N~~><~ cr]>}ʉ'Xt@AxzzÞ={kRZZ*bߣG~~whmmߋ سgs̡Kv177,߿` &0tPǏwvDFFrJr%$$DWXAxx8&Lz̙3?~^P$CCC|}}넆 V=mmmxyy1}t.\Huu5***HIILss3k׮eĉbIcc#+V@UU۷իIHHի>ǒ'N`nn7o&>>|?΂ x)TUU.DZZZ#--222xzzraxb>|={ÃJVX!$8p{{{>~Ȯ]8q۷o۷oȑ#ٶm'Nd̙HJJ[ؿ?(**R\\6^kkk:::PVVf̙}ѣGiBEEw… WPSS q ^zz:7n~c˖-HJJ+ƌ#fikk {{{{Q*6Z(-TBdKd EsKYBRZv^ZDgf{~S^񈉉a < Jyy9gfѢE466W=ʟ)f>cǎfN-nţGصkxyyE=!++K ˗/ʼy wތ7pb믿UV޽{Yv-VBQQL~xaƻw?󣴴T;͛PXXȣG`ڵ:l0prr4443gRRRу3k֬9 }Μ9[,--#005k͙ fbѢE8::;rHW^}XrQQ>}WTTqGG#99B<==Yt)WѣG2`Xnuuu̞=ݻw›7oܹs 8۷oS]]III8q+~Wbcc0aWz'eee$$$`bbBnnٳ*~Yx1ooo Ǐ3g&&&444ȩSxDEE.ݢeee(,,_~444&HII~"""DdǎZ8АLEBCCfrssccc2dXTvC HHH@JJD,A IDAT ӧ;GSS/_Żwē_YY͛7Fll,˗/5krEzAFF888JYYCE__`DLPGG}Mcc#>>>X3f`ll<,_z"##ooo͍҄\]]o޼yÇ:t(撖Fgg'8;;E+W8p ʓ'OPSS~ښ\ߏ}6$$$PPP`ȑ̛7O۞0vX"##eeeܹsx{{/gfϞ)ǎL0sӧcܹHIIQYYɖ-[xׯ_Ç <}}}1(++cѢEB`=zĜ9sÃÇى7)))#]/s1b $wʢØ1ctϮ.&Mڵk144dL0EEEvMyJ?y.>ԯ7n`jjJrr2ZZZ|*|}}quuEUU***x7n$##[[[144$,,}с=O>n߾k֬(**bbb"f6u ޽oFLL QQQ >"444زe gϞEKK OOOxΝ}}}fΜɭ[HLLHKK 0c7"##Yl?fڴi :0>}:---3zh޾}-666aeeEYYc777~ʺuz*K.eLJJ \|jrss)..LJ'ODFFpCv[͛G޽9y$/_(iӦEee%$%%$@Kzzz-/_$44===jjjXl666\r|FEYYHIMM gϞG| TTT8}47ofرN3rHnݺŭ[pssMdd$eU__Ϙ1cprrݝ'OΙ3g3f K,ϟS0QVVիWJWWxs-9y$ϟQTTdHHH`mmMdd$̟?,qcӦM >gggG覊._OOO1˗?PQQALL Æ >}$`XYYQQQ!QQQTWW' HNNF]]-[xbMFkk]<\\\());nTn\Ds Ž;pvv>p%%% 2rٸq#cʕHKKFAA---dddܹsٿ?DFFb``.\`TTT0uT 8rtttPPP`ӦM#G7oސ;v~~~1BF!: w!-- EEEȑ#RZZJaa!jjjxxx@BBCEII۷ocooOKK ,[6oѣE)+++tuuyƽ{ $$$SSS޽{=;wDJJÇcnn΋/Ņe˖7Yh888p =z"'OёqƑ/TXZZb`` ꄄIw .~zꅟ7oޤ;vJxx8qqq9wwwoFI= Ą6n܈&ݻ===d޽,[۷o.$...7ZuttD>}6ܻwmmmXnTUUիWƆׯ_s$%%QSScĈ~zO)PVVFWW&Lۛ۷o3yd(ل!Jy9O>ŋ;vLĄ?|@kk 7^rVqvv,\8 SRRBbb"ϧݻwGyy^h,YBNN/_J^?KAAͣ_~˗/燻;DGGSWWǟ8::beež}c 28 DFFzݻmdeeQ]]%'NРUV@BBvvv[?")))73sL Xr%۷oW^Kd+**pwwGNN8pB$d ?~իcǎe3w\.\ {XS>}sgZ[[ gggQm^POcʒȹϜ9SZZJvv6ddd`ccCAAo޼Ą={&ZRRR|IBCCijjb y(())ӧOYx1rgrwwŅٿ?&66#G̋/ذaW^%000I'..iii/_.ܣ;v`mɱqFܹÏ?3k׮%,,6LMM}6[HIIAZZøqPSS{>>>HIIf~H=V^MUU?ߟ>̨Q8qh Gjj`<w P7lСܼy6޼y;cƌVɓ144Ǐqttݻ455accCFF޽[(v͛9s 'Nd…룭Ǐa޽)SCeҤIXZZ2m4$%%QQQyyy޼y]UVVbhh={VXA~~x,Z:;;a񴶶boo>}%sUU=HHHm6ӧi&֯_O@@ӧOgҤIKVVкIIIɓ'155e 2?~ٲe uuu<}]]]IJJBSSpTUUٶmǏ'""~annNpp0ƍvBBBpuu%%%%%%233`V^-wNXŁQUUgϞɼy󈏏}o߾̞=[ȟ̙3Ei} Ǐ' @@9s VVV"}3dv)))L4I4rrr,Zo߾1m4!*AAAw$~7oݻwӯ_?rrr055Xpss___.\-yyyѻwo.q|f͚ڵߓŭ[ LÇLee%˖-c߾}:iii </^ &L0hiiǏAvv6---899Cee%8::@~PUUlz___;geeEmm-+V}}}-ZD^^jEEE|k׮l|>}PSScXiiiCΎS[[ׯ_qQ===ϟOhh(o۳l2FAWW7nܠ'OPQQ3jjj\x;v~ՙ8q"|y1߿'??J)((tvvĉ5k1l0ƌ#߿ț7o;Ch">|H߾}y%>D__e˖ ϟ?_~aco."}%%%(++ .rxx8.nAyy9;vE~fϞͳgե[nL<ŋӷo_444x9|򅼼<2e {% RSS@WWLLL EѣWȑ#]]]#%%Evv6׮]CBBiӦtRINN^%=z4{a2---| nܸoزe .}}},,,R|JKKdxzz"--˗ijjbb;sL<==پ};9998;;DCC6l`ٓ͛7rssErܸqƢ b<}#Gŏ?EKKvqvv@LK9Bii)>---lllHKK?SPPP,,,Ą<֬Yquueƍ$&&ݻwٲe *|鴷S]]:GɉK.all{~{QQQAJJ ӧOGSS7opA6o o///lllHOOGVV>}B`` G庺:dee @ c#GPQQϞ=uܹsKFYYYXl999ә4iC\Kbb"())Emm`&xb\]]Hߟ3tP$%%`Ȑ!BrmmƦM[ (**Ν;};v,'N@[[ Yd 899兽쪫xbΝKQQ/&99Tkk׮/_2m4>~(/s},,,j>~پ};'N@MMM֯_ϨQ&11rRSSYf [l˗/,^""".cǓ̱cǨ@RRWWW***FGGlD̰{!dΝKNNpUhmmɉ0sLΞ=Kff&W\a޼yѣv={D^^|۷XXX ))ɂ hnnfÆ ܹS***077ӧOX[[3`vͿ+"viiiRx"?d׮]RYYIAAނZ&^tuu܌ GgII ӧON>;}ѣIhh(|Ggݺu37779r$999|MfxxxLZZ鄅KFF&&& //Ϙ1c***lڴ kkktW9ϝ;"׮]#88={NWWk֬֘?˵k8qn`L8ǏȠA000`ѢEP\\ٳ'>|uuu"##IMMeҥ믜?W{=ÇK~Hii)eeeDEE1ydyΞ=sssTTTp߾}#== ae͚5gË/(((֖UV‡PRR"((K2bE@UUPHvv6>}}}}lllqVVVB -2e ]]]tvvbnnNYY`ժUҥKqssgϞ\~]v1n8jjj{.FFF8;; Ԁ߿?ͨr Ξ=Khh(Ç ^x'jjj ս+ٷoL66N<177W^r F/˖-cС <߿cff&]]]ۗ)S0m4233۷/_&88ϟyyytttg3ÇSDŽ De6~x,,,$%%Ξ= nb…ɓ'Mrr2޽#))I<<<8y$s-Ο?ONN/^(&PRRbܹx{{sAN<Ç100 555`l۶=zp!V^Mmm-&&&odffRPP$^^^>}Zؙ6mڄ$bKZZZ&00ŋ˗ݻՋ#FPZZJpp0FO>1j(F)^waݺu|Jeʔ)֒ƍy1Cزe\p߿ƍQ `PNN333ؼy3QQQ8p ?~,III#//uuu j*ܹèQ߿?}İa i)www91b :(PSSCVVcr<?dq)!p'NdРAիWJNJuu5-b߾}L2ϟ3gF"_ɑ>޽ :::_ѣajj*{Xd o޼AVVlBuu57 ꒛ˤI/D^^K.QVVFSS^^^DGGESSڢ˓'Oѣ,_\0!())#cccΜ9xIKK#!!ALL #..H455>}:#FQϟ?́EWWH\]]y޲26l444؈)B@@;v`̘1PUUESSEEE2|ZZZDQxcƌΎ={R\\,d\~2>,>o޼󣩩˗/zjB[[+W2| ŋL0!C0o}bΝĠLUUϟ'##www숏',,[2l0cڵBADoڴ KKKΞ=+fX[[b v655ȑ#ݛ߿pBoλwPSS#-- yyy6lofB²~ZZZطo-(ǏXZZ憎 XXXqFVXALL &&&[[[.\ gϞ顣ׯ'N-0h 6mիW"66LLLHLL$((///:::cŊ3ӧlݺHzz:ӧOٸq#.]bٲe|<Ϝ9Ê+7n߾}Çݻw144RҰښ6lbggǁ8p/---dffG&L'OPQQaĉB [[[ҥKUUUMFzz:'Nlڴ$ٰa'O֭[DEE"߾}cӦMOAA~~~ 0Ǐ2yyyĄf~eƍ TBNN L{{;_{{{޿ϐ!Cf\~__beeѣGy|6111͍wRVV… $..P__+vbСTWW### TTT닡!/ܻwӧOw^֬YC||<^^^2a,,,DzԔ'=*2o<x>}D|||<+V )))Kee%ٜ={]]]zٳϏ͛7sI:ڵk>L^^Xv+iM6ԩSR[[˯СCYp!ACC+58~?<|V;FFF_={ӱ˗|ӧOx[XXC={rilBgg',XQFQRRٳY`hiiQTTDHH cҕ!CA@@9990~x<<<ѣ3f̠UVˎ;8vk׮ԩStttsV\صhjj$n...lڴ8zIII (**2bf̘ҥK133bLZZ۷o'>>'O$|bqE]][455 u޼y/++---IBBOlܸ(9qo߾grA.ŋ=~6'm۶FBCC100`W ƢEX|9MMMl޼tTTT(++#www/^̬Y022BJJ3gqFAuttkbll۶m#11ǏLpp0)))ѯ_?z쉯/222|I999ʋ/ٳ'"//ϦMܹ,Xw`…ܹSڻZ)))ܾ}eeeǏ?# 3gΠIpp0wѣ()) ћ"skpuu ƍ7"$$ӧOS|||_ ‚R(**ljjj7o< 221c`dd_|ŋo1`144䯿b޼y\rB^^q )>>->|`ݺu޽7oް~zߙ9s&477Ɏ;ʕ+BVV111ʕ+IOOg۶mӇ}QQQ)f"--zɞ={(//}}}TTT ^_~SN-FFFXXX._>|GX[[SRRB~~>ү_?޽{dzgXp0KJJ2i$_ɓ7nؠٰaHIIpB|@]]]^۱FRRvݻw100 ++ ())--- 444`oo7:::,]]]$$$;⊈֖۷oTFFW&66gggiii}:IIIܹs)))LMM9t>} .`ffF`` GRR===^zrrr|}1|3$$[2k,OHH`ժUأG>}Ȳwvv2i$^|Iӓ?j={2m4V^M{{;>>>HHH _}}=aaaDGG܌7(**"%%ѣG'ϟ'::EEE ܹsՋH֐)))ѷo_ƍ'xwٳg;wSo>044:NJvv6dƌ ѠAޞIWWϟgx{{s8p ,NuYF'NHٹs'vvv͛;w.JJJX5k0tPjjjhii7UVKuu5xzz"!!jjjիW۷/[lZZZ8u.\n}MM ljBٳgq)aŊ0p@HLLDNN~znݺEMMׯYx CnݺL>vYJ֮]ˣG6l::: 27o2rH/ĉikk-ڏ?҂XXX'O)))>|@^^Hxu[®\ٳQPP ;;uuuJKK9v▾n:2e 6l{nK屳9 pף?_soܸ ';;tR^3NG3 $"w磡ׯ_BOOpTTT0''9sPZZ*xyy}vqvvF__KKK>|WUỤG~:/]]]1p@,--BCC>}Ν;)//B d6lV0-Zĉ).., Ǐۗ'OB`` QQQڊD'''|BmmЛuuu·o߰M욚x=waҥXXX`iiɢEPVVsss]&xǏI~+W EFF>}`bbBXXGalݺGGG cժUHJJڊ|===Ar\f RRRTWW Xڄ 3gW\ԩSRXXXXX|r kkk^~477#ZZZ044ĉQTT ɘRZZʚ5kx ڵSNN>}000ל?,ڰUW^hkkSQQYd /^FHNe񴵵[~)1ݙ!C %%E>}ׯnnnHHH{n?NCޛc}ۻLQf B(TViPͣe!QZQQQ>>޽{yC 0͛73yd p,\333~wEu˖-DFF'=ʻw8wx{{3rH^~-cƌAOOO0û⣦Cjj*ׯ_g ##êUMӧeeeRQQ!⊖ cccZ[[ŰVSS͎;2e DGGSQQI(,,͛L6 ___***`t }}}#D:t(:::>}CCC>400@MMN^~My 7aGzz:yyy8::ϟ@Ņf)**ߟv033CFFɓ'ZZZ122B^^K.!))ɰaèW^[:;;֭[>>\r%%%dddؼy3+Vؽ{7XXXJJJ<|+++-[&h^bXYY1j(g.[n%44,iii!>>-[PTTĩS:t(ܻwKÅ %,,L0ƍlZZZbooOTTaaaAPP7oٙ2F)DmmmĠNNNMMM˞={FE۷oijjbĉ 6LeddHNN%KK^^$%%1~xy֭#>>Fllls=zEEEqQƍGbb"&&&,Xlڴ"nܸO066ڵkۗR011!''/_`bb"2~"!!lmmy>|@AACCCm&ϟҒ a͚5<~I&1yd|}}y \|Y@ qss#22<~%TuFnn.舜^SYYYrXYYakkK||<XYYO ___KP@J+SNԩSϢE(..O> h={.]Djj* `رݻ#G2sL֭[!ښ}47nܠ3g2UUUŽ{HLL]]]x...ЧO$$$={6ZZZRPPKJJANN(** qӷo_w2/&##?~пz쉖>>>;;;._,ĉdggƚ5kFGG0n8FϏ\ΝKMM DGG BBB&lmm*]]]ϵkptt<ѣG#''ǭ[ $$]v)L_a``9s;o&88X/^HqqQF$Ϟ=ӓg2x`в-mgg;0arrrkܸqe˖(DRRRDGG@AA3gDKKw;*++ٰaW\AQQQ ~ .dܹTWWsy.^ȓ'O5jUUUƕ+Wf… 6 ^*%ܼyS 6:t(?~FBBB˘`mmMgg'+Wdذa ))#?~={n%=z6Ͱ&,Yŋ)))G3K.G2qD\Bmm-ܻw;w0o<^x9~8JJJ\x[PTTDJJ ^^^̘1/_K,YBQQZZZµkal޼;;;N>MzzӦM#FٳaҤI,_ooo<|0;vёy1$$$0r5Ο?ϧOXbrrrdggSZZ;hhhPTTĦM͍>~jkk ggghhh 33yQTTD[[!!!$&&r … ?>bmmMDD \vϟsa˗/b%ӧOѣtvvȋ/8u111cmm-fffhii1l0$''Ç̝;9r$?~$::F&M111jKYY6mbٲeRSS}Ǐhii1x`^xs۷o֒6k׮իWܼy___:;;qrrBFFӧO3uTRRR eѢE}5k`cc#"w֮]KMM ttt,_~Ջ/xfee۷9]T-ZĆ #FFFӭ[7{x{{tR޽{ƍ033gϞ듙ɕ+WDPR9 8wG7n..yyy}Fvv6+VM6|[%<$''f6oׯe̘12bXEE|vŷo,Ǐ888Cz쉡![lalٲ,,,066f =ݤIޞr,XݻrAEEE|---MKKx^UU쑛K^^FFF$$$p%u8}4.]ƍhii1~xZZZgƌ^4/_ʕ+ٻw/7n`ժU"| ظq#.\ 0իWNYYaaa[NEVVÇ7\|@HKKٙsQUU,߿B$ݻs ^~-={ y&0fEoذa7XQFYYPa"!deeq***JHRRIIIGEEQQQ…9999\\\ݻ7zzzZ}FNNa[[[STTDdd׏%K`eeEAAyyyHJJ[f͚Ō3;+V‚۷DOOO ^u놺:\vMkjj8{b-[LyҧOLMMߗǏ,^777>oƛ7oHMMEKKK x?>QQQM}}=ZZZcaa+L0R򰱱aDDDAVV!!!lذYYYq_qZ[[Νc"0еCɡL܌V222$&&R^^ٷo|_(++@}}=gϦm۶ISSSN,Y{{{طoDee%8piӦall޽{"99Y+?ΣG077Nhh(wEZZ'R__Oxx8RRR1i$q4qDJJJ8r'NڵkIIIAZZZܺk:tHw.,brrr())1tPNҥKAWW}ҳgOO+# zݩ_EZ:wܡkkk pssCAAiiirrrPQQӌ1###E{,33S.ڈ[dk-,,XjnbddDEEnnn(++CGGMMMɮ]&L -- KKKfΜɺu;v,ƍ333̙#ȅeeeȐ+ TVVrejjj0`\p___022hWbcc~:7n`߾}xzzrxmoo۷oL>tQUUeܹĈRAA!!!!?~`Νlذa d׮]=zv8s BaÆ!))FFF?ݻ3h پ};'O}}}l۶(ԩS;v,ܹs9999t~DDD"Uxx83f`ܹ.{Á3f SDR;::011ANN=ʫW}Ȑ!Lkk+DEEaii>/_DOOTUU111!::cҷo_퉈`ɬ\ptt$55[[[ژ?> ȠɬYXz5rrrܻw%K(:...xzz ʻwPTTUx466r!V^ͳghjjBBBCCCjkkBZUU3f@AA8|5{Srrr>|8jjjc~Hxx8&L(WNYY˗/̌ÇǏ? 鉍 ٳwRWW ?N0޽;ݛdA֭mmm񃒒H16l&)) A!''3Hyy9AAAx{{3c Dx\]]9z( >>>,^mmm)**6WWW߿/~;vҥKL:=z###bbb9s�]]]pJII1rH0444LCCeee[MMM83z*Gf˖-_¾}8y$քpAjkkg[ʐBEExQ򑕕Ϗ+VsaCI IDATuttƆ֭[Gvv]vg2k,mFjj>nݺ1h 455իb۷o߿?)))ڵ/_E@@7n$$$mmmBBBVtC===(((`Μ9tttK]]|)SDnneee < ++ŋ9~RRRے˛7oĄcǎ!##CHHΝcٲe}!CvZݻٱc+V޽{Yf ŧOHKKݻwS`bb۷o3f eee|rrrرckhh6%%% 4>ży #22֬Y#4ZZZ ^L\\>ό5?~puۑׯ_TUU1i$"##lÇ:uJnBUUU4~Gٱcٳ֬YIMMBEѷo_'~\m۶!''Ǖ+Wʕ+TUUuVfϞٳqsscL8UVmEWWWIJJBZZiӦ1|nݺ‚ݻwsaƎ+TUUj* سg"ϟ?ޞ/ 3رjV\Itt4<{ 777ٳO={6SL\ӧOc``@bb"L2dMׯ_R$7._Lcc#0k,xʤs-|}}8p <~ٲe AAA_ >Zu놎GaŊcjj3={%?2uTmFkk+o߾ENNm۶Q__Ϟ={ӧ#F477deeٺu+rrr1g&O̗/_طo]ѣGKnJDD/_;w2i$y%$88OOO֭[=z ..L,,,ڵk3o}x>>>҅Uӷo_x!#GӧO̚5={Yj?fСDDD //aaa~IxȡO0 ĀÇ$&&RUUUUUtޝj,--IJJёG(^^^ڵcR^^&L@ZZ((("d777>6mmmeȑ|CCCUMMM`V\qww'??BF%_~%&&aܹHJJܼy#GYQXX(ӧOo1`PSSCCC$%%E R@QQM6NHHBq5233ߙ1cǎUKĀشiݻwgɒ%!%%ł aŊ~***ظq#'OɉGammŋ F|ڿ?_~Ր aΜ9tvvgYV^Ν;qqq!88J,--cΝY777JJJ:u*ƍYYYu=zHKKϟ?h)}_~ϗ/_~HJJ >>>hkkcddį_زe ())ӉʊӧOvsٷo hs碮ϟŋpm>,`}xyy@~~>_޿Ouu5;wd$%%Q]]h!Wnܸdee%ϟQF"!!yHMM e144DZZN>L=8y$3h fϞ-]]l yyyTUUȠ'rhkkgϞ퍬,2f s7n>|9994442d'OFKK iiitm~o߾f ĵk>|8߾}'rmnܸ|+++￙2e '88k׮q)af211Ғ[nQ[[K[[tttPRR.~~~1sLfƍXZZˋ/GBBcNJĩSחѣGb |#G8fz)MMM TLǏ_^ ,--U)--%K ''GBBpE}Jmm-<{nݺKQQ/BCC%%%a ##TE|BMYYoƆ9s(t#G[nbrYjkkQUULj8q"qqqdeeO>č7=z4AAAL>G1gqQ'&&bkkδi(,,DCCCDYlIIITTTPVVF=9r$K.ޞ_~EFF#G?$::ZP\Bnn.>>>c}vΜ9(++sm֬YCtt4Æ ٳghjjƃ3n8$%%innGԺ]]]M^^jjjߟNOqq1cҤILYPSS… :sE&MĠAPSS#))ՕH>Lbb"ݻwĉ| KKKpvvHtٶm'Nr9uNsssrssٷoqqq,Y{{.\rrrӇ|@cc#eeebLnn.8::;޾}ʕ+)))aL>[n1zh455 $008EZZ 1/?><ZZZhllɉ͛7~HKKё/ To+WDBBB B{E||<9t^~.RTTDuu5%%%bll̠Aرc?~'OԩStޝϟ? )..f ٢Iֆ+L:hΜ9#j< ŋsLaa[oL~~>;8::رcinn&11悾gĔ[[[N}HMMe;vUVx=ׯ_'..NFA{{;"D@@Jjj*111X[[i&3g曓夤 EIIIX>L{{;(((0tPaa'N>-d `ѢEpYN>͋/^iӦ!))'Odȑbǝ?f\t<ߟtBBBeܸqKϟb!C O>ٱ`xv ߶w닾>STTDCCTVVbkk;䠠Xr:GUSS B8fddDSScƌԩSL6 {{{ptt޼yٳgYj.]'N?&F3]t֖"6mڄRRR2j( PTT$$$Vn߾ɓ'9wɓall̛7oرc޽#../_0d߿<Ϟ=KHH>>>3d-[Htڕs޾}޽;7?7o2gn߾-999¯_GCCC@ۙ2e 'N999N:ԩS:tM]]F˗|ǏE9y$O>e,ZM61i$"""ӧOv@MM ݺu =zDJJ=zmmmÀeee>~nbѢEdz|ry&&&:tX&MĥKСCӵkWaDuB~~>dggcffFjj*dee֭[|X"""5j׮]ׯ_;:;;y DMMMYh֭#++ٳgw^,--ihhgϞbbb/%%%}ښo2{lddd8<۷޽{s hmmҥK%+Whiia֬Y듕ŋٳDDD*4ZZZ$''SSSèQHJJBKK|䨪ǏR\\ɓ'G]]]X ϨaccYYYȾ}hooGBBtuuiii!;;KKKɉ+Wン3BdDPPEEE=HQ ՕÇEHHۗ]R]]1s o"''GII \rիW#!!A޽ȑ#رCboo/ͻwfiUU#Gܸq7oTWWS[[˺uسgH?2zh"""ׯ_+666LgϞ90bbbضmZZZ=Z81ssss5JXqtt%%%OIIRBBBFGG &MĢEׯRQQIݺǔ˗/ӭ[7 ϧ455ڵ+UUU_ݻw&p%ۿaUUU7p1`"7ndϞ=eBQOO]vQXXȍ7|E~e˖1o̟?ݻwرc%iň#Ǐyfnȑ#CKK* DGGG1qZ[[IHH={(leֲ|r^~Mrr2iiiHHH7W2sL $hjjRVV&cllŋv*PWW\H~8991yd>|8pY&NH>} Xp!ᔗ [(-- 0tFϱӧ|333 EEEy 2V"\z5o߾V:::شi/^`ڵIMM ߿gʕܸqZZZҥK,%%%#))I||<)))DFF„ X|9E׮] >>гgOmFFFÇgڴi:tZ6n(S srr2 DDDz {{{&O̠A… ىRRRBaa! "11KKKlmmEdOGGGȘݻ7kcΜ9$''ʏ? W"##Cxx8'NDCCl&N?ѣGgСCիb{e:;;7nTWWNii) :iiiVSS֭[lذA quu%::"x"rM!;v,zzz\///2229??ׯ_pBN8exܺuׯ_sA'88TPUUEII x .xbÇPSS 8zʢHt҅K??9}PVVƄ Р@dSPP X%cǎrqsscUWWGvv6555waȐ!ˆ ppp`׮]477#ҥ SNF|}}ښ ضm Aff&NYY555ӧϟ';;$ Fmm-,Y+++/_ns9/_ξ}Ғ$##fΜ)O>Օ/2sLbbb#ӧOK.\---OTTaaaL:)))hmm֖ZTUUY|9۳rJ8}4(++ANNׯg„ ȑ#fdeeѿ\\\XnlG\\&&&DGGpww(߭Llmm=z4~~~`dd$:7oϘCQQرC82äRRR011اObcc/_Yfq-޾}ˊ+ӧ#766wҥ ̟?_r;Fdd$'Od儆ezŚ5kXtȣwڕ .NII W͛74555|ߓD߾}ҥ ǎcRPP@FF>}ŋTVVԩS_SSv=JXX...BMCujС|rcӦMX[[-ƍߟCRYY*ƍ#''k2rH˗/lٲCgz˗/qvvfQUUŮ]"::Z􈈈ɉnחZ~=HHHN׮]SSSN}D=rwժU 8sq~JMM FFFB%K ++YYYra<<<0am62d>smr=!_Y~=eeej*>}cƌaӦM̜9nݺΦMx!EEE=.\@EEE$$$pppƆ+W?`ff&܉affۗ/˝;wǏGKK޽{cnn>fʔ)ddd`jj&߻wK,XΦ ___hhhɓdgg7VXoߨ%''E၌ qqqL4 8s Ǐ'00n޼IFFg3޽K^^`҃LJ͛7d doȐ! ''MHH۷oaÆ +D .""Bj\\\믿HNNFOO &pa]Ɔ ݻXYYaaaׯٰa7ǒq1h <bђGpp0(++ͷoPPPǂn:?ΰaø~:'O˜9s_~QVVFUUgϞgϞ;w͛7,𲲲 BUUɉZ?~+,,,}n:~Ν; ///lmm'6mԔ4ƍǂ Ipss͛7|q[dѢE׏cll6)))7|”)SH@k:uJ,_~ͣGTNo߾ܾ} ZFIII.]&((bZZZx9笠ƍ'$F"66 222سg䐕Ŋ+IϓIOOuuu޾}555[nq;wȡC1c;v͛7())ahhȶmё#G`ccÑ#Gw޼zHA_~b\^^...ܼyV^xzz~z.]JSSxyy&5,Z޽{Lmm-HKKAUUӧO'<<ϟl2$$$ÇܿSSSzATTsAy_FVV7o`ooOmm-xիWh":;;9}4<F-[Ƶkװ$55U`[ZZPWW8̙C\\<8p#GCNNk֬aرL8rrrhhh\|UUU򨬬$22555FMqq1?~dL}:466իW ܹsbhh4ؠDZZĬY())ȑ#ⶦvvv͢EhiiAUU/^@rr`WVVSTUU;v,$''caaA~`ٲe*?QXXʕ+Yz5FFFx{{СCQPPݻwzz^AGGG޽{>hjjGnn.| ՕrTTTܒ-[0c Y~=уǏFpp0III0rHRSSٻw/,] DUYYs2a.]Ǐv|}} aooOtt48::IIIaҥHIIѣG100 ..]vѣGY|9˖-&336 `ݺu?Я_?y5 03g?(..F]] . --͟Izz:_|sssx@?~ݻw?dވ#Xf Y8eĉbeeEss3 tvvsN4 Tbcc&##^zw^TTTx/^___v؁P͟?n߾͒%K())kkk'++ 6mMMM`ddļy󈌌6uEEEΞ=KGGmmm(**2j(k{CLMM>|87n@II 777`ʕXZZr-mƑ#G$%%Ή'022"77]]];/ܻwOA_x!`ooOssx=y)))޽;RWWFFFSB7o***hiisNillqwbccCqppgϞܾ}]bffɓ'QPP)S͛7yϟ?g˖-"%%ESS:::l2tuuSSS<==DFF<䈍ݝI&!%%űcxlٲM6̎;ׯX[[ӿ>}Dqq1B쌡!aaaioo'** $%%yzMHHŋ#''SN477į_ʢt${F__ɓ'b޽$''CzzhE߼y???(**"11nݺe۷/ѸpHMMeÆ puEGG瓐'(((B\\{ˋ/R__Ouu-ZHȸ+**055ڵk[.]ŋXZZ100ӓtzAKK (((p z%֭CSSSd{GMXXKNN Rs={$%%GSS `iiIYY>>>r-̙h#΁'mmmݻ133#''wr)|}}EI ???w֭[ ӧO,^ggg={Fjj*~~~|zRSS?>xxx:::׏C۷rrrt钘sQs9s&cǎҒ%KPWWG]]'Nŋ|ooox)ĉF>}xsyh3F޹sŋƩSѣ鄄wػw/]tggg{.Çg֭̚5bttt5k[ÇGdd$Æ իWѵkWHEE'Nŋ2e ߿ǏuVpssCVV===BBBdTWW3{lAJJ?R\\ nnn#'''@U;v '''Ν;Ǚ3g022BZZ 6m߿gȑ pEEk֬ݻӭ[7y0͛7+W %%%ԍYzzz}Wn߾9t҅srrr044 Nʈ#044D^^'ONmm-/gϞDDDн{wʐǏ[__ϐ!CΎѣGdee)))aر-%%%|IKKcɒ%q!8Yz5eee|OOOLLLصk:::|j:;;DNN< ۛ͛71̜9`F͛{.ܾ}PUU)))̙{ f̘%K(,,D_~` JJJ8q5̝;&(((ƍKbb"[nNU IDATVpO~cbb uI9}4///^|)f殮HKKSPP@\\ӧOڵkxyy+騩!##?/^)>dÆ b9}t6l NY^6lI`˝;w@[[___D555~~~HJJRRRB]]\p???޿Off&7oޤG$%%̙3b$%%8{,%"mmm\t+V`eeŕ+WK'N`ff&Ŀttt`ooIHHL:F1G9sFK{HKKb_εk׸pA{/طoZZZhhhKqwwGVVGGGLJJ"--Ç"ߧk0k,ׯ_^  ڵk EBB155%//e˖ ECC]VwՔ3(+++ѣo޼ÇvZ v{a͚5DFF2k,<<<7o_fڴib*nNTTϟG]]6:;;?:t(cƌ!))B޿̙3122˗ZDJJJطo 2{BCC۷oGsvv455իWIOOì[N=vJee%[eeeBBB1c/_dddpI~WXv->G,9y$Vb֢رcYlaaaܹsWBcc#111 0rrssߟ0|||8rnnnߟpDZZ޿OjjH3{lN:@ߵkݻwGOO}}}ƌCNNhii,XYYBUU]v%%%\mFll,̞=={䄡!ۋMcc#OΚ5kPTT͍$Xb'O>"%tttmDDddd`cc#,CeҤI 4#G{{-ɓIIIaРA?~RUPP7oW |}}ILLDUUU>/^Lxx8uuu,Xf\\\DII 1 aժU(++ĦMؿ?HKKj*˩?rIbbbprrbΜ9DGGӿ֮]˱chooGIIo[URRҒN6n(}>}=^TTͣ[nDEEQ]](qaٳg8p?h>:::ߟ~vZ$%%rrr1btǏ‚͛7UFF`"%%Eii)/^`ԩ}Vشi߾}#,,sBss3>}‚>InPP/^@BBǏo_cc#466.8::O^زe gϞ)))t=z4>>>رCHiiiavTVV2| F~~>:::xxxyѣ)((ڵk 2}RRR1tPLMMXEEV?"/_߿fbbbׯ'ptt$33g ##Dk777aڴibѣ && 6ƤIHMMDj}}}:;;ikkCOO)Sׯ8q"/***? Lbb"iii ؿ`$%%~N>M^^ޤuVn޼!IKKccc#W\xooox5'222Xd cƌʕ+,Z}}}=>ɓ'!++:_̙3ĉHKKӣG9|0Νcĉ3~x͑#Gػw/HKKg͛EEEl۶;wRWWl޼Y OΏ?>|8TVVRWWǁcȐ!YݻSZZʘ1cׯ_Չ*,Y‹/?)))&N(v_~%33ÇE\\~~~XZZ婼¦eZ[[ihhƞ۷tRlllDcm466tR߿ >>>BpATUU7۷cmm9~8 , ,,Ǐ؈9//b8rHkdĈڊӧOGNN>dy9#::/^```..J{{;pMw/,[L@133ŋ(111|Cm6imm%00[[[CSS888'D]}…888p%KEEIIIHJJraܹ͛7!l7ΝÃ1ceZZq߿>}P^^O}Hrr2є2i$ի666$&&^JPP9sGB@@'OLJ &oJÇʊiӦqe1c$''}v>)))BdHKK ǏG__۷oSLA]]ѣG'Z[[QPP֭[())1w\\ׯ4iMMM8::CRR 9 $88߿s9}VTT_~\p2Ν;Ghh(#F֭[޽{ټy3'OdƍHHH7\]] ͛ 0& 100aaa萜LVVeee8:: EEEܽ{ϟƍkpp0666ݛNʬY8\uu5?f|􉆆~-,˾};v,[lΎ ϟ?STTĚ5k믿;v,hii -#F@dd?6VX!{le;v`رӇڵk [[[VX c̙,[ EEE,Y³gϐ555BBB;Gzxx8~"44O>Q^^???ϟϹs022̙3ٳGnܸfΝ;$%%e6mtuu 3blmmӧOcooШ,//… !&&'$$hhh`֬YQXXȘ1cXnFBVV'OٳdԨQGػw/f .пPUǓ˗2>d˃ o߾>}͛'d퉍%77͛7s u]zMLL iiisaƎ/^ C=bƌzj*++IJJbΜ9477ɷoHJJwܿ'۶mc₿?lܸwww`;%66M6Kf̘>ۓNbҥ/FDDT\\\?׈cddD]]qqqXXX~l"|5Fw ,777￉Ғϟ?gttt3f tuu  ۅ̙37ʚPQQa%%%˜… ]s [$(-- //OBBNll,+Wbrrr*dyeeep9˙>}:7nΎӧzj\_ӧBNN1cп---;w999,,,ؾ};ꈋMtt4---rE֭['CHII MMM޾}?c!ACC>>>qi.]*?y6mΞ=Ktt43gdΜ9̜9999LMM9qℰHR<<<(,,dҤIիW?{ E DNN5557׮]EoݺC1qD~HJJ RRRݛgϞ1m4~;3f`ԩŸqXv-ӧO:::QXX(,N*#ZZZ6lʌ5 .p=֭[NJ+:t(---XYYqi֙QTT̝;tկ_? 9u7odggn:Ξ=ҥK :::>}:#F`kkק III9~8^"33dee2e ~@?~ׯ 2{{{[q҄DݝL~Muu5SNPš3?'ORVVƥKPRR… xxx+8s \t 8~8uuǔ/^0aRRRذa!!! 0w'@і/_Nxx8AAAx{{穬DSS;;;lllr ĠEGGgϦ/_`bb ϧo߾#AcaaA\\8;;c+Gaԩ !!!}vlmmޞ6v؁8XXXֆǏڞMMM̟?]]]6oL]]xyyQYYɺuPUUM`{`aa& 4зo_Mll,***b u[BB7oqqQddd={6jjj\v/bbb"l}?ܹ3f5 ***;wk׮ b갰0KJYY۶mرc;$LLL%44UVQTTڤ ?r-=ȑ#"!!q!&&ƥKIBO")##/_&ɓѣ̘1a݋ S^^>>>xzz BMO>=L0Aa"M^ghkkEvv6w玲D'ՔrU;o߾[XJJJhkk)TWW#%%EWW[nӼ~SYYINNeee<Xlll2dɜ:uF>|Hgg'bccCMM ϟ3O޽{lڴǏݻy&vvv\|߿κu먯ɓ' ;.4x`tuusHJJ `___={FRRw l}???$%%qss]&($믿hii!555DGG~^~锗 E#GpQ:Ď;ϟ̜9;;;vC'>>QUU hlll#!!7;wBll,>>>9 IDAT!..NPP`bbBiip#۷otww3w\„phh(ؘF,--QPP ## ƍǛ7om۶ѫW/Ν9Sp{jiiRRR$&&rMܹC޽EFF۷#**Jkk+/^`Ӈ ^xͿsB¤kkkΟ?Ozz߭[ؽ{7eee_2|}}ŋi&222غu+ՋH}ď?x̘1%%%2e yyyQ\\ yyyyl۶_)UgϞ͋/077߿/_(KJJgEDD8zxtssz}&x7ѣGֆ1Obb"n y ٔXv-ǏgժU>}ZXvyxx !!AGGl۶Mx-ݝ ^~ڵkB^^^pjhhȑ#Аӧ3h >~Hhhq~:lܸS oK!zhddEEE;v qqqo;v8gǎqF/^'N>逻wRRR޽{$%%QRRѣQRRbժUlذA0GUUUvZL ͣ'"''Dž &(('OEkii~zJJJtL4K.;3444NNNذqF R9۷o(++Ζ-[6mjjjbggǖ-[N,--6NNNJuu5FFFֆ$3gn_~999:::cٳBBB 25k+6mڄSLlذP,,,b+lTTTعs0vqqAMM uuu?~̔)S9r$rE,Y"YpEn޼$ .d۶mhhh<mFLL \x#G2rHtuuyܸq(Ϟ=cȐ!tuu1o<6lQeժU466r!͛G~Z |OAKK/ f777dddȑ#}~ׯyM $$ݻw'sEFFFH<{~=5773~x9wgΜaܻw:_mWOm׮]\r󄆆 ?/̟?W^w=ׯ__WJJ:z(ښPOŋYzH477Gii)AAA9;w`ggՕ aÆ1rHgܼyiii1b 8|05559gggzꅸ8iiii& 5j222lܸQ_Bbb"ڵ ONjj0?>}:DFFRQQǎ#;;wwwۅlUUPVVFSS 3l0qrr"))˗/3oǏGII,CII %//3f .\ P8)//g˖-p숉Ȉϟ?#//O\\iiid>|(FFF|JƌCCC#!!aÄŷ)DEE7FIzz:fff3f jjjܾ}`<sss&MDuu5)))Npp0UUUqFVX" -LΞ=1}tTTTDCCZ*++vuaʕP]]ͮ]Vjj* 7nPRR¡C8p ))),_WR\\ܹs8q"'N焆«WdӦMÇIт &33EEE8zyAEECtt4ܽ{---FM\\NNN$$$`aaAZZ֭[122bɸQ]]M]]P?aРA_`jD9"lcbbpqqmmmK6l{1DGG#!!Acc#O><˗/ߟ}bjj?Jjhh+,Y~ ...T077bbb2vvv899ѷo_!}AQRR7o$!!ݻw yV ĉ0a߿ʕ+m'qF ظq#]]]ߟ$Y|뱠6 ///,--޽  $bzJT߿'!!A}=2 |]pe޿RRRtttqF  ^^x߿,7mD޽e\x777^J^PQQڵk۷ٲe ϧ6m=bҤI,\'O?9NBNNn-[&h#""YxЍ5^z gbjjJpp0iii9III틮./^ؠ|9s&"%%-˗155%11/äȑ#Yd yyy͛eݺuܹgggV^+V֖.\ȧOpqqAOOLQWWGYYGGGhmmϟ?s8kkkkRRRLJ!C`ffFbb"ž { ֯_ʕ+1cWfժU@ ۶mʕ+ܻwQQQ133Ғv?~Lee0Μ9y%wϏ\ 'O$&&F0űtRBBBX|91j?N^OOOW!##(š FYYƌCQQMMMTWWSYY#O>E\\իWSRR-̳߿/ٳgXZZ*zzz,\UUU>|Hyy9ׯ_glݺ8Δ)SذaFFF <KKK.^1^BMM CCCf͚caaAHHSYY響! +Ve&gggxW^EZZc?0a)**UUU"??ׯ_ceeٳY`COOR[[+_8 Sbcc̝;s郪*O>EWWPQQ!-_|˹v>̌cbddD{{;߿%K7oraDDD5k˖-###CɥKhnn:MSWWWg„ 8|۩ !!ׯsmjjje޼yHIIQ\\ԩS۷QZZ1>wwwJKK122gߟL/_NAA%znzzzg055ϏlHAA<=55ddd1c|oooڸpCxxx ##Cxx8Ç',, kkky&666̜9hll$++R*++ikkCZZm۶ajjJ߾}aÆ twwO&O9x G!55@9,ڭ\v qqqx‚߿ajkk)*AeeevPؿ?툉ၔΝcѢEѻwo233!99s!**yyyL87o0yd222R /gΜImm FII UUUyСCXYYQSSÇQVVf=b$X,X N<&nnnhkk#4oop1V\ɗ/_aL>iӦ&O<ʊK ݂.֯_Ϟ={?OeiŊ[hϙ3o HJJƍϟ?7n***1B(/*\ѣ^^CCaÆ1j(}6nÃJ޽ c|j$ <777部(ǏΝ;lܸW^7V^3<|5k֐M~~>cǎ'N`iiɾ}FYYvjjj055EAAÇGKK w222B3Օ0!chh\rٳg۷ILLҒ?S(((|}}eD~~> >x&33zŢEueeeB9+++|HQQM~)ԃLKKcРAHHH &&Ν; DTT Yv-'Oׯ={D033ޞ3n8?~ߧ7o3\b̙HKK'֯_"|?C߽{ & ++KPPmmm_hhhk.Xr%QQQL89s`eeEYYD||<ݘcdaa9>|@__CCC.^Ν;gʒΰaHOOCnnnJ[[#Fv ldzm6pqqALLr޿͂ سg999yFbzyyh"ZZZݻsI100(PWWpiiiTUU{nĉPQQ ` UVoHJJB[[ׯ_ckk˩Sׯ_ڵ#Fʳgزe ݣ\L4N>|Hvv6$&&y勀Tׯ۷o 7t̘8q"W<44͛7cnn"AAA\p-[KZZڵ ^z+PZZ ۷DDD˶mۄ]F||<~~~| GGG]P*..KKK=L0r6mDNN111mL628pdgg燄uuu ?BcJKK177'!!yCZZ֬Y=O>%))+Vɓ'y-)))I& ) y5ŋX[[xbttt3ۗ7n0uTx灾sNN8{m۶͹s4i˖-#66)S׏fJJJ0aC̙30p@ddd(,,$??/_1?777>~H]]$%%JSS}6֭[lذgϞ ,]TXJ}޽{zj$%%0`"""₲@JJ mmmqss[hZZZ ZCR]]MII 䄿?444`iiX… GUUٻw/sΥDLLLHIIA\\,--qwwҒ{ٳg ^HGG>}wwwlllb̘1XZZ$%%'''AԄ1---CZ[[),,ڵCѫW/*쐕BYYYӧOCVV%%%~:hkk kkk^~-,X:ܹeeeʈ &&x zzz455JOQRRBzz:~ K!LDDSL!99YȲeˈfB IJJٳɳg%%%˄ طo @YYA}vӧO 4ooo***HIIǏHKKErr2PQQ/_yH[[uuuHKKӧOܹCuu5r)/^L޽˨cnnNNNzzzܾ}t4556miiiܿ.\#l޼Yh,GDDpƏݻEEE1220="<~A~~>Fr DHH]]]ӧO4i3gddgg ٳ+d֭ 8mmmjkk;ׯCCC|}}‚*LMM)**b̘18::bmmM\\&&&'bӦM޽`rss (=K$ c޼yaѢES__sqoߎ!ׯ_!C **Jll,xyy K666 ȼStttx 555EQQ(++edee;o޼!99wAQmǿ `̰Iq) B 5(ZidN: 3BIalJE.b h) Rr? Yϟ؝RseOTTxxxP[[Ve޽Y;w0j( 񡱱iӦde(Cʕ+),,oaΜ9︸(;2ϟO?īg}ƙ3gQ9}4111dggcggG[[ 6Z F;vNFF/Ç?777RRRhii*.]ڵk}6aaaFvIKK f''', o&22RyCnn.G~ СC9}4+V޽{r9u2 r޼y=z_|;;;j5TTT(W^^^͛7n}:-_~A* ڔ6k$&&OXX7o棏>h4R())ˋѣG+jmll(//ޞ@Z[[yٽ{7ׯ_'++#G2}teQCgOJx 1bnnnTUU)-:D^^Zɉ=Jzz:&:BBBXt)۷o'99cǎ)njRo_NAAjR˫OC$SUU?o5]$[.$I,$IVBn AO'Ob4:u*lmf'MDHHeee}:W}}=AAA8::reg[b0W2<ʣrEEEBHH/^^.xt6XƑ#G'MH*bbbB۷o)"##Cdee !1cX,v$f͚%߿lϟsB'tfqMpBQZZ*t-1m4.L&ظqcxУrUUU !(++3gB\B<:B{bƌB'M^̙3JqvvVZBp(((S"&&F׳ >!BeI_ekkwܹsL<A~~~dxs1hRu sY_'=l?F[[9qqq]VCY?Mjkk$$$f\]]LJ˗h:;W3&%%`5nʲe˺{dA DllrVjmmeРA]+PfjA0\x*?~\p~VZe:;3j \`0dA+Wk."""vW[&\z!fY׳ !pqq:޽k: rH^Jss3gϞeرJ>ޠjT:::!C={6zJEff}!CNc„ 8;;wy֖+V0eO\(ߓ͹$I/TIENDB`glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/data/0000755000175000017500000000000013752535025022614 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/data/image_cube_v0.glu0000644000175000017500000002414513502206677026021 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDQsIDIsIDUpLCB9ICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAcAAAAAAAAACAAAAAAAAAAJAAAAAAAAAAoAAAAAAAAACwAAAAAAAAAMAAAAAAAAAA0AAAAAAAAADgAAAAAAAAAPAAAAAAAAABAAAAAAAAAAEQAAAAAAAAASAAAAAAAAABMAAAAAAAAAFAAAAAAAAAAVAAAAAAAAABYAAAAAAAAAFwAAAAAAAAAYAAAAAAAAABkAAAAAAAAAGgAAAAAAAAAbAAAAAAAAABwAAAAAAAAAHQAAAAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAAAAAAhAAAAAAAAACIAAAAAAAAAIwAAAAAAAAAkAAAAAAAAACUAAAAAAAAAJgAAAAAAAAAnAAAAAAAAACgAAAAAAAAAKQAAAAAAAAAqAAAAAAAAACsAAAAAAAAALAAAAAAAAAAtAAAAAAAAAC4AAAAAAAAALwAAAAAAAAAwAAAAAAAAADEAAAAAAAAAMgAAAAAAAAAzAAAAAAAAADQAAAAAAAAANQAAAAAAAAA2AAAAAAAAADcAAAAAAAAAOAAAAAAAAAA5AAAAAAAAADoAAAAAAAAAOwAAAAAAAAA8AAAAAAAAAD0AAAAAAAAAPgAAAAAAAAA/AAAAAAAAAEAAAAAAAAAAQQAAAAAAAABCAAAAAAAAAEMAAAAAAAAARAAAAAAAAABFAAAAAAAAAEYAAAAAAAAARwAAAAAAAABIAAAAAAAAAEkAAAAAAAAASgAAAAAAAABLAAAAAAAAAEwAAAAAAAAATQAAAAAAAABOAAAAAAAAAE8AAAAAAAAAUAAAAAAAAABRAAAAAAAAAFIAAAAAAAAAUwAAAAAAAABUAAAAAAAAAFUAAAAAAAAAVgAAAAAAAABXAAAAAAAAAFgAAAAAAAAAWQAAAAAAAABaAAAAAAAAAFsAAAAAAAAAXAAAAAAAAABdAAAAAAAAAF4AAAAAAAAAXwAAAAAAAABgAAAAAAAAAGEAAAAAAAAAYgAAAAAAAABjAAAAAAAAAGQAAAAAAAAAZQAAAAAAAABmAAAAAAAAAGcAAAAAAAAAaAAAAAAAAABpAAAAAAAAAGoAAAAAAAAAawAAAAAAAABsAAAAAAAAAG0AAAAAAAAAbgAAAAAAAABvAAAAAAAAAHAAAAAAAAAAcQAAAAAAAAByAAAAAAAAAHMAAAAAAAAAdAAAAAAAAAB1AAAAAAAAAHYAAAAAAAAAdwAAAAAAAAA=" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 1, "pix2world": false, "to": [ "Pixel Axis 1" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 2, "pix2world": true, "to": [ "World 2" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 2, "pix2world": false, "to": [ "Pixel Axis 2" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 3, "pix2world": true, "to": [ "World 3" ] }, "CoordinateComponentLink_3": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 3, "pix2world": false, "to": [ "Pixel Axis 3" ] }, "CoordinateComponentLink_4": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_5": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0" ] }, "CoordinateComponentLink_6": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 1, "pix2world": true, "to": [ "World 1" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": false }, "CoordinateComponent_1": { "_type": "glue.core.component.CoordinateComponent", "axis": 2, "world": false }, "CoordinateComponent_2": { "_type": "glue.core.component.CoordinateComponent", "axis": 3, "world": false }, "CoordinateComponent_3": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "CoordinateComponent_4": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": true }, "CoordinateComponent_5": { "_type": "glue.core.component.CoordinateComponent", "axis": 2, "world": true }, "CoordinateComponent_6": { "_type": "glue.core.component.CoordinateComponent", "axis": 3, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DS9Normalize": { "_type": "glue.viewers.image.ds9norm.DS9Normalize", "bias": 0.5, "clip_hi": 100, "clip_lo": 0, "contrast": 1.0, "stretch": "linear", "vmax": null, "vmin": null }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "array_0", "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3", "World 0", "World 1", "World 2", "World 3" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "CoordinateComponent_1", "CoordinateComponent_2", "CoordinateComponent_3", "CoordinateComponent_4", "CoordinateComponent_5", "CoordinateComponent_6" ], "data": [ "array" ], "groups": [], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2", "CoordinateComponentLink_3", "CoordinateComponentLink_4", "CoordinateComponentLink_5", "CoordinateComponentLink_6" ], "subset_group_count": 0 }, "ImageWidget": { "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", "layers": [ { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "layer": "array", "norm": "DS9Normalize", "visible": true, "zorder": 1 } ], "pos": [ 0, 0 ], "properties": { "attribute": "array_0", "batt": null, "data": "array", "gatt": null, "ratt": null, "rgb_mode": false, "rgb_viz": [ true, true, true ], "slice": [ 2, "y", "x", 1 ] }, "session": "Session", "size": [ 731, 529 ] }, "Pixel Axis 0": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0" }, "Pixel Axis 1": { "_type": "glue.core.component_id.PixelComponentID", "axis": 1, "hidden": true, "label": "Pixel Axis 1" }, "Pixel Axis 2": { "_type": "glue.core.component_id.PixelComponentID", "axis": 2, "hidden": true, "label": "Pixel Axis 2" }, "Pixel Axis 3": { "_type": "glue.core.component_id.PixelComponentID", "axis": 3, "hidden": true, "label": "Pixel Axis 3" }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 1": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 1" }, "World 2": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 2" }, "World 3": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 3" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.plugins.tools.pv_slicer", "glue.viewers.histogram", "glue.viewers.table", "glue_vispy_viewers.volume", "glue.plugins.exporters.plotly", "glue.plugins.export_d3po", "glue.viewers.image", "glue.plugins.tools.spectrum_tool", "glue_vispy_viewers.scatter", "glue.viewers.scatter", "glue.plugins.coordinate_helpers", "glue.core.data_exporters" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ImageWidget" ] ] }, "array": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "array_0", "Component" ], [ "Pixel Axis 0", "CoordinateComponent" ], [ "Pixel Axis 1", "CoordinateComponent_0" ], [ "Pixel Axis 2", "CoordinateComponent_1" ], [ "Pixel Axis 3", "CoordinateComponent_2" ], [ "World 0", "CoordinateComponent_3" ], [ "World 1", "CoordinateComponent_4" ], [ "World 2", "CoordinateComponent_5" ], [ "World 3", "CoordinateComponent_6" ] ], "coords": "Coordinates", "label": "array", "primary_owner": [ "array_0", "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3", "World 0", "World 1", "World 2", "World 3" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "5e2335ce-5613-4fb1-aa58-1cf342f7b755" }, "array_0": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "array" } }glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/data/image_rgb_v0.glu0000644000175000017500000001700413502206677025651 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAA==" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoIAAAAAAAAAAkAAAAAAAAACgAAAAAAAAALAAAAAAAAAA==" }, "units": "" }, "Component_1": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoEAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAHAAAAAAAAAA==" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 1, "pix2world": false, "to": [ "Pixel Axis 1 [x]" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [y]" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 1, "pix2world": true, "to": [ "World 1" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": false }, "CoordinateComponent_1": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "CoordinateComponent_2": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DS9Normalize": { "_type": "glue.viewers.image.ds9norm.DS9Normalize", "bias": 0.5, "clip_hi": null, "clip_lo": null, "contrast": 1.0, "stretch": "arcsinh", "vmax": 3, "vmin": 0 }, "DS9Normalize_0": { "_type": "glue.viewers.image.ds9norm.DS9Normalize", "bias": 0.5, "clip_hi": 99, "clip_lo": 1, "contrast": 1.0, "stretch": "linear", "vmax": null, "vmin": null }, "DS9Normalize_1": { "_type": "glue.viewers.image.ds9norm.DS9Normalize", "bias": 0.5, "clip_hi": null, "clip_lo": null, "contrast": 1.0, "stretch": "linear", "vmax": 5.0, "vmin": -5.0 }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "a", "Pixel Axis 0 [y]", "Pixel Axis 1 [x]", "World 0", "World 1", "c", "b" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "CoordinateComponent_1", "CoordinateComponent_2", "Component_0", "Component_1" ], "data": [ "rgbcube" ], "groups": [], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2" ], "subset_group_count": 0 }, "ImageWidget": { "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", "layers": [ { "_type": "glue.viewers.image.layer_artist.RGBImageLayerArtist", "b": "b", "bnorm": "DS9Normalize", "color_visible": [ true, false, true ], "g": "c", "gnorm": "DS9Normalize_0", "layer": "rgbcube", "norm": "DS9Normalize_0", "r": "a", "rnorm": "DS9Normalize_1", "visible": true, "zorder": 1 } ], "pos": [ 0, 0 ], "properties": { "attribute": "a", "batt": "b", "data": "rgbcube", "gatt": "c", "ratt": "a", "rgb_mode": true, "rgb_viz": [ true, false, true ], "slice": [ "y", "x" ] }, "session": "Session", "size": [ 600, 400 ] }, "Pixel Axis 0 [y]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [y]" }, "Pixel Axis 1 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 1, "hidden": true, "label": "Pixel Axis 1 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 1": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 1" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.plugins.tools.pv_slicer", "glue.viewers.histogram", "glue.viewers.table", "glue_vispy_viewers.volume", "glue.plugins.exporters.plotly", "glue.plugins.export_d3po", "glue.viewers.image", "glue.plugins.tools.spectrum_tool", "glue_vispy_viewers.scatter", "glue.viewers.scatter", "glue.plugins.coordinate_helpers", "glue.core.data_exporters" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ImageWidget" ] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "c": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "c" }, "rgbcube": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component" ], [ "Pixel Axis 0 [y]", "CoordinateComponent" ], [ "Pixel Axis 1 [x]", "CoordinateComponent_0" ], [ "World 0", "CoordinateComponent_1" ], [ "World 1", "CoordinateComponent_2" ], [ "c", "Component_0" ], [ "b", "Component_1" ] ], "coords": "Coordinates", "label": "rgbcube", "primary_owner": [ "a", "Pixel Axis 0 [y]", "Pixel Axis 1 [x]", "World 0", "World 1", "c", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "d2fdde54-ab42-4370-9670-4b9906da51f2" } }glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/data/image_v0.glu0000644000175000017500000003715713502206677025032 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAA==" }, "units": "" }, "ComponentLink": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "World 0" ], "hidden": false, "inverse": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" }, "to": [ "a" ], "using": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" } }, "ComponentLink_0": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "World 1" ], "hidden": false, "inverse": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" }, "to": [ "b" ], "using": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" } }, "ComponentLink_1": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "b" ], "hidden": false, "inverse": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" }, "to": [ "World 1" ], "using": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" } }, "ComponentLink_2": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "a" ], "hidden": false, "inverse": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" }, "to": [ "World 0" ], "using": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" } }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsKSwgfSAgICAgICAgICAgIAoAAAAAAAD4PwAAAAAAAABA" }, "units": "" }, "Component_1": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsKSwgfSAgICAgICAgICAgIAoAAAAAAADgPwAAAAAAAABA" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates_0", "frm": [ "World 0_0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 1, "pix2world": true, "to": [ "World 1" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 1, "pix2world": false, "to": [ "Pixel Axis 1 [x]" ] }, "CoordinateComponentLink_3": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [y]" ] }, "CoordinateComponentLink_4": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates_0", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0_0" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": false }, "CoordinateComponent_1": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "CoordinateComponent_2": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": true }, "CoordinateComponent_3": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponent_4": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "Coordinates_0": { "_type": "glue.core.coordinates.Coordinates" }, "DS9Normalize": { "_type": "glue.viewers.image.ds9norm.DS9Normalize", "bias": 0.5, "clip_hi": 99.0, "clip_lo": 1.0, "contrast": 1.0, "stretch": "sqrt", "vmax": null, "vmin": null }, "DS9Normalize_0": { "_type": "glue.viewers.image.ds9norm.DS9Normalize", "bias": 0.5, "clip_hi": null, "clip_lo": null, "contrast": 1.0, "stretch": "linear", "vmax": 2.0, "vmin": -2.0 }, "DS9Normalize_1": { "_type": "glue.viewers.image.ds9norm.DS9Normalize", "bias": 0.5, "clip_hi": null, "clip_lo": null, "contrast": 1.0, "stretch": "arcsinh", "vmax": 4, "vmin": 1 }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "data1_0", "Pixel Axis 0 [y]", "Pixel Axis 1 [x]", "World 0", "World 1", "a", "b", "a", "Pixel Axis 0 [x]", "World 0_0", "b", "Pixel Axis 1 [x]", "World 1", "Pixel Axis 0 [y]", "World 0" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "CoordinateComponent_1", "CoordinateComponent_2", "DerivedComponent", "DerivedComponent_0", "Component_0", "CoordinateComponent_3", "CoordinateComponent_4", "Component_1", "DerivedComponent_1", "DerivedComponent_2", "DerivedComponent_3", "DerivedComponent_4" ], "data": [ "data1", "data2" ], "groups": [ "Subset 1" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "ComponentLink", "CoordinateComponentLink_2", "CoordinateComponentLink_3", "ComponentLink_0", "CoordinateComponentLink_4" ], "subset_group_count": 1 }, "DerivedComponent": { "_type": "glue.core.component.DerivedComponent", "link": "ComponentLink" }, "DerivedComponent_0": { "_type": "glue.core.component.DerivedComponent", "link": "ComponentLink_0" }, "DerivedComponent_1": { "_type": "glue.core.component.DerivedComponent", "link": "CoordinateComponentLink_2" }, "DerivedComponent_2": { "_type": "glue.core.component.DerivedComponent", "link": "ComponentLink_1" }, "DerivedComponent_3": { "_type": "glue.core.component.DerivedComponent", "link": "CoordinateComponentLink_3" }, "DerivedComponent_4": { "_type": "glue.core.component.DerivedComponent", "link": "ComponentLink_2" }, "ImageWidget": { "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", "layers": [ { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "layer": "data1", "norm": "DS9Normalize", "visible": true, "zorder": 1 }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "data2", "visible": true, "xatt": "Pixel Axis 1 [x]", "yatt": "Pixel Axis 0 [y]", "zorder": 2 }, { "_type": "glue.viewers.image.layer_artist.SubsetImageLayerArtist", "layer": "Subset 1_0", "visible": false, "zorder": 3 } ], "pos": [ -1, 1 ], "properties": { "attribute": "data1_0", "batt": null, "data": "data1", "gatt": null, "ratt": null, "rgb_mode": false, "rgb_viz": [ true, true, true ], "slice": [ "y", "x" ] }, "session": "Session", "size": [ 568, 490 ] }, "ImageWidget_0": { "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", "layers": [ { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "layer": "data1", "norm": "DS9Normalize_1", "visible": true, "zorder": 1 }, { "_type": "glue.viewers.image.layer_artist.SubsetImageLayerArtist", "layer": "Subset 1_0", "visible": true, "zorder": 2 } ], "pos": [ 568, 1 ], "properties": { "attribute": "data1_0", "batt": null, "data": "data1", "gatt": null, "ratt": null, "rgb_mode": false, "rgb_viz": [ true, true, true ], "slice": [ "y", "x" ] }, "session": "Session", "size": [ 606, 489 ] }, "ImageWidget_1": { "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", "layers": [ { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "layer": "data1", "norm": "DS9Normalize_0", "visible": true, "zorder": 1 }, { "_type": "glue.viewers.image.layer_artist.SubsetImageLayerArtist", "layer": "Subset 1_0", "visible": true, "zorder": 2 } ], "pos": [ 568, 487 ], "properties": { "attribute": "data1_0", "batt": null, "data": "data1", "gatt": null, "ratt": null, "rgb_mode": false, "rgb_viz": [ true, true, true ], "slice": [ "y", "x" ] }, "session": "Session", "size": [ 600, 400 ] }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "Pixel Axis 0 [y]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [y]" }, "Pixel Axis 1 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 1, "hidden": true, "label": "Pixel Axis 1 [x]" }, "PolygonalROI": { "_type": "glue.core.roi.PolygonalROI", "vx": [ 0.7380952380952381, 1.847619047619048, 1.847619047619048, 0.7380952380952381, 0.7380952380952381 ], "vy": [ 0.5866666666666664, 0.5866666666666664, 1.7666666666666662, 1.7666666666666662, 0.5866666666666664 ] }, "RoiSubsetState": { "_type": "glue.core.subset.RoiSubsetState", "roi": "PolygonalROI", "xatt": "Pixel Axis 1 [x]", "yatt": "Pixel Axis 0 [y]" }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "RoiSubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1_0", "Subset 1_1" ] }, "Subset 1_0": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "Subset 1_1": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 0_0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 1": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 1" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.plugins.tools.pv_slicer", "glue.viewers.histogram", "glue.viewers.table", "glue_vispy_viewers.volume", "glue.plugins.exporters.plotly", "glue.plugins.export_d3po", "glue.viewers.image", "glue.plugins.tools.spectrum_tool", "glue_vispy_viewers.scatter", "glue.viewers.scatter", "glue.plugins.coordinate_helpers", "glue.core.data_exporters" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ImageWidget", "ImageWidget_0", "ImageWidget_1" ] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "data1": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "data1_0", "Component" ], [ "Pixel Axis 0 [y]", "CoordinateComponent" ], [ "Pixel Axis 1 [x]", "CoordinateComponent_0" ], [ "World 0", "CoordinateComponent_1" ], [ "World 1", "CoordinateComponent_2" ], [ "a", "DerivedComponent" ], [ "b", "DerivedComponent_0" ] ], "coords": "Coordinates", "label": "data1", "primary_owner": [ "data1_0", "Pixel Axis 0 [y]", "Pixel Axis 1 [x]", "World 0", "World 1" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1_0" ], "uuid": "daad10a3-6ad4-4dd4-841a-ca2071dbe467" }, "data1_0": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "data1" }, "data2": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component_0" ], [ "Pixel Axis 0 [x]", "CoordinateComponent_3" ], [ "World 0_0", "CoordinateComponent_4" ], [ "b", "Component_1" ], [ "Pixel Axis 1 [x]", "DerivedComponent_1" ], [ "World 1", "DerivedComponent_2" ], [ "Pixel Axis 0 [y]", "DerivedComponent_3" ], [ "World 0", "DerivedComponent_4" ] ], "coords": "Coordinates_0", "label": "data2", "primary_owner": [ "a", "Pixel Axis 0 [x]", "World 0_0", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 1.0, "color": "#e60010", "linestyle": "solid", "linewidth": 1, "marker": "^", "markersize": 3 }, "subsets": [ "Subset 1_1" ], "uuid": "e0521c8f-3f02-41b8-8746-a71aa9ae5f49" } } glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/data/image_v1.glu0000644000175000017500000005013013644362032025010 0ustar noahfxnoahfx{ "CallbackList": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ImageLayerState", "ScatterLayerState", "ImageSubsetLayerState" ] }, "CallbackList_0": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ImageLayerState_0", "ImageSubsetLayerState_0" ] }, "CallbackList_1": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ImageLayerState_1", "ImageSubsetLayerState_1" ] }, "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAA==" }, "units": "" }, "ComponentLink": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "a" ], "hidden": false, "inverse": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" }, "to": [ "World 0" ], "using": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" } }, "ComponentLink_0": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "World 1" ], "hidden": false, "inverse": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" }, "to": [ "b" ], "using": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" } }, "ComponentLink_1": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "World 0" ], "hidden": false, "inverse": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" }, "to": [ "a" ], "using": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" } }, "ComponentLink_2": { "_type": "glue.core.component_link.ComponentLink", "frm": [ "b" ], "hidden": false, "inverse": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" }, "to": [ "World 1" ], "using": { "_type": "types.FunctionType", "function": "glue.core.component_link.identity" } }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsKSwgfSAgICAgICAgICAgIAoAAAAAAAD4PwAAAAAAAABA" }, "units": "" }, "Component_1": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsKSwgfSAgICAgICAgICAgIAoAAAAAAADgPwAAAAAAAABA" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates_0", "frm": [ "World 0_0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates_0", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0_0" ] }, "CoordinateComponentLink_10": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 1, "pix2world": false, "to": [ "Pixel Axis 1 [x]" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 1, "pix2world": true, "to": [ "World 1" ] }, "CoordinateComponentLink_3": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 1, "pix2world": false, "to": [ "Pixel Axis 1 [x]" ] }, "CoordinateComponentLink_4": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates_0", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0_0" ] }, "CoordinateComponentLink_5": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 1, "pix2world": true, "to": [ "World 1" ] }, "CoordinateComponentLink_6": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates_0", "frm": [ "World 0_0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_7": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [y]" ] }, "CoordinateComponentLink_8": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_9": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [y]" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": false }, "CoordinateComponent_1": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "CoordinateComponent_2": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": true }, "CoordinateComponent_3": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponent_4": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "Coordinates_0": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "data1_0", "Pixel Axis 0 [y]", "Pixel Axis 1 [x]", "World 0", "World 1", "a", "b", "a", "Pixel Axis 0 [x]", "World 0_0", "b", "Pixel Axis 1 [x]", "World 1", "Pixel Axis 0 [y]", "World 0" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "CoordinateComponent_1", "CoordinateComponent_2", "DerivedComponent", "DerivedComponent_0", "Component_0", "CoordinateComponent_3", "CoordinateComponent_4", "Component_1", "DerivedComponent_1", "DerivedComponent_2", "DerivedComponent_3", "DerivedComponent_4" ], "data": [ "data1", "data2" ], "groups": [ "Subset 1" ], "links": [ "ComponentLink", "ComponentLink_0", "CoordinateComponentLink", "CoordinateComponentLink_0", "ComponentLink_1", "CoordinateComponentLink_1", "CoordinateComponentLink_2", "CoordinateComponentLink_3", "CoordinateComponentLink_4", "CoordinateComponentLink_5", "CoordinateComponentLink_6", "CoordinateComponentLink_7", "CoordinateComponentLink_8", "ComponentLink_2", "CoordinateComponentLink_9", "CoordinateComponentLink_10" ], "subset_group_count": 1 }, "DerivedComponent": { "_type": "glue.core.component.DerivedComponent", "link": "ComponentLink_1" }, "DerivedComponent_0": { "_type": "glue.core.component.DerivedComponent", "link": "ComponentLink_0" }, "DerivedComponent_1": { "_type": "glue.core.component.DerivedComponent", "link": "CoordinateComponentLink_3" }, "DerivedComponent_2": { "_type": "glue.core.component.DerivedComponent", "link": "ComponentLink_2" }, "DerivedComponent_3": { "_type": "glue.core.component.DerivedComponent", "link": "CoordinateComponentLink_7" }, "DerivedComponent_4": { "_type": "glue.core.component.DerivedComponent", "link": "ComponentLink" }, "ImageLayerState": { "_type": "glue.viewers.image.state.ImageLayerState", "values": { "alpha": 0.8, "attribute": "data1_0", "bias": 0.5, "cmap": "LinearSegmentedColormap", "color": "st__0.35", "contrast": 1.0, "global_sync": true, "layer": "data1", "percentile": 99, "stretch": "st__sqrt", "v_max": 3.985, "v_min": 1.015, "visible": true, "zorder": 2 } }, "ImageLayerState_0": { "_type": "glue.viewers.image.state.ImageLayerState", "values": { "alpha": 0.8, "attribute": "data1_0", "bias": 0.5, "cmap": "LinearSegmentedColormap", "color": "st__0.35", "contrast": 1.0, "global_sync": true, "layer": "data1", "percentile": "st__Custom", "stretch": "st__arcsinh", "v_max": 4, "v_min": 1, "visible": true, "zorder": 2 } }, "ImageLayerState_1": { "_type": "glue.viewers.image.state.ImageLayerState", "values": { "alpha": 0.8, "attribute": "data1_0", "bias": 0.5, "cmap": "LinearSegmentedColormap", "color": "st__0.35", "contrast": 1.0, "global_sync": true, "layer": "data1", "percentile": "st__Custom", "stretch": "st__linear", "v_max": 2.0, "v_min": -2.0, "visible": true, "zorder": 2 } }, "ImageSubsetLayerState": { "_type": "glue.viewers.image.state.ImageSubsetLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "visible": false, "zorder": 4 } }, "ImageSubsetLayerState_0": { "_type": "glue.viewers.image.state.ImageSubsetLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "visible": true, "zorder": 3 } }, "ImageSubsetLayerState_1": { "_type": "glue.viewers.image.state.ImageSubsetLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "visible": true, "zorder": 3 } }, "ImageViewer": { "_protocol": 1, "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", "layers": [ { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "state": "ImageLayerState" }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState" }, { "_type": "glue.viewers.image.layer_artist.ImageSubsetLayerArtist", "state": "ImageSubsetLayerState" } ], "pos": [ -1, 1 ], "session": "Session", "size": [ 568, 490 ], "state": { "values": { "aspect": "st__equal", "color_mode": "st__Colormaps", "layers": "CallbackList", "reference_data": "data1", "slices": [ 0, 0 ], "x_att": "Pixel Axis 1 [x]", "x_att_world": "World 1", "x_log": false, "x_max": 1.937, "x_min": -0.936, "y_att": "Pixel Axis 0 [y]", "y_att_world": "World 0", "y_log": false, "y_max": 1.5, "y_min": -0.5 } } }, "ImageViewer_0": { "_protocol": 1, "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", "layers": [ { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "state": "ImageLayerState_0" }, { "_type": "glue.viewers.image.layer_artist.ImageSubsetLayerArtist", "state": "ImageSubsetLayerState_0" } ], "pos": [ 568, 1 ], "session": "Session", "size": [ 606, 489 ], "state": { "values": { "aspect": "st__equal", "color_mode": "st__Colormaps", "layers": "CallbackList_0", "reference_data": "data1", "slices": [ 0, 0 ], "x_att": "Pixel Axis 1 [x]", "x_att_world": "World 1", "x_log": false, "x_max": 1.937, "x_min": -0.936, "y_att": "Pixel Axis 0 [y]", "y_att_world": "World 0", "y_log": false, "y_max": 1.5, "y_min": -0.5 } } }, "ImageViewer_1": { "_protocol": 1, "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", "layers": [ { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "state": "ImageLayerState_1" }, { "_type": "glue.viewers.image.layer_artist.ImageSubsetLayerArtist", "state": "ImageSubsetLayerState_1" } ], "pos": [ 569, 488 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "aspect": "st__equal", "color_mode": "st__Colormaps", "layers": "CallbackList_1", "reference_data": "data1", "slices": [ 0, 0 ], "x_att": "Pixel Axis 1 [x]", "x_att_world": "World 1", "x_log": false, "x_max": 1.937, "x_min": -0.936, "y_att": "Pixel Axis 0 [y]", "y_att_world": "World 0", "y_log": false, "y_max": 1.5, "y_min": -0.5 } } }, "LinearSegmentedColormap": { "_type": "matplotlib.colors.LinearSegmentedColormap", "cmap": "gray" }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "Pixel Axis 0 [y]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [y]" }, "Pixel Axis 1 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 1, "hidden": true, "label": "Pixel Axis 1 [x]" }, "PolygonalROI": { "_type": "glue.core.roi.PolygonalROI", "vx": [ 0.7380952380952381, 1.847619047619048, 1.847619047619048, 0.7380952380952381, 0.7380952380952381 ], "vy": [ 0.5866666666666664, 0.5866666666666664, 1.7666666666666662, 1.7666666666666662, 0.5866666666666664 ] }, "RoiSubsetState": { "_type": "glue.core.subset.RoiSubsetState", "roi": "PolygonalROI", "xatt": "Pixel Axis 1 [x]", "yatt": "Pixel Axis 0 [y]" }, "ScatterLayerState": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 1.0, "color": "st__#e60010", "layer": "data2", "size": 3, "visible": true, "zorder": 3 } }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "RoiSubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1_0", "Subset 1_1" ] }, "Subset 1_0": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "Subset 1_1": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 0_0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 1": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 1" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.viewers.image", "glue.plugins.exporters.plotly", "specviz.app", "glue.viewers.scatter", "glue.viewers.histogram", "glue.core.data_exporters", "glue.plugins.tools.spectrum_tool", "glue.viewers.table", "glue_medical", "glue_vispy_viewers.scatter", "glue_vispy_viewers.volume", "glue.plugins.coordinate_helpers", "glue.plugins.tools.pv_slicer", "glue.plugins.export_d3po" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ImageViewer", "ImageViewer_0", "ImageViewer_1" ] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "data1": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "data1_0", "Component" ], [ "Pixel Axis 0 [y]", "CoordinateComponent" ], [ "Pixel Axis 1 [x]", "CoordinateComponent_0" ], [ "World 0", "CoordinateComponent_1" ], [ "World 1", "CoordinateComponent_2" ], [ "a", "DerivedComponent" ], [ "b", "DerivedComponent_0" ] ], "coords": "Coordinates", "label": "data1", "primary_owner": [ "data1_0", "Pixel Axis 0 [y]", "Pixel Axis 1 [x]", "World 0", "World 1" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1_0" ], "uuid": "daad10a3-6ad4-4dd4-841a-ca2071dbe467" }, "data1_0": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "data1" }, "data2": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component_0" ], [ "Pixel Axis 0 [x]", "CoordinateComponent_3" ], [ "World 0_0", "CoordinateComponent_4" ], [ "b", "Component_1" ], [ "Pixel Axis 1 [x]", "DerivedComponent_1" ], [ "World 1", "DerivedComponent_2" ], [ "Pixel Axis 0 [y]", "DerivedComponent_3" ], [ "World 0", "DerivedComponent_4" ] ], "coords": "Coordinates_0", "label": "data2", "primary_owner": [ "a", "Pixel Axis 0 [x]", "World 0_0", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 1.0, "color": "#e60010", "linestyle": "solid", "linewidth": 1, "marker": "^", "markersize": 3 }, "subsets": [ "Subset 1_1" ], "uuid": "e0521c8f-3f02-41b8-8746-a71aa9ae5f49" } } glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/data/image_rgb_v1.glu0000644000175000017500000002344213644362032025650 0ustar noahfxnoahfx{ "CallbackList": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ImageLayerState", "ImageLayerState_0", "ImageLayerState_1" ] }, "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAA==" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoIAAAAAAAAAAkAAAAAAAAACgAAAAAAAAALAAAAAAAAAA==" }, "units": "" }, "Component_1": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoEAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAHAAAAAAAAAA==" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 1, "pix2world": true, "to": [ "World 1" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 1, "pix2world": false, "to": [ "Pixel Axis 1 [x]" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_3": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [y]" ] }, "CoordinateComponentLink_4": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [y]", "Pixel Axis 1 [x]" ], "index": 1, "pix2world": true, "to": [ "World 1" ] }, "CoordinateComponentLink_5": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 1, "pix2world": false, "to": [ "Pixel Axis 1 [x]" ] }, "CoordinateComponentLink_6": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [y]" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": false }, "CoordinateComponent_1": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "CoordinateComponent_2": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "a", "Pixel Axis 0 [y]", "Pixel Axis 1 [x]", "World 0", "World 1", "c", "b" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "CoordinateComponent_1", "CoordinateComponent_2", "Component_0", "Component_1" ], "data": [ "rgbcube" ], "groups": [], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2", "CoordinateComponentLink_3", "CoordinateComponentLink_4", "CoordinateComponentLink_5", "CoordinateComponentLink_6" ], "subset_group_count": 0 }, "ImageLayerState": { "_type": "glue.viewers.image.state.ImageLayerState", "values": { "alpha": 0.8, "attribute": "a", "bias": 0.5, "cmap": "LinearSegmentedColormap", "color": "st__r", "contrast": 1.0, "global_sync": false, "layer": "rgbcube", "percentile": "st__Custom", "stretch": "st__linear", "v_max": 5.0, "v_min": -5.0, "visible": true, "zorder": 2 } }, "ImageLayerState_0": { "_type": "glue.viewers.image.state.ImageLayerState", "values": { "alpha": 0.8, "attribute": "c", "bias": 0.5, "cmap": "LinearSegmentedColormap", "color": "st__g", "contrast": 1.0, "global_sync": false, "layer": "rgbcube", "percentile": 99, "stretch": "st__linear", "v_max": 10.985, "v_min": 8.015, "visible": false, "zorder": 3 } }, "ImageLayerState_1": { "_type": "glue.viewers.image.state.ImageLayerState", "values": { "alpha": 0.8, "attribute": "b", "bias": 0.5, "cmap": "LinearSegmentedColormap", "color": "st__b", "contrast": 1.0, "global_sync": false, "layer": "rgbcube", "percentile": "st__Custom", "stretch": "st__arcsinh", "v_max": 3, "v_min": 0, "visible": true, "zorder": 4 } }, "ImageViewer": { "_protocol": 1, "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", "layers": [ { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "state": "ImageLayerState" }, { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "state": "ImageLayerState_0" }, { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "state": "ImageLayerState_1" } ], "pos": [ 0, 0 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "aspect": "st__equal", "color_mode": "st__One color per layer", "layers": "CallbackList", "reference_data": "rgbcube", "slices": [ 0, 0 ], "x_att": "Pixel Axis 1 [x]", "x_att_world": "World 1", "x_log": false, "x_max": 2.5253556681900653, "x_min": -1.4613891239424421, "y_att": "Pixel Axis 0 [y]", "y_att_world": "World 0", "y_log": false, "y_max": 1.555040949314538, "y_min": -0.5351753331368783 } } }, "LinearSegmentedColormap": { "_type": "matplotlib.colors.LinearSegmentedColormap", "cmap": "gray" }, "Pixel Axis 0 [y]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [y]" }, "Pixel Axis 1 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 1, "hidden": true, "label": "Pixel Axis 1 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 1": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 1" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.core.data_exporters", "glue.viewers.image", "glue.viewers.scatter", "glue_vispy_viewers.volume", "glue_medical", "glue.plugins.tools.pv_slicer", "glue.plugins.coordinate_helpers", "glue.viewers.table", "glue_vispy_viewers.scatter", "glue.plugins.export_d3po", "glue.viewers.histogram", "glue.plugins.tools.spectrum_tool", "glue.plugins.exporters.plotly", "specviz.app" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ImageViewer" ] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "c": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "c" }, "rgbcube": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component" ], [ "Pixel Axis 0 [y]", "CoordinateComponent" ], [ "Pixel Axis 1 [x]", "CoordinateComponent_0" ], [ "World 0", "CoordinateComponent_1" ], [ "World 1", "CoordinateComponent_2" ], [ "c", "Component_0" ], [ "b", "Component_1" ] ], "coords": "Coordinates", "label": "rgbcube", "primary_owner": [ "a", "Pixel Axis 0 [y]", "Pixel Axis 1 [x]", "World 0", "World 1", "c", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "d2fdde54-ab42-4370-9670-4b9906da51f2" } }glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/data/image_cube_v1.glu0000644000175000017500000003241413644362032026013 0ustar noahfxnoahfx{ "CallbackList": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ImageLayerState" ] }, "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDQsIDIsIDUpLCB9ICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAcAAAAAAAAACAAAAAAAAAAJAAAAAAAAAAoAAAAAAAAACwAAAAAAAAAMAAAAAAAAAA0AAAAAAAAADgAAAAAAAAAPAAAAAAAAABAAAAAAAAAAEQAAAAAAAAASAAAAAAAAABMAAAAAAAAAFAAAAAAAAAAVAAAAAAAAABYAAAAAAAAAFwAAAAAAAAAYAAAAAAAAABkAAAAAAAAAGgAAAAAAAAAbAAAAAAAAABwAAAAAAAAAHQAAAAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAAAAAAhAAAAAAAAACIAAAAAAAAAIwAAAAAAAAAkAAAAAAAAACUAAAAAAAAAJgAAAAAAAAAnAAAAAAAAACgAAAAAAAAAKQAAAAAAAAAqAAAAAAAAACsAAAAAAAAALAAAAAAAAAAtAAAAAAAAAC4AAAAAAAAALwAAAAAAAAAwAAAAAAAAADEAAAAAAAAAMgAAAAAAAAAzAAAAAAAAADQAAAAAAAAANQAAAAAAAAA2AAAAAAAAADcAAAAAAAAAOAAAAAAAAAA5AAAAAAAAADoAAAAAAAAAOwAAAAAAAAA8AAAAAAAAAD0AAAAAAAAAPgAAAAAAAAA/AAAAAAAAAEAAAAAAAAAAQQAAAAAAAABCAAAAAAAAAEMAAAAAAAAARAAAAAAAAABFAAAAAAAAAEYAAAAAAAAARwAAAAAAAABIAAAAAAAAAEkAAAAAAAAASgAAAAAAAABLAAAAAAAAAEwAAAAAAAAATQAAAAAAAABOAAAAAAAAAE8AAAAAAAAAUAAAAAAAAABRAAAAAAAAAFIAAAAAAAAAUwAAAAAAAABUAAAAAAAAAFUAAAAAAAAAVgAAAAAAAABXAAAAAAAAAFgAAAAAAAAAWQAAAAAAAABaAAAAAAAAAFsAAAAAAAAAXAAAAAAAAABdAAAAAAAAAF4AAAAAAAAAXwAAAAAAAABgAAAAAAAAAGEAAAAAAAAAYgAAAAAAAABjAAAAAAAAAGQAAAAAAAAAZQAAAAAAAABmAAAAAAAAAGcAAAAAAAAAaAAAAAAAAABpAAAAAAAAAGoAAAAAAAAAawAAAAAAAABsAAAAAAAAAG0AAAAAAAAAbgAAAAAAAABvAAAAAAAAAHAAAAAAAAAAcQAAAAAAAAByAAAAAAAAAHMAAAAAAAAAdAAAAAAAAAB1AAAAAAAAAHYAAAAAAAAAdwAAAAAAAAA=" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 2, "pix2world": false, "to": [ "Pixel Axis 2" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 3, "pix2world": false, "to": [ "Pixel Axis 3" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 2, "pix2world": false, "to": [ "Pixel Axis 2" ] }, "CoordinateComponentLink_10": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 1, "pix2world": true, "to": [ "World 1" ] }, "CoordinateComponentLink_11": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 2, "pix2world": true, "to": [ "World 2" ] }, "CoordinateComponentLink_12": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0" ] }, "CoordinateComponentLink_13": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 1, "pix2world": true, "to": [ "World 1" ] }, "CoordinateComponentLink_14": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 3, "pix2world": false, "to": [ "Pixel Axis 3" ] }, "CoordinateComponentLink_3": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0" ] }, "CoordinateComponentLink_4": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 2, "pix2world": true, "to": [ "World 2" ] }, "CoordinateComponentLink_5": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_6": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 3, "pix2world": true, "to": [ "World 3" ] }, "CoordinateComponentLink_7": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 1, "pix2world": false, "to": [ "Pixel Axis 1" ] }, "CoordinateComponentLink_8": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3" ], "index": 3, "pix2world": true, "to": [ "World 3" ] }, "CoordinateComponentLink_9": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0", "World 1", "World 2", "World 3" ], "index": 1, "pix2world": false, "to": [ "Pixel Axis 1" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": false }, "CoordinateComponent_1": { "_type": "glue.core.component.CoordinateComponent", "axis": 2, "world": false }, "CoordinateComponent_2": { "_type": "glue.core.component.CoordinateComponent", "axis": 3, "world": false }, "CoordinateComponent_3": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "CoordinateComponent_4": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": true }, "CoordinateComponent_5": { "_type": "glue.core.component.CoordinateComponent", "axis": 2, "world": true }, "CoordinateComponent_6": { "_type": "glue.core.component.CoordinateComponent", "axis": 3, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "array_0", "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3", "World 0", "World 1", "World 2", "World 3" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "CoordinateComponent_1", "CoordinateComponent_2", "CoordinateComponent_3", "CoordinateComponent_4", "CoordinateComponent_5", "CoordinateComponent_6" ], "data": [ "array" ], "groups": [], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2", "CoordinateComponentLink_3", "CoordinateComponentLink_4", "CoordinateComponentLink_5", "CoordinateComponentLink_6", "CoordinateComponentLink_7", "CoordinateComponentLink_8", "CoordinateComponentLink_9", "CoordinateComponentLink_10", "CoordinateComponentLink_11", "CoordinateComponentLink_12", "CoordinateComponentLink_13", "CoordinateComponentLink_14" ], "subset_group_count": 0 }, "ImageLayerState": { "_type": "glue.viewers.image.state.ImageLayerState", "values": { "alpha": 0.8, "attribute": "array_0", "bias": 0.5, "cmap": "LinearSegmentedColormap", "color": "st__0.35", "contrast": 1.0, "global_sync": true, "layer": "array", "percentile": 100, "stretch": "st__linear", "v_max": 119.0, "v_min": 0.0, "visible": true, "zorder": 2 } }, "ImageViewer": { "_protocol": 1, "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", "layers": [ { "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", "state": "ImageLayerState" } ], "pos": [ 0, 0 ], "session": "Session", "size": [ 731, 529 ], "state": { "values": { "aspect": "st__equal", "color_mode": "st__Colormaps", "layers": "CallbackList", "reference_data": "array", "slices": [ 2, 0, 0, 1 ], "x_att": "Pixel Axis 2", "x_att_world": "World 2", "x_log": false, "x_max": 4.075455766965458, "x_min": -2.932007545418843, "y_att": "Pixel Axis 1", "y_att_world": "World 1", "y_log": false, "y_max": 3.6720458592035916, "y_min": -0.6781873651245816 } } }, "LinearSegmentedColormap": { "_type": "matplotlib.colors.LinearSegmentedColormap", "cmap": "gray" }, "Pixel Axis 0": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0" }, "Pixel Axis 1": { "_type": "glue.core.component_id.PixelComponentID", "axis": 1, "hidden": true, "label": "Pixel Axis 1" }, "Pixel Axis 2": { "_type": "glue.core.component_id.PixelComponentID", "axis": 2, "hidden": true, "label": "Pixel Axis 2" }, "Pixel Axis 3": { "_type": "glue.core.component_id.PixelComponentID", "axis": 3, "hidden": true, "label": "Pixel Axis 3" }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "World 1": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 1" }, "World 2": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 2" }, "World 3": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 3" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.viewers.scatter", "glue.viewers.table", "glue_medical", "specviz.app", "glue.plugins.exporters.plotly", "glue.core.data_exporters", "glue.plugins.export_d3po", "glue_vispy_viewers.volume", "glue.plugins.tools.spectrum_tool", "glue.plugins.coordinate_helpers", "glue.viewers.histogram", "glue.plugins.tools.pv_slicer", "glue_vispy_viewers.scatter", "glue.viewers.image" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ImageViewer" ] ] }, "array": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "array_0", "Component" ], [ "Pixel Axis 0", "CoordinateComponent" ], [ "Pixel Axis 1", "CoordinateComponent_0" ], [ "Pixel Axis 2", "CoordinateComponent_1" ], [ "Pixel Axis 3", "CoordinateComponent_2" ], [ "World 0", "CoordinateComponent_3" ], [ "World 1", "CoordinateComponent_4" ], [ "World 2", "CoordinateComponent_5" ], [ "World 3", "CoordinateComponent_6" ] ], "coords": "Coordinates", "label": "array", "primary_owner": [ "array_0", "Pixel Axis 0", "Pixel Axis 1", "Pixel Axis 2", "Pixel Axis 3", "World 0", "World 1", "World 2", "World 3" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "5e2335ce-5613-4fb1-aa58-1cf342f7b755" }, "array_0": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "array" } }glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/test_data_viewer.py0000644000175000017500000010221313752534424025607 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import os import gc from collections import Counter import pytest from astropy.wcs import WCS import numpy as np from numpy.testing import assert_allclose from glue.viewers.image.frb_artist import FRBArtist from glue.core.coordinates import IdentityCoordinates from glue.core.message import SubsetUpdateMessage from glue.core import HubListener, Data from glue.core.roi import XRangeROI, RectangularROI from glue.core.subset import RoiSubsetState from glue.utils.qt import combo_as_string, process_events from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer from glue.core.state import GlueUnSerializer from glue.app.qt.layer_tree_widget import LayerTreeWidget from glue.viewers.scatter.state import ScatterLayerState from glue.viewers.image.state import ImageLayerState, ImageSubsetLayerState, AggregateSlice from glue.core.link_helpers import LinkSame from glue.app.qt import GlueApplication from glue.core.fixed_resolution_buffer import ARRAY_CACHE, PIXEL_CACHE from glue.core.data_derived import IndexedData from ..data_viewer import ImageViewer DATA = os.path.join(os.path.dirname(__file__), 'data') class TestImageCommon(BaseTestMatplotlibDataViewer): def init_data(self): return Data(label='d1', x=np.arange(24).reshape((2, 3, 4)), y=np.ones((2, 3, 4))) viewer_cls = ImageViewer @pytest.mark.skip() def test_double_add_ignored(self): pass def test_update_data_processed_if_data_present(self): # Patch for the main test of the same name - we need to explicilty set # global_sync to True here for things to work correctly. self.init_draw_count() self.data_collection.append(self.data) self.viewer.add_data(self.data) ct0 = self.draw_count self.viewer.state.layers[0].global_sync = True self.data.style.color = 'blue' assert self.draw_count > ct0 def test_slice_change_single_draw(self): # Regression test for a bug that caused Matplotlib to draw once per # data/subset when changing slices. self.viewer.add_data(self.data) self.data_collection.new_subset_group(label='a', subset_state=self.data.id['x'] > 1) self.data_collection.new_subset_group(label='b', subset_state=self.data.id['x'] > 2) self.data_collection.new_subset_group(label='c', subset_state=self.data.id['x'] > 3) self.init_draw_count() assert self.draw_count == 0 self.viewer.state.slices = (1, 1, 1) assert self.draw_count == 1 class MyCoords(IdentityCoordinates): def __init__(self, n_dim=2): super().__init__(n_dim=n_dim) @property def world_axis_names(self): return ['Apple', 'Banana'] class TestImageViewer(object): def setup_method(self, method): self.coords = MyCoords() self.image1 = Data(label='image1', x=[[1, 2], [3, 4]], y=[[4, 5], [2, 3]]) self.image2 = Data(label='image2', a=[[3, 3], [2, 2]], b=[[4, 4], [3, 2]], coords=self.coords) self.catalog = Data(label='catalog', c=[1, 3, 2], d=[4, 3, 3]) self.hypercube = Data(label='hypercube', x=np.arange(120).reshape((2, 3, 4, 5))) # Create data versions with WCS coordinates self.image1_wcs = Data(label='image1_wcs', x=self.image1['x'], coords=WCS(naxis=2)) self.hypercube_wcs = Data(label='hypercube_wcs', x=self.hypercube['x'], coords=WCS(naxis=4)) self.application = GlueApplication() self.session = self.application.session self.hub = self.session.hub self.data_collection = self.session.data_collection self.data_collection.append(self.image1) self.data_collection.append(self.image2) self.data_collection.append(self.catalog) self.data_collection.append(self.hypercube) self.data_collection.append(self.image1_wcs) self.data_collection.append(self.hypercube_wcs) self.viewer = self.application.new_data_viewer(ImageViewer) self.data_collection.register_to_hub(self.hub) self.viewer.register_to_hub(self.hub) self.options_widget = self.viewer.options_widget() def teardown_method(self, method): # Properly close viewer and application self.viewer.close() self.viewer = None self.application.close() self.application = None # Make sure cache is empty if len(PIXEL_CACHE) > 0: raise Exception("Pixel cache contains {0} elements".format(len(PIXEL_CACHE))) if len(ARRAY_CACHE) > 0: raise Exception("Array cache contains {0} elements".format(len(ARRAY_CACHE))) def test_basic(self): # Check defaults when we add data self.viewer.add_data(self.image1) assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Pixel Axis 0 [y]:Pixel Axis 1 [x]' assert combo_as_string(self.options_widget.ui.combosel_y_att_world) == 'Coordinate components:Pixel Axis 0 [y]:Pixel Axis 1 [x]' assert self.viewer.axes.get_xlabel() == 'Pixel Axis 1 [x]' assert self.viewer.state.x_att_world is self.image1.id['Pixel Axis 1 [x]'] assert self.viewer.state.x_att is self.image1.pixel_component_ids[1] assert_allclose(self.viewer.state.x_min, -0.8419913419913423) assert_allclose(self.viewer.state.x_max, +1.8419913419913423) assert self.viewer.axes.get_ylabel() == 'Pixel Axis 0 [y]' assert self.viewer.state.y_att_world is self.image1.id['Pixel Axis 0 [y]'] assert self.viewer.state.y_att is self.image1.pixel_component_ids[0] assert self.viewer.state.y_min == -0.5 assert self.viewer.state.y_max == +1.5 assert not self.viewer.state.x_log assert not self.viewer.state.y_log assert len(self.viewer.state.layers) == 1 def test_custom_coords(self): # Check defaults when we add data with coordinates self.viewer.add_data(self.image2) assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Banana:Apple' assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Banana:Apple' assert self.viewer.axes.get_xlabel() == 'Apple' assert self.viewer.state.x_att_world is self.image2.id['Apple'] assert self.viewer.state.x_att is self.image2.pixel_component_ids[1] assert self.viewer.axes.get_ylabel() == 'Banana' assert self.viewer.state.y_att_world is self.image2.id['Banana'] assert self.viewer.state.y_att is self.image2.pixel_component_ids[0] def test_flip(self): self.viewer.add_data(self.image1) x_min_start = self.viewer.state.x_min x_max_start = self.viewer.state.x_max self.options_widget.button_flip_x.click() assert self.viewer.state.x_min == x_max_start assert self.viewer.state.x_max == x_min_start y_min_start = self.viewer.state.y_min y_max_start = self.viewer.state.y_max self.options_widget.button_flip_y.click() assert self.viewer.state.y_min == y_max_start assert self.viewer.state.y_max == y_min_start def test_combo_updates_with_component_add(self): self.viewer.add_data(self.image1) self.image1.add_component([[9, 9], [8, 8]], 'z') assert self.viewer.state.x_att_world is self.image1.id['Pixel Axis 1 [x]'] assert self.viewer.state.y_att_world is self.image1.id['Pixel Axis 0 [y]'] # TODO: there should be an easier way to do this layer_style_editor = self.viewer._view.layout_style_widgets[self.viewer.layers[0]] assert combo_as_string(layer_style_editor.ui.combosel_attribute) == 'x:y:z' def test_apply_roi(self): self.viewer.add_data(self.image1) roi = RectangularROI(0.4, 1.6, -0.6, 0.6) assert len(self.viewer.layers) == 1 self.viewer.apply_roi(roi) assert len(self.viewer.layers) == 2 assert len(self.image1.subsets) == 1 assert_allclose(self.image1.subsets[0].to_mask(), [[0, 1], [0, 0]]) state = self.image1.subsets[0].subset_state assert isinstance(state, RoiSubsetState) def test_apply_roi_empty(self): # Make sure that doing an ROI selection on an empty viewer doesn't # produce error messsages roi = XRangeROI(-0.2, 0.1) self.viewer.apply_roi(roi) def test_identical(self): # Check what happens if we set both attributes to the same coordinates self.viewer.add_data(self.image2) assert self.viewer.state.x_att_world is self.image2.id['Apple'] assert self.viewer.state.y_att_world is self.image2.id['Banana'] self.viewer.state.y_att_world = self.image2.id['Apple'] assert self.viewer.state.x_att_world is self.image2.id['Banana'] assert self.viewer.state.y_att_world is self.image2.id['Apple'] self.viewer.state.x_att_world = self.image2.id['Apple'] assert self.viewer.state.x_att_world is self.image2.id['Apple'] assert self.viewer.state.y_att_world is self.image2.id['Banana'] def test_duplicate_subsets(self): # Regression test: make sure that when adding a seconda layer for the # same dataset, we don't add the subsets all over again. self.viewer.add_data(self.image1) self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A') assert len(self.viewer.layers) == 2 self.viewer.add_data(self.image1) assert len(self.viewer.layers) == 3 def test_aspect_subset(self): self.viewer.add_data(self.image1) assert self.viewer.state.aspect == 'equal' self.viewer.state.aspect = 'auto' self.data_collection.new_subset_group('s1', self.image1.id['x'] > 0.) assert len(self.viewer.state.layers) == 2 assert self.viewer.state.aspect == 'auto' self.viewer.state.aspect = 'equal' self.data_collection.new_subset_group('s2', self.image1.id['x'] > 1.) assert len(self.viewer.state.layers) == 3 assert self.viewer.state.aspect == 'equal' def test_hypercube(self): # Check defaults when we add data self.viewer.add_data(self.hypercube) assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Pixel Axis 0:Pixel Axis 1:Pixel Axis 2:Pixel Axis 3' assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Pixel Axis 0:Pixel Axis 1:Pixel Axis 2:Pixel Axis 3' assert self.viewer.axes.get_xlabel() == 'Pixel Axis 3' assert self.viewer.state.x_att_world is self.hypercube.id['Pixel Axis 3'] assert self.viewer.state.x_att is self.hypercube.pixel_component_ids[3] assert_allclose(self.viewer.state.x_min, -0.6839826839826846) assert_allclose(self.viewer.state.x_max, +4.6839826839826846) assert self.viewer.axes.get_ylabel() == 'Pixel Axis 2' assert self.viewer.state.y_att_world is self.hypercube.id['Pixel Axis 2'] assert self.viewer.state.y_att is self.hypercube.pixel_component_ids[2] assert self.viewer.state.y_min == -0.5 assert self.viewer.state.y_max == +3.5 assert not self.viewer.state.x_log assert not self.viewer.state.y_log assert len(self.viewer.state.layers) == 1 def test_hypercube_world(self): # Check defaults when we add data wcs = WCS(naxis=4) hypercube2 = Data() hypercube2.coords = wcs hypercube2.add_component(np.random.random((2, 3, 4, 5)), 'a') self.data_collection.append(hypercube2) self.viewer.add_data(hypercube2) def test_incompatible_subset(self): self.viewer.add_data(self.image1) self.data_collection.new_subset_group(subset_state=self.catalog.id['c'] > 1, label='A') def test_invisible_subset(self): # Regression test for a bug that caused a subset layer that started # off as invisible to have issues when made visible. We emulate the # initial invisible (but enabled) state by invalidating the cache. self.viewer.add_data(self.image1) self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A') self.viewer.layers[1].visible = False self.viewer.layers[1].image_artist.invalidate_cache() self.viewer.layers[1].redraw() process_events() assert not np.any(self.viewer.layers[1].image_artist._A.mask) self.viewer.layers[1].visible = True assert not np.any(self.viewer.layers[1].image_artist._A.mask) def test_apply_roi_single(self): # Regression test for a bug that caused mode.update to be called # multiple times and resulted in all other viewers receiving many # messages regarding subset updates (this occurred when multiple) # datasets were present. layer_tree = LayerTreeWidget(session=self.session) layer_tree.set_checkable(False) layer_tree.setup(self.data_collection) layer_tree.bind_selection_to_edit_subset() class Client(HubListener): def __init__(self, *args, **kwargs): super(Client, self).__init__(*args, **kwargs) self.count = Counter() def ping(self, message): self.count[message.sender] += 1 def register_to_hub(self, hub): hub.subscribe(self, SubsetUpdateMessage, handler=self.ping) d1 = Data(a=[[1, 2], [3, 4]], label='d1') d2 = Data(b=[[1, 2], [3, 4]], label='d2') d3 = Data(c=[[1, 2], [3, 4]], label='d3') d4 = Data(d=[[1, 2], [3, 4]], label='d4') self.data_collection.append(d1) self.data_collection.append(d2) self.data_collection.append(d3) self.data_collection.append(d4) client = Client() client.register_to_hub(self.hub) self.viewer.add_data(d1) self.viewer.add_data(d3) roi = XRangeROI(2.5, 3.5) self.viewer.apply_roi(roi) for subset in client.count: assert client.count[subset] == 1 def test_disable_incompatible(self): # Test to make sure that image and image subset layers are disabled if # their pixel coordinates are not compatible with the ones of the # reference data. self.viewer.add_data(self.image1) self.viewer.add_data(self.image2) assert self.viewer.state.reference_data is self.image1 self.data_collection.new_subset_group() process_events() assert len(self.viewer.layers) == 4 # Only the two layers associated with the reference data should be enabled for layer_artist in self.viewer.layers: if layer_artist.layer in (self.image1, self.image1.subsets[0]): assert layer_artist.enabled else: assert not layer_artist.enabled py1, px1 = self.image1.pixel_component_ids py2, px2 = self.image2.pixel_component_ids link1 = LinkSame(px1, px2) self.data_collection.add_link(link1) process_events() # One link isn't enough, second dataset layers are still not enabled for layer_artist in self.viewer.layers: if layer_artist.layer in (self.image1, self.image1.subsets[0]): assert layer_artist.enabled else: assert not layer_artist.enabled link2 = LinkSame(py1, py2) self.data_collection.add_link(link2) process_events() # All layers should now be enabled for layer_artist in self.viewer.layers: assert layer_artist.enabled self.data_collection.remove_link(link2) process_events() # We should now be back to the original situation for layer_artist in self.viewer.layers: if layer_artist.layer in (self.image1, self.image1.subsets[0]): assert layer_artist.enabled else: assert not layer_artist.enabled def test_change_reference_data(self, capsys): # Test to make sure everything works fine if we change the reference data. self.viewer.add_data(self.image1) self.viewer.add_data(self.image2) assert self.viewer.state.reference_data is self.image1 assert self.viewer.state.x_att_world is self.image1.pixel_component_ids[-1] assert self.viewer.state.y_att_world is self.image1.pixel_component_ids[-2] assert self.viewer.state.x_att is self.image1.pixel_component_ids[-1] assert self.viewer.state.y_att is self.image1.pixel_component_ids[-2] self.viewer.state.reference_data = self.image2 assert self.viewer.state.reference_data is self.image2 assert self.viewer.state.x_att_world is self.image2.world_component_ids[-1] assert self.viewer.state.y_att_world is self.image2.world_component_ids[-2] assert self.viewer.state.x_att is self.image2.pixel_component_ids[-1] assert self.viewer.state.y_att is self.image2.pixel_component_ids[-2] self.viewer.state.reference_data = self.image1 assert self.viewer.state.reference_data is self.image1 assert self.viewer.state.x_att_world is self.image1.pixel_component_ids[-1] assert self.viewer.state.y_att_world is self.image1.pixel_component_ids[-2] assert self.viewer.state.x_att is self.image1.pixel_component_ids[-1] assert self.viewer.state.y_att is self.image1.pixel_component_ids[-2] # Some exceptions used to happen during callbacks, and these show up # in stderr but don't interrupt the code, so we make sure here that # nothing was printed to stdout nor stderr. out, err = capsys.readouterr() assert out.strip() == "" assert err.strip() == "" @pytest.mark.parametrize('wcs', [False, True]) def test_change_reference_data_dimensionality(self, capsys, wcs): # Regression test for a bug that caused an exception when changing # the dimensionality of the reference data if wcs: first = self.image1_wcs second = self.hypercube_wcs else: first = self.image1 second = self.hypercube self.viewer.add_data(first) self.viewer.add_data(second) assert self.viewer.state.reference_data is first if wcs: assert self.viewer.state.x_att_world is first.world_component_ids[-1] assert self.viewer.state.y_att_world is first.world_component_ids[-2] else: assert self.viewer.state.x_att_world is first.pixel_component_ids[-1] assert self.viewer.state.y_att_world is first.pixel_component_ids[-2] assert self.viewer.state.x_att is first.pixel_component_ids[-1] assert self.viewer.state.y_att is first.pixel_component_ids[-2] self.viewer.state.reference_data = second assert self.viewer.state.reference_data is second if wcs: assert self.viewer.state.x_att_world is second.world_component_ids[-1] assert self.viewer.state.y_att_world is second.world_component_ids[-2] else: assert self.viewer.state.x_att_world is second.pixel_component_ids[-1] assert self.viewer.state.y_att_world is second.pixel_component_ids[-2] assert self.viewer.state.x_att is second.pixel_component_ids[-1] assert self.viewer.state.y_att is second.pixel_component_ids[-2] self.viewer.state.reference_data = first assert self.viewer.state.reference_data is first if wcs: assert self.viewer.state.x_att_world is first.world_component_ids[-1] assert self.viewer.state.y_att_world is first.world_component_ids[-2] else: assert self.viewer.state.x_att_world is first.pixel_component_ids[-1] assert self.viewer.state.y_att_world is first.pixel_component_ids[-2] assert self.viewer.state.x_att is first.pixel_component_ids[-1] assert self.viewer.state.y_att is first.pixel_component_ids[-2] # Some exceptions used to happen during callbacks, and these show up # in stderr but don't interrupt the code, so we make sure here that # nothing was printed to stdout nor stderr. out, err = capsys.readouterr() assert out.strip() == "" assert err.strip() == "" def test_scatter_overlay(self): self.viewer.add_data(self.image1) self.viewer.add_data(self.catalog) def test_removed_subset(self): # Regression test for a bug in v0.11.0 that meant that if a subset # was removed, the image viewer would then crash when changing view # (e.g. zooming in). The bug was caused by undeleted references to # FRBArtist due to circular references. We therefore check in this # test how many FRBArtist objects exist. def get_frb_artists(): mi = [] gc.collect() for obj in gc.get_objects(): try: if isinstance(obj, FRBArtist): mi.append(obj) except ReferenceError: pass return mi # The viewer starts off with one FRBArtist. This is also a good test # that other FRBArtist in other tests have been removed. assert len(get_frb_artists()) == 1 large_image = Data(x=np.random.random((2048, 2048))) self.data_collection.append(large_image) # The subset group can be made from any dataset subset_group = self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A') self.viewer.add_data(large_image) # Since the dataset added has a subset, and each subset has its own # FRBArtist, this increases the count. assert len(get_frb_artists()) == 2 assert len(self.viewer.layers) == 2 self.data_collection.remove_subset_group(subset_group) # Removing the subset should bring the count back to 1 again assert len(get_frb_artists()) == 1 def test_select_previously_incompatible_layer(self): # Regression test for a bug that caused a selection in a previously disabled # layer to enable the layer without updating the subset view self.viewer.add_data(self.image1) self.viewer.add_data(self.catalog) self.catalog.add_component([4, 5, 6], 'e') link1 = LinkSame(self.catalog.id['c'], self.image1.pixel_component_ids[0]) link2 = LinkSame(self.catalog.id['d'], self.image1.pixel_component_ids[1]) self.data_collection.add_link(link1) self.data_collection.add_link(link2) self.data_collection.new_subset_group(subset_state=self.catalog.id['e'] > 4) process_events() assert self.viewer.layers[0].enabled # image assert self.viewer.layers[1].enabled # scatter assert not self.viewer.layers[2].enabled # image subset assert self.viewer.layers[3].enabled # scatter subset assert not self.viewer.layers[2].image_artist.get_visible() self.data_collection.subset_groups[0].subset_state = self.catalog.id['c'] > -1 process_events() assert self.viewer.layers[0].enabled # image assert self.viewer.layers[1].enabled # scatter assert self.viewer.layers[2].enabled # image subset assert self.viewer.layers[3].enabled # scatter subset assert self.viewer.layers[2].image_artist.get_visible() def test_linking_and_enabling(self): # Regression test for a bug that caused layers not not be correctly # enabled/disabled. self.viewer.add_data(self.image1) self.viewer.add_data(self.catalog) self.catalog.add_component([4, 5, 6], 'e') self.data_collection.new_subset_group(subset_state=self.catalog.id['e'] > 4) process_events() assert self.viewer.layers[0].enabled # image assert not self.viewer.layers[1].enabled # scatter assert not self.viewer.layers[2].enabled # image subset assert not self.viewer.layers[3].enabled # scatter subset link1 = LinkSame(self.catalog.id['c'], self.image1.pixel_component_ids[0]) link2 = LinkSame(self.catalog.id['d'], self.image1.pixel_component_ids[1]) self.data_collection.add_link(link1) self.data_collection.add_link(link2) process_events() assert self.viewer.layers[0].enabled # image assert self.viewer.layers[1].enabled # scatter assert not self.viewer.layers[2].enabled # image subset assert self.viewer.layers[3].enabled # scatter subset def test_save_aggregate_slice(self, tmpdir): # Regression test to make sure that image viewers that include # aggregate slice objects in the slices can be saved/restored self.viewer.add_data(self.hypercube) self.viewer.state.slices = AggregateSlice(slice(1, 3), 10, np.sum), 3, 0, 0 filename = tmpdir.join('session.glu').strpath self.application.save_session(filename) self.application.close() app2 = GlueApplication.restore_session(filename) viewer_state = app2.viewers[0][0].state slices = viewer_state.slices assert isinstance(slices[0], AggregateSlice) assert slices[0].slice == slice(1, 3) assert slices[0].center == 10 assert slices[0].function is np.sum assert slices[1:] == (3, 0, 0) app2.close() def test_subset_cube_image(self): # Regression test to make sure that if an image and cube are present # in an image viewer and a subset is also present, we don't get an # error when trying to access the subset shape self.viewer.add_data(self.image1) self.data_collection.new_subset_group(label='subset', subset_state=self.image1.id['x'] > 1.5) self.viewer.add_data(self.hypercube) self.viewer.state.reference_data = self.hypercube assert self.viewer.layers[1].subset_array.shape == (4, 5) assert self.viewer.layers[3].subset_array.shape == (4, 5) def test_preserve_slice(self): # Regression test to make sure that when adding a second dataset to # an image viewer, the current slice in a cube does not change. self.viewer.add_data(self.hypercube) self.viewer.state.slices = (1, 2, 3, 4) self.viewer.add_data(self.image1) assert self.viewer.state.slices == (1, 2, 3, 4) def test_close(self): # Regression test for a bug that caused an error related to the toolbar # and _mpl_nav not being present when closing the viewer. self.viewer.toolbar.active_tool = self.viewer.toolbar.tools['mpl:zoom'] self.viewer.close(warn=False) def test_legend(self): from matplotlib.colors import to_hex viewer_state = self.viewer.state self.viewer.add_data(self.image1) self.viewer.state.legend.visible = True handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 1 assert labels[0] == 'image1' self.data_collection.new_subset_group('test', self.image1.id['x'] > 1) assert len(viewer_state.layers) == 2 handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 2 assert labels[1] == 'test' assert to_hex(handles[1].get_facecolor()) == viewer_state.layers[1].color class TestSessions(object): @pytest.mark.parametrize('protocol', [0, 1]) def test_session_back_compat(self, protocol): filename = os.path.join(DATA, 'image_v{0}.glu'.format(protocol)) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') dc = ga.session.data_collection assert len(dc) == 2 assert dc[0].label == 'data1' assert dc[1].label == 'data2' viewer1 = ga.viewers[0][0] assert len(viewer1.state.layers) == 3 assert viewer1.state.x_att_world is dc[0].id['World 1'] assert viewer1.state.y_att_world is dc[0].id['World 0'] assert viewer1.state.x_min < -0.5 assert viewer1.state.x_max > 1.5 assert viewer1.state.y_min <= -0.5 assert viewer1.state.y_max >= 1.5 layer_state = viewer1.state.layers[0] assert isinstance(layer_state, ImageLayerState) assert layer_state.visible assert layer_state.bias == 0.5 assert layer_state.contrast == 1.0 assert layer_state.stretch == 'sqrt' assert layer_state.percentile == 99 layer_state = viewer1.state.layers[1] assert isinstance(layer_state, ScatterLayerState) assert layer_state.visible layer_state = viewer1.state.layers[2] assert isinstance(layer_state, ImageSubsetLayerState) assert not layer_state.visible viewer2 = ga.viewers[0][1] assert len(viewer2.state.layers) == 2 assert viewer2.state.x_att_world is dc[0].id['World 1'] assert viewer2.state.y_att_world is dc[0].id['World 0'] assert viewer2.state.x_min < -0.5 assert viewer2.state.x_max > 1.5 assert viewer2.state.y_min <= -0.5 assert viewer2.state.y_max >= 1.5 layer_state = viewer2.state.layers[0] assert layer_state.visible assert layer_state.stretch == 'arcsinh' assert layer_state.v_min == 1 assert layer_state.v_max == 4 layer_state = viewer2.state.layers[1] assert layer_state.visible viewer3 = ga.viewers[0][2] assert len(viewer3.state.layers) == 2 assert viewer3.state.x_att_world is dc[0].id['World 1'] assert viewer3.state.y_att_world is dc[0].id['World 0'] assert viewer3.state.x_min < -0.5 assert viewer3.state.x_max > 1.5 assert viewer3.state.y_min <= -0.5 assert viewer3.state.y_max >= 1.5 layer_state = viewer3.state.layers[0] assert layer_state.visible assert layer_state.stretch == 'linear' assert layer_state.v_min == -2 assert layer_state.v_max == 2 layer_state = viewer3.state.layers[1] assert layer_state.visible ga.close() @pytest.mark.parametrize('protocol', [0, 1]) def test_session_cube_back_compat(self, protocol): filename = os.path.join(DATA, 'image_cube_v{0}.glu'.format(protocol)) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') dc = ga.session.data_collection assert len(dc) == 1 assert dc[0].label == 'array' viewer1 = ga.viewers[0][0] assert len(viewer1.state.layers) == 1 assert viewer1.state.x_att_world is dc[0].id['World 2'] assert viewer1.state.y_att_world is dc[0].id['World 1'] assert viewer1.state.slices == [2, 0, 0, 1] ga.close() @pytest.mark.parametrize('protocol', [0, 1]) def test_session_rgb_back_compat(self, protocol): filename = os.path.join(DATA, 'image_rgb_v{0}.glu'.format(protocol)) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') dc = ga.session.data_collection assert len(dc) == 1 assert dc[0].label == 'rgbcube' viewer1 = ga.viewers[0][0] assert len(viewer1.state.layers) == 3 assert viewer1.state.color_mode == 'One color per layer' layer_state = viewer1.state.layers[0] assert layer_state.visible assert layer_state.attribute.label == 'a' assert layer_state.color == 'r' layer_state = viewer1.state.layers[1] assert not layer_state.visible assert layer_state.attribute.label == 'c' assert layer_state.color == 'g' layer_state = viewer1.state.layers[2] assert layer_state.visible assert layer_state.attribute.label == 'b' assert layer_state.color == 'b' ga.close() def test_indexed_data(capsys): # Make sure that the image viewer works properly with IndexedData objects data_4d = Data(label='hypercube_wcs', x=np.random.random((3, 5, 4, 3)), coords=WCS(naxis=4)) data_2d = IndexedData(data_4d, (2, None, 3, None)) application = GlueApplication() session = application.session hub = session.hub data_collection = session.data_collection data_collection.append(data_4d) data_collection.append(data_2d) viewer = application.new_data_viewer(ImageViewer) viewer.add_data(data_2d) assert viewer.state.x_att is data_2d.pixel_component_ids[1] assert viewer.state.y_att is data_2d.pixel_component_ids[0] assert viewer.state.x_att_world is data_2d.world_component_ids[1] assert viewer.state.y_att_world is data_2d.world_component_ids[0] process_events() application.close() # Some exceptions used to happen during callbacks, and these show up # in stderr but don't interrupt the code, so we make sure here that # nothing was printed to stdout nor stderr. out, err = capsys.readouterr() assert out.strip() == "" assert err.strip() == "" glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/tests/test_regression.py0000644000175000017500000000272613605357235025505 0ustar noahfxnoahfx# Miscellaneous regression tests for the image viewer import pytest import numpy as np from glue.core import Data from glue.viewers.image.qt import ImageViewer from glue.core.tests.util import simple_session from glue.tests.helpers import PYSIDE2_INSTALLED # noqa @pytest.mark.skipif('PYSIDE2_INSTALLED') @pytest.mark.mpl_image_compare(tolerance=1, savefig_kwargs={'dpi': 50}) def test_resample_on_zoom(): # For images where the aspect ratio of pixels is fixed to be square, when # the user zooms in, the limits of the axes are actually changed twice by # matplotlib - a second time when the aspect ratio is enforced. So we need # to make sure that we update the FRB artist when this is the case. session = simple_session() np.random.seed(12345) data = Data(x=np.random.random((2048, 2048)), label='image') session.data_collection.append(data) image = ImageViewer(session=session) image.add_data(data) image.show() try: device_ratio = image.axes.figure.canvas.devicePixelRatio() except AttributeError: device_ratio = 1. image.axes.figure.canvas.key_press_event('o') image.axes.figure.canvas.button_press_event(200 * device_ratio, 200 * device_ratio, 1) image.axes.figure.canvas.motion_notify_event(400 * device_ratio, 210 * device_ratio) image.axes.figure.canvas.button_release_event(400 * device_ratio, 210 * device_ratio, 1) figure = image.axes.figure image.close() return figure glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/data_viewer.py0000644000175000017500000000457713630201003023377 0ustar noahfxnoahfxfrom glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer from glue.viewers.scatter.qt.layer_style_editor import ScatterLayerStyleEditor from glue.viewers.scatter.layer_artist import ScatterLayerArtist from glue.viewers.image.qt.layer_style_editor import ImageLayerStyleEditor from glue.viewers.image.qt.layer_style_editor_subset import ImageLayerSubsetStyleEditor from glue.viewers.image.layer_artist import ImageLayerArtist, ImageSubsetLayerArtist from glue.viewers.image.qt.options_widget import ImageOptionsWidget from glue.viewers.image.qt.mouse_mode import RoiClickAndDragMode from glue.viewers.image.state import ImageViewerState from glue.utils import defer_draw, decorate_all_methods # Import the mouse mode to make sure it gets registered from glue.viewers.image.qt.contrast_mouse_mode import ContrastBiasMode # noqa from glue.viewers.image.qt.profile_viewer_tool import ProfileViewerTool # noqa from glue.viewers.image.qt.pixel_selection_mode import PixelSelectionTool # noqa from glue.viewers.image.viewer import MatplotlibImageMixin __all__ = ['ImageViewer'] @decorate_all_methods(defer_draw) class ImageViewer(MatplotlibImageMixin, MatplotlibDataViewer): LABEL = '2D Image' _default_mouse_mode_cls = RoiClickAndDragMode _layer_style_widget_cls = {ImageLayerArtist: ImageLayerStyleEditor, ImageSubsetLayerArtist: ImageLayerSubsetStyleEditor, ScatterLayerArtist: ScatterLayerStyleEditor} _state_cls = ImageViewerState _options_cls = ImageOptionsWidget allow_duplicate_data = True # NOTE: _data_artist_cls and _subset_artist_cls are not defined - instead # we override get_data_layer_artist and get_subset_layer_artist for # more advanced logic. tools = ['select:rectangle', 'select:xrange', 'select:yrange', 'select:circle', 'select:polygon', 'image:point_selection', 'image:contrast_bias', 'profile-viewer'] def __init__(self, session, parent=None, state=None): MatplotlibDataViewer.__init__(self, session, wcs=True, parent=parent, state=state) MatplotlibImageMixin.setup_callbacks(self) def closeEvent(self, *args): super(ImageViewer, self).closeEvent(*args) if self.axes._composite_image is not None: self.axes._composite_image.remove() self.axes._composite_image = None glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/options_widget.ui0000644000175000017500000002733413661230662024145 0ustar noahfxnoahfx Widget 0 0 288 238 2D Image 5 5 5 5 5 0 General 10 10 10 10 10 5 75 true mode Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QComboBox::AdjustToMinimumContentsLength QComboBox::AdjustToMinimumContentsLength Qt::Horizontal Qt::Horizontal 40 5 75 true reference Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QComboBox::AdjustToMinimumContentsLength 75 true aspect Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true x axis Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true y axis Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QComboBox::AdjustToMinimumContentsLength QComboBox::AdjustToMinimumContentsLength Qt::Vertical 20 40 Limits 10 10 10 10 10 5 padding: 0px 75 true y axis Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 40 5 Qt::Vertical 20 40 75 true x axis Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter padding: 0px label_2 valuetext_x_min button_flip_x valuetext_x_max label_2 valuetext_y_min button_flip_y valuetext_y_max verticalSpacer_3 horizontalSpacer Axes 5 5 5 5 Legend 5 5 5 5 AxesEditorWidget QWidget
glue.viewers.matplotlib.qt.axes_editor
LegendEditorWidget QWidget
glue.viewers.matplotlib.qt.legend_editor
glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/layer_style_editor_subset.ui0000644000175000017500000000474713502206677026404 0ustar noahfxnoahfx Form 0 0 234 55 Form 5 5 0 0 100 Qt::Horizontal 75 true transparency Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true color Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 QColorBox QLabel
glue.utils.qt.colors
glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/slice_widget.py0000644000175000017500000001231313752534424023557 0ustar noahfxnoahfximport numpy as np from glue.core.coordinates import LegacyCoordinates from glue.core.coordinate_helpers import dependent_axes, world_axis from glue.viewers.common.qt.data_slice_widget import SliceWidget from glue.viewers.image.state import AggregateSlice from glue.utils.decorators import avoid_circular __all__ = ['MultiSliceWidgetHelper'] class MultiSliceWidgetHelper(object): def __init__(self, viewer_state=None, layout=None): self.viewer_state = viewer_state self.layout = layout self.layout.setSpacing(4) self.layout.setContentsMargins(0, 3, 0, 3) self.viewer_state.add_callback('x_att', self.sync_sliders_from_state) self.viewer_state.add_callback('y_att', self.sync_sliders_from_state) self.viewer_state.add_callback('slices', self.sync_sliders_from_state) self.viewer_state.add_callback('reference_data', self.sync_sliders_from_state) self._sliders = [] self._reference_data = None self._x_att = None self._y_att = None self.sync_sliders_from_state() @property def data(self): return self.viewer_state.reference_data def _clear(self): for _ in range(self.layout.count()): self.layout.takeAt(0) for s in self._sliders: if s is not None: s.close() self._sliders = [] @avoid_circular def sync_state_from_sliders(self, *args): slices = [] for i, slider in enumerate(self._sliders): if slider is not None: slices.append(slider.state.slice_center) else: slices.append(self.viewer_state.slices[i]) self.viewer_state.slices = tuple(slices) @avoid_circular def sync_sliders_from_state(self, *args): if self.data is None or self.viewer_state.x_att is None or self.viewer_state.y_att is None: return if self.viewer_state.x_att is self.viewer_state.y_att: return # Update sliders if needed if (self.viewer_state.reference_data is not self._reference_data or self.viewer_state.x_att is not self._x_att or self.viewer_state.y_att is not self._y_att): self._reference_data = self.viewer_state.reference_data self._x_att = self.viewer_state.x_att self._y_att = self.viewer_state.y_att self._clear() for i in range(self.data.ndim): if i == self.viewer_state.x_att.axis or i == self.viewer_state.y_att.axis: self._sliders.append(None) continue # TODO: For now we simply pass a single set of world coordinates, # but we will need to generalize this in future. We deliberately # check the type of data.coords here since we want to treat # subclasses differently. if getattr(self.data, 'coords') is not None and type(self.data.coords) != LegacyCoordinates: world_axis_index = self.data.ndim - 1 - i world = world_axis(self.data.coords, self.data, pixel_axis=world_axis_index, world_axis=world_axis_index) world_unit = self.data.coords.world_axis_units[world_axis_index] world_warning = len(dependent_axes(self.data.coords, i)) > 1 world_label = self.data.world_component_ids[i].label else: world = None world_unit = None world_warning = False world_label = self.data.pixel_component_ids[i].label slider = SliceWidget(world_label, hi=self.data.shape[i] - 1, world=world, world_unit=world_unit, world_warning=world_warning) self.slider_state = slider.state self.slider_state.add_callback('slice_center', self.sync_state_from_sliders) self._sliders.append(slider) self.layout.addWidget(slider) for i in range(self.data.ndim): if self._sliders[i] is not None: if isinstance(self.viewer_state.slices[i], AggregateSlice): self._sliders[i].state.slice_center = self.viewer_state.slices[i].center else: self._sliders[i].state.slice_center = self.viewer_state.slices[i] if __name__ == "__main__": from glue.core import Data from glue.utils.qt import get_qapp from echo import CallbackProperty from glue.core.state_objects import State app = get_qapp() class FakeViewerState(State): x_att = CallbackProperty() y_att = CallbackProperty() reference_data = CallbackProperty() slices = CallbackProperty() viewer_state = FakeViewerState() data = Data(x=np.random.random((3, 50, 20, 5, 3))) viewer_state.reference_data = data viewer_state.x_att = data.pixel_component_ids[0] viewer_state.y_att = data.pixel_component_ids[3] viewer_state.slices = [0] * 5 widget = MultiSliceWidgetHelper(viewer_state) widget.show() app.exec_() glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/__init__.py0000644000175000017500000000030413630201003022624 0ustar noahfxnoahfxfrom .data_viewer import ImageViewer # noqa from .standalone_image_viewer import StandaloneImageViewer # noqa def setup(): from glue.config import qt_client qt_client.add(ImageViewer) glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/options_widget.py0000644000175000017500000000312513752534424024154 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui, fix_tab_widget_fontsize from glue.viewers.image.qt.slice_widget import MultiSliceWidgetHelper from glue.viewers.matplotlib.state import MatplotlibDataViewerState __all__ = ['ImageOptionsWidget'] class ImageOptionsWidget(QtWidgets.QWidget): def __init__(self, viewer_state, session, parent=None): super(ImageOptionsWidget, self).__init__(parent=parent) self.ui = load_ui('options_widget.ui', self, directory=os.path.dirname(__file__)) fix_tab_widget_fontsize(self.ui.tab_widget) self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) self._connections_axes = autoconnect_callbacks_to_qt(viewer_state, self.ui.axes_editor.ui) connect_kwargs = {'alpha': dict(value_range=(0, 1))} self._connections_legend = autoconnect_callbacks_to_qt(viewer_state.legend, self.ui.legend_editor.ui, connect_kwargs) self.viewer_state = viewer_state self.slice_helper = MultiSliceWidgetHelper(viewer_state=self.viewer_state, layout=self.ui.layout_slices) self.session = session self.ui.axes_editor.button_apply_all.clicked.connect(self._apply_all_viewers) def _apply_all_viewers(self): for tab in self.session.application.viewers: for viewer in tab: if isinstance(viewer.state, MatplotlibDataViewerState): viewer.state.update_axes_settings_from(self.viewer_state) glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/mouse_mode.py0000644000175000017500000000607013636642246023257 0ustar noahfxnoahfxfrom qtpy.QtGui import QCursor from qtpy.QtWidgets import QMenu, QAction from glue.core.subset import RoiSubsetState from glue.core.roi import MplPolygonalROI from glue.core.edit_subset_mode import ReplaceMode from glue.viewers.matplotlib.mouse_mode import MouseMode from glue.viewers.image.layer_artist import ImageSubsetLayerArtist __all__ = ['RoiClickAndDragMode'] _MPL_LEFT_CLICK = 1 _MPL_RIGHT_CLICK = 3 class RoiClickAndDragMode(MouseMode): """ A MouseMode that enables clicking and dragging of existing ROIs. """ def __init__(self, viewer, **kwargs): super(RoiClickAndDragMode, self).__init__(viewer, **kwargs) self._viewer = viewer self._dc = viewer.state.data_collection self._edit_subset_mode = viewer.session.edit_subset_mode self._roi = None self._subset = None self._selected = False def _select_roi(self, roi, index, event): self._roi = MplPolygonalROI(self._axes, roi=roi) self._roi.start_selection(event, scrubbing=True) self._edit_subset_mode.edit_subset = [self._dc.subset_groups[index]] def _deselect_roi(self, event): self._edit_subset_mode.edit_subset = [] if self._roi: self._roi = None self._subset = None def _display_roi_context_menu(self, roi_index): def delete_roi(event): self._dc.remove_subset_group(self._dc.subset_groups[roi_index]) context_menu = QMenu() action = QAction("Delete ROI", context_menu) action.triggered.connect(delete_roi) context_menu.addAction(action) pos = self._viewer.mapToParent(QCursor().pos()) context_menu.exec_(pos) def press(self, event): # Ignore button presses outside the data viewer canvas if event.xdata is None or event.ydata is None: return x, y = (int(event.xdata + 0.5), int(event.ydata + 0.5)) roi_index = 0 for layer in self._viewer.layers: if not isinstance(layer, ImageSubsetLayerArtist): continue subset_state = layer.state.layer.subset_state if layer.visible and isinstance(subset_state, RoiSubsetState): if subset_state.roi.contains(x, y): if event.button == _MPL_LEFT_CLICK: self._select_roi(subset_state.roi, roi_index, event) self._subset = layer.state.layer elif event.button == _MPL_RIGHT_CLICK: self._display_roi_context_menu(roi_index) self._selected = True break roi_index += 1 else: self._selected = False self._deselect_roi(event) def move(self, event): if self._roi is None or not self._selected: return self._roi.update_selection(event) def release(self, event): if self._roi: self._viewer.apply_roi(self._roi.roi(), override_mode=ReplaceMode) self._roi.finalize_selection(event) self._selected = False glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/pixel_selection_mode.py0000644000175000017500000000367413605357235025321 0ustar noahfxnoahfxfrom glue.config import viewer_tool from glue.core.command import ApplySubsetState from glue.core.edit_subset_mode import ReplaceMode from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase from glue.viewers.image.pixel_selection_subset_state import PixelSubsetState __all__ = ['PixelSelectionTool'] @viewer_tool class PixelSelectionTool(ToolbarModeBase): """ Selects pixel under mouse cursor. """ icon = "glue_crosshair" tool_id = 'image:point_selection' action_text = 'Pixel' tool_tip = 'Select a single pixel based on mouse location' status_tip = 'CLICK to select a point, CLICK and DRAG to update the selection in real time' _pressed = False def __init__(self, *args, **kwargs): super(PixelSelectionTool, self).__init__(*args, **kwargs) self._move_callback = self._select_pixel self._press_callback = self._on_press self._release_callback = self._on_release def _on_press(self, mode): self._pressed = True self.viewer.session.edit_subset_mode.mode = ReplaceMode self._select_pixel(mode) def _on_release(self, mode): self._pressed = False def _select_pixel(self, mode): """ Select a pixel """ if not self._pressed: return x, y = self._event_xdata, self._event_ydata if x is None or y is None: return None x = int(round(x)) y = int(round(y)) slices = [slice(None)] * self.viewer.state.reference_data.ndim slices[self.viewer.state.x_att.axis] = slice(x, x + 1) slices[self.viewer.state.y_att.axis] = slice(y, y + 1) subset_state = PixelSubsetState(self.viewer.state.reference_data, slices) cmd = ApplySubsetState(data_collection=self.viewer._data, subset_state=subset_state, override_mode=None) self.viewer._session.command_stack.do(cmd) glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/standalone_image_viewer.py0000644000175000017500000001222013630201003025740 0ustar noahfxnoahfximport numpy as np from qtpy import QtCore, QtWidgets from glue.config import colormaps from glue.viewers.common.qt.toolbar import BasicToolbar from glue.viewers.matplotlib.qt.widget import MplWidget from glue.viewers.image.composite_array import CompositeArray from glue.external.modest_image import imshow from glue.utils import defer_draw from glue.viewers.matplotlib.mpl_axes import init_mpl # Import the mouse mode to make sure it gets registered from glue.viewers.image.qt.contrast_mouse_mode import ContrastBiasMode # noqa __all__ = ['StandaloneImageViewer'] class StandaloneImageViewer(QtWidgets.QMainWindow): """ A simplified image viewer, without any brushing or linking, but with the ability to adjust contrast and resample. """ window_closed = QtCore.Signal() _toolbar_cls = BasicToolbar tools = ['image:contrast', 'image:colormap'] def __init__(self, image=None, wcs=None, parent=None, **kwargs): """ :param image: Image to display (2D numpy array) :param parent: Parent widget (optional) :param kwargs: Extra keywords to pass to imshow """ super(StandaloneImageViewer, self).__init__(parent) self.central_widget = MplWidget() self.setCentralWidget(self.central_widget) self._setup_axes() self._composite = CompositeArray() self._composite.allocate('image') self._im = None self.initialize_toolbar() if image is not None: self.set_image(image=image, wcs=wcs, **kwargs) def _setup_axes(self): _, self._axes = init_mpl(self.central_widget.canvas.fig, axes=None, wcs=True) self._axes.set_aspect('equal', adjustable='datalim') @defer_draw def set_image(self, image=None, wcs=None, **kwargs): """ Update the image shown in the widget """ if self._im is not None: self._im.remove() self._im = None kwargs.setdefault('origin', 'upper') self._composite.set('image', array=image, color=colormaps.members[0][1]) self._im = imshow(self._axes, self._composite, **kwargs) self._im_array = image self._set_norm(self._contrast_mode) if 'extent' in kwargs: self.axes.set_xlim(kwargs['extent'][:2]) self.axes.set_ylim(kwargs['extent'][2:]) else: ny, nx = image.shape self.axes.set_xlim(-0.5, nx - 0.5) self.axes.set_ylim(-0.5, ny - 0.5) # FIXME: for a reason I don't quite understand, dataLim doesn't # get updated immediately here, which means that there are then # issues in the first draw of the image (the limits are such that # only part of the image is shown). We just set dataLim manually # to avoid this issue. This is also done in ImageViewer. self.axes.dataLim.intervalx = self.axes.get_xlim() self.axes.dataLim.intervaly = self.axes.get_ylim() self._redraw() @property def axes(self): """ The Matplotlib axes object for this figure """ return self._axes def show(self): super(StandaloneImageViewer, self).show() self._redraw() def _redraw(self): self.central_widget.canvas.draw_idle() def set_cmap(self, cmap): self._composite.set('image', color=cmap) self._im.invalidate_cache() self._redraw() def mdi_wrap(self): """ Embed this widget in a GlueMdiSubWindow """ from glue.app.qt.mdi_area import GlueMdiSubWindow sub = GlueMdiSubWindow() sub.setWidget(self) self.destroyed.connect(sub.close) self.window_closed.connect(sub.close) sub.resize(self.size()) self._mdi_wrapper = sub return sub def closeEvent(self, event): if self._im is not None: self._im.remove() self._im = None self.window_closed.emit() return super(StandaloneImageViewer, self).closeEvent(event) def _set_norm(self, mode): """ Use the `ContrastMouseMode` to adjust the transfer function """ pmin, pmax = mode.get_clip_percentile() if pmin is None: clim = mode.get_vmin_vmax() else: clim = (np.nanpercentile(self._im_array, pmin), np.nanpercentile(self._im_array, pmax)) stretch = mode.stretch self._composite.set('image', clim=clim, stretch=stretch, bias=mode.bias, contrast=mode.contrast) self._im.invalidate_cache() self._redraw() def initialize_toolbar(self): from glue.config import viewer_tool self.toolbar = self._toolbar_cls(self) for tool_id in self.tools: mode_cls = viewer_tool.members[tool_id] if tool_id == 'image:contrast': mode = mode_cls(self, move_callback=self._set_norm) self._contrast_mode = mode else: mode = mode_cls(self) self.toolbar.add_tool(mode) self.addToolBar(self.toolbar) def set_status(self, message): sb = self.statusBar() sb.showMessage(message) glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/layer_style_editor.ui0000644000175000017500000001770213502206677025012 0ustar noahfxnoahfx Form 0 0 292 197 Form 5 5 5 5 10 5 QComboBox::AdjustToMinimumContentsLength 75 true attribute Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true limits Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 10 80 0 QComboBox::AdjustToMinimumContentsLength false 0 0 50 0 100 Qt::Horizontal Sync true 2 padding: 0px 50 0 QComboBox::AdjustToMinimumContentsLength 50 0 QComboBox::AdjustToMinimumContentsLength 10 50 0 1000 Qt::Horizontal 50 0 1000 Qt::Horizontal Reset 75 true contrast/bias Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true color/opacity Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 40 5 QColorBox QLabel
glue.utils.qt.colors
QColormapCombo QComboBox
glue.utils.qt.colors
glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/contrast_mouse_mode.py0000644000175000017500000000231213657331513025162 0ustar noahfxnoahfx# New contrast/bias mode that operates on viewers with state objects from echo import delay_callback from glue.config import viewer_tool from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase @viewer_tool class ContrastBiasMode(ToolbarModeBase): """ Uses right mouse button drags to set bias and contrast, DS9-style. The horizontal position of the mouse sets the bias, the vertical position sets the contrast. """ icon = 'glue_contrast' tool_id = 'image:contrast_bias' action_text = 'Contrast/Bias' tool_tip = 'Adjust the bias/contrast' status_tip = ('CLICK and DRAG on image from left to right to adjust ' 'bias and up and down to adjust contrast') def move(self, event): """ Update bias and contrast on Right Mouse button drag. """ if event.button not in (1, 3): return x, y = self.viewer.axes.transAxes.inverted().transform((event.x, event.y)) state = self.viewer.selected_layer.state with delay_callback(state, 'bias', 'contrast'): state.bias = -(x * 2 - 1.5) state.contrast = 10. ** (y * 2 - 1) super(ContrastBiasMode, self).move(event) glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/profile_viewer_tool.py0000644000175000017500000000653413630201003025156 0ustar noahfxnoahfxfrom glue.config import viewer_tool from glue.viewers.common.tool import Tool from glue.core.qt.dialogs import info, warn from glue.core.component_id import PixelComponentID @viewer_tool class ProfileViewerTool(Tool): icon = 'glue_spectrum' tool_id = 'profile-viewer' @property def profile_viewers_exist(self): from glue.viewers.profile.qt import ProfileViewer for tab in self.viewer.session.application.viewers: for viewer in tab: if isinstance(viewer, ProfileViewer): return True return False def activate(self): if self.profile_viewers_exist: proceed = warn('A profile viewer was already created', 'Do you really want to create a new one?', default='Cancel', setting='show_warn_profile_duplicate') if not proceed: return else: proceed = info('Creating a profile viewer', 'Note: profiles are ' 'computed from datasets and subsets collapsed along all but one ' 'dimension. To view the profile of part of the data, once you ' 'click OK you can draw and update a subset in the current ' 'image viewer and the profile will update accordingly.', setting='show_info_profile_open') if not proceed: return from glue.viewers.profile.qt import ProfileViewer profile_viewer = self.viewer.session.application.new_data_viewer(ProfileViewer) any_added = False for data in self.viewer.session.data_collection: if data in self.viewer._layer_artist_container: result = profile_viewer.add_data(data) any_added = any_added or result if not any_added: profile_viewer.close() return # If the reference data for the current image viewer is in the profile # viewer, we make sure that it is used as the reference data there too if self.viewer.state.reference_data in profile_viewer._layer_artist_container: profile_viewer.state.reference_data = self.viewer.state.reference_data # We now pick an attribute in the profile viewer that is one of the ones # with a slider in the image viewer. Note that the attribute viewer may # be a pixel attribute or world attribute depending on what information # is available in the coordinates, so we need to be careful about that. x_att = profile_viewer.state.x_att reference_data = self.viewer.state.reference_data if isinstance(profile_viewer.state.x_att, PixelComponentID): for att in reference_data.pixel_component_ids: if att is not self.viewer.state.x_att and att is not self.viewer.state.y_att: if att is not profile_viewer.state.x_att: profile_viewer.state.x_att = att else: for att in reference_data.world_component_ids: if att is not self.viewer.state.x_att_world and att is not self.viewer.state.y_att_world: if att is not profile_viewer.state.x_att: profile_viewer.state.x_att = att glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/layer_style_editor_subset.py0000644000175000017500000000110513657331513026377 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui class ImageLayerSubsetStyleEditor(QtWidgets.QWidget): def __init__(self, layer, parent=None): super(ImageLayerSubsetStyleEditor, self).__init__(parent=parent) self.ui = load_ui('layer_style_editor_subset.ui', self, directory=os.path.dirname(__file__)) connect_kwargs = {'alpha': dict(value_range=(0, 1))} self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) glueviz-1.0.1+dfsg.orig/glue/viewers/image/qt/layer_style_editor.py0000644000175000017500000000230513657331513025015 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui class ImageLayerStyleEditor(QtWidgets.QWidget): def __init__(self, layer, parent=None): super(ImageLayerStyleEditor, self).__init__(parent=parent) self.ui = load_ui('layer_style_editor.ui', self, directory=os.path.dirname(__file__)) connect_kwargs = {'alpha': dict(value_range=(0, 1)), 'contrast': dict(value_range=(0.1, 10), log=True), 'bias': dict(value_range=(1.5, -0.5))} self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) layer._viewer_state.add_callback('color_mode', self._update_color_mode) self._update_color_mode(layer._viewer_state.color_mode) self.ui.bool_global_sync.setToolTip('Whether to sync the color and transparency with other viewers') def _update_color_mode(self, color_mode): if color_mode == 'Colormaps': self.ui.color_color.hide() self.ui.combodata_cmap.show() else: self.ui.color_color.show() self.ui.combodata_cmap.hide() glueviz-1.0.1+dfsg.orig/glue/viewers/image/python_export.py0000644000175000017500000000773513661230662023423 0ustar noahfxnoahfxfrom glue.viewers.common.python_export import code, serialize_options def python_export_image_layer(layer, *args): if not layer.enabled or not layer.visible: return [], None script = "" imports = ["from glue.viewers.image.state import get_sliced_data_maker"] imports += ["import matplotlib.patches as mpatches"] slices, agg_func, transpose = layer._viewer_state.numpy_slice_aggregation_transpose # TODO: implement aggregation, ignore for now script += "# Define a function that will get a fixed resolution buffer\n" options = {'data': code('layer_data'), 'x_axis': layer._viewer_state.x_att.axis, 'y_axis': layer._viewer_state.y_att.axis, 'slices': slices, 'target_cid': code("layer_data.id['{0}']".format(layer.state.attribute))} if transpose: options['transpose'] = True script += "array_maker = get_sliced_data_maker({0})\n\n".format(serialize_options(options)) script += "composite.allocate('{0}')\n".format(layer.uuid) if layer._viewer_state.color_mode == 'Colormaps': color = code('plt.cm.' + layer.state.cmap.name) else: color = layer.state.color options = dict(array=code('array_maker'), clim=(layer.state.v_min, layer.state.v_max), visible=layer.state.visible, zorder=layer.state.zorder, color=color, contrast=layer.state.contrast, bias=layer.state.bias, alpha=layer.state.alpha, stretch=layer.state.stretch) script += "composite.set('{0}', {1})\n".format(layer.uuid, serialize_options(options)) if layer._viewer_state.color_mode == 'Colormaps': imports += ["from glue.utils.matplotlib import ColormapPatchHandler"] script += "handle = mpatches.Patch(color='{0}')\n".format(layer.state.color) script += "handler = ColormapPatchHandler(" + code('plt.cm.' + layer.state.cmap.name) + ")\n" script += "legend_handles.append(handle)\n" script += "legend_handler_dict[handle] = handler\n" script += "legend_labels.append(layer_data.label)\n" else: options = dict(color=layer.state.color, alpha=layer.state.alpha) script += "handle = mpatches.Patch({0})\n".format(serialize_options(options)) script += "legend_handles.append(handle)\n" script += "legend_labels.append(layer_data.label)\n" script += "\n" return imports, script.strip() def python_export_image_subset_layer(layer, *args): if not layer.enabled or not layer.visible: return [], None script = "" imports = ["from glue.viewers.image.state import get_sliced_data_maker"] slices, agg_func, transpose = layer._viewer_state.numpy_slice_aggregation_transpose # TODO: implement aggregation, ignore for now script += "# Define a function that will get a fixed resolution buffer of the mask\n" options = {'data': code('layer_data'), 'x_axis': layer._viewer_state.x_att.axis, 'y_axis': layer._viewer_state.y_att.axis, 'slices': slices} if transpose: options['transpose'] = True script += "array_maker = get_sliced_data_maker({0})\n\n".format(serialize_options(options)) script += "\n\n" options = dict(origin='lower', interpolation='nearest', color=layer.state.color, vmin=0, vmax=1, aspect=layer._viewer_state.aspect, zorder=layer.state.zorder, alpha=layer.state.alpha) script += "imshow(ax, {0}, {1})\n".format(code('array_maker'), serialize_options(options)) # legend options = dict(color=layer.state.color, alpha=layer.state.alpha) script += "handle = mpatches.Patch({0})\n".format(serialize_options(options)) script += "legend_handles.append(handle)\n" script += "legend_labels.append(layer_data.label)\n" script += "\n" return imports, script.strip() glueviz-1.0.1+dfsg.orig/glue/viewers/image/frb_artist.py0000644000175000017500000000427713605357235022642 0ustar noahfxnoahfx""" Matplotlib artist for fixed resolution buffers. """ from mpl_scatter_density.base_image_artist import BaseImageArtist import numpy as np from glue.utils import color2rgb class FRBArtist(BaseImageArtist): def __init__(self, ax, **kwargs): self._array_maker = None super(FRBArtist, self).__init__(ax, update_while_panning=False, array_func=self.array_func_wrapper, **kwargs) def array_func_wrapper(self, bins=None, range=None): if self._array_maker is None: return np.array([[np.nan]]) else: ny, nx = bins (ymin, ymax), (xmin, xmax) = range bounds = [(ymin, ymax, ny), (xmin, xmax, nx)] array = self._array_maker(bounds) if array is None: return np.array([[np.nan]]) else: return array def set_array_maker(self, array_maker): self._array_maker = array_maker def invalidate_cache(self): self.stale = True self.set_visible(True) def imshow(axes, array_maker, aspect=None, vmin=None, vmax=None, color=None, **kwargs): """ Similar to matplotlib's imshow command, but produces a FRBArtist """ axes.set_aspect(aspect) im = FRBArtist(axes, **kwargs) if color: def wrapper(bounds=None): # Get original array mask = array_maker(bounds=bounds) # Convert to RGBA array" r, g, b = color2rgb(color) mask = np.dstack((r * mask, g * mask, b * mask, mask * .5)) mask = (255 * mask).astype(np.uint8) return mask im.set_array_maker(wrapper) else: im.set_array_maker(array_maker) axes._set_artist_props(im) if im.get_clip_path() is None: # image does not already have clipping set, clip to axes patch im.set_clip_path(axes.patch) if vmin is not None or vmax is not None: im.set_clim(vmin, vmax) axes.images.append(im) def remove(h): axes.images.remove(h) # The following is needed for Matplotlib's .remove() method to work im._remove_method = remove return im glueviz-1.0.1+dfsg.orig/glue/viewers/image/state.py0000644000175000017500000005722513752534424021624 0ustar noahfxnoahfximport uuid from collections import defaultdict from glue.core import BaseData from glue.config import colormaps from glue.viewers.matplotlib.state import (MatplotlibDataViewerState, MatplotlibLayerState, DeferredDrawCallbackProperty as DDCProperty, DeferredDrawSelectionCallbackProperty as DDSCProperty) from glue.core.state_objects import StateAttributeLimitsHelper from glue.utils import defer_draw, view_shape from echo import delay_callback from glue.core.data_combo_helper import ManualDataComboHelper, ComponentIDComboHelper from glue.core.exceptions import IncompatibleDataException __all__ = ['ImageViewerState', 'ImageLayerState', 'ImageSubsetLayerState', 'AggregateSlice'] def get_sliced_data_maker(x_axis=None, y_axis=None, slices=None, data=None, target_cid=None, reference_data=None, transpose=False): """ Convenience function for use in exported Python scripts. """ if reference_data is None: reference_data = data def get_array(bounds=None): full_bounds = list(slices) full_bounds[y_axis] = bounds[0] full_bounds[x_axis] = bounds[1] if isinstance(data, BaseData): array = data.compute_fixed_resolution_buffer(full_bounds, target_data=reference_data, target_cid=target_cid, broadcast=False) else: array = data.data.compute_fixed_resolution_buffer(full_bounds, target_data=reference_data, subset_state=data.subset_state, broadcast=False) if transpose: array = array.transpose() return array return get_array class AggregateSlice(object): def __init__(self, slice=None, center=None, function=None): self.slice = slice self.center = center self.function = function def __gluestate__(self, context): state = dict(slice=context.do(self.slice), center=self.center, function=context.do(self.function)) return state @classmethod def __setgluestate__(cls, rec, context): return cls(slice=context.object(rec['slice']), center=rec['center'], function=context.object(rec['function'])) class ImageViewerState(MatplotlibDataViewerState): """ A state class that includes all the attributes for an image viewer. """ x_att = DDCProperty(docstring='The component ID giving the pixel component ' 'shown on the x axis') y_att = DDCProperty(docstring='The component ID giving the pixel component ' 'shown on the y axis') x_att_world = DDSCProperty(docstring='The component ID giving the world component ' 'shown on the x axis', default_index=-1) y_att_world = DDSCProperty(docstring='The component ID giving the world component ' 'shown on the y axis', default_index=-2) aspect = DDSCProperty(0, docstring='Whether to enforce square pixels (``equal``) ' 'or fill the axes (``auto``)') reference_data = DDSCProperty(docstring='The dataset that is used to define the ' 'available pixel/world components, and ' 'which defines the coordinate frame in ' 'which the images are shown') slices = DDCProperty(docstring='The current slice along all dimensions') color_mode = DDSCProperty(0, docstring='Whether each layer can have ' 'its own colormap (``Colormaps``) or ' 'whether each layer is assigned ' 'a single color (``One color per layer``)') dpi = DDCProperty(72, docstring='The resolution (in dots per inch) of density maps, if present') def __init__(self, **kwargs): super(ImageViewerState, self).__init__() self.limits_cache = {} # NOTE: we don't need to use StateAttributeLimitsHelper here because # we can simply call reset_limits below when x/y attributes change. # Using StateAttributeLimitsHelper makes things a lot slower. self.ref_data_helper = ManualDataComboHelper(self, 'reference_data') self.xw_att_helper = ComponentIDComboHelper(self, 'x_att_world', numeric=False, datetime=False, categorical=False) self.yw_att_helper = ComponentIDComboHelper(self, 'y_att_world', numeric=False, datetime=False, categorical=False) self.add_callback('reference_data', self._reference_data_changed, priority=1000) self.add_callback('layers', self._layers_changed, priority=1000) self.add_callback('x_att', self._on_xatt_change, priority=500) self.add_callback('y_att', self._on_yatt_change, priority=500) self.add_callback('x_att_world', self._on_xatt_world_change, priority=1000) self.add_callback('y_att_world', self._on_yatt_world_change, priority=1000) aspect_display = {'equal': 'Square Pixels', 'auto': 'Automatic'} ImageViewerState.aspect.set_choices(self, ['equal', 'auto']) ImageViewerState.aspect.set_display_func(self, aspect_display.get) ImageViewerState.color_mode.set_choices(self, ['Colormaps', 'One color per layer']) self.update_from_dict(kwargs) def reset_limits(self): if self.reference_data is None or self.x_att is None or self.y_att is None: return nx = self.reference_data.shape[self.x_att.axis] ny = self.reference_data.shape[self.y_att.axis] with delay_callback(self, 'x_min', 'x_max', 'y_min', 'y_max'): self.x_min = -0.5 self.x_max = nx - 0.5 self.y_min = -0.5 self.y_max = ny - 0.5 # We need to adjust the limits in here to avoid triggering all # the update events then changing the limits again. self._adjust_limits_aspect() @property def _display_world(self): return getattr(self.reference_data, 'coords', None) is not None def _reference_data_changed(self, *args): # This signal can get emitted if just the choices but not the actual # reference data change, so we check here that the reference data has # actually changed if self.reference_data is not getattr(self, '_last_reference_data', None): self._last_reference_data = self.reference_data # Note that we deliberately use nested delay_callback here, because # we want to make sure that x_att_world and y_att_world both get # updated first, then x_att and y_att can be changed, before # subsequent events are fired. with delay_callback(self, 'x_att', 'y_att'): with delay_callback(self, 'x_att_world', 'y_att_world', 'slices'): if self._display_world: self.xw_att_helper.pixel_coord = False self.yw_att_helper.pixel_coord = False self.xw_att_helper.world_coord = True self.yw_att_helper.world_coord = True else: self.xw_att_helper.pixel_coord = True self.yw_att_helper.pixel_coord = True self.xw_att_helper.world_coord = False self.yw_att_helper.world_coord = False self._update_combo_att() self._set_default_slices() # We need to make sure that we update x_att and y_att # at the same time before any other callbacks get called, # so we do this here manually. self._on_xatt_world_change() self._on_yatt_world_change() def _layers_changed(self, *args): # The layers callback gets executed if anything in the layers changes, # but we only care about whether the actual set of 'layer' attributes # for all layers change. layers_data = self.layers_data layers_data_cache = getattr(self, '_layers_data_cache', []) if layers_data == layers_data_cache: return self._update_combo_ref_data() self._set_reference_data() self._update_syncing() self._layers_data_cache = layers_data def _update_syncing(self): # If there are multiple layers for a given dataset, we disable the # syncing by default. layer_state_by_data = defaultdict(list) for layer_state in self.layers: if isinstance(layer_state.layer, BaseData): layer_state_by_data[layer_state.layer].append(layer_state) for data, layer_states in layer_state_by_data.items(): if len(layer_states) > 1: for layer_state in layer_states: # Scatter layers don't have global_sync so we need to be # careful here and make sure we return a default value if getattr(layer_state, 'global_sync', False): layer_state.global_sync = False def _update_combo_ref_data(self): self.ref_data_helper.set_multiple_data(self.layers_data) def _update_combo_att(self): with delay_callback(self, 'x_att_world', 'y_att_world'): if self.reference_data is None: self.xw_att_helper.set_multiple_data([]) self.yw_att_helper.set_multiple_data([]) else: self.xw_att_helper.set_multiple_data([self.reference_data]) self.yw_att_helper.set_multiple_data([self.reference_data]) def _update_priority(self, name): if name == 'layers': return 3 elif name == 'reference_data': return 2 elif name.endswith(('_min', '_max')): return 0 else: return 1 @defer_draw def _on_xatt_change(self, *args): if self.x_att is not None: if self._display_world: self.x_att_world = self.reference_data.world_component_ids[self.x_att.axis] else: self.x_att_world = self.x_att @defer_draw def _on_yatt_change(self, *args): if self.y_att is not None: if self._display_world: self.y_att_world = self.reference_data.world_component_ids[self.y_att.axis] else: self.y_att_world = self.y_att @defer_draw def _on_xatt_world_change(self, *args): if self.x_att_world is not None: with delay_callback(self, 'y_att_world', 'x_att'): if self.x_att_world == self.y_att_world: if self._display_world: world_ids = self.reference_data.world_component_ids else: world_ids = self.reference_data.pixel_component_ids if self.x_att_world == world_ids[-1]: self.y_att_world = world_ids[-2] else: self.y_att_world = world_ids[-1] if self._display_world: index = self.reference_data.world_component_ids.index(self.x_att_world) self.x_att = self.reference_data.pixel_component_ids[index] else: self.x_att = self.x_att_world @defer_draw def _on_yatt_world_change(self, *args): if self.y_att_world is not None: with delay_callback(self, 'x_att_world', 'y_att'): if self.y_att_world == self.x_att_world: if self._display_world: world_ids = self.reference_data.world_component_ids else: world_ids = self.reference_data.pixel_component_ids if self.y_att_world == world_ids[-1]: self.x_att_world = world_ids[-2] else: self.x_att_world = world_ids[-1] if self._display_world: index = self.reference_data.world_component_ids.index(self.y_att_world) self.y_att = self.reference_data.pixel_component_ids[index] else: self.y_att = self.y_att_world def _set_reference_data(self): if self.reference_data is None: for layer in self.layers: if isinstance(layer.layer, BaseData): self.reference_data = layer.layer return def _set_default_slices(self): # Need to make sure this gets called immediately when reference_data is changed if self.reference_data is None: self.slices = () else: self.slices = (0,) * self.reference_data.ndim @property def numpy_slice_aggregation_transpose(self): """ Returns slicing information usable by Numpy. This returns two objects: the first is an object that can be used to slice Numpy arrays and return a 2D array, and the second object is a boolean indicating whether to transpose the result. """ if self.reference_data is None: return None slices = [] agg_func = [] for i in range(self.reference_data.ndim): if i == self.x_att.axis or i == self.y_att.axis: slices.append(slice(None)) agg_func.append(None) else: if isinstance(self.slices[i], AggregateSlice): slices.append(self.slices[i].slice) agg_func.append(self.slices[i].function) else: slices.append(self.slices[i]) transpose = self.y_att.axis > self.x_att.axis return slices, agg_func, transpose @property def wcsaxes_slice(self): """ Returns slicing information usable by WCSAxes. This returns an iterable of slices, and including ``'x'`` and ``'y'`` for the dimensions along which we are not slicing. """ if self.reference_data is None: return None slices = [] for i in range(self.reference_data.ndim): if i == self.x_att.axis: slices.append('x') elif i == self.y_att.axis: slices.append('y') else: if isinstance(self.slices[i], AggregateSlice): slices.append(self.slices[i].center) else: slices.append(self.slices[i]) return slices[::-1] def flip_x(self): """ Flip the x_min/x_max limits. """ with delay_callback(self, 'x_min', 'x_max'): self.x_min, self.x_max = self.x_max, self.x_min def flip_y(self): """ Flip the y_min/y_max limits. """ with delay_callback(self, 'y_min', 'y_max'): self.y_min, self.y_max = self.y_max, self.y_min class BaseImageLayerState(MatplotlibLayerState): _viewer_callbacks_set = False _image_cache = None _pixel_cache = None def get_sliced_data_shape(self, view=None): if (self.viewer_state.reference_data is None or self.viewer_state.x_att is None or self.viewer_state.y_att is None): return None x_axis = self.viewer_state.x_att.axis y_axis = self.viewer_state.y_att.axis shape = self.viewer_state.reference_data.shape shape_slice = shape[y_axis], shape[x_axis] if view is None: return shape_slice else: return view_shape(shape_slice, view) def get_sliced_data(self, view=None, bounds=None): full_view, agg_func, transpose = self.viewer_state.numpy_slice_aggregation_transpose x_axis = self.viewer_state.x_att.axis y_axis = self.viewer_state.y_att.axis # For this method, we make use of Data.compute_fixed_resolution_buffer, # which requires us to specify bounds in the form (min, max, nsteps). # We also allow view to be passed here (which is a normal Numpy view) # and, if given, translate it to bounds. If neither are specified, # we behave as if view was [slice(None), slice(None)]. def slice_to_bound(slc, size): min, max, step = slc.indices(size) n = (max - min - 1) // step max = min + step * n return (min, max, n + 1) if bounds is None: # The view should be that which should just be applied to the data # slice, not to all the dimensions of the data - thus it should have at # most two dimensions if view is None: view = [slice(None), slice(None)] elif len(view) == 1: view = view + [slice(None)] elif len(view) > 2: raise ValueError('view should have at most two elements') full_view[x_axis] = view[1] full_view[y_axis] = view[0] else: full_view[x_axis] = bounds[1] full_view[y_axis] = bounds[0] for i in range(self.viewer_state.reference_data.ndim): if isinstance(full_view[i], slice): full_view[i] = slice_to_bound(full_view[i], self.viewer_state.reference_data.shape[i]) # We now get the fixed resolution buffer if isinstance(self.layer, BaseData): image = self.layer.compute_fixed_resolution_buffer(full_view, target_data=self.viewer_state.reference_data, target_cid=self.attribute, broadcast=False, cache_id=self.uuid) else: image = self.layer.data.compute_fixed_resolution_buffer(full_view, target_data=self.viewer_state.reference_data, subset_state=self.layer.subset_state, broadcast=False, cache_id=self.uuid) # We apply aggregation functions if needed if agg_func is None: if image.ndim != 2: raise IncompatibleDataException() else: if image.ndim != len(agg_func): raise ValueError("Sliced image dimensions ({0}) does not match " "aggregation function list ({1})" .format(image.ndim, len(agg_func))) for axis in range(image.ndim - 1, -1, -1): func = agg_func[axis] if func is not None: image = func(image, axis=axis) if image.ndim != 2: raise ValueError("Image after aggregation should have two dimensions") # And finally we transpose the data if the order of x/y is different # from the native order. if transpose: image = image.transpose() return image class ImageLayerState(BaseImageLayerState): """ A state class that includes all the attributes for data layers in an image plot. """ attribute = DDSCProperty(docstring='The attribute shown in the layer') v_min = DDCProperty(docstring='The lower level shown') v_max = DDCProperty(docstring='The upper level shown') percentile = DDSCProperty(docstring='The percentile value used to ' 'automatically calculate levels') contrast = DDCProperty(1, docstring='The contrast of the layer') bias = DDCProperty(0.5, docstring='A constant value that is added to the ' 'layer before rendering') cmap = DDCProperty(docstring='The colormap used to render the layer') stretch = DDSCProperty(docstring='The stretch used to render the layer, ' 'which should be one of ``linear``, ' '``sqrt``, ``log``, or ``arcsinh``') global_sync = DDCProperty(False, docstring='Whether the color and transparency ' 'should be synced with the global ' 'color and transparency for the data') def __init__(self, layer=None, viewer_state=None, **kwargs): self.uuid = str(uuid.uuid4()) super(ImageLayerState, self).__init__(layer=layer, viewer_state=viewer_state) self.attribute_lim_helper = StateAttributeLimitsHelper(self, attribute='attribute', percentile='percentile', lower='v_min', upper='v_max') self.attribute_att_helper = ComponentIDComboHelper(self, 'attribute', numeric=True, categorical=False) percentile_display = {100: 'Min/Max', 99.5: '99.5%', 99: '99%', 95: '95%', 90: '90%', 'Custom': 'Custom'} ImageLayerState.percentile.set_choices(self, [100, 99.5, 99, 95, 90, 'Custom']) ImageLayerState.percentile.set_display_func(self, percentile_display.get) stretch_display = {'linear': 'Linear', 'sqrt': 'Square Root', 'arcsinh': 'Arcsinh', 'log': 'Logarithmic'} ImageLayerState.stretch.set_choices(self, ['linear', 'sqrt', 'arcsinh', 'log']) ImageLayerState.stretch.set_display_func(self, stretch_display.get) self.add_callback('global_sync', self._update_syncing) self.add_callback('layer', self._update_attribute) self._update_syncing() if layer is not None: self._update_attribute() self.update_from_dict(kwargs) if self.cmap is None: self.cmap = self.layer.style.preferred_cmap or colormaps.members[0][1] def _update_attribute(self, *args): if self.layer is not None: self.attribute_att_helper.set_multiple_data([self.layer]) self.attribute = self.layer.main_components[0] def _update_priority(self, name): if name == 'layer': return 3 elif name == 'attribute': return 2 elif name == 'global_sync': return 1.5 elif name.endswith(('_min', '_max')): return 0 else: return 1 def _update_syncing(self, *args): if self.global_sync: self._sync_color.enable_syncing() self._sync_alpha.enable_syncing() else: self._sync_color.disable_syncing() self._sync_alpha.disable_syncing() def _get_image(self, view=None): return self.layer[self.attribute, view] def flip_limits(self): """ Flip the image levels. """ self.attribute_lim_helper.flip_limits() def reset_contrast_bias(self): with delay_callback(self, 'contrast', 'bias'): self.contrast = 1 self.bias = 0.5 class ImageSubsetLayerState(BaseImageLayerState): """ A state class that includes all the attributes for subset layers in an image plot. """ # TODO: we can save memory by not showing subset multiple times for # different image datasets since the footprint should be the same. def __init__(self, *args, **kwargs): self.uuid = str(uuid.uuid4()) super(ImageSubsetLayerState, self).__init__(*args, **kwargs) def _get_image(self, view=None): return self.layer.to_mask(view=view) glueviz-1.0.1+dfsg.orig/glue/viewers/image/__init__.py0000644000175000017500000000000013502206677022215 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/image/compat.py0000644000175000017500000001140313605357235021753 0ustar noahfxnoahfximport uuid import numpy as np from glue.viewers.image.state import ImageLayerState, ImageSubsetLayerState from glue.viewers.scatter.state import ScatterLayerState STATE_CLASS = {} STATE_CLASS['ImageLayerArtist'] = ImageLayerState STATE_CLASS['ScatterLayerArtist'] = ScatterLayerState STATE_CLASS['SubsetImageLayerArtist'] = ImageSubsetLayerState class DS9Compat(object): @classmethod def __setgluestate__(cls, rec, context): result = cls() for k, v in rec.items(): setattr(result, k, v) return result def update_image_viewer_state(rec, context): """ Given viewer session information, make sure the session information is compatible with the current version of the viewers, and if not, update the session information in-place. """ if '_protocol' not in rec: # Note that files saved with protocol < 1 have bin settings saved per # layer but they were always restricted to be the same, so we can just # use the settings from the first layer rec['state'] = {} rec['state']['values'] = {} # TODO: could generalize this into a mapping properties = rec.pop('properties') viewer_state = rec['state']['values'] viewer_state['color_mode'] = 'st__Colormaps' viewer_state['reference_data'] = properties['data'] data = context.object(properties['data']) # TODO: add an id method to unserializer x_index = properties['slice'].index('x') y_index = properties['slice'].index('y') viewer_state['x_att_world'] = str(uuid.uuid4()) context.register_object(viewer_state['x_att_world'], data.world_component_ids[x_index]) viewer_state['y_att_world'] = str(uuid.uuid4()) context.register_object(viewer_state['y_att_world'], data.world_component_ids[y_index]) viewer_state['x_att'] = str(uuid.uuid4()) context.register_object(viewer_state['x_att'], data.pixel_component_ids[x_index]) viewer_state['y_att'] = str(uuid.uuid4()) context.register_object(viewer_state['y_att'], data.pixel_component_ids[y_index]) viewer_state['x_min'] = -0.5 viewer_state['x_max'] = data.shape[1] - 0.5 viewer_state['y_min'] = -0.5 viewer_state['y_max'] = data.shape[0] - 0.5 viewer_state['aspect'] = 'st__equal' # Slicing with cubes viewer_state['slices'] = [s if np.isreal(s) else 0 for s in properties['slice']] # RGB mode for layer in rec['layers'][:]: if layer['_type'].split('.')[-1] == 'RGBImageLayerArtist': for icolor, color in enumerate('rgb'): new_layer = {} new_layer['_type'] = 'glue.viewers.image.layer_artist.ImageLayerArtist' new_layer['layer'] = layer['layer'] new_layer['attribute'] = layer[color] new_layer['norm'] = layer[color + 'norm'] new_layer['zorder'] = layer['zorder'] new_layer['visible'] = layer['color_visible'][icolor] new_layer['color'] = color rec['layers'].append(new_layer) rec['layers'].remove(layer) viewer_state['color_mode'] = 'st__One color per layer' layer_states = [] for layer in rec['layers']: state_id = str(uuid.uuid4()) state_cls = STATE_CLASS[layer['_type'].split('.')[-1]] state = state_cls(layer=context.object(layer.pop('layer'))) for prop in ('visible', 'zorder'): value = layer.pop(prop) value = context.object(value) setattr(state, prop, value) if 'attribute' in layer: state.attribute = context.object(layer['attribute']) else: state.attribute = context.object(properties['attribute']) if 'norm' in layer: norm = context.object(layer['norm']) state.bias = norm.bias state.contrast = norm.contrast state.stretch = norm.stretch if norm.clip_hi is not None: state.percentile = norm.clip_hi else: if norm.vmax is not None: state.v_min = norm.vmin state.v_max = norm.vmax state.percentile = 'Custom' if 'color' in layer: state.global_sync = False state.color = layer['color'] context.register_object(state_id, state) layer['state'] = state_id layer_states.append(state) list_id = str(uuid.uuid4()) context.register_object(list_id, layer_states) rec['state']['values']['layers'] = list_id glueviz-1.0.1+dfsg.orig/glue/viewers/image/pixel_selection_subset_state.py0000644000175000017500000000444713605357235026455 0ustar noahfxnoahfximport numpy as np from glue.core.subset import SliceSubsetState from glue.core.exceptions import IncompatibleAttribute from glue.core.link_manager import pixel_cid_to_pixel_cid_matrix __all__ = ['PixelSubsetState'] class PixelSubsetState(SliceSubsetState): def copy(self): return PixelSubsetState(self.reference_data, self.slices) def to_array(self, data, att): try: return super(PixelSubsetState, self).to_array(data, att) except IncompatibleAttribute: if data is not self.reference_data: pix_coord_out = self._to_linked_pixel_coords(data) pix_coord_out = tuple([slice(None) if p is None else slice(p, p + 1) for p in pix_coord_out]) return data[att, pix_coord_out] raise IncompatibleAttribute() def _to_linked_pixel_coords(self, data): # Determine which pixel dimensions are being sliced over dimensions = [idim for idim, slc in enumerate(self.slices) if slc.start is not None] # Determine pixel to pixel correlation matrix matrix = pixel_cid_to_pixel_cid_matrix(self.reference_data, data) # Find pixel dimensions in 'data' that are correlated correlated_dims = np.nonzero(np.any(matrix[dimensions], axis=0))[0] # Check that if we do the operation backwards we just get the # original dimensions back check_dimensions = np.nonzero(np.any(matrix[:, correlated_dims], axis=1))[0] if np.array_equal(dimensions, check_dimensions): pix_coord_in = tuple([slice(0, 1) if slc.start is None else slc for slc in self.slices]) pix_coord_out = [] for idim, pix_cid in enumerate(data.pixel_component_ids): if idim in correlated_dims: coord = int(np.round(self.reference_data[pix_cid, pix_coord_in].ravel()[0])) else: coord = None pix_coord_out.append(coord) return pix_coord_out raise IncompatibleAttribute() def get_xy(self, data, dim1, dim2): pix_coord_out = self._to_linked_pixel_coords(data) if pix_coord_out[dim1] is None or pix_coord_out[dim2] is None: raise IncompatibleAttribute else: return pix_coord_out[dim1], pix_coord_out[dim2] glueviz-1.0.1+dfsg.orig/glue/viewers/image/layer_artist.py0000644000175000017500000003211413661230662023170 0ustar noahfxnoahfximport uuid import weakref import numpy as np import matplotlib.patches as mpatches import matplotlib.colors as mcolors from glue.utils import defer_draw, broadcast_to from glue.viewers.image.state import ImageLayerState, ImageSubsetLayerState from glue.viewers.image.python_export import python_export_image_layer, python_export_image_subset_layer from glue.viewers.image.pixel_selection_subset_state import PixelSubsetState from glue.viewers.matplotlib.layer_artist import MatplotlibLayerArtist from glue.core.exceptions import IncompatibleAttribute from glue.utils import color2rgb from glue.utils.matplotlib import ColormapPatchHandler from glue.core import BaseData, HubListener from glue.core.message import (ComponentsChangedMessage, ExternallyDerivableComponentsChangedMessage, PixelAlignedDataChangedMessage) from glue.viewers.image.frb_artist import imshow from glue.core.fixed_resolution_buffer import ARRAY_CACHE, PIXEL_CACHE class BaseImageLayerArtist(MatplotlibLayerArtist, HubListener): def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(BaseImageLayerArtist, self).__init__(axes, viewer_state, layer_state=layer_state, layer=layer) # Watch for changes in the viewer state which would require the # layers to be redrawn self._viewer_state.add_global_callback(self._update_image) self.state.add_global_callback(self._update_image) self.layer.hub.subscribe(self, ComponentsChangedMessage, handler=self.update, filter=self._is_data_object) self.layer.hub.subscribe(self, ExternallyDerivableComponentsChangedMessage, handler=self.update, filter=self._is_data_object) self.layer.hub.subscribe(self, PixelAlignedDataChangedMessage, handler=self.update, filter=self._is_data_object) def _is_data_object(self, message): if isinstance(self.layer, BaseData): return message.sender is self.layer else: return message.sender is self.layer.data def _update_image(self, force=False, **kwargs): raise NotImplementedError() class ImageLayerArtist(BaseImageLayerArtist): _layer_state_cls = ImageLayerState _python_exporter = python_export_image_layer def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(ImageLayerArtist, self).__init__(axes, viewer_state, layer_state=layer_state, layer=layer) # We use a custom object to deal with the compositing of images, and we # store it as a private attribute of the axes to make sure it is # accessible for all layer artists. self.uuid = str(uuid.uuid4()) self.composite = self.axes._composite self.composite.allocate(self.uuid) self.composite.set(self.uuid, array=self.get_image_data, shape=self.get_image_shape) self.composite_image = self.axes._composite_image @property def label(self): return "%s (%s)" % (self.layer.label, self.state.attribute.label) def get_layer_color(self): if self._viewer_state.color_mode == 'One color per layer': return self.state.color else: return self.state.cmap def get_handle_legend(self): if self.enabled and self.state.visible: # a fancier patch drawing the colormap color = self.get_layer_color() label = self.layer.label if isinstance(color, mcolors.Colormap): handle = mpatches.Patch(color=color(0.5)) # actual color of the handle is not important handler = ColormapPatchHandler(color) else: handle = mpatches.Patch(color=color, alpha=self.layer.style.alpha) handler = None # default handler is good enough return handle, label, handler else: return None, None, None def enable(self): if hasattr(self, 'composite_image'): self.composite_image.invalidate_cache() super(ImageLayerArtist, self).enable() def remove(self): super(ImageLayerArtist, self).remove() if self.uuid in self.composite: self.composite.deallocate(self.uuid) self.composite_image.invalidate_cache() ARRAY_CACHE.pop(self.state.uuid, None) PIXEL_CACHE.pop(self.state.uuid, None) self.uuid = None def get_image_shape(self): if self.uuid is None or self._viewer_state.reference_data is None or self._viewer_state.x_att is None or self._viewer_state.y_att is None: return None x_axis = self._viewer_state.x_att.axis y_axis = self._viewer_state.y_att.axis full_shape = self._viewer_state.reference_data.shape return full_shape[y_axis], full_shape[x_axis] def get_image_data(self, bounds=None): if self.uuid is None: return None try: image = self.state.get_sliced_data(bounds=bounds) except (IncompatibleAttribute, IndexError): # The following includes a call to self.clear() self.disable_invalid_attributes(self.state.attribute) return None else: self.enable() return image def _update_image_data(self): self.composite_image.invalidate_cache() self.redraw() @defer_draw def _update_visual_attributes(self): if not self.enabled: return if self._viewer_state.color_mode == 'Colormaps': color = self.state.cmap else: color = self.state.color self.composite.set(self.uuid, clim=(self.state.v_min, self.state.v_max), visible=self.state.visible, zorder=self.state.zorder, color=color, contrast=self.state.contrast, bias=self.state.bias, alpha=self.state.alpha, stretch=self.state.stretch) self.composite_image.invalidate_cache() self.redraw() @defer_draw def _update_image(self, force=False, **kwargs): if self.uuid is None or self.state.attribute is None or self.state.layer is None: return changed = set() if force else self.pop_changed_properties() if force or any(prop in changed for prop in ('layer', 'attribute', 'slices', 'x_att', 'y_att')): self._update_image_data() force = True # make sure scaling and visual attributes are updated if force or any(prop in changed for prop in ('v_min', 'v_max', 'contrast', 'bias', 'alpha', 'color_mode', 'cmap', 'color', 'zorder', 'visible', 'stretch')): self._update_visual_attributes() @defer_draw def update(self, *event): ARRAY_CACHE.pop(self.state.uuid, None) PIXEL_CACHE.pop(self.state.uuid, None) self._update_image(force=True) self.redraw() class ImageSubsetArray(object): def __init__(self, viewer_state, layer_artist): self._viewer_state = weakref.ref(viewer_state) self._layer_artist = weakref.ref(layer_artist) self._layer_state = weakref.ref(layer_artist.state) @property def layer_artist(self): return self._layer_artist() @property def layer_state(self): return self._layer_state() @property def viewer_state(self): return self._viewer_state() @property def shape(self): x_axis = self.viewer_state.x_att.axis y_axis = self.viewer_state.y_att.axis full_shape = self.viewer_state.reference_data.shape return full_shape[y_axis], full_shape[x_axis] def __call__(self, bounds): if (self.layer_artist is None or self.layer_state is None or self.viewer_state is None): return broadcast_to(np.nan, self.shape) # We should compute the mask even if the layer is not visible as we need # the layer to show up properly when it is made visible (which doesn't # trigger __getitem__) try: mask = self.layer_state.get_sliced_data(bounds=bounds) except IncompatibleAttribute: self.layer_artist.disable_incompatible_subset() return broadcast_to(np.nan, self.shape) else: self.layer_artist.enable(redraw=False) r, g, b = color2rgb(self.layer_state.color) mask = np.dstack((r * mask, g * mask, b * mask, mask * .5)) mask = (255 * mask).astype(np.uint8) return mask @property def dtype(self): return np.uint8 @property def ndim(self): return 2 @property def size(self): return np.product(self.shape) class ImageSubsetLayerArtist(BaseImageLayerArtist): _layer_state_cls = ImageSubsetLayerState _python_exporter = python_export_image_subset_layer def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(ImageSubsetLayerArtist, self).__init__(axes, viewer_state, layer_state=layer_state, layer=layer) self.subset_array = ImageSubsetArray(self._viewer_state, self) self.image_artist = imshow(self.axes, self.subset_array, origin='lower', interpolation='nearest', vmin=0, vmax=1, aspect='auto') self._line_x = self.axes.axvline(0) self._line_x.set_visible(False) self._line_y = self.axes.axhline(0) self._line_y.set_visible(False) self.mpl_artists = [self.image_artist, self._line_x, self._line_y] @defer_draw def _update_data(self): if isinstance(self.state.layer.subset_state, PixelSubsetState): try: x, y = self.state.layer.subset_state.get_xy(self.layer.data, self._viewer_state.x_att.axis, self._viewer_state.y_att.axis) except IncompatibleAttribute: self._line_x.set_visible(False) self._line_y.set_visible(False) else: self._line_x.set_data([x, x], [0, 1]) self._line_x.set_visible(True) self._line_y.set_data([0, 1], [y, y]) self._line_y.set_visible(True) else: self._line_x.set_visible(False) self._line_y.set_visible(False) self.image_artist.invalidate_cache() @defer_draw def _update_visual_attributes(self, redraw=True): if not self.enabled: return for artist in self.mpl_artists: if artist is self.image_artist: artist.set_visible(self.state.visible) artist.set_alpha(self.state.alpha) else: if self.state.visible: artist.set_visible(isinstance(self.state.layer.subset_state, PixelSubsetState)) else: artist.set_visible(False) artist.set_color(self.state.color) artist.set_alpha(self.state.alpha * 0.5) artist.set_zorder(self.state.zorder) if redraw: self.redraw() def _update_image(self, force=False, **kwargs): if self.state.layer is None: return changed = set() if force else self.pop_changed_properties() if force or any(prop in changed for prop in ('layer', 'attribute', 'color', 'x_att', 'y_att', 'slices')): self._update_data() force = True # make sure scaling and visual attributes are updated if force or any(prop in changed for prop in ('zorder', 'visible', 'alpha')): self._update_visual_attributes() def remove(self): super(ImageSubsetLayerArtist, self).remove() self.image_artist.invalidate_cache() ARRAY_CACHE.pop(self.state.uuid, None) PIXEL_CACHE.pop(self.state.uuid, None) def enable(self, redraw=True): super(ImageSubsetLayerArtist, self).enable() # We need to now ensure that image_artist, which may have been marked # as not being visible when the layer was cleared is made visible # again. if hasattr(self, 'image_artist'): self.image_artist.invalidate_cache() self._update_visual_attributes(redraw=redraw) @defer_draw def update(self, *event): ARRAY_CACHE.pop(self.state.uuid, None) PIXEL_CACHE.pop(self.state.uuid, None) self._update_image(force=True) self.redraw() glueviz-1.0.1+dfsg.orig/glue/viewers/image/composite_array.py0000644000175000017500000001170213605357235023672 0ustar noahfxnoahfx# This artist can be used to deal with the sampling of the data as well as any # RGB blending. import numpy as np from matplotlib.colors import ColorConverter, Colormap from astropy.visualization import (LinearStretch, SqrtStretch, AsinhStretch, LogStretch, ManualInterval, ContrastBiasStretch) __all__ = ['CompositeArray'] COLOR_CONVERTER = ColorConverter() STRETCHES = { 'linear': LinearStretch, 'sqrt': SqrtStretch, 'arcsinh': AsinhStretch, 'log': LogStretch } class CompositeArray(object): def __init__(self, **kwargs): # We keep a dictionary of layers. The key should be the UUID of the # layer artist, and the values should be dictionaries that contain # 'zorder', 'visible', 'array', 'color', and 'alpha'. self.layers = {} self._first = True def allocate(self, uuid): self.layers[uuid] = {'zorder': 0, 'visible': True, 'array': None, 'shape': None, 'color': '0.5', 'alpha': 1, 'clim': (0, 1), 'contrast': 1, 'bias': 0.5, 'stretch': 'linear'} def deallocate(self, uuid): self.layers.pop(uuid) def set(self, uuid, **kwargs): for key, value in kwargs.items(): if key not in self.layers[uuid]: raise KeyError("Unknown key: {0}".format(key)) else: self.layers[uuid][key] = value @property def shape(self): for layer in self.layers.values(): if callable(layer['shape']): shape = layer['shape']() elif layer['shape'] is not None: shape = layer['shape'] elif callable(layer['array']): array = layer['array']() if array is None: return None else: shape = array.shape else: shape = layer['array'].shape if shape is not None: return shape return None def __getitem__(self, item): return self()[item] def __call__(self, bounds=None): img = None visible_layers = 0 for uuid in sorted(self.layers, key=lambda x: self.layers[x]['zorder']): layer = self.layers[uuid] if not layer['visible']: continue interval = ManualInterval(*layer['clim']) contrast_bias = ContrastBiasStretch(layer['contrast'], layer['bias']) if callable(layer['array']): array = layer['array'](bounds=bounds) else: array = layer['array'] if array is None: continue if np.isscalar(array): scalar = True array = np.atleast_2d(array) else: scalar = False data = STRETCHES[layer['stretch']]()(contrast_bias(interval(array))) data[np.isnan(data)] = 0 if isinstance(layer['color'], Colormap): if img is None: img = np.ones(data.shape + (4,)) # Compute colormapped image plane = layer['color'](data) alpha_plane = layer['alpha'] * plane[:, :, 3] # Use traditional alpha compositing plane[:, :, 0] = plane[:, :, 0] * alpha_plane plane[:, :, 1] = plane[:, :, 1] * alpha_plane plane[:, :, 2] = plane[:, :, 2] * alpha_plane img[:, :, 0] *= (1 - alpha_plane) img[:, :, 1] *= (1 - alpha_plane) img[:, :, 2] *= (1 - alpha_plane) img[:, :, 3] = 1 else: if img is None: img = np.zeros(data.shape + (4,)) # Get color and pre-multiply by alpha values color = COLOR_CONVERTER.to_rgba_array(layer['color'])[0] color *= layer['alpha'] # We should treat NaN values as zero (post-stretch), which means # that those pixels don't contribute towards the final image. reset = np.isnan(data) if np.any(reset): data[reset] = 0. plane = data[:, :, np.newaxis] * color plane[:, :, 3] = 1 visible_layers += 1 if scalar: plane = plane[0, 0] img += plane if img is None: return None else: img = np.clip(img, 0, 1) return img @property def dtype(self): return np.float @property def ndim(self): return 2 @property def size(self): return np.product(self.shape) def __contains__(self, item): return item in self.layers glueviz-1.0.1+dfsg.orig/glue/viewers/image/viewer.py0000644000175000017500000002252313661230662021772 0ustar noahfxnoahfximport os from astropy.wcs import WCS from glue.core.subset import roi_to_subset_state from glue.core.coordinates import Coordinates, LegacyCoordinates from glue.core.coordinate_helpers import dependent_axes from glue.viewers.scatter.layer_artist import ScatterLayerArtist from glue.viewers.image.layer_artist import ImageLayerArtist, ImageSubsetLayerArtist from glue.viewers.image.compat import update_image_viewer_state from glue.viewers.image.frb_artist import imshow from glue.viewers.image.composite_array import CompositeArray __all__ = ['MatplotlibImageMixin'] def get_identity_wcs(naxis): wcs = WCS(naxis=naxis) wcs.wcs.ctype = ['X'] * naxis wcs.wcs.crval = [0.] * naxis wcs.wcs.crpix = [1.] * naxis wcs.wcs.cdelt = [1.] * naxis return wcs EXTRA_FOOTER = """ # Set tick label size - for now tick_params (called lower down) doesn't work # properly, but these lines won't be needed in future. ax.coords[{x_att_axis}].set_ticklabel(size={x_ticklabel_size}) ax.coords[{y_att_axis}].set_ticklabel(size={y_ticklabel_size}) """.strip() class MatplotlibImageMixin(object): def setup_callbacks(self): self._wcs_set = False self._changing_slice_requires_wcs_update = None self.axes.set_adjustable('datalim') self.state.add_callback('x_att', self._set_wcs) self.state.add_callback('y_att', self._set_wcs) self.state.add_callback('slices', self._on_slice_change) self.state.add_callback('reference_data', self._set_wcs) self.axes._composite = CompositeArray() self.axes._composite_image = imshow(self.axes, self.axes._composite, aspect='auto', origin='lower', interpolation='nearest') self._set_wcs() def update_x_ticklabel(self, *event): # We need to overload this here for WCSAxes if hasattr(self, '_wcs_set') and self._wcs_set and self.state.x_att is not None: axis = self.state.reference_data.ndim - self.state.x_att.axis - 1 else: axis = 0 self.axes.coords[axis].set_ticklabel(size=self.state.x_ticklabel_size) self.redraw() def update_y_ticklabel(self, *event): # We need to overload this here for WCSAxes if hasattr(self, '_wcs_set') and self._wcs_set and self.state.y_att is not None: axis = self.state.reference_data.ndim - self.state.y_att.axis - 1 else: axis = 1 self.axes.coords[axis].set_ticklabel(size=self.state.y_ticklabel_size) self.redraw() def _update_axes(self, *args): if self.state.x_att_world is not None: self.state.x_axislabel = self.state.x_att_world.label if self.state.y_att_world is not None: self.state.y_axislabel = self.state.y_att_world.label self.axes.figure.canvas.draw_idle() def add_data(self, data): result = super(MatplotlibImageMixin, self).add_data(data) # If this is the first layer (or the first after all layers were) # removed, set the WCS for the axes. if len(self.layers) == 1: self._set_wcs() return result def _on_slice_change(self, event=None): if self._changing_slice_requires_wcs_update: self._set_wcs(event=event, relim=False) def _set_wcs(self, event=None, relim=True): if self.state.x_att is None or self.state.y_att is None or self.state.reference_data is None: return ref_coords = getattr(self.state.reference_data, 'coords', None) if ref_coords is None or isinstance(ref_coords, LegacyCoordinates): self.axes.reset_wcs(slices=self.state.wcsaxes_slice, wcs=get_identity_wcs(self.state.reference_data.ndim)) else: self.axes.reset_wcs(slices=self.state.wcsaxes_slice, wcs=ref_coords) # Reset the axis labels to match the fact that the new axes have no labels self.state.x_axislabel = '' self.state.y_axislabel = '' self._update_appearance_from_settings() self._update_axes() self.update_x_ticklabel() self.update_y_ticklabel() if relim: self.state.reset_limits() # Determine whether changing slices requires changing the WCS if ref_coords is None or type(ref_coords) == Coordinates: self._changing_slice_requires_wcs_update = False else: ix = self.state.x_att.axis iy = self.state.y_att.axis x_dep = list(dependent_axes(ref_coords, ix)) y_dep = list(dependent_axes(ref_coords, iy)) if ix in x_dep: x_dep.remove(ix) if iy in x_dep: x_dep.remove(iy) if ix in y_dep: y_dep.remove(ix) if iy in y_dep: y_dep.remove(iy) self._changing_slice_requires_wcs_update = bool(x_dep or y_dep) self._wcs_set = True def apply_roi(self, roi, override_mode=None): # Force redraw to get rid of ROI. We do this because applying the # subset state below might end up not having an effect on the viewer, # for example there may not be any layers, or the active subset may not # be one of the layers. So we just explicitly redraw here to make sure # a redraw will happen after this method is called. self.redraw() if len(self.layers) == 0: return if self.state.x_att is None or self.state.y_att is None or self.state.reference_data is None: return subset_state = roi_to_subset_state(roi, x_att=self.state.x_att, y_att=self.state.y_att) self.apply_subset_state(subset_state, override_mode=override_mode) def _scatter_artist(self, axes, state, layer=None, layer_state=None): if len(self._layer_artist_container) == 0: raise Exception("Can only add a scatter plot overlay once an image is present") return ScatterLayerArtist(axes, state, layer=layer, layer_state=None) def get_data_layer_artist(self, layer=None, layer_state=None): if layer.ndim == 1: cls = self._scatter_artist else: cls = ImageLayerArtist return self.get_layer_artist(cls, layer=layer, layer_state=layer_state) def get_subset_layer_artist(self, layer=None, layer_state=None): if layer.ndim == 1: cls = self._scatter_artist else: cls = ImageSubsetLayerArtist return self.get_layer_artist(cls, layer=layer, layer_state=layer_state) @staticmethod def update_viewer_state(rec, context): return update_image_viewer_state(rec, context) def show_crosshairs(self, x, y): if getattr(self, '_crosshairs', None) is not None: self._crosshairs.remove() self._crosshairs, = self.axes.plot([x], [y], '+', ms=12, mfc='none', mec='#d32d26', mew=1, zorder=100) self.axes.figure.canvas.draw_idle() def hide_crosshairs(self): if getattr(self, '_crosshairs', None) is not None: self._crosshairs.remove() self._crosshairs = None self.axes.figure.canvas.draw_idle() def _script_header(self): imports = [] imports.append('import matplotlib.pyplot as plt') imports.append('from glue.viewers.matplotlib.mpl_axes import init_mpl') imports.append('from glue.viewers.image.composite_array import CompositeArray') imports.append('from glue.viewers.image.frb_artist import imshow') script = "" script += "fig, ax = init_mpl(wcs=True)\n" script += "ax.set_aspect('{0}')\n".format(self.state.aspect) script += '\ncomposite = CompositeArray()\n' script += "image = imshow(ax, composite, origin='lower', interpolation='nearest', aspect='{0}')\n\n".format(self.state.aspect) dindex = self.session.data_collection.index(self.state.reference_data) script += "ref_data = data_collection[{0}]\n".format(dindex) ref_coords = self.state.reference_data.coords if hasattr(ref_coords, 'wcs'): script += "ax.reset_wcs(slices={0}, wcs=ref_data.coords.wcs)\n".format(self.state.wcsaxes_slice) elif hasattr(ref_coords, 'wcsaxes_dict'): raise NotImplementedError() else: imports.append('from glue.viewers.image.viewer import get_identity_wcs') script += "ax.reset_wcs(slices={0}, wcs=get_identity_wcs(ref_data.ndim))\n".format(self.state.wcsaxes_slice) script += "# for the legend\n" script += "legend_handles = []\n" script += "legend_labels = []\n" script += "legend_handler_dict = dict()\n\n" return imports, script def _script_footer(self): imports, script = super(MatplotlibImageMixin, self)._script_footer() options = dict(x_att_axis=0 if self.state.x_att is None else self.state.reference_data.ndim - self.state.x_att.axis - 1, y_att_axis=1 if self.state.y_att is None else self.state.reference_data.ndim - self.state.y_att.axis - 1, x_ticklabel_size=self.state.x_ticklabel_size, y_ticklabel_size=self.state.y_ticklabel_size) return [], EXTRA_FOOTER.format(**options) + os.linesep * 2 + script glueviz-1.0.1+dfsg.orig/glue/viewers/__init__.py0000644000175000017500000000000013455362716021137 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/common/0000755000175000017500000000000013752535025020323 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/common/tests/0000755000175000017500000000000013752535025021465 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/common/tests/test_viewer.py0000644000175000017500000000270313502206677024402 0ustar noahfxnoahfxfrom glue.config import layer_artist_maker from glue.core.application_base import Application from glue.core import Data from glue.viewers.common.viewer import Viewer from glue.viewers.common.layer_artist import LayerArtist def test_custom_layer_artist_maker(): # This is a test of the infrastructure that allows users/developers to # define custom layer artist makers - i.e. functions that given a dataset # and a viewer can decide whether to create a custom layer artist rather # than the default types available for a given viewer. class CustomLayerArtist(LayerArtist): pass class CustomApplication(Application): def add_widget(self, *args, **kwargs): pass @layer_artist_maker('custom_maker') def custom_maker(viewer, data): if hasattr(data, 'custom'): return CustomLayerArtist(viewer.state, layer=data) app = CustomApplication() data1 = Data(x=[1, 2, 3], label='test1') data2 = Data(x=[1, 2, 3], label='test2') data2.custom = True app.data_collection.append(data1) app.data_collection.append(data2) viewer = app.new_data_viewer(Viewer) assert len(viewer.layers) == 0 # NOTE: Check exact type, not using isinstance viewer.add_data(data1) assert len(viewer.layers) == 1 assert type(viewer.layers[0]) is LayerArtist viewer.add_data(data2) assert len(viewer.layers) == 2 assert type(viewer.layers[1]) is CustomLayerArtist glueviz-1.0.1+dfsg.orig/glue/viewers/common/tests/__init__.py0000644000175000017500000000000013455362716023571 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/common/tests/test_utils.py0000644000175000017500000000230513507416115024232 0ustar noahfxnoahfxfrom glue.viewers.common.viewer import Viewer from glue.viewers.common.utils import get_viewer_tools class ViewerWithTools(Viewer): inherit_tools = True tools = ['save'] subtools = {'save': []} def test_get_viewer_tools(): class CustomViewer1(ViewerWithTools): pass tools, subtools = get_viewer_tools(CustomViewer1) assert tools == ['save'] assert subtools == {'save': []} class CustomViewer2(ViewerWithTools): tools = ['banana'] subtools = {'save': ['apple', 'pear']} tools, subtools = get_viewer_tools(CustomViewer2) assert tools == ['save', 'banana'] assert subtools == {'save': ['apple', 'pear']} CustomViewer2.inherit_tools = False tools, subtools = get_viewer_tools(CustomViewer2) assert tools == ['banana'] assert subtools == {'save': ['apple', 'pear']} class Mixin(object): pass class CustomViewer3(CustomViewer2, Mixin): tools = ['orange'] subtools = {'banana': ['one', 'two']} inherit_tools = True tools, subtools = get_viewer_tools(CustomViewer3) assert tools == ['banana', 'orange'] assert subtools == {'save': ['apple', 'pear'], 'banana': ['one', 'two']} glueviz-1.0.1+dfsg.orig/glue/viewers/common/utils.py0000644000175000017500000000214113605357235022035 0ustar noahfxnoahfx__all__ = ['get_viewer_tools'] def get_viewer_tools(cls, tools=None, subtools=None): """ Given a viewer class, find all the tools and subtools to include in the viewer. Parameters ---------- cls : type The viewer class for which to look for tools. tools : list The list to add the tools to - this is modified in-place. subtools : dict The dictionary to add the subtools to - this is modified in-place. """ if not hasattr(cls, 'inherit_tools'): return if tools is None: tools = [] if subtools is None: subtools = {} if cls.inherit_tools: for parent_cls in cls.__bases__: get_viewer_tools(parent_cls, tools, subtools) for tool_id in cls.tools: if tool_id not in tools: tools.append(tool_id) for tool_id in cls.subtools: if tool_id not in subtools: subtools[tool_id] = [] for subtool_id in cls.subtools[tool_id]: if subtool_id not in subtools[tool_id]: subtools[tool_id].append(subtool_id) return tools, subtools glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/0000755000175000017500000000000013752535025020747 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/tests/0000755000175000017500000000000013752535025022111 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/tests/test_toolbar.py0000644000175000017500000000207413605357235025171 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import pytest from glue.config import viewer_tool from glue.viewers.common.qt.data_viewer import DataViewer from glue.viewers.common.tool import Tool from glue.viewers.common.qt.toolbar import BasicToolbar from glue.core.tests.util import simple_session @viewer_tool class ExampleTool1(Tool): tool_id = 'TEST1' tool_tip = 'tes1' icon = 'glue_square' shortcut = 'A' @viewer_tool class ExampleTool2(Tool): tool_id = 'TEST2' tool_tip = 'tes2' icon = 'glue_square' shortcut = 'A' class ExampleViewer2(DataViewer): _toolbar_cls = BasicToolbar tools = ['TEST1', 'TEST2'] def __init__(self, session, parent=None): super(ExampleViewer2, self).__init__(session, parent=parent) def test_duplicate_shortcut(): session = simple_session() expected_warning = ("Tools 'TEST1' and 'TEST2' have the same " r"shortcut \('A'\). Ignoring shortcut for 'TEST2'") with pytest.warns(UserWarning, match=expected_warning): ExampleViewer2(session) glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/tests/__init__.py0000644000175000017500000000000013455362716024215 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/tests/test_data_viewer.py0000644000175000017500000000602413605357235026020 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import numpy as np from unittest.mock import MagicMock, patch from glue.core import Data, DataCollection from glue.app.qt import GlueApplication from glue.core.tests.util import simple_session from glue.viewers.histogram.qt import HistogramViewer from glue.viewers.image.qt import ImageViewer from glue.viewers.scatter.qt import ScatterViewer from glue.utils.qt import process_events # TODO: We should maybe consider running these tests for all # registered Qt viewers. class BaseTestDataViewer(object): ndim = 1 def test_unregister_on_close(self): session = simple_session() hub = session.hub w = self.widget_cls(session) w.register_to_hub(hub) with patch.object(w, 'unregister') as unregister: w.close() unregister.assert_called_once_with(hub) def test_single_draw_call_on_create(self): d = Data(x=np.random.random((2,) * self.ndim)) dc = DataCollection([d]) app = GlueApplication(dc) try: from glue.viewers.matplotlib.qt.widget import MplCanvas draw = MplCanvas.draw MplCanvas.draw = MagicMock() app.new_data_viewer(self.widget_cls, data=d) # each Canvas instance gives at most 1 draw call selfs = [c[0][0] for c in MplCanvas.draw.call_arg_list] assert len(set(selfs)) == len(selfs) finally: MplCanvas.draw = draw app.close() def test_close_on_last_layer_remove(self): # regression test for 391 d1 = Data(x=np.random.random((2,) * self.ndim)) d2 = Data(y=np.random.random((2,) * self.ndim)) dc = DataCollection([d1, d2]) app = GlueApplication(dc) w = app.new_data_viewer(self.widget_cls, data=d1) w.add_data(d2) process_events() assert len(app.viewers[0]) == 1 dc.remove(d1) process_events() assert len(app.viewers[0]) == 1 dc.remove(d2) process_events() assert len(app.viewers[0]) == 0 app.close() def test_viewer_size(self, tmpdir): # regression test for #781 # viewers were not restored with the right size d1 = Data(x=np.random.random((2,) * self.ndim)) d2 = Data(x=np.random.random((2,) * self.ndim)) dc = DataCollection([d1, d2]) app = GlueApplication(dc) w = app.new_data_viewer(self.widget_cls, data=d1) w.viewer_size = (300, 400) filename = tmpdir.join('session.glu').strpath app.save_session(filename, include_data=True) app2 = GlueApplication.restore_session(filename) for viewer in app2.viewers: assert viewer[0].viewer_size == (300, 400) app.close() app2.close() class TestDataViewerScatter(BaseTestDataViewer): widget_cls = ScatterViewer class TestDataViewerImage(BaseTestDataViewer): ndim = 2 widget_cls = ImageViewer class TestDataViewerHistogram(BaseTestDataViewer): widget_cls = HistogramViewer glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/tests/test_data_slice_widget.py0000644000175000017500000000405713605357235027165 0ustar noahfxnoahfxfrom ..data_slice_widget import SliceWidget class TestSliceWidget(object): def test_slice_center(self): s = SliceWidget(lo=0, hi=10) assert s.state.slice_center == 5 def test_browse_slice(self): s = SliceWidget(lo=0, hi=10) assert s.state.slice_center == 5 s.button_prev.click() assert s.state.slice_center == 4 s.button_next.click() s.button_next.click() assert s.state.slice_center == 6 s.button_first.click() assert s.state.slice_center == 0 s.button_prev.click() assert s.state.slice_center == 10 s.button_next.click() assert s.state.slice_center == 0 s.button_last.click() assert s.state.slice_center == 10 s.button_next.click() assert s.state.slice_center == 0 s.button_prev.click() assert s.state.slice_center == 10 s.button_prev.click() assert s.state.slice_center == 9 def test_slice_world(self): s = SliceWidget(lo=0, hi=5, world=[1, 3, 5, 5.5, 8, 12]) # Check switching between world and pixel coordinates s.state.slice_center = 0 assert s.state.slider_label == '1.0' s.state.use_world = False assert s.state.slider_label == '0' s.state.slice_center = 3 assert s.state.slider_label == '3' s.state.use_world = True assert s.state.slider_label == '5.5' # Round to nearest s.state.slider_label = '11' assert s.state.slice_center == 5 assert s.state.slider_label == '12.0' # Make sure out of bound values work s.state.slider_label = '20' assert s.state.slice_center == 5 assert s.state.slider_label == '12.0' s.state.slider_label = '-10' assert s.state.slice_center == 0 assert s.state.slider_label == '1.0' # And disable world and try and set by pixel s.state.use_world = False s.state.slider_label = '4' assert s.state.slice_center == 4 assert s.state.slider_label == '4' glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/toolbar.py0000644000175000017500000001605613637163236022776 0ustar noahfxnoahfximport os import warnings from qtpy import QtCore, QtGui, QtWidgets from qtpy.QtCore import Qt from glue.core.callback_property import add_callback from glue.viewers.common.tool import CheckableTool, DropdownTool from glue.icons.qt import get_icon __all__ = ['BasicToolbar'] class BasicToolbar(QtWidgets.QToolBar): tool_activated = QtCore.Signal() tool_deactivated = QtCore.Signal() def __init__(self, parent, default_mouse_mode_cls=None): """ Create a new toolbar object """ super(BasicToolbar, self).__init__(parent=parent) self.actions = {} self.tools = {} self.setIconSize(QtCore.QSize(25, 25)) self.layout().setSpacing(1) self.setFocusPolicy(Qt.StrongFocus) self._active_tool = None self._default_mouse_mode_cls = default_mouse_mode_cls self._default_mouse_mode = None self.setup_default_modes() def setup_default_modes(self): if self._default_mouse_mode_cls is not None: self._default_mouse_mode = self._default_mouse_mode_cls(self.parent()) self._default_mouse_mode.activate() @property def active_tool(self): return self._active_tool @active_tool.setter def active_tool(self, new_tool): if isinstance(new_tool, str): if new_tool in self.tools: new_tool = self.tools[new_tool] else: raise ValueError("Unrecognized tool '{0}', should be one of {1}" .format(new_tool, ", ".join(sorted(self.tools)))) old_tool = self._active_tool # If the tool is as before, we don't need to do anything if old_tool is new_tool: return # Otheriwse, if the tool changes, then we need to disable the previous # tool... if old_tool is not None: self.deactivate_tool(old_tool) if isinstance(old_tool, CheckableTool): button = self.actions[old_tool.tool_id] if button.isChecked(): button.blockSignals(True) button.setChecked(False) button.blockSignals(False) # We need to then set that no tool is set so that if the next tool # opens a viewer that needs to check whether a tool is active, we # know that it isn't. self._active_tool = None # ... and enable the new one if new_tool is not None: self.activate_tool(new_tool) if isinstance(new_tool, CheckableTool): button = self.actions[new_tool.tool_id] if not button.isChecked(): button.blockSignals(True) button.setChecked(True) button.blockSignals(False) if isinstance(new_tool, CheckableTool): self._active_tool = new_tool self.parent().set_status(new_tool.status_tip) self.tool_activated.emit() else: self.parent().set_status('') self.tool_deactivated.emit() def activate_tool(self, tool): if isinstance(tool, CheckableTool) and self._default_mouse_mode is not None: self._default_mouse_mode.deactivate() tool.activate() def deactivate_tool(self, tool): if isinstance(tool, CheckableTool): tool.deactivate() if self._default_mouse_mode is not None: self._default_mouse_mode.activate() def _make_action(self, tool, menu=None): parent = QtWidgets.QToolBar.parent(self) if isinstance(tool.icon, str): if os.path.exists(tool.icon): icon = QtGui.QIcon(tool.icon) else: icon = get_icon(tool.icon) else: icon = tool.icon if isinstance(tool, DropdownTool): # We use a QToolButton here explicitly so that we can make sure # that the whole button opens the pop-up. button = QtWidgets.QToolButton() if tool.action_text: button.setText(tool.action_text) if icon: button.setIcon(icon) button.setPopupMode(button.InstantPopup) button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) action = self.addWidget(button) if menu: button.setMenu(menu) button.clicked.connect(button.showMenu) else: if icon: action = QtWidgets.QAction(icon, tool.action_text, parent) else: action = QtWidgets.QAction(tool.action_text, parent) def toggle(checked): if checked: self.active_tool = tool else: self.active_tool = None def trigger(): self.active_tool = tool if isinstance(tool, CheckableTool): action.toggled.connect(toggle) else: action.triggered.connect(trigger) shortcut = None if tool.shortcut is not None: # Make sure that the keyboard shortcut is unique for m in self.tools.values(): if tool.shortcut == m.shortcut: warnings.warn("Tools '{0}' and '{1}' have the same shortcut " "('{2}'). Ignoring shortcut for " "'{1}'".format(m.tool_id, tool.tool_id, tool.shortcut)) break else: shortcut = tool.shortcut action.setShortcut(tool.shortcut) action.setShortcutContext(Qt.WidgetShortcut) if shortcut is None: action.setToolTip(tool.tool_tip) else: action.setToolTip(tool.tool_tip + " [shortcut: {0}]".format(shortcut)) action.setCheckable(isinstance(tool, CheckableTool)) return action def add_tool(self, tool): if isinstance(tool, DropdownTool) and len(tool.subtools) > 0: menu = QtWidgets.QMenu(self) for t in tool.subtools: action = self._make_action(t) menu.addAction(action) elif len(tool.menu_actions()) > 0: menu = QtWidgets.QMenu(self) for ma in tool.menu_actions(): ma.setParent(self) menu.addAction(ma) else: menu = None action = self._make_action(tool, menu=menu) self.addAction(action) self.actions[tool.tool_id] = action # Bind tool visibility to tool.enabled def toggle(state): action.setVisible(state) action.setEnabled(state) add_callback(tool, 'enabled', toggle) self.tools[tool.tool_id] = tool return action def cleanup(self): # We need to make sure we set _default_mouse_mode to None otherwise # we keep a reference to the viewer (parent) inside the mouse mode, # creating a circular reference. if self._default_mouse_mode is not None: self._default_mouse_mode.deactivate() self._default_mouse_mode = None self.active_tool = None glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/data_viewer.py0000644000175000017500000001561113605360406023613 0ustar noahfxnoahfxfrom qtpy.QtCore import Qt from qtpy import QtWidgets from glue.core.qt.layer_artist_model import QtLayerArtistContainer, LayerArtistWidget from glue.utils.qt import set_cursor, messagebox_on_error from glue.core.qt.dialogs import warn from glue.utils.noconflict import classmaker from glue.config import viewer_tool from glue.viewers.common.qt.base_widget import BaseQtViewerWidget from glue.viewers.common.tool import SimpleToolMenu from glue.viewers.common.qt.toolbar import BasicToolbar from glue.viewers.common.viewer import Viewer from glue.viewers.common.utils import get_viewer_tools __all__ = ['DataViewer', 'get_viewer_tools'] class ToolbarInitializer(object): """ This is a meta-class which ensures that initialize_toolbar is always called on DataViewer instances and sub-class instances after all the __init__ code has been executed. We need to do this, because often the toolbar can only be initialized after everything else (e.g. canvas, etc.) has been set up, so we can't do it in DataViewer.__init__. """ def __call__(cls, *args, **kwargs): obj = type.__call__(cls, *args, **kwargs) obj.initialize_toolbar() return obj @viewer_tool class SaveTool(SimpleToolMenu): """ A generic 'save/export' tool that plugins can register new save/export tools with. To register a new save/export option, add an entry to the viewer ``subtools['save']`` list. """ tool_id = 'save' icon = 'glue_filesave' tool_tip = 'Save/export the plot' # Note: we need to use classmaker here because otherwise we run into issues when # trying to use the meta-class with the Qt class. class DataViewer(Viewer, BaseQtViewerWidget, metaclass=classmaker(left_metas=(ToolbarInitializer,))): """ Base class for all Qt DataViewer widgets. This defines a minimal interface, and implements the following:: * An automatic call to unregister on window close * Drag and drop support for adding data """ _layer_artist_container_cls = QtLayerArtistContainer _layer_style_widget_cls = None _toolbar_cls = BasicToolbar # This defines the mouse mode to be used when no toolbar modes are active _default_mouse_mode_cls = None inherit_tools = True tools = ['save'] subtools = {'save': []} _close_on_last_layer_removed = True _options_cls = None large_data_size = None def __init__(self, session, state=None, parent=None): """ :type session: :class:`~glue.core.session.Session` """ BaseQtViewerWidget.__init__(self, parent) Viewer.__init__(self, session, state=state) self._view = LayerArtistWidget(layer_style_widget_cls=self._layer_style_widget_cls, hub=session.hub) self._view.layer_list.setModel(self._layer_artist_container.model) # Set up the options widget, which will include options that control the # viewer state if self._options_cls is None: self.options = QtWidgets.QWidget() else: self.options = self._options_cls(viewer_state=self.state, session=session) self._tb_vis = {} # store whether toolbars are enabled self.toolbar = None self._toolbars = [] self._warn_close = True # close window when last plot layer deleted if self._close_on_last_layer_removed: self._layer_artist_container.on_empty(self._close_nowarn) self._layer_artist_container.on_changed(self.update_window_title) self.update_window_title() @property def selected_layer(self): return self._view.layer_list.current_artist() @set_cursor(Qt.WaitCursor) def apply_roi(self, roi): pass def warn(self, message, *args, **kwargs): return warn(message, *args, **kwargs) def _close_nowarn(self): return self.close(warn=False) def closeEvent(self, event): super(DataViewer, self).closeEvent(event) Viewer.cleanup(self) # We tell the toolbar to do cleanup to make sure we get rid of any # circular references if self.toolbar: self.toolbar.cleanup() def layer_view(self): return self._view def addToolBar(self, tb): super(DataViewer, self).addToolBar(tb) self._toolbars.append(tb) self._tb_vis[tb] = True def initialize_toolbar(self): from glue.config import viewer_tool self.toolbar = self._toolbar_cls(self, default_mouse_mode_cls=self._default_mouse_mode_cls) # Need to include tools and subtools declared by parent classes unless # specified otherwise tool_ids, subtool_ids = get_viewer_tools(self.__class__) for tool_id in tool_ids: mode_cls = viewer_tool.members[tool_id] if tool_id in subtool_ids: subtools = [] for subtool_id in subtool_ids[tool_id]: subtools.append(viewer_tool.members[subtool_id](self)) mode = mode_cls(self, subtools=subtools) else: mode = mode_cls(self) self.toolbar.add_tool(mode) self.addToolBar(self.toolbar) self.toolbar_added.emit() def show_toolbars(self): """ Re-enable any toolbars that were hidden with `hide_toolbars()` Does not re-enable toolbars that were hidden by other means """ for tb in self._toolbars: if self._tb_vis.get(tb, False): tb.setEnabled(True) def hide_toolbars(self): """ Disable all the toolbars in the viewer. This action can be reversed by calling `show_toolbars()` """ for tb in self._toolbars: self._tb_vis[tb] = self._tb_vis.get(tb, False) or tb.isVisible() tb.setEnabled(False) def set_focus(self, state): super(DataViewer, self).set_focus(state) if state: self.show_toolbars() else: self.hide_toolbars() def __gluestate__(self, context): state = Viewer.__gluestate__(self, context) state['size'] = self.viewer_size state['pos'] = self.position state['_protocol'] = 1 return state def update_viewer_state(rec, context): pass @classmethod def __setgluestate__(cls, rec, context): if rec.get('_protocol', 0) < 1: cls.update_viewer_state(rec, context) viewer = super(DataViewer, cls).__setgluestate__(rec, context) viewer.viewer_size = rec['size'] x, y = rec['pos'] viewer.move(x=x, y=y) return viewer @messagebox_on_error("Failed to add data") def add_data(self, data): return super(DataViewer, self).add_data(data) @messagebox_on_error("Failed to add subset") def add_subset(self, subset): return super(DataViewer, self).add_subset(subset) glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/base_widget.py0000644000175000017500000001515413605360406023600 0ustar noahfxnoahfximport os from qtpy.QtCore import Qt from qtpy import QtCore, QtWidgets from glue.utils.qt import get_qapp from glue.core.qt.mime import LAYERS_MIME_TYPE, LAYER_MIME_TYPE __all__ = ['BaseQtViewerWidget'] class BaseQtViewerWidget(QtWidgets.QMainWindow): """ Base Qt class for all DataViewer widgets. This is not a viewer class in itself but is the base widget that should be used for any Qt viewer that is to appear inside the MDI area. """ window_closed = QtCore.Signal() toolbar_added = QtCore.Signal() _closed = False def __init__(self, parent=None): """ :type session: :class:`~glue.core.session.Session` """ super(BaseQtViewerWidget, self).__init__(parent) self.setWindowIcon(get_qapp().windowIcon()) status_bar = self.statusBar() status_bar.setSizeGripEnabled(False) status_bar.setStyleSheet("QStatusBar{font-size:10px}") self.setFocusPolicy(Qt.StrongFocus) self.setAttribute(Qt.WA_DeleteOnClose) self.setAcceptDrops(True) self.setAnimated(False) self.setContentsMargins(2, 2, 2, 2) self._mdi_wrapper = None # GlueMdiSubWindow that self is embedded in self._warn_close = True def dragEnterEvent(self, event): """ Accept drag-and-drop of data or subset objects. """ if event.mimeData().hasFormat(LAYER_MIME_TYPE): event.accept() elif event.mimeData().hasFormat(LAYERS_MIME_TYPE): event.accept() else: event.ignore() def dropEvent(self, event): """ Accept drag-and-drop of data or subset objects. """ if event.mimeData().hasFormat(LAYER_MIME_TYPE): self.request_add_layer(event.mimeData().data(LAYER_MIME_TYPE)) assert event.mimeData().hasFormat(LAYERS_MIME_TYPE) for layer in event.mimeData().data(LAYERS_MIME_TYPE): self.request_add_layer(layer) event.accept() def mousePressEvent(self, event): """ Consume mouse press events, and prevent them from propagating down to the MDI area. """ event.accept() def close(self, warn=True): if self._closed: return if warn and not self._confirm_close(): return self._warn_close = False if getattr(self, '_mdi_wrapper', None) is not None: self._mdi_wrapper.close() self._mdi_wrapper = None else: try: QtWidgets.QMainWindow.close(self) except RuntimeError: # In some cases the above can raise a "wrapped C/C++ object of # type ... has been deleted" error, in which case we can just # ignore and carry on. pass self._closed = True def mdi_wrap(self): """ Wrap this object in a GlueMdiSubWindow """ from glue.app.qt.mdi_area import GlueMdiSubWindow sub = GlueMdiSubWindow() sub.setWidget(self) self.destroyed.connect(sub.close) sub.resize(self.size()) self._mdi_wrapper = sub return sub @property def position(self): """ The location of the viewer as a tuple of ``(x, y)`` """ target = self._mdi_wrapper or self pos = target.pos() return pos.x(), pos.y() @position.setter def position(self, xy): x, y = xy self.move(x, y) def move(self, x=None, y=None): """ Move the viewer to a new XY pixel location You can also set the position attribute to a new tuple directly. Parameters ---------- x : int (optional) New x position y : int (optional) New y position """ x0, y0 = self.position if x is None: x = x0 if y is None: y = y0 if self._mdi_wrapper is not None: self._mdi_wrapper.move(x, y) else: QtWidgets.QMainWindow.move(self, x, y) @property def viewer_size(self): """ Size of the viewer as a tuple of ``(width, height)`` """ if self._mdi_wrapper is not None: sz = self._mdi_wrapper.size() else: sz = self.size() return sz.width(), sz.height() @viewer_size.setter def viewer_size(self, value): width, height = value if self._mdi_wrapper is None: self.resize(width, height) else: self._mdi_wrapper.resize(width, height) def closeEvent(self, event): """ Call unregister on window close """ if self._warn_close and not self._confirm_close(): event.ignore() return super(BaseQtViewerWidget, self).closeEvent(event) event.accept() self.window_closed.emit() def isVisible(self): # Override this so as to catch RuntimeError: wrapped C/C++ object of # type ... has been deleted try: return self.isVisible() except RuntimeError: return False def _confirm_close(self): """Ask for close confirmation :rtype: bool. True if user wishes to close. False otherwise """ if self._warn_close and not os.environ.get('GLUE_TESTING'): buttons = QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel dialog = QtWidgets.QMessageBox.warning(self, "Confirm Close", "Do you want to close this window?", buttons=buttons, defaultButton=QtWidgets.QMessageBox.Cancel) return dialog == QtWidgets.QMessageBox.Ok return True def layer_view(self): return QtWidgets.QWidget() def options_widget(self): return QtWidgets.QWidget() def set_focus(self, state): if state: css = """ DataViewer { border: 2px solid; border-color: rgb(56, 117, 215); } """ else: css = """ DataViewer { border: none; } """ self.setStyleSheet(css) @property def window_title(self): return str(self) def update_window_title(self): try: self.setWindowTitle(self.window_title) except RuntimeError: # Avoid C/C++ errors when closing viewer pass def set_status(self, message): sb = self.statusBar() sb.showMessage(message) glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/__init__.py0000644000175000017500000000000013455362716023053 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/tool.py0000644000175000017500000000030013507416115022263 0ustar noahfxnoahfximport warnings from glue.viewers.common.tool import * # noqa warnings.warn('glue.viewers.common.qt.tool is deprecated, use ' 'glue.viewers.common.tool instead', UserWarning) glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/data_slice_widget.py0000644000175000017500000001635013657331513024761 0ustar noahfxnoahfximport os import numpy as np from qtpy import QtCore, QtWidgets from glue.utils.qt import load_ui from glue.utils import nonpartial, format_minimal from glue.icons.qt import get_icon from glue.core.state_objects import State, CallbackProperty from echo.qt import autoconnect_callbacks_to_qt class SliceState(State): label = CallbackProperty() slider_label = CallbackProperty() slider_unit = CallbackProperty() slice_center = CallbackProperty() use_world = CallbackProperty() class SliceWidget(QtWidgets.QWidget): slice_changed = QtCore.Signal(int) def __init__(self, label='', world=None, lo=0, hi=10, parent=None, world_unit=None, world_warning=False): super(SliceWidget, self).__init__(parent) self.state = SliceState() self.state.label = label self.state.slice_center = (lo + hi) // 2 self._world = np.asarray(world) self._world_warning = world_warning self._world_unit = world_unit self.ui = load_ui('data_slice_widget.ui', self, directory=os.path.dirname(__file__)) self._connections = autoconnect_callbacks_to_qt(self.state, self.ui) font = self.text_warning.font() font.setPointSize(font.pointSize() * 0.75) self.text_warning.setFont(font) self.button_first.setStyleSheet('border: 0px') self.button_first.setIcon(get_icon('playback_first')) self.button_prev.setStyleSheet('border: 0px') self.button_prev.setIcon(get_icon('playback_prev')) self.button_back.setStyleSheet('border: 0px') self.button_back.setIcon(get_icon('playback_back')) self.button_stop.setStyleSheet('border: 0px') self.button_stop.setIcon(get_icon('playback_stop')) self.button_forw.setStyleSheet('border: 0px') self.button_forw.setIcon(get_icon('playback_forw')) self.button_next.setStyleSheet('border: 0px') self.button_next.setIcon(get_icon('playback_next')) self.button_last.setStyleSheet('border: 0px') self.button_last.setIcon(get_icon('playback_last')) self.value_slice_center.setMinimum(lo) self.value_slice_center.setMaximum(hi) self.value_slice_center.valueChanged.connect(nonpartial(self.set_label_from_slider)) # Figure out the optimal format to use to show the world values. We do # this by figuring out the precision needed so that when converted to # a string, every string value is different. if world is not None and len(world) > 1: self.label_fmt = format_minimal(world)[0] else: self.label_fmt = "{:g}" self.text_slider_label.setMinimumWidth(80) self.state.slider_label = self.label_fmt.format(self.value_slice_center.value()) self.text_slider_label.editingFinished.connect(nonpartial(self.set_slider_from_label)) self._play_timer = QtCore.QTimer() self._play_timer.setInterval(500) self._play_timer.timeout.connect(nonpartial(self._play_slice)) self.button_first.clicked.connect(nonpartial(self._browse_slice, 'first')) self.button_prev.clicked.connect(nonpartial(self._browse_slice, 'prev')) self.button_back.clicked.connect(nonpartial(self._adjust_play, 'back')) self.button_stop.clicked.connect(nonpartial(self._adjust_play, 'stop')) self.button_forw.clicked.connect(nonpartial(self._adjust_play, 'forw')) self.button_next.clicked.connect(nonpartial(self._browse_slice, 'next')) self.button_last.clicked.connect(nonpartial(self._browse_slice, 'last')) self.bool_use_world.toggled.connect(nonpartial(self.set_label_from_slider)) if world is None: self.state.use_world = False self.bool_use_world.hide() else: self.state.use_world = not world_warning if world_unit: self.state.slider_unit = world_unit else: self.state.slider_unit = '' self._play_speed = 0 self.set_label_from_slider() def set_label_from_slider(self): value = self.state.slice_center if self.state.use_world: value = self._world[value] if self._world_warning: self.text_warning.show() else: self.text_warning.hide() self.state.slider_unit = self._world_unit self.state.slider_label = self.label_fmt.format(value) else: self.text_warning.hide() self.state.slider_unit = '' self.state.slider_label = str(value) def set_slider_from_label(self): # Ignore recursive calls - we do this rather than ignore_callback # below when setting slider_label, otherwise we might be stopping other # subscribers to that event from being correctly updated if getattr(self, '_in_set_slider_from_label', False): return else: self._in_set_slider_from_label = True text = self.text_slider_label.text() if self.state.use_world: # Don't want to assume world is sorted, pick closest value value = np.argmin(np.abs(self._world - float(text))) self.state.slider_label = self.label_fmt.format(self._world[value]) else: value = int(text) self.value_slice_center.setValue(value) self._in_set_slider_from_label = False def _adjust_play(self, action): if action == 'stop': self._play_speed = 0 elif action == 'back': if self._play_speed > 0: self._play_speed = -1 else: self._play_speed -= 1 elif action == 'forw': if self._play_speed < 0: self._play_speed = +1 else: self._play_speed += 1 if self._play_speed == 0: self._play_timer.stop() else: self._play_timer.start() self._play_timer.setInterval(500 / abs(self._play_speed)) def _play_slice(self): if self._play_speed > 0: self._browse_slice('next', play=True) elif self._play_speed < 0: self._browse_slice('prev', play=True) def _browse_slice(self, action, play=False): imin = self.value_slice_center.minimum() imax = self.value_slice_center.maximum() value = self.value_slice_center.value() # If this was not called from _play_slice, we should stop the # animation. if not play: self._adjust_play('stop') if action == 'first': value = imin elif action == 'last': value = imax elif action == 'prev': value = value - 1 if value < imin: value = imax elif action == 'next': value = value + 1 if value > imax: value = imin else: raise ValueError("Action should be one of first/prev/next/last") self.value_slice_center.setValue(value) if __name__ == "__main__": from glue.utils.qt import get_qapp app = get_qapp() widget = SliceWidget(label='BANANA') widget.show() widget = SliceWidget(world=[1, 2, 3, 4, 5, 6, 7], lo=1, hi=7) widget.show() app.exec_() glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/contrastlimits.ui0000644000175000017500000001056613455362716024402 0ustar noahfxnoahfx min_max Qt::WindowModal 0 0 250 150 0 0 Dialog false false 3 6 0 35 Choose Intensity Limits Qt::AlignHCenter|Qt::AlignTop 50 0 Low Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Data value to set to black 100 50 0 High Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Data value to set to white 100 Qt::Horizontal QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() min_max accept() 248 254 157 274 buttonBox rejected() min_max reject() 316 260 286 274 glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/data_slice_widget.ui0000644000175000017500000001542313502206677024747 0ustar noahfxnoahfx Form 0 0 291 90 Form 0 0 75 true Main label Qt::Horizontal 40 20 Show real coordinates true false color: rgb(255, 33, 28) Warning: real coordinates are not aligned with pixel grid. The coordinate shown above is the value at the center of the slice. Qt::AlignCenter true 10 Qt::Horizontal 2 Qt::Horizontal 40 20 Go to first slice << 16 16 Go to previous slice < 16 16 Play backwards (click multiple times to speed up) < 16 16 Stop the playback 16 16 Play forwards (click multiple times to speed up) > 16 16 Go to next slice > 16 16 true Go to last slice >> 16 16 Qt::Horizontal 40 20 50 16777215 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter TextLabel glueviz-1.0.1+dfsg.orig/glue/viewers/common/qt/data_viewer_with_state.py0000644000175000017500000000021713605357235026050 0ustar noahfxnoahfxfrom glue.viewers.common.qt.data_viewer import DataViewer __all__ = ['DataViewerWithState'] class DataViewerWithState(DataViewer): pass glueviz-1.0.1+dfsg.orig/glue/viewers/common/python_export.py0000644000175000017500000000051413605357235023621 0ustar noahfxnoahfx__all__ = ['code', 'serialize_options'] class code(str): pass def serialize_options(options): result = [] for key, value in options.items(): if isinstance(value, code): result.append(key + '=' + value) else: result.append(key + '=' + repr(value)) return ', '.join(result) glueviz-1.0.1+dfsg.orig/glue/viewers/common/state.py0000644000175000017500000000270513657331513022021 0ustar noahfxnoahfxfrom echo import CallbackProperty, ListCallbackProperty from glue.core.state_objects import State __all__ = ['ViewerState', 'LayerState'] class ViewerState(State): """ A base class for all viewer states. """ layers = ListCallbackProperty(docstring='A collection of all layers in the viewer') @property def layers_data(self): return [layer_state.layer for layer_state in self.layers] class LayerState(State): """ A base class for all layer states. """ layer = CallbackProperty(docstring='The :class:`~glue.core.data.Data` or ' ':class:`~glue.core.subset.Subset` ' 'represented by the layer') zorder = CallbackProperty(0, docstring='A value used to indicate which ' 'layers are shown in front of which ' '(larger zorder values are on top of ' 'other layers)') visible = CallbackProperty(True, docstring='Whether the layer is currently visible') def __init__(self, viewer_state=None, **kwargs): super(LayerState, self).__init__(**kwargs) self.viewer_state = viewer_state def __repr__(self): if self.layer is None: return "%s with layer unset" % (self.__class__.__name__) else: return "%s for %s" % (self.__class__.__name__, self.layer.label) glueviz-1.0.1+dfsg.orig/glue/viewers/common/__init__.py0000644000175000017500000000000013455362716022427 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/common/layer_artist.py0000644000175000017500000000550013661230662023375 0ustar noahfxnoahfxfrom echo import keep_in_sync, CallbackProperty from glue.core.layer_artist import LayerArtistBase from glue.viewers.common.state import LayerState from glue.core.message import LayerArtistVisibilityMessage __all__ = ['LayerArtist'] class LayerArtist(LayerArtistBase): zorder = CallbackProperty() visible = CallbackProperty() _layer_state_cls = LayerState _python_exporter = None def __init__(self, viewer_state, layer_state=None, layer=None): super(LayerArtist, self).__init__(layer) self._viewer_state = viewer_state self.layer = layer or layer_state.layer self.state = layer_state or self._layer_state_cls(viewer_state=viewer_state, layer=self.layer) if self.state not in self._viewer_state.layers: self._viewer_state.layers.append(self.state) self.zorder = self.state.zorder self.visible = self.state.visible self._sync_zorder = keep_in_sync(self, 'zorder', self.state, 'zorder') self._sync_visible = keep_in_sync(self, 'visible', self.state, 'visible') self.state.add_callback('visible', self._on_visibility_change) self._reset_cache() def _reset_cache(self): self._last_viewer_state = {} self._last_layer_state = {} def _on_visibility_change(self, *args): if self.state.layer is not None and self.state.layer.hub is not None: self.state.layer.hub.broadcast(LayerArtistVisibilityMessage(self)) def __gluestate__(self, context): return dict(state=context.id(self.state)) def pop_changed_properties(self): """ Return the names of properties on the viewer and layer state classes that have changed since the last call. Note that calling this method updates the underlying cache, so if it is called immediately after being called a first time, it will return an empty set the second time. """ # Figure out which attributes are different from before. Ideally we # shouldn't need this but some methods in layer artists are called # multiple times if an attribute is changed due to x_att changing then # hist_x_min, hist_x_max, etc. This method is called pop because it # returns the list of changed properties and resets the cache. changed = set() for key, value in self._viewer_state.as_dict().items(): if value != self._last_viewer_state.get(key, None): changed.add(key) for key, value in self.state.as_dict().items(): if value != self._last_layer_state.get(key, None): changed.add(key) self._last_viewer_state.update(self._viewer_state.as_dict()) self._last_layer_state.update(self.state.as_dict()) return changed glueviz-1.0.1+dfsg.orig/glue/viewers/common/tool.py0000644000175000017500000000462113605357235021657 0ustar noahfxnoahfx# The classes in this file define toolbar tools. Mouse modes specifically # are defined in mouse_modes.py from glue.core.callback_property import CallbackProperty __all__ = ['Tool', 'CheckableTool', 'DropdownTool', 'SimpleToolMenu'] class Tool(object): """ The base class for all toolbar tools. All tools have the following attributes: * icon : QIcon object * tool_id : a short name for the tool * action_text : the action title (used if the tool is made available in a menu) * tool_tip : a tip that is shown when the user hovers over the icon * status_tip : a tip that is shown in the status bar when the tool is active * shortcut : keyboard shortcut to toggle the tool """ enabled = CallbackProperty(True) icon = None tool_id = None action_text = None tool_tip = None status_tip = None shortcut = None def __init__(self, viewer=None): self.viewer = viewer if hasattr(self.viewer, 'window_closed'): self.viewer.window_closed.connect(self._do_close) def activate(self): """ Fired when the toolbar button is activated """ pass def menu_actions(self): """ List of QtWidgets.QActions to be attached to this tool as a context menu. """ return [] def _do_close(self, *args): # We do this so that tools can override close self.close() def close(self): if hasattr(self.viewer, 'window_closed'): self.viewer.window_closed.disconnect(self._do_close) self.viewer = None class CheckableTool(Tool): """ A tool that is checkable. When checked, the ``activate`` method is executed, and when unchecked, the ``deactivate`` method is executed. """ def activate(self): """ Fired when the toolbar button is activated """ pass def deactivate(self): """ Fired when the toolbar button is deactivated """ pass class DropdownTool(Tool): """ A base class for all tools that show a drop-down menu. """ def __init__(self, *args, **kwargs): self.subtools = kwargs.pop('subtools', []) super(DropdownTool, self).__init__(*args, **kwargs) class SimpleToolMenu(DropdownTool): """ A base class for tools that have no action it themselves but show a dropdown of other tools. """ pass glueviz-1.0.1+dfsg.orig/glue/viewers/common/viewer.py0000644000175000017500000003732413661230662022205 0ustar noahfxnoahfximport os import warnings from IPython import get_ipython from glue.core.hub import HubListener from glue.core import BaseData, Subset from glue.core import command from glue.core.command import ApplySubsetState from glue.core.state import save from glue.core import message as msg from glue.core.exceptions import IncompatibleDataException from glue.core.state import lookup_class_with_patches from echo import delay_callback from glue.core.layer_artist import LayerArtistContainer from glue.viewers.common.state import ViewerState from glue.viewers.common.layer_artist import LayerArtist from glue.config import layer_artist_maker __all__ = ['BaseViewer', 'Viewer'] def get_layer_artist_from_registry(data, viewer): """ Check whether any plugins define an appropriate custom layer artist for the specified data and viewer. """ for maker in layer_artist_maker.members: layer_artist = maker.function(viewer, data) if layer_artist is not None: return layer_artist class BaseViewer(HubListener): """ The base class for all viewers. """ LABEL = 'Override this' def __init__(self, session): self._session = session self._data = session.data_collection self._hub = None def register_to_hub(self, hub): self._hub = hub @property def session(self): return self._session def request_add_layer(self, layer): """ Issue a command to add a layer """ cmd = command.AddLayer(layer=layer, viewer=self) self._session.command_stack.do(cmd) def add_layer(self, layer): if isinstance(layer, BaseData): self.add_data(layer) elif isinstance(layer, Subset): self.add_subset(layer) def remove_layer(self, layer): pass def add_data(self, data): raise NotImplementedError() def add_subset(self, subset): raise NotImplementedError() def __str__(self): return self.LABEL def __gluestate__(self, context): return dict(session=context.id(self._session)) @classmethod def __setgluestate__(cls, rec, context): session = context.object(rec['session']) return cls(session) def apply_subset_state(self, subset_state, override_mode=None): cmd = ApplySubsetState(data_collection=self._data, subset_state=subset_state, override_mode=override_mode) self._session.command_stack.do(cmd) TEMPLATE_SCRIPT = """ # This script was produced by glue and can be used to further customize a # particular plot. ### Package imports {imports} ### Set up data data_collection = load('{data}') ### Set up viewer {header} ### Set up layers {layers} ### Legend {legend} ### Finalize viewer {footer} """.strip() class Viewer(BaseViewer): """ A viewer class that uses a state class to represent the overall viewer state, and uses layer artists and state classes to handle each dataset and subset in the data viewer. """ # The LayerArtistContainer class/subclass to use _layer_artist_container_cls = LayerArtistContainer # The state class/subclass to use _state_cls = ViewerState _data_artist_cls = LayerArtist _subset_artist_cls = LayerArtist allow_duplicate_data = False allow_duplicate_subset = False large_data_size = None def __init__(self, session, state=None): super(Viewer, self).__init__(session) # Set up the state which will contain everything needed to represent # the current state of the viewer self.state = state or self._state_cls() self.state.data_collection = session.data_collection # Create the layer artist container, which is the object in which # we will add LayerArtist objects self._layer_artist_container = self._layer_artist_container_cls() # When layer artists are removed from the layer artist container, we # need to make sure we remove matching layer states in the viewer state # layers attribute. self._layer_artist_container.on_changed(self._sync_state_layers) # And vice-versa when layer states are removed from the viewer state, we # need to keep the layer_artist_container in sync self.state.add_callback('layers', self._sync_layer_artist_container, priority=10000) self.state.add_callback('layers', self.draw_legend) def draw_legend(self, *args): pass def _sync_state_layers(self, *args): # Remove layer state objects that no longer have a matching layer for layer_state in self.state.layers: if layer_state.layer not in self._layer_artist_container: self.state.layers.remove(layer_state) def _sync_layer_artist_container(self, *args): # Remove layer artists that no longer have a matching layer state layer_states = set(layer_state.layer for layer_state in self.state.layers) for layer_artist in self._layer_artist_container: if layer_artist.layer not in layer_states: self._layer_artist_container.remove(layer_artist) def warn(self, message, *args, **kwargs): warnings.warn(message) return True def add_data(self, data): # Check if data already exists in viewer if not self.allow_duplicate_data and data in self._layer_artist_container: return True if self.large_data_size is not None and data.size >= self.large_data_size: proceed = self.warn('Add large data set?', 'Data set {0:s} has {1:d} points, and ' 'may render slowly.'.format(data.label, data.size), default='Cancel', setting='show_large_data_warning') if not proceed: return False if data not in self.session.data_collection: raise IncompatibleDataException("Data not in DataCollection") # Create layer artist and add to container. First check whether any # plugins want to make a custom layer artist. layer = get_layer_artist_from_registry(data, self) or self.get_data_layer_artist(data) if layer is None: return False self._layer_artist_container.append(layer) layer.update() # Add existing subsets to viewer for subset in data.subsets: self.add_subset(subset) return True def remove_data(self, data): with delay_callback(self.state, 'layers'): for layer_state in self.state.layers[::-1]: if isinstance(layer_state.layer, BaseData): if layer_state.layer is data: self.state.layers.remove(layer_state) else: if layer_state.layer.data is data: self.state.layers.remove(layer_state) def get_data_layer_artist(self, layer=None, layer_state=None): return self.get_layer_artist(self._data_artist_cls, layer=layer, layer_state=layer_state) def get_subset_layer_artist(self, layer=None, layer_state=None): return self.get_layer_artist(self._subset_artist_cls, layer=layer, layer_state=layer_state) def get_layer_artist(self, cls, layer=None, layer_state=None): return cls(self.state, layer=layer, layer_state=layer_state) def add_subset(self, subset): # Check if subset already exists in viewer if not self.allow_duplicate_subset and subset in self._layer_artist_container: return True # Create layer artist and add to container. First check whether any # plugins want to make a custom layer artist. layer = get_layer_artist_from_registry(subset, self) or self.get_subset_layer_artist(subset) if layer is None: return False self._layer_artist_container.append(layer) layer.update() return True def remove_subset(self, subset): if subset in self._layer_artist_container: self._layer_artist_container.pop(subset) def _add_subset(self, message): self.add_subset(message.subset) def _update_data(self, message): if message.data in self._layer_artist_container: for layer_artist in self._layer_artist_container: if isinstance(layer_artist.layer, Subset): if layer_artist.layer.data is message.data: layer_artist.update() else: if layer_artist.layer is message.data: layer_artist.update() def _update_subset(self, message): if message.attribute == 'style': return if message.subset in self._layer_artist_container: for layer_artist in self._layer_artist_container[message.subset]: layer_artist.update() def _remove_subset(self, message): self.remove_subset(message.subset) def options_widget(self): return self.options def _subset_has_data(self, x): return x.sender.data in self._layer_artist_container.layers def _has_data_or_subset(self, x): return x.sender in self._layer_artist_container.layers def _remove_data(self, message): self.remove_data(message.data) def _is_appearance_settings(self, msg): return ('BACKGROUND_COLOR' in msg.settings or 'FOREGROUND_COLOR' in msg.settings) def register_to_hub(self, hub): super(Viewer, self).register_to_hub(hub) hub.subscribe(self, msg.SubsetCreateMessage, handler=self._add_subset, filter=self._subset_has_data) hub.subscribe(self, msg.SubsetUpdateMessage, handler=self._update_subset, filter=self._has_data_or_subset) hub.subscribe(self, msg.SubsetDeleteMessage, handler=self._remove_subset, filter=self._has_data_or_subset) hub.subscribe(self, msg.NumericalDataChangedMessage, handler=self._update_data, filter=self._has_data_or_subset) hub.subscribe(self, msg.DataCollectionDeleteMessage, handler=self._remove_data) hub.subscribe(self, msg.ComponentsChangedMessage, handler=self._update_data, filter=self._has_data_or_subset) hub.subscribe(self, msg.ExternallyDerivableComponentsChangedMessage, handler=self._update_data, filter=self._has_data_or_subset) hub.subscribe(self, msg.SettingsChangeMessage, self._update_appearance_from_settings, filter=self._is_appearance_settings) hub.subscribe(self, msg.ComputationMessage, self._update_computation, filter=self._has_layer_artist) hub.subscribe(self, msg.LayerArtistDisabledMessage, self.draw_legend, filter=self._has_layer_artist) def _has_layer_artist(self, message): return message.layer_artist in self.layers def _update_computation(self, message=None): pass def _update_appearance_from_settings(self, message=None): pass def __gluestate__(self, context): return dict(state=self.state.__gluestate__(context), session=context.id(self._session), layers=list(map(context.do, self.layers)), _protocol=1) @classmethod def __setgluestate__(cls, rec, context): session = context.object(rec['session']) viewer_state = cls._state_cls.__setgluestate__(rec['state'], context) viewer = cls(session, state=viewer_state) viewer.register_to_hub(session.hub) # Restore layer artists. Ideally we would delay instead of ignoring the # callback here. with viewer._layer_artist_container.ignore_callbacks(): for l in rec['layers']: cls = lookup_class_with_patches(l.pop('_type')) layer_state = context.object(l['state']) layer_artist = viewer.get_layer_artist(cls, layer_state=layer_state) layer_state.viewer_state = viewer.state viewer._layer_artist_container.append(layer_artist) viewer.draw_legend() # need to be called here because callbacks are ignored in previous step return viewer def cleanup(self): if self._hub is not None: self.unregister(self._hub) self._layer_artist_container.clear_callbacks() self._layer_artist_container.clear() # Remove any references to the viewer in the IPython namespace. We use # list() here to force an explicit copy since we are modifying the # dictionary in-place shell = get_ipython() if shell is not None: for key in list(shell.user_ns): if shell.user_ns[key] is self: shell.user_ns.pop(key) def remove_layer(self, layer): self._layer_artist_container.pop(layer) @property def layers(self): """Return a tuple of layers in this viewer. A layer is a visual representation of a dataset or subset within the viewer""" return tuple(self._layer_artist_container) def _script_header(self): raise NotImplementedError() def _script_legend(self): return [], "" def _script_footer(self): raise NotImplementedError() def export_as_script(self, filename): data_filename = os.path.relpath(filename) + '.data' save(data_filename, self.session.data_collection) imports = ['from glue.core.state import load'] imports_header, header = self._script_header() imports.extend(imports_header) layers = "" for ilayer, layer in enumerate(self.layers): if layer.layer.label: layers += '## Layer {0}: {1}\n\n'.format(ilayer + 1, layer.layer.label) else: layers += '## Layer {0}\n\n'.format(ilayer + 1) if layer.visible and layer.enabled: if isinstance(layer.layer, BaseData): index = self.session.data_collection.index(layer.layer) layers += "layer_data = data_collection[{0}]\n\n".format(index) else: dindex = self.session.data_collection.index(layer.layer.data) sindex = layer.layer.data.subsets.index(layer.layer) layers += ("layer_data = data_collection[{0}].subsets[{1}]\n\n" .format(dindex, sindex)) imports_layer, layer_script = layer._python_exporter(layer) if layer_script is None: continue imports.extend(imports_layer) layers += layer_script.strip() + "\n" imports_legend, legend = self._script_legend() imports.extend(imports_legend) imports_footer, footer = self._script_footer() imports.extend(imports_footer) imports = os.linesep.join(sorted(set(imports), key=lambda s: s.strip('# '))) # The sorting key is added keep together similar but commented imports # Typical ex: # matplotlib.use('Agg') # # matplotlib.use('qt5Agg') script = TEMPLATE_SCRIPT.format(data=os.path.basename(data_filename), imports=imports.strip(), header=header.strip(), layers=layers.strip(), legend=legend.strip(), footer=footer.strip()) with open(filename, 'w') as f: f.write(script) glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/0000755000175000017500000000000013752535025020500 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/0000755000175000017500000000000013752535025021124 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/tests/0000755000175000017500000000000013752535025022266 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/tests/test_python_export.py0000644000175000017500000002713413752534424026632 0ustar noahfxnoahfximport numpy as np import matplotlib.pyplot as plt from astropy.utils import NumpyRNGContext from glue.core import Data, DataCollection from glue.app.qt.application import GlueApplication from glue.viewers.matplotlib.qt.tests.test_python_export import BaseTestExportPython, random_with_nan from glue.viewers.scatter.qt import ScatterViewer class TestExportPython(BaseTestExportPython): def setup_method(self, method): with NumpyRNGContext(12345): self.data = Data(**dict((name, random_with_nan(100, nan_index=idx + 1)) for idx, name in enumerate('abcdefgh'))) self.data['angle'] = np.random.uniform(0, 360, 100) self.data_collection = DataCollection([self.data]) self.app = GlueApplication(self.data_collection) self.viewer = self.app.new_data_viewer(ScatterViewer) self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['a'] self.viewer.state.y_att = self.data.id['b'] def teardown_method(self, method): self.viewer.close() self.viewer = None self.app.close() self.app = None def test_simple(self, tmpdir): self.assert_same(tmpdir) def test_simple_legend(self, tmpdir): self.viewer.state.legend.visible = True self.assert_same(tmpdir) def test_simple_nofill(self, tmpdir): self.viewer.state.layers[0].fill = False self.viewer.state.layers[0].size_scaling = 10 self.assert_same(tmpdir) def test_simple_visual(self, tmpdir): self.viewer.state.layers[0].color = 'blue' self.viewer.state.layers[0].markersize = 30 self.viewer.state.layers[0].alpha = 0.5 self.assert_same(tmpdir) def test_simple_visual_legend(self, tmpdir): self.viewer.state.legend.visible = True self.viewer.state.layers[0].color = 'blue' self.viewer.state.layers[0].markersize = 30 self.viewer.state.layers[0].alpha = 0.5 self.assert_same(tmpdir) def test_cmap_mode(self, tmpdir): self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap_att = self.data.id['c'] self.viewer.state.layers[0].cmap = plt.cm.BuGn self.viewer.state.layers[0].cmap_vmin = 0.2 self.viewer.state.layers[0].cmap_vmax = 0.7 self.viewer.state.layers[0].alpha = 0.8 self.assert_same(tmpdir) def test_cmap_mode_nofill(self, tmpdir): self.viewer.state.layers[0].fill = False self.test_cmap_mode(tmpdir) def test_size_mode(self, tmpdir): self.viewer.state.layers[0].size_mode = 'Linear' self.viewer.state.layers[0].size_att = self.data.id['d'] self.viewer.state.layers[0].size_vmin = 0.1 self.viewer.state.layers[0].size_vmax = 0.8 self.viewer.state.layers[0].size_scaling = 0.4 self.viewer.state.layers[0].alpha = 0.7 self.assert_same(tmpdir) def test_size_mode_legend(self, tmpdir): self.viewer.state.legend.visible = True self.viewer.state.layers[0].size_mode = 'Linear' self.viewer.state.layers[0].size_att = self.data.id['d'] self.viewer.state.layers[0].size_vmin = 0.1 self.viewer.state.layers[0].size_vmax = 0.8 self.viewer.state.layers[0].size_scaling = 0.4 self.viewer.state.layers[0].alpha = 0.7 self.assert_same(tmpdir) def test_size_mode_nofill(self, tmpdir): self.viewer.state.layers[0].fill = False self.test_size_mode(tmpdir) def test_line(self, tmpdir): self.viewer.state.layers[0].line_visible = True self.viewer.state.layers[0].linewidth = 10 self.viewer.state.layers[0].linestype = 'dashed' self.viewer.state.layers[0].color = 'orange' self.viewer.state.layers[0].alpha = 0.7 self.viewer.state.layers[0].markersize = 100 self.assert_same(tmpdir, tol=5) def test_line_cmap(self, tmpdir): self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap_vmin = 0.2 self.viewer.state.layers[0].cmap_vmax = 0.7 self.viewer.state.layers[0].cmap = plt.cm.BuGn self.test_line(tmpdir) def test_errorbarx(self, tmpdir): self.viewer.state.layers[0].xerr_visible = True self.viewer.state.layers[0].xerr_att = self.data.id['e'] self.viewer.state.layers[0].color = 'purple' self.viewer.state.layers[0].alpha = 0.5 self.assert_same(tmpdir) def test_errorbary(self, tmpdir): self.viewer.state.layers[0].yerr_visible = True self.viewer.state.layers[0].yerr_att = self.data.id['f'] self.viewer.state.layers[0].color = 'purple' self.viewer.state.layers[0].alpha = 0.5 self.assert_same(tmpdir) def test_errorbarxy(self, tmpdir): self.viewer.state.layers[0].xerr_visible = True self.viewer.state.layers[0].xerr_att = self.data.id['e'] self.viewer.state.layers[0].yerr_visible = True self.viewer.state.layers[0].yerr_att = self.data.id['f'] self.viewer.state.layers[0].color = 'purple' self.viewer.state.layers[0].alpha = 0.5 self.assert_same(tmpdir) def test_errorbarxy_legend(self, tmpdir): self.viewer.state.legend.visible = True self.viewer.state.layers[0].xerr_visible = True self.viewer.state.layers[0].xerr_att = self.data.id['e'] self.viewer.state.layers[0].yerr_visible = True self.viewer.state.layers[0].yerr_att = self.data.id['f'] self.viewer.state.layers[0].color = 'purple' self.viewer.state.layers[0].alpha = 0.5 self.assert_same(tmpdir) def test_errorbarxy_cmap(self, tmpdir): self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap_vmin = 0.2 self.viewer.state.layers[0].cmap_vmax = 0.7 self.viewer.state.layers[0].cmap = plt.cm.BuGn self.test_errorbarxy(tmpdir) def _vector_common(self, tmpdir): self.viewer.state.layers[0].vector_visible = True self.viewer.state.layers[0].vy_att = self.data.id['g'] self.viewer.state.layers[0].vector_arrowhead = True self.viewer.state.layers[0].vector_origin = 'tail' self.viewer.state.layers[0].vector_scaling = 1.5 self.viewer.state.layers[0].color = 'teal' self.viewer.state.layers[0].alpha = 0.9 self.assert_same(tmpdir, tol=1) def test_vector_cartesian(self, tmpdir): self.viewer.state.layers[0].vector_mode = 'Cartesian' self.viewer.state.layers[0].vx_att = self.data.id['h'] self._vector_common(tmpdir) def test_vector_polar(self, tmpdir): self.viewer.state.layers[0].vector_mode = 'Polar' self.viewer.state.layers[0].vx_att = self.data.id['angle'] self._vector_common(tmpdir) def test_vector_cartesian_cmap(self, tmpdir): self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap_vmin = 0.2 self.viewer.state.layers[0].cmap_vmax = 0.7 self.viewer.state.layers[0].cmap = plt.cm.BuGn self.test_vector_cartesian(tmpdir) def test_vector_cartesian_xflip(self, tmpdir): # Regression test for a bug that caused vectors to not be flipped self.viewer.state.layers[0].vector_mode = 'Cartesian' self.viewer.state.layers[0].vx_att = self.data.id['h'] self.viewer.state.flip_x() self._vector_common(tmpdir) def test_subset(self, tmpdir): self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) self.assert_same(tmpdir) def test_density_map_with_subset(self, tmpdir): self.viewer.state.dpi = 2 self.viewer.state.layers[0].density_map = True self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) self.assert_same(tmpdir) def test_density_map_cmap_with_subset(self, tmpdir): self.viewer.state.dpi = 2 self.viewer.state.layers[0].density_map = True self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap_vmin = 0.2 self.viewer.state.layers[0].cmap_vmax = 0.7 self.viewer.state.layers[0].cmap = plt.cm.BuGn self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) self.assert_same(tmpdir) def test_density_map_cmap_with_subset_legend(self, tmpdir): self.viewer.state.legend.visible = True self.viewer.state.dpi = 2 self.viewer.state.layers[0].density_map = True self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap_vmin = 0.2 self.viewer.state.layers[0].cmap_vmax = 0.7 self.viewer.state.layers[0].cmap = plt.cm.BuGn self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) self.assert_same(tmpdir) def test_cmap_mode_change(self, tmpdir): # Regression test for a bug that caused scatter markers to not change # color when going from Linear to Fixed mode self.viewer.state.layers[0].size_mode = 'Linear' self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap_mode = 'Fixed' self.assert_same(tmpdir) def test_density_map_change(self, tmpdir): # Regression test for a bug that caused the density map to still # be visible if using color-coding with the density map then # switching to markers. self.viewer.state.layers[0].density_map = True self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap = plt.cm.BuGn self.viewer.state.layers[0].density_map = False self.assert_same(tmpdir) def test_simple_polar_plot(self, tmpdir): self.viewer.state.plot_mode = 'polar' self.viewer.state.x_att = self.data.id['c'] self.viewer.state.y_att = self.data.id['d'] self.assert_same(tmpdir) def test_full_sphere(self, tmpdir): self.viewer.state.plot_mode = 'aitoff' self.viewer.state.x_att = self.data.id['c'] self.viewer.state.y_att = self.data.id['d'] self.assert_same(tmpdir) self.viewer.state.plot_mode = 'hammer' self.viewer.state.x_att = self.data.id['e'] self.viewer.state.y_att = self.data.id['f'] self.assert_same(tmpdir) self.viewer.state.plot_mode = 'lambert' self.viewer.state.x_att = self.data.id['g'] self.viewer.state.y_att = self.data.id['h'] self.assert_same(tmpdir) self.viewer.state.plot_mode = 'mollweide' self.viewer.state.x_att = self.data.id['a'] self.viewer.state.y_att = self.data.id['b'] self.assert_same(tmpdir) def test_cmap_size_noncartesian(self, tmpdir): self.viewer.state.layers[0].size_mode = 'Linear' self.viewer.state.layers[0].cmap_mode = 'Linear' for proj in ['polar', 'aitoff', 'hammer', 'lambert', 'mollweide']: self.viewer.state.plot_mode = proj self.assert_same(tmpdir) def test_vectors_noncartesian(self, tmpdir): for proj in ['polar', 'aitoff', 'hammer', 'lambert', 'mollweide']: self.viewer.state.plot_mode = proj self._vector_common(tmpdir) def test_errorbarxy_noncartesian(self, tmpdir): self.viewer.state.layers[0].xerr_visible = True self.viewer.state.layers[0].xerr_att = self.data.id['e'] self.viewer.state.layers[0].yerr_visible = True self.viewer.state.layers[0].yerr_att = self.data.id['f'] self.viewer.state.layers[0].color = 'purple' self.viewer.state.layers[0].alpha = 0.5 for proj in ['polar', 'aitoff', 'hammer', 'lambert', 'mollweide']: self.viewer.state.plot_mode = proj self.assert_same(tmpdir) glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/tests/__init__.py0000644000175000017500000000000013455362716024372 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/tests/data/0000755000175000017500000000000013752535025023177 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/tests/data/scatter_v0.glu0000644000175000017500000002305713502206677025772 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoAAAAAAADwP5qZmZmZmck/AAAAAAAA8L8=" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoDAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAA=" }, "units": "" }, "Component_1": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoAAAAAAAAQQJqZmZmZmdk/AAAAAAAAFEA=" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "a", "Pixel Axis 0 [x]", "World 0", "b", "c" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0", "Component_1" ], "data": [ "basic" ], "groups": [ "Subset 1_0", "Subset 2_0" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0" ], "subset_group_count": 2 }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "PolygonalROI": { "_type": "glue.core.roi.PolygonalROI", "vx": [ 0.010048309178743997, 0.22106280193236705, 0.22106280193236705, 0.010048309178743997, 0.010048309178743997 ], "vy": [ 1.9849289099526066, 1.9849289099526066, 2.167298578199052, 2.167298578199052, 1.9849289099526066 ] }, "PolygonalROI_0": { "_type": "glue.core.roi.PolygonalROI", "vx": [ -1.04, 0.4521739130434783, 0.4521739130434783, -1.04, -1.04 ], "vy": [ 1.9997156398104265, 1.9997156398104265, 2.2806635071090047, 2.2806635071090047, 1.9997156398104265 ] }, "RoiSubsetState": { "_type": "glue.core.subset.RoiSubsetState", "roi": "PolygonalROI", "xatt": "a", "yatt": "b" }, "RoiSubsetState_0": { "_type": "glue.core.subset.RoiSubsetState", "roi": "PolygonalROI_0", "xatt": "a", "yatt": "b" }, "ScatterWidget": { "_type": "glue.viewers.scatter.qt.viewer_widget.ScatterWidget", "layers": [ { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "basic", "visible": true, "xatt": "a", "yatt": "b", "zorder": 1 }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "Subset 1", "visible": true, "xatt": "a", "yatt": "b", "zorder": 2 }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "Subset 2", "visible": true, "xatt": "a", "yatt": "b", "zorder": 3 } ], "pos": [ 0, 0 ], "properties": { "hidden": false, "xatt": "a", "xflip": false, "xlog": false, "xmax": 1.04, "xmin": -1.04, "yatt": "b", "yflip": false, "ylog": false, "ymax": 3.02, "ymin": 1.98 }, "session": "Session", "size": [ 600, 400 ] }, "ScatterWidget_0": { "_type": "glue.viewers.scatter.qt.viewer_widget.ScatterWidget", "layers": [ { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "basic", "visible": true, "xatt": "a", "yatt": "c", "zorder": 1 }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "Subset 1", "visible": false, "xatt": "a", "yatt": "c", "zorder": 2 }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "Subset 2", "visible": true, "xatt": "a", "yatt": "c", "zorder": 3 } ], "pos": [ 600, 0 ], "properties": { "hidden": false, "xatt": "a", "xflip": false, "xlog": true, "xmax": 1.05, "xmin": 9.5e-06, "yatt": "c", "yflip": false, "ylog": true, "ymax": 5.25, "ymin": 0.38 }, "session": "Session", "size": [ 600, 400 ] }, "ScatterWidget_1": { "_type": "glue.viewers.scatter.qt.viewer_widget.ScatterWidget", "layers": [ { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "basic", "visible": true, "xatt": "b", "yatt": "a", "zorder": 1 }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "Subset 1", "visible": true, "xatt": "b", "yatt": "a", "zorder": 2 }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "layer": "Subset 2", "visible": false, "xatt": "b", "yatt": "a", "zorder": 3 } ], "pos": [ 0, 400 ], "properties": { "hidden": false, "xatt": "b", "xflip": false, "xlog": false, "xmax": 5.0, "xmin": 0.0, "yatt": "a", "yflip": true, "ylog": false, "ymax": 5.0, "ymin": -5.0 }, "session": "Session", "size": [ 600, 400 ] }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "Subset 1_0": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "RoiSubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1" ] }, "Subset 2": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 2_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#33a02c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "Subset 2_0": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 2", "state": "RoiSubsetState_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#33a02c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 2" ] }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.viewers.scatter" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ScatterWidget", "ScatterWidget_0", "ScatterWidget_1" ] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "basic": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component" ], [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "b", "Component_0" ], [ "c", "Component_1" ] ], "coords": "Coordinates", "label": "basic", "primary_owner": [ "a", "Pixel Axis 0 [x]", "World 0", "b", "c" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1", "Subset 2" ], "uuid": "dde080dc-a65a-4988-bed9-c0cb90ff91a8" }, "c": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "c" } }glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/tests/data/scatter_and_line_v1.glu0000644000175000017500000001633113644362032027614 0ustar noahfxnoahfx{ "CallbackList": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ScatterLayerState" ] }, "CallbackList_0": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ScatterLayerState_0" ] }, "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoCAAAAAAAAAAMAAAAAAAAABAAAAAAAAAA=" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "a", "Pixel Axis 0 [x]", "World 0", "b" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0" ], "data": [ "table" ], "groups": [], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0" ], "subset_group_count": 0 }, "LinearSegmentedColormap": { "_type": "matplotlib.colors.LinearSegmentedColormap", "cmap": "gray" }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "ScatterLayerState": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.7777777777777778, "cmap": "LinearSegmentedColormap", "cmap_att": "a", "cmap_mode": "st__Fixed", "cmap_vmax": 3.0, "cmap_vmin": 1.0, "color": "st__#404040", "layer": "table", "linestyle": "st__solid", "linewidth": 1, "size": 3, "size_att": "a", "size_mode": "st__Fixed", "size_scaling": 1, "size_vmax": 3.0, "size_vmin": 1.0, "style": "st__Scatter", "visible": true, "xerr_att": "a", "xerr_visible": false, "yerr_att": "a", "yerr_visible": false, "zorder": 1 } }, "ScatterLayerState_0": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.7777777777777778, "cmap": "LinearSegmentedColormap", "cmap_att": "a", "cmap_mode": "st__Fixed", "cmap_vmax": 3.0, "cmap_vmin": 1.0, "color": "st__#404040", "layer": "table", "linestyle": "st__solid", "linewidth": 1, "size": 3, "size_att": "a", "size_mode": "st__Fixed", "size_scaling": 1, "size_vmax": 3.0, "size_vmin": 1.0, "style": "st__Line", "visible": true, "xerr_att": "a", "xerr_visible": false, "yerr_att": "a", "yerr_visible": false, "zorder": 1 } }, "ScatterViewer": { "_protocol": 1, "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", "layers": [ { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState" } ], "pos": [ 0, 0 ], "session": "Session", "size": [ 605, 444 ], "state": { "values": { "aspect": "st__auto", "layers": "CallbackList", "x_att": "a", "x_log": false, "x_max": 3.0, "x_min": 1.0, "y_att": "b", "y_log": false, "y_max": 4.0, "y_min": 2.0 } } }, "ScatterViewer_0": { "_protocol": 1, "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", "layers": [ { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState_0" } ], "pos": [ 605, 0 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "aspect": "st__auto", "layers": "CallbackList_0", "x_att": "a", "x_log": false, "x_max": 3.0, "x_min": 1.0, "y_att": "b", "y_log": false, "y_max": 4.0, "y_min": 2.0 } } }, "Session": { "_type": "glue.core.session.Session" }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.core.data_exporters", "glue.viewers.histogram", "glue.io.formats.fits", "glue_exp.tools.zoom_buttons", "glue.viewers.scatter", "glue.plugins.export_d3po", "glue.plugins.exporters.plotly", "glue_samp", "glue_exp.importers.webcam", "glue.viewers.table", "glue_aladin", "glue.plugins.tools.spectrum_tool", "glue_vispy_viewers.volume", "glue.plugins.dendro_viewer", "glue.viewers.image", "glue_exp.tools.contour_selection", "glue_wwt", "glue.plugins.tools.pv_slicer", "glue_exp.tools.floodfill_selection", "glue_exp.importers.vizier", "glue_vispy_viewers.scatter", "glue.plugins.coordinate_helpers" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ScatterViewer", "ScatterViewer_0" ] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "table": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component" ], [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "b", "Component_0" ] ], "coords": "Coordinates", "label": "table", "primary_owner": [ "a", "Pixel Axis 0 [x]", "World 0", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.7777777777777778, "color": "#404040", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [], "uuid": "3755b390-c67e-44a0-b5b5-ebfc709c859e" } }glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/tests/data/scatter_v1.glu0000644000175000017500000003127313644362032025765 0ustar noahfxnoahfx{ "CallbackList": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ScatterLayerState", "ScatterLayerState_0", "ScatterLayerState_1" ] }, "CallbackList_0": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ScatterLayerState_2", "ScatterLayerState_3", "ScatterLayerState_4" ] }, "CallbackList_1": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ScatterLayerState_5", "ScatterLayerState_6", "ScatterLayerState_7" ] }, "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoAAAAAAADwP5qZmZmZmck/AAAAAAAA8L8=" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoDAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAA=" }, "units": "" }, "Component_1": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoAAAAAAAAQQJqZmZmZmdk/AAAAAAAAFEA=" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "a", "Pixel Axis 0 [x]", "World 0", "b", "c" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0", "Component_1" ], "data": [ "basic" ], "groups": [ "Subset 1", "Subset 2" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2" ], "subset_group_count": 2 }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "PolygonalROI": { "_type": "glue.core.roi.PolygonalROI", "vx": [ 0.010048309178743997, 0.22106280193236705, 0.22106280193236705, 0.010048309178743997, 0.010048309178743997 ], "vy": [ 1.9849289099526066, 1.9849289099526066, 2.167298578199052, 2.167298578199052, 1.9849289099526066 ] }, "PolygonalROI_0": { "_type": "glue.core.roi.PolygonalROI", "vx": [ -1.04, 0.4521739130434783, 0.4521739130434783, -1.04, -1.04 ], "vy": [ 1.9997156398104265, 1.9997156398104265, 2.2806635071090047, 2.2806635071090047, 1.9997156398104265 ] }, "RoiSubsetState": { "_type": "glue.core.subset.RoiSubsetState", "roi": "PolygonalROI", "xatt": "a", "yatt": "b" }, "RoiSubsetState_0": { "_type": "glue.core.subset.RoiSubsetState", "roi": "PolygonalROI_0", "xatt": "a", "yatt": "b" }, "ScatterLayerState": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.8, "color": "st__0.35", "layer": "basic", "size": 3, "visible": true, "zorder": 2 } }, "ScatterLayerState_0": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "size": 7, "visible": true, "zorder": 3 } }, "ScatterLayerState_1": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.5, "color": "st__#33a02c", "layer": "Subset 2_0", "size": 7, "visible": true, "zorder": 4 } }, "ScatterLayerState_2": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.8, "color": "st__0.35", "layer": "basic", "size": 3, "visible": true, "zorder": 2 } }, "ScatterLayerState_3": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "size": 7, "visible": false, "zorder": 3 } }, "ScatterLayerState_4": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.5, "color": "st__#33a02c", "layer": "Subset 2_0", "size": 7, "visible": true, "zorder": 4 } }, "ScatterLayerState_5": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.8, "color": "st__0.35", "layer": "basic", "size": 3, "visible": true, "zorder": 2 } }, "ScatterLayerState_6": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "size": 7, "visible": true, "zorder": 3 } }, "ScatterLayerState_7": { "_type": "glue.viewers.scatter.state.ScatterLayerState", "values": { "alpha": 0.5, "color": "st__#33a02c", "layer": "Subset 2_0", "size": 7, "visible": false, "zorder": 4 } }, "ScatterViewer": { "_protocol": 1, "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", "layers": [ { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState" }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState_0" }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState_1" } ], "pos": [ 0, 0 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "layers": "CallbackList", "x_att": "a", "x_log": false, "x_max": 1.04, "x_min": -1.04, "y_att": "b", "y_log": false, "y_max": 3.02, "y_min": 1.98 } } }, "ScatterViewer_0": { "_protocol": 1, "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", "layers": [ { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState_2" }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState_3" }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState_4" } ], "pos": [ 600, 0 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "layers": "CallbackList_0", "x_att": "a", "x_log": true, "x_max": 1.05, "x_min": 9.5e-06, "y_att": "c", "y_log": true, "y_max": 5.25, "y_min": 0.38 } } }, "ScatterViewer_1": { "_protocol": 1, "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", "layers": [ { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState_5" }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState_6" }, { "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", "state": "ScatterLayerState_7" } ], "pos": [ 0, 400 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "layers": "CallbackList_1", "x_att": "b", "x_log": false, "x_max": 5.0, "x_min": 0.0, "y_att": "a", "y_log": false, "y_max": 5.0, "y_min": -5.0 } } }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "RoiSubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1_0" ] }, "Subset 1_0": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "Subset 2": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 2", "state": "RoiSubsetState_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#33a02c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 2_0" ] }, "Subset 2_0": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 2", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#33a02c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.plugins.exporters.plotly", "specviz.app", "glue.plugins.coordinate_helpers", "glue.viewers.histogram", "glue.core.data_exporters", "glue.plugins.export_d3po", "glue_vispy_viewers.volume", "glue_vispy_viewers.scatter", "glue.viewers.table", "glue.viewers.image", "glue_medical", "glue.plugins.tools.spectrum_tool", "glue.plugins.tools.pv_slicer", "glue.viewers.scatter" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ScatterViewer", "ScatterViewer_0", "ScatterViewer_1" ] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "basic": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component" ], [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "b", "Component_0" ], [ "c", "Component_1" ] ], "coords": "Coordinates", "label": "basic", "primary_owner": [ "a", "Pixel Axis 0 [x]", "World 0", "b", "c" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1_0", "Subset 2_0" ], "uuid": "dde080dc-a65a-4988-bed9-c0cb90ff91a8" }, "c": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "c" } }glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/tests/test_data_viewer.py0000644000175000017500000011070613752534424026200 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import os from collections import Counter import pytest import numpy as np from numpy.testing import assert_allclose, assert_equal from glue.config import colormaps from glue.core.message import SubsetUpdateMessage from glue.core import HubListener, Data from glue.core.roi import XRangeROI, RectangularROI, CircularROI from glue.core.roi_pretransforms import ProjectionMplTransform from glue.core.subset import RoiSubsetState, AndState from glue import core from glue.core.component_id import ComponentID from glue.utils.qt import combo_as_string, process_events from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer from glue.core.state import GlueUnSerializer from glue.app.qt.layer_tree_widget import LayerTreeWidget from glue.app.qt import GlueApplication from ..data_viewer import ScatterViewer DATA = os.path.join(os.path.dirname(__file__), 'data') fullsphere_projections = ['aitoff', 'hammer', 'lambert', 'mollweide'] class TestScatterCommon(BaseTestMatplotlibDataViewer): def init_data(self): return Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=['a', 'b', 'c', 'a']) viewer_cls = ScatterViewer class TestScatterViewer(object): def setup_method(self, method): self.data = Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=[3.2, 3.3, 3.4, 3.5], z=['a', 'b', 'c', 'a']) self.data_2d = Data(label='d2', a=[[1, 2], [3, 4]], b=[[5, 6], [7, 8]], x=[[3, 5], [5.4, 1]], y=[[1.2, 4], [7, 8]]) self.app = GlueApplication() self.session = self.app.session self.hub = self.session.hub self.data_collection = self.session.data_collection self.data_collection.append(self.data) self.data_collection.append(self.data_2d) self.viewer = self.app.new_data_viewer(ScatterViewer) def teardown_method(self, method): self.viewer.close() self.viewer = None self.app.close() self.app = None def test_basic(self): viewer_state = self.viewer.state # Check defaults when we add data self.viewer.add_data(self.data) assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' assert viewer_state.x_att is self.data.id['x'] assert_allclose(viewer_state.x_min, -1.1 - 0.18) assert_allclose(viewer_state.x_max, 3.4 + 0.18) assert viewer_state.y_att is self.data.id['y'] assert_allclose(viewer_state.y_min, 3.2 - 0.012) assert_allclose(viewer_state.y_max, 3.5 + 0.012) assert not viewer_state.x_log assert not viewer_state.y_log assert len(viewer_state.layers) == 1 # Change to categorical component and check new values viewer_state.y_att = self.data.id['z'] assert viewer_state.x_att is self.data.id['x'] assert_allclose(viewer_state.x_min, -1.1 - 0.18) assert_allclose(viewer_state.x_max, 3.4 + 0.18) assert viewer_state.y_att is self.data.id['z'] assert_allclose(viewer_state.y_min, -0.5 - 0.12) assert_allclose(viewer_state.y_max, 2.5 + 0.12) assert not viewer_state.x_log assert not viewer_state.y_log def test_flip(self): viewer_state = self.viewer.state self.viewer.add_data(self.data) assert_allclose(viewer_state.x_min, -1.1 - 0.18) assert_allclose(viewer_state.x_max, 3.4 + 0.18) self.viewer.options_widget().button_flip_x.click() assert_allclose(viewer_state.x_max, -1.1 - 0.18) assert_allclose(viewer_state.x_min, 3.4 + 0.18) assert_allclose(viewer_state.y_min, 3.2 - 0.012) assert_allclose(viewer_state.y_max, 3.5 + 0.012) self.viewer.options_widget().button_flip_y.click() assert_allclose(viewer_state.y_max, 3.2 - 0.012) assert_allclose(viewer_state.y_min, 3.5 + 0.012) def test_remove_data(self): self.viewer.add_data(self.data) assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' self.data_collection.remove(self.data) assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == '' assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == '' def test_update_component_updates_title(self): self.viewer.add_data(self.data) assert self.viewer.windowTitle() == '2D Scatter' self.viewer.state.x_att = self.data.id['y'] assert self.viewer.windowTitle() == '2D Scatter' def test_combo_updates_with_component_add(self): self.viewer.add_data(self.data) self.data.add_component([3, 4, 1, 2], 'a') assert self.viewer.state.x_att is self.data.id['x'] assert self.viewer.state.y_att is self.data.id['y'] assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:a:Coordinate components:Pixel Axis 0 [x]' assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:a:Coordinate components:Pixel Axis 0 [x]' def test_nonnumeric_first_component(self): # regression test for #208. Shouldn't complain if # first component is non-numerical data = core.Data() data.add_component(['a', 'b', 'c'], label='c1') data.add_component([1, 2, 3], label='c2') self.data_collection.append(data) self.viewer.add_data(data) def test_apply_roi(self): self.viewer.add_data(self.data) roi = RectangularROI(0, 3, 3.25, 3.45) assert len(self.viewer.layers) == 1 self.viewer.apply_roi(roi) assert len(self.viewer.layers) == 2 assert len(self.data.subsets) == 1 assert_allclose(self.data.subsets[0].to_mask(), [0, 1, 0, 0]) state = self.data.subsets[0].subset_state assert isinstance(state, RoiSubsetState) def test_apply_roi_categorical(self): viewer_state = self.viewer.state self.viewer.add_data(self.data) viewer_state.y_att = self.data.id['z'] roi = RectangularROI(0, 3, -0.4, 0.3) assert len(self.viewer.layers) == 1 self.viewer.apply_roi(roi) assert len(self.viewer.layers) == 2 assert len(self.data.subsets) == 1 assert_allclose(self.data.subsets[0].to_mask(), [0, 0, 0, 1]) state = self.data.subsets[0].subset_state assert isinstance(state, AndState) def test_apply_roi_empty(self): # Make sure that doing an ROI selection on an empty viewer doesn't # produce error messsages roi = XRangeROI(-0.2, 0.1) self.viewer.apply_roi(roi) def test_axes_labels(self): viewer_state = self.viewer.state self.viewer.add_data(self.data) assert self.viewer.axes.get_xlabel() == 'x' assert self.viewer.axes.get_ylabel() == 'y' viewer_state.x_log = True assert self.viewer.axes.get_xlabel() == 'Log x' assert self.viewer.axes.get_ylabel() == 'y' viewer_state.x_att = self.data.id['y'] assert self.viewer.axes.get_xlabel() == 'y' assert self.viewer.axes.get_ylabel() == 'y' viewer_state.y_log = True assert self.viewer.axes.get_xlabel() == 'y' assert self.viewer.axes.get_ylabel() == 'Log y' def test_component_replaced(self): # regression test for 508 - if a component ID is replaced, we should # make sure that the component ID is selected if the old component ID # was selected self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['x'] test = ComponentID('test') self.data.update_id(self.viewer.state.x_att, test) assert self.viewer.state.x_att is test assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:test:y:z:Coordinate components:Pixel Axis 0 [x]' def test_nan_component(self): # regression test for case when all values are NaN in a component data = core.Data() data.add_component([np.nan, np.nan, np.nan], label='c1') self.data_collection.append(data) self.viewer.add_data(data) def test_density_map(self): kwargs = dict(range=[(-5, 5), (-5, 5)], bins=(2, 2)) self.viewer.add_data(self.data) self.viewer.state.layers[0].points_mode = 'auto' assert self.viewer.layers[0].state.compute_density_map(**kwargs).sum() == 0 self.viewer.state.layers[0].points_mode = 'density' assert self.viewer.layers[0].state.compute_density_map(**kwargs).sum() == 4 self.viewer.state.layers[0].points_mode = 'markers' assert self.viewer.layers[0].state.compute_density_map(**kwargs).sum() == 0 def test_density_map_color(self): # Regression test to make sure things don't crash when changing # back to markers if the color mode is cmap self.viewer.add_data(self.data) self.viewer.state.layers[0].points_mode = 'density' self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].size_mode = 'Linear' self.viewer.state.layers[0].points_mode = 'markers' self.viewer.state.layers[0].points_mode = 'density' @pytest.mark.parametrize('protocol', [0, 1]) def test_session_back_compat(self, protocol): filename = os.path.join(DATA, 'scatter_v{0}.glu'.format(protocol)) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') dc = ga.session.data_collection assert len(dc) == 1 assert dc[0].label == 'basic' viewer1 = ga.viewers[0][0] assert len(viewer1.state.layers) == 3 assert viewer1.state.x_att is dc[0].id['a'] assert viewer1.state.y_att is dc[0].id['b'] assert_allclose(viewer1.state.x_min, -1.04) assert_allclose(viewer1.state.x_max, 1.04) assert_allclose(viewer1.state.y_min, 1.98) assert_allclose(viewer1.state.y_max, 3.02) assert not viewer1.state.x_log assert not viewer1.state.y_log assert viewer1.state.layers[0].visible assert viewer1.state.layers[1].visible assert viewer1.state.layers[2].visible viewer2 = ga.viewers[0][1] assert len(viewer2.state.layers) == 3 assert viewer2.state.x_att is dc[0].id['a'] assert viewer2.state.y_att is dc[0].id['c'] assert_allclose(viewer2.state.x_min, 9.5e-6) assert_allclose(viewer2.state.x_max, 1.05) assert_allclose(viewer2.state.y_min, 0.38) assert_allclose(viewer2.state.y_max, 5.25) assert viewer2.state.x_log assert viewer2.state.y_log assert viewer2.state.layers[0].visible assert not viewer2.state.layers[1].visible assert viewer2.state.layers[2].visible viewer3 = ga.viewers[0][2] assert len(viewer3.state.layers) == 3 assert viewer3.state.x_att is dc[0].id['b'] assert viewer3.state.y_att is dc[0].id['a'] assert_allclose(viewer3.state.x_min, 0) assert_allclose(viewer3.state.x_max, 5) assert_allclose(viewer3.state.y_min, -5) assert_allclose(viewer3.state.y_max, 5) assert not viewer3.state.x_log assert not viewer3.state.y_log assert viewer3.state.layers[0].visible assert viewer3.state.layers[1].visible assert not viewer3.state.layers[2].visible ga.close() def test_session_line_back_compat(self): # Backward-compatibility for v0.11 files in which the line and scatter # plots were defined as separate styles. filename = os.path.join(DATA, 'scatter_and_line_v1.glu') with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') dc = ga.session.data_collection assert len(dc) == 1 assert dc[0].label == 'table' viewer1 = ga.viewers[0][0] assert len(viewer1.state.layers) == 1 assert viewer1.state.x_att is dc[0].id['a'] assert viewer1.state.y_att is dc[0].id['b'] assert viewer1.state.layers[0].markers_visible assert not viewer1.state.layers[0].line_visible viewer1 = ga.viewers[0][1] assert len(viewer1.state.layers) == 1 assert viewer1.state.x_att is dc[0].id['a'] assert viewer1.state.y_att is dc[0].id['b'] assert not viewer1.state.layers[0].markers_visible assert viewer1.state.layers[0].line_visible ga.close() def test_save_svg(self, tmpdir): # Regression test for a bug in AxesCache that caused SVG saving to # fail (because renderer.buffer_rgba did not exist) self.viewer.add_data(self.data) filename = tmpdir.join('test.svg').strpath self.viewer.axes.figure.savefig(filename) def test_2d(self): viewer_state = self.viewer.state self.viewer.add_data(self.data_2d) assert viewer_state.x_att is self.data_2d.id['a'] assert_allclose(viewer_state.x_min, 1 - 0.12) assert_allclose(viewer_state.x_max, 4 + 0.12) assert viewer_state.y_att is self.data_2d.id['b'] assert_allclose(viewer_state.y_min, 5 - 0.12) assert_allclose(viewer_state.y_max, 8 + 0.12) assert self.viewer.layers[0].plot_artist.get_xdata().shape == (4,) def test_apply_roi_single(self): # Regression test for a bug that caused mode.update to be called # multiple times and resulted in all other viewers receiving many # messages regarding subset updates (this occurred when multiple) # datasets were present. layer_tree = LayerTreeWidget(session=self.session) layer_tree.set_checkable(False) layer_tree.setup(self.data_collection) layer_tree.bind_selection_to_edit_subset() class Client(HubListener): def __init__(self, *args, **kwargs): super(Client, self).__init__(*args, **kwargs) self.count = Counter() def ping(self, message): self.count[message.sender] += 1 def register_to_hub(self, hub): hub.subscribe(self, SubsetUpdateMessage, handler=self.ping) d1 = Data(a=[1, 2, 3], label='d3') d2 = Data(b=[1, 2, 3], label='d4') d3 = Data(c=[1, 2, 3], label='d5') d4 = Data(d=[1, 2, 3], label='d6') self.data_collection.append(d1) self.data_collection.append(d2) self.data_collection.append(d3) self.data_collection.append(d4) client = Client() client.register_to_hub(self.hub) self.viewer.add_data(d1) self.viewer.add_data(d3) roi = XRangeROI(2.5, 3.5) self.viewer.apply_roi(roi) for subset in client.count: assert client.count[subset] == 1 @pytest.mark.parametrize('ndim', [1, 2]) def test_all_options(self, ndim): # This test makes sure that all the code for the different scatter modes # gets run, though does not check the result. viewer_state = self.viewer.state if ndim == 1: data = self.data elif ndim == 2: data = self.data_2d self.viewer.add_data(data) layer_state = viewer_state.layers[0] layer_state.style = 'Scatter' layer_state.size_mode = 'Linear' layer_state.size_att = data.id['y'] layer_state.size_vmin = 1.2 layer_state.size_vmax = 4. layer_state.size_scaling = 2 layer_state.cmap_mode = 'Linear' layer_state.cmap_att = data.id['x'] layer_state.cmap_vmin = -1 layer_state.cmap_vmax = 2. layer_state.cmap = colormaps.members[3][1] # Check inverting works layer_state.cmap_vmin = 3. layer_state.size_mode = 'Fixed' layer_state.xerr_visible = True layer_state.xerr_att = data.id['x'] layer_state.yerr_visible = True layer_state.yerr_att = data.id['y'] layer_state.style = 'Line' layer_state.linewidth = 3 layer_state.linestyle = 'dashed' def test_session_categorical(self, tmpdir): def visible_xaxis_labels(ax): # Due to a bug in Matplotlib the labels returned outside the field # of view may be incorrect: https://github.com/matplotlib/matplotlib/issues/9397 pos = ax.xaxis.get_ticklocs() labels = [tick.get_text() for tick in ax.xaxis.get_ticklabels()] xmin, xmax = ax.get_xlim() return [labels[i] for i in range(len(pos)) if pos[i] >= xmin and pos[i] <= xmax] # Regression test for a bug that caused a restored scatter viewer # with a categorical component to not show the categorical labels # as tick labels. filename = tmpdir.join('test_session_categorical.glu').strpath self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['z'] process_events() assert visible_xaxis_labels(self.viewer.axes) == ['a', 'b', 'c'] self.session.application.save_session(filename) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') dc = ga.session.data_collection viewer = ga.viewers[0][0] assert viewer.state.x_att is dc[0].id['z'] assert visible_xaxis_labels(self.viewer.axes) == ['a', 'b', 'c'] ga.close() def test_enable_disable_components_combo(self): # Regression test for a bug that caused an error when turning off pixel # components from combo boxes. self.viewer.add_data(self.data) self.data['a'] = self.data.id['x'] + 5 self.viewer.state.x_att_helper.pixel_coord = True self.viewer.state.x_att = self.data.pixel_component_ids[0] self.viewer.state.x_att_helper.pixel_coord = False def test_datetime64_support(self, tmpdir): self.data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1') self.data.add_component(np.array([200, 300, 400, 500], dtype='M8[D]'), 't2') self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['t1'] self.viewer.state.y_att = self.data.id['y'] # Matplotlib deals with dates by converting them to the number of days # since 01-01-0001, so we can check that the limits are correctly # converted (and not 100 to 400) assert self.viewer.axes.get_xlim() == (719251.0, 719575.0) assert self.viewer.axes.get_ylim() == (3.2 - 0.012, 3.5 + 0.012) # Apply an ROI selection in plotting coordinates roi = RectangularROI(xmin=719313, xmax=719513, ymin=3, ymax=4) self.viewer.apply_roi(roi) # Check that the two middle elements are selected assert_equal(self.data.subsets[0].to_mask(), [0, 1, 1, 0]) # Now do the same with the y axis self.viewer.state.y_att = self.data.id['t2'] assert self.viewer.axes.get_xlim() == (719251.0, 719575.0) assert self.viewer.axes.get_ylim() == (719351.0, 719675.0) # Apply an ROI selection in plotting coordinates edit = self.session.edit_subset_mode edit.edit_subset = [] roi = CircularROI(xc=719463, yc=719563, radius=200) self.viewer.apply_roi(roi) assert_equal(self.data.subsets[1].to_mask(), [0, 1, 1, 1]) # Make sure that the Qt labels look ok self.viewer.state.y_att = self.data.id['y'] options = self.viewer.options_widget().ui assert options.valuetext_x_min.text() == '1970-03-30' assert options.valuetext_x_max.text() == '1971-02-17' assert options.valuetext_y_min.text() == '3.188' assert options.valuetext_y_max.text() == '3.512' # Make sure that we can set the xmin/xmax to a string date assert_equal(self.viewer.state.x_min, np.datetime64('1970-03-30', 'D')) options.valuetext_x_min.setText('1970-04-14') options.valuetext_x_min.editingFinished.emit() assert self.viewer.axes.get_xlim() == (719266.0, 719575.0) assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D')) # Make sure that everything works fine after saving/reloading filename = tmpdir.join('test_datetime64.glu').strpath self.session.application.save_session(filename) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') viewer = ga.viewers[0][0] options = viewer.options_widget().ui assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D')) assert options.valuetext_x_min.text() == '1970-04-14' assert options.valuetext_x_max.text() == '1971-02-17' assert options.valuetext_y_min.text() == '3.188' assert options.valuetext_y_max.text() == '3.512' ga.close() def test_datetime64_disabled(self, capsys): # Make sure that datetime components aren't options for the vector and # error markers. data = Data(label='test') data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1') data.add_component(np.array([200, 300, 400, 500], dtype='M8[D]'), 't2') data.add_component(np.array([200., 300., 400., 500.]), 'x') data.add_component(np.array([200., 300., 400., 500.]), 'y') self.data_collection.append(data) self.viewer.add_data(data) self.viewer.state.x_att = data.id['x'] self.viewer.state.y_att = data.id['y'] self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap_att = data.id['x'] self.viewer.state.layers[0].size_mode = 'Linear' self.viewer.state.layers[0].size_att = data.id['y'] self.viewer.state.layers[0].vector_visible = True self.viewer.state.layers[0].xerr_visible = True self.viewer.state.layers[0].yerr_visible = True process_events() self.viewer.state.x_att = data.id['t1'] self.viewer.state.y_att = data.id['t2'] process_events() # We use capsys here because the # error is otherwise only apparent in stderr. out, err = capsys.readouterr() assert out.strip() == "" assert err.strip() == "" def test_density_map_incompatible_subset(self, capsys): # Regression test for a bug that caused the scatter viewer to crash # if subset for density map was incompatible. data2 = Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=[3.2, 3.3, 3.4, 3.5], z=['a', 'b', 'c', 'a']) self.data_collection.append(data2) self.viewer.add_data(self.data) self.viewer.add_data(data2) self.data_collection.new_subset_group('test', self.data.id['x'] > 1) for layer in self.viewer.state.layers: layer.density_map = True self.viewer.figure.canvas.draw() process_events() assert self.viewer.layers[0].enabled assert not self.viewer.layers[1].enabled assert self.viewer.layers[2].enabled assert not self.viewer.layers[3].enabled def test_density_map_line_error_vector(self, capsys): # Make sure that we don't allow/show lines/errors/vectors # if in density map mode. self.viewer.add_data(self.data) self.viewer.state.layers[0].line_visible = True self.viewer.state.layers[0].xerr_visible = True self.viewer.state.layers[0].yerr_visible = True self.viewer.state.layers[0].vector_visible = True # Setting density_map to True resets the visibility of # lines/errors/vectors. self.viewer.state.layers[0].density_map = True assert not self.viewer.state.layers[0].line_visible assert not self.viewer.state.layers[0].xerr_visible assert not self.viewer.state.layers[0].yerr_visible assert not self.viewer.state.layers[0].vector_visible def test_legend(self): viewer_state = self.viewer.state self.viewer.add_data(self.data) viewer_state.legend.visible = True handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 1 assert labels[0] == 'd1' self.data_collection.new_subset_group('test', self.data.id['x'] > 1) assert len(viewer_state.layers) == 2 handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 2 assert labels[1] == 'test' print(handles[1][0]) # assert handles[1][0].get_color() == viewer_state.layers[1].state.color # Add a non visible layer data2 = Data(label='d2', x=[3.4, 2.3, -1.1, 0.3], y=[3.2, 3.3, 3.4, 3.5]) self.data_collection.append(data2) self.viewer.add_data(data2) assert len(viewer_state.layers) == 4 # 'd2' is not enabled (no linked component) handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 2 def test_changing_plot_modes(self): viewer_state = self.viewer.state viewer_state.plot_mode = 'polar' assert 'polar' in str(type(self.viewer.axes)).lower() viewer_state.plot_mode = 'aitoff' assert 'aitoff' in str(type(self.viewer.axes)).lower() viewer_state.plot_mode = 'hammer' assert 'hammer' in str(type(self.viewer.axes)).lower() viewer_state.plot_mode = 'lambert' assert 'lambert' in str(type(self.viewer.axes)).lower() viewer_state.plot_mode = 'mollweide' assert 'mollweide' in str(type(self.viewer.axes)).lower() def test_limit_log_set_polar(self): self.viewer.add_data(self.data) viewer_state = self.viewer.state viewer_state.plot_mode = "polar" axes = self.viewer.axes viewer_state.x_min = 0.5 viewer_state.x_max = 1.5 assert_allclose(axes.get_xlim(), [0.5, 1.5]) viewer_state.y_min = -2.5 viewer_state.y_max = 2.5 assert_allclose(axes.get_ylim(), [-2.5, 2.5]) viewer_state.y_log = True assert axes.get_yscale() == 'log' def test_limit_set_fullsphere(self): # Make sure that the full-sphere projections ignore instead of throwing exceptions self.viewer.add_data(self.data) viewer_state = self.viewer.state for proj in fullsphere_projections: viewer_state.plot_mode = proj error_msg = 'Issue with {} projection'.format(proj) axes = self.viewer.axes viewer_state.x_min = 0.5 viewer_state.x_max = 1.5 viewer_state.y_min = -2.5 viewer_state.y_max = 2.5 assert_allclose(axes.get_xlim(), [-np.pi, np.pi], err_msg=error_msg) assert_allclose(axes.get_ylim(), [-np.pi / 2, np.pi / 2], err_msg=error_msg) def test_changing_mode_limits(self): self.viewer.add_data(self.data) viewer_state = self.viewer.state old_xmin = viewer_state.x_min old_xmax = viewer_state.x_max old_ymin = viewer_state.y_min old_ymax = viewer_state.y_max # Make sure limits are reset first viewer_state.x_max += 3 viewer_state.plot_mode = 'polar' assert_allclose(viewer_state.x_min, old_xmin) assert_allclose(viewer_state.x_max, old_xmax) assert_allclose(self.viewer.axes.get_xlim(), [old_xmin, old_xmax]) assert_allclose(viewer_state.y_min, old_ymin) assert_allclose(viewer_state.y_max, old_ymax) assert_allclose(self.viewer.axes.get_ylim(), [old_ymin, old_ymax]) viewer_state.plot_mode = 'rectilinear' assert_allclose(viewer_state.x_min, old_xmin) assert_allclose(viewer_state.x_max, old_xmax) assert_allclose(self.viewer.axes.get_xlim(), [old_xmin, old_xmax]) assert_allclose(viewer_state.y_min, old_ymin) assert_allclose(viewer_state.y_max, old_ymax) assert_allclose(self.viewer.axes.get_ylim(), [old_ymin, old_ymax]) for proj in fullsphere_projections: viewer_state.plot_mode = 'rectilinear' viewer_state.plot_mode = proj error_msg = 'Issue with {} projection'.format(proj) assert_allclose(viewer_state.x_min, -np.pi) assert_allclose(viewer_state.x_max, np.pi) assert_allclose(self.viewer.axes.get_xlim(), [-np.pi, np.pi], err_msg=error_msg) assert_allclose(viewer_state.y_min, -np.pi / 2) assert_allclose(viewer_state.y_max, np.pi / 2) assert_allclose(self.viewer.axes.get_ylim(), [-np.pi / 2, np.pi / 2], err_msg=error_msg) def test_changing_mode_log(self): # Test to make sure we reset the log axes to false when changing modes to prevent problems self.viewer.add_data(self.data) viewer_state = self.viewer.state viewer_state.x_log = True viewer_state.y_log = True viewer_state.plot_mode = 'polar' assert not viewer_state.x_log assert not viewer_state.y_log assert self.viewer.axes.get_xscale() == 'linear' assert self.viewer.axes.get_yscale() == 'linear' viewer_state.y_log = True viewer_state.plot_mode = 'rectilinear' assert not viewer_state.x_log assert not viewer_state.y_log assert self.viewer.axes.get_xscale() == 'linear' assert self.viewer.axes.get_yscale() == 'linear' for proj in fullsphere_projections: viewer_state.plot_mode = 'rectilinear' viewer_state.x_log = True viewer_state.y_log = True viewer_state.plot_mode = proj error_msg = 'Issue with {} projection'.format(proj) assert not viewer_state.x_log, error_msg assert not viewer_state.y_log, error_msg assert self.viewer.axes.get_xscale() == 'linear', error_msg assert self.viewer.axes.get_yscale() == 'linear', error_msg def test_full_circle_utility(self): # Make sure that the full circle function behaves well self.viewer.add_data(self.data) viewer_state = self.viewer.state old_xmin = viewer_state.x_min old_xmax = viewer_state.x_max old_ymin = viewer_state.y_min old_ymax = viewer_state.y_max viewer_state.full_circle() assert_allclose([viewer_state.x_min, viewer_state.x_max], [old_xmin, old_xmax]) assert_allclose([viewer_state.y_min, viewer_state.y_max], [old_ymin, old_ymax]) viewer_state.plot_mode = 'polar' viewer_state.full_circle() assert_allclose([viewer_state.x_min, viewer_state.x_max], [0, 2 * np.pi]) assert_allclose([viewer_state.y_min, viewer_state.y_max], [old_ymin, old_ymax]) for proj in fullsphere_projections: error_msg = 'Issue with {} projection'.format(proj) viewer_state.plot_mode = proj viewer_state.full_circle() assert_allclose([viewer_state.x_min, viewer_state.x_max], [-np.pi, np.pi], err_msg=error_msg) assert_allclose([viewer_state.y_min, viewer_state.y_max], [-np.pi / 2, np.pi / 2], err_msg=error_msg) def test_limits_log_widget_polar_cartesian(self): ui = self.viewer.options_widget().ui viewer_state = self.viewer.state viewer_state.plot_mode = 'polar' assert not ui.bool_x_log.isEnabled() assert not ui.bool_x_log_.isEnabled() assert ui.bool_y_log.isEnabled() assert ui.bool_y_log_.isEnabled() assert ui.valuetext_x_min.isEnabled() assert ui.button_flip_x.isEnabled() assert ui.valuetext_x_max.isEnabled() assert ui.valuetext_y_min.isEnabled() assert ui.button_flip_y.isEnabled() assert ui.valuetext_y_max.isEnabled() assert not ui.button_full_circle.isHidden() viewer_state.plot_mode = 'rectilinear' assert ui.bool_x_log.isEnabled() assert ui.bool_x_log_.isEnabled() assert ui.bool_y_log.isEnabled() assert ui.bool_y_log_.isEnabled() assert ui.valuetext_x_min.isEnabled() assert ui.button_flip_x.isEnabled() assert ui.valuetext_x_max.isEnabled() assert ui.valuetext_y_min.isEnabled() assert ui.button_flip_y.isEnabled() assert ui.valuetext_y_max.isEnabled() assert ui.button_full_circle.isHidden() assert ui.button_full_circle.isHidden() def test_limits_log_widget_fullsphere(self): ui = self.viewer.options_widget().ui viewer_state = self.viewer.state for proj in fullsphere_projections: error_msg = 'Issue with {} projection'.format(proj) viewer_state.plot_mode = proj not ui.bool_x_log.isEnabled() assert not ui.bool_x_log_.isEnabled(), error_msg assert not ui.bool_y_log.isEnabled(), error_msg assert not ui.bool_y_log_.isEnabled(), error_msg assert not ui.valuetext_x_min.isEnabled(), error_msg assert not ui.button_flip_x.isEnabled(), error_msg assert not ui.valuetext_x_max.isEnabled(), error_msg assert not ui.valuetext_y_min.isEnabled(), error_msg assert not ui.button_flip_y.isEnabled(), error_msg assert not ui.valuetext_y_max.isEnabled(), error_msg assert ui.button_full_circle.isHidden(), error_msg viewer_state.plot_mode = 'rectilinear' assert ui.bool_x_log.isEnabled() assert ui.bool_x_log_.isEnabled() assert ui.bool_y_log.isEnabled() assert ui.bool_y_log_.isEnabled() assert ui.valuetext_x_min.isEnabled() assert ui.button_flip_x.isEnabled() assert ui.valuetext_x_max.isEnabled() assert ui.valuetext_y_min.isEnabled() assert ui.button_flip_y.isEnabled() assert ui.valuetext_y_max.isEnabled() assert ui.button_full_circle.isHidden() def test_apply_roi_polar(self): self.viewer.add_data(self.data) viewer_state = self.viewer.state roi = RectangularROI(0, 0.5, 0, 0.5) viewer_state.plot_mode = 'polar' viewer_state.full_circle() assert len(self.viewer.layers) == 1 self.viewer.apply_roi(roi) assert len(self.viewer.layers) == 2 assert len(self.data.subsets) == 1 assert_allclose(self.data.subsets[0].to_mask(), [1, 0, 0, 0]) state = self.data.subsets[0].subset_state assert isinstance(state, RoiSubsetState) assert state.pretransform pretrans = state.pretransform assert isinstance(pretrans, ProjectionMplTransform) assert pretrans._state['projection'] == 'polar' assert_allclose(pretrans._state['x_lim'], [viewer_state.x_min, viewer_state.x_max]) assert_allclose(pretrans._state['y_lim'], [viewer_state.y_min, viewer_state.y_max]) assert pretrans._state['x_scale'] == 'linear' assert pretrans._state['y_scale'] == 'linear' self.data.subsets[0].delete() viewer_state.y_log = True self.viewer.apply_roi(roi) state = self.data.subsets[0].subset_state assert state.pretransform pretrans = state.pretransform assert isinstance(pretrans, ProjectionMplTransform) assert pretrans._state['y_scale'] == 'log' def test_apply_roi_fullsphere(self): self.viewer.add_data(self.data) viewer_state = self.viewer.state roi = RectangularROI(0, 0.5, 0, 0.5) for proj in fullsphere_projections: viewer_state.plot_mode = proj assert len(self.viewer.layers) == 1 self.viewer.apply_roi(roi) assert len(self.viewer.layers) == 2 assert len(self.data.subsets) == 1 subset = self.data.subsets[0] state = subset.subset_state assert isinstance(state, RoiSubsetState) assert state.pretransform pretrans = state.pretransform assert isinstance(pretrans, ProjectionMplTransform) assert pretrans._state['projection'] == proj assert_allclose(pretrans._state['x_lim'], [viewer_state.x_min, viewer_state.x_max]) assert_allclose(pretrans._state['y_lim'], [viewer_state.y_min, viewer_state.y_max]) assert pretrans._state['x_scale'] == 'linear' assert pretrans._state['y_scale'] == 'linear' subset.delete() glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/data_viewer.py0000644000175000017500000000273413752534424024000 0ustar noahfxnoahfxfrom glue.utils import defer_draw, decorate_all_methods from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer from glue.viewers.scatter.qt.layer_style_editor import ScatterLayerStyleEditor from glue.viewers.scatter.layer_artist import ScatterLayerArtist from glue.viewers.scatter.qt.options_widget import ScatterOptionsWidget from glue.viewers.scatter.state import ScatterViewerState from glue.viewers.scatter.viewer import MatplotlibScatterMixin __all__ = ['ScatterViewer'] @decorate_all_methods(defer_draw) class ScatterViewer(MatplotlibScatterMixin, MatplotlibDataViewer): LABEL = '2D Scatter' _layer_style_widget_cls = ScatterLayerStyleEditor _state_cls = ScatterViewerState _options_cls = ScatterOptionsWidget _data_artist_cls = ScatterLayerArtist _subset_artist_cls = ScatterLayerArtist tools = ['select:rectangle', 'select:xrange', 'select:yrange', 'select:circle', 'select:polygon'] def __init__(self, session, parent=None, state=None): proj = None if not state or not state.plot_mode else state.plot_mode MatplotlibDataViewer.__init__(self, session, parent=parent, state=state, projection=proj) MatplotlibScatterMixin.setup_callbacks(self) def limits_to_mpl(self, *args): # These projections throw errors if we try to set the limits if self.state.plot_mode in ['aitoff', 'hammer', 'lambert', 'mollweide']: return super().limits_to_mpl(*args) glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/options_widget.ui0000644000175000017500000002561213752534424024531 0ustar noahfxnoahfx Widget 0 0 332 152 2D Scatter 5 5 5 5 5 QTabWidget::North 0 false false false false General 10 10 10 10 10 5 Qt::Vertical 20 40 75 true x axis 75 true y axis QComboBox::AdjustToMinimumContentsLength log true Qt::Horizontal 40 5 QComboBox::AdjustToMinimumContentsLength log true 75 true type Limits 10 10 10 10 10 5 75 true x axis Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 log true 75 true y axis Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter padding: 0px padding: 0px log true Qt::Horizontal 40 5 true full circle Axes 5 5 5 5 Legend 5 5 5 5 AxesEditorWidget QWidget
glue.viewers.matplotlib.qt.axes_editor
LegendEditorWidget QWidget
glue.viewers.matplotlib.qt.legend_editor
glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/__init__.py0000644000175000017500000000020513502206677023233 0ustar noahfxnoahfxfrom .data_viewer import ScatterViewer # noqa def setup(): from glue.config import qt_client qt_client.add(ScatterViewer) glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/options_widget.py0000644000175000017500000000746013752534424024545 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui, fix_tab_widget_fontsize from glue.viewers.matplotlib.state import MatplotlibDataViewerState __all__ = ['ScatterOptionsWidget'] def _get_labels(proj): if proj == 'rectilinear': return 'x axis', 'y axis' elif proj == 'polar': return 'theta (rad)', 'radius' elif proj in ['aitoff', 'hammer', 'lambert', 'mollweide']: return 'long (rad)', 'lat (rad)' else: return 'axis 1', 'axis 2' class ScatterOptionsWidget(QtWidgets.QWidget): def __init__(self, viewer_state, session, parent=None): super(ScatterOptionsWidget, self).__init__(parent=parent) self.ui = load_ui('options_widget.ui', self, directory=os.path.dirname(__file__)) fix_tab_widget_fontsize(self.ui.tab_widget) self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) self._connections_axes = autoconnect_callbacks_to_qt(viewer_state, self.ui.axes_editor.ui) connect_kwargs = {'alpha': dict(value_range=(0, 1))} self._connections_legend = autoconnect_callbacks_to_qt(viewer_state.legend, self.ui.legend_editor.ui, connect_kwargs) self.viewer_state = viewer_state viewer_state.add_callback('x_att', self._update_x_attribute) viewer_state.add_callback('y_att', self._update_y_attribute) viewer_state.add_callback('plot_mode', self._update_plot_mode) self.ui.button_full_circle.setVisible(False) self.session = session self.ui.axes_editor.button_apply_all.clicked.connect(self._apply_all_viewers) def _apply_all_viewers(self): for tab in self.session.application.viewers: for viewer in tab: if isinstance(viewer.state, MatplotlibDataViewerState): viewer.state.update_axes_settings_from(self.viewer_state) def _update_x_attribute(self, *args): # If at least one of the components is categorical or a date, disable log button log_enabled = ('categorical' not in self.viewer_state.x_kinds and self.viewer_state.plot_mode not in ['aitoff', 'hammer', 'lambert', 'mollweide', 'polar']) self.ui.bool_x_log.setEnabled(log_enabled) self.ui.bool_x_log_.setEnabled(log_enabled) if not log_enabled: self.ui.bool_x_log.setChecked(False) self.ui.bool_x_log_.setChecked(False) def _update_y_attribute(self, *args): # If at least one of the components is categorical or a date, disable log button log_enabled = ('categorical' not in self.viewer_state.y_kinds and self.viewer_state.plot_mode not in ['aitoff', 'hammer', 'lambert', 'mollweide']) self.ui.bool_y_log.setEnabled(log_enabled) self.ui.bool_y_log_.setEnabled(log_enabled) if not log_enabled: self.ui.bool_y_log.setChecked(False) self.ui.bool_y_log_.setChecked(False) def _update_plot_mode(self, *args): x_label, y_label = _get_labels(self.viewer_state.plot_mode) self.ui.x_lab.setText(x_label) self.ui.x_lab_2.setText(x_label) self.ui.y_lab.setText(y_label) self.ui.y_lab_2.setText(y_label) lim_enabled = self.viewer_state.plot_mode not in ['aitoff', 'hammer', 'lambert', 'mollweide'] is_polar = self.viewer_state.plot_mode == 'polar' self.ui.valuetext_x_min.setEnabled(lim_enabled) self.ui.button_flip_x.setEnabled(lim_enabled) self.ui.valuetext_x_max.setEnabled(lim_enabled) self.ui.valuetext_y_min.setEnabled(lim_enabled) self.ui.button_flip_y.setEnabled(lim_enabled) self.ui.valuetext_y_max.setEnabled(lim_enabled) self.ui.button_full_circle.setVisible(is_polar) self._update_x_attribute() self._update_y_attribute() glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/layer_style_editor.ui0000644000175000017500000007612513502206677025401 0ustar noahfxnoahfx Form 0 0 330 296 Form 5 5 5 5 0 Qt::ElideNone Color 10 3 10 3 10 5 100 Qt::Horizontal 75 true opacity Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 40 1 0 0 Qt::Vertical 20 40 QComboBox::AdjustToMinimumContentsLength 80 16777215 padding: 0px 75 true color Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true limits Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true colormap Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true attribute Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 QComboBox::AdjustToMinimumContentsLength Points 10 3 10 3 10 5 120 16777215 QComboBox::AdjustToMinimumContentsLength 75 true show Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true Qt::LeftToRight limits Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter padding: 0px 75 true dpi Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true Qt::LeftToRight attribute Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true Qt::LeftToRight scaling Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true Qt::LeftToRight size Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 40 1 Qt::Vertical 20 40 100 50 Qt::Horizontal 0 0 80 16777215 75 true type Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QComboBox::AdjustToMinimumContentsLength 75 true stretch Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 16777215 16777215 QComboBox::AdjustToMinimumContentsLength 75 true contrast Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 75 true fill Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Line 10 3 10 3 10 5 Qt::Horizontal 40 1 1 75 true width Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 75 true style Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true show Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Errors 10 3 10 3 10 5 Qt::Vertical 20 40 Qt::Horizontal 40 1 QComboBox::AdjustToMinimumContentsLength QComboBox::AdjustToMinimumContentsLength 75 true show y 75 true show x color: #BB2200 Warning: adding errorbars may be slow Qt::AlignCenter Vectors 10 3 10 3 10 5 Show arrow QComboBox::AdjustToMinimumContentsLength 75 true show Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QComboBox::AdjustToMinimumContentsLength 75 true vx Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 16777215 16777215 75 true arrow Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true type Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true vy Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true origin Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 40 1 Qt::Vertical 20 40 Qt::Horizontal 75 true scaling Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter color: #BB2200 Warning: adding vectors may be slow Qt::AlignCenter false QColorBox QLabel
glue.utils.qt.colors
QColormapCombo QComboBox
glue.utils.qt.colors
glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/qt/layer_style_editor.py0000644000175000017500000002416113752534424025406 0ustar noahfxnoahfximport os import numpy as np from qtpy import QtWidgets, QtGui from qtpy.QtCore import Qt from glue.core import BaseData from echo.qt import autoconnect_callbacks_to_qt, connect_value from glue.utils.qt import load_ui, fix_tab_widget_fontsize from glue.core.exceptions import IncompatibleAttribute class ScatterLayerStyleEditor(QtWidgets.QWidget): def __init__(self, layer, parent=None): super(ScatterLayerStyleEditor, self).__init__(parent=parent) self.ui = load_ui('layer_style_editor.ui', self, directory=os.path.dirname(__file__)) connect_kwargs = {'alpha': dict(value_range=(0, 1)), 'size_scaling': dict(value_range=(0.1, 10), log=True), 'density_contrast': dict(value_range=(0, 1)), 'vector_scaling': dict(value_range=(0.1, 10), log=True)} self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) self._connection_dpi = connect_value(layer.state.viewer_state, 'dpi', self.ui.value_dpi, value_range=(12, 144), log=True) fix_tab_widget_fontsize(self.ui.tab_widget) self.layer_state = layer.state self.layer_state.add_callback('markers_visible', self._update_markers_visible) self.layer_state.add_callback('line_visible', self._update_line_visible) self.layer_state.add_callback('xerr_visible', self._update_xerr_visible) self.layer_state.add_callback('yerr_visible', self._update_yerr_visible) self.layer_state.add_callback('vector_visible', self._update_vectors_visible) self.layer_state.add_callback('cmap_mode', self._update_cmap_mode) self.layer_state.add_callback('size_mode', self._update_size_mode) self.layer_state.add_callback('vector_mode', self._update_vector_mode) self.layer_state.add_callback('density_map', self._update_size_mode) self.layer_state.add_callback('density_map', self._update_warnings) self.layer_state.add_callback('density_map', self._update_checkboxes) self.layer_state.viewer_state.add_callback('x_att', self._update_checkboxes) self.layer_state.viewer_state.add_callback('y_att', self._update_checkboxes) if hasattr(self.layer_state.viewer_state, 'plot_mode'): self.layer_state.viewer_state.add_callback('plot_mode', self._update_vectors_visible) self.layer_state.add_callback('layer', self._update_warnings) self._update_markers_visible() self._update_line_visible() self._update_xerr_visible() self._update_yerr_visible() self._update_vectors_visible() self._update_size_mode() self._update_vector_mode() self._update_cmap_mode() self._update_checkboxes() self._update_warnings() def _update_warnings(self, *args): if self.layer_state.layer is None: n_points = 0 else: n_points = np.product(self.layer_state.layer.shape) warning = " (may be slow given data size)" for combo, threshold in [(self.ui.combosel_size_mode, 10000), (self.ui.combosel_cmap_mode, 50000)]: if n_points > threshold and not self.layer_state.density_map: for item in range(combo.count()): text = combo.itemText(item) if text != 'Fixed': combo.setItemText(item, text + warning) combo.setItemData(item, QtGui.QBrush(Qt.red), Qt.TextColorRole) else: for item in range(combo.count()): text = combo.itemText(item) if text != 'Fixed': if warning in text: combo.setItemText(item, text.replace(warning, '')) combo.setItemData(item, QtGui.QBrush(), Qt.TextColorRole) if n_points > 10000: self.ui.label_warning_errorbar.show() else: self.ui.label_warning_errorbar.hide() if n_points > 10000: self.ui.label_warning_vector.show() else: self.ui.label_warning_vector.hide() def _update_size_mode(self, size_mode=None): visible = not self.layer_state.density_map and not self.layer_state.size_mode == 'Fixed' self.ui.label_size_attribute.setVisible(visible) self.ui.combosel_size_att.setVisible(visible) self.ui.label_size_limits.setVisible(visible) self.ui.valuetext_size_vmin.setVisible(visible) self.ui.valuetext_size_vmax.setVisible(visible) self.ui.button_flip_size.setVisible(visible) visible = not self.layer_state.density_map and self.layer_state.size_mode == 'Fixed' self.ui.value_size.setVisible(visible) density = self.layer_state.density_map self.ui.value_dpi.setVisible(density) self.ui.label_dpi.setVisible(density) self.ui.label_stretch.setVisible(density) self.ui.combosel_stretch.setVisible(density) self.ui.value_density_contrast.setVisible(density) self.ui.label_contrast.setVisible(density) self.ui.combosel_size_mode.setVisible(not density) self.ui.value_size_scaling.setVisible(not density) self.ui.label_size_mode.setVisible(not density) self.ui.label_size_scaling.setVisible(not density) self.ui.label_fill.setVisible(not density) self.ui.bool_fill.setVisible(not density) def _update_markers_visible(self, *args): self.ui.combosel_size_mode.setEnabled(self.layer_state.markers_visible) self.ui.value_size.setEnabled(self.layer_state.markers_visible) self.ui.combosel_size_att.setEnabled(self.layer_state.markers_visible) self.ui.valuetext_size_vmin.setEnabled(self.layer_state.markers_visible) self.ui.valuetext_size_vmax.setEnabled(self.layer_state.markers_visible) self.ui.button_flip_size.setEnabled(self.layer_state.markers_visible) self.ui.value_size_scaling.setEnabled(self.layer_state.markers_visible) self.ui.value_dpi.setEnabled(self.layer_state.markers_visible) self.ui.combosel_stretch.setEnabled(self.layer_state.markers_visible) self.ui.label_size_scaling.setEnabled(self.layer_state.markers_visible) self.ui.combosel_points_mode.setEnabled(self.layer_state.markers_visible) self.ui.value_density_contrast.setEnabled(self.layer_state.markers_visible) def _update_checkboxes(self, *args): if isinstance(self.layer_state.layer, BaseData): layer = self.layer_state.layer else: layer = self.layer_state.layer.data try: x_datetime = layer.get_kind(self.layer_state.viewer_state.x_att) == 'datetime' except IncompatibleAttribute: x_datetime = False try: y_datetime = layer.get_kind(self.layer_state.viewer_state.y_att) == 'datetime' except IncompatibleAttribute: y_datetime = False for checkbox in [self.ui.bool_line_visible, self.ui.bool_xerr_visible, self.ui.bool_yerr_visible, self.ui.bool_vector_visible]: if self.layer_state.density_map: checkbox.setEnabled(False) checkbox.setToolTip('Not available with density map') else: if ((x_datetime and checkbox in (self.ui.bool_xerr_visible, self.ui.bool_vector_visible)) or (y_datetime and checkbox in (self.ui.bool_yerr_visible, self.ui.bool_vector_visible))): checkbox.setEnabled(False) checkbox.setToolTip('Not available when using datetime attribute') else: checkbox.setEnabled(True) checkbox.setToolTip('') def _update_line_visible(self, *args): self.ui.value_linewidth.setEnabled(self.layer_state.line_visible) self.ui.combosel_linestyle.setEnabled(self.layer_state.line_visible) def _update_xerr_visible(self, *args): self.ui.combosel_xerr_att.setEnabled(self.layer_state.xerr_visible) def _update_yerr_visible(self, *args): self.ui.combosel_yerr_att.setEnabled(self.layer_state.yerr_visible) def _update_vectors_visible(self, *args): viewer_state = self.layer_state.viewer_state is_rect = not hasattr(viewer_state, 'plot_mode') or viewer_state.plot_mode == 'rectilinear' self.ui.combosel_vector_mode.setEnabled(self.layer_state.vector_visible and is_rect) self.ui.combosel_vx_att.setEnabled(self.layer_state.vector_visible) self.ui.combosel_vy_att.setEnabled(self.layer_state.vector_visible) self.ui.value_vector_scaling.setEnabled(self.layer_state.vector_visible) self.ui.combosel_vector_origin.setEnabled(self.layer_state.vector_visible) self.ui.bool_vector_arrowhead.setEnabled(self.layer_state.vector_visible) def _update_vector_mode(self, vector_mode=None): if self.layer_state.vector_mode == 'Cartesian': self.ui.label_vector_x.setText('vx') self.ui.label_vector_y.setText('vy') elif self.layer_state.vector_mode == 'Polar': self.ui.label_vector_x.setText('angle (deg)') self.ui.label_vector_y.setText('length') def _update_cmap_mode(self, cmap_mode=None): if self.layer_state.cmap_mode == 'Fixed': self.ui.label_cmap_attribute.hide() self.ui.combosel_cmap_att.hide() self.ui.label_cmap_limits.hide() self.ui.valuetext_cmap_vmin.hide() self.ui.valuetext_cmap_vmax.hide() self.ui.button_flip_cmap.hide() self.ui.combodata_cmap.hide() self.ui.label_colormap.hide() self.ui.color_color.show() else: self.ui.label_cmap_attribute.show() self.ui.combosel_cmap_att.show() self.ui.label_cmap_limits.show() self.ui.valuetext_cmap_vmin.show() self.ui.valuetext_cmap_vmax.show() self.ui.button_flip_cmap.show() self.ui.combodata_cmap.show() self.ui.label_colormap.show() self.ui.color_color.hide() glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/python_export.py0000644000175000017500000002151613752534424024003 0ustar noahfxnoahfxfrom glue.viewers.common.python_export import code, serialize_options def python_export_scatter_layer(layer, *args): if len(layer.mpl_artists) == 0 or not layer.enabled or not layer.visible: return [], None script = "" imports = ["import numpy as np"] script += "layer_handles = [] # for legend" script += "# Get main data values\n" script += "x = layer_data['{0}']\n".format(layer._viewer_state.x_att.label) script += "y = layer_data['{0}']\n".format(layer._viewer_state.y_att.label) script += "keep = ~np.isnan(x) & ~np.isnan(y)\n\n" if layer.state.cmap_mode == 'Linear': script += "# Set up colors\n" script += "colors = layer_data['{0}']\n".format(layer.state.cmap_att.label) script += "cmap_vmin = {0}\n".format(layer.state.cmap_vmin) script += "cmap_vmax = {0}\n".format(layer.state.cmap_vmax) script += "keep &= ~np.isnan(colors)\n" script += "colors = plt.cm.{0}((colors - cmap_vmin) / (cmap_vmax - cmap_vmin))\n\n".format(layer.state.cmap.name) if layer.state.size_mode == 'Linear': imports = ['import numpy as np'] script += "# Set up size values\n" script += "sizes = layer_data['{0}']\n".format(layer.state.size_att.label) script += "size_vmin = {0}\n".format(layer.state.size_vmin) script += "size_vmax = {0}\n".format(layer.state.size_vmax) script += "keep &= ~np.isnan(sizes)\n" script += "sizes = 30 * (np.clip((sizes - size_vmin) / (size_vmax - size_vmin), 0, 1) * 0.95 + 0.05) * {0}\n\n".format(layer.state.size_scaling) if layer.state.markers_visible: if layer.state.density_map: imports += ["from mpl_scatter_density import ScatterDensityArtist"] imports += ["from glue.viewers.scatter.layer_artist import DensityMapLimits, STRETCHES"] imports += ["from astropy.visualization import ImageNormalize"] script += "density_limits = DensityMapLimits()\n" script += "density_limits.contrast = {0}\n\n".format(layer.state.density_contrast) options = dict(alpha=layer.state.alpha, zorder=layer.state.zorder, dpi=layer._viewer_state.dpi) if layer.state.cmap_mode == 'Fixed': options['color'] = layer.state.color options['vmin'] = code('density_limits.min') options['vmax'] = code('density_limits.max') options['norm'] = code("ImageNormalize(stretch=STRETCHES['{0}']())".format(layer.state.stretch)) else: options['c'] = code("layer_data['{0}']".format(layer.state.cmap_att.label)) options['cmap'] = code("plt.cm.{0}".format(layer.state.cmap.name)) options['vmin'] = layer.state.cmap_vmin options['vmax'] = layer.state.cmap_vmax script += "density = ScatterDensityArtist(ax, x, y, {0})\n".format(serialize_options(options)) script += "ax.add_artist(density)\n\n" # legend imports += ["from matplotlib.lines import Line2D"] options = dict(ms=layer.state.size, alpha=layer.state.alpha, color=layer.state.color) script += "layer_handles.append(\n" script += " Line2D([0, ], [0, ],\n" script += " marker='.', linestyle='none',\n" script += " {0}))\n".format(serialize_options(options)) else: if layer._use_plot_artist(): options = dict(color=layer.state.color, markersize=layer.state.size * layer.state.size_scaling, alpha=layer.state.alpha, zorder=layer.state.zorder, label=layer.layer.label) if layer.state.fill: options['mec'] = 'none' else: options['mfc'] = 'none' script += "plot_artists = ax.plot(x[keep], y[keep], 'o', {0})\n".format(serialize_options(options)) script += "layer_handles.extend(plot_artists)\n\n" else: options = dict(alpha=layer.state.alpha, zorder=layer.state.zorder) if layer.state.cmap_mode == 'Fixed': options['facecolor'] = layer.state.color else: options['c'] = code('colors[keep]') if layer.state.size_mode == 'Fixed': options['s'] = code('{0} ** 2'.format(layer.state.size * layer.state.size_scaling)) else: options['s'] = code('sizes[keep] ** 2') if layer.state.fill: options['edgecolor'] = 'none' script += "scatter_artist = ax.scatter(x[keep], y[keep], {0})\n".format(serialize_options(options)) if not layer.state.fill: script += "scatter_artist.set_edgecolors(scatter_artist.get_facecolors())\n" script += "scatter_artist.set_facecolors('none')\n" script += "layer_handles.append(scatter_artist)\n\n" if layer.state.vector_visible: if layer.state.vx_att is not None and layer.state.vy_att is not None: imports += ['import numpy as np'] script += "# Get vector data\n" if layer.state.vector_mode == 'Polar': script += "angle = layer_data['{0}'][keep]\n".format(layer.state.vx_att.label) script += "length = layer_data['{0}'][keep]\n".format(layer.state.vy_att.label) script += "vx = length * np.cos(np.radians(angle))\n" script += "vy = length * np.sin(np.radians(angle))\n" else: script += "vx = layer_data['{0}'][keep]\n".format(layer.state.vx_att.label) script += "vy = layer_data['{0}'][keep]\n".format(layer.state.vy_att.label) if layer.state.vector_arrowhead: hw = 3 hl = 5 else: hw = 1 hl = 0 script += 'vmax = np.nanmax(np.hypot(vx, vy))\n\n' scale = code('{0} * vmax'.format(10 / layer.state.vector_scaling)) options = dict(units='width', pivot=layer.state.vector_origin, headwidth=hw, headlength=hl, scale_units='width', scale=scale, angles='xy', alpha=layer.state.alpha, zorder=layer.state.zorder) if layer.state.cmap_mode == 'Fixed': options['color'] = layer.state.color else: options['color'] = code('colors[keep]') script += "vector_artist = ax.quiver(x[keep], y[keep], vx, vy, {0})\n".format(serialize_options(options)) script += "layer_handles.append(vector_artist)\n\n" if layer.state.xerr_visible or layer.state.yerr_visible: if layer.state.xerr_visible and layer.state.xerr_att is not None: xerr = code("xerr[keep]") else: xerr = code("None") if layer.state.yerr_visible and layer.state.yerr_att is not None: yerr = code("yerr[keep]") else: yerr = code("None") options = dict(fmt='none', xerr=xerr, yerr=yerr, alpha=layer.state.alpha, zorder=layer.state.zorder) if layer.state.cmap_mode == 'Fixed': options['ecolor'] = layer.state.color else: options['ecolor'] = code('colors[keep]') script += "xerr = layer_data['{0}']\n".format(layer.state.xerr_att.label) script += "yerr = layer_data['{0}']\n".format(layer.state.yerr_att.label) script += "keep &= ~np.isnan(xerr) & ~np.isnan(yerr)\n" script += "error_artist = ax.errorbar(x[keep], y[keep], {0})\n".format(serialize_options(options)) script += "layer_handles.append(error_artist)\n\n" if layer.state.line_visible: options = dict(color=layer.state.color, linewidth=layer.state.linewidth, linestyle=layer.state.linestyle, alpha=layer.state.alpha, zorder=layer.state.zorder) if layer.state.cmap_mode == 'Fixed': script += "ax.plot(x[keep], y[keep], '-', {0})\n\n".format(serialize_options(options)) else: options['c'] = code('colors') imports.append("from glue.viewers.scatter.layer_artist import plot_colored_line") script += "line_collection = plot_colored_line(ax, x, y, {0})\n".format(serialize_options(options)) script += "layer_handles.append(line_collection)\n\n" script += "legend_handles.append(tuple(layer_handles))\n" script += "legend_labels.append(layer_data.label)\n\n" return imports, script.strip() glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/state.py0000644000175000017500000004336513752534424022207 0ustar noahfxnoahfx# -*- coding: utf-8 -*- import numpy as np from glue.core import BaseData, Subset from glue.config import colormaps from glue.viewers.matplotlib.state import (MatplotlibDataViewerState, MatplotlibLayerState, DeferredDrawCallbackProperty as DDCProperty, DeferredDrawSelectionCallbackProperty as DDSCProperty) from glue.core.state_objects import StateAttributeLimitsHelper from echo import keep_in_sync, delay_callback from glue.core.data_combo_helper import ComponentIDComboHelper, ComboHelper from glue.core.exceptions import IncompatibleAttribute from matplotlib.projections import get_projection_names __all__ = ['ScatterViewerState', 'ScatterLayerState'] class ScatterViewerState(MatplotlibDataViewerState): """ A state class that includes all the attributes for a scatter viewer. """ x_att = DDSCProperty(docstring='The attribute to show on the x-axis', default_index=0) y_att = DDSCProperty(docstring='The attribute to show on the y-axis', default_index=1) dpi = DDCProperty(72, docstring='The resolution (in dots per inch) of density maps, if present') plot_mode = DDSCProperty(docstring="Whether to plot the data in cartesian, polar or another projection") def __init__(self, **kwargs): super(ScatterViewerState, self).__init__() self.limits_cache = {} self.x_lim_helper = StateAttributeLimitsHelper(self, attribute='x_att', lower='x_min', upper='x_max', log='x_log', margin=0.04, limits_cache=self.limits_cache) self.y_lim_helper = StateAttributeLimitsHelper(self, attribute='y_att', lower='y_min', upper='y_max', log='y_log', margin=0.04, limits_cache=self.limits_cache) self.add_callback('layers', self._layers_changed) self.x_att_helper = ComponentIDComboHelper(self, 'x_att', pixel_coord=True, world_coord=True) self.y_att_helper = ComponentIDComboHelper(self, 'y_att', pixel_coord=True, world_coord=True) self.plot_mode_helper = ComboHelper(self, 'plot_mode') self.plot_mode_helper.choices = [proj for proj in get_projection_names() if proj not in ['3d', 'scatter_density']] self.plot_mode_helper.selection = 'rectilinear' self.update_from_dict(kwargs) self.add_callback('x_log', self._reset_x_limits) self.add_callback('y_log', self._reset_y_limits) def _reset_x_limits(self, *args): if self.x_att is None: return self.x_lim_helper.percentile = 100 self.x_lim_helper.update_values(force=True) def _reset_y_limits(self, *args): if self.y_att is None: return self.y_lim_helper.percentile = 100 self.y_lim_helper.update_values(force=True) def reset_limits(self): self._reset_x_limits() self._reset_y_limits() def flip_x(self): """ Flip the x_min/x_max limits. """ self.x_lim_helper.flip_limits() def flip_y(self): """ Flip the y_min/y_max limits. """ self.y_lim_helper.flip_limits() def full_circle(self): if not self.plot_mode == 'polar': return self.x_min = 0 self.x_max = 2 * np.pi @property def x_categories(self): return self._categories(self.x_att) @property def y_categories(self): return self._categories(self.y_att) def _categories(self, cid): categories = [] for layer_state in self.layers: if isinstance(layer_state.layer, BaseData): layer = layer_state.layer else: layer = layer_state.layer.data try: if layer.data.get_kind(cid) == 'categorical': categories.append(layer.data.get_data(cid).categories) except IncompatibleAttribute: pass if len(categories) == 0: return None else: return np.unique(np.hstack(categories)) @property def x_kinds(self): return self._component_kinds(self.x_att) @property def y_kinds(self): return self._component_kinds(self.y_att) def _component_kinds(self, cid): # Construct list of component kinds over all layers kinds = set() for layer_state in self.layers: if isinstance(layer_state.layer, BaseData): layer = layer_state.layer else: layer = layer_state.layer.data try: kinds.add(layer.data.get_kind(cid)) except IncompatibleAttribute: pass return kinds def _layers_changed(self, *args): layers_data = self.layers_data layers_data_cache = getattr(self, '_layers_data_cache', []) if layers_data == layers_data_cache: return self.x_att_helper.set_multiple_data(self.layers_data) self.y_att_helper.set_multiple_data(self.layers_data) self._layers_data_cache = layers_data def display_func_slow(x): if x == 'Linear': return 'Linear (WARNING: may be slow due to data size)' else: return x class ScatterLayerState(MatplotlibLayerState): """ A state class that includes all the attributes for layers in a scatter plot. """ # Color cmap_mode = DDSCProperty(docstring="Whether to use color to encode an attribute") cmap_att = DDSCProperty(docstring="The attribute to use for the color") cmap_vmin = DDCProperty(docstring="The lower level for the colormap") cmap_vmax = DDCProperty(docstring="The upper level for the colormap") cmap = DDCProperty(docstring="The colormap to use (when in colormap mode)") # Points points_mode = DDSCProperty(docstring='Whether to use markers or a density map') # Markers markers_visible = DDCProperty(True, docstring="Whether to show markers") size = DDCProperty(docstring="The size of the markers") size_mode = DDSCProperty(docstring="Whether to use size to encode an attribute") size_att = DDSCProperty(docstring="The attribute to use for the size") size_vmin = DDCProperty(docstring="The lower level for the size mapping") size_vmax = DDCProperty(docstring="The upper level for the size mapping") size_scaling = DDCProperty(1, docstring="Relative scaling of the size") fill = DDCProperty(True, docstring="Whether to fill the markers") # Density map density_map = DDCProperty(False, docstring="Whether to show the points as a density map") stretch = DDSCProperty(default='log', docstring='The stretch used to render the layer, ' 'which should be one of ``linear``, ' '``sqrt``, ``log``, or ``arcsinh``') density_contrast = DDCProperty(1, docstring="The dynamic range of the density map") # Note that we keep the dpi in the viewer state since we want it to always # be in sync between layers. # Line line_visible = DDCProperty(False, docstring="Whether to show a line connecting all positions") linewidth = DDCProperty(1, docstring="The line width") linestyle = DDSCProperty(docstring="The line style") # Errorbars xerr_visible = DDCProperty(False, docstring="Whether to show x error bars") yerr_visible = DDCProperty(False, docstring="Whether to show y error bars") xerr_att = DDSCProperty(docstring="The attribute to use for the x error bars") yerr_att = DDSCProperty(docstring="The attribute to use for the y error bars") # Vectors vector_visible = DDCProperty(False, docstring="Whether to show vector plot") vx_att = DDSCProperty(docstring="The attribute to use for the x vector arrow") vy_att = DDSCProperty(docstring="The attribute to use for the y vector arrow") vector_arrowhead = DDCProperty(False, docstring="Whether to show vector arrow") vector_mode = DDSCProperty(default_index=0, docstring="Whether to plot the vectors in cartesian or polar mode") vector_origin = DDSCProperty(default_index=1, docstring="Whether to place the vector so that the origin is at the tail, middle, or tip") vector_scaling = DDCProperty(1, docstring="The relative scaling of the arrow length") def __init__(self, viewer_state=None, layer=None, **kwargs): super(ScatterLayerState, self).__init__(viewer_state=viewer_state, layer=layer) self.limits_cache = {} self.cmap_lim_helper = StateAttributeLimitsHelper(self, attribute='cmap_att', lower='cmap_vmin', upper='cmap_vmax', limits_cache=self.limits_cache) self.size_lim_helper = StateAttributeLimitsHelper(self, attribute='size_att', lower='size_vmin', upper='size_vmax', limits_cache=self.limits_cache) self.cmap_att_helper = ComponentIDComboHelper(self, 'cmap_att', numeric=True, datetime=False, categorical=False) self.size_att_helper = ComponentIDComboHelper(self, 'size_att', numeric=True, datetime=False, categorical=False) self.xerr_att_helper = ComponentIDComboHelper(self, 'xerr_att', numeric=True, datetime=False, categorical=False) self.yerr_att_helper = ComponentIDComboHelper(self, 'yerr_att', numeric=True, datetime=False, categorical=False) self.vx_att_helper = ComponentIDComboHelper(self, 'vx_att', numeric=True, datetime=False, categorical=False) self.vy_att_helper = ComponentIDComboHelper(self, 'vy_att', numeric=True, datetime=False, categorical=False) points_mode_display = {'auto': 'Density map or markers (auto)', 'markers': 'Markers', 'density': 'Density map'} ScatterLayerState.points_mode.set_choices(self, ['auto', 'markers', 'density']) ScatterLayerState.points_mode.set_display_func(self, points_mode_display.get) self.add_callback('points_mode', self._update_density_map_mode) self.add_callback('density_map', self._on_density_map_change, priority=10000) ScatterLayerState.cmap_mode.set_choices(self, ['Fixed', 'Linear']) ScatterLayerState.size_mode.set_choices(self, ['Fixed', 'Linear']) linestyle_display = {'solid': '–––––––', 'dashed': '– – – – –', 'dotted': '· · · · · · · ·', 'dashdot': '– · – · – ·'} ScatterLayerState.linestyle.set_choices(self, ['solid', 'dashed', 'dotted', 'dashdot']) ScatterLayerState.linestyle.set_display_func(self, linestyle_display.get) ScatterLayerState.vector_mode.set_choices(self, ['Cartesian', 'Polar']) vector_origin_display = {'tail': 'Tail of vector', 'middle': 'Middle of vector', 'tip': 'Tip of vector'} ScatterLayerState.vector_origin.set_choices(self, ['tail', 'middle', 'tip']) ScatterLayerState.vector_origin.set_display_func(self, vector_origin_display.get) stretch_display = {'linear': 'Linear', 'sqrt': 'Square Root', 'arcsinh': 'Arcsinh', 'log': 'Logarithmic'} ScatterLayerState.stretch.set_choices(self, ['linear', 'sqrt', 'arcsinh', 'log']) ScatterLayerState.stretch.set_display_func(self, stretch_display.get) if self.viewer_state is not None: self.viewer_state.add_callback('x_att', self._on_xy_change, priority=10000) self.viewer_state.add_callback('y_att', self._on_xy_change, priority=10000) self._on_xy_change() self.add_callback('layer', self._on_layer_change) if layer is not None: self._on_layer_change() self.cmap = colormaps.members[0][1] self.size = self.layer.style.markersize self._sync_size = keep_in_sync(self, 'size', self.layer.style, 'markersize') self.update_from_dict(kwargs) def _on_xy_change(self, *event): if self.viewer_state.x_att is None or self.viewer_state.y_att is None: return if isinstance(self.layer, BaseData): layer = self.layer else: layer = self.layer.data try: x_datetime = layer.get_kind(self.viewer_state.x_att) == 'datetime' except IncompatibleAttribute: x_datetime = False try: y_datetime = layer.get_kind(self.viewer_state.y_att) == 'datetime' except IncompatibleAttribute: y_datetime = False with delay_callback(self, 'xerr_visible', 'yerr_visible', 'vector_visible'): if x_datetime: self.xerr_visible = False if y_datetime: self.yerr_visible = False if x_datetime or y_datetime: self.vector_visible = False def _on_layer_change(self, layer=None): with delay_callback(self, 'cmap_vmin', 'cmap_vmax', 'size_vmin', 'size_vmax', 'density_map'): self._update_density_map_mode() if self.layer is None: self.cmap_att_helper.set_multiple_data([]) self.size_att_helper.set_multiple_data([]) else: self.cmap_att_helper.set_multiple_data([self.layer]) self.size_att_helper.set_multiple_data([self.layer]) if self.layer is None: self.xerr_att_helper.set_multiple_data([]) self.yerr_att_helper.set_multiple_data([]) else: self.xerr_att_helper.set_multiple_data([self.layer]) self.yerr_att_helper.set_multiple_data([self.layer]) if self.layer is None: self.vx_att_helper.set_multiple_data([]) self.vy_att_helper.set_multiple_data([]) else: self.vx_att_helper.set_multiple_data([self.layer]) self.vy_att_helper.set_multiple_data([self.layer]) def _update_density_map_mode(self, *args): if self.points_mode == 'auto': if self.layer.size > 100000: self.density_map = True else: self.density_map = False elif self.points_mode == 'density': self.density_map = True else: self.density_map = False def _on_density_map_change(self, *args): # If the density map mode is used, we should disable the lines/errors/vectors if self.density_map: with delay_callback(self, 'line_visible', 'xerr_visible', 'yerr_visible', 'vector_visible'): if self.line_visible: self.line_visible = False if self.xerr_visible: self.xerr_visible = False if self.yerr_visible: self.yerr_visible = False if self.vector_visible: self.vector_visible = False def flip_cmap(self): """ Flip the cmap_vmin/cmap_vmax limits. """ self.cmap_lim_helper.flip_limits() def flip_size(self): """ Flip the size_vmin/size_vmax limits. """ self.size_lim_helper.flip_limits() @property def cmap_name(self): return colormaps.name_from_cmap(self.cmap) def compute_density_map(self, bins=None, range=None): if not self.markers_visible or not self.density_map: return np.zeros(bins) if isinstance(self.layer, Subset): data = self.layer.data subset_state = self.layer.subset_state else: data = self.layer subset_state = None count = data.compute_histogram([self.viewer_state.y_att, self.viewer_state.x_att], subset_state=subset_state, bins=bins, log=(self.viewer_state.y_log, self.viewer_state.x_log), range=range) if self.cmap_mode == 'Fixed': return count else: total = data.compute_histogram([self.viewer_state.y_att, self.viewer_state.x_att], subset_state=subset_state, bins=bins, weights=self.cmap_att, log=(self.viewer_state.y_log, self.viewer_state.x_log), range=range) return total / count @classmethod def __setgluestate__(cls, rec, context): # Patch for glue files produced with glue v0.11 if 'style' in rec['values']: style = context.object(rec['values'].pop('style')) if style == 'Scatter': rec['values']['markers_visible'] = True rec['values']['line_visible'] = False elif style == 'Line': rec['values']['markers_visible'] = False rec['values']['line_visible'] = True return super(ScatterLayerState, cls).__setgluestate__(rec, context) glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/__init__.py0000644000175000017500000000000013502206677022600 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/scatter/compat.py0000644000175000017500000000401613605357235022340 0ustar noahfxnoahfximport uuid from .state import ScatterLayerState STATE_CLASS = {} STATE_CLASS['ScatterLayerArtist'] = ScatterLayerState def update_scatter_viewer_state(rec, context): """ Given viewer session information, make sure the session information is compatible with the current version of the viewers, and if not, update the session information in-place. """ if '_protocol' not in rec: # Note that files saved with protocol < 1 have bin settings saved per # layer but they were always restricted to be the same, so we can just # use the settings from the first layer rec['state'] = {} rec['state']['values'] = {} # TODO: could generalize this into a mapping properties = rec.pop('properties') viewer_state = rec['state']['values'] viewer_state['x_min'] = properties['xmin'] viewer_state['x_max'] = properties['xmax'] viewer_state['y_min'] = properties['ymin'] viewer_state['y_max'] = properties['ymax'] viewer_state['x_log'] = properties['xlog'] viewer_state['y_log'] = properties['ylog'] viewer_state['x_att'] = properties['xatt'] viewer_state['y_att'] = properties['yatt'] layer_states = [] for layer in rec['layers']: state_id = str(uuid.uuid4()) state_cls = STATE_CLASS[layer['_type'].split('.')[-1]] state = state_cls(layer=context.object(layer.pop('layer'))) for prop in ('visible', 'zorder'): value = layer.pop(prop) value = context.object(value) setattr(state, prop, value) context.register_object(state_id, state) layer['state'] = state_id layer_states.append(state) layer.pop('lo', None) layer.pop('hi', None) layer.pop('nbins', None) layer.pop('xlog', None) list_id = str(uuid.uuid4()) context.register_object(list_id, layer_states) rec['state']['values']['layers'] = list_id glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/layer_artist.py0000644000175000017500000005733513752534424023573 0ustar noahfxnoahfximport warnings import numpy as np from matplotlib.colors import Normalize from matplotlib.collections import LineCollection from mpl_scatter_density.generic_density_artist import GenericDensityArtist from astropy.visualization import (ImageNormalize, LinearStretch, SqrtStretch, AsinhStretch, LogStretch) from glue.utils import defer_draw, broadcast_to, nanmax, ensure_numerical, datetime64_to_mpl from glue.viewers.scatter.state import ScatterLayerState from glue.viewers.scatter.python_export import python_export_scatter_layer from glue.viewers.matplotlib.layer_artist import MatplotlibLayerArtist from glue.core.exceptions import IncompatibleAttribute from matplotlib.lines import Line2D STRETCHES = {'linear': LinearStretch, 'sqrt': SqrtStretch, 'arcsinh': AsinhStretch, 'log': LogStretch} CMAP_PROPERTIES = set(['cmap_mode', 'cmap_att', 'cmap_vmin', 'cmap_vmax', 'cmap']) MARKER_PROPERTIES = set(['size_mode', 'size_att', 'size_vmin', 'size_vmax', 'size_scaling', 'size', 'fill']) LINE_PROPERTIES = set(['linewidth', 'linestyle']) DENSITY_PROPERTIES = set(['dpi', 'stretch', 'density_contrast']) VISUAL_PROPERTIES = (CMAP_PROPERTIES | MARKER_PROPERTIES | DENSITY_PROPERTIES | LINE_PROPERTIES | set(['color', 'alpha', 'zorder', 'visible'])) DATA_PROPERTIES = set(['layer', 'x_att', 'y_att', 'cmap_mode', 'size_mode', 'density_map', 'xerr_att', 'yerr_att', 'xerr_visible', 'yerr_visible', 'vector_visible', 'vx_att', 'vy_att', 'vector_arrowhead', 'vector_mode', 'vector_origin', 'line_visible', 'markers_visible', 'vector_scaling']) def ravel_artists(errorbar_artist): for artist_container in errorbar_artist: if artist_container is not None: for artist in artist_container: if artist is not None: yield artist class InvertedNormalize(Normalize): def __call__(self, *args, **kwargs): return 1 - super(InvertedNormalize, self).__call__(*args, **kwargs) class DensityMapLimits(object): contrast = 1 def min(self, array): return 0 def max(self, array): return 10. ** (np.log10(nanmax(array)) * self.contrast) def set_mpl_artist_cmap(artist, values, state=None, cmap=None, vmin=None, vmax=None): if state is not None: vmin = state.cmap_vmin vmax = state.cmap_vmax cmap = state.cmap if not isinstance(artist, GenericDensityArtist): artist.set_array(values) artist.set_cmap(cmap) if vmin > vmax: artist.set_clim(vmax, vmin) artist.set_norm(InvertedNormalize(vmax, vmin)) else: artist.set_clim(vmin, vmax) artist.set_norm(Normalize(vmin, vmax)) class ColoredLineCollection(LineCollection): def __init__(self, x, y, **kwargs): segments = np.zeros((0, 2, 2)) super(ColoredLineCollection, self).__init__(segments, **kwargs) self.set_points(x, y) def set_points(self, x, y, oversample=True): if len(x) == 0: self.set_segments(np.zeros((0, 2, 2))) return if oversample: x_fine = np.zeros(len(x) * 2 - 1, dtype=float) y_fine = np.zeros(len(y) * 2 - 1, dtype=float) x_fine[::2] = x x_fine[1::2] = 0.5 * (x[1:] + x[:-1]) y_fine[::2] = y y_fine[1::2] = 0.5 * (y[1:] + y[:-1]) points = np.array([x_fine, y_fine]).transpose().reshape(-1, 1, 2) segments = np.concatenate([points[:-1], points[1:]], axis=1) self.set_segments(segments) else: points = np.array([x, y]).transpose() self.set_segments([points]) def set_linearcolor(self, color=None, data=None, **kwargs): if color is None: data_new = np.zeros((len(data) - 1) * 2) data_new[::2] = data[:-1] data_new[1::2] = data[1:] set_mpl_artist_cmap(self, data_new, **kwargs) else: if isinstance(color, np.ndarray): color_new = np.zeros(((color.shape[0] - 1) * 2,) + color.shape[1:]) color_new[::2] = color[:-1] color_new[1::2] = color[1:] color = color_new self.set_array(None) self.set_color(color) def plot_colored_line(ax, x, y, c=None, cmap=None, vmin=None, vmax=None, **kwargs): lc = ColoredLineCollection(x, y, **kwargs) lc.set_linearcolor(color=c, cmap=cmap, vmin=vmin, vmax=vmax) ax.add_collection(lc) return lc class ScatterLayerArtist(MatplotlibLayerArtist): _layer_state_cls = ScatterLayerState _python_exporter = python_export_scatter_layer def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(ScatterLayerArtist, self).__init__(axes, viewer_state, layer_state=layer_state, layer=layer) # Watch for changes in the viewer state which would require the # layers to be redrawn self._viewer_state.add_global_callback(self._update_scatter) self.state.add_global_callback(self._update_scatter) # Scatter density self.density_auto_limits = DensityMapLimits() self._set_axes(axes) self.errorbar_index = 2 self.vector_index = 3 # NOTE: Matplotlib can't deal with NaN values in errorbar correctly, so # we need to prefilter values - the following variable is used to store # the mask for the values we keep, so that we can apply it to the color # See also https://github.com/matplotlib/matplotlib/issues/13799 self._errorbar_keep = None def _set_axes(self, axes): self.axes = axes self.scatter_artist = self.axes.scatter([], []) self.plot_artist = self.axes.plot([], [], 'o', mec='none')[0] self.errorbar_artist = self.axes.errorbar([], [], fmt='none') self.vector_artist = None self.line_collection = ColoredLineCollection([], []) self.axes.add_collection(self.line_collection) with warnings.catch_warnings(): warnings.filterwarnings("ignore", message='All-NaN slice encountered') self.density_artist = GenericDensityArtist(self.axes, color='white', vmin=self.density_auto_limits.min, vmax=self.density_auto_limits.max, update_while_panning=False, histogram2d_func=self.compute_density_map, label=None) self.axes.add_artist(self.density_artist) self.mpl_artists = [self.scatter_artist, self.plot_artist, self.errorbar_artist, self.vector_artist, self.line_collection, self.density_artist] def compute_density_map(self, *args, **kwargs): try: density_map = self.state.compute_density_map(*args, **kwargs) except IncompatibleAttribute: self.disable_invalid_attributes(self._viewer_state.x_att, self._viewer_state.y_att) return np.array([[np.nan]]) else: self.enable() return density_map @defer_draw def _update_data(self): # Layer artist has been cleared already if len(self.mpl_artists) == 0: return try: if not self.state.density_map: x = ensure_numerical(self.layer[self._viewer_state.x_att].ravel()) if x.dtype.kind == 'M': x = datetime64_to_mpl(x) except (IncompatibleAttribute, IndexError): # The following includes a call to self.clear() self.disable_invalid_attributes(self._viewer_state.x_att) return else: self.enable() try: if not self.state.density_map: y = ensure_numerical(self.layer[self._viewer_state.y_att].ravel()) if y.dtype.kind == 'M': y = datetime64_to_mpl(y) except (IncompatibleAttribute, IndexError): # The following includes a call to self.clear() self.disable_invalid_attributes(self._viewer_state.y_att) return else: self.enable() if self.state.markers_visible: if self.state.density_map: # We don't use x, y here because we actually make use of the # ability of the density artist to call a custom histogram # method which is defined on this class and does the data # access. self.plot_artist.set_data([], []) self.scatter_artist.set_offsets(np.zeros((0, 2))) else: self.density_artist.set_label(None) if self._use_plot_artist(): # In this case we use Matplotlib's plot function because it has much # better performance than scatter. self.plot_artist.set_data(x, y) else: offsets = np.vstack((x, y)).transpose() self.scatter_artist.set_offsets(offsets) else: self.plot_artist.set_data([], []) self.scatter_artist.set_offsets(np.zeros((0, 2))) if self.state.line_visible: if self.state.cmap_mode == 'Fixed': self.line_collection.set_points(x, y, oversample=False) else: # In the case where we want to color the line, we need to over # sample the line by a factor of two so that we can assign the # correct colors to segments - if we didn't do this, then # segments on one side of a point would be a different color # from the other side. With oversampling, we can have half a # segment on either side of a point be the same color as a # point self.line_collection.set_points(x, y) else: self.line_collection.set_points([], []) for eartist in ravel_artists(self.errorbar_artist): try: eartist.remove() except ValueError: pass if self.vector_artist is not None: self.vector_artist.remove() self.vector_artist = None if self.state.vector_visible: if self.state.vx_att is not None and self.state.vy_att is not None: vx = ensure_numerical(self.layer[self.state.vx_att].ravel()) vy = ensure_numerical(self.layer[self.state.vy_att].ravel()) if self.state.vector_mode == 'Polar': ang = vx length = vy # assume ang is anti clockwise from the x axis vx = length * np.cos(np.radians(ang)) vy = length * np.sin(np.radians(ang)) else: vx = None vy = None if self.state.vector_arrowhead: hw = 3 hl = 5 else: hw = 1 hl = 0 vmax = nanmax(np.hypot(vx, vy)) self.vector_artist = self.axes.quiver(x, y, vx, vy, units='width', pivot=self.state.vector_origin, headwidth=hw, headlength=hl, scale_units='width', angles='xy', scale=10 / self.state.vector_scaling * vmax ) self.mpl_artists[self.vector_index] = self.vector_artist if self.state.xerr_visible or self.state.yerr_visible: keep = ~np.isnan(x) & ~np.isnan(y) if self.state.xerr_visible and self.state.xerr_att is not None: xerr = ensure_numerical(self.layer[self.state.xerr_att].ravel()).copy() keep &= ~np.isnan(xerr) else: xerr = None if self.state.yerr_visible and self.state.yerr_att is not None: yerr = ensure_numerical(self.layer[self.state.yerr_att].ravel()).copy() keep &= ~np.isnan(yerr) else: yerr = None if xerr is not None: xerr = xerr[keep] if yerr is not None: yerr = yerr[keep] self._errorbar_keep = keep self.errorbar_artist = self.axes.errorbar(x[keep], y[keep], fmt='none', xerr=xerr, yerr=yerr ) self.mpl_artists[self.errorbar_index] = self.errorbar_artist @defer_draw def _update_visual_attributes(self, changed, force=False): if not self.enabled: return if self.state.markers_visible: if self.state.density_map: if self.state.cmap_mode == 'Fixed': if force or 'color' in changed or 'cmap_mode' in changed: self.density_artist.set_color(self.state.color) self.density_artist.set_clim(self.density_auto_limits.min, self.density_auto_limits.max) elif force or any(prop in changed for prop in CMAP_PROPERTIES): c = ensure_numerical(self.layer[self.state.cmap_att].ravel()) set_mpl_artist_cmap(self.density_artist, c, self.state) if force or 'stretch' in changed: self.density_artist.set_norm(ImageNormalize(stretch=STRETCHES[self.state.stretch]())) if force or 'dpi' in changed: self.density_artist.set_dpi(self._viewer_state.dpi) if force or 'density_contrast' in changed: self.density_auto_limits.contrast = self.state.density_contrast self.density_artist.stale = True else: if self._use_plot_artist(): if force or 'color' in changed or 'fill' in changed: if self.state.fill: self.plot_artist.set_markeredgecolor('none') self.plot_artist.set_markerfacecolor(self.state.color) else: self.plot_artist.set_markeredgecolor(self.state.color) self.plot_artist.set_markerfacecolor('none') if force or 'size' in changed or 'size_scaling' in changed: self.plot_artist.set_markersize(self.state.size * self.state.size_scaling) else: # TEMPORARY: Matplotlib has a bug that causes set_alpha to # change the colors back: https://github.com/matplotlib/matplotlib/issues/8953 if 'alpha' in changed: force = True if self.state.cmap_mode == 'Fixed': if force or 'color' in changed or 'cmap_mode' in changed or 'fill' in changed: self.scatter_artist.set_array(None) if self.state.fill: self.scatter_artist.set_facecolors(self.state.color) self.scatter_artist.set_edgecolors('none') else: self.scatter_artist.set_facecolors('none') self.scatter_artist.set_edgecolors(self.state.color) elif force or any(prop in changed for prop in CMAP_PROPERTIES) or 'fill' in changed: self.scatter_artist.set_edgecolors(None) self.scatter_artist.set_facecolors(None) c = ensure_numerical(self.layer[self.state.cmap_att].ravel()) set_mpl_artist_cmap(self.scatter_artist, c, self.state) if self.state.fill: self.scatter_artist.set_edgecolors('none') else: colors = self.scatter_artist.get_facecolors() self.scatter_artist.set_facecolors('none') self.scatter_artist.set_edgecolors(colors) if force or any(prop in changed for prop in MARKER_PROPERTIES): if self.state.size_mode == 'Fixed': s = self.state.size * self.state.size_scaling s = broadcast_to(s, self.scatter_artist.get_sizes().shape) else: s = ensure_numerical(self.layer[self.state.size_att].ravel()) s = ((s - self.state.size_vmin) / (self.state.size_vmax - self.state.size_vmin)) # The following ensures that the sizes are in the # range 3 to 30 before the final size_scaling. np.clip(s, 0, 1, out=s) s *= 0.95 s += 0.05 s *= (30 * self.state.size_scaling) # Note, we need to square here because for scatter, s is actually # proportional to the marker area, not radius. self.scatter_artist.set_sizes(s ** 2) if self.state.line_visible: if self.state.cmap_mode == 'Fixed': if force or 'color' in changed or 'cmap_mode' in changed: self.line_collection.set_linearcolor(color=self.state.color) elif force or any(prop in changed for prop in CMAP_PROPERTIES): # Higher up we oversampled the points in the line so that # half a segment on either side of each point has the right # color, so we need to also oversample the color here. c = ensure_numerical(self.layer[self.state.cmap_att].ravel()) self.line_collection.set_linearcolor(data=c, state=self.state) if force or 'linewidth' in changed: self.line_collection.set_linewidth(self.state.linewidth) if force or 'linestyle' in changed: self.line_collection.set_linestyle(self.state.linestyle) if self.state.vector_visible and self.vector_artist is not None: if self.state.cmap_mode == 'Fixed': if force or 'color' in changed or 'cmap_mode' in changed: self.vector_artist.set_array(None) self.vector_artist.set_color(self.state.color) elif force or any(prop in changed for prop in CMAP_PROPERTIES): c = ensure_numerical(self.layer[self.state.cmap_att].ravel()) set_mpl_artist_cmap(self.vector_artist, c, self.state) if self.state.xerr_visible or self.state.yerr_visible: for eartist in ravel_artists(self.errorbar_artist): if self.state.cmap_mode == 'Fixed': if force or 'color' in changed or 'cmap_mode' in changed: eartist.set_color(self.state.color) elif force or any(prop in changed for prop in CMAP_PROPERTIES): c = ensure_numerical(self.layer[self.state.cmap_att].ravel()).copy() c = c[self._errorbar_keep] set_mpl_artist_cmap(eartist, c, self.state) if force or 'alpha' in changed: eartist.set_alpha(self.state.alpha) if force or 'visible' in changed: eartist.set_visible(self.state.visible) if force or 'zorder' in changed: eartist.set_zorder(self.state.zorder) for artist in [self.scatter_artist, self.plot_artist, self.vector_artist, self.line_collection, self.density_artist]: if artist is None: continue if force or 'alpha' in changed: artist.set_alpha(self.state.alpha) if force or 'zorder' in changed: artist.set_zorder(self.state.zorder) if force or 'visible' in changed: # We need to hide the density artist if it is not needed because # otherwise it might still show even if there is no data as the # neutral/zero color might not be white. if artist is self.density_artist: artist.set_visible(self.state.visible and self.state.density_map and self.state.markers_visible) else: artist.set_visible(self.state.visible) if self._use_plot_artist(): self.scatter_artist.set_visible(False) else: self.plot_artist.set_visible(False) self.redraw() @defer_draw def _update_scatter(self, force=False, **kwargs): if (self._viewer_state.x_att is None or self._viewer_state.y_att is None or self.state.layer is None): return changed = set() if force else self.pop_changed_properties() if force or len(changed & DATA_PROPERTIES) > 0: self._update_data() force = True if force or len(changed & VISUAL_PROPERTIES) > 0: self._update_visual_attributes(changed, force=force) def get_layer_color(self): if self.state.cmap_mode == 'Fixed': return self.state.color else: return self.state.cmap @defer_draw def update(self): self._update_scatter(force=True) self.redraw() def remove(self): super(ScatterLayerArtist, self).remove() # Clean up the density artist to avoid circular references to do a # reference to the self.histogram2d method in density artist. self.density_artist = None def get_handle_legend(self): if self.enabled and self.state.visible: handles = [] if self.state.markers_visible: if self.state.density_map: if self.state.cmap_mode == 'Fixed': color = self.get_layer_color() else: color = self.layer.style.color handle = Line2D([0, ], [0, ], marker=".", linestyle="none", ms=self.state.size, alpha=self.state.alpha, color=color) handles.append(handle) # as placeholder else: if self._use_plot_artist(): handles.append(self.plot_artist) else: handles.append(self.scatter_artist) if self.state.line_visible: handles.append(self.line_collection) if self.state.vector_visible: handles.append(self.vector_artist) if self.state.xerr_visible or self.state.yerr_visible: handles.append(self.errorbar_artist) handles = tuple(handles) if len(handles) > 0: return handles, self.layer.label, None else: return None, None, None else: return None, None, None def _use_plot_artist(self): res = self.state.cmap_mode == 'Fixed' and self.state.size_mode == 'Fixed' return res and (not hasattr(self._viewer_state, 'plot_mode') or not self._viewer_state.plot_mode == 'polar') glueviz-1.0.1+dfsg.orig/glue/viewers/scatter/viewer.py0000644000175000017500000001077413752534424022366 0ustar noahfxnoahfxfrom glue.core.subset import roi_to_subset_state from glue.core.util import update_ticks from glue.core.roi_pretransforms import ProjectionMplTransform from glue.utils import mpl_to_datetime64 from glue.viewers.scatter.compat import update_scatter_viewer_state from glue.viewers.matplotlib.mpl_axes import init_mpl __all__ = ['MatplotlibScatterMixin'] class MatplotlibScatterMixin(object): def setup_callbacks(self): self.state.add_callback('x_att', self._update_axes) self.state.add_callback('y_att', self._update_axes) self.state.add_callback('x_log', self._update_axes) self.state.add_callback('y_log', self._update_axes) self.state.add_callback('plot_mode', self._update_projection) self._update_axes() def _update_axes(self, *args): if self.state.x_att is not None: # Update ticks, which sets the labels to categories if components are categorical update_ticks(self.axes, 'x', self.state.x_kinds, self.state.x_log, self.state.x_categories, projection=self.state.plot_mode) if self.state.x_log: self.state.x_axislabel = 'Log ' + self.state.x_att.label else: self.state.x_axislabel = self.state.x_att.label if self.state.y_att is not None: # Update ticks, which sets the labels to categories if components are categorical update_ticks(self.axes, 'y', self.state.y_kinds, self.state.y_log, self.state.y_categories, projection=self.state.plot_mode) if self.state.y_log: self.state.y_axislabel = 'Log ' + self.state.y_att.label else: self.state.y_axislabel = self.state.y_att.label self.axes.figure.canvas.draw_idle() def _update_projection(self, *args): self.figure.delaxes(self.axes) _, self.axes = init_mpl(self.figure, projection=self.state.plot_mode) for layer in self.layers: layer._set_axes(self.axes) layer.state.vector_mode = 'Cartesian' layer.update() self.axes.callbacks.connect('xlim_changed', self.limits_from_mpl) self.axes.callbacks.connect('ylim_changed', self.limits_from_mpl) self.removeToolBar(self.toolbar) self.initialize_toolbar() self.update_x_axislabel() self.update_y_axislabel() self.update_x_ticklabel() self.update_y_ticklabel() # Reset and roundtrip the limits to have reasonable and synced limits when changing self.state.x_log = self.state.y_log = False self.state.reset_limits() self.limits_to_mpl() self.limits_from_mpl() self.figure.canvas.draw_idle() def apply_roi(self, roi, override_mode=None): # Force redraw to get rid of ROI. We do this because applying the # subset state below might end up not having an effect on the viewer, # for example there may not be any layers, or the active subset may not # be one of the layers. So we just explicitly redraw here to make sure # a redraw will happen after this method is called. self.redraw() if len(self.layers) == 0: return x_date = 'datetime' in self.state.x_kinds y_date = 'datetime' in self.state.y_kinds if x_date or y_date: roi = roi.transformed(xfunc=mpl_to_datetime64 if x_date else None, yfunc=mpl_to_datetime64 if y_date else None) use_transform = self.state.plot_mode != 'rectilinear' subset_state = roi_to_subset_state(roi, x_att=self.state.x_att, x_categories=self.state.x_categories, y_att=self.state.y_att, y_categories=self.state.y_categories, use_pretransform=use_transform) if use_transform: subset_state.pretransform = ProjectionMplTransform(self.state.plot_mode, self.axes.get_xlim(), self.axes.get_ylim(), self.axes.get_xscale(), self.axes.get_yscale()) self.apply_subset_state(subset_state, override_mode=override_mode) @staticmethod def update_viewer_state(rec, context): return update_scatter_viewer_state(rec, context) glueviz-1.0.1+dfsg.orig/glue/viewers/profile/0000755000175000017500000000000013752535025020473 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/profile/tests/0000755000175000017500000000000013752535025021635 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/profile/tests/__init__.py0000644000175000017500000000000013502206677023735 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/profile/tests/test_state.py0000644000175000017500000001135413657331513024372 0ustar noahfxnoahfximport numpy as np from numpy.testing import assert_allclose from glue.core import Data, Coordinates from glue.core.tests.test_state import clone from ..state import ProfileViewerState, ProfileLayerState class SimpleCoordinates(Coordinates): def __init__(self): super().__init__(pixel_n_dim=3, world_n_dim=3) def pixel_to_world_values(self, *args): return tuple([2.0 * p for p in args]) def world_to_pixel_values(self, *args): return tuple([0.5 * w for w in args]) @property def axis_correlation_matrix(self): matrix = np.zeros((self.world_n_dim, self.pixel_n_dim), dtype=bool) matrix[2, 2] = True matrix[0:2, 0:2] = True return matrix class TestProfileViewerState: def setup_method(self, method): self.data = Data(label='d1') self.data.coords = SimpleCoordinates() self.data['x'] = np.arange(24).reshape((3, 4, 2)).astype(float) self.viewer_state = ProfileViewerState() self.layer_state = ProfileLayerState(viewer_state=self.viewer_state, layer=self.data) self.viewer_state.layers.append(self.layer_state) self.viewer_state.function = 'mean' def test_basic(self): x, y = self.layer_state.profile assert_allclose(x, [0, 2, 4]) assert_allclose(y, [3.5, 11.5, 19.5]) def test_basic_world(self): self.viewer_state.x_att = self.data.world_component_ids[0] x, y = self.layer_state.profile assert_allclose(x, [0, 2, 4]) assert_allclose(y, [3.5, 11.5, 19.5]) def test_x_att(self): self.viewer_state.x_att = self.data.pixel_component_ids[0] x, y = self.layer_state.profile assert_allclose(x, [0, 1, 2]) assert_allclose(y, [3.5, 11.5, 19.5]) self.viewer_state.x_att = self.data.pixel_component_ids[1] x, y = self.layer_state.profile assert_allclose(x, [0, 1, 2, 3]) assert_allclose(y, [8.5, 10.5, 12.5, 14.5]) self.viewer_state.x_att = self.data.pixel_component_ids[2] x, y = self.layer_state.profile assert_allclose(x, [0, 1]) assert_allclose(y, [11, 12]) def test_function(self): self.viewer_state.function = 'mean' x, y = self.layer_state.profile assert_allclose(y, [3.5, 11.5, 19.5]) self.viewer_state.function = 'minimum' x, y = self.layer_state.profile assert_allclose(y, [0, 8, 16]) self.viewer_state.function = 'maximum' x, y = self.layer_state.profile assert_allclose(y, [7, 15, 23]) self.viewer_state.function = 'sum' x, y = self.layer_state.profile assert_allclose(y, [28, 92, 156]) self.viewer_state.function = 'median' x, y = self.layer_state.profile assert_allclose(y, [3.5, 11.5, 19.5]) def test_subset(self): subset = self.data.new_subset() subset.subset_state = self.data.id['x'] > 10 self.layer_state.layer = subset x, y = self.layer_state.profile assert_allclose(x, [0, 2, 4]) assert_allclose(y, [np.nan, 13., 19.5]) subset.subset_state = self.data.id['x'] > 100 # TODO: the fact we have to call this isn't ideal self.layer_state.reset_cache() x, y = self.layer_state.profile assert len(x) == 0 assert len(y) == 0 def test_clone(self): self.viewer_state.x_att = self.data.pixel_component_ids[1] self.viewer_state.function = 'median' self.layer_state.attribute = self.data.id['x'] self.layer_state.linewidth = 3 viewer_state_new = clone(self.viewer_state) assert viewer_state_new.x_att.label == 'Pixel Axis 1 [y]' assert viewer_state_new.function == 'median' assert self.layer_state.attribute.label == 'x' assert self.layer_state.linewidth == 3 def test_limits(self): self.viewer_state.x_att = self.data.pixel_component_ids[0] assert self.viewer_state.x_min == -0.5 assert self.viewer_state.x_max == 2.5 self.viewer_state.flip_x() assert self.viewer_state.x_min == 2.5 assert self.viewer_state.x_max == -0.5 self.viewer_state.x_min = 1 self.viewer_state.x_max = 1.5 assert self.viewer_state.x_min == 1 assert self.viewer_state.x_max == 1.5 self.viewer_state.reset_limits() assert self.viewer_state.x_min == -0.5 assert self.viewer_state.x_max == 2.5 def test_visible(self): self.layer_state.visible = False assert self.layer_state.profile is None self.layer_state.visible = True x, y = self.layer_state.profile assert_allclose(x, [0, 2, 4]) assert_allclose(y, [3.5, 11.5, 19.5]) glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/0000755000175000017500000000000013752535025021117 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/tests/0000755000175000017500000000000013752535025022261 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/tests/test_mouse_mode.py0000644000175000017500000001014613605357235026032 0ustar noahfxnoahfxfrom unittest.mock import MagicMock from matplotlib import pyplot as plt from ..mouse_mode import NavigateMouseMode, RangeMouseMode def test_navigate_mouse_mode(): callback = MagicMock() fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.set_xlim(0, 10) viewer = MagicMock() viewer.axes = ax mode = NavigateMouseMode(viewer, press_callback=callback) event = MagicMock() event.xdata = 1.5 event.inaxes = True mode.press(event) assert mode.state.x is None mode.move(event) assert mode.state.x is None mode.release(event) assert mode.state.x is None mode.activate() mode.press(event) assert callback.call_count == 1 assert mode.state.x == 1.5 event.xdata = 2.5 mode.move(event) assert mode.state.x == 2.5 mode.release(event) event.xdata = 3.5 mode.move(event) assert mode.state.x == 2.5 mode.deactivate() event.xdata = 1.5 mode.press(event) assert callback.call_count == 1 assert mode.state.x == 2.5 mode.activate() event.xdata = 3.5 mode.press(event) assert callback.call_count == 2 assert mode.state.x == 3.5 event.inaxes = False event.xdata = 4.5 mode.press(event) assert callback.call_count == 2 assert mode.state.x == 3.5 def test_range_mouse_mode(): fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.set_xlim(0, 10) viewer = MagicMock() viewer.axes = ax mode = RangeMouseMode(viewer) event = MagicMock() event.xdata = 1.5 event.inaxes = True # Pressing, moving, and releasing doesn't do anything until mode is active mode.press(event) assert mode.state.x_min is None assert mode.state.x_max is None mode.move(event) assert mode.state.x_min is None assert mode.state.x_max is None mode.release(event) assert mode.state.x_min is None assert mode.state.x_max is None mode.activate() # Click and drag then creates an interval where x_min is the first value # that was clicked and x_max is set to the position of the mouse while # dragging and at the point of releasing. mode.press(event) assert mode.state.x_min == 1.5 assert mode.state.x_max == 1.5 event.xdata = 2.5 mode.move(event) assert mode.state.x_min == 1.5 assert mode.state.x_max == 2.5 event.xdata = 3.5 mode.move(event) assert mode.state.x_min == 1.5 assert mode.state.x_max == 3.5 mode.release(event) event.xdata = 4.5 mode.move(event) assert mode.state.x_min == 1.5 assert mode.state.x_max == 3.5 # Test that we can drag the existing edges by clicking on then event.xdata = 1.49 mode.press(event) event.xdata = 1.25 mode.move(event) assert mode.state.x_min == 1.25 assert mode.state.x_max == 3.5 mode.release(event) event.xdata = 3.51 mode.press(event) event.xdata = 4.0 mode.move(event) assert mode.state.x_min == 1.25 assert mode.state.x_max == 4.0 mode.release(event) # Test that we can drag the entire interval by clicking inside event.xdata = 2 mode.press(event) event.xdata = 3 mode.move(event) assert mode.state.x_min == 2.25 assert mode.state.x_max == 5.0 mode.release(event) # Test that x_range works assert mode.state.x_range == (2.25, 5.0) # Clicking outside the range starts a new interval event.xdata = 6 mode.press(event) event.xdata = 7 mode.move(event) assert mode.state.x_min == 6 assert mode.state.x_max == 7 mode.release(event) # Deactivate and activate again to make sure that code for hiding/showing # artists gets executed mode.deactivate() event.xdata = 8 mode.press(event) assert mode.state.x_min == 6 assert mode.state.x_max == 7 mode.activate() event.xdata = 9 mode.press(event) event.xdata = 10 mode.move(event) assert mode.state.x_min == 9 assert mode.state.x_max == 10 # Check that events outside the axes get ignored event.inaxes = False event.xdata = 11 mode.press(event) assert mode.state.x_min == 9 assert mode.state.x_max == 10 glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/tests/test_python_export.py0000644000175000017500000000562013752534424026621 0ustar noahfxnoahfxfrom astropy.utils import NumpyRNGContext from glue.core import Data, DataCollection from glue.app.qt.application import GlueApplication from glue.viewers.matplotlib.qt.tests.test_python_export import BaseTestExportPython, random_with_nan from glue.viewers.profile.qt import ProfileViewer from glue.viewers.profile.tests.test_state import SimpleCoordinates class TestExportPython(BaseTestExportPython): def setup_method(self, method): self.data = Data(label='d1') self.data.coords = SimpleCoordinates() with NumpyRNGContext(12345): self.data['x'] = random_with_nan(48, 5).reshape((6, 4, 2)) self.data['y'] = random_with_nan(48, 12).reshape((6, 4, 2)) self.data_collection = DataCollection([self.data]) self.app = GlueApplication(self.data_collection) self.viewer = self.app.new_data_viewer(ProfileViewer) self.viewer.add_data(self.data) def teardown_method(self, method): self.viewer.close() self.viewer = None self.app.close() self.app = None def test_simple(self, tmpdir): self.assert_same(tmpdir) def test_simple_legend(self, tmpdir): self.viewer.state.legend.visible = True self.assert_same(tmpdir) def test_color(self, tmpdir): self.viewer.state.layers[0].color = '#ac0567' self.assert_same(tmpdir) def test_linewidth(self, tmpdir): self.viewer.state.layers[0].linewidth = 7.25 self.assert_same(tmpdir) def test_max(self, tmpdir): self.viewer.state.function = 'maximum' self.assert_same(tmpdir) def test_min(self, tmpdir): self.viewer.state.function = 'minimum' self.assert_same(tmpdir) def test_mean(self, tmpdir): self.viewer.state.function = 'mean' self.assert_same(tmpdir) def test_median(self, tmpdir): self.viewer.state.function = 'median' self.assert_same(tmpdir) def test_sum(self, tmpdir): self.viewer.state.function = 'sum' self.assert_same(tmpdir) def test_normalization(self, tmpdir): self.viewer.state.normalize = True self.assert_same(tmpdir) def test_subset(self, tmpdir): self.viewer.state.function = 'mean' self.data_collection.new_subset_group('mysubset', self.data.id['x'] > 0.25) self.assert_same(tmpdir) def test_subset_legend(self, tmpdir): self.viewer.state.legend.visible = True self.viewer.state.function = 'mean' self.viewer.state.layers[0].linewidth = 7.25 self.data_collection.new_subset_group('mysubset', self.data.id['x'] > 0.25) self.assert_same(tmpdir) def test_xatt(self, tmpdir): self.viewer.x_att = self.data.pixel_component_ids[1] self.assert_same(tmpdir) def test_profile_att(self, tmpdir): self.viewer.layers[0].state.attribute = self.data.id['y'] self.assert_same(tmpdir) glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/tests/test_profile_tools.py0000644000175000017500000002005013612627503026545 0ustar noahfxnoahfximport pytest import numpy as np from numpy.testing import assert_allclose from glue.core import Data from glue.tests.helpers import PYSIDE2_INSTALLED # noqa from glue.app.qt import GlueApplication from glue.utils import nanmean from glue.utils.qt import process_events from glue.viewers.image.state import AggregateSlice from glue.viewers.image.qt import ImageViewer from glue.viewers.profile.tests.test_state import SimpleCoordinates from ..data_viewer import ProfileViewer class TestProfileTools(object): def setup_method(self, method): self.data = Data(label='d1') self.data.coords = SimpleCoordinates() self.data['x'] = np.arange(240).reshape((30, 4, 2)).astype(float) self.app = GlueApplication() self.session = self.app.session self.hub = self.session.hub self.data_collection = self.session.data_collection self.data_collection.append(self.data) self.viewer = self.app.new_data_viewer(ProfileViewer) self.viewer.state.function = 'mean' self.viewer.toolbar.active_tool = 'profile-analysis' self.profile_tools = self.viewer.toolbar.tools['profile-analysis']._profile_tools def teardown_method(self, method): self.viewer.close() self.viewer = None self.app.close() self.app = None def test_navigate_sync_image(self): self.viewer.add_data(self.data) image_viewer = self.app.new_data_viewer(ImageViewer) image_viewer.add_data(self.data) assert image_viewer.state.slices == (0, 0, 0) self.viewer.state.x_att = self.data.pixel_component_ids[0] # Force events to be processed to make sure that the callback functions # for the computation thread are executed (since they rely on signals) self.viewer.layers[0].wait() process_events() x, y = self.viewer.axes.transData.transform([[1, 4]])[0] self.viewer.axes.figure.canvas.button_press_event(x, y, 1) self.viewer.axes.figure.canvas.button_release_event(x, y, 1) assert image_viewer.state.slices == (1, 0, 0) self.viewer.state.x_att = self.data.world_component_ids[0] x, y = self.viewer.axes.transData.transform([[10, 4]])[0] self.viewer.axes.figure.canvas.button_press_event(x, y, 1) self.viewer.axes.figure.canvas.button_release_event(x, y, 1) assert image_viewer.state.slices == (5, 0, 0) @pytest.mark.skipif('PYSIDE2_INSTALLED') def test_fit_polynomial(self): # TODO: need to deterministically set to polynomial fitter self.viewer.add_data(self.data) self.profile_tools.ui.tabs.setCurrentIndex(1) # First try in pixel coordinates self.viewer.state.x_att = self.data.pixel_component_ids[0] # Force events to be processed to make sure that the callback functions # for the computation thread are executed (since they rely on signals) self.viewer.layers[0].wait() process_events() x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0] self.viewer.axes.figure.canvas.button_press_event(x, y, 1) x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0] self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) assert_allclose(self.profile_tools.rng_mode.state.x_range, (0.9, 15.1)) self.profile_tools.ui.button_fit.click() self.profile_tools.wait_for_fit() # Force events to be processed to make sure that the callback functions # for the computation thread are executed (since they rely on signals) process_events() pixel_log = self.profile_tools.text_log.toPlainText().splitlines() assert pixel_log[0] == 'd1' assert pixel_log[1] == 'Coefficients:' assert pixel_log[-2] == '8.000000e+00' assert pixel_log[-1] == '3.500000e+00' self.profile_tools.ui.button_clear.click() assert self.profile_tools.text_log.toPlainText() == '' # Next, try in world coordinates self.viewer.state.x_att = self.data.world_component_ids[0] x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0] self.viewer.axes.figure.canvas.button_press_event(x, y, 1) x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0] self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) assert_allclose(self.profile_tools.rng_mode.state.x_range, (1.9, 30.1)) self.profile_tools.ui.button_fit.click() self.profile_tools.wait_for_fit() process_events() world_log = self.profile_tools.text_log.toPlainText().splitlines() assert world_log[0] == 'd1' assert world_log[1] == 'Coefficients:' assert world_log[-2] == '4.000000e+00' assert world_log[-1] == '3.500000e+00' def test_collapse(self): self.viewer.add_data(self.data) image_viewer = self.app.new_data_viewer(ImageViewer) image_viewer.add_data(self.data) self.profile_tools.ui.tabs.setCurrentIndex(2) # First try in pixel coordinates self.viewer.state.x_att = self.data.pixel_component_ids[0] # Force events to be processed to make sure that the callback functions # for the computation thread are executed (since they rely on signals) self.viewer.layers[0].wait() process_events() x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0] self.viewer.axes.figure.canvas.button_press_event(x, y, 1) x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0] self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) self.profile_tools.ui.button_collapse.click() assert isinstance(image_viewer.state.slices[0], AggregateSlice) assert image_viewer.state.slices[0].slice.start == 1 assert image_viewer.state.slices[0].slice.stop == 15 assert image_viewer.state.slices[0].center == 0 assert image_viewer.state.slices[0].function is nanmean # Next, try in world coordinates self.viewer.state.x_att = self.data.world_component_ids[0] # Force events to be processed to make sure that the callback functions # for the computation thread are executed (since they rely on signals) self.viewer.layers[0].wait() process_events() x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0] self.viewer.axes.figure.canvas.button_press_event(x, y, 1) x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0] self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) self.profile_tools.ui.button_collapse.click() assert isinstance(image_viewer.state.slices[0], AggregateSlice) assert image_viewer.state.slices[0].slice.start == 1 assert image_viewer.state.slices[0].slice.stop == 15 assert image_viewer.state.slices[0].center == 0 assert image_viewer.state.slices[0].function is nanmean def test_collapse_reverse(self, capsys): # Regression test for a bug that caused collapsing to fail if selecting # the range in the reverse direction. self.viewer.add_data(self.data) image_viewer = self.app.new_data_viewer(ImageViewer) image_viewer.add_data(self.data) self.profile_tools.ui.tabs.setCurrentIndex(2) self.viewer.state.x_att = self.data.pixel_component_ids[0] # Force events to be processed to make sure that the callback functions # for the computation thread are executed (since they rely on signals) self.viewer.layers[0].wait() process_events() x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0] self.viewer.axes.figure.canvas.button_press_event(x, y, 1) x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0] self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) self.profile_tools.ui.button_collapse.click() process_events() # We use capsys here because the # error is otherwise only apparent in stderr. out, err = capsys.readouterr() assert out.strip() == "" assert err.strip() == "" glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/tests/__init__.py0000644000175000017500000000000013502206677024361 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/tests/data/0000755000175000017500000000000013752535025023172 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/tests/data/profile_v1.glu0000644000175000017500000004244313644362032025754 0ustar noahfxnoahfx{ "CallbackList": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ProfileLayerState", "ProfileLayerState_0", "ProfileLayerState_1" ] }, "CallbackList_0": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ProfileLayerState_2", "ProfileLayerState_3", "ProfileLayerState_4" ] }, "CallbackList_1": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ProfileLayerState_5", "ProfileLayerState_6", "ProfileLayerState_7" ] }, "CallbackList_2": { "_type": "glue.external.echo.list.CallbackList", "values": [ "ProfileLayerState_8", "ProfileLayerState_9", "ProfileLayerState_10" ] }, "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDQsIDUpLCB9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoAAAAAAAAAAAAAAAAAAPA/AAAAAAAAAEAAAAAAAAAIQAAAAAAAABBAAAAAAAAAFEAAAAAAAAAYQAAAAAAAABxAAAAAAAAAIEAAAAAAAAAiQAAAAAAAACRAAAAAAAAAJkAAAAAAAAAoQAAAAAAAACpAAAAAAAAALEAAAAAAAAAuQAAAAAAAADBAAAAAAAAAMUAAAAAAAAAyQAAAAAAAADNAAAAAAAAANEAAAAAAAAA1QAAAAAAAADZAAAAAAAAAN0AAAAAAAAA4QAAAAAAAADlAAAAAAAAAOkAAAAAAAAA7QAAAAAAAADxAAAAAAAAAPUAAAAAAAAA+QAAAAAAAAD9AAAAAAAAAQEAAAAAAAIBAQAAAAAAAAEFAAAAAAACAQUAAAAAAAABCQAAAAAAAgEJAAAAAAAAAQ0AAAAAAAIBDQAAAAAAAAERAAAAAAACAREAAAAAAAABFQAAAAAAAgEVAAAAAAAAARkAAAAAAAIBGQAAAAAAAAEdAAAAAAACAR0AAAAAAAABIQAAAAAAAgEhAAAAAAAAASUAAAAAAAIBJQAAAAAAAAEpAAAAAAACASkAAAAAAAABLQAAAAAAAgEtAAAAAAAAATEAAAAAAAIBMQAAAAAAAAE1AAAAAAACATUA=" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": false }, "CoordinateComponent_1": { "_type": "glue.core.component.CoordinateComponent", "axis": 2, "world": false }, "CoordinateComponent_2": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "CoordinateComponent_3": { "_type": "glue.core.component.CoordinateComponent", "axis": 1, "world": true }, "CoordinateComponent_4": { "_type": "glue.core.component.CoordinateComponent", "axis": 2, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 4, "_type": "glue.core.data_collection.DataCollection", "cids": [ "Pixel Axis 0 [z]", "Pixel Axis 1 [y]", "Pixel Axis 2 [x]", "World 0", "World 1", "World 2", "array_0" ], "components": [ "CoordinateComponent", "CoordinateComponent_0", "CoordinateComponent_1", "CoordinateComponent_2", "CoordinateComponent_3", "CoordinateComponent_4", "Component" ], "data": [ "array" ], "groups": [ "Subset 1", "Subset 2" ], "links": [], "subset_group_count": 2 }, "Pixel Axis 0 [z]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "label": "Pixel Axis 0 [z]" }, "Pixel Axis 1 [y]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 1, "label": "Pixel Axis 1 [y]" }, "Pixel Axis 2 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 2, "label": "Pixel Axis 2 [x]" }, "ProfileLayerState": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.8, "attribute": "array_0", "color": "st__#919191", "layer": "array", "linewidth": 1, "percentile": 100, "v_max": 59.0, "v_min": 19.0, "visible": true, "zorder": 1 } }, "ProfileLayerState_0": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.5, "attribute": "array_0", "color": "st__#e31a1c", "layer": "Subset 1_0", "linewidth": 1, "percentile": 100, "v_max": 57.0, "v_min": 17.0, "visible": true, "zorder": 2 } }, "ProfileLayerState_1": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.5, "attribute": "array_0", "color": "st__#33a02c", "layer": "Subset 2_0", "linewidth": 1, "percentile": 100, "v_max": 58.0, "v_min": 18.0, "visible": true, "zorder": 3 } }, "ProfileLayerState_10": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.5, "attribute": "array_0", "color": "st__#33a02c", "layer": "Subset 2_0", "linewidth": 1, "percentile": 100, "v_max": 30.5, "v_min": 30.5, "visible": false, "zorder": 3 } }, "ProfileLayerState_2": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.8, "attribute": "array_0", "color": "st__#919191", "layer": "array", "linewidth": 1, "percentile": 100, "v_max": 59.0, "v_min": 44.0, "visible": true, "zorder": 1 } }, "ProfileLayerState_3": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.5, "attribute": "array_0", "color": "st__#e31a1c", "layer": "Subset 1_0", "linewidth": 1, "percentile": 100, "v_max": 57.0, "v_min": 42.0, "visible": false, "zorder": 2 } }, "ProfileLayerState_4": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.5, "attribute": "array_0", "color": "st__#33a02c", "layer": "Subset 2_0", "linewidth": 1, "percentile": 100, "v_max": 58.0, "v_min": 43.0, "visible": true, "zorder": 3 } }, "ProfileLayerState_5": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.8, "attribute": "array_0", "color": "st__#919191", "layer": "array", "linewidth": 1, "percentile": 100, "v_max": 4.0, "v_min": 0.0, "visible": true, "zorder": 1 } }, "ProfileLayerState_6": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.5, "attribute": "array_0", "color": "st__#e31a1c", "layer": "Subset 1_0", "linewidth": 1, "percentile": 100, "v_max": 2.0, "v_min": 1.0, "visible": true, "zorder": 2 } }, "ProfileLayerState_7": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.5, "attribute": "array_0", "color": "st__#33a02c", "layer": "Subset 2_0", "linewidth": 1, "percentile": 100, "v_max": 3.0, "v_min": 3.0, "visible": false, "zorder": 3 } }, "ProfileLayerState_8": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.8, "attribute": "array_0", "color": "st__#919191", "layer": "array", "linewidth": 1, "percentile": 100, "v_max": 31.5, "v_min": 27.5, "visible": true, "zorder": 1 } }, "ProfileLayerState_9": { "_type": "glue.viewers.profile.state.ProfileLayerState", "values": { "alpha": 0.5, "attribute": "array_0", "color": "st__#e31a1c", "layer": "Subset 1_0", "linewidth": 1, "percentile": 100, "v_max": 29.5, "v_min": 28.5, "visible": false, "zorder": 2 } }, "ProfileViewer": { "_protocol": 1, "_type": "glue.viewers.profile.qt.data_viewer.ProfileViewer", "layers": [ { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState" }, { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_0" }, { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_1" } ], "pos": [ 0, 0 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "aspect": "st__auto", "function": "st__maximum", "layers": "CallbackList", "normalize": false, "reference_data": "array", "show_axes": true, "x_att": "Pixel Axis 0 [z]", "x_att_pixel": "Pixel Axis 0 [z]", "x_axislabel": "st__Pixel Axis 0 [z]", "x_axislabel_size": 10, "x_axislabel_weight": "st__normal", "x_log": false, "x_max": 2.5, "x_min": -0.5, "x_ticklabel_size": 8, "y_axislabel": "st__Data values", "y_axislabel_size": 10, "y_axislabel_weight": "st__normal", "y_log": false, "y_max": 63.0, "y_min": 13.0, "y_ticklabel_size": 8 } } }, "ProfileViewer_0": { "_protocol": 1, "_type": "glue.viewers.profile.qt.data_viewer.ProfileViewer", "layers": [ { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_2" }, { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_3" }, { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_4" } ], "pos": [ 600, 1 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "aspect": "st__auto", "function": "st__maximum", "layers": "CallbackList_0", "normalize": true, "reference_data": "array", "show_axes": true, "x_att": "Pixel Axis 1 [y]", "x_att_pixel": "Pixel Axis 1 [y]", "x_axislabel": "st__Pixel Axis 1 [y]", "x_axislabel_size": 10, "x_axislabel_weight": "st__normal", "x_log": false, "x_max": 3.5, "x_min": -0.5, "x_ticklabel_size": 8, "y_axislabel": "st__Normalized data values", "y_axislabel_size": 10, "y_axislabel_weight": "st__normal", "y_log": false, "y_max": 1.1, "y_min": -0.1, "y_ticklabel_size": 8 } } }, "ProfileViewer_1": { "_protocol": 1, "_type": "glue.viewers.profile.qt.data_viewer.ProfileViewer", "layers": [ { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_5" }, { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_6" }, { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_7" } ], "pos": [ 0, 400 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "aspect": "st__auto", "function": "st__minimum", "layers": "CallbackList_1", "normalize": false, "reference_data": "array", "show_axes": true, "x_att": "Pixel Axis 2 [x]", "x_att_pixel": "Pixel Axis 2 [x]", "x_axislabel": "st__Pixel Axis 2 [x]", "x_axislabel_size": 10, "x_axislabel_weight": "st__normal", "x_log": false, "x_max": 4.5, "x_min": -0.5, "x_ticklabel_size": 8, "y_axislabel": "st__Data values", "y_axislabel_size": 10, "y_axislabel_weight": "st__normal", "y_log": false, "y_max": 4.4, "y_min": -0.4, "y_ticklabel_size": 8 } } }, "ProfileViewer_2": { "_protocol": 1, "_type": "glue.viewers.profile.qt.data_viewer.ProfileViewer", "layers": [ { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_8" }, { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_9" }, { "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", "state": "ProfileLayerState_10" } ], "pos": [ 600, 400 ], "session": "Session", "size": [ 600, 400 ], "state": { "values": { "aspect": "st__auto", "function": "st__mean", "layers": "CallbackList_2", "normalize": false, "reference_data": "array", "show_axes": true, "x_att": "Pixel Axis 2 [x]", "x_att_pixel": "Pixel Axis 2 [x]", "x_axislabel": "st__Pixel Axis 2 [x]", "x_axislabel_size": 10, "x_axislabel_weight": "st__normal", "x_log": false, "x_max": 9.5, "x_min": -5.5, "x_ticklabel_size": 8, "y_axislabel": "st__Data values", "y_axislabel_size": 10, "y_axislabel_weight": "st__normal", "y_log": false, "y_max": 31.9, "y_min": 27.1, "y_ticklabel_size": 8 } } }, "RangeSubsetState": { "_type": "glue.core.subset.RangeSubsetState", "att": "Pixel Axis 2 [x]", "hi": 2.2825159914712154, "lo": 0.9925373134328361 }, "RangeSubsetState_0": { "_type": "glue.core.subset.RangeSubsetState", "att": "Pixel Axis 2 [x]", "hi": 3.2633262260127935, "lo": 2.932835820895522 }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "RangeSubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 2.5, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1_0" ] }, "Subset 1_0": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 2.5, "marker": "o", "markersize": 7 } }, "Subset 2": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 2", "state": "RangeSubsetState_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#33a02c", "linestyle": "solid", "linewidth": 2.5, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 2_0" ] }, "Subset 2_0": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 2", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#33a02c", "linestyle": "solid", "linewidth": 2.5, "marker": "o", "markersize": 7 } }, "World 0": { "_type": "glue.core.component_id.ComponentID", "label": "World 0" }, "World 1": { "_type": "glue.core.component_id.ComponentID", "label": "World 1" }, "World 2": { "_type": "glue.core.component_id.ComponentID", "label": "World 2" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue_plotly", "glue.plugins.export_d3po", "glue.plugins.tools", "glue_vispy_viewers.scatter", "glue.plugins.wcs_autolinking", "glue.viewers.image.qt", "glue.viewers.scatter.qt", "glue.viewers.profile.qt", "glue.io.formats.fits", "glue.plugins.tools.pv_slicer", "glue.plugins.dendro_viewer.qt", "glue.core.data_exporters", "glue.viewers.table.qt", "glue.viewers.histogram.qt", "glue.plugins.coordinate_helpers", "glue_vispy_viewers.volume", "glue.plugins.data_factories.spectral_cube" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "ProfileViewer", "ProfileViewer_0", "ProfileViewer_1", "ProfileViewer_2" ] ] }, "array": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "Pixel Axis 0 [z]", "CoordinateComponent" ], [ "Pixel Axis 1 [y]", "CoordinateComponent_0" ], [ "Pixel Axis 2 [x]", "CoordinateComponent_1" ], [ "World 0", "CoordinateComponent_2" ], [ "World 1", "CoordinateComponent_3" ], [ "World 2", "CoordinateComponent_4" ], [ "array_0", "Component" ] ], "coords": "Coordinates", "label": "array", "meta": { "_type": "collections.OrderedDict", "contents": {} }, "primary_owner": [ "Pixel Axis 0 [z]", "Pixel Axis 1 [y]", "Pixel Axis 2 [x]", "World 0", "World 1", "World 2", "array_0" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "#919191", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1_0", "Subset 2_0" ], "uuid": "bcc66633-1537-48a3-8710-96e303bf6a6e" }, "array_0": { "_type": "glue.core.component_id.ComponentID", "label": "array" } }glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/tests/test_data_viewer.py0000644000175000017500000002313113612622074026160 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import os import pytest import numpy as np from numpy.testing import assert_equal, assert_allclose from glue.core import Data from glue.core.roi import XRangeROI from glue.core.subset import SliceSubsetState from glue.app.qt import GlueApplication from glue.core.component_link import ComponentLink from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer from glue.core.coordinates import IdentityCoordinates from glue.viewers.profile.tests.test_state import SimpleCoordinates from glue.core.tests.test_state import clone from glue.core.state import GlueUnSerializer from ..data_viewer import ProfileViewer DATA = os.path.join(os.path.dirname(__file__), 'data') class TestProfileCommon(BaseTestMatplotlibDataViewer): def init_data(self): return Data(label='d1', x=np.random.random(24).reshape((3, 4, 2))) viewer_cls = ProfileViewer @pytest.mark.skip() def test_double_add_ignored(self): pass class TestProfileViewer(object): def setup_method(self, method): self.data = Data(label='d1') self.data.coords = SimpleCoordinates() self.data['x'] = np.arange(24).reshape((3, 4, 2)) self.data2 = Data(label='d2') self.data2['y'] = np.arange(24).reshape((3, 4, 2)) self.app = GlueApplication() self.session = self.app.session self.hub = self.session.hub self.data_collection = self.session.data_collection self.data_collection.append(self.data) self.data_collection.append(self.data2) self.viewer = self.app.new_data_viewer(ProfileViewer) def teardown_method(self, method): self.viewer.close() self.viewer = None self.app.close() self.app = None def test_functions(self): self.viewer.add_data(self.data) self.viewer.state.function = 'mean' assert len(self.viewer.layers) == 1 layer_artist = self.viewer.layers[0] assert_allclose(layer_artist.state.profile[0], [0, 2, 4]) assert_allclose(layer_artist.state.profile[1], [3.5, 11.5, 19.5]) def test_incompatible(self): self.viewer.add_data(self.data) data2 = Data(y=np.random.random((3, 4, 2))) self.data_collection.append(data2) self.viewer.add_data(data2) assert len(self.viewer.layers) == 2 assert self.viewer.layers[0].enabled assert not self.viewer.layers[1].enabled def test_selection(self): self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.pixel_component_ids[0] roi = XRangeROI(0.9, 2.1) self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 assert_equal(self.data.subsets[0].to_mask()[:, 0, 0], [0, 1, 1]) self.viewer.state.x_att = self.data.world_component_ids[0] roi = XRangeROI(1.9, 3.1) self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 assert_equal(self.data.subsets[0].to_mask()[:, 0, 0], [0, 1, 0]) def test_enabled_layers(self): data2 = Data(label='d1', y=np.arange(24).reshape((3, 4, 2)), coords=IdentityCoordinates(n_dim=3)) self.data_collection.append(data2) self.viewer.add_data(self.data) self.viewer.add_data(data2) assert self.viewer.layers[0].enabled assert not self.viewer.layers[1].enabled self.data_collection.add_link(ComponentLink([data2.world_component_ids[1]], self.data.world_component_ids[0], using=lambda x: 2 * x)) assert self.viewer.layers[0].enabled assert self.viewer.layers[1].enabled def test_slice_subset_state(self): self.viewer.add_data(self.data) subset = self.data.new_subset() subset.subset_state = SliceSubsetState(self.data, [slice(1, 2), slice(None)]) assert self.viewer.layers[0].enabled assert self.viewer.layers[1].enabled def test_clone(self): # Regression test for a bug that meant that deserializing a profile # viewer resulted in disabled layers self.viewer.add_data(self.data) subset = self.data.new_subset() subset.subset_state = SliceSubsetState(self.data, [slice(1, 2), slice(None)]) app = clone(self.app) assert app.viewers[0][0].layers[0].enabled assert app.viewers[0][0].layers[1].enabled app.close() def test_incompatible_on_add(self): # Regression test for a bug when adding a dataset to a profile viewer # with a single incompatible subset. subset_state = SliceSubsetState(self.data, [slice(1, 2), slice(None)]) self.data_collection.new_subset_group(subset_state=subset_state, label='s1') data2 = Data(x=[[2, 3], [4, 3]], label='d2') self.data_collection.append(data2) self.viewer.add_data(data2) def test_dependent_axes(self): # Make sure that if we pick a world component that has correlations with # others and is not lined up with the pixel grid, a warning is shown. self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.pixel_component_ids[0] assert self.viewer.options_widget().ui.text_warning.text() == '' self.viewer.state.x_att = self.data.pixel_component_ids[1] assert self.viewer.options_widget().ui.text_warning.text() == '' self.viewer.state.x_att = self.data.pixel_component_ids[2] assert self.viewer.options_widget().ui.text_warning.text() == '' self.viewer.state.x_att = self.data.world_component_ids[0] assert self.viewer.options_widget().ui.text_warning.text() == '' self.viewer.state.x_att = self.data.world_component_ids[1] assert self.viewer.options_widget().ui.text_warning.text() != '' self.viewer.state.x_att = self.data.world_component_ids[2] assert self.viewer.options_widget().ui.text_warning.text() != '' def test_multiple_data(self, tmpdir): # Regression test for issues when multiple datasets are present # and the reference data is not the default one. self.viewer.add_data(self.data) self.viewer.add_data(self.data2) assert self.viewer.layers[0].enabled assert not self.viewer.layers[1].enabled # Make sure that when changing the reference data, which layer # is enabled changes. self.viewer.state.reference_data = self.data2 assert not self.viewer.layers[0].enabled assert self.viewer.layers[1].enabled # Make sure that everything works fine after saving/reloading filename = tmpdir.join('test_multiple_data.glu').strpath self.session.application.save_session(filename) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') viewer = ga.viewers[0][0] assert not viewer.layers[0].enabled assert viewer.layers[1].enabled ga.close() @pytest.mark.parametrize('protocol', [1]) def test_session_back_compat(self, protocol): filename = os.path.join(DATA, 'profile_v{0}.glu'.format(protocol)) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') dc = ga.session.data_collection assert len(dc) == 1 assert dc[0].label == 'array' viewer1 = ga.viewers[0][0] assert len(viewer1.state.layers) == 3 assert viewer1.state.x_att_pixel is dc[0].pixel_component_ids[0] assert_allclose(viewer1.state.x_min, -0.5) assert_allclose(viewer1.state.x_max, 2.5) assert_allclose(viewer1.state.y_min, 13) assert_allclose(viewer1.state.y_max, 63) assert viewer1.state.function == 'maximum' assert not viewer1.state.normalize assert viewer1.state.layers[0].visible assert viewer1.state.layers[1].visible assert viewer1.state.layers[2].visible viewer2 = ga.viewers[0][1] assert viewer2.state.x_att_pixel is dc[0].pixel_component_ids[1] assert_allclose(viewer2.state.x_min, -0.5) assert_allclose(viewer2.state.x_max, 3.5) assert_allclose(viewer2.state.y_min, -0.1) assert_allclose(viewer2.state.y_max, 1.1) assert viewer2.state.function == 'maximum' assert viewer2.state.normalize assert viewer2.state.layers[0].visible assert not viewer2.state.layers[1].visible assert viewer2.state.layers[2].visible viewer3 = ga.viewers[0][2] assert viewer3.state.x_att_pixel is dc[0].pixel_component_ids[2] assert_allclose(viewer3.state.x_min, -0.5) assert_allclose(viewer3.state.x_max, 4.5) assert_allclose(viewer3.state.y_min, -0.4) assert_allclose(viewer3.state.y_max, 4.4) assert viewer3.state.function == 'minimum' assert not viewer3.state.normalize assert viewer3.state.layers[0].visible assert viewer3.state.layers[1].visible assert not viewer3.state.layers[2].visible viewer4 = ga.viewers[0][3] assert viewer4.state.x_att_pixel is dc[0].pixel_component_ids[2] assert_allclose(viewer4.state.x_min, -5.5) assert_allclose(viewer4.state.x_max, 9.5) assert_allclose(viewer4.state.y_min, 27.1) assert_allclose(viewer4.state.y_max, 31.9) assert viewer4.state.function == 'mean' assert not viewer4.state.normalize assert viewer4.state.layers[0].visible assert not viewer4.state.layers[1].visible assert not viewer4.state.layers[2].visible ga.close() glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/data_viewer.py0000644000175000017500000000234513641107542023763 0ustar noahfxnoahfxfrom glue.utils import defer_draw, decorate_all_methods from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer from glue.viewers.profile.qt.layer_style_editor import ProfileLayerStyleEditor from glue.viewers.profile.qt.layer_artist import QThreadedProfileLayerArtist from glue.viewers.profile.qt.options_widget import ProfileOptionsWidget from glue.viewers.profile.state import ProfileViewerState from glue.viewers.profile.qt.profile_tools import ProfileAnalysisTool # noqa from glue.viewers.profile.viewer import MatplotlibProfileMixin __all__ = ['ProfileViewer'] @decorate_all_methods(defer_draw) class ProfileViewer(MatplotlibProfileMixin, MatplotlibDataViewer): LABEL = '1D Profile' _layer_style_widget_cls = ProfileLayerStyleEditor _state_cls = ProfileViewerState _options_cls = ProfileOptionsWidget _data_artist_cls = QThreadedProfileLayerArtist _subset_artist_cls = QThreadedProfileLayerArtist large_data_size = 1e8 allow_duplicate_data = True tools = ['select:xrange', 'profile-analysis'] def __init__(self, session, parent=None, state=None): MatplotlibDataViewer.__init__(self, session, parent=parent, state=state) MatplotlibProfileMixin.setup_callbacks(self) glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/options_widget.ui0000644000175000017500000002610113661230662024512 0ustar noahfxnoahfx Widget 0 0 269 418 1D Profile 5 5 5 5 5 0 General 10 10 10 10 10 5 75 true reference Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QComboBox::AdjustToMinimumContentsLength 75 true function Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true x axis Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 40 5 75 true normalize Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter color: rgb(255, 33, 28) Warning Qt::AlignCenter true QComboBox::AdjustToMinimumContentsLength Qt::Vertical 20 40 Limits 10 10 10 10 10 5 padding: 0px 75 true x axis 75 true y axis Qt::Vertical 20 40 Qt::Horizontal 40 5 log true log true bool_x_log valuetext_x_max button_flip_x valuetext_x_min valuetext_y_min valuetext_y_max bool_y_log label_2 label_5 verticalSpacer horizontalSpacer_2 Axes 5 5 5 5 Legend 5 5 5 5 AxesEditorWidget QWidget
glue.viewers.matplotlib.qt.axes_editor
LegendEditorWidget QWidget
glue.viewers.matplotlib.qt.legend_editor
glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/__init__.py0000644000175000017500000000020513502206677023226 0ustar noahfxnoahfxfrom .data_viewer import ProfileViewer # noqa def setup(): from glue.config import qt_client qt_client.add(ProfileViewer) glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/layer_artist.py0000644000175000017500000000440113641154460024167 0ustar noahfxnoahfximport time from glue.viewers.profile.layer_artist import ProfileLayerArtist from glue.viewers.matplotlib.qt.compute_worker import ComputeWorker from glue.utils import defer_draw __all__ = ['QThreadedProfileLayerArtist'] class QThreadedProfileLayerArtist(ProfileLayerArtist): def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(QThreadedProfileLayerArtist, self).__init__(axes, viewer_state, layer_state=layer_state, layer=layer) self.setup_thread() def wait(self): # Wait 0.5 seconds to make sure that the computation has properly started time.sleep(0.5) while self._worker.running: time.sleep(1 / 25) from glue.utils.qt import process_events process_events() def remove(self): super(QThreadedProfileLayerArtist, self).remove() if self._worker is not None: self._worker.work_queue.put('stop') self._worker.exit() # Need to wait otherwise the thread will be destroyed while still # running, causing a segmentation fault self._worker.wait() self._worker = None @property def is_computing(self): return self._worker is not None and self._worker.running def setup_thread(self): self._worker = ComputeWorker(self._calculate_profile_thread) self._worker.compute_end.connect(self._calculate_profile_postthread) self._worker.compute_error.connect(self._calculate_profile_error) self._worker.compute_start.connect(self.notify_start_computation) self._worker.start() @defer_draw def _calculate_profile(self, reset=False): if self.state.layer is not None and self.state.layer.size > 1e7: self._worker.work_queue.put(reset) else: super(QThreadedProfileLayerArtist, self)._calculate_profile(reset=reset) def _calculate_profile_postthread(self): # If the worker has started running again, we should stop at this point # since this function will get called again. if self._worker is not None and self._worker.running: return super(QThreadedProfileLayerArtist, self)._calculate_profile_postthread() glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/options_widget.py0000644000175000017500000000374513752534424024542 0ustar noahfxnoahfximport os from qtpy import QtWidgets from glue.core.coordinate_helpers import dependent_axes from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui, fix_tab_widget_fontsize __all__ = ['ProfileOptionsWidget'] WARNING_TEXT = ("Warning: the coordinate '{label}' is not aligned with pixel " "grid, so the values shown on the x-axis are approximate.") class ProfileOptionsWidget(QtWidgets.QWidget): def __init__(self, viewer_state, session, parent=None): super(ProfileOptionsWidget, self).__init__(parent=parent) self.ui = load_ui('options_widget.ui', self, directory=os.path.dirname(__file__)) fix_tab_widget_fontsize(self.ui.tab_widget) self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) self._connections_axes = autoconnect_callbacks_to_qt(viewer_state, self.ui.axes_editor.ui) connect_kwargs = {'alpha': dict(value_range=(0, 1))} self._connections_legend = autoconnect_callbacks_to_qt(viewer_state.legend, self.ui.legend_editor.ui, connect_kwargs) self.viewer_state = viewer_state self.session = session self.viewer_state.add_callback('x_att', self._on_attribute_change) self.ui.text_warning.hide() def _on_attribute_change(self, *args): if (self.viewer_state.reference_data is None or self.viewer_state.x_att_pixel is None or self.viewer_state.x_att is self.viewer_state.x_att_pixel): self.ui.text_warning.hide() return world_warning = len(dependent_axes(self.viewer_state.reference_data.coords, self.viewer_state.x_att_pixel.axis)) > 1 if world_warning: self.ui.text_warning.show() self.ui.text_warning.setText(WARNING_TEXT.format(label=self.viewer_state.x_att.label)) else: self.ui.text_warning.hide() self.ui.text_warning.setText('') glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/mouse_mode.py0000644000175000017500000001456613657331513023641 0ustar noahfxnoahfxfrom echo import CallbackProperty, delay_callback from glue.core.state_objects import State from glue.viewers.matplotlib.mouse_mode import MouseMode __all__ = ['NavigateMouseMode', 'RangeMouseMode'] COLOR = (0.0, 0.25, 0.7) class NavigationModeState(State): x = CallbackProperty(None) class NavigateMouseMode(MouseMode): def __init__(self, viewer, press_callback=None): super(NavigateMouseMode, self).__init__(viewer) self.state = NavigationModeState() self.state.add_callback('x', self._update_artist) self.pressed = False self.active = False self._press_callback = press_callback def press(self, event): if not self.active or not event.inaxes: return if self._press_callback is not None: self._press_callback() self.pressed = True self.state.x = event.xdata def move(self, event): if not self.active or not self.pressed or not event.inaxes: return self.state.x = event.xdata def release(self, event): if not self.active: return self.pressed = False def _update_artist(self, *args): if hasattr(self, '_line'): if self.state.x is None: self._line.set_visible(False) else: self._line.set_visible(True) self._line.set_data([self.state.x, self.state.x], [0, 1]) else: if self.state.x is not None: self._line = self._axes.axvline(self.state.x, color=COLOR) self._canvas.draw_idle() def deactivate(self): if hasattr(self, '_line'): self._line.set_visible(False) self._canvas.draw_idle() super(NavigateMouseMode, self).deactivate() self.active = False def activate(self): if hasattr(self, '_line'): self._line.set_visible(True) self._canvas.draw_idle() super(NavigateMouseMode, self).activate() self.active = True def clear(self): self.state.x = None class RangeModeState(State): x_min = CallbackProperty(None) x_max = CallbackProperty(None) @property def x_range(self): return self.x_min, self.x_max PICK_THRESH = 0.02 class RangeMouseMode(MouseMode): def __init__(self, viewer): super(RangeMouseMode, self).__init__(viewer) self.state = RangeModeState() self.state.add_callback('x_min', self._update_artist) self.state.add_callback('x_max', self._update_artist) self.pressed = False self.mode = None self.move_params = None self.active = False def press(self, event): if not self.active or not event.inaxes: return self.pressed = True x_min, x_max = self._axes.get_xlim() x_range = abs(x_max - x_min) if self.state.x_min is None or self.state.x_max is None: self.mode = 'move-x-max' with delay_callback(self.state, 'x_min', 'x_max'): self.state.x_min = event.xdata self.state.x_max = event.xdata elif abs(event.xdata - self.state.x_min) / x_range < PICK_THRESH: self.mode = 'move-x-min' elif abs(event.xdata - self.state.x_max) / x_range < PICK_THRESH: self.mode = 'move-x-max' elif (event.xdata > self.state.x_min) is (event.xdata < self.state.x_max): self.mode = 'move' self.move_params = (event.xdata, self.state.x_min, self.state.x_max) else: self.mode = 'move-x-max' self.state.x_min = event.xdata def move(self, event): if not self.active or not self.pressed or not event.inaxes: return if self.mode == 'move-x-min': self.state.x_min = event.xdata elif self.mode == 'move-x-max': self.state.x_max = event.xdata elif self.mode == 'move': orig_click, orig_x_min, orig_x_max = self.move_params with delay_callback(self.state, 'x_min', 'x_max'): self.state.x_min = orig_x_min + (event.xdata - orig_click) self.state.x_max = orig_x_max + (event.xdata - orig_click) def release(self, event): if not self.active: return self.pressed = False self.mode = None self.move_params def _update_artist(self, *args): y_min, y_max = self._axes.get_ylim() if hasattr(self, '_lines'): if self.state.x_min is None or self.state.x_max is None: self._lines[0].set_visible(False) self._lines[1].set_visible(False) self._interval.set_visible(False) else: self._lines[0].set_data([self.state.x_min, self.state.x_min], [0, 1]) self._lines[1].set_data([self.state.x_max, self.state.x_max], [0, 1]) self._interval.set_xy([[self.state.x_min, 0], [self.state.x_min, 1], [self.state.x_max, 1], [self.state.x_max, 0], [self.state.x_min, 0]]) else: if self.state.x_min is not None and self.state.x_max is not None: self._lines = (self._axes.axvline(self.state.x_min, color=COLOR), self._axes.axvline(self.state.x_max, color=COLOR)) self._interval = self._axes.axvspan(self.state.x_min, self.state.x_max, color=COLOR, alpha=0.05) self._canvas.draw_idle() def deactivate(self): if hasattr(self, '_lines'): self._lines[0].set_visible(False) self._lines[1].set_visible(False) self._interval.set_visible(False) self._canvas.draw_idle() super(RangeMouseMode, self).deactivate() self.active = False def activate(self): if hasattr(self, '_lines'): self._lines[0].set_visible(True) self._lines[1].set_visible(True) self._interval.set_visible(True) self._canvas.draw_idle() super(RangeMouseMode, self).activate() self.active = True def clear(self): with delay_callback(self.state, 'x_min', 'x_max'): self.state.x_min = None self.state.x_max = None glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/profile_tools.py0000644000175000017500000002771213657331513024362 0ustar noahfxnoahfximport os import weakref import traceback from collections import OrderedDict import numpy as np from qtpy.QtCore import Qt from qtpy import QtWidgets, QtGui from glue.utils import color2hex, nanmean, nanmedian, nanmin, nanmax, nansum from glue.config import fit_plugin, viewer_tool from glue.utils.qt import load_ui, fix_tab_widget_fontsize from glue.viewers.profile.qt.mouse_mode import NavigateMouseMode, RangeMouseMode from glue.core.qt.fitters import FitSettingsWidget from glue.utils.qt import Worker from glue.viewers.common.tool import Tool from glue.viewers.image.state import AggregateSlice from glue.core.aggregate import mom1, mom2 from glue.core import BaseData, Subset from glue.viewers.image.qt import ImageViewer from glue.core.link_manager import is_convertible_to_single_pixel_cid from echo import SelectionCallbackProperty from echo.qt import connect_combo_selection __all__ = ['ProfileTools'] MODES = ['navigate', 'fit', 'collapse'] COLLAPSE_FUNCS = OrderedDict([(nanmean, 'Mean'), (nanmedian, 'Median'), (nanmin, 'Minimum'), (nanmax, 'Maximum'), (nansum, 'Sum'), (mom1, 'Moment 1'), (mom2, 'Moment 2')]) @viewer_tool class ProfileAnalysisTool(Tool): action_text = 'Options' tool_id = 'profile-analysis' def __init__(self, viewer): super(ProfileAnalysisTool, self).__init__(viewer) self._profile_tools = ProfileTools(viewer) container_widget = QtWidgets.QSplitter(Qt.Horizontal) plot_widget = viewer.centralWidget() container_widget.addWidget(plot_widget) container_widget.addWidget(self._profile_tools) viewer.setCentralWidget(container_widget) self._profile_tools.enable() self._profile_tools.hide() def activate(self): if self._profile_tools.isVisible(): self._profile_tools.hide() else: self._profile_tools.show() class ProfileTools(QtWidgets.QWidget): fit_function = SelectionCallbackProperty() collapse_function = SelectionCallbackProperty() def __init__(self, parent=None): super(ProfileTools, self).__init__(parent=parent) self._viewer = weakref.ref(parent) self.ui = load_ui('profile_tools.ui', self, directory=os.path.dirname(__file__)) fix_tab_widget_fontsize(self.ui.tabs) self.image_viewer = None @property def viewer(self): return self._viewer() def show(self, *args): super(ProfileTools, self).show(*args) self._on_tab_change() def hide(self, *args): super(ProfileTools, self).hide(*args) self.rng_mode.deactivate() self.nav_mode.deactivate() def enable(self): self.nav_mode = NavigateMouseMode(self.viewer, press_callback=self._on_nav_activate) self.rng_mode = RangeMouseMode(self.viewer) self.nav_mode.state.add_callback('x', self._on_slider_change) self.ui.tabs.setCurrentIndex(0) self.ui.tabs.currentChanged.connect(self._on_tab_change) self.ui.button_settings.clicked.connect(self._on_settings) self.ui.button_fit.clicked.connect(self._on_fit) self.ui.button_clear.clicked.connect(self._on_clear) self.ui.button_collapse.clicked.connect(self._on_collapse) font = QtGui.QFont("Courier") font.setStyleHint(font.Monospace) self.ui.text_log.document().setDefaultFont(font) self.ui.text_log.setLineWrapMode(self.ui.text_log.NoWrap) self.axes = self.viewer.axes self.canvas = self.axes.figure.canvas self._fit_artists = [] ProfileTools.fit_function.set_choices(self, list(fit_plugin)) ProfileTools.fit_function.set_display_func(self, lambda fitter: fitter.label) self._connection_fit = connect_combo_selection(self, 'fit_function', self.ui.combosel_fit_function) ProfileTools.collapse_function.set_choices(self, list(COLLAPSE_FUNCS)) ProfileTools.collapse_function.set_display_func(self, COLLAPSE_FUNCS.get) self._connection_collapse = connect_combo_selection(self, 'collapse_function', self.ui.combosel_collapse_function) self._toolbar_connected = False self.viewer.toolbar_added.connect(self._on_toolbar_added) self.viewer.state.add_callback('x_att', self._on_x_att_change) def _on_x_att_change(self, *event): self.nav_mode.clear() self.rng_mode.clear() def _on_nav_activate(self, *args): self._nav_data = self._visible_data() self._nav_viewers = {} for data in self._nav_data: pix_cid = is_convertible_to_single_pixel_cid(data, self.viewer.state.x_att_pixel) self._nav_viewers[data] = self._viewers_with_data_slice(data, pix_cid) def _on_slider_change(self, *args): x = self.nav_mode.state.x if x is None: return for data in self._nav_data: axis, slc = self._get_axis_and_pixel_slice(data, x) for viewer in self._nav_viewers[data]: slices = list(viewer.state.slices) slices[axis] = slc viewer.state.slices = tuple(slices) def _get_axis_and_pixel_slice(self, data, x): if self.viewer.state.x_att in data.pixel_component_ids: axis = self.viewer.state.x_att.axis slc = int(round(x)) else: pix_cid = is_convertible_to_single_pixel_cid(data, self.viewer.state.x_att_pixel) axis = pix_cid.axis axis_view = [0] * data.ndim axis_view[pix_cid.axis] = slice(None) axis_values = data[self.viewer.state.x_att, axis_view] slc = int(np.argmin(np.abs(axis_values - x))) return axis, slc def _on_settings(self): d = FitSettingsWidget(self.fit_function()) d.exec_() def _on_fit(self): """ Fit a model to the data The fitting happens on a dedicated thread, to keep the UI responsive """ if self.rng_mode.state.x_min is None or self.rng_mode.state.x_max is None: return x_range = self.rng_mode.state.x_range fitter = self.fit_function() def on_success(result): fit_results, x, y = result report = "" normalize = {} for layer_artist in fit_results: report += ("{1}" "".format(color2hex(layer_artist.state.color), layer_artist.layer.label)) report += "
" + fitter.summarize(fit_results[layer_artist], x, y) + "
" if self.viewer.state.normalize: normalize[layer_artist] = layer_artist.state.normalize_values self._report_fit(report) self._plot_fit(fitter, fit_results, x, y, normalize) def on_fail(exc_info): exc = '\n'.join(traceback.format_exception(*exc_info)) self._report_fit("Error during fitting:\n%s" % exc) def on_done(): self.ui.button_fit.setText("Fit") self.ui.button_fit.setEnabled(True) self.canvas.draw_idle() self.ui.button_fit.setText("Running...") self.ui.button_fit.setEnabled(False) w = Worker(self._fit, fitter, xlim=x_range) w.result.connect(on_success) w.error.connect(on_fail) w.finished.connect(on_done) self._fit_worker = w # hold onto a reference w.start() def wait_for_fit(self): self._fit_worker.wait() def _report_fit(self, report): self.ui.text_log.document().setHtml(report) def _on_clear(self): self.ui.text_log.document().setPlainText('') self._clear_fit() self.canvas.draw_idle() def _fit(self, fitter, xlim=None): # We cycle through all the visible layers and get the plotted data # for each one of them. results = {} for layer in self.viewer.layers: if layer.enabled and layer.visible: x, y = layer.state.profile x = np.asarray(x) y = np.asarray(y) keep = (x >= min(xlim)) & (x <= max(xlim)) if len(x) > 0: results[layer] = fitter.build_and_fit(x[keep], y[keep]) return results, x, y def _clear_fit(self): for artist in self._fit_artists[:]: artist.remove() self._fit_artists.remove(artist) def _plot_fit(self, fitter, fit_result, x, y, normalize): self._clear_fit() for layer in fit_result: # y_model = fitter.predict(fit_result[layer], x) self._fit_artists.append(fitter.plot(fit_result[layer], self.axes, x, alpha=layer.state.alpha, linewidth=layer.state.linewidth * 0.5, color=layer.state.color, normalize=normalize.get(layer, None))[0]) self.canvas.draw_idle() def _visible_data(self): datasets = set() for layer_artist in self.viewer.layers: if layer_artist.enabled and layer_artist.visible: if isinstance(layer_artist.state.layer, BaseData): datasets.add(layer_artist.state.layer) elif isinstance(layer_artist.state.layer, Subset): datasets.add(layer_artist.state.layer.data) return list(datasets) def _viewers_with_data_slice(self, data, xatt): if self.viewer.session.application is None: return [] viewers = [] for tab in self.viewer.session.application.viewers: for viewer in tab: if isinstance(viewer, ImageViewer): for layer_artist in viewer._layer_artist_container[data]: if layer_artist.enabled and layer_artist.visible: if len(viewer.state.slices) >= xatt.axis: viewers.append(viewer) return viewers def _on_collapse(self): if self.rng_mode.state.x_min is None or self.rng_mode.state.x_max is None: return func = self.collapse_function x_range = self.rng_mode.state.x_range for data in self._visible_data(): pix_cid = is_convertible_to_single_pixel_cid(data, self.viewer.state.x_att_pixel) for viewer in self._viewers_with_data_slice(data, pix_cid): slices = list(viewer.state.slices) # TODO: don't need to fetch axis twice axis, imin = self._get_axis_and_pixel_slice(data, x_range[0]) axis, imax = self._get_axis_and_pixel_slice(data, x_range[1]) current_slice = slices[axis] if isinstance(current_slice, AggregateSlice): current_slice = current_slice.center imin, imax = min(imin, imax), max(imin, imax) slices[axis] = AggregateSlice(slice(imin, imax), current_slice, func) viewer.state.slices = tuple(slices) @property def mode(self): return MODES[self.tabs.currentIndex()] def _on_toolbar_added(self, *event): self.viewer.toolbar.tool_activated.connect(self._on_toolbar_activate) self.viewer.toolbar.tool_deactivated.connect(self._on_tab_change) def _on_toolbar_activate(self, *event): self.rng_mode.deactivate() self.nav_mode.deactivate() def _on_tab_change(self, *event): mode = self.mode if mode == 'navigate': self.rng_mode.deactivate() self.nav_mode.activate() else: self.rng_mode.activate() self.nav_mode.deactivate() glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/profile_tools.ui0000644000175000017500000001600113502206677024335 0ustar noahfxnoahfx Form 0 0 293 254 Form 1 Navigate Qt::Vertical 20 40 <html><head/><body><p>To <span style=" font-weight:600;">slide </span>through any cubes in image viewers that show the same data as the one here, drag the handle from side to side.</p><p>Note that modifying the sliders in the image viewers will not change the slider here or in other viewers.</p></body></html> Qt::AlignJustify|Qt::AlignVCenter true Qt::Vertical 20 40 Fit Click and drag in the profile to define the range over which to fit models to the data. true Function: Settings Qt::Horizontal 40 20 Fit Clear Collapse Qt::Vertical 20 40 Click and drag in the profile to define the range over which to fit models to the data. true Function: Qt::Horizontal 40 20 Collapse Qt::Horizontal 40 20 Qt::Vertical 20 40 glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/layer_style_editor.ui0000644000175000017500000001237113502206677025365 0ustar noahfxnoahfx Form 0 0 292 150 Form 5 5 5 5 10 5 75 true attribute Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 padding: 0px Qt::Horizontal 40 5 0 0 75 true color Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 100 Qt::Horizontal 75 true opacity Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true linewidth Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true limits Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QComboBox::AdjustToMinimumContentsLength QColorBox QLabel
glue.utils.qt.colors
glueviz-1.0.1+dfsg.orig/glue/viewers/profile/qt/layer_style_editor.py0000644000175000017500000000205413657331513025374 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui class ProfileLayerStyleEditor(QtWidgets.QWidget): def __init__(self, layer, parent=None): super(ProfileLayerStyleEditor, self).__init__(parent=parent) self.ui = load_ui('layer_style_editor.ui', self, directory=os.path.dirname(__file__)) connect_kwargs = {'alpha': dict(value_range=(0, 1))} self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) self.viewer_state = layer.state.viewer_state self.viewer_state.add_callback('normalize', self._on_normalize_change) self._on_normalize_change() def _on_normalize_change(self, *event): self.ui.label_limits.setVisible(self.viewer_state.normalize) self.ui.valuetext_v_min.setVisible(self.viewer_state.normalize) self.ui.valuetext_v_max.setVisible(self.viewer_state.normalize) self.ui.button_flip_limits.setVisible(self.viewer_state.normalize) glueviz-1.0.1+dfsg.orig/glue/viewers/profile/python_export.py0000644000175000017500000000446313661230662023774 0ustar noahfxnoahfxfrom glue.viewers.common.python_export import serialize_options from glue.core import Subset def python_export_profile_layer(layer, *args): if len(layer.mpl_artists) == 0 or not layer.enabled or not layer.visible: return [], None script = "" imports = ["import numpy as np"] script += "# Calculate the profile of the data\n" script += "profile_axis = {0}\n".format(layer._viewer_state.x_att_pixel.axis) script += "collapsed_axes = tuple(i for i in range(layer_data.ndim) if i != profile_axis)\n" if isinstance(layer.state.layer, Subset): script += "base_data = layer_data.data\n" script += "cid = base_data.find_component_id('{0}')\n".format(layer.state.attribute.label) script += "profile_values = base_data.compute_statistic('{0}', cid, axis=collapsed_axes, subset_state=layer_data.subset_state)\n\n".format(layer._viewer_state.function) else: script += "cid = layer_data.find_component_id('{0}')\n".format(layer.state.attribute.label) script += "profile_values = layer_data.compute_statistic('{0}', cid, axis=collapsed_axes)\n\n".format(layer._viewer_state.function) script += "# Extract the values for the x-axis\n" script += "axis_view = [0] * layer_data.ndim\n" script += "axis_view[profile_axis] = slice(None)\n" script += "profile_x_values = layer_data['{0}', tuple(axis_view)]\n".format(layer._viewer_state.x_att) script += "keep = ~np.isnan(profile_values) & ~np.isnan(profile_x_values)\n\n" if layer._viewer_state.normalize: script += "# Normalize the profile data\n" script += "vmax = np.nanmax(profile_values)\n" script += "vmin = np.nanmin(profile_values)\n" script += "profile_values = (profile_values - vmin)/(vmax - vmin)\n\n" script += "# Plot the profile\n" plot_options = dict(color=layer.state.color, linewidth=layer.state.linewidth, alpha=layer.state.alpha, zorder=layer.state.zorder, drawstyle='steps-mid') script += "handle, = ax.plot(profile_x_values[keep], profile_values[keep], '-', {0})\n".format(serialize_options(plot_options)) script += "legend_handles.append(handle)\n" script += "legend_labels.append(layer_data.label)\n\n" return imports, script.strip() glueviz-1.0.1+dfsg.orig/glue/viewers/profile/state.py0000644000175000017500000002650013657331513022170 0ustar noahfxnoahfxfrom collections import OrderedDict import numpy as np from glue.core import Subset from echo import delay_callback from glue.viewers.matplotlib.state import (MatplotlibDataViewerState, MatplotlibLayerState, DeferredDrawCallbackProperty as DDCProperty, DeferredDrawSelectionCallbackProperty as DDSCProperty) from glue.core.data_combo_helper import ManualDataComboHelper, ComponentIDComboHelper from glue.utils import defer_draw, nanmin, nanmax from glue.core.link_manager import is_convertible_to_single_pixel_cid from glue.core.exceptions import IncompatibleDataException __all__ = ['ProfileViewerState', 'ProfileLayerState'] FUNCTIONS = OrderedDict([('maximum', 'Maximum'), ('minimum', 'Minimum'), ('mean', 'Mean'), ('median', 'Median'), ('sum', 'Sum')]) class ProfileViewerState(MatplotlibDataViewerState): """ A state class that includes all the attributes for a Profile viewer. """ x_att_pixel = DDCProperty(docstring='The component ID giving the pixel component ' 'shown on the x axis') x_att = DDSCProperty(docstring='The component ID giving the pixel or world component ' 'shown on the x axis') reference_data = DDSCProperty(docstring='The dataset that is used to define the ' 'available pixel/world components, and ' 'which defines the coordinate frame in ' 'which the images are shown') function = DDSCProperty(docstring='The function to use for collapsing data') normalize = DDCProperty(False, docstring='Whether to normalize all profiles ' 'to the [0:1] range') # TODO: add function to use def __init__(self, **kwargs): super(ProfileViewerState, self).__init__() self.ref_data_helper = ManualDataComboHelper(self, 'reference_data') self.add_callback('layers', self._layers_changed) self.add_callback('reference_data', self._reference_data_changed) self.add_callback('x_att', self._update_att) self.add_callback('normalize', self._reset_y_limits) self.x_att_helper = ComponentIDComboHelper(self, 'x_att', numeric=False, datetime=False, categorical=False, pixel_coord=True) ProfileViewerState.function.set_choices(self, list(FUNCTIONS)) ProfileViewerState.function.set_display_func(self, FUNCTIONS.get) self.update_from_dict(kwargs) def _update_combo_ref_data(self): self.ref_data_helper.set_multiple_data(self.layers_data) def reset_limits(self): with delay_callback(self, 'x_min', 'x_max', 'y_min', 'y_max'): self._reset_x_limits() self._reset_y_limits() @property def _display_world(self): return getattr(self.reference_data, 'coords', None) is not None @defer_draw def _update_att(self, *args): if self.x_att is not None: if self._display_world: if self.x_att in self.reference_data.pixel_component_ids: self.x_att_pixel = self.x_att else: index = self.reference_data.world_component_ids.index(self.x_att) self.x_att_pixel = self.reference_data.pixel_component_ids[index] else: self.x_att_pixel = self.x_att self._reset_x_limits() def _reset_x_limits(self, *event): # NOTE: we don't use AttributeLimitsHelper because we need to avoid # trying to get the minimum of *all* the world coordinates in the # dataset. Instead, we use the same approach as in the layer state below # and in the case of world coordinates we use online the spine of the # data. if self.reference_data is None or self.x_att_pixel is None: return data = self.reference_data if self.x_att in data.pixel_component_ids: x_min, x_max = -0.5, data.shape[self.x_att.axis] - 0.5 else: axis = data.world_component_ids.index(self.x_att) axis_view = [0] * data.ndim axis_view[axis] = slice(None) axis_values = data[self.x_att, tuple(axis_view)] x_min, x_max = np.nanmin(axis_values), np.nanmax(axis_values) with delay_callback(self, 'x_min', 'x_max'): self.x_min = x_min self.x_max = x_max def _reset_y_limits(self, *event): if self.normalize: with delay_callback(self, 'y_min', 'y_max'): self.y_min = -0.1 self.y_max = +1.1 def flip_x(self): """ Flip the x_min/x_max limits. """ with delay_callback(self, 'x_min', 'x_max'): self.x_min, self.x_max = self.x_max, self.x_min @defer_draw def _layers_changed(self, *args): self._update_combo_ref_data() @defer_draw def _reference_data_changed(self, *args): for layer in self.layers: layer.reset_cache() # This signal can get emitted if just the choices but not the actual # reference data change, so we check here that the reference data has # actually changed if self.reference_data is not getattr(self, '_last_reference_data', None): self._last_reference_data = self.reference_data with delay_callback(self, 'x_att'): if self.reference_data is None: self.x_att_helper.set_multiple_data([]) else: self.x_att_helper.set_multiple_data([self.reference_data]) if self._display_world: self.x_att_helper.world_coord = True self.x_att = self.reference_data.world_component_ids[0] else: self.x_att_helper.world_coord = False self.x_att = self.reference_data.pixel_component_ids[0] self._update_att() def _update_priority(self, name): if name == 'layers': return 2 elif name == 'reference_data': return 1.5 elif name.endswith(('_min', '_max')): return 0 else: return 1 class ProfileLayerState(MatplotlibLayerState): """ A state class that includes all the attributes for layers in a Profile plot. """ linewidth = DDCProperty(1, docstring='The width of the line') attribute = DDSCProperty(docstring='The attribute shown in the layer') v_min = DDCProperty(docstring='The lower level shown') v_max = DDCProperty(docstring='The upper level shown') percentile = DDSCProperty(docstring='The percentile value used to ' 'automatically calculate levels') _viewer_callbacks_set = False _profile_cache = None def __init__(self, layer=None, viewer_state=None, **kwargs): super(ProfileLayerState, self).__init__(layer=layer, viewer_state=viewer_state) self.attribute_att_helper = ComponentIDComboHelper(self, 'attribute', numeric=True, categorical=False) percentile_display = {100: 'Min/Max', 99.5: '99.5%', 99: '99%', 95: '95%', 90: '90%', 'Custom': 'Custom'} ProfileLayerState.percentile.set_choices(self, [100, 99.5, 99, 95, 90, 'Custom']) ProfileLayerState.percentile.set_display_func(self, percentile_display.get) self.add_callback('layer', self._update_attribute, priority=1000) if layer is not None: self._update_attribute() self.update_from_dict(kwargs) def _update_attribute(self, *args): if self.layer is not None: self.attribute_att_helper.set_multiple_data([self.layer]) @property def independent_x_att(self): return is_convertible_to_single_pixel_cid(self.layer, self.viewer_state.x_att) is not None def normalize_values(self, values): return (np.asarray(values) - self.v_min) / (self.v_max - self.v_min) def reset_cache(self, *args): self._profile_cache = None @property def viewer_state(self): return self._viewer_state @viewer_state.setter def viewer_state(self, viewer_state): self._viewer_state = viewer_state @property def profile(self): self.update_profile() return self._profile_cache def update_profile(self, update_limits=True): if self._profile_cache is not None: return self._profile_cache if not self.visible: return if not self._viewer_callbacks_set: self.viewer_state.add_callback('x_att', self.reset_cache, priority=100000) self.viewer_state.add_callback('function', self.reset_cache, priority=100000) if self.is_callback_property('attribute'): self.add_callback('attribute', self.reset_cache, priority=100000) self._viewer_callbacks_set = True if self.viewer_state is None or self.viewer_state.x_att is None or self.attribute is None: raise IncompatibleDataException() # Check what pixel axis in the current dataset x_att corresponds to pix_cid = is_convertible_to_single_pixel_cid(self.layer, self.viewer_state.x_att_pixel) if pix_cid is None: raise IncompatibleDataException() # If we get here, then x_att does correspond to a single pixel axis in # the cube, so we now prepare a list of axes to collapse over. axes = tuple(i for i in range(self.layer.ndim) if i != pix_cid.axis) # We now get the y values for the data # TODO: in future we should optimize the case where the mask is much # smaller than the data to just average the relevant 'spaxels' in the # data rather than collapsing the whole cube. if isinstance(self.layer, Subset): data = self.layer.data subset_state = self.layer.subset_state else: data = self.layer subset_state = None profile_values = data.compute_statistic(self.viewer_state.function, self.attribute, axis=axes, subset_state=subset_state) if np.all(np.isnan(profile_values)): self._profile_cache = [], [] else: axis_view = [0] * data.ndim axis_view[pix_cid.axis] = slice(None) axis_values = data[self.viewer_state.x_att, tuple(axis_view)] self._profile_cache = axis_values, profile_values if update_limits: self.update_limits(update_profile=False) def update_limits(self, update_profile=True): with delay_callback(self, 'v_min', 'v_max'): if update_profile: self.update_profile(update_limits=False) if self._profile_cache is not None and len(self._profile_cache[1]) > 0: self.v_min = nanmin(self._profile_cache[1]) self.v_max = nanmax(self._profile_cache[1]) glueviz-1.0.1+dfsg.orig/glue/viewers/profile/__init__.py0000644000175000017500000000000013502206677022573 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/profile/layer_artist.py0000644000175000017500000001517413661230662023555 0ustar noahfxnoahfximport sys import warnings import numpy as np from matplotlib.lines import Line2D from glue.core import BaseData from glue.utils import defer_draw, nanmin, nanmax from glue.viewers.profile.state import ProfileLayerState from glue.viewers.matplotlib.layer_artist import MatplotlibLayerArtist from glue.core.exceptions import IncompatibleAttribute, IncompatibleDataException from glue.viewers.profile.python_export import python_export_profile_layer class ProfileLayerArtist(MatplotlibLayerArtist): _layer_state_cls = ProfileLayerState _python_exporter = python_export_profile_layer def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(ProfileLayerArtist, self).__init__(axes, viewer_state, layer_state=layer_state, layer=layer) # Watch for changes in the viewer state which would require the # layers to be redrawn self._viewer_state.add_global_callback(self._update_profile) self.state.add_global_callback(self._update_profile) self.plot_artist = self.axes.plot([1, 2, 3], [3, 4, 5], 'k-', drawstyle='steps-mid')[0] self.mpl_artists = [self.plot_artist] @defer_draw def _calculate_profile(self, reset=False): try: self.notify_start_computation() self._calculate_profile_thread(reset=reset) except Exception: self._calculate_profile_error(sys.exc_info()) else: self._calculate_profile_postthread() def _calculate_profile_thread(self, reset=False): # We need to ignore any warnings that happen inside the thread # otherwise the thread tries to send these to the glue logger (which # uses Qt), which then results in this kind of error: # QObject::connect: Cannot queue arguments of type 'QTextCursor' with warnings.catch_warnings(): warnings.simplefilter("ignore") if reset: self.state.reset_cache() self.state.update_profile(update_limits=False) def _calculate_profile_postthread(self): self.notify_end_computation() # It's possible for this method to get called but for the state to have # been updated in the mean time to have a histogram that raises an # exception (for example an IncompatibleAttribute). If any errors happen # here, we simply ignore them since _calculate_histogram_error will get # called directly. try: visible_data = self.state.profile except Exception: return self.enable() # The following can happen if self.state.visible is None - in this case # we just terminate early. If the visible property is changed, it will # trigger the _calculate_profile code to re-run. if visible_data is None: return x, y = visible_data # Update the data values. if len(x) > 0: self.state.update_limits() # Normalize profile values to the [0:1] range based on limits if self._viewer_state.normalize: y = self.state.normalize_values(y) self.plot_artist.set_data(x, y) else: # We need to do this otherwise we get issues on Windows when # passing an empty list to plot_artist self.plot_artist.set_data([0.], [0.]) # TODO: the following was copy/pasted from the histogram viewer, maybe # we can find a way to avoid duplication? # We have to do the following to make sure that we reset the y_max as # needed. We can't simply reset based on the maximum for this layer # because other layers might have other values, and we also can't do: # # self._viewer_state.y_max = max(self._viewer_state.y_max, result[0].max()) # # because this would never allow y_max to get smaller. if not self._viewer_state.normalize and len(y) > 0: y_min = nanmin(y) y_max = nanmax(y) y_range = y_max - y_min self.state._y_min = y_min - y_range * 0.1 self.state._y_max = y_max + y_range * 0.1 largest_y_max = max(getattr(layer, '_y_max', 0) for layer in self._viewer_state.layers) if largest_y_max != self._viewer_state.y_max: self._viewer_state.y_max = largest_y_max smallest_y_min = min(getattr(layer, '_y_min', np.inf) for layer in self._viewer_state.layers) if smallest_y_min != self._viewer_state.y_min: self._viewer_state.y_min = smallest_y_min self.redraw() @defer_draw def _calculate_profile_error(self, exc): self.plot_artist.set_visible(False) self.notify_end_computation() self.redraw() if issubclass(exc[0], IncompatibleAttribute): if isinstance(self.state.layer, BaseData): self.disable_invalid_attributes(self.state.attribute) else: self.disable_incompatible_subset() elif issubclass(exc[0], IncompatibleDataException): self.disable("Incompatible data") @defer_draw def _update_visual_attributes(self): if not self.enabled: return for mpl_artist in self.mpl_artists: mpl_artist.set_visible(self.state.visible) mpl_artist.set_zorder(self.state.zorder) mpl_artist.set_color(self.state.color) mpl_artist.set_alpha(self.state.alpha) mpl_artist.set_linewidth(self.state.linewidth) self.redraw() def _update_profile(self, force=False, **kwargs): if (self._viewer_state.x_att is None or self.state.attribute is None or self.state.layer is None): return changed = set() if force else self.pop_changed_properties() if force or any(prop in changed for prop in ('layer', 'x_att', 'attribute', 'function', 'normalize', 'v_min', 'v_max', 'visible')): self._calculate_profile(reset=force) if force or any(prop in changed for prop in ('alpha', 'color', 'zorder', 'linewidth')): self._update_visual_attributes() @defer_draw def update(self): self.state.reset_cache() self._update_profile(force=True) self.redraw() def get_handle_legend(self): if self.enabled and self.state.visible: handle = Line2D([0], [0], alpha=self.state.alpha, linestyle="-", linewidth=self.state.linewidth, color=self.get_layer_color()) return handle, self.layer.label, None else: return None, None, None glueviz-1.0.1+dfsg.orig/glue/viewers/profile/viewer.py0000644000175000017500000000237213605357235022354 0ustar noahfxnoahfxfrom glue.core.subset import roi_to_subset_state __all__ = ['MatplotlibProfileMixin'] class MatplotlibProfileMixin(object): def setup_callbacks(self): self.state.add_callback('x_att', self._update_axes) self.state.add_callback('normalize', self._update_axes) def _update_axes(self, *args): if self.state.x_att is not None: self.state.x_axislabel = self.state.x_att.label if self.state.normalize: self.state.y_axislabel = 'Normalized data values' else: self.state.y_axislabel = 'Data values' self.axes.figure.canvas.draw_idle() def apply_roi(self, roi, override_mode=None): # Force redraw to get rid of ROI. We do this because applying the # subset state below might end up not having an effect on the viewer, # for example there may not be any layers, or the active subset may not # be one of the layers. So we just explicitly redraw here to make sure # a redraw will happen after this method is called. self.redraw() if len(self.layers) == 0: return subset_state = roi_to_subset_state(roi, x_att=self.state.x_att) self.apply_subset_state(subset_state, override_mode=override_mode) glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/0000755000175000017500000000000013752535025021030 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/histogram/tests/0000755000175000017500000000000013752535025022172 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/histogram/tests/__init__.py0000644000175000017500000000000013455362716024276 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/histogram/tests/test_layer_artist.py0000644000175000017500000000667113662766630026327 0ustar noahfxnoahfxfrom collections import Counter import sys from glue.core import Data, DataCollection from ..layer_artist import HistogramLayerArtist from ..state import HistogramViewerState from matplotlib import pyplot as plt class CallCounter(object): def __init__(self): self._counter = Counter() def __call__(self, frame, event, arg): if event == 'call': self._counter[frame.f_code.co_name] += 1 def __getitem__(self, item): return self._counter[item] class TestHistogramLayerArtist(object): def setup_method(self, method): self.viewer_state = HistogramViewerState() ax = plt.subplot(1, 1, 1) self.data = Data(x=[1, 2, 3], y=[2, 3, 4]) self.subset = self.data.new_subset() self.subset.subset_state = self.data.id['x'] > 1 dc = DataCollection([self.data]) # TODO: The following line shouldn't be needed self.viewer_state.data_collection = dc self.artist = HistogramLayerArtist(ax, self.viewer_state, layer=self.subset) self.layer_state = self.artist.state self.viewer_state.layers.append(self.layer_state) self.call_counter = CallCounter() sys.setprofile(self.call_counter) def teardown_method(self, method): self.artist.remove() sys.setprofile(None) def test_recalc_on_state_changes(self): assert self.call_counter['_calculate_histogram'] == 0 assert self.call_counter['_update_artists'] == 0 # attribute self.viewer_state.x_att = self.data.id['y'] assert self.call_counter['_calculate_histogram'] == 1 assert self.call_counter['_update_artists'] == 1 # lo self.viewer_state.hist_x_min = -1 assert self.call_counter['_calculate_histogram'] == 2 assert self.call_counter['_update_artists'] == 2 # hi self.viewer_state.hist_x_max = 5 assert self.call_counter['_calculate_histogram'] == 3 assert self.call_counter['_update_artists'] == 3 # nbins self.viewer_state.hist_n_bin += 1 assert self.call_counter['_calculate_histogram'] == 4 assert self.call_counter['_update_artists'] == 4 # xlog self.viewer_state.x_log ^= True assert self.call_counter['_calculate_histogram'] == 5 assert self.call_counter['_update_artists'] == 5 # TODO: find a way to determine whether the histogram calculation is # carried out since _calculate_histogram calls are no longer a good # way to find out (we now rely on state cache) # ylog -- no call self.viewer_state.y_log ^= True # assert self.call_counter['_calculate_histogram'] == 5 assert self.call_counter['_update_artists'] == 6 # cumulative -- no call self.viewer_state.cumulative ^= True # assert self.call_counter['_calculate_histogram'] == 5 assert self.call_counter['_update_artists'] == 7 # normed -- no call self.viewer_state.normalize ^= True # assert self.call_counter['_calculate_histogram'] == 5 assert self.call_counter['_update_artists'] == 8 # subset style -- no call self.subset.style.color = '#00ff00' # assert self.call_counter['_calculate_histogram'] == 5 assert self.call_counter['_update_artists'] == 8 # legend -- no call self.viewer_state.show_legend = True assert self.call_counter['_update_artists'] == 8 glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/0000755000175000017500000000000013752535025021454 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/tests/0000755000175000017500000000000013752535025022616 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/tests/test_python_export.py0000644000175000017500000000431113752534424027152 0ustar noahfxnoahfxfrom astropy.utils import NumpyRNGContext from glue.core import Data, DataCollection from glue.app.qt.application import GlueApplication from glue.viewers.histogram.qt import HistogramViewer from glue.viewers.matplotlib.qt.tests.test_python_export import BaseTestExportPython, random_with_nan class TestExportPython(BaseTestExportPython): def setup_method(self, method): with NumpyRNGContext(12345): self.data = Data(**dict((name, random_with_nan(100, nan_index=idx + 1)) for idx, name in enumerate('abcdefgh'))) self.data_collection = DataCollection([self.data]) self.app = GlueApplication(self.data_collection) self.viewer = self.app.new_data_viewer(HistogramViewer) self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['a'] def teardown_method(self, method): self.viewer.close() self.viewer = None self.app.close() self.app = None def test_simple(self, tmpdir): self.assert_same(tmpdir) def test_simple_visual(self, tmpdir): self.viewer.state.layers[0].color = 'blue' self.viewer.state.layers[0].alpha = 0.5 self.assert_same(tmpdir) def test_simple_visual_legend(self, tmpdir): self.viewer.state.legend.visible = True self.viewer.state.layers[0].color = 'blue' self.viewer.state.layers[0].alpha = 0.5 self.assert_same(tmpdir) def test_cumulative(self, tmpdir): self.viewer.state.cumulative = True self.assert_same(tmpdir) def test_normalize(self, tmpdir): self.viewer.state.normalize = True self.assert_same(tmpdir) def test_subset(self, tmpdir): self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) self.assert_same(tmpdir) def test_subset_legend(self, tmpdir): self.viewer.state.legend.visible = True self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) self.assert_same(tmpdir) def test_empty(self, tmpdir): self.viewer.state.x_min = 10 self.viewer.state.x_max = 11 self.viewer.state.hist_x_min = 10 self.viewer.state.hist_x_max = 11 self.assert_same(tmpdir) glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/tests/__init__.py0000644000175000017500000000000013455362716024722 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/tests/data/0000755000175000017500000000000013752535025023527 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/tests/data/histogram_v0.glu0000644000175000017500000002117213502206677026646 0ustar noahfxnoahfx{ "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEwLCksIH0gICAgICAgICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAcAAAAAAAAACAAAAAAAAAAJAAAAAAAAAA==" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEwLCksIH0gICAgICAgICAgIAoAAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAGAAAAAAAAAAgAAAAAAAAACgAAAAAAAAAMAAAAAAAAAA4AAAAAAAAAEAAAAAAAAAASAAAAAAAAAA==" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "a", "Pixel Axis 0 [x]", "World 0", "b" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0" ], "data": [ "data" ], "groups": [ "Subset 1_0" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0" ], "subset_group_count": 1 }, "HistogramWidget": { "_type": "glue.viewers.histogram.qt.viewer_widget.HistogramWidget", "layers": [ { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "hi": 9, "layer": "data", "lo": 0, "nbins": 6.0, "visible": true, "xlog": false, "zorder": 1 }, { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "hi": 9, "layer": "Subset 1", "lo": 0, "nbins": 6.0, "visible": false, "xlog": false, "zorder": 2 } ], "pos": [ 0, 0 ], "properties": { "autoscale": true, "component": "a", "cumulative": false, "nbins": 6.0, "normed": false, "xlog": false, "xmax": 9.0, "xmin": 0.0, "ylog": false }, "session": "Session", "size": [ 600, 400 ] }, "HistogramWidget_0": { "_type": "glue.viewers.histogram.qt.viewer_widget.HistogramWidget", "layers": [ { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "hi": 16.0, "layer": "data", "lo": 2.0, "nbins": 8.0, "visible": true, "xlog": false, "zorder": 1 }, { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "hi": 16.0, "layer": "Subset 1", "lo": 2.0, "nbins": 8.0, "visible": true, "xlog": false, "zorder": 2 } ], "pos": [ 502, 362 ], "properties": { "autoscale": true, "component": "b", "cumulative": false, "nbins": 8.0, "normed": false, "xlog": false, "xmax": 16.0, "xmin": 2.0, "ylog": false }, "session": "Session", "size": [ 600, 400 ] }, "HistogramWidget_1": { "_type": "glue.viewers.histogram.qt.viewer_widget.HistogramWidget", "layers": [ { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "hi": 9, "layer": "data", "lo": 0, "nbins": 10.0, "visible": true, "xlog": false, "zorder": 1 }, { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "hi": 9, "layer": "Subset 1", "lo": 0, "nbins": 10.0, "visible": true, "xlog": false, "zorder": 2 } ], "pos": [ 502, 0 ], "properties": { "autoscale": true, "component": "a", "cumulative": false, "nbins": 10.0, "normed": true, "xlog": false, "xmax": 9.0, "xmin": 0.0, "ylog": true }, "session": "Session", "size": [ 600, 400 ] }, "HistogramWidget_2": { "_type": "glue.viewers.histogram.qt.viewer_widget.HistogramWidget", "layers": [ { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "hi": 10.0, "layer": "data", "lo": -1.0, "nbins": 4.0, "visible": true, "xlog": false, "zorder": 1 }, { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "hi": 10.0, "layer": "Subset 1", "lo": -1.0, "nbins": 4.0, "visible": true, "xlog": false, "zorder": 2 } ], "pos": [ 0, 362 ], "properties": { "autoscale": true, "component": "a", "cumulative": true, "nbins": 4.0, "normed": false, "xlog": false, "xmax": 10.0, "xmin": -1.0, "ylog": false }, "session": "Session", "size": [ 600, 400 ] }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "RangeSubsetState": { "_type": "glue.core.subset.RangeSubsetState", "att": "a", "hi": 4.5, "lo": 1.5 }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "Subset 1_0": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "RangeSubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1" ] }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue_vispy_viewers.scatter", "glue.plugins.tools.spectrum_tool", "glue.viewers.table", "glue.viewers.histogram", "glue.viewers.scatter", "glue.viewers.image", "glue.plugins.tools.pv_slicer", "glue.plugins.coordinate_helpers", "glue.plugins.exporters.plotly", "glue.plugins.export_d3po", "glue.core.data_exporters", "glue_h5part", "glue_vispy_viewers.volume" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "HistogramWidget", "HistogramWidget_0", "HistogramWidget_1", "HistogramWidget_2" ] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "data": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component" ], [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "b", "Component_0" ] ], "coords": "Coordinates", "label": "data", "primary_owner": [ "a", "Pixel Axis 0 [x]", "World 0", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1" ], "uuid": "103f82ee-7731-4816-b207-164b683762d2" } }glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/tests/data/histogram_v1.glu0000644000175000017500000002714713644362032026652 0ustar noahfxnoahfx{ "CallbackList": { "_type": "glue.external.echo.list.CallbackList", "values": [ "HistogramLayerState", "HistogramLayerState_0" ] }, "CallbackList_0": { "_type": "glue.external.echo.list.CallbackList", "values": [ "HistogramLayerState_1", "HistogramLayerState_2" ] }, "CallbackList_1": { "_type": "glue.external.echo.list.CallbackList", "values": [ "HistogramLayerState_3", "HistogramLayerState_4" ] }, "CallbackList_2": { "_type": "glue.external.echo.list.CallbackList", "values": [ "HistogramLayerState_5", "HistogramLayerState_6" ] }, "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEwLCksIH0gICAgICAgICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAcAAAAAAAAACAAAAAAAAAAJAAAAAAAAAA==" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEwLCksIH0gICAgICAgICAgIAoAAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAGAAAAAAAAAAgAAAAAAAAACgAAAAAAAAAMAAAAAAAAAA4AAAAAAAAAEAAAAAAAAAASAAAAAAAAAA==" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "a", "Pixel Axis 0 [x]", "World 0", "b" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0" ], "data": [ "data" ], "groups": [ "Subset 1" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2" ], "subset_group_count": 1 }, "HistogramLayerState": { "_type": "glue.viewers.histogram.state.HistogramLayerState", "values": { "alpha": 0.8, "color": "st__0.35", "layer": "data", "visible": true, "zorder": 2 } }, "HistogramLayerState_0": { "_type": "glue.viewers.histogram.state.HistogramLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "visible": false, "zorder": 3 } }, "HistogramLayerState_1": { "_type": "glue.viewers.histogram.state.HistogramLayerState", "values": { "alpha": 0.8, "color": "st__0.35", "layer": "data", "visible": true, "zorder": 2 } }, "HistogramLayerState_2": { "_type": "glue.viewers.histogram.state.HistogramLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "visible": true, "zorder": 3 } }, "HistogramLayerState_3": { "_type": "glue.viewers.histogram.state.HistogramLayerState", "values": { "alpha": 0.8, "color": "st__0.35", "layer": "data", "visible": true, "zorder": 2 } }, "HistogramLayerState_4": { "_type": "glue.viewers.histogram.state.HistogramLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "visible": true, "zorder": 3 } }, "HistogramLayerState_5": { "_type": "glue.viewers.histogram.state.HistogramLayerState", "values": { "alpha": 0.8, "color": "st__0.35", "layer": "data", "visible": true, "zorder": 2 } }, "HistogramLayerState_6": { "_type": "glue.viewers.histogram.state.HistogramLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "visible": true, "zorder": 3 } }, "HistogramViewer": { "_protocol": 1, "_type": "glue.viewers.histogram.qt.data_viewer.HistogramViewer", "layers": [ { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "state": "HistogramLayerState" }, { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "state": "HistogramLayerState_0" } ], "pos": [ 664, 475 ], "session": "Session", "size": [ 663, 473 ], "state": { "values": { "common_n_bin": true, "cumulative": false, "hist_n_bin": 6, "hist_x_max": 9.0, "hist_x_min": 0.0, "layers": "CallbackList", "normalize": false, "x_att": "a", "x_log": false, "x_max": 9.0, "x_min": 0.0, "y_log": false, "y_max": 2.4, "y_min": 0 } } }, "HistogramViewer_0": { "_protocol": 1, "_type": "glue.viewers.histogram.qt.data_viewer.HistogramViewer", "layers": [ { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "state": "HistogramLayerState_1" }, { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "state": "HistogramLayerState_2" } ], "pos": [ 0, 475 ], "session": "Session", "size": [ 664, 473 ], "state": { "values": { "common_n_bin": true, "cumulative": false, "hist_n_bin": 8, "hist_x_max": 16.0, "hist_x_min": 2.0, "layers": "CallbackList_0", "normalize": false, "x_att": "b", "x_log": false, "x_max": 16.0, "x_min": 2.0, "y_log": false, "y_max": 1.2, "y_min": 0 } } }, "HistogramViewer_1": { "_protocol": 1, "_type": "glue.viewers.histogram.qt.data_viewer.HistogramViewer", "layers": [ { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "state": "HistogramLayerState_3" }, { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "state": "HistogramLayerState_4" } ], "pos": [ 664, 0 ], "session": "Session", "size": [ 663, 475 ], "state": { "values": { "common_n_bin": true, "cumulative": false, "hist_n_bin": 10, "hist_x_max": 9.0, "hist_x_min": 0.0, "layers": "CallbackList_1", "normalize": true, "x_att": "a", "x_log": false, "x_max": 9.0, "x_min": 0.0, "y_log": true, "y_max": 0.7407407407407407, "y_min": 0.01111111111111111 } } }, "HistogramViewer_2": { "_protocol": 1, "_type": "glue.viewers.histogram.qt.data_viewer.HistogramViewer", "layers": [ { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "state": "HistogramLayerState_5" }, { "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", "state": "HistogramLayerState_6" } ], "pos": [ 0, 0 ], "session": "Session", "size": [ 664, 475 ], "state": { "values": { "common_n_bin": true, "cumulative": true, "hist_n_bin": 4, "hist_x_max": 10.0, "hist_x_min": -1.0, "layers": "CallbackList_2", "normalize": false, "x_att": "a", "x_log": false, "x_max": 10.0, "x_min": -1.0, "y_log": false, "y_max": 12.0, "y_min": 0 } } }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "RangeSubsetState": { "_type": "glue.core.subset.RangeSubsetState", "att": "a", "hi": 4.5, "lo": 1.5 }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "RangeSubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1_0" ] }, "Subset 1_0": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue_vispy_viewers.scatter", "glue.plugins.coordinate_helpers", "glue.plugins.exporters.plotly", "glue.viewers.table", "glue.core.data_exporters", "glue_medical", "glue.plugins.tools.pv_slicer", "specviz.app", "glue.viewers.scatter", "glue.viewers.histogram", "glue_vispy_viewers.volume", "glue.plugins.tools.spectrum_tool", "glue.plugins.export_d3po", "glue.viewers.image" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "HistogramViewer", "HistogramViewer_0", "HistogramViewer_1", "HistogramViewer_2" ] ] }, "a": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "a" }, "b": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "b" }, "data": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "a", "Component" ], [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "b", "Component_0" ] ], "coords": "Coordinates", "label": "data", "primary_owner": [ "a", "Pixel Axis 0 [x]", "World 0", "b" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.8, "color": "0.35", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1_0" ], "uuid": "103f82ee-7731-4816-b207-164b683762d2" } } glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/tests/test_data_viewer.py0000644000175000017500000006340413752534424026532 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import os from collections import Counter import pytest import numpy as np from numpy.testing import assert_equal, assert_allclose from glue.core.message import SubsetUpdateMessage from glue.core import HubListener, Data from glue.core.roi import XRangeROI, RectangularROI from glue.core.subset import RangeSubsetState, CategoricalROISubsetState, RoiSubsetState from glue import core from glue.app.qt import GlueApplication from glue.core.component_id import ComponentID from glue.utils.qt import combo_as_string, process_events from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer from glue.core.state import GlueUnSerializer from glue.app.qt.layer_tree_widget import LayerTreeWidget from glue.tests.helpers import requires_matplotlib_ge_22 from ..data_viewer import HistogramViewer DATA = os.path.join(os.path.dirname(__file__), 'data') class TestHistogramCommon(BaseTestMatplotlibDataViewer): def init_data(self): return Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=['a', 'b', 'c', 'a']) viewer_cls = HistogramViewer class TestHistogramViewer(object): def setup_method(self, method): self.data = Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=['a', 'b', 'c', 'a']) self.app = GlueApplication() self.session = self.app.session self.hub = self.session.hub self.data_collection = self.session.data_collection self.data_collection.append(self.data) self.viewer = self.app.new_data_viewer(HistogramViewer) def teardown_method(self, method): self.viewer.close() self.viewer = None self.app.close() self.app = None def test_basic(self): viewer_state = self.viewer.state # Check defaults when we add data self.viewer.add_data(self.data) assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:Coordinate components:Pixel Axis 0 [x]' assert viewer_state.x_att is self.data.id['x'] assert viewer_state.x_min == -1.1 assert viewer_state.x_max == 3.4 assert viewer_state.y_min == 0.0 assert viewer_state.y_max == 1.2 assert viewer_state.hist_x_min == -1.1 assert viewer_state.hist_x_max == 3.4 assert viewer_state.hist_n_bin == 15 assert not viewer_state.cumulative assert not viewer_state.normalize assert not viewer_state.x_log assert not viewer_state.y_log assert len(viewer_state.layers) == 1 # Change to categorical component and check new values viewer_state.x_att = self.data.id['y'] assert viewer_state.x_min == -0.5 assert viewer_state.x_max == 2.5 assert viewer_state.y_min == 0.0 assert viewer_state.y_max == 2.4 assert viewer_state.hist_x_min == -0.5 assert viewer_state.hist_x_max == 2.5 assert viewer_state.hist_n_bin == 3 assert not viewer_state.cumulative assert not viewer_state.normalize assert not viewer_state.x_log assert not viewer_state.y_log def test_log_labels(self): # Regression test to make sure the labels are correctly changed to log # when the x-axis is in log space. viewer_state = self.viewer.state data = Data(x=np.logspace(-5, 5, 10000)) self.data_collection.append(data) self.viewer.add_data(data) viewer_state.x_log = True process_events() labels = [x.get_text() for x in self.viewer.axes.xaxis.get_ticklabels()] # Different Matplotlib versions return slightly different # labels, but the ones below should be present regardless # of Matplotlib version. expected_present = ['$\\mathdefault{10^{-5}}$', '$\\mathdefault{10^{-3}}$', '$\\mathdefault{10^{-1}}$', '$\\mathdefault{10^{1}}$', '$\\mathdefault{10^{3}}$', '$\\mathdefault{10^{5}}$'] for label in expected_present: assert label in labels def test_flip(self): viewer_state = self.viewer.state self.viewer.add_data(self.data) assert viewer_state.x_min == -1.1 assert viewer_state.x_max == 3.4 self.viewer.options_widget().button_flip_x.click() assert viewer_state.x_min == 3.4 assert viewer_state.x_max == -1.1 def test_remove_data(self): self.viewer.add_data(self.data) assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:Coordinate components:Pixel Axis 0 [x]' self.data_collection.remove(self.data) assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == '' def test_update_component_updates_title(self): self.viewer.add_data(self.data) assert self.viewer.windowTitle() == '1D Histogram' self.viewer.state.x_att = self.data.id['y'] assert self.viewer.windowTitle() == '1D Histogram' def test_combo_updates_with_component_add(self): self.viewer.add_data(self.data) self.data.add_component([3, 4, 1, 2], 'z') assert self.viewer.state.x_att is self.data.id['x'] assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' def test_nonnumeric_first_component(self): # regression test for #208. Shouldn't complain if # first component is non-numerical data = core.Data() data.add_component(['a', 'b', 'c'], label='c1') data.add_component([1, 2, 3], label='c2') self.data_collection.append(data) self.viewer.add_data(data) def test_nan_component(self): # regression test for case when all values are NaN in a component data = core.Data() data.add_component([np.nan, np.nan, np.nan], label='c1') self.data_collection.append(data) self.viewer.add_data(data) def test_histogram_values(self): # Check the actual values of the histograms viewer_state = self.viewer.state self.viewer.add_data(self.data) # Numerical attribute viewer_state.hist_x_min = -5 viewer_state.hist_x_max = 5 viewer_state.hist_n_bin = 4 assert_allclose(self.viewer.state.y_max, 2.4) assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 1, 2, 1]) assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) cid = self.data.main_components[0] self.data_collection.new_subset_group('subset 1', cid < 2) assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 1, 1, 0]) assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) viewer_state.normalize = True assert_allclose(self.viewer.state.y_max, 0.24) assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0.1, 0.2, 0.1]) assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 0.2, 0.2, 0]) assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) viewer_state.cumulative = True assert_allclose(self.viewer.state.y_max, 1.2) assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0.25, 0.75, 1.0]) assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 0.5, 1.0, 1.0]) assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) viewer_state.normalize = False assert_allclose(self.viewer.state.y_max, 4.8) assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 1, 3, 4]) assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 1, 2, 2]) assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) viewer_state.cumulative = False # Categorical attribute viewer_state.x_att = self.data.id['y'] formatter = self.viewer.axes.xaxis.get_major_formatter() xlabels = [formatter.format_data(pos) for pos in range(3)] assert xlabels == ['a', 'b', 'c'] assert_allclose(self.viewer.state.y_max, 2.4) assert_allclose(self.viewer.layers[0].state.histogram[1], [2, 1, 1]) assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].state.histogram[1], [1, 0, 1]) assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) viewer_state.normalize = True assert_allclose(self.viewer.state.y_max, 0.6) assert_allclose(self.viewer.layers[0].state.histogram[1], [0.5, 0.25, 0.25]) assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].state.histogram[1], [0.5, 0, 0.5]) assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) viewer_state.cumulative = True assert_allclose(self.viewer.state.y_max, 1.2) assert_allclose(self.viewer.layers[0].state.histogram[1], [0.5, 0.75, 1]) assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].state.histogram[1], [0.5, 0.5, 1]) assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) viewer_state.normalize = False assert_allclose(self.viewer.state.y_max, 4.8) assert_allclose(self.viewer.layers[0].state.histogram[1], [2, 3, 4]) assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].state.histogram[1], [1, 1, 2]) assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) # TODO: add tests for log def test_apply_roi(self): # Check that when doing an ROI selection, the ROI clips to the bin edges # outside the selection viewer_state = self.viewer.state self.viewer.add_data(self.data) viewer_state.hist_x_min = -5 viewer_state.hist_x_max = 5 viewer_state.hist_n_bin = 4 roi = XRangeROI(-0.2, 0.1) assert len(self.viewer.layers) == 1 self.viewer.apply_roi(roi) assert len(self.viewer.layers) == 2 assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 1, 2, 1]) assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 1, 2, 0]) assert_allclose(self.data.subsets[0].to_mask(), [0, 1, 1, 1]) state = self.data.subsets[0].subset_state assert isinstance(state, RangeSubsetState) assert state.lo == -2.5 assert state.hi == 2.5 # TODO: add a similar test in log space def test_apply_roi_categorical(self): # Check that when doing an ROI selection, the ROI clips to the bin edges # outside the selection viewer_state = self.viewer.state self.viewer.add_data(self.data) viewer_state.x_att = self.data.id['y'] roi = XRangeROI(0.3, 0.9) assert len(self.viewer.layers) == 1 self.viewer.apply_roi(roi) assert len(self.viewer.layers) == 2 assert_allclose(self.viewer.layers[0].state.histogram[1], [2, 1, 1]) assert_allclose(self.viewer.layers[1].state.histogram[1], [2, 1, 0]) assert_allclose(self.data.subsets[0].to_mask(), [1, 1, 0, 1]) state = self.data.subsets[0].subset_state assert isinstance(state, CategoricalROISubsetState) assert_equal(state.roi.categories, ['a', 'b']) def test_apply_roi_empty(self): # Make sure that doing an ROI selection on an empty viewer doesn't # produce error messsages roi = XRangeROI(-0.2, 0.1) self.viewer.apply_roi(roi) def test_axes_labels(self): viewer_state = self.viewer.state self.viewer.add_data(self.data) assert self.viewer.axes.get_xlabel() == 'x' assert self.viewer.axes.get_ylabel() == 'Number' viewer_state.x_log = True assert self.viewer.axes.get_xlabel() == 'Log x' assert self.viewer.axes.get_ylabel() == 'Number' viewer_state.x_att = self.data.id['y'] assert self.viewer.axes.get_xlabel() == 'y' assert self.viewer.axes.get_ylabel() == 'Number' viewer_state.normalize = True assert self.viewer.axes.get_xlabel() == 'y' assert self.viewer.axes.get_ylabel() == 'Normalized number' viewer_state.normalize = False viewer_state.cumulative = True assert self.viewer.axes.get_xlabel() == 'y' assert self.viewer.axes.get_ylabel() == 'Number' def test_y_min_y_max(self): # Regression test for a bug that caused y_max to not be set correctly # when multiple subsets were present and after turning on normalization # after switching to a different attribute from that used to make the # selection. viewer_state = self.viewer.state self.viewer.add_data(self.data) self.data.add_component([3.4, 3.5, 10.2, 20.3], 'z') viewer_state.x_att = self.data.id['x'] cid = self.data.main_components[0] self.data_collection.new_subset_group('subset 1', cid < 1) cid = self.data.main_components[0] self.data_collection.new_subset_group('subset 2', cid < 2) cid = self.data.main_components[0] self.data_collection.new_subset_group('subset 3', cid < 3) assert_allclose(self.viewer.state.y_min, 0) assert_allclose(self.viewer.state.y_max, 1.2) viewer_state.x_att = self.data.id['z'] assert_allclose(self.viewer.state.y_min, 0) assert_allclose(self.viewer.state.y_max, 2.4) viewer_state.normalize = True assert_allclose(self.viewer.state.y_min, 0) assert_allclose(self.viewer.state.y_max, 0.5325443786982249) def test_update_when_limits_unchanged(self): # Regression test for glue-viz/glue#1010 - this bug caused histograms # to not be recomputed if the attribute changed but the limits and # number of bins did not. viewer_state = self.viewer.state self.viewer.add_data(self.data) viewer_state.x_att = self.data.id['y'] viewer_state.hist_x_min = -10.1 viewer_state.hist_x_max = +10 viewer_state.hist_n_bin = 5 assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 3, 1, 0]) viewer_state.x_att = self.data.id['x'] viewer_state.hist_x_min = -10.1 viewer_state.hist_x_max = +10 viewer_state.hist_n_bin = 5 assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 2, 2, 0]) viewer_state.x_att = self.data.id['y'] assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 3, 1, 0]) viewer_state.x_att = self.data.id['x'] assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 2, 2, 0]) def test_component_replaced(self): # regression test for 508 - if a component ID is replaced, we should # make sure that the component ID is selected if the old component ID # was selected self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['x'] test = ComponentID('test') self.data.update_id(self.viewer.state.x_att, test) assert self.viewer.state.x_att is test assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:test:y:Coordinate components:Pixel Axis 0 [x]' def test_nbin_override_persists_over_numerical_attribute_change(self): # regression test for #398 self.data.add_component([3, 4, 1, 2], 'z') self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['x'] self.viewer.state.hist_n_bin = 7 self.viewer.state.x_att = self.data.id['z'] assert self.viewer.state.hist_n_bin == 7 @pytest.mark.parametrize('protocol', [0, 1]) def test_session_back_compat(self, protocol): filename = os.path.join(DATA, 'histogram_v{0}.glu'.format(protocol)) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') dc = ga.session.data_collection assert len(dc) == 1 assert dc[0].label == 'data' viewer1 = ga.viewers[0][0] assert len(viewer1.state.layers) == 2 assert viewer1.state.x_att is dc[0].id['a'] assert_allclose(viewer1.state.x_min, 0) assert_allclose(viewer1.state.x_max, 9) assert_allclose(viewer1.state.y_min, 0) assert_allclose(viewer1.state.y_max, 2.4) assert_allclose(viewer1.state.hist_x_min, 0) assert_allclose(viewer1.state.hist_x_max, 9) assert_allclose(viewer1.state.hist_n_bin, 6) assert not viewer1.state.x_log assert not viewer1.state.y_log assert viewer1.state.layers[0].visible assert not viewer1.state.layers[1].visible assert not viewer1.state.cumulative assert not viewer1.state.normalize viewer2 = ga.viewers[0][1] assert viewer2.state.x_att is dc[0].id['b'] assert_allclose(viewer2.state.x_min, 2) assert_allclose(viewer2.state.x_max, 16) assert_allclose(viewer2.state.y_min, 0) assert_allclose(viewer2.state.y_max, 1.2) assert_allclose(viewer2.state.hist_x_min, 2) assert_allclose(viewer2.state.hist_x_max, 16) assert_allclose(viewer2.state.hist_n_bin, 8) assert not viewer2.state.x_log assert not viewer2.state.y_log assert viewer2.state.layers[0].visible assert viewer2.state.layers[1].visible assert not viewer2.state.cumulative assert not viewer2.state.normalize viewer3 = ga.viewers[0][2] assert viewer3.state.x_att is dc[0].id['a'] assert_allclose(viewer3.state.x_min, 0) assert_allclose(viewer3.state.x_max, 9) assert_allclose(viewer3.state.y_min, 0.01111111111111111) assert_allclose(viewer3.state.y_max, 0.7407407407407407) assert_allclose(viewer3.state.hist_x_min, 0) assert_allclose(viewer3.state.hist_x_max, 9) assert_allclose(viewer3.state.hist_n_bin, 10) assert not viewer3.state.x_log assert viewer3.state.y_log assert viewer3.state.layers[0].visible assert viewer3.state.layers[1].visible assert not viewer3.state.cumulative assert viewer3.state.normalize viewer4 = ga.viewers[0][3] assert viewer4.state.x_att is dc[0].id['a'] assert_allclose(viewer4.state.x_min, -1) assert_allclose(viewer4.state.x_max, 10) assert_allclose(viewer4.state.y_min, 0) assert_allclose(viewer4.state.y_max, 12) assert_allclose(viewer4.state.hist_x_min, -1) assert_allclose(viewer4.state.hist_x_max, 10) assert_allclose(viewer4.state.hist_n_bin, 4) assert not viewer4.state.x_log assert not viewer4.state.y_log assert viewer4.state.layers[0].visible assert viewer4.state.layers[1].visible assert viewer4.state.cumulative assert not viewer4.state.normalize ga.close() def test_apply_roi_single(self): # Regression test for a bug that caused mode.update to be called # multiple times and resulted in all other viewers receiving many # messages regarding subset updates (this occurred when multiple) # datasets were present. layer_tree = LayerTreeWidget(session=self.session) layer_tree.set_checkable(False) layer_tree.setup(self.data_collection) layer_tree.bind_selection_to_edit_subset() class Client(HubListener): def __init__(self, *args, **kwargs): super(Client, self).__init__(*args, **kwargs) self.count = Counter() def ping(self, message): self.count[message.sender] += 1 def register_to_hub(self, hub): hub.subscribe(self, SubsetUpdateMessage, handler=self.ping) d1 = Data(a=[1, 2, 3], label='d1') d2 = Data(b=[1, 2, 3], label='d2') d3 = Data(c=[1, 2, 3], label='d3') d4 = Data(d=[1, 2, 3], label='d4') self.data_collection.append(d1) self.data_collection.append(d2) self.data_collection.append(d3) self.data_collection.append(d4) client = Client() client.register_to_hub(self.hub) self.viewer.add_data(d1) self.viewer.add_data(d3) roi = XRangeROI(2.5, 3.5) self.viewer.apply_roi(roi) for subset in client.count: assert client.count[subset] == 1 @pytest.mark.filterwarnings('ignore:elementwise') def test_datetime64_support(self, tmpdir): self.data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1') self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['t1'] # Matplotlib deals with dates by converting them to the number of days # since 01-01-0001, so we can check that the limits are correctly # converted (and not 100 to 400) assert self.viewer.axes.get_xlim() == (719263.0, 719563.0) # Apply an ROI selection in plotting coordinates roi = XRangeROI(719313, 719513) self.viewer.apply_roi(roi) # Check that the two middle elements are selected assert_equal(self.data.subsets[0].to_mask(), [0, 1, 1, 0]) # Make sure that the Qt labels look ok options = self.viewer.options_widget().ui assert options.valuetext_x_min.text() == '1970-04-11' assert options.valuetext_x_max.text() == '1971-02-05' # Make sure that we can set the xmin/xmax to a string date assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-11', 'D')) options.valuetext_x_min.setText('1970-04-14') options.valuetext_x_min.editingFinished.emit() assert self.viewer.axes.get_xlim() == (719266.0, 719563.0) assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D')) # Make sure that everything works fine after saving/reloading filename = tmpdir.join('test_datetime64.glu').strpath self.session.application.save_session(filename) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') viewer = ga.viewers[0][0] options = viewer.options_widget().ui assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D')) assert options.valuetext_x_min.text() == '1970-04-14' assert options.valuetext_x_max.text() == '1971-02-05' ga.close() @requires_matplotlib_ge_22 def test_categorical_labels(self, tmpdir): # Fix a bug that caused labels on histograms of categorical variables # to not be restored correctly after saving and reloading session self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['y'] self.viewer.figure.canvas.draw() assert [x.get_text() for x in self.viewer.axes.xaxis.get_ticklabels()] == ['', 'a', 'b', 'c', ''] # Make sure that everything works fine after saving/reloading filename = tmpdir.join('test_categorical_labels.glu').strpath self.session.application.save_session(filename) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') viewer = ga.viewers[0][0] viewer.figure.canvas.draw() assert [x.get_text() for x in viewer.axes.xaxis.get_ticklabels()] == ['', 'a', 'b', 'c', ''] ga.close() def test_legend(self): from matplotlib.colors import to_hex viewer_state = self.viewer.state self.viewer.add_data(self.data) self.viewer.state.legend.visible = True handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 1 assert labels[0] == 'd1' self.data_collection.new_subset_group('test', self.data.id['x'] > 1) assert len(viewer_state.layers) == 2 handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 2 assert labels[1] == 'test' assert to_hex(handles[1].get_facecolor()) == viewer_state.layers[1].color def test_with_dask_array(): # Regression test for a bug that caused the histogram to now work when # making spatial selections on a cube represented as a dask array da = pytest.importorskip('dask.array') data = Data(x=da.arange(1000).reshape((10, 10, 10)), label='d1') app = GlueApplication() session = app.session hub = session.hub data_collection = session.data_collection data_collection.append(data) viewer = app.new_data_viewer(HistogramViewer) viewer.add_data(data) viewer.state.hist_n_bin = 1 process_events(0.5) assert len(viewer.layers) == 1 assert viewer.layers[0].enabled zid, yid, xid = data.pixel_component_ids subset_state = RoiSubsetState(xatt=xid, yatt=yid, roi=RectangularROI(xmin=3.5, xmax=5.5, ymin=3.7, ymax=7.5)) data_collection.new_subset_group(subset_state=subset_state, label='subset') assert len(viewer.layers) == 2 assert viewer.layers[0].enabled assert viewer.layers[1].enabled assert viewer.state.layers[0].histogram[1][0] == 1000. assert viewer.state.layers[1].histogram[1][0] == 80. viewer.close() viewer = None app.close() app = None glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/data_viewer.py0000644000175000017500000000221513605357235024322 0ustar noahfxnoahfxfrom glue.utils import defer_draw, decorate_all_methods from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer from glue.viewers.histogram.qt.layer_style_editor import HistogramLayerStyleEditor from glue.viewers.histogram.qt.options_widget import HistogramOptionsWidget from glue.viewers.histogram.qt.layer_artist import QThreadedHistogramLayerArtist from glue.viewers.histogram.state import HistogramViewerState from glue.viewers.histogram.viewer import MatplotlibHistogramMixin __all__ = ['HistogramViewer'] @decorate_all_methods(defer_draw) class HistogramViewer(MatplotlibHistogramMixin, MatplotlibDataViewer): LABEL = '1D Histogram' _layer_style_widget_cls = HistogramLayerStyleEditor _options_cls = HistogramOptionsWidget _state_cls = HistogramViewerState _data_artist_cls = QThreadedHistogramLayerArtist _subset_artist_cls = QThreadedHistogramLayerArtist large_data_size = 2e7 tools = ['select:xrange'] def __init__(self, session, parent=None, state=None): super(HistogramViewer, self).__init__(session, parent=parent, state=state) MatplotlibHistogramMixin.setup_callbacks(self) glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/options_widget.ui0000644000175000017500000003264313661230662025057 0ustar noahfxnoahfx Widget 0 0 269 418 1D Histogram 5 5 5 5 5 0 General 10 10 10 10 10 5 0 0 5 0 0 0 0 normalized true cumulative true log true Qt::Horizontal 1 20 50 false bins Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Update bins to view Qt::Horizontal 40 5 Qt::Vertical QSizePolicy::Fixed 20 5 Qt::Vertical 20 40 75 true y axis Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Fixed # of numerical bins 75 true x axis Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true 1000 QComboBox::AdjustToMinimumContentsLength log true Limits 10 10 10 10 10 5 padding: 0px 75 true x axis 75 true y axis Qt::Vertical 20 40 Qt::Horizontal 40 5 log true log true bool_x_log valuetext_x_max button_flip_x valuetext_x_min valuetext_y_min valuetext_y_max bool_y_log label_2 label_5 verticalSpacer horizontalSpacer_2 Axes 5 5 5 5 Legend 5 5 5 5 AxesEditorWidget QWidget
glue.viewers.matplotlib.qt.axes_editor
LegendEditorWidget QWidget
glue.viewers.matplotlib.qt.legend_editor
glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/__init__.py0000644000175000017500000000021113502206677023560 0ustar noahfxnoahfxfrom .data_viewer import HistogramViewer # noqa def setup(): from glue.config import qt_client qt_client.add(HistogramViewer) glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/layer_artist.py0000644000175000017500000000444213662760562024542 0ustar noahfxnoahfximport time from glue.utils import defer_draw from glue.viewers.histogram.layer_artist import HistogramLayerArtist from glue.viewers.matplotlib.qt.compute_worker import ComputeWorker __all__ = ['QThreadedHistogramLayerArtist'] class QThreadedHistogramLayerArtist(HistogramLayerArtist): def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(QThreadedHistogramLayerArtist, self).__init__(axes, viewer_state, layer_state=layer_state, layer=layer) self.setup_thread() def wait(self): # Wait 0.5 seconds to make sure that the computation has properly started time.sleep(0.5) while self._worker.running: time.sleep(1 / 25) from glue.utils.qt import process_events process_events() def remove(self): super(QThreadedHistogramLayerArtist, self).remove() if self._worker is not None: self._worker.work_queue.put('stop') self._worker.exit() # Need to wait otherwise the thread will be destroyed while still # running, causing a segmentation fault self._worker.wait() self._worker = None @property def is_computing(self): return self._worker is not None and self._worker.running def setup_thread(self): self._worker = ComputeWorker(self._calculate_histogram_thread) self._worker.compute_end.connect(self._calculate_histogram_postthread) self._worker.compute_error.connect(self._calculate_histogram_error) self._worker.compute_start.connect(self.notify_start_computation) self._worker.start() @defer_draw def _calculate_histogram(self, reset=False): if self.state.layer is not None and self.state.layer.size > 1e7: self._worker.work_queue.put(reset) else: super(QThreadedHistogramLayerArtist, self)._calculate_histogram(reset=reset) def _calculate_histogram_postthread(self): # If the worker has started running again, we should stop at this point # since this function will get called again. if self._worker is not None and self._worker.running: return super(QThreadedHistogramLayerArtist, self)._calculate_histogram_postthread() glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/options_widget.py0000644000175000017500000000354713752534424025077 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui, fix_tab_widget_fontsize from glue.viewers.matplotlib.state import MatplotlibDataViewerState __all__ = ['HistogramOptionsWidget'] class HistogramOptionsWidget(QtWidgets.QWidget): def __init__(self, viewer_state, session, parent=None): super(HistogramOptionsWidget, self).__init__(parent=parent) self.ui = load_ui('options_widget.ui', self, directory=os.path.dirname(__file__)) fix_tab_widget_fontsize(self.ui.tab_widget) self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) self._connections_axes = autoconnect_callbacks_to_qt(viewer_state, self.ui.axes_editor.ui) connect_kwargs = {'alpha': dict(value_range=(0, 1))} self._connections_legend = autoconnect_callbacks_to_qt(viewer_state.legend, self.ui.legend_editor.ui, connect_kwargs) self.viewer_state = viewer_state viewer_state.add_callback('x_att', self._update_attribute) self.session = session self.ui.axes_editor.button_apply_all.clicked.connect(self._apply_all_viewers) def _apply_all_viewers(self): for tab in self.session.application.viewers: for viewer in tab: if isinstance(viewer.state, MatplotlibDataViewerState): viewer.state.update_axes_settings_from(self.viewer_state) def _update_attribute(self, *args): # If at least one of the components is categorical or a date, disable log button log_enabled = 'categorical' not in self.viewer_state.x_kinds self.ui.bool_x_log.setEnabled(log_enabled) self.ui.bool_x_log_.setEnabled(log_enabled) if not log_enabled: self.ui.bool_x_log.setChecked(False) self.ui.bool_x_log_.setChecked(False) glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/layer_style_editor.ui0000644000175000017500000000610313502206677025716 0ustar noahfxnoahfx Form 0 0 154 96 Form 5 5 5 5 10 5 0 0 75 true color Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true opacity Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 100 Qt::Horizontal Qt::Vertical 20 40 Qt::Horizontal 40 5 QColorBox QLabel
glue.utils.qt.colors
glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/qt/layer_style_editor.py0000644000175000017500000000107213657331513025730 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui class HistogramLayerStyleEditor(QtWidgets.QWidget): def __init__(self, layer, parent=None): super(HistogramLayerStyleEditor, self).__init__(parent=parent) self.ui = load_ui('layer_style_editor.ui', self, directory=os.path.dirname(__file__)) connect_kwargs = {'alpha': dict(value_range=(0, 1))} self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/python_export.py0000644000175000017500000000466513661230662024335 0ustar noahfxnoahfxfrom distutils.version import LooseVersion from matplotlib import __version__ from glue.viewers.common.python_export import code, serialize_options from glue.utils import nanmin, nanmax MATPLOTLIB_GE_30 = LooseVersion(__version__) > '3' def python_export_histogram_layer(layer, *args): if len(layer.mpl_artists) == 0 or not layer.enabled or not layer.visible: return [], None script = "" imports = ["import numpy as np"] x = layer.layer[layer._viewer_state.x_att] x_min = nanmin(x) x_max = nanmax(x) hist_x_min = layer._viewer_state.hist_x_min hist_x_max = layer._viewer_state.hist_x_max script += "# Get main data values\n" script += "x = layer_data['{0}']\n\n".format(layer._viewer_state.x_att.label) script += "# Set up histogram bins\n" script += "hist_n_bin = {0}\n".format(layer._viewer_state.hist_n_bin) if abs((x_min - hist_x_min) / (hist_x_max - hist_x_min)) < 0.001: script += "hist_x_min = np.nanmin(x)\n" else: script += "hist_x_min = {0}\n".format(hist_x_min) if abs((x_max - hist_x_max) / (hist_x_max - hist_x_min)) < 0.001: script += "hist_x_max = np.nanmax(x)\n" else: script += "hist_x_max = {0}\n".format(hist_x_max) options = dict(alpha=layer.state.alpha, color=layer.state.color, zorder=layer.state.zorder, edgecolor='none') if layer._viewer_state.x_log: script += "bins = np.logspace(np.log10(hist_x_min), np.log10(hist_x_max), hist_n_bin)\n" options['bins'] = code('bins') else: options['range'] = code('[hist_x_min, hist_x_max]') options['bins'] = code('hist_n_bin') if layer._viewer_state.normalize: if MATPLOTLIB_GE_30: options['density'] = True else: options['normed'] = True if layer._viewer_state.cumulative: options['cumulative'] = True script += "\nx = x[(x >= hist_x_min) & (x <= hist_x_max)]\n\n" script += "ax.hist(x, {0})\n\n".format(serialize_options(options)) options = dict( facecolor=layer.state.color, edgecolor='none', alpha=layer.state.alpha) imports += ["from matplotlib.patches import Patch"] script += "handle = Patch({0}) # for legend\n".format(serialize_options(options)) script += "legend_handles.append(handle)\n" script += "legend_labels.append(layer_data.label)\n" return imports, script.strip() glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/state.py0000644000175000017500000002314313662766630022535 0ustar noahfxnoahfximport numpy as np from glue.core import BaseData, Subset from echo import delay_callback from glue.viewers.matplotlib.state import (MatplotlibDataViewerState, MatplotlibLayerState, DeferredDrawCallbackProperty as DDCProperty, DeferredDrawSelectionCallbackProperty as DDSCProperty) from glue.core.state_objects import (StateAttributeLimitsHelper, StateAttributeHistogramHelper) from glue.core.exceptions import IncompatibleAttribute, IncompatibleDataException from glue.core.data_combo_helper import ComponentIDComboHelper from glue.utils import defer_draw, datetime64_to_mpl from glue.utils.decorators import avoid_circular __all__ = ['HistogramViewerState', 'HistogramLayerState'] class HistogramViewerState(MatplotlibDataViewerState): """ A state class that includes all the attributes for a histogram viewer. """ x_att = DDSCProperty(docstring='The attribute to compute the histograms for') cumulative = DDCProperty(False, docstring='Whether to show the histogram as ' 'a cumulative histogram') normalize = DDCProperty(False, docstring='Whether to normalize the histogram ' '(based on the total sum)') hist_x_min = DDCProperty(docstring='The minimum value used to compute the ' 'histogram') hist_x_max = DDCProperty(docstring='The maximum value used to compute the ' 'histogram') hist_n_bin = DDCProperty(docstring='The number of bins in the histogram') common_n_bin = DDCProperty(True, docstring='The number of bins to use for ' 'all numerical components') def __init__(self, **kwargs): super(HistogramViewerState, self).__init__() self.hist_helper = StateAttributeHistogramHelper(self, 'x_att', lower='hist_x_min', upper='hist_x_max', n_bin='hist_n_bin', common_n_bin='common_n_bin') self.x_lim_helper = StateAttributeLimitsHelper(self, 'x_att', lower='x_min', upper='x_max', log='x_log') self.add_callback('layers', self._layers_changed) self.x_att_helper = ComponentIDComboHelper(self, 'x_att', pixel_coord=True, world_coord=True) self.update_from_dict(kwargs) # This should be added after update_from_dict since we don't want to # influence the restoring of sessions. self.add_callback('hist_x_min', self.update_view_to_bins) self.add_callback('hist_x_max', self.update_view_to_bins) self.add_callback('x_log', self._reset_x_limits, priority=1000) def _reset_x_limits(self, *args): if self.x_att is None: return with delay_callback(self, 'hist_x_min', 'hist_x_max', 'x_min', 'x_max', 'x_log'): self.x_lim_helper.percentile = 100 self.x_lim_helper.update_values(force=True) self.update_bins_to_view() def reset_limits(self): self._reset_x_limits() self.y_min = min(getattr(layer, '_y_min', np.inf) for layer in self.layers) self.y_max = max(getattr(layer, '_y_max', 0) for layer in self.layers) def _update_priority(self, name): if name == 'layers': return 2 elif name.endswith('_log'): return 0.5 elif name.endswith(('_min', '_max', '_bin')): return 0 else: return 1 def flip_x(self): """ Flip the x_min/x_max limits. """ self.x_lim_helper.flip_limits() @avoid_circular def update_bins_to_view(self, *args): """ Update the bins to match the current view. """ with delay_callback(self, 'hist_x_min', 'hist_x_max'): if self.x_max > self.x_min: self.hist_x_min = self.x_min self.hist_x_max = self.x_max else: self.hist_x_min = self.x_max self.hist_x_max = self.x_min @avoid_circular def update_view_to_bins(self, *args): """ Update the view to match the histogram interval """ with delay_callback(self, 'x_min', 'x_max'): self.x_min = self.hist_x_min self.x_max = self.hist_x_max @property def x_categories(self): return self._categories(self.x_att) def _categories(self, cid): categories = [] for layer_state in self.layers: if isinstance(layer_state.layer, BaseData): layer = layer_state.layer else: layer = layer_state.layer.data try: if layer.data.get_kind(cid) == 'categorical': categories.append(layer.data.get_data(cid).categories) except IncompatibleAttribute: pass if len(categories) == 0: return None else: return np.unique(np.hstack(categories)) @property def x_kinds(self): return self._component_kinds(self.x_att) def _component_kinds(self, cid): # Construct list of component kinds over all layers kinds = set() for layer_state in self.layers: if isinstance(layer_state.layer, BaseData): layer = layer_state.layer else: layer = layer_state.layer.data try: kinds.add(layer.data.get_kind(cid)) except IncompatibleAttribute: pass return kinds @property def bins(self): """ The position of the bins for the histogram based on the current state. """ if self.hist_x_min is None or self.hist_x_max is None or self.hist_n_bin is None: return None if self.x_log: return np.logspace(np.log10(self.hist_x_min), np.log10(self.hist_x_max), self.hist_n_bin + 1) elif isinstance(self.hist_x_min, np.datetime64): x_min = self.hist_x_min.astype(int) x_max = self.hist_x_max.astype(self.hist_x_min.dtype).astype(int) return np.linspace(x_min, x_max, self.hist_n_bin + 1).astype(self.hist_x_min.dtype) else: return np.linspace(self.hist_x_min, self.hist_x_max, self.hist_n_bin + 1) @defer_draw def _layers_changed(self, *args): self.x_att_helper.set_multiple_data(self.layers_data) class HistogramLayerState(MatplotlibLayerState): """ A state class that includes all the attributes for layers in a histogram plot. """ _histogram_cache = None def reset_cache(self, *args): self._histogram_cache = None @property def viewer_state(self): return self._viewer_state @viewer_state.setter def viewer_state(self, viewer_state): self._viewer_state = viewer_state @property def histogram(self): self.update_histogram() edges, unscaled = self._histogram_cache[1] scaled = unscaled.astype(np.float) dx = edges[1] - edges[0] if self.viewer_state.cumulative: scaled = scaled.cumsum() if self.viewer_state.normalize: scaled /= scaled.max() elif self.viewer_state.normalize: scaled /= (scaled.sum() * dx) return edges, scaled def update_histogram(self): current_settings = (id(self.viewer_state.x_att), self.viewer_state.x_log, self.viewer_state.hist_x_min, self.viewer_state.hist_x_max, self.viewer_state.hist_n_bin) if self._histogram_cache is not None and self._histogram_cache[0] == current_settings: return self._histogram_cache[1] if (self.viewer_state is None or self.viewer_state.x_att is None or self.viewer_state.hist_x_min is None or self.viewer_state.hist_x_max is None or self.viewer_state.hist_n_bin is None or self.viewer_state.x_log is None): raise IncompatibleDataException() if isinstance(self.layer, Subset): data = self.layer.data subset_state = self.layer.subset_state else: data = self.layer subset_state = None range = sorted((self.viewer_state.hist_x_min, self.viewer_state.hist_x_max)) hist_values = data.compute_histogram([self._viewer_state.x_att], range=[range], bins=[self._viewer_state.hist_n_bin], log=[self._viewer_state.x_log], subset_state=subset_state) # TODO: determine whether this belongs here or in the layer artist if isinstance(range[0], np.datetime64): range = [datetime64_to_mpl(range[0]), datetime64_to_mpl(range[1])] if self._viewer_state.x_log: hist_edges = np.logspace(np.log10(range[0]), np.log10(range[1]), self._viewer_state.hist_n_bin + 1) else: hist_edges = np.linspace(range[0], range[1], self._viewer_state.hist_n_bin + 1) self._histogram_cache = current_settings, (hist_edges, hist_values) glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/__init__.py0000644000175000017500000000000013502206677023130 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/histogram/compat.py0000644000175000017500000000424713605357235022676 0ustar noahfxnoahfximport uuid from .state import HistogramLayerState STATE_CLASS = {} STATE_CLASS['HistogramLayerArtist'] = HistogramLayerState def update_histogram_viewer_state(rec, context): """ Given viewer session information, make sure the session information is compatible with the current version of the viewers, and if not, update the session information in-place. """ if '_protocol' not in rec: # Note that files saved with protocol < 1 have bin settings saved per # layer but they were always restricted to be the same, so we can just # use the settings from the first layer rec['state'] = {} rec['state']['values'] = {} # TODO: could generalize this into a mapping properties = rec.pop('properties') viewer_state = rec['state']['values'] viewer_state['x_min'] = properties['xmin'] viewer_state['x_max'] = properties['xmax'] viewer_state['hist_n_bin'] = int(properties['nbins']) viewer_state['hist_x_min'] = properties['xmin'] viewer_state['hist_x_max'] = properties['xmax'] viewer_state['x_log'] = properties['xlog'] viewer_state['y_log'] = properties['ylog'] viewer_state['normalize'] = properties['normed'] viewer_state['cumulative'] = properties['cumulative'] viewer_state['x_att'] = properties['component'] layer_states = [] for layer in rec['layers']: state_id = str(uuid.uuid4()) state_cls = STATE_CLASS[layer['_type'].split('.')[-1]] state = state_cls(layer=context.object(layer.pop('layer'))) for prop in ('visible', 'zorder'): value = layer.pop(prop) value = context.object(value) setattr(state, prop, value) context.register_object(state_id, state) layer['state'] = state_id layer_states.append(state) layer.pop('lo', None) layer.pop('hi', None) layer.pop('nbins', None) layer.pop('xlog', None) list_id = str(uuid.uuid4()) context.register_object(list_id, layer_states) rec['state']['values']['layers'] = list_id glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/layer_artist.py0000644000175000017500000001556113662766630024124 0ustar noahfxnoahfximport sys import warnings import numpy as np from matplotlib.patches import Rectangle, Patch from glue.utils import defer_draw from glue.core import BaseData from glue.viewers.histogram.state import HistogramLayerState from glue.viewers.histogram.python_export import python_export_histogram_layer from glue.viewers.matplotlib.layer_artist import MatplotlibLayerArtist from glue.core.exceptions import IncompatibleAttribute, IncompatibleDataException class HistogramLayerArtist(MatplotlibLayerArtist): _layer_state_cls = HistogramLayerState _python_exporter = python_export_histogram_layer def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(HistogramLayerArtist, self).__init__(axes, viewer_state, layer_state=layer_state, layer=layer) # Watch for changes in the viewer state which would require the # layers to be redrawn self._viewer_state.add_global_callback(self._update_histogram) self.state.add_global_callback(self._update_histogram) @defer_draw def _calculate_histogram(self, reset=False): try: self.notify_start_computation() self._calculate_histogram_thread(reset=reset) except Exception: self._calculate_histogram_error(sys.exc_info()) else: self._calculate_histogram_postthread() def _calculate_histogram_thread(self, reset=False): # We need to ignore any warnings that happen inside the thread # otherwise the thread tries to send these to the glue logger (which # uses Qt), which then results in this kind of error: # QObject::connect: Cannot queue arguments of type 'QTextCursor' with warnings.catch_warnings(): warnings.simplefilter("ignore") if reset: self.state.reset_cache() self.state.update_histogram() def _calculate_histogram_postthread(self): self.notify_end_computation() self._update_artists() self._update_visual_attributes() @defer_draw def _calculate_histogram_error(self, exc): self.notify_end_computation() self.redraw() if issubclass(exc[0], (IncompatibleAttribute, IndexError)): if isinstance(self.state.layer, BaseData): self.disable_invalid_attributes(self._viewer_state.x_att) else: self.disable_incompatible_subset() elif issubclass(exc[0], IncompatibleDataException): self.disable("Incompatible data") @defer_draw def _update_artists(self): # It's possible for this method to get called but for the state to have # been updated in the mean time to have a histogram that raises an # exception (for example an IncompatibleAttribute). If any errors happen # here, we simply ignore them since _calculate_histogram_error will get # called directly. try: mpl_hist_edges, mpl_hist = self.state.histogram except Exception: return if mpl_hist_edges.size == 0 or mpl_hist.sum() == 0: return if len(self.mpl_artists) > len(mpl_hist): for artist in self.mpl_artists[len(mpl_hist):]: artist.remove() self.mpl_artists = self.mpl_artists[:len(mpl_hist)] elif len(self.mpl_artists) < len(mpl_hist): for i in range(len(mpl_hist) - len(self.mpl_artists)): artist = Rectangle((0, 0), 0, 0) self.mpl_artists.append(artist) self.axes.add_artist(artist) self._update_visual_attributes() widths = np.diff(mpl_hist_edges) bottom = 0 if not self._viewer_state.y_log else 1e-100 for mpl_artist, x, y, dx in zip(self.mpl_artists, mpl_hist_edges[:-1], mpl_hist, widths): mpl_artist.set_width(dx) mpl_artist.set_height(y) mpl_artist.set_xy((x, bottom)) # TODO: move the following to state # We have to do the following to make sure that we reset the y_max as # needed. We can't simply reset based on the maximum for this layer # because other layers might have other values, and we also can't do: # # self._viewer_state.y_max = max(self._viewer_state.y_max, result[0].max()) # # because this would never allow y_max to get smaller. self.state._y_max = mpl_hist.max() if self._viewer_state.y_log: self.state._y_max *= 2 else: self.state._y_max *= 1.2 if self._viewer_state.y_log: keep = mpl_hist > 0 if np.any(keep): self.state._y_min = mpl_hist[mpl_hist > 0].min() / 10 else: self.state._y_min = 0 else: self.state._y_min = 0 largest_y_max = max(getattr(layer, '_y_max', 0) for layer in self._viewer_state.layers) if largest_y_max != self._viewer_state.y_max: self._viewer_state.y_max = largest_y_max smallest_y_min = min(getattr(layer, '_y_min', np.inf) for layer in self._viewer_state.layers) if smallest_y_min != self._viewer_state.y_min: self._viewer_state.y_min = smallest_y_min self.redraw() @defer_draw def _update_visual_attributes(self): if not self.enabled: return for mpl_artist in self.mpl_artists: mpl_artist.set_visible(self.state.visible) mpl_artist.set_zorder(self.state.zorder) mpl_artist.set_edgecolor('none') mpl_artist.set_facecolor(self.state.color) mpl_artist.set_alpha(self.state.alpha) self.redraw() def _update_histogram(self, force=False, **kwargs): if (self._viewer_state.hist_x_min is None or self._viewer_state.hist_x_max is None or self._viewer_state.hist_n_bin is None or self._viewer_state.x_att is None or self.state.layer is None): return changed = set() if force else self.pop_changed_properties() if force or any(prop in changed for prop in ('layer', 'x_att', 'hist_x_min', 'hist_x_max', 'hist_n_bin', 'x_log', 'y_log', 'normalize', 'cumulative')): self._calculate_histogram(reset=force) if force or any(prop in changed for prop in ('alpha', 'color', 'zorder', 'visible')): self._update_visual_attributes() def get_handle_legend(self): # The default legend handle for matplotlib viewer if self.enabled and self.state.visible: handle = Patch(facecolor=self.get_layer_color(), edgecolor='none', alpha=self.layer.style.alpha) return handle, self.layer.label, None else: return None, None, None @defer_draw def update(self): self.state.reset_cache() self._update_histogram(force=True) self.redraw() glueviz-1.0.1+dfsg.orig/glue/viewers/histogram/viewer.py0000644000175000017500000000473713605357235022720 0ustar noahfxnoahfxfrom glue.core.util import update_ticks from glue.core.roi import RangeROI from glue.core.subset import roi_to_subset_state from glue.utils import mpl_to_datetime64 from glue.viewers.histogram.compat import update_histogram_viewer_state __all__ = ['MatplotlibHistogramMixin'] class MatplotlibHistogramMixin(object): def setup_callbacks(self): self.state.add_callback('x_att', self._update_axes) self.state.add_callback('x_log', self._update_axes) self.state.add_callback('normalize', self._update_axes) self._update_axes() def _update_axes(self, *args): if self.state.x_att is not None: # Update ticks, which sets the labels to categories if components are categorical update_ticks(self.axes, 'x', self.state.x_kinds, self.state.x_log, self.state.x_categories) if self.state.x_log: self.state.x_axislabel = 'Log ' + self.state.x_att.label else: self.state.x_axislabel = self.state.x_att.label if self.state.normalize: self.state.y_axislabel = 'Normalized number' else: self.state.y_axislabel = 'Number' self.axes.figure.canvas.draw_idle() def apply_roi(self, roi, override_mode=None): # Force redraw to get rid of ROI. We do this because applying the # subset state below might end up not having an effect on the viewer, # for example there may not be any layers, or the active subset may not # be one of the layers. So we just explicitly redraw here to make sure # a redraw will happen after this method is called. self.redraw() if len(self.layers) == 0: return x_date = 'datetime' in self.state.x_kinds if x_date: roi = roi.transformed(xfunc=mpl_to_datetime64 if x_date else None) bins = self.state.bins x = roi.to_polygon()[0] lo, hi = min(x), max(x) if lo >= bins.min(): lo = bins[bins <= lo].max() if hi <= bins.max(): hi = bins[bins >= hi].min() roi_new = RangeROI(min=lo, max=hi, orientation='x') subset_state = roi_to_subset_state(roi_new, x_att=self.state.x_att, x_categories=self.state.x_categories) self.apply_subset_state(subset_state, override_mode=override_mode) @staticmethod def update_viewer_state(rec, context): return update_histogram_viewer_state(rec, context) glueviz-1.0.1+dfsg.orig/glue/viewers/custom/0000755000175000017500000000000013752535025020345 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/custom/tests/0000755000175000017500000000000013752535025021507 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/custom/tests/__init__.py0000644000175000017500000000000013455362716023613 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/custom/qt/0000755000175000017500000000000013752535025020771 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/custom/qt/tests/0000755000175000017500000000000013752535025022133 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/custom/qt/tests/__init__.py0000644000175000017500000000000013455362716024237 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/custom/qt/tests/test_elements.py0000644000175000017500000000334713605357235025371 0ustar noahfxnoahfximport pytest from glue.core.state_objects import State from ..elements import (FormElement, NumberElement, ChoiceElement, FloatElement, TextBoxElement) def get_value(element): prefix, widget_cls, property = element.ui_and_state() class TemporaryState(State): a = property temp = TemporaryState() return temp.a class TestFormElements(object): def test_number_default_value(self): e = FormElement.auto((0, 100, 30)) assert get_value(e) == 30 def test_number_float(self): e = FormElement.auto((0.0, 1.0, 0.3)) assert get_value(e) == 0.3 def test_number_list(self): e = FormElement.auto([0, 10]) assert isinstance(e, NumberElement) def test_choice_list(self): e = FormElement.auto(['a', 'b']) assert isinstance(e, ChoiceElement) def test_choice_tuple(self): e = FormElement.auto(('a', 'b')) assert isinstance(e, ChoiceElement) def test_float(self): e = FormElement.auto(1.2) assert isinstance(e, FloatElement) e = FormElement.auto(2) assert isinstance(e, FloatElement) assert get_value(e) == 2 def test_textbox(self): e = FormElement.auto('_str') assert isinstance(e, TextBoxElement) assert get_value(e) == 'str' def test_recognizes_subsubclasses(self): class SubClassFormElement(TextBoxElement): @classmethod def recognizes(cls, params): return params == 'specific_class' e = FormElement.auto('specific_class') assert isinstance(e, SubClassFormElement) def test_unrecognized(self): with pytest.raises(ValueError): FormElement.auto(None) glueviz-1.0.1+dfsg.orig/glue/viewers/custom/qt/tests/test_custom_viewer.py0000644000175000017500000002234013605357235026442 0ustar noahfxnoahfxfrom collections import OrderedDict import numpy as np from matplotlib.axes import Axes from unittest.mock import MagicMock from numpy.testing import assert_array_equal from glue.core.tests.test_state import clone from glue.core.tests.util import simple_session from glue.core.subset import SubsetState from glue.core import Data from glue import custom_viewer from glue.app.qt import GlueApplication from glue.app.qt.tests.test_application import check_clone_app from ..custom_viewer import CustomViewer, CustomSubsetState, AttributeWithInfo def _make_widget(viewer): s = simple_session() return viewer._viewer_cls(s) viewer = custom_viewer('Testing Custom Viewer', a=(0, 100), b='att', c='att(x)', d=True, e=False, f=['a', 'b', 'c'], g=OrderedDict(a=1, b=2, c=3), h=64 ) setup = MagicMock() settings_changed = MagicMock() plot_subset = MagicMock() plot_data = MagicMock() make_selector = MagicMock() make_selector.return_value = MagicMock(spec=SubsetState) make_selector().copy.return_value = MagicMock(spec=SubsetState) make_selector().copy().to_mask.return_value = np.array([False, True, True]) @viewer.setup def _setup(axes): setup(axes) @viewer.plot_data def _plot_data(axes, a, b, g, h): plot_data(axes=axes, a=a, b=b, g=g, h=h) return [] @viewer.plot_subset def _plot_subset(b, c, d, e, f, style): plot_subset(b=b, c=c, d=d, e=e, f=f, style=style) return [] @viewer.settings_changed def _settings_changed(state): settings_changed(state=state) @viewer.make_selector def _make_selector(roi, c): make_selector(roi=roi, c=c) return SubsetState() def test_custom_classes_dont_share_methods(): """Regression test for #479""" a = custom_viewer('a') b = custom_viewer('b') assert a._custom_functions is not b._custom_functions class ViewerSubclass(CustomViewer): a = (0, 100) b = 'att' c = 'att(x)' d = True e = False f = ['a', 'b', 'c'] g = OrderedDict(a=1, b=2, c=3) h = 64 def setup(self, axes): return setup(axes) def plot_data(self, axes, a, b, g, h): return plot_data(axes=axes, a=a, b=b, g=g, h=h) def plot_subset(self, b, c, d, e, f, style): return plot_subset(b=b, c=c, d=d, e=e, f=f, style=style) def settings_changed(self, state): return settings_changed(state=state) def make_selector(self, roi, c): return make_selector(roi=roi, c=c) class TestCustomViewer(object): def setup_class(self): self.viewer = viewer def setup_method(self, method): setup.reset_mock() settings_changed.reset_mock() plot_subset.reset_mock() plot_data.reset_mock() make_selector.reset_mock() self.data = Data(x=[1, 2, 3], y=[2, 3, 4]) self.session = simple_session() self.dc = self.session.data_collection self.dc.append(self.data) def teardown_method(self, method): if hasattr(self, 'w'): self.w.unregister(self.session.hub) def build(self): w = self.viewer._viewer_cls(self.session) w.register_to_hub(self.session.hub) self.w = w return w def test_setup_called_on_init(self): ct = setup.call_count self.build() assert setup.call_count == ct + 1 def test_separate_widgets_have_separate_state(self): w1 = self.build() w2 = self.build() assert w1._coordinator is not w2._coordinator assert w1._coordinator.state is not w2._coordinator.state def test_plot_data(self): w = self.build() w.add_data(self.data) a, k = plot_data.call_args assert isinstance(k['axes'], Axes) assert set(k.keys()) == set(('axes', 'a', 'b', 'g', 'h')) assert k['a'] == 50 assert k['g'] == 1 assert k['h'] == 64 def test_plot_subset(self): w = self.build() w.add_data(self.data) self.dc.new_subset_group(subset_state=self.data.id['x'] > 2) a, k = plot_subset.call_args assert set(k.keys()) == set(('b', 'c', 'd', 'e', 'f', 'style')) assert_array_equal(k['b'], [3]) assert_array_equal(k['c'], [3]) assert k['d'] assert not k['e'] assert k['f'] == 'a' def test_make_selector(self): w = self.build() w.add_data(self.data) roi = MagicMock() w.apply_roi(roi) a, k = make_selector.call_args assert set(k.keys()) == set(('roi', 'c')) assert k['roi'] is roi def test_settings_change(self): w = self.build() ct = settings_changed.call_count w.options_widget().bool_d.setChecked(False) assert settings_changed.call_count == ct + 1 a, k = settings_changed.call_args assert 'state' in k def test_component_autoupdate(self): w = self.build() w.add_data(self.data) assert w.options_widget().combosel_b.count() == 2 self.data.add_component([10, 20, 30], label='c') assert w.options_widget().combosel_b.count() == 3 def test_settings_changed_called_on_init(self): self.build() assert settings_changed.call_count == 1 def test_selections_enabled(self): w = self.build() assert w._coordinator.selections_enabled assert 'select:rectangle' in w.toolbar.tools assert 'select:polygon' in w.toolbar.tools def test_state_save(): app = GlueApplication() w = app.new_data_viewer(viewer._viewer_cls) # noqa check_clone_app(app) def test_state_save_with_data_layers(): app = GlueApplication() dc = app.data_collection d = Data(x=[1, 2, 3], label='test') dc.append(d) w = app.new_data_viewer(viewer._viewer_cls) w.add_data(d) check_clone_app(app) class TestCustomSelectMethod(object): def setup_class(self): self.viewer = custom_viewer('CustomSelectViewer', x='att(x)', flip=False) @self.viewer.select def select(roi, x, flip): if flip: return x <= 1 return x > 1 def setup_method(self, method): self.data = Data(x=[1, 2, 3], y=[2, 3, 4]) self.session = simple_session() self.dc = self.session.data_collection self.dc.append(self.data) def build(self): return self.viewer._viewer_cls(self.session) def test_subset_state(self): w = self.build() v = w._coordinator roi = MagicMock() s = CustomSubsetState(v, roi) assert_array_equal(s.to_mask(self.data), [False, True, True]) def test_subset_state_view(self): w = self.build() v = w._coordinator roi = MagicMock() s = CustomSubsetState(v, roi) assert_array_equal(s.to_mask(self.data, view=slice(None, None, 2)), [False, True]) def test_settings_frozen_at_creation(self): w = self.build() v = w._coordinator roi = MagicMock() s = CustomSubsetState(v, roi) w.flip = True assert_array_equal(s.to_mask(self.data), [False, True, True]) def test_save_load(self): app = GlueApplication(session=self.session) w = app.new_data_viewer(self.viewer._viewer_cls) v = w._coordinator roi = None s = CustomSubsetState(v, roi) app.data_collection.new_subset_group(subset_state=s, label='test') app2 = clone(app) s2 = app2.data_collection[0].subsets[0].subset_state assert_array_equal(s2.to_mask(self.data), [False, True, True]) class TestCustomViewerSubclassForm(TestCustomViewer): def setup_class(self): self.viewer = ViewerSubclass class TestAttributeWithInfo(object): def setup_method(self, method): d = Data(x=[1, 2, 3, 4, 5], c=['a', 'b', 'a', 'a', 'b'], label='test') s = d.new_subset() s.subset_state = d.id['x'] > 2 self.d = d self.s = s def test_numerical(self): v = AttributeWithInfo.from_layer(self.d, self.d.id['x']) assert_array_equal(v, [1, 2, 3, 4, 5]) assert v.id == self.d.id['x'] assert v.categories is None def test_categorical(self): v = AttributeWithInfo.from_layer(self.d, self.d.id['c']) assert_array_equal(v, [0, 1, 0, 0, 1]) assert v.id == self.d.id['c'] assert_array_equal(v.categories, ['a', 'b']) def test_subset(self): v = AttributeWithInfo.from_layer(self.s, self.d.id['x']) assert_array_equal(v, [3, 4, 5]) assert v.id == self.d.id['x'] assert v.categories is None def test_two_custom_viewer_classes(): class MyWidget1(CustomViewer): text_box1_Widget1 = '_Hello' def setup(self, text_box1_Widget1): pass class MyWidget2(CustomViewer): text_box1_Widget2 = '_Hello' text_box2_Widget2 = '_world' def setup(self, text_box1_Widget2, text_box2_Widget2): pass app = GlueApplication() dc = app.data_collection d = Data(x=[1, 2, 3], label='test') dc.append(d) app.new_data_viewer(MyWidget1._viewer_cls) app.new_data_viewer(MyWidget2._viewer_cls) glueviz-1.0.1+dfsg.orig/glue/viewers/custom/qt/elements.py0000644000175000017500000001710713657331513023165 0ustar noahfxnoahfxfrom echo import CallbackProperty, SelectionCallbackProperty from qtpy.QtWidgets import (QSlider, QLineEdit, QComboBox, QWidget, QLabel, QHBoxLayout, QCheckBox) from qtpy.QtCore import Qt __all__ = ["FormElement", "NumberElement", "TextBoxElement", "FloatElement", "BoolElement", "ChoiceElement", "FixedComponentIDProperty", "FixedComponentElement", "ComponenentElement", "DynamicComponentIDProperty"] class QLabeledSlider(QWidget): """ A labeled slider widget """ range = None integer = None def __init__(self, parent=None): super(QLabeledSlider, self).__init__(parent) self._range = range self._slider = QSlider() self._slider.setMinimum(0) self._slider.setMaximum(100) self._slider.setOrientation(Qt.Horizontal) self._label = QLabel('') self._layout = QHBoxLayout() self._layout.setContentsMargins(2, 2, 2, 2) self._layout.addWidget(self._slider) self._layout.addWidget(self._label) self._slider.valueChanged.connect(self._update_label) self.setLayout(self._layout) def _update_label(self, *args): self._label.setText(str(self.value())) @property def valueChanged(self): return self._slider.valueChanged def value(self, layer=None, view=None): value = self._slider.value() / 100. * (self.range[1] - self.range[0]) + self.range[0] if self.integer: return int(value) else: return(value) _in_set_value = False def setValue(self, value): if self._in_set_value: return self._in_set_value = True value = int(100 * (value - self.range[0]) / (self.range[1] - self.range[0])) self._slider.setValue(value) self._in_set_value = False class FormElement(object): """ Base class for user-defined settings in a custom widget. Each form element has a value() and a widget. Subclasses must override _build_ui, value, and recognizes. They may override register_to_hub and add_data. """ def __init__(self, params): self.params = params @classmethod def recognizes(cls, params): """ Returns whether or not a shorthand "params" object can be passed to __init__ to construct an element """ raise NotImplementedError def ui_and_state(self): """ Build and return a widget to represent this setting. """ raise NotImplementedError() @staticmethod def auto(params): """ Construct the appropriate FormElement subclass, given a shorthand object. For examle, FormElement.auto((0., 1.)) returns a NumberElement """ def subclasses(cls): return cls.__subclasses__() + [g for s in cls.__subclasses__() for g in subclasses(s)] for cls in subclasses(FormElement): if cls.recognizes(params): return cls(params) raise ValueError("Unrecognzied UI Component: %s" % (params,)) class NumberElement(FormElement): """ A form element representing a number The shorthand is a tuple of 2 or 3 numbers: (min, max) or (min, max default):: e = FormElement.auto((0., 1.)) """ @classmethod def recognizes(cls, params): try: if len(params) not in [2, 3]: return False return all(isinstance(p, (int, float)) for p in params) except TypeError: return False def ui_and_state(self): if len(self.params) == 3: default = self.params[2] else: default = 0.5 * (self.params[0] + self.params[1]) # We can't initialize QLabeledSlider yet because this could get called # before the Qt application has been initialized. So for now we just make # a subclass of QLabeledSlider with the range we need class CustomSlider(QLabeledSlider): range = self.params[:2] integer = isinstance(self.params[0], int) and isinstance(self.params[1], int) return 'value_', CustomSlider, CallbackProperty(default) class TextBoxElement(FormElement): """ A form element representing a generic textbox The shorthand is any string starting with an _.:: e = FormElement.auto("_default") Everything after the underscore is taken as the default value. """ @classmethod def recognizes(cls, params): try: if isinstance(params, str) & params.startswith('_'): return True except AttributeError: return None def ui_and_state(self): default = self.params[1:] return 'text_', QLineEdit, CallbackProperty(default) class FloatElement(FormElement): """ A form element representing a generic number box. The shorthand is any number:: e = FormElement.auto(2) The number itself is taken as the default value. """ @classmethod def recognizes(cls, params): return isinstance(params, (int, float)) and not isinstance(params, bool) def ui_and_state(self): default = self.params return 'valuetext_', QLineEdit, CallbackProperty(default) class BoolElement(FormElement): """ A checkbox representing a boolean setting The shorthand notation is True or False:: e = FormElement.auto(False) """ @classmethod def recognizes(cls, params): return isinstance(params, bool) def ui_and_state(self): default = self.params return 'bool_', QCheckBox, CallbackProperty(default) class ChoiceElement(FormElement): """ A dropdown selector to choose between a set of items Shorthand notation is a sequence of strings or a dict:: e = FormElement.auto({'a':1, 'b':2}) e = FormElement.auto(['a', 'b', 'c']) """ @classmethod def recognizes(cls, params): if isinstance(params, str): return False try: return all(isinstance(p, str) for p in params) except TypeError: return False def ui_and_state(self): if isinstance(self.params, list): choices = self.params display_func = None else: params_inv = dict((value, key) for key, value in self.params.items()) choices = list(params_inv.keys()) display_func = params_inv.get property = SelectionCallbackProperty(default_index=0, choices=choices, display_func=display_func) return 'combosel_', QComboBox, property class FixedComponentIDProperty(CallbackProperty): pass class FixedComponentElement(FormElement): """ An element for a Data Component. Does not have a widget The shorthand notation is 'att(comp_name)':: e = FormElement.auto('att(foo)') """ @classmethod def recognizes(cls, params): try: return params.startswith('att(') except AttributeError: return False def ui_and_state(self): component_name = self.params.split('(')[-1][:-1] return None, None, FixedComponentIDProperty(component_name) class DynamicComponentIDProperty(SelectionCallbackProperty): pass class ComponenentElement(FormElement): """ A dropdown selector to choose a component The shorthand notation is 'att':: e = FormElement.auto('att') """ @classmethod def recognizes(cls, params): return params == 'att' def ui_and_state(self): return 'combosel_', QComboBox, DynamicComponentIDProperty() glueviz-1.0.1+dfsg.orig/glue/viewers/custom/qt/__init__.py0000644000175000017500000000004513605357235023103 0ustar noahfxnoahfxfrom .custom_viewer import * # noqa glueviz-1.0.1+dfsg.orig/glue/viewers/custom/qt/custom_viewer.py0000644000175000017500000006072713657331513024252 0ustar noahfxnoahfx""" This module provides utilities for creating custom data viewers. The goal of this module is to make it easy for users to make new data viewers by focusing on matplotlib visualization logic, and not UI or event processing logic. The end user typically interacts with this code via :func:`glue.custom_viewer` """ # Implementation notes: # # Here's a high-level summary of how this code works right now: # # The user creates a custom viewer using either of the following # syntaxes: # # # from glue import custom_viewer # my_viewer = custom_viewer('my viewer', checked=True, x='att', ...) # @my_viewer.plot_data # def plot_data(x, checked, axes): # if checked: # axes.plot(x) # ... # # or # # from glue.viewers.custom.qt import CustomViewer # class MyViewer(CustomViewer): # # checked = True # x = 'att' # # def plot_data(self, x, checked, axes): # if checked: # axes.plot(x) # # This code has two "magic" features: # # 1. Attributes like 'checked' and 'x', passed as kwargs to custom_viewer # or set as class-level attributes in the subclass, are turned # into widgets based on their value # # 2. Functions like plot_data can take these settings as input (as well # as some general purpose arguments like axes). Glue takes care of # passing the proper arguments to these functions by introspecting # their call signature. Furthermore, it extracts the current # value of each setting (ie checked is set to True or False depending # on what if the box is checked). # # The intention of all of this magic is to let a user write "simple" functions # to draw custom plots, without having to use Glue or Qt logic directly. # # Internally, Glue accomlishes this magic as follows: # # `FormElement`s are created for each attribute in (1). They build the widget # and have a method of extracting the current value of the widget # # Functions like `plot_data` that are designed to be overriden by users # are defined as custom descriptors -- when called at the class level, # they become decorators that wrap and register the user-defined function. # When called at the instance level, they become dispatch functions which # deal with the logic in (2). The metaclass deals with registering # UDFs when they are overridden in a subclass. from inspect import getmodule from functools import partial from inspect import getfullargspec from types import FunctionType, MethodType import numpy as np from qtpy.QtWidgets import QWidget, QGridLayout, QLabel from echo.qt import autoconnect_callbacks_to_qt from glue.config import qt_client from glue.core import BaseData from glue.core.subset import SubsetState from glue.core.data_combo_helper import ComponentIDComboHelper from glue.core.component_id import ComponentID from glue.utils import as_list, all_artists, new_artists, categorical_ndarray, defer_draw from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer from glue.viewers.matplotlib.state import MatplotlibDataViewerState, MatplotlibLayerState from glue.viewers.matplotlib.layer_artist import MatplotlibLayerArtist from glue.viewers.custom.qt.elements import (FormElement, DynamicComponentIDProperty, FixedComponentIDProperty) __all__ = ["AttributeWithInfo", "ViewerUserState", "UserDefinedFunction", "CustomViewer", "CustomViewerMeta", "CustomSubsetState", "CustomViewer", "CustomLayerArtist", "CustomMatplotlibDataViewer"] class AttributeWithInfo(np.ndarray): """ An array subclass wrapping a Component of a dataset It is an array with the following additional attributes: ``id`` contains the ComponentID or string name of the Component, and ``categories`` is an array or `None`. For categorical Components, it contains the distinct categories which are integer-encoded in the AttributeWithInfo """ @classmethod def make(cls, id, values, categories=None): values = np.asarray(values) result = values.view(AttributeWithInfo) result.id = id result.values = values result.categories = categories return result @classmethod def from_layer(cls, layer, cid, view=None): """ Build an AttributeWithInfo out of a subset or dataset. Parameters ---------- layer : :class:`~glue.core.data.Data` or :class:`~glue.core.subset.Subset` The data to use cid : ComponentID The ComponentID to use view : numpy-style view (optional) What slice into the data to use """ values = layer[cid, view] if isinstance(values, categorical_ndarray): categories = values.categories values = values.codes else: categories = None return cls.make(cid, values, categories) def __gluestate__(self, context): return dict(cid=context.id(self.id)) @classmethod def __setgluestate__(cls, rec, context): return cls.make(context.object(rec['cid']), [], None) class ViewerUserState(object): """ Empty object for users to store data inside. """ def __gluestate__(self, context): return dict(data=[(k, context.id(v)) for k, v in self.__dict__.items()]) @classmethod def __setgluestate__(cls, rec, context): result = cls() rec = rec['data'] for k in rec: setattr(result, k, context.object(rec[k])) return result class UserDefinedFunction(object): """ Descriptor to specify a UserDefinedFunction. Defined in CustomViewer like this:: class CustomViewer(object): ... plot_data = UserDefinedFunction('plot_data') The descriptor gives CustomViewer.plot_data a dual functionality. When accessed at the class level, it behaves as a decorator to register new UDFs:: cv = custom_viewer(...) @cv.plot_data # becomes a decorator def plot_data_implementation(...): ... When accessed at the instance level, it becomes a dispatch function that calls `plot_data_implementation` with the proper arguments Alternatively, plot_data_implementation can be specified by explicitly overriding plot_data in a subclass. A metaclass takes care of registering the UDF in that case, so you can define plot_data as a normal (non-decorator, non-descriptor) method. """ def __init__(self, name): self.name = name def __get__(self, instance, cls=None): if instance is None: # accessed from class level, return a decorator # to wrap a custom UDF return partial(cls._register_override_method, self.name) # method called at instance level, # return a dispatcher to the UDF return partial(instance._call_udf, self.name) def introspect_and_call(func, state, override): """ Introspect a function for its arguments, extract values for those arguments from a state class, and call the function Parameters ---------- func : function A function to call. It should not define any keywords state : State A state class containing the values to pass override : dict A dictionary containing values that should override the state Returns ------- The result of calling func with the proper arguments *Example* def a(x, y): return x, y introspect_and_call(a, state) will return a(state.x, state.y) Attributes will be used from ``override`` before ``state``. """ a, k = getfullargspec(func)[:2] args = [] for item in a: if item in override: args.append(override[item]) elif hasattr(state, item): args.append(getattr(state, item)) else: setting_list = "\n -".join(state.callback_properties() + list(override)) raise MissingSettingError("This custom viewer is trying to use an " "unrecognized variable named %s\n. Valid " "variable names are\n -%s" % (item, setting_list)) k = k or {} return func(*args, **k) class MissingSettingError(KeyError): pass class CustomViewerMeta(type): """ Metaclass to construct CustomViewer and subclasses The metaclass does two things when constructing new classes: - it finds the class-level attributes that describe ui elements (eg `checked=False`). It bundles these into a `ui` dict attribute, later used to construct the FormElements and widgets to represent each setting - It creates the qt DataViewer widget class associated with this class. - It looks for overridden user-defined methods like `plot_subset`, and registers them for later use. """ def __new__(cls, name, bases, attrs): # don't muck with the base class if name == 'CustomViewer': return type.__new__(cls, name, bases, attrs) # Find ui elements ui = {} for key, value in list(attrs.items()): if key.startswith('_') or key in CustomViewer.__dict__: continue if not isinstance(value, (MethodType, FunctionType)): ui[key] = attrs.pop(key) attrs['ui'] = ui attrs.setdefault('name', name) # collect the user defined functions udfs = {} for nm, value in list(attrs.items()): dscr = CustomViewer.__dict__.get(nm, None) if isinstance(dscr, UserDefinedFunction): # remove them as class method # register them below instead udfs[nm] = attrs.pop(nm) result = type.__new__(cls, name, bases, attrs) result._custom_functions = {} # now wrap the custom user defined functions using the descriptors for k, v in udfs.items(): # register UDF by mimicing the decorator syntax udf_decorator = getattr(result, k) udf_decorator(v) result._build_data_viewer() return result class CustomSubsetState(SubsetState): """ A SubsetState subclass that uses a CustomViewer's "select" function """ def __init__(self, coordinator, roi): super(CustomSubsetState, self).__init__() self._coordinator = coordinator self._roi = roi def to_mask(self, data, view=None): return self._coordinator.select(layer=data, roi=self._roi, view=view) def copy(self): return CustomSubsetState(self._coordinator, self._roi) def __gluestate__(self, context): result = {} result['viewer'] = context.id(self._coordinator.viewer) result['roi'] = context.id(self._roi) return result @classmethod def __setgluestate__(cls, rec, context): roi = context.object(rec['roi']) subset_state = cls(None, roi) subset_state._viewer_rec = rec['viewer'] return subset_state def __setgluestate_callback__(self, context): # When __setgluestate__ is created, the viewers might not yet be # deserialized, and these depend on the Data and Subsets existing so # we need to deserialize the viewer in a callback so it can be called # later on. viewer = context.object(self._viewer_rec) self._coordinator = viewer._coordinator self._viewer_rec = None class BaseCustomOptionsWidget(QWidget): """ Base class for the Qt widget which will be used to show the options. """ _widgets = None def __init__(self, viewer_state=None, session=None): super(BaseCustomOptionsWidget, self).__init__() layout = QGridLayout() for row, (name, (prefix, viewer_cls)) in enumerate(self._widgets.items()): widget = viewer_cls() setattr(self, prefix + name, widget) layout.addWidget(QLabel(name.capitalize()), row, 0) layout.addWidget(widget, row, 1) if len(self._widgets) > 0: layout.setRowStretch(row + 1, 10) self.setLayout(layout) self.viewer_state = viewer_state self.session = session self._connections = autoconnect_callbacks_to_qt(self.viewer_state, self) class CustomViewer(object, metaclass=CustomViewerMeta): """ Base class for custom data viewers. Users can either subclass this class and override one or more custom methods listed below, or use the :func:`glue.custom_viewer` function and decorate custom plot functions. *Custom Plot Methods* The following methods can be overridden: - :meth:`CustomViewer.setup` - :meth:`CustomViewer.plot_data` - :meth:`CustomViewer.plot_subset` - :meth:`CustomViewer.settings_changed` - :meth:`CustomViewer.make_selector` - :meth:`CustomViewer.select` *Method Signatures* Custom methods should use argument names from the following list: - The name of a UI element (e.g. keywords passed to :func:`glue.custom_viewer`, or class-level variables in subclasses). The value assigned to this argument will be the current UI setting (e.g. booleans for checkboxes). - ``axes`` will contain a matplotlib Axes object - ``roi`` will contain the ROI a user has drawn (only available for ``make_selector``) - ``state`` will contain a general-purpose object to store other data - ``style`` contains the :class:`~glue.core.visual.VisualAttributes` describing a subset or dataset. Only available for ``plot_data`` and `plot_subset`` - ``subset`` will contain the relevant :class:`~glue.core.subset.Subset` object. Only available for ``plot_subset`` *Defining the UI* Simple widget-based UIs can be specified by providing keywords to :func:`~glue.custom_viewer` or class-level variables to subsets. The kind of widget to associate with each UI element is determined from it's type. *Example decorator* :: v = custom_viewer('Example', checkbox=False) @v.plot_data def plot(checkbox, axes): axes.plot([1, 2, 3]) *Example subclass* :: class CustomViewerSubset(CustomViewer): checkbox = False def plot_data(self, checkbox, axes): axes.plot([1, 2, 3]) The order of arguments can be listed in any order. """ # Label to give this widget in the GUI name = '' # Container to hold user descriptions of desired FormElements to create ui = {} # map, e.g., 'plot_data' -> user defined function - we also make sure we # override this in sub-classes in CustomViewerMeta _custom_functions = {} def __init__(self, viewer): self.viewer = viewer self.state = ViewerUserState() self.setup() @property def selections_enabled(self): return 'make_selector' in self._custom_functions or 'select' in self._custom_functions @classmethod def create_new_subclass(cls, name, **kwargs): """ Convenience method to build a new CustomViewer subclass. This is used by the custom_viewer function. Parameters ---------- name : str Name of the new viewer kwargs UI elements in the subclass """ kwargs = kwargs.copy() kwargs['name'] = name # each subclass needs its own dict kwargs['_custom_functions'] = {} name = name.replace(' ', '') return CustomViewerMeta(name, (CustomViewer,), kwargs) @classmethod def _build_data_viewer(cls): """ Build the DataViewer subclass for this viewer. """ # At this point, the metaclass has put all the user options in a dict # called .ui, so we go over this dictionary and find the widgets and # callback properties for each of them. widgets = {} properties = {} for name in sorted(cls.ui): value = cls.ui[name] prefix, widget, property = FormElement.auto(value).ui_and_state() if widget is not None: widgets[name] = prefix, widget properties[name] = property options_cls = type(cls.__name__ + 'OptionsWidget', (BaseCustomOptionsWidget,), {'_widgets': widgets}) state_cls = type(cls.__name__ + 'ViewerState', (CustomMatplotlibViewerState,), properties) widget_dict = {'LABEL': cls.name, 'ui': cls.ui, '_options_cls': options_cls, '_state_cls': state_cls, '_coordinator_cls': cls} viewer_cls = type(cls.__name__ + 'DataViewer', (CustomMatplotlibDataViewer,), widget_dict) cls._viewer_cls = viewer_cls qt_client.add(viewer_cls) # add new classes to module namespace # needed for proper state saving/restoring for c in [viewer_cls, cls]: mod = getmodule(ViewerUserState) w = getattr(mod, c.__name__, None) if w is not None: raise RuntimeError("Duplicate custom viewer detected %s" % c) setattr(mod, c.__name__, c) c.__module__ = mod.__name__ @classmethod def _register_override_method(cls, name, func): """ Register a new custom method like "plot_data" Users need not call this directly - it is called when a method is overridden or decorated """ cls._custom_functions[name] = func def _build_subset_state(self, roi): if 'make_selector' in self._custom_functions: return self.make_selector(roi=roi) if 'select' in self._custom_functions: return CustomSubsetState(self, roi) raise RuntimeError("Selection not supported for this viewer.") # List of user-defined functions. # Users can either use these as decorators to # wrap custom functions, or override them in subclasses. setup = UserDefinedFunction('setup') """ Custom method called when plot is created """ plot_subset = UserDefinedFunction('plot_subset') """ Custom method called to show a subset """ plot_data = UserDefinedFunction('plot_data') """ Custom method called to show a dataset """ make_selector = UserDefinedFunction('make_selector') """ Custom method called to build a :class:`~glue.core.subset.SubsetState` from an ROI. See :meth:`~CustomViewer.select` for an alternative way to define selections, by returning Boolean arrays instead of SubsetStates. Functions have access to the roi by accepting an ``roi`` argument to this function """ settings_changed = UserDefinedFunction('settings_changed') """ Custom method called when UI settings change. """ select = UserDefinedFunction('select') """ Custom method called to filter data using an ROI. This is an alternative function to :meth:`~CustomViewer.make_selector`, which returns a numpy boolean array instead of a SubsetState. Functions have access to the roi by accepting an ``roi`` argument to this function """ def _call_udf(self, method_name, **kwargs): """ Call a user-defined function stored in the _custom_functions dict Parameters ---------- method_name : str The name of the user-defined method to setup a dispatch for use_cid : bool, optional Whether to pass component IDs to the user function instead of the data itself. **kwargs : dict Custom settings to pass to the UDF if they are requested by name as input arguments Returns ------- The result of the UDF Notes ----- This function builds the necessary arguments to the user-defined function. It also attempts to monitor the state of the matplotlib plot, removing stale artists and re-rendering the canvas as needed. """ # get the custom function try: func = self._custom_functions[method_name] except KeyError: return [] override = kwargs.copy() if 'layer' not in override and len(self.viewer.state.layers) > 0: override['layer'] = self.viewer.state.layers[0].layer if 'layer' in override: override.setdefault('style', override['layer'].style) # Dereference attributes for name, property in self.viewer.state.iter_callback_properties(): value = getattr(self.viewer.state, name) if isinstance(value, ComponentID) or isinstance(property, FixedComponentIDProperty): override[name] = AttributeWithInfo.from_layer(override['layer'], value, view=override.get('view', None)) # add some extra information that the user might want override.setdefault('self', self) override.setdefault('axes', self.viewer.axes) override.setdefault('figure', self.viewer.axes.figure) override.setdefault('state', self.state) # call method, keep track of newly-added artists result = introspect_and_call(func, self.viewer.state, override) self.viewer.redraw() return result class CustomLayerArtist(MatplotlibLayerArtist): """ LayerArtist for simple custom viewers that use Matplotlib """ _layer_state_cls = MatplotlibLayerState def __init__(self, coordinator, *args, **kwargs): super(CustomLayerArtist, self).__init__(*args, **kwargs) self._coordinator = coordinator self.state.add_global_callback(self.update) self._viewer_state.add_global_callback(self.update) def update(self, *args, **kwargs): if not self._visible: return self.clear() old = all_artists(self.axes.figure) if isinstance(self.state.layer, BaseData): a = self._coordinator.plot_data(layer=self.state.layer) else: a = self._coordinator.plot_subset(layer=self.state.layer, subset=self.state.layer) # if user explicitly returns the newly-created artists, # then use them. Otherwise, introspect to find the new artists if a is None: self.mpl_artists = list(new_artists(self.axes.figure, old)) else: self.mpl_artists = as_list(a) for a in self.mpl_artists: a.set_zorder(self.state.zorder) class CustomMatplotlibDataViewer(MatplotlibDataViewer): """ Base Qt widget class for simple custom viewers that use Matplotlib """ LABEL = '' tools = ['select:rectangle', 'select:polygon'] _state_cls = None _options_cls = None _layer_style_viewer_cls = None _data_artist_cls = CustomLayerArtist _subset_artist_cls = CustomLayerArtist _coordinator_cls = None def __init__(self, session, parent=None, **kwargs): super(CustomMatplotlibDataViewer, self).__init__(session, parent, **kwargs) self._coordinator = self._coordinator_cls(self) self.state.add_global_callback(self._on_state_change) self._on_state_change() def _on_state_change(self, *args, **kwargs): self._coordinator.settings_changed() def get_layer_artist(self, cls, layer=None, layer_state=None): return cls(self._coordinator, self.axes, self.state, layer=layer, layer_state=layer_state) @defer_draw def apply_roi(self, roi): # Force redraw to get rid of ROI. We do this because applying the # subset state below might end up not having an effect on the viewer, # for example there may not be any layers, or the active subset may not # be one of the layers. So we just explicitly redraw here to make sure # a redraw will happen after this method is called. self.redraw() if len(self.layers) == 0: return subset_state = self._coordinator._build_subset_state(roi=roi) self.apply_subset_state(subset_state) class CustomMatplotlibViewerState(MatplotlibDataViewerState): def __init__(self, *args, **kwargs): super(CustomMatplotlibViewerState, self).__init__(*args) self._cid_helpers = [] for name, property in self.iter_callback_properties(): if isinstance(property, DynamicComponentIDProperty): self._cid_helpers.append(ComponentIDComboHelper(self, name)) self.add_callback('layers', self._on_layer_change) self.update_from_dict(kwargs) def _on_layer_change(self, *args): for helper in self._cid_helpers: helper.set_multiple_data(self.layers_data) glueviz-1.0.1+dfsg.orig/glue/viewers/custom/__init__.py0000644000175000017500000000000013455362716022451 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/viewers/custom/helper.py0000644000175000017500000000740513502206677022205 0ustar noahfxnoahfx# This is a helper function that gets imported into the top-level namespace of glue. __all__ = ['custom_viewer'] def custom_viewer(name, **kwargs): """ Create a custom interactive data viewer. To use this, first create a new variable by calling custom_viewer. Then, register one or more viewer functions using decorators. :param name: The name of the new viewer :type name: str Named arguments are used to build widgets and pass data to viewer functions. See ``specifying widgets`` below. Example:: v = custom_viewer('My custom viewer', check=False, x='att(x)') @v.setup def setup_func(axes): ''' Setup the plot when the viewer is created ''' ... @v.plot_data def plot_data_func(axes, check, style): ''' Visualize a full dataset ''' ... @v.plot_subset def plot_subset_func(axes, check, style): ''' Visualize a subset ''' ... @v.update_settings def update_settings_func(check): ''' Respond to the user changing a widget setting ''' ... @v.select def select(roi, x): ''' Filter a dataset based on an roi. Return a boolean array ''' ... @v.make_selector def make_selector_func(roi): ''' Turn a roi into a subset state ''' ... **Specifying Widgets** Keywords passed to ``custom_viewer`` serve two purposes: they setup information to be passed into the viewer functions, and they create widgets. The type of widget that is created depends on the keyword value: * ``keyword=False | True`` creates a checkbox. The check state is passed as a Boolean into the viewer functions * ``keyword=(10, 20, [15])`` creates a slider. The current value of the slider is passed as a number to the viewer functions. The first two numbers specify the minimum and maximum allowed value, while the optional third number specifies the initial value. * ``keyword=['a', 'b', 'c']`` creates a dropdown menu. The current selection is passed as a string to the viewer functions. * ``keyword={'a':1, 'b':2}`` behaves similarly to the lists above, but uses the keys as dropdown labels and values as the setting passed to viewer functions. * ``keyword='att(foo)'`` doesn't create any widget, but passes in the attribute named ``foo`` to the viewer functions, as an :class:`~glue.viewers.custom.qt.custom_viewer.AttributeWithInfo` object. * ``keyword='att'`` creates a dropdown to let the user select one of the attributes from the data. The selected attribute is passed as an :class:`~glue.viewers.custom.qt.custom_viewer.AttributeWithInfo` **Viewer Functions** Custom viewers can implement any of the following functions: * ``setup_func`` is called once, when the viewer is created. * ``plot_data`` is called to update the visualization of a full dataset. * ``plot_subset`` is used to visualize data subsets. * ``update_settings`` is called whenever a user modifies a widget setting. * ``select`` specifies how user-drawn regions on the viewer are used to filter data. It has access to an :class:`~glue.core.roi.Roi` input, and returns a Boolean array testing whether each element in a dataset is part of a subset. * ``make_selector`` is an alternative to ``select``. Instead of returning an array, ``make_selector`` returns a :class:`~glue.core.subset.SubsetState` """ # For now we only support Qt, but this function should be extended to work # with non-Qt front-ends in future. from glue.viewers.custom.qt import CustomViewer return CustomViewer.create_new_subclass(name, **kwargs) glueviz-1.0.1+dfsg.orig/glue/external/0000755000175000017500000000000013752535025017171 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/tests/0000755000175000017500000000000013752535025020333 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/tests/__init__.py0000644000175000017500000000000013455362716022437 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/echo/0000755000175000017500000000000013752535025020107 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/echo/qt/0000755000175000017500000000000013752535025020533 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/echo/qt/__init__.py0000644000175000017500000000002613657331513022642 0ustar noahfxnoahfxfrom echo.qt import * glueviz-1.0.1+dfsg.orig/glue/external/echo/qt/connect.py0000644000175000017500000000013413657331513022534 0ustar noahfxnoahfxfrom echo.qt.connect import * from echo.qt.connect import _find_combo_data, UserDataWrapper glueviz-1.0.1+dfsg.orig/glue/external/echo/qt/autoconnect.py0000644000175000017500000000011313657331513023422 0ustar noahfxnoahfxfrom echo.qt.autoconnect import * from echo.qt.autoconnect import HANDLERS glueviz-1.0.1+dfsg.orig/glue/external/echo/core.py0000644000175000017500000000003013657331513021402 0ustar noahfxnoahfxfrom echo.core import * glueviz-1.0.1+dfsg.orig/glue/external/echo/list.py0000644000175000017500000000003613657331513021433 0ustar noahfxnoahfxfrom echo.containers import * glueviz-1.0.1+dfsg.orig/glue/external/echo/__init__.py0000644000175000017500000000017013657331513022216 0ustar noahfxnoahfximport warnings from echo import * warnings.warn('glue.external.echo is deprecated, import from echo directly instead') glueviz-1.0.1+dfsg.orig/glue/external/echo/selection.py0000644000175000017500000000003513657331513022444 0ustar noahfxnoahfxfrom echo.selection import * glueviz-1.0.1+dfsg.orig/glue/external/echo/callback_container.py0000644000175000017500000000004613657331513024257 0ustar noahfxnoahfxfrom echo.callback_container import * glueviz-1.0.1+dfsg.orig/glue/external/__init__.py0000644000175000017500000000022213502206677021277 0ustar noahfxnoahfx""" Modules in this directory smooth over importing functionality that may be present in different libraries, depending on the users' system. """ glueviz-1.0.1+dfsg.orig/glue/external/axescache.py0000644000175000017500000001432313507416115021466 0ustar noahfxnoahfx""" The AxesCache class alters how an Axes instance is rendered. While enabled, the AxesCache quickly re-renders an original view, properly scaled and translated to reflect changes in the viewport. The downside is that the re-rendered image is fuzzy and/or truncated. The best way to use an AxesCache is to enable it during window resize drags and pan/zoom mouse drags; these generate rapid draw requests, and users might prefer high refresh rates to pixel-perfect renders. Unfortunately, Matplotlib on it's own doesn't provide an easy mechanism to attach event handlers to either window resize drags or pan/zoom drags. This code must be added separately. """ import numpy as np from matplotlib.axes import Axes from matplotlib.image import AxesImage from matplotlib.collections import QuadMesh class RenderCapture(object): """ A RemderCapture saves an image of a fully-rendered Axes instance, and provides a method for re-rendering a properly transformed image during panning and zooming """ def __init__(self, axes, renderer): self.axes = axes self._corners = self._get_corners(axes) px, py, dx, dy = self._corners im = self.extract_image(renderer) im = im[py[0]: py[-1] + 1, px[0]: px[-1] + 1, :] self.im = im self._mesh = None self._image = None self.image @property def image(self): if self._image is not None: return self._image px, py, dx, dy = self._corners self._image = AxesImage(self.axes, origin='lower', interpolation='nearest') self._image.set_data(self.im) self._image.set_extent((dx[0], dx[-1], dy[0], dy[-1])) self.axes._set_artist_props(self._image) return self._image @property def mesh(self): if self._mesh is not None: return self._mesh px, py, dx, dy = self._corners x, y, c = self.axes._pcolorargs('pcolormesh', dx, dy, self.im[:, :, 0], allmatch=False) ny, nx = x.shape coords = np.column_stack((x.ravel(), y.ravel())) collection = QuadMesh(nx - 1, ny - 1, coords, shading='flat', antialiased=False, edgecolors='None', cmap='gray') collection.set_array(c.ravel()) collection.set_clip_path(self.axes.patch) collection.set_transform(self.axes.transData) self._mesh = collection return self._mesh def draw(self, renderer, *args, **kwargs): if self.axes.get_xscale() == 'linear' and \ self.axes.get_yscale() == 'linear': self.image.draw(renderer, *args, **kwargs) else: self.mesh.draw(renderer, *args, **kwargs) @staticmethod def _get_corners(axes): """ Return the device and data coordinates for a box slightly inset from the edge of an axes instance Returns 4 1D arrays: px : Pixel X locations for each column of the box py : Pixel Y locations for each row of the box dx : Data X locations for each column of the box dy : Data Y locations for each row of the box """ xlim = axes.get_xlim() ylim = axes.get_ylim() pts = np.array([[xlim[0], ylim[0]], [xlim[1], ylim[1]]]) corners = axes.transData.transform(pts).astype(np.int) # move in 5 pixels, to avoid grabbing the tick marks px = np.arange(corners[0, 0] + 5, corners[1, 0] - 5) py = np.arange(corners[0, 1] + 5, corners[1, 1] - 5) tr = axes.transData.inverted().transform dx = tr(np.column_stack((px, px)))[:, 0] dy = tr(np.column_stack((py, py)))[:, 1] return px, py, dx, dy @staticmethod def extract_image(renderer): try: buf = renderer.buffer_rgba() except TypeError: # mpl v1.1 has different signature buf = renderer.buffer_rgba(0, 0) result = np.frombuffer(buf, dtype=np.uint8) result = result.reshape((int(renderer.height), int(renderer.width), 4)).copy() return np.flipud(result) class AxesCache(object): def __init__(self, axes): self.axes = axes self._capture = None self.axes.draw = self.draw self._enabled = False def draw(self, renderer, *args, **kwargs): if self._capture is None or not self._enabled: Axes.draw(self.axes, renderer, *args, **kwargs) if hasattr(renderer, 'buffer_rgba'): self._capture = RenderCapture(self.axes, renderer) else: self.axes.axesPatch.draw(renderer, *args, **kwargs) self._capture.draw(renderer, *args, **kwargs) self.axes.xaxis.draw(renderer, *args, **kwargs) self.axes.yaxis.draw(renderer, *args, **kwargs) for s in self.axes.spines.values(): s.draw(renderer, *args, **kwargs) def clear_cache(self): """ Clear the cache, forcing the a full re-render """ self._capture = None def disable(self): """ Temporarily disable cache re-renders. Render results are still saved, for when enable() is next called """ self._enabled = False self.axes.figure.canvas.draw_idle() def enable(self): """ Enable cached-rerenders """ self._enabled = True def teardown(self): """ Permanently disable this cache, and restore normal Axes render behavior """ self.axes.draw = Axes.draw.__get__(self.axes) if __name__ == "__main__": import matplotlib.pyplot as plt num = 1000000 plt.subplot(111) plt.subplots_adjust(bottom=.5, top=.8) plt.scatter(np.random.randn(num), np.random.randn(num), s=np.random.randint(10, 50, num), c=np.random.randint(0, 255, num), alpha=.2, linewidths=0) plt.plot([0, 1, 2, 3], [0, 1, 2, 3]) cache = AxesCache(plt.gca()) cache.enable() plt.grid('on') # plt.xscale('log') plt.show() glueviz-1.0.1+dfsg.orig/glue/external/modest_image.py0000644000175000017500000002671213607640331022204 0ustar noahfxnoahfx""" Modification of Chris Beaumont's mpl-modest-image package to allow the use of set_extent. """ from __future__ import print_function, division import matplotlib rcParams = matplotlib.rcParams import matplotlib.image as mi import matplotlib.colors as mcolors import matplotlib.cbook as cbook from matplotlib.transforms import IdentityTransform, Affine2D import numpy as np IDENTITY_TRANSFORM = IdentityTransform() class ModestImage(mi.AxesImage): """ Computationally modest image class. ModestImage is an extension of the Matplotlib AxesImage class better suited for the interactive display of larger images. Before drawing, ModestImage resamples the data array based on the screen resolution and view window. This has very little affect on the appearance of the image, but can substantially cut down on computation since calculations of unresolved or clipped pixels are skipped. The interface of ModestImage is the same as AxesImage. However, it does not currently support setting the 'extent' property. There may also be weird coordinate warping operations for images that I'm not aware of. Don't expect those to work either. """ def __init__(self, *args, **kwargs): self._pressed = False self._full_res = None self._full_extent = kwargs.get('extent', None) super(ModestImage, self).__init__(*args, **kwargs) self.invalidate_cache() self.axes.figure.canvas.mpl_connect('button_press_event', self._press) self.axes.figure.canvas.mpl_connect('button_release_event', self._release) self.axes.figure.canvas.mpl_connect('resize_event', self._resize) self._timer = self.axes.figure.canvas.new_timer(interval=500) self._timer.single_shot = True self._timer.add_callback(self._resize_paused) def remove(self): super(ModestImage, self).remove() self._timer.stop() self._timer = None def _resize(self, *args): self._pressed = True self._timer.start() def _resize_paused(self, *args): # If the artist has been removed, self.axes is no longer defined, so # we can return early here. if self.axes is None: return self._pressed = False self.axes.figure.canvas.draw_idle() def _press(self, *args): self._pressed = True def _release(self, *args): self._pressed = False self.stale = True self.axes.figure.canvas.draw_idle() def set_data(self, A): """ Set the image array ACCEPTS: numpy/PIL Image A """ self._full_res = A self._A = A if self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype, np.float): raise TypeError("Image data can not convert to float") if (self._A.ndim not in (2, 3) or (self._A.ndim == 3 and self._A.shape[-1] not in (3, 4))): raise TypeError("Invalid dimensions for image data") self.invalidate_cache() def invalidate_cache(self): self._bounds = None self._imcache = None self._rgbacache = None self._oldxslice = None self._oldyslice = None self._sx, self._sy = None, None self._pixel2world_cache = None self._world2pixel_cache = None def get_cursor_data(self, event): return None def contains(self, mouseevent): if self._A is None or self._A.shape is None: return False else: return super(ModestImage, self).contains(mouseevent) def set_extent(self, extent): self._full_extent = extent self.invalidate_cache() mi.AxesImage.set_extent(self, extent) def get_array(self): """Override to return the full-resolution array""" return self._full_res @property def _pixel2world(self): if self._pixel2world_cache is None: # Pre-compute affine transforms to convert between the 'world' # coordinates of the axes (what is shown by the axis labels) to # 'pixel' coordinates in the underlying array. extent = self._full_extent if extent is None: self._pixel2world_cache = IDENTITY_TRANSFORM else: self._pixel2world_cache = Affine2D() self._pixel2world.translate(+0.5, +0.5) self._pixel2world.scale((extent[1] - extent[0]) / self._full_res.shape[1], (extent[3] - extent[2]) / self._full_res.shape[0]) self._pixel2world.translate(extent[0], extent[2]) self._world2pixel_cache = None return self._pixel2world_cache @property def _world2pixel(self): if self._world2pixel_cache is None: self._world2pixel_cache = self._pixel2world.inverted() return self._world2pixel_cache def _scale_to_res(self): """ Change self._A and _extent to render an image whose resolution is matched to the eventual rendering. """ # Find out how we need to slice the array to make sure we match the # resolution of the display. We pass self._world2pixel which matters # for cases where the extent has been set. x0, x1, sx, y0, y1, sy = extract_matched_slices(axes=self.axes, shape=self._full_res.shape, transform=self._world2pixel) # Check whether we've already calculated what we need, and if so just # return without doing anything further. if (self._bounds is not None and sx >= self._sx and sy >= self._sy and x0 >= self._bounds[0] and x1 <= self._bounds[1] and y0 >= self._bounds[2] and y1 <= self._bounds[3]): return # Slice the array using the slices determined previously to optimally # match the display self._A = self._full_res[y0:y1:sy, x0:x1:sx] self._A = cbook.safe_masked_invalid(self._A) # We now determine the extent of the subset of the image, by determining # it first in pixel space, and converting it to the 'world' coordinates. # See https://github.com/matplotlib/matplotlib/issues/8693 for a # demonstration of why origin='upper' and extent=None needs to be # special-cased. if self.origin == 'upper' and self._full_extent is None: xmin, xmax, ymin, ymax = x0 - .5, x1 - .5, y1 - .5, y0 - .5 else: xmin, xmax, ymin, ymax = x0 - .5, x1 - .5, y0 - .5, y1 - .5 xmin, ymin, xmax, ymax = self._pixel2world.transform([(xmin, ymin), (xmax, ymax)]).ravel() mi.AxesImage.set_extent(self, [xmin, xmax, ymin, ymax]) # self.set_extent([xmin, xmax, ymin, ymax]) # Finally, we cache the current settings to avoid re-computing similar # arrays in future. self._sx = sx self._sy = sy self._bounds = (x0, x1, y0, y1) self.changed() def draw(self, renderer, *args, **kwargs): if self._full_res.shape is None: return if not self._pressed or self._bounds is None: self._scale_to_res() # Due to a bug in Matplotlib, we need to return here if all values # in the array are masked. if hasattr(self._A, 'mask') and np.all(self._A.mask): return super(ModestImage, self).draw(renderer, *args, **kwargs) def main(): from time import time import matplotlib.pyplot as plt x, y = np.mgrid[0:2000, 0:2000] data = np.sin(x / 10.) * np.cos(y / 30.) f = plt.figure() ax = f.add_subplot(111) # try switching between artist = ModestImage(ax, data=data) ax.set_aspect('equal') artist.norm.vmin = -1 artist.norm.vmax = 1 ax.add_artist(artist) t0 = time() plt.gcf().canvas.draw_idle() t1 = time() print("Draw time for %s: %0.1f ms" % (artist.__class__.__name__, (t1 - t0) * 1000)) plt.show() def imshow(axes, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, origin=None, extent=None, shape=None, filternorm=1, filterrad=4.0, imlim=None, resample=None, url=None, **kwargs): """Similar to matplotlib's imshow command, but produces a ModestImage Unlike matplotlib version, must explicitly specify axes """ if norm is not None: assert(isinstance(norm, mcolors.Normalize)) if aspect is None: aspect = rcParams['image.aspect'] axes.set_aspect(aspect) im = ModestImage(axes, cmap=cmap, norm=norm, interpolation=interpolation, origin=origin, extent=extent, filternorm=filternorm, filterrad=filterrad, resample=resample, **kwargs) im.set_data(X) im.set_alpha(alpha) axes._set_artist_props(im) if im.get_clip_path() is None: # image does not already have clipping set, clip to axes patch im.set_clip_path(axes.patch) # if norm is None and shape is None: # im.set_clim(vmin, vmax) if vmin is not None or vmax is not None: im.set_clim(vmin, vmax) # elif norm is None: # im.autoscale_None() im.set_url(url) # update ax.dataLim, and, if autoscaling, set viewLim # to tightly fit the image, regardless of dataLim. if extent is not None: im.set_extent(extent) axes.images.append(im) def remove(h): axes.images.remove(h) im._remove_method = remove return im def extract_matched_slices(axes=None, shape=None, extent=None, transform=IDENTITY_TRANSFORM): """Determine the slice parameters to use, matched to the screen. :param ax: Axes object to query. It's extent and pixel size determine the slice parameters :param shape: Tuple of the full image shape to slice into. Upper boundaries for slices will be cropped to fit within this shape. :rtype: tulpe of x0, x1, sx, y0, y1, sy Indexing the full resolution array as array[y0:y1:sy, x0:x1:sx] returns a view well-matched to the axes' resolution and extent """ # Find extent in display pixels (this gives the resolution we need # to sample the array to) ext = (axes.transAxes.transform([(1, 1)]) - axes.transAxes.transform([(0, 0)]))[0] # Find the extent of the axes in 'world' coordinates xlim, ylim = axes.get_xlim(), axes.get_ylim() # Transform the limits to pixel coordinates ind0 = transform.transform([min(xlim), min(ylim)]) ind1 = transform.transform([max(xlim), max(ylim)]) def _clip(val, lo, hi): return int(max(min(val, hi), lo)) # Determine the range of pixels to extract from the array, including a 5 # pixel margin all around. We ensure that the shape of the resulting array # will always be at least (1, 1) even if there is really no overlap, to # avoid issues. y0 = _clip(ind0[1] - 5, 0, shape[0] - 1) y1 = _clip(ind1[1] + 5, 1, shape[0]) x0 = _clip(ind0[0] - 5, 0, shape[1] - 1) x1 = _clip(ind1[0] + 5, 1, shape[1]) # Determine the strides that can be used when extracting the array sy = int(max(1, min((y1 - y0) / 5., np.ceil(abs((ind1[1] - ind0[1]) / ext[1]))))) sx = int(max(1, min((x1 - x0) / 5., np.ceil(abs((ind1[0] - ind0[0]) / ext[0]))))) return x0, x1, sx, y0, y1, sy if __name__ == "__main__": main() glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/0000755000175000017500000000000013752535025021552 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/pvextractor/gui.py0000644000175000017500000003214513557050406022713 0ustar noahfxnoahfxfrom __future__ import print_function import os import math import warnings import numpy as np from matplotlib.collections import LineCollection from matplotlib.transforms import Bbox from matplotlib.patches import Polygon from .geometry.path import Path, get_endpoints from . import extract_pv_slice def distance(x1, y1, x2, y2, x3, y3): """ Find the shortest distance between a point (x3, y3) and the line passing through the points (x1, y1) and (x2, y2). """ px = x2-x1 py = y2-y1 something = px * px + py * py u = ((x3 - x1) * px + (y3 - y1) * py) / float(something) x = x1 + u * px y = y1 + u * py dx = x - x3 dy = y - y3 dist = math.sqrt(dx*dx + dy*dy) return dist class MovableSliceBox(object): def __init__(self, box, callback): self.box = box self.press = None self.background = None self.point_counter = 0 self.callback = callback self.mode = 0 self.show_poly = False self.cidpress = self.box.figure.canvas.mpl_connect('draw_event', self.draw_slicer) def connect(self): self.cidpress = self.box.figure.canvas.mpl_connect('key_press_event', self.key_press) self.cidpress = self.box.figure.canvas.mpl_connect('button_press_event', self.on_press) self.cidmotion = self.box.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) def draw_slicer(self, event): axes = self.box.axes canvas = self.box.figure.canvas self.box.axes.draw_artist(self.box) if self.show_poly: path = Path(zip(self.box.x, self.box.y)) path.width = self.box.width patches = path.to_patches(1, ec='green', fc='none', transform=self.box.axes.transData, clip_on=True, clip_box=self.box.axes.bbox) for patch in patches: self.box.axes.draw_artist(patch) def on_press(self, event): if self.box.figure.canvas.toolbar.mode != '': return if event.inaxes != self.box.axes: return if self.mode == 1: self.callback(self.box) self.mode += 1 return if self.mode == 2: self.box.x = [] self.box.y = [] self.mode = 0 self.point_counter = 0 self.press = event.xdata, event.ydata self.point_counter += 1 axes = self.box.axes canvas = self.box.figure.canvas if self.point_counter == 1: # first point self.box.x.append(event.xdata) self.box.x.append(event.xdata) self.box.y.append(event.ydata) self.box.y.append(event.ydata) self.box.width = 0. self.box.set_animated(True) canvas.draw() self.background = canvas.copy_from_bbox(self.box.axes.bbox) elif self.mode == 0: self.box.x.append(event.xdata) self.box.y.append(event.ydata) self.box._update_segments() # now redraw just the lineangle axes.draw_artist(self.box) def key_press(self, event): if self.box.figure.canvas.toolbar.mode != '': return if event.key == 'enter' and self.mode == 0: self.mode += 1 self.box.x = self.box.x[:-1] self.box.y = self.box.y[:-1] if event.key == 'y' and self.mode == 2: self.show_poly = not self.show_poly self.draw_slicer(event) self.box.figure.canvas.draw() def on_motion(self, event): if self.box.figure.canvas.toolbar.mode != '': return if self.point_counter == 0: return if self.mode == 2: return canvas = self.box.figure.canvas axes = self.box.axes canvas.restore_region(self.background) if event.inaxes != axes: return if self.mode == 0: self.box.x[-1] = event.xdata self.box.y[-1] = event.ydata elif self.mode == 1: self.box.width = distance(self.box.x[-2], self.box.y[-2], self.box.x[-1], self.box.y[-1], event.xdata, event.ydata) * 2 self.box._update_segments() # redraw just the current lineangle axes.draw_artist(self.box) canvas.blit() def disconnect(self): self.box.figure.canvas.mpl_disconnect(self.cidpress) self.box.figure.canvas.mpl_disconnect(self.cidmotion) class SliceCurve(LineCollection): def __init__(self, x=[], y=[], width=None, **kwargs): super(SliceCurve, self).__init__([], **kwargs) self.x = x self.y = y self.width = width self._update_segments() def _update_segments(self): if not self.x or self.width is None or len(self.x) < 2: return # Find central line line = zip(self.x, self.y) if self.width: x1, y1, x2, y2 = get_endpoints(self.x, self.y, self.width) # Find bounding rectangle rect = zip(np.hstack([x1,x2[::-1], x1[0]]), np.hstack([y1,y2[::-1], y1[0]])) self.set_segments([list(line), list(rect)]) self.set_linestyles(['solid', 'dashed']) self.set_linewidths([2, 1]) else: self.set_segments([list(line)]) self.set_linestyles(['solid']) self.set_linewidths([2,]) def unitless(x): if hasattr(x, 'unit'): return x.value else: return x class PVSlicer(object): def __init__(self, filename_or_cube, backend=None, clim=None, cmap=None): try: from spectral_cube import SpectralCube if isinstance(filename_or_cube, SpectralCube): cube = filename_or_cube else: cube = SpectralCube.read(filename_or_cube, format='fits') self.cube = cube self.array = self.cube self.shape = cube.shape except ImportError: warnings.warn("spectral_cube package is not available - using astropy.io.fits directly") from astropy.io import fits self.array = fits.getdata(filename_or_cube) self.shape = array.shape self.ok_mask = np.isfinite(self.array) if self.array.ndim != 3: raise ValueError("dataset does not have 3 dimensions (install the spectral_cube package to avoid this error)") import matplotlib as mpl # We reset the rc parameters to the default values to make sure we use # the default interactive backend and to make sure that the UI is # consistent. mpl.rcdefaults() if backend is not None: mpl.use(backend) import matplotlib.pyplot as plt self.fig = plt.figure(figsize=(8, 5)) self.backend = mpl.get_backend() print("Using Matplotlib backend: {0}".format(self.backend)) self.cmap = cmap self.ax1 = self.fig.add_axes([0.1, 0.1, 0.4, 0.7], aspect='equal', adjustable='datalim') if clim is None: warnings.warn("clim not defined and will be determined from the data") # To work with large arrays, sub-sample the data # (but don't do it for small arrays) n1 = int(np.round(max(self.shape[0] / 10, 1))) n2 = int(np.round(max(self.shape[1] / 10, 1))) n3 = int(np.round(max(self.shape[2] / 10, 1))) if hasattr(self,'cube'): sub_cube = self.cube[::n1,::n2,::n3] cmin = sub_cube.min().value cmax = sub_cube.max().value else: sub_array = self.array[::n1,::n2,::n3] sub_mask = self.ok_mask[::n1,::n2,::n3] cmin = sub_array[sub_mask].min() cmax = sub_array[sub_mask].max() crange = cmax - cmin self._clim = (cmin - crange, cmax + crange) else: self._clim = clim self.slice = int(round(self.shape[0] / 2.)) from matplotlib.widgets import Slider self.slice_slider_ax = self.fig.add_axes([0.1, 0.95, 0.4, 0.03]) self.slice_slider_ax.set_xticklabels("") self.slice_slider_ax.set_yticklabels("") self.slice_slider = Slider(self.slice_slider_ax, "3-d slice", 0, self.shape[0]-1, valinit=self.slice, valfmt="%i") self.slice_slider.on_changed(self.update_slice) self.slice_slider.drawon = False self.image = self.ax1.imshow(unitless(self.array[self.slice, :,:]), origin='lower', interpolation='nearest', vmin=self._clim[0], vmax=self._clim[1], cmap=self.cmap) self.vmin_slider_ax = self.fig.add_axes([0.1, 0.90, 0.4, 0.03]) self.vmin_slider_ax.set_xticklabels("") self.vmin_slider_ax.set_yticklabels("") self.vmin_slider = Slider(self.vmin_slider_ax, "vmin", self._clim[0], self._clim[1], valinit=self._clim[0]) self.vmin_slider.on_changed(self.update_vmin) self.vmin_slider.drawon = False self.vmax_slider_ax = self.fig.add_axes([0.1, 0.85, 0.4, 0.03]) self.vmax_slider_ax.set_xticklabels("") self.vmax_slider_ax.set_yticklabels("") self.vmax_slider = Slider(self.vmax_slider_ax, "vmax", self._clim[0], self._clim[1], valinit=self._clim[1]) self.vmax_slider.on_changed(self.update_vmax) self.vmax_slider.drawon = False self.grid1 = None self.grid2 = None self.grid3 = None self.ax2 = self.fig.add_axes([0.55, 0.1, 0.4, 0.7]) # Add slicing box self.box = SliceCurve(colors=(0.8, 0.0, 0.0)) self.ax1.add_collection(self.box) self.movable = MovableSliceBox(self.box, callback=self.update_pv_slice) self.movable.connect() # Add save button from matplotlib.widgets import Button self.save_button_ax = self.fig.add_axes([0.65, 0.90, 0.20, 0.05]) self.save_button = Button(self.save_button_ax, 'Save slice to FITS') self.save_button.on_clicked(self.save_fits) self.file_status_text = self.fig.text(0.75, 0.875, "", ha='center', va='center') self.set_file_status(None) self.set_file_status(None) self.pv_slice = None self.cidpress = self.fig.canvas.mpl_connect('button_press_event', self.click) def set_file_status(self, status, filename=None): if status == 'instructions': self.file_status_text.set_text('Please enter filename in terminal') self.file_status_text.set_color('red') elif status == 'saved': self.file_status_text.set_text('File successfully saved to {0}'.format(filename)) self.file_status_text.set_color('green') else: self.file_status_text.set_text('') self.file_status_text.set_color('black') self.fig.canvas.draw() def click(self, event): if event.inaxes != self.ax2: return self.slice_slider.set_val(event.ydata) def save_fits(self, *args, **kwargs): if self.pv_slice is None: return # When using Qt, we need to use a proper Qt save dialog since input() # does not play nicely with the Qt event loop. if self.backend.lower().startswith('qt'): from qtpy.compat import getsavefilename plot_name, _ = getsavefilename() if not plot_name: return else: self.set_file_status('instructions') print("Enter filename: ", end='') plot_name = str(input()) from astropy.io import fits self.pv_slice.writeto(plot_name, overwrite=True) print("Saved file to: ", plot_name) self.set_file_status('saved', filename=plot_name) def update_pv_slice(self, box): path = Path(zip(box.x, box.y)) path.width = box.width self.pv_slice = extract_pv_slice(self.array, path) self.ax2.cla() self.ax2.imshow(self.pv_slice.data, origin='lower', aspect='auto', interpolation='nearest', cmap=self.cmap) self.fig.canvas.draw() def show(self, block=True): import matplotlib.pyplot as plt plt.show(block=block) def update_slice(self, pos=None): if self.array.ndim == 2: self.image.set_array(unitless(self.array)) else: self.slice = int(round(pos)) self.image.set_array(unitless(self.array[self.slice, :, :])) self.fig.canvas.draw() def update_vmin(self, vmin): if vmin > self._clim[1]: self._clim = (self._clim[1], self._clim[1]) else: self._clim = (vmin, self._clim[1]) self.image.set_clim(*self._clim) self.fig.canvas.draw() def update_vmax(self, vmax): if vmax < self._clim[0]: self._clim = (self._clim[0], self._clim[0]) else: self._clim = (self._clim[0], vmax) self.image.set_clim(*self._clim) self.fig.canvas.draw() def close(self): import matplotlib.pyplot as plt plt.close(self.fig) glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/LICENSE0000644000175000017500000000272513503376220022557 0ustar noahfxnoahfxCopyright (c) 2014, pvextractor developers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/__init__.py0000644000175000017500000000035513503376220023660 0ustar noahfxnoahfx# Licensed under a 3-clause BSD style license - see LICENSE.rst from . import utils from .pvextractor import extract_pv_slice from .utils.wcs_slicing import slice_wcs from .geometry import Path from .pvregions import paths_from_regfile glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/0000755000175000017500000000000013752535025023405 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/tests/0000755000175000017500000000000013752535025024547 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/tests/__init__.py0000644000175000017500000000000013503376220026640 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/path.py0000644000175000017500000001773213557050406024723 0ustar noahfxnoahfxfrom __future__ import print_function import sys import numpy as np from astropy.wcs import WCSSUB_CELESTIAL from astropy.wcs.utils import wcs_to_celestial_frame from astropy.coordinates import BaseCoordinateFrame from ..utils.wcs_utils import get_spatial_scale class Polygon(object): def __init__(self, x, y): self.x = x self.y = y def segment_angles(x, y): dx = np.diff(x) dy = np.diff(y) d = np.hypot(dx, dy) cos_theta = (-dx[:-1] * dx[1:] - dy[:-1] * dy[1:]) / (d[:-1] * d[1:]) cos_theta = np.clip(cos_theta, -1., 1.) sin_theta = (-dx[:-1] * dy[1:] + dy[:-1] * dx[1:]) / (d[:-1] * d[1:]) sin_theta = np.clip(sin_theta, -1., 1.) theta = np.arctan2(sin_theta, cos_theta) theta[0] = np.pi theta[-1] = np.pi return theta def get_endpoints(x, y, width): # Pad with same values at ends, to find slope of perpendicular end # lines. try: xp = np.pad(x, 1, mode='edge') yp = np.pad(y, 1, mode='edge') except AttributeError: # Numpy < 1.7 xp = np.hstack([x[0], x, x[-1]]) yp = np.hstack([y[0], y, y[-1]]) dx = np.diff(xp) dy = np.diff(yp) alpha = segment_angles(xp, yp) / 2. beta = np.arctan2(dy, dx)[:-1] beta[0] = beta[1] gamma = -(np.pi - alpha - beta) dx = np.cos(gamma) dy = np.sin(gamma) angles = segment_angles(xp, yp) / 2. # Find points offset from main curve, on bisecting lines x1 = x - dx * width * 0.5 / np.sin(angles) x2 = x + dx * width * 0.5 / np.sin(angles) y1 = y - dy * width * 0.5 / np.sin(angles) y2 = y + dy * width * 0.5 / np.sin(angles) return x1, y1, x2, y2 class Path(object): """ A curved path that may have a non-zero width and is used to extract slices from cubes. Parameters ---------- xy_or_coords : list or Astropy coordinates The points defining the path. This can be passed as a list of (x, y) tuples, which is interpreted as being pixel positions, or it can be an Astropy coordinate object containing an array of 2 or more coordinates. width : None or float or :class:`~astropy.units.Quantity` The width of the path. If ``coords`` is passed as a list of pixel positions, the width should be given (if passed) as a floating-point value in pixels. If ``coords`` is a coordinate object, the width should be passed as a :class:`~astropy.units.Quantity` instance with units of angle. If None, interpolation is used at the position of the path. """ def __init__(self, xy_or_coords, width=None): if isinstance(xy_or_coords, list): self._xy = xy_or_coords self._coords = None elif sys.version_info[0] > 2 and isinstance(xy_or_coords, zip): self._xy = list(xy_or_coords) self._coords = None else: self._xy = None self._coords = xy_or_coords self.width = width def add_point(self, xy_or_coord): """ Add a point to the path Parameters ---------- xy_or_coord : tuple or Astropy coordinate A tuple (x, y) containing the coordinates of the point to add (if the path is defined in pixel space), or an Astropy coordinate object (if it is defined in world coordinates). """ if self._xy is not None: if isinstance(xy_or_coord, tuple): self._xy.append(xy_or_coord) else: raise TypeError("Path is defined as a list of pixel " "coordinates, so `xy_or_coord` should be " "a tuple of `(x,y)` pixel coordinates.") else: if isinstance(xy_or_coord, BaseCoordinateFrame): raise NotImplementedError("Cannot yet append world coordinates to path") else: raise TypeError("Path is defined in world coordinates, " "so `xy_or_coord` should be an Astropy " "coordinate object.") def get_xy(self, wcs=None): """ Return the pixel coordinates of the path. If the path is defined in world coordinates, the appropriate WCS transformation should be passed. Parameters ---------- wcs : :class:`~astropy.wcs.WCS` The WCS transformation to assume in order to transform the path to pixel coordinates. """ if self._xy is not None: return self._xy else: if wcs is None: raise ValueError("`wcs` is needed in order to compute " "the pixel coordinates") else: # Extract the celestial component of the WCS wcs_sky = wcs.sub([WCSSUB_CELESTIAL]) # Find the astropy name for the coordinates celestial_system = wcs_to_celestial_frame(wcs_sky) world_coords = self._coords.transform_to(celestial_system) xw, yw = world_coords.spherical.lon.degree, world_coords.spherical.lat.degree return list(zip(*wcs_sky.wcs_world2pix(xw, yw, 0))) def sample_points_edges(self, spacing, wcs=None): x, y = zip(*self.get_xy(wcs=wcs)) # Find the distance interval between all pairs of points dx = np.diff(x) dy = np.diff(y) dd = np.hypot(dx, dy) # Find the total displacement along the broken curve d = np.hstack([0., np.cumsum(dd)]) # Figure out the number of points to sample, and stop short of the # last point. n_points = int(np.floor(d[-1] / spacing)) if n_points == 0: raise ValueError("Path is shorter than spacing") d_sampled = np.linspace(0., n_points * spacing, n_points + 1) x_sampled = np.interp(d_sampled, d, x) y_sampled = np.interp(d_sampled, d, y) return d_sampled, x_sampled, y_sampled def sample_points(self, spacing, wcs=None): d_sampled, x_sampled, y_sampled = self.sample_points_edges(spacing, wcs=wcs) x_sampled = 0.5 * (x_sampled[:-1] + x_sampled[1:]) y_sampled = 0.5 * (y_sampled[:-1] + y_sampled[1:]) return x_sampled, y_sampled def sample_polygons(self, spacing, wcs=None): x, y = zip(*self.get_xy(wcs=wcs)) d_sampled, x_sampled, y_sampled = self.sample_points_edges(spacing, wcs=wcs) # Find the distance interval between all pairs of points dx = np.diff(x) dy = np.diff(y) dd = np.hypot(dx, dy) # Normalize to find unit vectors dx = dx / dd dy = dy / dd # Find the total displacement along the broken curve d = np.hstack([0., np.cumsum(dd)]) interval = np.searchsorted(d, d_sampled) - 1 interval[0] = 0 dx = dx[interval] dy = dy[interval] polygons = [] x_beg = x_sampled x_end = x_sampled + dx * spacing y_beg = y_sampled y_end = y_sampled + dy * spacing if hasattr(self.width, 'unit'): scale = get_spatial_scale(wcs) width = (self.width / scale).decompose().value else: width = self.width x1 = x_beg - dy * width * 0.5 y1 = y_beg + dx * width * 0.5 x2 = x_end - dy * width * 0.5 y2 = y_end + dx * width * 0.5 x3 = x_end + dy * width * 0.5 y3 = y_end - dx * width * 0.5 x4 = x_beg + dy * width * 0.5 y4 = y_beg - dx * width * 0.5 for i in range(len(x_sampled) - 1): p = Polygon([x1[i], x2[i], x3[i], x4[i]], [y1[i], y2[i], y3[i], y4[i]]) polygons.append(p) return polygons def to_patches(self, spacing, wcs=None, **kwargs): from matplotlib.patches import Polygon as MPLPolygon patches = [] for poly in self.sample_polygons(spacing, wcs=wcs): patches.append(MPLPolygon(list(zip(poly.x, poly.y)), **kwargs)) return patches glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/poly_slices.py0000644000175000017500000000374213557050406026310 0ustar noahfxnoahfxfrom __future__ import print_function import numpy as np from astropy.utils.console import ProgressBar from .polygon import square_polygon_overlap_area def extract_poly_slice(cube, polygons, return_area=False): """ Extract the values of polygonal chunks from a data cube Parameters ---------- cube : np.ndarray polygons : return_area : bool If set, return the area of each polygon and the sum over that area. Otherwise, return the mean. """ nx = len(polygons) nz = cube.shape[0] total_slice = np.zeros((nz, nx)) total_area = np.zeros((nz, nx)) p = ProgressBar(len(polygons)) for i, polygon in enumerate(polygons): p.update() # Find bounding box bbxmin = int(round(np.min(polygon.x))-1) bbxmax = int(round(np.max(polygon.x))+2) bbymin = int(round(np.min(polygon.y))-1) bbymax = int(round(np.max(polygon.y))+2) # Clip to cube box bbxmin = max(bbxmin, 0) bbxmax = min(bbxmax, cube.shape[2]) bbymin = max(bbymin, 0) bbymax = min(bbymax, cube.shape[1]) # Loop through pixels that might overlap for xmin in np.arange(bbxmin, bbxmax): for ymin in np.arange(bbymin, bbymax): area = square_polygon_overlap_area(xmin-0.5, xmin+0.5, ymin-0.5, ymin+0.5, polygon.x, polygon.y) if area > 0: dataslice = cube[:, ymin, xmin] good_values = np.isfinite(dataslice) if np.any(good_values): total_slice[good_values, i] += dataslice[good_values] * area total_area[good_values, i] += area total_slice[total_area == 0.] = np.nan if return_area: return total_slice, total_area total_slice[total_area > 0.] /= total_area[total_area > 0.] print("") return total_slice glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/__init__.py0000644000175000017500000000013513557050406025513 0ustar noahfxnoahfxfrom .slices import extract_slice from .path import Path from .helpers import PathFromCenter glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/helpers.py0000644000175000017500000000775713557050406025437 0ustar noahfxnoahfximport numpy as np from astropy import units as u from astropy.coordinates import (SkyCoord, BaseCoordinateFrame, UnitSphericalRepresentation, CartesianRepresentation) from .path import Path class PathFromCenter(Path): """ A simple path defined by a center, length, and position angle. Parameters ---------- center : `~astropy.coordinates.SkyCoord` The center of the path length : `~astropy.units.Quantity` The length of the path in angular units angle : `~astropy.units.Quantity` The position angle of the path, counter-clockwise sample : int How many points to sample along the path. By default, this is 2 (the two end points. For small fields of view, this will be a good approximation to the path, but for larger fields of view, where spherical distortions become important, this should be increased to provide a smooth path. width : None or float or :class:`~astropy.units.Quantity` The width of the path. If ``coords`` is passed as a list of pixel positions, the width should be given (if passed) as a floating-point value in pixels. If ``coords`` is a coordinate object, the width should be passed as a :class:`~astropy.units.Quantity` instance with units of angle. If None, interpolation is used at the position of the path. Notes ----- The orientation of the final path will be such that for a position angle of zero, the path goes from South to North. For a position angle of 90 degrees, the path will go from West to East. """ def __init__(self, center, length=None, angle=None, sample=2, width=None): # Check input types if not isinstance(center, (SkyCoord, BaseCoordinateFrame)): raise TypeError("The central position should be given as a SkyCoord object") if not isinstance(length, u.Quantity) or not length.unit.is_equivalent(u.deg): raise TypeError("The length should be given as an angular Quantity") if not isinstance(angle, u.Quantity) or not angle.unit.is_equivalent(u.deg): raise TypeError("The angle should be given as an angular Quantity") # We set up the path by adding and removing half the length along the # declination axis, then rotate the resulting two points around the # center. # Convert the central position to cartesian coordinates c1, c2, c3 = center.cartesian.xyz.value # Find the end points of the path clon, clat = center.spherical.lon, center.spherical.lat try: plat = clat + np.linspace(-length * 0.5, length * 0.5, sample) except ValueError: # Numpy 1.10+ plat = clat + np.linspace(-length.value * 0.5, length.value * 0.5, sample) * length.unit x, y, z = UnitSphericalRepresentation(clon, plat).to_cartesian().xyz.value # Rotate around central point # Because longitude increases to the left, we have to take -angle angle = -angle # We rotate (x,y,z) around (c1,c2,c3) by making use of the following # equations: # # x' = x cos a + (1 - cos a)(c1c1x + c1c2y + c1c3z) + (c2z - c3y)sin a # y' = y cos a + (1 - cos a)(c2c1x + c2c2y + c2c3z) + (c3x - c1z)sin a # z' = z cos a + (1 - cos a)(c3c1x + c3c2y + c3c3z) + (c1y - c2x)sin a # # Source: https://www.uwgb.edu/dutchs/MATHALGO/sphere0.htm cosa = np.cos(angle) sina = np.sin(angle) xd = x * cosa + (1 - cosa) * (c1*c1*x + c1*c2*y + c1*c3*z) + (c2 * z - c3 * y) * sina yd = y * cosa + (1 - cosa) * (c2*c1*x + c2*c2*y + c2*c3*z) + (c3 * x - c1 * z) * sina zd = z * cosa + (1 - cosa) * (c3*c1*x + c3*c2*y + c3*c3*z) + (c1 * y - c2 * x) * sina # Construct representations points = center.realize_frame(CartesianRepresentation(x=xd, y=yd, z=zd)) super(PathFromCenter, self).__init__(points, width=width) glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/polygon.py0000644000175000017500000000166613503376220025451 0ustar noahfxnoahfx""" This module implements polygon-square intersection using matplotlib. It is twice as fast as Shapely for this specific case and avoids requiring another dependency. """ import numpy as np from matplotlib.path import Path from matplotlib.transforms import Bbox def square_polygon_intersection(xmin, xmax, ymin, ymax, x, y): poly = Path(list(zip(x, y))) box = Bbox([[xmin, ymin], [xmax, ymax]]) try: clipped_poly = poly.clip_to_bbox(box) except ValueError: return [], [] else: return clipped_poly.vertices[:, 0], clipped_poly.vertices[:, 1] def polygon_area(x, y): x1 = x x2 = np.roll(x, -1) y1 = y y2 = np.roll(y, -1) return abs(0.5 * np.sum(x1 * y2 - x2 * y1)) def square_polygon_overlap_area(xmin, xmax, ymin, ymax, x, y): x, y = square_polygon_intersection(xmin, xmax, ymin, ymax, x, y) if len(x) == 0: return 0. else: return polygon_area(x, y) glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/slices.py0000644000175000017500000000247513503376220025243 0ustar noahfxnoahfximport numpy as np from .line_slices import extract_line_slice from .poly_slices import extract_poly_slice def extract_slice(cube, path, spacing=1.0, order=3, respect_nan=True, wcs=None): """ Given an array with shape (z, y, x), extract a (z, n) slice from a path with ``n`` segments. All units are in *pixels* .. note:: If there are NaNs in the cube, they will be treated as zeros when using spline interpolation. Parameters ---------- path : `Path` The path along which to define the slice spacing : float The position resolution in the final slice order : int, optional Spline interpolation order when using line paths. Does not have any effect for polygon paths. respect_nan : bool, optional If set to `False`, NaN values are changed to zero before computing the slices. Returns ------- slice : `numpy.ndarray` The slice """ if not respect_nan: cube = np.nan_to_num(cube) if path.width is None: x, y = path.sample_points(spacing=spacing, wcs=wcs) slice = extract_line_slice(cube, x, y, order=order) else: polygons = path.sample_polygons(spacing=spacing, wcs=wcs) slice = extract_poly_slice(cube, polygons) return slice glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/geometry/line_slices.py0000644000175000017500000000404613605360531026247 0ustar noahfxnoahfxfrom __future__ import print_function import numpy as np from scipy.ndimage import map_coordinates def extract_line_slice(cube, x, y, order=3, respect_nan=True): """ Given an array with shape (z, y, x), extract a (z, n) slice by interpolating at n (x, y) points. All units are in *pixels*. .. note:: If there are NaNs in the cube, they will be treated as zeros when using spline interpolation. Parameters ---------- cube : `~numpy.ndarray` The data cube to extract the slice from curve : list or tuple A list or tuple of (x, y) pairs, with minimum length 2 order : int, optional Spline interpolation order. Set to ``0`` for nearest-neighbor interpolation. Returns ------- slice : `numpy.ndarray` The (z, d) slice """ if order == 0: total_slice = np.zeros([cube.shape[0], len(x)]) + np.nan x = np.round(x) y = np.round(y) ok = (x >= 0) & (y >= 0) & (x < cube.shape[2]) & (y < cube.shape[1]) total_slice[:,ok] = cube[:, y[ok].astype(int), x[ok].astype(int)] elif order > 0 and order == int(order): nx = len(x) nz = cube.shape[0] zi = np.outer(np.arange(nz, dtype=int), np.ones(nx)) xi = np.outer(np.ones(nz), x) yi = np.outer(np.ones(nz), y) if np.any(np.isnan(cube)): # map_coordinates does not deal well with NaN values so we have # to remove the NaN values then re-mask the final slice. total_slice = map_coordinates(np.nan_to_num(cube), [zi,yi,xi], order=order, cval=np.nan) slice_bad = map_coordinates(np.nan_to_num(np.isnan(cube).astype(int)), [zi,yi,xi], order=order) total_slice[np.nonzero(slice_bad)] = np.nan else: total_slice = map_coordinates(cube, [zi,yi,xi], order=order, cval=np.nan) else: raise TypeError("order should be a positive integer") return total_slice glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/pvextractor.py0000644000175000017500000001050113605357235024504 0ustar noahfxnoahfxfrom __future__ import print_function import numpy as np import six import warnings from astropy import units as u from astropy.io.fits import PrimaryHDU, ImageHDU, Header from .utils.wcs_utils import get_spatial_scale, sanitize_wcs from .geometry import extract_slice from .geometry import path as paths from .utils.wcs_slicing import slice_wcs def extract_pv_slice(cube, path, wcs=None, spacing=1.0, order=3, respect_nan=True): """ Given a position-position-velocity cube with dimensions (nv, ny, nx), and a path, extract a position-velocity slice. Alternative implementations: gipsy::sliceview karma::kpvslice casaviewer::slice Parameters ---------- cube : :class:`~numpy.ndarray` or :class:`~spectral_cube.SpectralCube` or str or HDU The cube to extract a slice from. If this is a plain :class:`~numpy.ndarray` instance, the WCS information can optionally be specified with the ``wcs`` parameter. If a string, it should be the name of a file containing a spectral cube. path : `Path` or list of 2-tuples The path along which to define the position-velocity slice. The path can contain coordinates defined in pixel or world coordinates. wcs : :class:`~astropy.wcs.WCS`, optional The WCS information to use for the cube. This should only be specified if the ``cube`` parameter is a plain :class:`~numpy.ndarray` instance. spacing : float The position resolution in the final position-velocity slice. This can be given in pixel coordinates or as a :class:`~astropy.units.Quantity` instance with angle units. order : int, optional Spline interpolation order when using paths with zero width. Does not have any effect for paths with a non-zero width. respect_nan : bool, optional If set to `False`, NaN values are changed to zero before computing the slices. If set to `True`, in the case of line paths a second computation is performed to ignore the NaN value while interpolating, and set the output values of NaNs to NaN. Returns ------- slice : `PrimaryHDU` The position-velocity slice, as a FITS HDU object """ if isinstance(cube, (str, ImageHDU, PrimaryHDU)): try: from spectral_cube import SpectralCube cube = SpectralCube.read(cube) except ImportError: raise ImportError("spectral_cube package required for working " "with fits data. Install spectral_cube or " "use NumPy arrays") if _is_spectral_cube(cube): wcs = cube.wcs # The fits HEADER will preserve the UNIT, but pvextractor does not care # what the flux units are cube = cube.filled_data[...].value if wcs is not None: wcs = sanitize_wcs(wcs) if not isinstance(cube, np.ndarray) or wcs is not None: scale = get_spatial_scale(wcs) if isinstance(spacing, u.Quantity): pixel_spacing = (spacing / scale).decompose() world_spacing = spacing else: pixel_spacing = spacing world_spacing = spacing * scale else: if isinstance(spacing, u.Quantity): raise TypeError("No WCS has been specified, so spacing should be given in pixels") else: pixel_spacing = spacing world_spacing = None # Allow path to be passed in as list of 2-tuples if not isinstance(path, paths.Path): path = paths.Path(path) pv_slice = extract_slice(cube, path, wcs=wcs, spacing=pixel_spacing, order=order, respect_nan=respect_nan) # Generate output header if wcs is None: header = Header() else: header = slice_wcs(wcs, spatial_scale=world_spacing).to_header() # TODO: write path to BinTableHDU return PrimaryHDU(data=pv_slice, header=header) def _is_spectral_cube(obj): try: from spectral_cube.spectral_cube import BaseSpectralCube return isinstance(obj, BaseSpectralCube) except ImportError: if 'SpectralCube' in str(obj.__class__): warnings.warn("Object appears to be a SpectralCube, but" " the spectral_cube module could not be loaded") return False glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/pvregions.py0000644000175000017500000001220613557050406024137 0ustar noahfxnoahfximport numpy as np from .geometry import path from astropy import coordinates from astropy import units as u import re csystems = {'galactic':coordinates.Galactic, 'fk5':coordinates.FK5, 'fk4':coordinates.FK4, 'icrs':coordinates.ICRS} cel_systems = ['fk5','fk4','icrs'] # ecliptic, detector, etc. not supported (because I don't know what they mean) # (or with ecliptic, how to deal with them) all_systems = cel_systems+['galactic','image','physical'] class SimpleRegion(object): def __init__(self, coord_list, coord_format, name): self.name = name self.coord_format = coord_format self.coord_list = coord_list def __repr__(self): return "Region: {0}, {1}, {2}".format(self.name, self.coord_list, self.coord_format) valid_regions = ['line', 'segment', 'vector'] valid_region_re = [re.compile("^"+n) for n in valid_regions] def simple_region_parser(regionstring, coord_format): rs = regionstring.lstrip("# ") rtype = None for rt, rre in zip(valid_regions, valid_region_re): if rre.search(rs): rtype = rt break if rtype is None: # not a usable region return coordre = re.compile("^[a-z]*\((.*)\)") coord_list = coordre.findall(rs) if len(coord_list) != 1: raise ValueError("Invalid region") coords = coord_list[0].split(",") outcoords = [] for ii,cs in enumerate(coords): if coord_format in csystems: if ":" in cs: # sexagesimal if coord_format in cel_systems and ii % 2 == 0: # odd, celestial = RA = hours crd = coordinates.Angle(cs, unit=u.hour) else: crd = coordinates.Angle(cs, unit=u.deg) else: try: # if it's a float, it's in degrees crd = float(cs) * u.deg except ValueError: crd = coordinates.Angle(cs) else: # assume pixel units crd = float(cs) outcoords.append(crd) reg = SimpleRegion(coord_list=outcoords, coord_format=coord_format, name=rtype) return reg def load_regions_file(rfile): with open(rfile,'r') as fh: lines = fh.readlines() return load_regions_stringlist(lines) def load_regions_stringlist(lines): coord_format = None for line in lines: if line.strip() in all_systems: coord_format = line.strip() break if coord_format is None: raise ValueError("No valid coordinate format found.") regions_ = [simple_region_parser(line, coord_format) for line in lines] regions = [r for r in regions_ if r is not None] return regions def line_to_path(region): """ Convert a line or segment to a path """ l,b = None,None endpoints = [] for x in region.coord_list: if l is None: if hasattr(x,'unit'): l = x.to(u.deg).value else: l = x else: if hasattr(x,'unit'): b = x.to(u.deg).value else: b = x if l is not None and b is not None: if hasattr(b,'unit') or hasattr(l,'unit'): raise TypeError("Can't work with a list of quantities") endpoints.append((l,b)) l,b = None,None else: raise ValueError("unmatched l,b") lbarr = np.array(endpoints) C = csystems[region.coord_format](lbarr[:,0]*u.deg, lbarr[:,1]*u.deg) # TODO: add widths for projection p = path.Path(C) return p def vector_to_path(vector_region): """ Convert a vector region to a path # vector(48.944348,-0.36432694,485.647",124.082) vector=1 """ x,y = vector_region.coord_list[:2] length = vector_region.coord_list[2] angle = vector_region.coord_list[3] C1 = csystems[vector_region.coord_format](x, y) dx,dy = length * np.cos(angle), length * np.sin(angle) # -dx because we're in the flippy coordsys C2 = csystems[vector_region.coord_format](C1.spherical.lon - dx, C1.spherical.lat + dy) C = csystems[vector_region.coord_format]([C1.spherical.lon.deg,C2.spherical.lon.deg]*u.deg, [C1.spherical.lat.deg,C2.spherical.lat.deg]*u.deg) p = path.Path(C) return p region_converters = {'line':line_to_path, 'segment':line_to_path, 'vector':vector_to_path} def paths_from_regfile(regfile): """ Given a ds9 region file, extract pv diagrams for each: group of points [NOT IMPLEMENTED] panda [NOT IMPLEMENTED] vector [NOT IMPLEMENTED] segment [NOT IMPLEMENTED] group of lines [NOT IMPLEMENTED] """ #import pyregion #regions = pyregion.open(regfile) regions = load_regions_file(regfile) return paths_from_regions(regions) def paths_from_regions(regions): paths = [region_converters[r.name](r) for r in regions if r.name in region_converters] return paths glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/utils/0000755000175000017500000000000013752535025022712 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/pvextractor/utils/__init__.py0000644000175000017500000000000013503376220025003 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/external/pvextractor/utils/wcs_slicing.py0000644000175000017500000000234613557050406025573 0ustar noahfxnoahfximport numpy as np from astropy import units as u from astropy.wcs import WCS, WCSSUB_SPECTRAL from .wcs_utils import get_spectral_scale def slice_wcs(wcs, spatial_scale): """ Slice a WCS header for a spectral cube to a Position-Velocity WCS, with ctype "OFFSET" for the spatial offset direction Parameters ---------- wcs : :class:`~astropy.wcs.WCS` The WCS of the spectral cube. This should already be sanitized and have the spectral axis along the third dimension. spatial_scale: :class:`~astropy.units.Quantity` The spatial scale of the position axis Returns ------- wcs_slice :class:`~astropy.wcs.WCS` The resulting WCS slice """ # Extract spectral slice wcs_slice = wcs.sub([0, WCSSUB_SPECTRAL]) # Set spatial parameters wcs_slice.wcs.crpix[0] = 1. wcs_slice.wcs.cdelt[0] = spatial_scale.to(u.degree).value wcs_slice.wcs.crval[0] = 0. wcs_slice.wcs.ctype[0] = "OFFSET" wcs_slice.wcs.cunit[0] = 'deg' # Not clear why this is needed, but apparently sub with 0 sets pc[1,0] = 1, # which is incorrect try: wcs_slice.wcs.pc[1,0] = wcs_slice.wcs.pc[0,1] = 0 except AttributeError: pass return wcs_slice glueviz-1.0.1+dfsg.orig/glue/external/pvextractor/utils/wcs_utils.py0000644000175000017500000000462713612622074025305 0ustar noahfxnoahfximport numpy as np from astropy import units as u from astropy.wcs import WCSSUB_CELESTIAL, WCSSUB_SPECTRAL def get_spatial_scale(wcs, assert_square=True): # Code adapted from APLpy wcs = wcs.sub([WCSSUB_CELESTIAL]) cdelt = np.matrix(wcs.wcs.get_cdelt()) pc = np.matrix(wcs.wcs.get_pc()) scale = np.array(cdelt * pc) if assert_square: try: np.testing.assert_almost_equal(abs(cdelt[0,0]), abs(cdelt[0,1])) np.testing.assert_almost_equal(abs(pc[0,0]), abs(pc[1,1])) np.testing.assert_almost_equal(abs(scale[0,0]), abs(scale[0,1])) except AssertionError: raise ValueError("Non-square pixels. Please resample data.") return abs(scale[0,0]) * u.Unit(wcs.wcs.cunit[0]) def get_spectral_scale(wcs): # Code adapted from APLpy wcs = wcs.sub([WCSSUB_SPECTRAL]) cdelt = np.matrix(wcs.wcs.get_cdelt()) pc = np.matrix(wcs.wcs.get_pc()) scale = np.array(cdelt * pc) return abs(scale[0,0]) * u.Unit(wcs.wcs.cunit[0]) def sanitize_wcs(mywcs): pc = np.matrix(mywcs.wcs.get_pc()) if (pc[:,2].sum() != pc[2,2] or pc[2,:].sum() != pc[2,2]): raise ValueError("Non-independent 3rd axis.") axtypes = mywcs.get_axis_types() if ((axtypes[0]['coordinate_type'] != 'celestial' or axtypes[1]['coordinate_type'] != 'celestial' or axtypes[2]['coordinate_type'] != 'spectral')): cunit3 = mywcs.wcs.cunit[2] ctype3 = mywcs.wcs.ctype[2] if cunit3 != '': cunit3 = u.Unit(cunit3) if cunit3.is_equivalent(u.m/u.s): mywcs.wcs.ctype[2] = 'VELO' elif cunit3.is_equivalent(u.Hz): mywcs.wcs.ctype[2] = 'FREQ' elif cunit3.is_equivalent(u.m): mywcs.wcs.ctype[2] = 'WAVE' else: raise ValueError("Could not determine type of 3rd axis.") elif ctype3 != '': if 'VELO' in ctype3: mywcs.wcs.ctype[2] = 'VELO' elif 'FELO' in ctype3: mywcs.wcs.ctype[2] = 'VELO-F2V' elif 'FREQ' in ctype3: mywcs.wcs.ctype[2] = 'FREQ' elif 'WAVE' in ctype3: mywcs.wcs.ctype[2] = 'WAVE' else: raise ValueError("Could not determine type of 3rd axis.") else: raise ValueError("Cube axes not in expected orientation: PPV") return mywcs glueviz-1.0.1+dfsg.orig/glue/__init__.py0000644000175000017500000000263013527542473017466 0ustar noahfxnoahfx# Set up configuration variables __all__ = ['custom_viewer', 'qglue', 'test'] import os import sys from pkg_resources import get_distribution, DistributionNotFound try: __version__ = get_distribution('glue-core').version except DistributionNotFound: __version__ = 'undefined' from ._mpl_backend import MatplotlibBackendSetter sys.meta_path.append(MatplotlibBackendSetter()) from glue.viewers.custom.helper import custom_viewer # Load user's configuration file from .config import load_configuration env = load_configuration() from .qglue import qglue from .main import load_plugins # noqa def test(no_optional_skip=False): from pytest import main root = os.path.abspath(os.path.dirname(__file__)) args = [root, '-x'] if no_optional_skip: args.append('--no-optional-skip') return main(args=args) from glue._settings_helpers import load_settings load_settings() # In PyQt 5.5+, PyQt overrides the default exception catching and fatally # crashes the Qt application without printing out any details about the error. # Below we revert the exception hook to the original Python one. Note that we # can't just do sys.excepthook = sys.__excepthook__ otherwise PyQt will detect # the default excepthook is in place and override it. def handle_exception(exc_type, exc_value, exc_traceback): sys.__excepthook__(exc_type, exc_value, exc_traceback) sys.excepthook = handle_exception glueviz-1.0.1+dfsg.orig/glue/logo.png0000644000175000017500000004524613455362716017035 0ustar noahfxnoahfxPNG  IHDRKC4)iCCPICC Profilec``2ptqre``+) rwRR` ``\\yy 5F}Yd<^\PTQJjq2]^Rgd$e@좐 g ͗a_ ' v@t0l-bVep/,L(Q0200PpLOJU,.I-VK/*/J,IM ! A!ahiiI P<@X(v!ɥEePa>Œ9 KX Lz00OE2030ïPo\' pHYsgR IDATxy\T0 .(&hjVXiW[jMVXh^WYR"((30 KBL&|||pqquhB! I(,,dfdINNz 4H%!V#ɒ"INN"%%BCB!JdIDAAǎرcWgq!8IUF'$p1FB!*%ɒӧOw2! !J%|vIjjCB!f7RO>/K=9wyBQKMtqG/q98xݻ֡!f75PÄJ>̾Q4m֌::WWPB!D.YP> ;H =ؿ?48ڿ5>>>4h@B]$jǎ(9;;H$ABaw$Yʞ={#:ts.88BQ}, c(Jő=t*G&BԌ$KEaMoBΝqt4oBam,;s*ɉ^{ӲeK+D%BX$KF._*tv8swѬY3+D%BX5r`~ +ё>J$VdITۅ H:T~zѼysB!T ɒ#P;Һuk+E$BXYfstqӓNQXA899U:BO, 9{y;A|| -!d9MjNq."/\@_zW777ƻ/[Ŀu+[xO$!@vYΜ>DΜdΝ=KJNϟ/㠦4mڐ-ӬUKkZUso,ǰ TJĪvn\VL/gJ*GKX1cѳ> ?,e40[ܮ]nxVDo߹ #(j4ƛp?gW>d/KYeúN;HJ%j:hH o-1 ɒ0G+F#Ԓ=boUO<#:m2 EÔ<-(vtlׄ?>tk._@^^˖.v9<䓖 prv7VHaϚte0|"mΖoϽ5;̣+|"'2on i&ItFV d)Tr˛VZ.u3fD}%oij:wFeY!l,{? fmmz+^mIT3w3|دBD%;!!ݭ8S2aJpgTp_\dx0 qM5}Z}m#f:M~de%Y*ŭA>egeՋd nswwNJ1%}m J7gD-c)ٳ|Z)Qݽ96ÃZtf-w#cJZ(]ÝF&3H3kptCԈNLXc iYfq֔lbPXXx"'G'6퍫k}Zg.e0&JWw3Y[G" S7*|T0 H`H0I$momcHMI}}}}Թ3=z஻& ZgZGb1Sf0(]=>)O$YfѺ,L15z/5Ws*FZZ[laAt܉G$bذ:lE՝`Fi3{=MDzkAXaŒuks}|y})t+Fݻ3g(,,LJqwwǍ?28{$J`fwynxkywQ;^)O!mo}mmh7BT̗,,b,0Z8aqrr*s4TO$rssYt)_YåK%9;;Ӳe 'ά ̚77s,$)YVstM\ip1tCe+{ sqq-oM kpj'? ʽf4oJ5jD;zs-]iߚ&Md"++SNq`v+!~> Zޡ[z-eOeQ¡odYLƍ>.2%m۶,edd0n*Zsppŗ_bq8;WmP(ڵ/VfVK>ug`s{Q Q%f1/Oǹ K^i쉋e*ۤq/P00[:x[˥3kT֢wud-3ڿIQf-?@hh(9sEF0uҶbXpXҬn/sYcbK#[ʰ<5*CYٍY,0hf/foȾ/Y% 1*,,dM5*KQK]:ԩnږϛ7sȑ#պ'qFǶlْ?X/L /^,s,ˋ̮êRy[<g#roN[G!;chI(f[ \gZOB2+t(Sgab)>j&oF% srr"|_#~A9z49mqwX...oOO@7oTd隻X{Y=Z>y5:~VO<#f` Ӌ g¬y Z:h.{sg^L|Gmeu<}U?`\8Ԣ $K=C>Nתdi؃X2;Z?~}^zb;VvRGqZU+HN$&޴^x6{본,jRw/adHM'o8 y!Dg;VڄٚT l0HƩ'z?LP?˥81F5F+_-Vŋ9>6g+n޼iii,^"uY፟XR7X Q*NCסOH *Da8̲)jY{'Jip-P-Q%QmwmL~~{H]M6M4H >>̏*2+Wqtg T.RЈdlx&c>N!]Js~۲QACWVz~n6IDg4)`0q5еk\cҷ__pL&˖.h}5e8m WV;%ށٖ])]Qwʕ1X,IDuҙ?XRĢ˗-cȠysF3={ҧoJ<R|~ܸuVO"б{%WI][`J?~Ru$k٪LXӘf Э8IEѧ>gNgc}:Uz=:v0k:f͚;oWys=@KdK2%Dg0sd͇WggXv_Q!lj#=2D+ǘfX*Y&[Ջ/ɓ&x$%%1>AFmhڴZ+EEErEN>͡CػgU%] ,]Fs YzRf\Fˬ%yF$gWxhP)*7UXD6Zwc\BjoU%aQڱnzymVZU-7EQH8@¡gLО/9yG9j.,ZN;>z(]tQ58C:d:M!MdLZ) SQU[vW3" Fl)&tc$Yƿf❷Xl___ϘA+kdggJDs3Z|}L'8YJaVuE,:k|ËS+ '#ˈ6lV@GAJWőb$A5v$Yҥ |)Yr?o̥ cP=8=P䞞xzڠgUͽݖaHʐn v_{u+"Lj . ’VQ{Z*hӣ/RV2Lp6oI7gǟk.8ȉD233KܪsppI&jՊvt O>jچgP>'Dz0-5hzrr0^ml*]7Qǩ{G =u?p9'ŀKdIX+w}7w} ɡ˝vm֐wu+.~^%d@$lI!٪f_?XYg,Tu֥ɢ%Y6dh5UJIY _54iI-lUmX/n]H:5+, Q)gRְQC+FRZ.q[Rͥk՞0[!vKo ]k>$KBTSaa!'N(w{FʟO"z1aAFc7<|S ːdIjJHH@חI&V&d6o!D)ҨiIvU];@$%Y~rҦm[+FSuk0[B I+e}!498Z-BaO!L%!aڵnjHጺ͵md QIn!ɒfc++ܧV|β$$KB>#7TQFzmV,NfGlLQOH$N:ŲK+gCld TF+$KBT(̙=;5Jْωki&SԝYVA%!he ٳ'۷RDOFnMĵ0I~ ]zMn8_m^!߷hat8 T,=K,ybH9,h QUY1k:Gn5,E%!*o{ g/i\6zɡ6"3ARwK*5;MT܆'˕;WQUij %Ssϊo Q{lBtm;8GI;L%!ۯ2z(.^XcN;[!kдįfw|fG$ǘ[!иWv!Η(E%!J)**b3q?M/[!2G*Q5?.Bu6!d"%9FuWBC` ?{a$Y'N0&r4UG ř7~WWiuo%ߓrUOl,Tl:4YBG+ՒxbJQ:G%! .(rK]tQ1жouN6۰-n6ה|6*ttѱj<&OP7a}9ƅYe[SUZif6rÑhլ$CF& ‚!VnUБXJ,:Y ϦČWg9zEU筌XջD0v.jog2`%4PU[BHWܐg/Նu3< Uz19Vm7$b h+A5?7I͉^MHL:&΀(k౶ar?ڿGU mzGr6̈́~cb-pl-"VC2FsU0>㲚T.}DX06D >՛)w2lVG$Qbga5u=*6Uk(a?/P1Q"{wH$KBLJw-I&t!Ld-72lQ6=6!dWr-:Xxae-:1V5)ݠ `Ӧ1{oMTۖDDZF,n~X}pfZ`CLdQ9/xcAAē{G888:$hyj 'z(9u}ߺ9ލ$F%.egs*016I=iq S*-3ѣ/n)_u[ $IJZU_Q<LzMScE=g˧1c)'j5DM$&j"^_:y IDATyH} ^'\/31?"Y1ղSPX,YrquSJ˫fidM/cbg(bf!}D1`mw4>UjMX xg)V]R|r&\4o\~p.4f*$e!Tn QQVu_(Oؘo b-[YN[0ŋw _x[!j)O<;AoرQء$D$KBG OzSUƙם[?!ɶyN&~< 7"TZR%!)P~ٽaXH<Sg vzwNvef [GQu>[YaZ` TooIk;jPPu;۔Ƌ6SZ/ Wo$ m2hk8|J:ܹd?x}D-oAgld02:}4]FJ 5.;K_'7}ۼe59jélY?_̷:kL4Iup[ ,QJvTƑCY:oX_ptn%Hb[Zyfʉ_5S!^NbF_+H|D(md\]`o+ Vvm8!Dx2a2c ;6hx9c[v'aܷ !^ Exj-+2vƢ6;6;P6"ԿWQ0T,t eƜ$6.aS>`odU/1$KBh< geSd緍kY` X)X92CwV:]؏ 7`I᷵Kf舩 q2W[S_v4RS'||sm-3"c~l}WF^WQ(BG_ >0撛'GxMЍn:t:K|9ZYFu2¥'Ώ~'oe'og_N:7E2$|wՋ5>Ey0FG~o8d+?_ WYYpSG=m&rH$0 Z'Z'֛v^-죥.zOhp^ Cv&)gIM:4e\ zpsrp74YSZϧ=U#97ht^NHἲC28pq{XKً;z&`:߫tW0rTK}*kzzE`PW["ЕYd 77?Ʌl(+..xxxw4jڔ&MikuQcB2^K:\g-:O-:O/ΌB/0?ѢG6.{, !j̽U ׬, QI$LT,?vľn<*K5oU1:{SuC5U$KBeoV2dYƝ՝{Z4Zg0`8'gS{!w }uVm~}95TNU:_5H$Dsw3=n\ɸU$A~*ס&#oQW> ~;pT]hy]ҪO6qrW<W}4j2"9nt> -Ǖkv Pp5d `#yW՘^ E }AW[QX yӟ%wQ ݽp?ģ_ҕ]SK'߸|^х?FoÉ<Ty{'fd"'RVn ?=.ixj^M8?w ™pD8_葻BqAF)Mwg|ͥ8t\1"4۟ʺ.=*:Intp}>O=FU~j-KL<ٵk/^k*G bښWB;~MLy;1H|6wt.Lɞs\jL\f}\n gM/U擝1[.^?pϓ|= W\r2 cfCzP$H_r%ƌrvG:Γ$BڒS42_sfp9q:B֔MRVg6d0l!|/~]L92|*yLN<4~Sxt7^&[z_^B}g|s4=wFgľh3p׮\ ~orpoI_`.={x.s>/%}7çpq;x sל3w]9Me cv6GSyuJ~9ĕ :pH0p2U6Gv)&)j;qұ] U>n1?JخZ xPҿ^+瑣Y[\”߲T8rGʙùM5D R|c󕸀'ld7DJۓ(JrrP=bܯ RG0D9(JNeo@JArμW RdY=J|@7%z`W 諤%=z$jр %;Jz%ʹO*qC,E^)䟔Rd\ RC*QI}.H xR̸QW/g[r/&)q]}ǔsŞ=*q},5)J5ynb*vh}+rVyL%%Wb4&+z)q|yԟA޷w*(i*qC(^bw~xWb}yRRןū/Jϫׯ7ncrlЕכ ϵ9ֲsY !,LD-]56 1sٷ$fִ#;ܐӭ"Vy\Q.0uq+DJK\ H7tHQ&}ݒŜwʽ^`Ө#05|Z_;J[)rÑ|b6}z$rX6p xnVtk;%o}Xi9g. ~27[3iVlg玏z|sp3>ۂ\ Mc<ߚKbϽƧ#'UZt/TXF˕[7sckiM௕᭡cs/Zwl<թ9A0П-m<,p$ɳb 5Jʅp+rVpc0N=:v8W1Ek34oTj\K} ]IT@>PD3rɾ` as0m݇FIyjI%c}S}`i N0Esh{]#}C)jܬjf? ۃhοTfdҾ99$\1͢o9k4Hq]Jυ%v Cf3䔯q6] )v]Y/ʯ@ [ {M;H fǀfr.ji@j>Es=g81z^vd'y[ɋVs(x'mp$|8zCLg/r@Mgt_);%]H6uv/rE<#w-8}%rfK {>#|9hZvƑDN+9s}']4IKVa4ibIGS܀ K7]w#7ute^U"ٹ 2cqU4 -G_~ ]SyFLnm#9V2~8{=#|G\8Y_Zw+qTt+JgÅаWۛ/VUΫׯV͹R/̯qe稪ɒy(z 3egn 'N:ZJC|B\75vjo)e} vk8.?}v69Wrh̕_ſ8p.|7 cw{gfraG}Vv{(^}}_Ƒ/$o8p\h4-ns+Rù#0d"W8w:6+vZ1\L86jW:wkd9ޟN[EG*&Uή@"yiiO W1,R嘮q<WqDRvyD\ $=&nx3\YPpZxZ^: GI>Z:8 }f ǯwNJOeϱ HҐ8!iOp3dܲ:p.wWǞ@^] '}S 9g?_W~w%`;nejM Sէup(nSҸp>M433|vz.EF#n7wWWvb55)#ɺE>bI#b]; ߨyWW5JۘɸA-bѓrx\}g qf-8>m.GWϽh ׭ W-'m1s91S~|EoerDX_*N?Hzc&?chϼéifb.WS]ΛqxµF9Qo0Fq/h7 ` џx5 +:\ZZDoi2hjKkjrIx&[\}s V\8tl^ Nŕ]'N`"hث' {U<Ńί/M#S_̷:tF1xڷoF赏Ώ~KZUҦf<_!XBQ%d/s_SN! |(ct>ci-%4׷ľcN$&^lK5g|j1=ӵpf/N&n[,~N&Ng0{hY?܄vI-1#cFYe.Y2$aJQ_,(2sf.(ڿMϞ=l*q&ZO KcYa.IH8Oܞ?bKpX{A^ӵK3B5S(?T‹v K%양"6'OR+ԺA?(‡`2}6󩤜9Mr8$Rr8wgHk IDAT\dIЖCVhJ5ќӛ5x/Œ%θy;2h`5CBQȤhi0 pts,|}0Q ޠR4 gB!j:Ѳ$#~?31egԨxdeeV+ ! !,ܹs`u(͚U(]MJ$\Ҳx'1 v74zH!,h[ټi#B!$YRQneUJ-`:!6"ɒe0 ̝3ݻsOvZ&F֡ !2$D^ye6?<<F&ڤeIR'$H/IKKrDB!lI%!JIHHpVD!=dIR7pgۅB-,#))x=ϛe}xyyM6t j刄Bؒ$KW}6< Se됄hZ DXblB[p8rFcd<4aDšBw͛HMI??p?:֡ !2&KVѡJ^|iҴ)-ZrYǎG-8]EoQy6i1clBi֩FgEaqVSB!%K.\VU2:Of@]HOKgŊO8t {r00|Bag,uGGGUeqݦ pQFŋ?y&c 3/F&X-Yj,\0l *'-]>|ۘ~\՛9E3g59bB#Y8p c7036 V~nD=rESح[%YBq̳$X !_$YNf ,w{h^VF!dIK/4BuvBa$YvdqU>}Xf5}'m۶eɼ{88TmT!,w"Jjj*Q_b1MtN'[r)2I{w _|)+~LG+n+,} B!iYv#;;>M0QEQ:x^AK1eePʩYsI`BBQI$}FzzMpa\:po/s[%Y4F!$KnrmW1JAjBM%a7T1U.BTF%a7pts+sk:Y$6!$KnD vݺIeܴ mLrh-%.fda7X֛>-[)((gϞ QM[[h#x{[1z!u$K 0u[vJ !B!D$YB!@ w3Si׮U[uɄBQdL{%٠B!jEԠm']6.(((`O?q1y5#| lBQZ,www.]dx)4ZqVzZ:Olj-|,\]wmBJԊ @WIKMլZM۶hJh47cǍ5>qN^"Q|2/N}[ФiE&BTV$K|j۹cEEE^xd k9Ύ?,sۥKؼy>B!FʪpBa>I۴[&q{c* H+h\Cء>h ]X@P/jZ,6:8SZSp~ 7L,peYƋ_{`ɋ`~bح,ˈ܇q`? \V ެ(bTpg'NDӉV{\H,Tk֢ss @XHK 5h^'n{ǣ,;is5kDl{pIuCQӬ˫5\ٌŇ8~Fl=ڊ~SXvr4=X1* @XHK b A,$% @XHK b A,$܌ro9;=~GPk7߽#b|n{@w0уIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/0000755000175000017500000000000013752535025016462 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/icons/glue_zoom_to_rect.png0000644000175000017500000002275513455362716022727 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<%zIDATx} tř_-Ylm/c[mlc 6dYpÒM`7dO6YH f&  r:dH,lK%zK=fF3ݵ]UU=O{zG3j1xմ-oq-zA8=u vہ17̾ڴ'#r1 ,s7xٟ4K3+f϶"d\cULJ`#׉ ƔqcnJS r!;(;{8{hnZʊGs_ULfD͡ʵd_a8 0|`j&'K8νϨ5} .56Bz$ūJ,y>./؋@D-1;>vϜŤ;`[wm=;6qЯo۰ W$H h'؈ s0. T FN {.؄޵|EEtA89D`Dț_ }MgebʹȚ_[~䈚 Q+hlPPkvTݏA!pGMiY]֪5 &MQMF:%ntkߒ~0tԑ+܍mKG*V!nd%M[wHu%0xlmG8!jOOFSVm715 5`z'jE܅%vGոcI$~Iw[F`%dKJxTg׌ `= }ly WIwPnHLI0\ l03}\qT&)˹ tZӧrMwﹴ- L/(#ROy5d VVdb!#jFs|e%PNM/d ̜M{(&rF#'`opKh\8$!<:Km^ܱ<_5cɂ.bK j64n(\u'3In1p5t?3i &N e?ii|N}C}wў.4ՅYYYR 0PF2R| ZnyuC )˟FHD-Pm7,}ͣ0֥O$݄m="/gҝ.K#M>9ht+?DD~\@BUUt&S&𛢀=IaC&k瘯tG EPܰIkܯ &_)EH[{R>}BC=cbG\zj /ƾz*,_;TJ#vm=#j_0^ W۴.-jS'ZAQSv>Й:gswuU8߿GRm}yc2 Oyߟ s' ~ތr1A'Y}o1q<JH'eW2d;,Х %w#~u,)=g,نtiCy矬_3& 2r0䵁 Ժ3J;ZZ:;P9$WJ~2#qxM; y-vJD5"P=vMd95 : wn:ŎwMNctI<B+?O|>[(־69fбuL7 d g>ܞ#~R𓒒Fn>z=K}օZ8yB↕s$ggGKl↍OD~dH@wNڦ] tA-U.qv_ZX]2X̤I6|CZZ}ԩA}")W9֖# /@b%w}JA+B S7dA)aWtJwvخQKE5h^fHҎ1tTxzZ0z\ӎ~ZUwo`nJՔӁ_pu)UNB(YzO=$WM 77R7ݚewJq T"jjgMg@| UOA60n.c8woj_6R5Sf^^^H"j(+B@vk̇*KOؽU:v99;!ʝ<ٯƜ$|xCVt]{߯oٿ/_Vv< !5?#!p'tÞ UПo- so[u[eg߃m9ݣ@2n{PL7sUqHj{Q鷺_{,@ g30I%Pu eaW tB҉[o_YYzf8sGo hDj\9l܆[ | Ps!]qv5ǷJQu/UxnwniĞ}t'̬Kjp0hܰ'/5r@_<᜴GOkP t!s؁m6;d@D`P <_#}ǧ 2B9ZCf9.7d7/G r;"52\5@c˿׾v{m,]^ Ov\c*!$EOmb1غ?|QTeOE?#lqsuN/ hMzFoGGwfK̴9 7ÃUyU& 3׍ylyCo3kGI?Dcg[8Ďwl2fпj.$$nPx=x4q`K]Յ>89:5GrHV,wὯ9L10L5 +foڻa~qLKK Yܩ3P\rPt3OTf㒵&9-ڴ][/N\3VpBxqۭILwͣ!!wu@l4d#v\,v\_kY!5>4Nlc3t^=&nL5x:`N4> T'7^:k| -ICW_茼Ѭٞ;N D`(8s~mzM]kntY]k4Cv^yV gq>>?];:~%e V|f?9}ok(^ 7+h 醍%7|LWzb)s?XPƵw1{H ]OkRpJfaSM]N55_/'g(̞ddwȡpS05# w N÷ @͐k ns q\eWuѫ{^a˗ v!j,8I}~mݠ0tʋ4+we2p,vPerköFϨcoccb?jCX``n -HͅG12÷\eۼ\?\ N~Nyq"4௞|&h Țp- ӳ_Ug{L8k(gdMҊZf:39!@om p7'k9cqJflFŋ7޺f@Ԏ78 F f2s tY7S_2.>]T"I!v&I)t? |yČip7͑6ҽn!㇑1RǟJ-FNȧv$P& 'Kڵw8/]e13_sJ9iGqoI$&6啕5v/rpßۅc4FAKρJp?R鎵'>i<gD ϿKApz;h#o5 XA²ԡ^G0Q,jMbT>6?բsշ>Ԇ`@ۉCt~E:Za#e5 ;냌3t{QvHŪNYԺ1'~|M}n29tjeP< BѴ"])h,̩ؓte;O '30n(*-=I8 JH|-:`2$K${d;!`I2iT6m"}l^G[+ 9x kl&)$W4/NMD1i舏1`]*yӧ#jjMW܆;*Cv P+4Νyre&@1}}] "߁Z4s,*i!r`܈)Q q4yE՝XVXuSThI웬2w6@SAΝ{e/G?Q&6nu JGa:{6bOu`kV6t&?\ 8z?TͣIY9J !b?V~ƍay0-5M])kǤMνZnyEwܚ3,yg02Du11vE2*nd唪3a97`o8'mVlrC?6 S9"a1[ l_5L/4(LBĶ:RGEπZp5'rguI$㠻}!(6@ӂKCg9CA6h~gRKױkX.To!_X 8s$9[(GKMc}~ƾhغԽ!k׷qwmո:?:)99Uk*bfliɢ|zۀ9aU͚Mn-bE$֣7|%9 to*K0W ձ6 :G@Amp\rîN@L5!1m 7B12SF'7p)a-$0KqVi˒RD7YMdmo/1X3'!v˦ W`&&L!i5P A[ݎc7n;ޮ >Z2/˶ 8fA' 7-3KivN fdk-s˄vn9VጼIj 6| 6<0gT1-nˋJ-7<)ƞyſ.]ቯL8,)ݓު6x'Yt |Pp^ymEucuq[zC#;.lsm.2kز2®WeZtduu%kيk c,K19ej(X۞6]95.ib#;]!qYLφ399m4R(7$ 2nL横 žҬW5N+tJƯJ,GP\ĴD,evߞ=gmuIؔt&*5^UB̪=Zo83$5λ-w9rpw8byN|EG xk& )rc~V{{J %tP7OܴؓtyϿ@%+&ɄY5'Kjd$1H,x KɡXpfzGЅ;?z ,K*|+1 `ˍ,Ys:'_H"1: `&q$Wu}"UI%.P"rpGZ+ܴS_UT9ԯ_Z[/N,LɈn*~[;6wsMWlA{T>:IVtu]/p?6,8;)%4p' ez:ʌ:HjM*FD};޵9tf.@S|Z"lE4,\5{48iڐt !(:NJfĊУѶmJGaU5e퍥3!OMH,:y [?}5'RY0h".=bT_yq_}z3M Gd!ƒ-dmw@98oUŋTr.2#u-ukܥ΃mkʂMzŒ~MmXAvzS"#&[}}ͥ/g`ȟ@]:ģ 1w0>'\,!؈vk+ny-Awl(6myVϪ./=RT@3oDک76f/_ycE& Zor<,/'E"Yg')“,ȑ2kqʌ8) ԖR>#/b@g>v ŐBx?\噯Q2|^," :tP%RV5ܒ۪^2η2R )m㡠* -_7g<}$K;M¬yM \;|gk͇K )+V1O虿x|B?ݟ9%i7ܤR I:'9'ꞬP ]}bb|]tiغr_G*BŸ n$2,͸QEwI+" qOɇ=m+W8.X{yy6lLKؑ#-+YVȟGH72So/U_c7] Y=-lG,u˺m:X)%ի*zW^WLQVh?x熘# x RxA ÇxjYħ ¼C'~ڽ:|:jڜWR^ B ްh)5}bM,&A#~ C#{wBY|y}Jg΄tKf/#s#2dd=؜%64B8)si<=l5/$KuNi=0|h ŶhM2eD{I7=''MO9~@g}ׅ;CHK}X%HpF%9UWQ&`LWngpAFbLet |hXHd64#GǀO _g+Dz`]~r%gD]C0E].u:$4ANVr>Zv`Cd3{σ1>@ )+lbH7gl{ŮGlwua V}<򇇯=}nӎSM!|kyp4"+*v/bVST 53f:TFdX`j?{dFy+l'0^{lۤzurjB/^B'B  "yEFM+ԔTL Y'jYDZ|#}Oj q{T |b#]@D͞9(z9y# %cɪɃ48Å@.smUDMKı\A>rTG:ѭa5lN81K ;q6S`\ү^24^{'ꘝ ž | kc ~q 1=XU݈ճq̾G ͞#̔Tq([xoq-zA8=ޮUO?*IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/tests/0000755000175000017500000000000013752535025017624 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/icons/tests/test_main.py0000644000175000017500000000046313502206677022165 0ustar noahfxnoahfximport os from .. import icon_path def test_icon_path(): path = icon_path('glue_replace') assert os.path.exists(path) path = icon_path('glue_replace', icon_format='png') assert os.path.exists(path) path = icon_path('glue_replace', icon_format='svg') assert os.path.exists(path) glueviz-1.0.1+dfsg.orig/glue/icons/tests/__init__.py0000644000175000017500000000000013502206677021724 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/icons/glue_link.svg0000644000175000017500000001164213455362716021165 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/qt/0000755000175000017500000000000013752535025017106 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/icons/qt/__init__.py0000644000175000017500000000003713502206677021220 0ustar noahfxnoahfxfrom .helpers import * # noqa glueviz-1.0.1+dfsg.orig/glue/icons/qt/helpers.py0000644000175000017500000000420013605357235021120 0ustar noahfxnoahfxfrom qtpy import QtGui from matplotlib.colors import Colormap from glue.utils.qt import mpl_to_qt_color, tint_pixmap, cmap2pixmap from glue.icons import icon_path __all__ = ['symbol_icon', 'layer_icon', 'layer_artist_icon', 'get_icon', 'POINT_ICONS'] POINT_ICONS = {'o': 'glue_circle_point', 's': 'glue_box_point', '^': 'glue_triangle_up', '*': 'glue_star', '+': 'glue_cross'} def symbol_icon(symbol, color=None): bm = QtGui.QBitmap(icon_path(POINT_ICONS.get(symbol, 'glue_circle'))) if color is not None: return QtGui.QIcon(tint_pixmap(bm, color)) return QtGui.QIcon(bm) def layer_icon(layer): """Create a QtGui.QIcon for a Data or Subset instance :type layer: :class:`~glue.core.data.Data`, :class:`~glue.core.subset.Subset`, or object with a .style attribute :rtype: QtGui.QIcon """ icon = POINT_ICONS.get(layer.style.marker, 'circle_point') bm = QtGui.QBitmap(icon_path(icon)) color = mpl_to_qt_color(layer.style.color) pm = tint_pixmap(bm, color) return QtGui.QIcon(pm) def layer_artist_icon(artist): """Create a QtGui.QIcon for a LayerArtist instance""" # TODO: need a test for this from glue.viewers.scatter.layer_artist import ScatterLayerArtist color = artist.get_layer_color() if isinstance(color, Colormap): pm = cmap2pixmap(color) else: if isinstance(artist, ScatterLayerArtist): bm = QtGui.QBitmap(icon_path(POINT_ICONS.get(artist.layer.style.marker, 'glue_circle_point'))) else: bm = QtGui.QBitmap(icon_path('glue_box_point')) color = mpl_to_qt_color(color) pm = tint_pixmap(bm, color) return QtGui.QIcon(pm) def get_icon(icon_name): """ Build a QtGui.QIcon from an image name Parameters ---------- icon_name : str Name of image file. Assumed to be a png file in glue/qt/icons Do not include the extension Returns ------- A QtGui.QIcon object """ return QtGui.QIcon(icon_path(icon_name)) glueviz-1.0.1+dfsg.orig/glue/icons/glue_move_x.svg0000644000175000017500000001530613752534424021523 0ustar noahfxnoahfx image/svg+xml glueviz-1.0.1+dfsg.orig/glue/icons/glue_home.png0000644000175000017500000001661413455362716021151 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx ]u}o%3, L&IJBB`A*GT@-(VZQsGiOmUвBЀ1$d0{N&{df2ow;-b@mtmr *+[zyo6=?çhOF1fXm < 6dT( Uav?ǧϼ:3'I:VW  C3(/8F٦7&}=/gےwM[C$8率8E@MSԒ7v,NdY 8ꭀskpCT?/CnC=tdzR@#ꢏ~郱Aoa'KIN#ۮH=}X0SI|Ytaǂ*`G/ 爢a) $7Ūu楙 [W2_V_\GKРR 5pP<PSMNLWF :̭[X:x i2Dr8U?ڣ%[51Nف'e 8d p.@3SH:dyKO8ntaBR|Ύ‘<3{ q3\\>:0F̪9l Zy#H#}v4) <9GNUcPG#=ʱ3Η LLAf=m)໌ރ5FgN-9#unsʞ#@c`tE4<"t*S5;jbc =܏sF$7!(ɾ-ɞ$ y.sߍEIz͘xSI?R5]LݯrsƵS;sb:!μl+\9 ARHlOr*7);ϹGx(Rqʾ5Κ#ݓA? oK|Qɻ3*UKT }q^jBonS;5QzH=.s8x7΁P YL3*pKIkgo˲ aθ5' طxlw|WAćR_L)`L G۶,5ge:߮| ;#L@abjNo + ;mI7p_΁x倱 cQa><U OY|[/蠋3)=m/spt@/U[ ;Px2ci%x@ϥqcclۦNK9g[xti([*\2)z eTv >0[~8^;IW3=HbJ{*uX|{Κ/vLO6ڠ65օΓ_[B9`ˢ>v:zgm<ׇ}4^_ʹ>l<3\ b1kwQ}C#vy&dgg0$Qe~y_%_ܒ .u'xR \TJA"v9P3vr]Ed; ='Bv r௉XOq;?tڕ &f)̑RzR _SÖ]Upمz35z8\fwȐ YȷE]*\O&x;~'2.w-H&E;yNj[xe|dX{ uM) t=c=4zrˑ_qg"$ :|9 &Q08&9% xsYK?:H[3Yݶ.VuGY44Vt#c,?ҵ*pڥwi:zy`I'ҡaݮ2jcHgW\] scu:rXE ӕ6{5haﺋN\r5hKTyD]+uyzLrO(?K OpؒuCGХkoOF ? ԑe36N\ ÊtKedNeU,kyt gE3%)\`n}q7?.^ddۃi8#'vl5| Sf";wwC.ےuѫ`skA@T)U-eNeD/¥k|}E \4S ;R?-<*cc^j,%ˣ-۲9>eUS8*!ik& pVϸ8#]^{!~O!.-Ƨ3~/8xLXy?(EK:3RopFN6=y,.2d8oj|vH}{!gmW^Ҍ~AZ1C\p Xu-1kֲuZ9c0O) pkp`xluĒoE|oр' eԹoط5}Jj]? 1DZҍ>̺cp`p`#ܭSJXO <,z8;fe9:AYװ]1Nʺ~g{~} G#}T;qHdίZ ykڱH ܬ Ot~3ftw,,n2݊ _-ak*ٞDB m۟}:݃_R'LO؉2̧ظ;ǎܑb8c;]2 =2^e Pόݳ)|^ot|MÀ?UwC$ ~$4>G1S/*Y#ZEC࠹u+Du>S#nX._&Y+@&൵7phZŢk'*[Uǭ~Ϧ {!}gGskG3Zr\1XrLhF2cŦ~wl=wt?mͫ[sW}n)ʀߑ7Og'K`'$\SIb8h C%>YѲ=QAWy]'п<,Ͼ9xoڦM?cU(&)"k]c_XRuMUu<_6^e]p'f -J!/+8==NQs;TJ)3o\zO߸μ |Ҥ[ hx `'D]#3GO}{œsݠ$ Re-/~U_1VM_'v?|ax __8),0xm|{$p "}ug؄ϧj=i m -lnYQ͢t  =ǰm4wƱV❣(ﷀ| R͓jO}_MPU]Y@nM3a#"pqeXT/ é];3}&R_ \o2bD3}D7&#yJiCcHHa Tk7?Ku2ϝ: Սa\,wqQ4~b԰<)bۖu7ϞKW-k;΁MIeS8G{6c /⟓`ᢒ,GOO}k/;p[akkƌ3) Ln|+9[Z1,X9}M7ДkJ}^ϝ7ȖVP74P/(?q\4V]Shnin.{qC/_wX5O!y u_Ǻо/$joo#܇|}Aܵj >7^nmjjJ&Oپ#@ỏ`bVzRZaA5C>+p Kxb[q/8 <*[_:(Ffg&O=~eu/eTȨVu 3#>>ps=[~ל\~M >@ҎFx&*b5xOxo@>:U==h0;nmo,p<vLRa*-Xdl)A䱺ʔC>m. Ky6<\KʍK?o8SDR![Y[#HnyԆݩC?_[QU!T=QY*l^?o~+;0AXIzl,[Ȳt@&Zҍ"boX1jJbXr 5,$`۾rD-}\y N`Y;DX.~/$uHO7$q_);25w+Yi<*`?WGFzrr0A778)~}^F~UώLP ^5mF8(ۀ] kJ0~@\.? 1E-u]έXz(Czr-HU9<#|F#%8t <iqbzQepO}GTu@Ֆz!T9`'%xʑHI]Ƶ@Eއz5G,2cPmY[9͎?/#܈ ! V|RXO#nr* Ffx(/<@(eŴG2-G%aCh{v!>zQĀFZge5bT ?_>R8喯2 }wDVBvK:#g#M r 'T!j nPzD a0Y˯wod> 5Yd~HA]{(݇oo7ZoAc'pj؂@em_k3Oo}jhZ f"<Ƌsxyr' 81)S4=#L+)މ*@7/?oXnv4oãO䱲^FW V^FtW IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_slice.png0000644000175000017500000000275213630201003021266 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<wIDATxMnF@'NlP@@9@b/ }Kh 캓 jvmV- ;~d8Ґ X}3 HtrE.'||۞ Oۉot]_~aQs!{!G:cN/tֱ#?w_?ߏ??.'6,]##;kr[zK'mmΕu:«))NV&#N8[6{(NΩ.#ߤ#:ftyt ϶UzGx5-\ w9c+| nHΗW 9$܅t&|W)+BR:vh:ݶ3mc%By9 5v_.- 5uS`waX4i|ІmppWpD8R8ha qWvI>kj翿wMdwz_LڙhG:z"pڈܢ^'G|1:_e)⸥,Cqҥw#(J>Q7GS7os]N8Yd 3\Nٕ'|lTWÂ|"-֢^kVDqPoo)ZTA]sѤ|ٖIEPaktf xʧ=e"ZwGʗ[-4kwTK $O%P\F\˂DxӘ;%}^KHKL@"egvi!F&#-L~SJO "Xw^Pw승xeқonz_~;/=FPlqTR{Ʊ"8FPFԦr+bjqW@\U^PGyjlZ42`څ{0CG1] Ho\!ltWo8 Z6l3r׶[[i|<a_\yЭ O0W%IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_lasso.svg0000644000175000017500000001432113455362716021346 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_filesave.png0000644000175000017500000001506013455362716022011 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx]]$u>gfwv~G%k!D/JQ ˄ď2ؐ_ Fz Q`NH H؊$ۑ}OsU_{HDh,`-@_苶}/Eiz}Ojtz؞Ӿ|k/=vӋnrF|z|hcTN @-SJ_:=6AxJ6)/VsGm+uG/x(;5L`JUOMp9|wF!xf:GR- 8)?88Ҁg,u68Vwwvv.LznwYzO4=GiA h4>ӏVWVpR7\.R8,/|r/7/88=v MÝ~^h FA, &i" YA(E y@*aV 5 8MmbC"{aTP 7 X y29dqlZ `f;13oRƁbtl@C>N8'栜PTv8p>+|?2?TJE'护/,@YH. l0Bl(?ebLk,Ahrsa|7s\A bc43:Hclg;Ɯ pr&|B} ۻKU$Cc:&w"#I J;v"S/@ά$˫KȘbB}ukəBi:G+?XT9%K+nn-{5̎{[\x|'Ǥ U%ҵxD`xuvVA.'DP@>st Fc'gذ&_;Ka}bKhMG,^ٹ]vl'(\f|<y8 \u|D!kԩ gFO[v; m8n6JO'COD˛ iFڰw #t:Qpr0DV"F䉛\TuPumnQ׹"{D3 zE @2{ٽ9%rEF ՀSYQ$2FyDpP:{ܿyvH4]&i(,8 pgC4Lڡ|ԣ0&}^(kgHmjIFiUl<Hk`{cYcinz2ɡ宬?v;:3m?cIb "3'3eLz}Ww31Cn B6 Lkl蔑Jb8Aq1C|,}Ȥo;"N!N mE\a,nG]u "ww#/wp9N[7NRjrfҤ@k5'N>$Qog:gC(|G]0'/m 45e? YLS 8",eLhC<. 3+&aadk-i t˱@^`( 睢VLj:2S#qOnU8 U' t[0D31*ϦQ^$-HO3 ˲'buam~C4~I9Tԡ\.bLXȎo~dD6@"+RJ1 B,lH2X Er2Ey*b[t8i#A%l@eљMOX "(=7A;.M1+FO+z&&n^T:>5>Ne3~u-$$! VKVSbBdr,"bg#Y܌fylğk2yon@N}fاs:+S^EG6(5p൙%$C>HF"7 59fk@w,ю U t1ál c_fU1t">:G9aRχ !a'EPҍMWkvcߙor,45KG禴 q|RlUWҢ'l0kfsYƊqɡgI 1Ea0}oݤx E_Qmǧћ!5vd1P){éYĥ;3l4ΰ͝N47?ӦI@sQ]Lnx`^ DWG"f̓)%;RԄM튌LȆ:^cWz s*,Y(TQdd!7MtKxQFM\Q\!yxU"1(D?_-:WG˽q F]II2Хn LNf*__O㶠x # EѥKRY3: W p^C|KVlˍ3ij1YO7+hǍ(P$l"YZEg#)Z` . Ir󜞠* >*gڑ )5g|\[ <ubJzV ` 4J[-P|- 1Sd?"oUJӡvg%tf/2O7 G7r2>]@%|K, A4gu`i9/=hD.D7`Cbhg$RA޾.z ŋuxጃnL8<"4U9FLF VPgٴ|qEYoY$!0$hnoeeЏόYo{'tVmguwϤx )$Nȷ]hr)ߏόYo鉮4M<=vBb';>BЗ˽Xj/"xR<:\I>P.9;Rjnjy?GS$/1F gУ֟XjD#HFN].8z8;ןhWHX0PV%CdQ\xL7NP)"P?ӯN4^zly`LPfU|Sb"r &Bz2c1ZAq"lʿ?;\MŽR(BN$-'k/w޻V.w{vs)g"Y^T\ٽRVW4S}OOp\!Q-Ňv\zv֝%C 7%CpfpPbD4|F'5] rIō01Օ(v_L>K"F|Ff0I3DKE'*l-w0 tt![ D) ˎi>Ҧ _.Kٍ )l&OP0Ր6bh}[JE}KDk[p-֤5b޾2|n)ߨ )yAhPSG4UVrnٹJsL ulw#b\/W|q̌ 7/mFb;ṊO'|6Į}%cf/ZDlusd/xpǁ/L8&b9[OdBa &elp I^KqeBP|&_`\+f'^%?Бc,XhٯܮLĪfS{B5j~/Mɤ^ GٲMF\.kc~[:_7X;Cm?Ɉ& J$ciJ.gZ=womhf8]y_^z1Y3Nlw BV[YX[SbDwBoq!L]]*FUIFNU@VA( f= Z <#kͻYPfR2.kbE,eW&%5|ܻs~]Qc-eL=uȖpTU xإ#79g$2ĂL]^ϑ3L|0j#bx`OcFIiS=&_МGk&Pb^bUE8*\kO[um {:! va\&/DK4&x)$j9Yg[_LSnGN<9  :"|3bbur7a;?3u n=ӥZ׬c4,I&~,nHBMK:#npͥǭ8+fMVʅAE(2{\eL^l?Cvvj htPdRT zHI3u%SIQI诧` h 4gOka$S6L 'a)GfnnlN_^d~^6n>Tph.rp_,.!=46)Ϥ//ŀt;S{)6$oHîm`V>=GϞ8?5f?怃܌DbSK+ 'R?K_7N$~֝[`> image/svg+xml glueviz-1.0.1+dfsg.orig/glue/icons/glue_move_x.png0000644000175000017500000002033513752534424021506 0ustar noahfxnoahfxPNG  IHDR}}l%^zTXtRaw profile type exifxڭu;# ބ?FuS+33ȦT;_|r|Qog}z>o>^<x ;ϧoxÍϛzׁ^/tf"y?ϻ:Py?ƻϙ/%|~"' 㳂t~2](zܧk%/A~e_XW/:qXnp9 Uh~U5Ec|~Iʗv=]s[}n''IZ\%GO2F/*9~x{*g^zoۜ3"gGXW] eNy q畷t~~J v;CVy/S? D;Ȁ.$-c%A .%X!do^3`HtV!7-tc~JPO!ŔRN%UZ9Sιd\/ĒJ.Jƚjjo00J޽霨s; ?ˆ#<ʨ>)gyYg}0*vf;.N8O9?5ʚGx֔>$匌xQ(hbʜrf)gI1)c0nq?ʛI9/2gHݏyI֖xnތ=]@Z~cͻoiuU_r,ZncJ 'J)qw nטr]e3vʨcZG.rrinPf;q4hƎ\%b]}ƶmDfc4 Rt+ce|9a{>xrebO\o*PSHP `6SX5:(QC{B}T"υ#-c <ꎍZ`9f Hؑ*q/znZd vUzIr!`b[-JqUQmc Ҍ%P6EMx~LBnINi>:wu|Z",*Խf:)9PPȱwK6ʣbwTUbȊ'pMafm=K!Cn5߇$GotZ̲ܪ@À\%CۑY.0S 9NJHURڴCKDj;p~-rdzB SNISZŕAUP ]|ؠqLMk ؃D.)s¹re{fi1jՎ*,!QUU9[n7 ~:|\Pvm"\WCqX^*R7ˏ*kVq4F."jhiži`MRsm)kK\+ZB') ]EZMq|B!Fhi1ؾxl&ϋ@%<#@dk>(얊x?~29NU l`.=LNI+̭7rXDT*w`.0niF(-r<~ّ'>Uj6iwY,PPX2bvt Ӣ zN;X5{BQ,T]yn6B΂@0\:FyrUY&@t0[?L A<֡,=`SUI$O1L;A\MATV4xC-r{йK[z!o]R8с0 =Lax ZZEӇtA})fҲ5l6=pĸjf*e !SQMf+@~HӀHi0ݥh jT2DZxnr bv .M ܭ]R! 0GL:`jxT(j,)`gЖ洠 ޕj0Ԍ&XM)}ǞF/=@AB&1Zh@PM(a+-`ΰ&`R4gs۵U0p,(<Hp'IhqqS,F>`5Tu "&nDZ ^ uUCH4EDx|u+hx*M(V5u}3 aUDn}nY@]e oX/{h%XSuwM벘K0ݫ8iAp@/US]V &F: N*P[|'b ƧT\o45 I8堗aPZbxJVoJ(|ڠS9 4+p[Q D2I= ,4~\nȣǚ2(d"Z2q4^L-ƗZ||\ UM4(c!MDDYzmO' 10{j1EI/,˩+@+Af4O@H8kVedz|-sq36Kj_Mueځ;3k6;TRmݰ`\Xq MJ/(84CCs \ ǰ6C7U Ę$XBc/ U" ~󾔴 bK6fշs`hy"J&YE[)pB #JkB&F ğ8R퀵,[K6{2AABD'H)^PPK|QqP, v٨Ua{0%ttX(4E<=`+3 zJyKx$ƝdjcEάnB: "S4*47A,qoyRB1"&(HH |%Yʓ:ܘtF´t1ғi}xtwq"@;d~UK׃%Uxw?+ +E, n\`5*VYW'.$EXbax_!;΃b*Q.p Xc=IL|Vj!9mG%M} , '@? P<C`hek+,z9vحqIQ3zGivA1``5m\7ϐ=( Q0ӈKKC.9١n_iBQF6́O5)9wы_V-ZJF0mPuؚ $oHm({C֣MrnQ>iPy"+N# y;m!]^O!G^F14b Ӊׁԉħ\$R5cA۝oYvweY3d["* *Rk7şi51aȕw@jCjȥi)@tź:P P!!xDztv;jJA[ @YH@dz)F^kTD. r,.%6,a%4Jg<`Y-NhSUC<j X ^\.îƢ@14j#UAN/K ntT)Mq%?jv:"Fw5uvYt. U!3|[z 1*m< ϾXȭ3j͙Π,YC!Fsk[ވQaB?F9AƜ~ Zd2G{`DeԿg"THL-Q>1,c"յ9<@n Bλ}^f1eq!]B|BHb>'Ix=uRxwߟգLYs'tAG.q.8#' 6ۘ x83.+8*kޓ0VNk ,b "Ȩ2,hH1U#*P!9~?=[3?9&@m]QcngJk+u`ZKuK`I ɑ<~Fߔo5wns>iU88F PiArybKGD pHYs.#.#x?vtIMESoIDATx흿Dƿ,J @r'ED"RZe+Lo^L;' IܔD\>'oH.(8].=乓|562N X~Sḋt!Ρ k*RgAtM*ܤk+Sp"]:::::Cg1)Qf/xگГ41+5I )I=p S۩Mf/4<$;CW`]-́9Mf,A8]SO5p;*ag c]Ūw'-f*2I#"?_;K)fv#;m0ƺu| ]ott~p|o9\ht&-ze֪ZVJOހr~g8Ư(kcR1Ƽ=z+Ѳ͏u[ zp9 evn>w܋ǣf17-'5wkJJZӋ{F˦qA1C]M({q}+6*v&_h_ˁ m6k }4C]FK.jЭ]/R;![Kzʔ(4@q;4ꖰ);xREFnLw=\Au:Q˞:"5 /sG{7NwWׯZ&u\:estR*=fblo4v\ >cne}CՁ'a1^Qzu 'gt}q/Ц.&˃;KxCnvI@po-o֮>e z[WTٺ_>fcz6|e;8͝&8gcwuy`V#E;ǟ%8b~ Xf́# 4\:6ZڸeKj>{q:YIYŚXv;\݅ N׳onc͋;}3n>_佋ٜF\ǎܚ_::::z >c8r*+O7N0C}p2r}ߟBry]?nK CZAC/٪H/t.1Uh8%/:赨U3^ =2.X9b>X'}'s cDSu1O&B .94t "֣zNo|t/ )֍>GNg1%bɠKIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_slice.svg0000644000175000017500000000744613630201003021306 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_circle_point.svg0000644000175000017500000000363413455362716022704 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/playback_prev.png0000644000175000017500000000225513455362716022023 0ustar noahfxnoahfxPNG  IHDR}}%gAMA a cHRMz&u0`:pQ<bKGD̿ pHYsItIME 'W{IDATxAMak4&(b\a"6QR2+VHY̆iܻ`E(("LHY|t;5 LLLLLL.fmo&P 贳#OJ/bf5ޖ`QQӚni]ܼ[7 9IEel ->4nLӭ)crtkguNnm ӭQ67?Y2.܎$e$HY\bg=8@M9Þj)TՕNpv*T݆ۘ8Vugt;̉[N)ztMnm't+ҭZt2r9:n52W mjm`65\@uӮf 趆tbjn+@ mu\ݖiMjnhT m!uu2J5R@nZMmY*-^[<3SrSfFwƩqWl(Oj~Doߌ  J?`P:iOr4(8ǃr2(|A Jr1(ݝ}`7WB`4$;Cϭt/q'$|^ ykl'ʃ_RӐtwxkSːtWnJi": zx[Դqz] ՊzLGLGLGLGLGLGO<41%tEXtdate:create2016-05-03T12:07:18+01:00GZ%tEXtdate:modify2016-05-03T12:07:18+01:006ctEXtSoftwarewww.inkscape.org<IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/arithmetic.svg0000644000175000017500000000542313502206677021341 0ustar noahfxnoahfx image/svg+xml x 2 glueviz-1.0.1+dfsg.orig/glue/icons/glue_xrange_select.png0000644000175000017500000001150013502206677023025 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYs.#.#x?vtEXtSoftwarewww.inkscape.org<IDATx]{TřyO04JF`7YQ8Ced`?zz{;9޾}Us@\q |0/[nҢ\ P P 19Ad@gOϘn~ -_ƀO9biqi۶zx<L@>{~(C[nj4O8e@K0ЏԿ1=9,u$?ʼc?ІtL@!4!)BҤ q3@l26$.A tǰ"z)$0lŐuX1,HO+Zulf{ۈAw ͮIrtI^1ݺ+Awtp^18r.8= jҥn'Ld35g;鱺դr {1(X_C?0W>9UhY<⭛{q+ȉăiѱ6D_"\yp%˵^ȉ(}0>o<⽻0v.8vAՎkWwGWnG0 _R.ב. ld.x}]z-VJ@ܻОyzVjƛC\(&>g]C{SIUvQu}G"Z3^+e˖@N ƠI~冊pJU3AjN&O b6v 'wj7+kt;$S( J׎&("W-V0rJhruj!6~!}trr_{s/GbBԵ.(!| _B_)hO+筭"1>30Ã{׀V1*FzK+(<RB)'1ͺ-X))8M?0̏!YGMKp`ǻFF7RheщZGR@Hvyo\-</ު1NCEI{p8L+wXȤ5N{V1PKJ. >#"0m̺.a) &~*ڽ˸j3pǙMu, BWGF)^,/yRY@kҳΕC,R=#:6l\?:tۑ>{goU2s;&bNʽD"e]u)XaMMO\zu]Pxk'vd1`[~>3heu@8un I S[3U~Xݡ5ZyYBM C5녅O[A^ ߾wA9_V0{YXםD;&]fWPQt\]n2%]k]k;`pzP~WT:"hvnGő0zyph֕oZnh<$~,'Q+Ôc'@ͤWbsܝM894Vwhٮ~8.5;Gݕ3`~SiT7CkAyQ.m]q sgӺ;&ݺhZSv> L|^))ZY4O_CՁwFQv5ѻ֤Sg)=w+6bEO)] ͅg|-9bSf!>\a W^t˰˖-+z_@Ǐ<Ȑs=I1`gͲJ'fJg_I9)lP`ޢ> d0r嫧*LI706=uUD`<>_.J/]fxsE 2l(r1#@qFS)n";ZutJڽϚxd |]SHT 6u6N͞8EŕܻFOOopx6 GZ5%o`&ۊrFؓPhz$9&14&]\@aH A-DgEx yo`S"ʰFATJ<#+ Iw^{I\t ~s;ZCwY8x<1?֙BvS b$.szĠ?wU?-s-ϔ:SKzb OWɺr3p_gn`$ZS.Q+"b1p8!G)<-֤ +qfv g1H`3 }e52V5ǞTO٤? ׊P<$O Z3ؓ8"khx0,j wg?;(d5f ?,ElμρSHF oswH=19蝲A-ˆA%EsCvCP5 ❤<5)J%+W-_=F"@*JLp8\_+ŠY>/CjpFo%w`nWO4X{`475}i|:E^;p2^ǩgݜF!#=Uno"K=S+԰n@Kzhck>i-Mزto*[e#.|{WO,E]+V`!ZgdpցqCfN|c/pÓ?@cHHBuMֆa}(̐^?o>v?|1ǻi\̙Ɛ 䎝0L9ߤHº.ڝk;*O _xLmk ch;eU'1?B1ꆽ얌ڽIضQ68Vw (fe\ (V1XSyU+j?\9SNJ7@` ^$g*y(2Do!!?پ B_9Z*:1+``ٮ!"q 4@I N2ђn7rR7=~g*Ogԃ{+^Vj{/߽r;1dy X}L&{$MCVYgj20iNؔp@\*#y-NlY&1]Ww i}/] U9fċEP˞bgHrþs/ L;0?qQf]^u=s^_p/鍣}oںCt!a<++`+)#Y |Vy5GHe;S<X7*FKsqA艧|t37,e9&B(C0߀dJN^>o}8x‚peI7X#VE8&Lg“T;8^S WwhM0u@ٛ#cjǞ?ؕpu֤[QJH *Zr$pp]ךt+!eW?KvˋĠ;&J*ģJ')nޤ[/c?`oX1{kVjg;&GH ƒ ¶@Linf&fU)˰Hh҅xޝ:h6M,kjP՚t;-‘_G3N))Mq^v)Š;wg h ̾zzКtQe'Qe[/rК| (2{"ϠqhN vbp`/py 9Bd= +Ix2bYۭlj0I Lz&hM+*']:7۠5i ŕW.ZEs Jի7VBb lvݺuE7FəUR=+]Ǎ(wuz->+ЌxWX꼙W5Gݜ7HĐQ܈B_5+N,^;1p)St/[7+̀E#eVMVjUv9s!\}Վb'0rdb#Vۑ\y&{V1pSA}ytiπ \zBPZnۑW p @ܮW.ۭҽޤAt@SEzMXnqNѬȋOOTl3/2o Jw&LSȸr ;1ʭa\yG>]ؑʲTλd:/FOzccŒp ER{ \$ᄂD־ ` @p @y y҇֬Q$S0uTohVlå8~ tluN(E㟢q^2Zʹ:ډAҕEZ+c#&]%hp+&=xF 6|Мt'W.Wγ^Bp֤vN)9grQwp֤+G+=52{b#%EBkӄ (2|@oҝn\ʝRIZ-cObIОtˈaq9\W-~F&]H9NH6, !;gj-J^"ՁVIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_delete.svg0000644000175000017500000000434413455362716021473 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_image.png0000644000175000017500000000453613455362716021303 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATxnFWEȡFzm>'rTy+O "=A'ZCr+  ' Z~ȝ3B""?lFK| : Cga, VwdgO_eőDzd 7.CϿ,A2bA9[k,>X  3M Me3t}pu@TG)ebw| [T$mM6;6޳}DлlrITe10-[_!uncL7oD޽u{RNuhaڪ 1~Vޞ5ej[ =هϱJ_ ^o 헟w-~cf}.aN Cު]^]C1۶[ϲe! tKOdAigl&~}۱Zd2y&. $OdެRI}'~|@%1QGڼC˖aE3Z?E%O>]? Ida4=T3]EUpjQ=Y"ȑ%Ts:iʯ{4\lP 29ҹ8/Tl)ZQLNF0*.XO9B0(q}`jOVCNj.$ T=W8 tʠ+9$5WԱ ~i eW3]GRvHYШҹ ?U8wcFAơ):Vx+P_nMCXXO.ZrXE:B$YS4SގQB˗,Rnˆ3eF_)~ۚ zP An8|yM@_z@cI`}GɠD#Fryo`;h!FM+ d֑TbbL`)t+\: Cga, Q_6{iIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_not.svg0000644000175000017500000000436113455362716021030 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_row_select.png0000644000175000017500000002153113455362716022361 0ustar noahfxnoahfxPNG  IHDR}}l%sRGB pHYs.#.#x?viTXtXML:com.adobe.xmp Adobe ImageReady 1 ).=!'IDATx] pW~3ݷ%Klljq!Y;YX' U$Ͳl-rԲUlEABU)LdI q!!l'߷%ۺF\,g~{A'pW^o Hc_uXy}C<:%%9_Z{pPA֤/QOlfiY( |([\Vʛ׸B0[,G.eɖt,rV̅h ٺh 3Cs'3iGeN7<%'R9FϤxӈ_\JCf|.4Kyx#}}LɈæWc QRUT2ߘlYx?8xOpAܐ!Y*iHscg/wF)[&vc i뀥@nL3 W2~0 z2g@&: +e xFH aJ`0aHIv`L iRd,yޏ<`Mx (T`p. p/0A;9 U3qBH& 1jG2Q"b_ZǨ|ħy~p" idNE]ܸ2.Y> (-! ?=ֽQNiS\[BKdWt`L0ѷEbhLl|FE [:Vdp /ׁ̾F-e#~M.ٯj0S>H{U0TIJhNU'Tl2S ,)]S_?=9ˁO#&K.MB`^7,*:jT@ֶht$WZX5Ԉc0h5\iG`/1QAhtёRdʴmio`ʍ\ HE-[\thkŰ4[ c9 Htz[ gIC.fBI)dCJĜY͞h/P'q⎏g0$J!Fa  ,>|0q%.W77WI%'*YS.sӀ*xS+=cUJȆUcKE,7:')}jzJ+3)3gXhXE#R~p )C2wVa"?|>;E9)ܧtGsPoq>#MEg3`a^5oPϖuj㣫Fis0lS#jo*Pk? k7mAdYFOh~lG ΩVWcrCvίuFrn,Q'ۛU]UziSEVۤ2T "y-=^2ZZGKMSצme7h۝)@hEgl 5GzR[/U YH{#>s䔒b(&o =q7w/Dg|K5PeĎŗrYhIGu?i|Ӊ$!a9Og@>x*f9Kg9v:adEz,C-[C(;e>b5$~Ruz~|ikV.;W@R%/W}xڂ5a*N"PL рb;'`oуQ@\X0FwKLf {Xi^QK?`mq'kP&dp K bfIwpSDYSr|A w2su71xGNIݭΣ5+O߫T-*k(dq̰ECa,ܞv A7'R jEˤ4_T6XL-/WYKRsN|CJPO]Zdd ^Ra&[م` ̌",2Ϗ+KU?֜x'ڑNtzІrI(t F2NOݧ^:z;dځCwˣvY Df qG87kmHIkQZ[|zrU_A}^tPԜf,|ǦA=C#N{22ɀy?+@qMܟ֑>DSsC1m)LЉ*m:*֢IQR+[F5 ^W-WpL4R!k ǶFR}V `tÍH-±F$]q HmJSop23lr*ሲGwCm>2]?mmh=~劫Au_kj ݸ҇;x&Q9W-MO &8hL:\ZW 2 &Νӟr^d}Oߤ~ϖ+dK>3$^9ʚ:= +dX|Pu0Q$0::vxY~̝%H0\-+^bb[mOSԟZ^v^<^7Ůc& d)=#&BKM"*mN&2HC@Ansݺn1.=R,bKc=+ZT ~{7xeBZ\'zӎKp\=WHUsMic|4 1GRpl/cioi2b?uwnҚSPrzt0肏[tc峰ZK۽'Uџ* ^^Ԣ/#?0]]]&jdB8j` b'&r$atq)w}Ͳhtlzk[ F1o(} Bt^HiӆNiik"Fٳ*ޡo{GFZH6P4$UYLɠ'G^kj4ZKzC]{%;vtɆ'EORV> ϡ2^ik^UT-OllO$םU]q2'=5xsN{gO Ab4kG^`}kJO gGfݕ82^iC#QgLu:\h` nyx}ʹvYߤ¡}F>/G%΋(3H2 V|*'Sw)f6 C' 9-;rޓ 㻳y?v\_>Gig媛>y"^Q L$6wgӧᑨ*zJW,B KZ.↏3;J1ut9 SfpoOr'5!tG*׭6yuk}פЭh66p-}TkS/CKwln/β畫ubY- )[>I>+/rTxi#Z1= aBͮٳԾUK-UW3'3arwvnLVؽsGdf{] ]I:oB:|DF/;H[ 7auwvcdˉyZԁkZFy%ƩooSo_߅sNw_la$΃RKלLvTS`sd!W$`2~+w=aTycrl޼`Dgv]w>$>%IQ>F;(c5o.~8InHEq9 ody|YkhLtAG0ccf ZT+.2#a5,:zIYEf/D9R'|9w@9䧲|Jwյ՗\Wuv'AU[.PhZv}nvNŢdz $l-_.O$)ٺ05@֜gcKgTyRfq{qOyِ5`iK ߉fzsinpك0>]aPvr3vIk{k?2vttж;㤌c" 6iE?Ʉ'A\رu{ s\1 fxƀL4 0 oŘ(Gt: ˊք恺ZeZ;?XVF֭[GTGLR(q |^7|&!Qf(?q]151ٚk[0>J9X5˗OG;u94؝EϽ+{y}|\c3 !h|1 _{]?mCwl*/iF / Rw}С~׏ٛ7wɤ{Ltcԑ]{G}z9V“=w%{baFj,QZZ#?&:|{zpma@LI{JkRc欩 ^vHl1Jv6J?fURql@5W#XI=眨6Cm]~i6h#pGEgԉ٘#< u}śwCOu|a:ݧꏝrrE 3m=tx;@2fGe^C -}BrzDKe=2j.{3 ps}7?;X}G55TLa*\JZ4r.F]4:0iqwtX8SјٱeNnǾQ34[\|GΨS/[;ahGoWoꭎX{@*'(h: ,1= 7c=- "4o~[w|GP)U|b>^_mmTVyI]]E+,?uڟY@I&c@ݑ ?[m}\'l]]4.-G~WN~}XwX&I:YW­Yն]-lyP#>kژ59k{l! lxԏ|4ܺ<ۢ{X9c=uNd6o5۳^?Z.Wgsjxi^^8HlH*[`HGBfoy.(kpd]~s,\];9ԣGХ]yTsf5TJC( 33A ($HCHW@5&˳¾w,PvG~ts{eگlynPgԡ_=wcgUs|!2 Z ۨ*JԲv6guv&yi'3RCHWŢi$h;^mOF|CoRfeK;<|?mr$c}I>K֖-78[aIX n"&eTV+K|Aʐ,$0C;_x,߼w=>+])?:V}& *.Ha8`+n<"BO8*%2 1 a/ !ȉmٍK̚}uZhtt2Hh5@l8&1V.oUٝ.$L h=تj>tvGȳˎٌ=-M&qN#txPo\=wdO}ugiisPvc {}PO_MpG.n^Q} GOF0qj>8-eo?24?B;kkTLY) 0>U|QN@ U@g9| BO(T>|^0<JJ`̴=]_^Q\DG1p"ľ#PI\ovSݽ ?q)銜2.Za Zn2z\Vf?9} vm(-kF3tK B9_SVs1ҴX#Pfw%z׶'! 4]B# _o9XFb7@RآHxNHw)Rœ :H (JSVʔB܌ƗisFDZ\*L#IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_xrange_select.svg0000644000175000017500000002124213455362716023050 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_link.png0000644000175000017500000001412713455362716021153 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx]p}: $l`ɎI]3C$qO=&SNn;Et4Nک]4maNmIfl#NN8BׄƢcc[  ~t?Ҿݽݻ}gv@}Ü -,U`QM*nf]qy5?mc^>`]}5b])ߒT#\Bw dn] oC@= 7@沞kA0pptں[S^ E|>ln*j s[ŊbX%D :qCT8}J|8[KK+xB.lEkOru5I3S/~42.==ؔ A k #7Byl7g|a.ݮ^w]kD|pn\":Ux픗נּ*tw`4/յbxfj'o+?}}WjWqea,k?'%c;F΋%cѲ"]Я}|XZ]5*چ]X1V~Zr}O׮SF "~3[=G qO.npscnbe#]El6ΟũjF߫M-/u km4%;L{G oֈ/w[;ȩ_5JlȯbR1e+}.5cYSĸ3׈]^N7o[]gաqe@{-şx+8$7NkcoB|67qEֽ M?. kl>?de+rEeZdi Pi.7!>9:-R{G"p/Euws3]䁏yguYD1] v1fU{; T *8z~a򺥊8#so?h}ng \dq -Uw.%ܱM5pxgֺ/~3а6}r!ixoͷeQw2B~Q^i76rr9\ɳ\+Q  ;6 xy5HK;&Qr(R oN eb6PX+܊䅓˱Ȕ1b$y,/ԫV=e>(=ХhWU*oxi%]<ʮ`; ?ٴdcGSg.dêG,IX0=YSѮXjXe2/$ Y n\ c56m)Eevѱd]+T=!=>ߟh &5t|NAvh5'j8nD@n9[Ξ=+yWwC?k39xxkr{$q "WxU󵵵ae r4}XNw=ZōGQWc2n_R=c4d)1IcfuU;G˴š۬/X~z1::J:sQ7%hV]34JGE+eWU-c2n=BZ>FZώ1bm g׏~KN<(^o=E:$Rvk­YdNԷij$NۿdvHA5d3kh.8|rkɒlZ(!CmpF+xR&*x=Cڪ7 c&^k) &=>yY|7[m,&AT$@0]P2G8 ],sx뽴WW+oӺyHc3~xR0EsCVZ JO,IOl/kM(1o+D+UG~zhȒ3|DGⷃ[[۔ I?6$b6.5ks}J>[k ֮;^ߩ:5 x3& lRTt! zF@sFzuWO[.U-%_)KԢ1ݦp&IõK5wD)8 p^W'RMEOzl:ʦyvM˗+$2 ,6)]yp J$ H8vn] J﹚z7<*lJZw0(C->O$B]Ya5ah坂d?ѥ;0_XRҤ u_sd:d)a##PxYLH<$!ҴWLq!>szH\WhB߀C&=$FC.&dGnXTZgwhTtX q2E-tv̜ u@U6_Ɯ #uېFІPj4N-PՊ&Autw>" asqc yU3vM?0]{mx )E*Uiz&'lO {iyn0-jD~mfBU^n.Xf[c |ڥ]Ay&Y vOqRi?Fֈ1{抜jbDd|Zt˟0krqs:C-jPcO hws\b%1/56]#JӦ[&5.g\ɃEELcpNȡ A `7cŷct[{Ң1oxb5m'V0D-K-vf{gfN?ޥk@6b>fКrċQ'X|"}TExDͨz6TI^`:"ٙTj]*(໨a 1p# d6i@%F×.QSq"u9DfT;8@ìegn[S7"kҏTDt@A7 *bʚc>,JqkrϪN t7gL"P2E|Z1p{GnŒGOkrOu܌N׼i5ۑn!ťbS3JLxcc*Yri 8﯀U,,.F [Җ()6Bbj|idu)5U:NbXmUG"3La$|IÛv@E]Ruk's2YAq'Ը0@FB$BQ&سi<7= YToĢ׺6`q'SM^ p|.bPB*wX×Ld̺]'wVp-= €6dJԘj5x>>˺/!PIIҨVe+iK ^_\.{zVi !S mWv 4xX?Pv(B%<:<-BnjUIH'Z:eNW=kiB1)L]r+-_f e Ȑ.]`,Pb1%eF q'&d@c >:yi8W[$#!msf}g KHr>1U8N''j"^ JϷU@\C{ K&=w`1czh=G$M`L`Ӡmè̬7>++Cv2͓*9kS}SPKa7t%l K2džQ%8EZgmX^망j`_;3-='2QDpEۢv,m6UQh1o$=3 d|&/#gx F.~ЖG& R"n-i`ᤤ?:;.nroζaP!4!#6µ}uaTǝ~99t3Qh@Q= 9 -8x'iJ<5Od)- xř62ȃzŒyqiL':tTZ0˝V ")R={Rc~c+9o8"XQ7يvd)LF][TI. +s'g6Q[_}k7R V1jZuܡ| c3?Wc sCs*URKF<3Cڕ5駥$@7u`;5ZP`h./:MSôdN`b^@CtI/[ ln:Ol Nk"F%hkLCH3=YҠ;|TʹKib LhK K_7o訁 `dy\MrXTU)tqYn>ޱ&\ pz!j˓e>)vix۸3p„(q܈Z[h.]n85}W =v.ysItk3>rpy[قt/ǎlaz2G'U_eD&ՃĒ7붪ݏE>q@/~g>sD*`nmX(eu^u˩jAz}p| qV 26Sjn̤,J+o|Vp} NDE ږ$ٲ.Rķ^]z˸”FC0q7Ώzu Dܑo-=mftG~Ĵѿ\C T2X䔻_Р |XloSa#B.N v}t #`J_ #S ~ v:/23zń=w^zw^zw^zw^zw^zw^zHm:L*$!ut3ww0d`)f~5r2Rf":LE^7~4SI{ FNv~$iZth}NnMNՒ$&^\#i.$߉_A;5^jvPoA[i%)} `Awy J$&&X;k$!:H.Rȝ,TʉK%6:HRJ IJXI^ I}%~ZMr\+$I_P)^?(:H*r=sϓL53 )5O 4M\s&4IĖj=4E>"I=+% "O.cr<ޝ5:ȰDJMIgfnA!tIgT|gRtA>SRϣ 4J:5:H N(m0 mNRYw &>>x㣃`pqtQ:aȱAj)I_QJ.):H >+iathtՒ&GGmMI}&.hs7,RvKp. 5he7$&&0\A*2Md."YA\G۹}Td"ޠdG8{E2I#C.ʚ_\%館 8`EAk;"t4+܅GI:YX7EGM:te#sE34<"GQ~ʚ_?p:"3즈 6dtlAk.%(AFl_2WF(Y6 |̖G)UAA*?(Zʔ~~T`t 3[dҤ~tkfߎ$\Ajp]⅔"!\_` }p1:H م!Eۥ?oFf6?:h'EWl^thӭAjp6JnR9!6ӍAjkfJ&f::H Q雁lEt\7PJ{6H6d&:̂GGuX?J?"u.P_4DJo]inf4Mn9!ݧm[8 M[6w^43Kx9/4 ]xKw]t%"3bZMg| )!b3|:M7|J H-[ghp/>YP韸FIM*K9u?$)_]P-ArѴki)ݧ>ArҤULArӔݧ S7A"hB/A xWAV+&d`#~G1:H$H8T=FP;]Zo&Bq3=  isu聡G1AV+Eea4?nwԆ`\REjW[NѼl}BD8~az2j¦\ Z.\@m(WЧ !0N{-9o҅stu!6mPb(O±.*Qlrph:k4N " 0'#kBĄcwe.pHb;4XW( $.]ZW۟:Xm8Ҋ Ǎ@5"=\AwmķL/==BTᾸ 'a9T7WNk_Uzf{yPkro;f Om݁"%*Ut.·~.99X< 3\>\NHnu< e1Q|8(%G^*0kQח ;B>$E@<fL#T.&{^i(ටf\  GHkzݥX]~ԋR?0:#1`xpl|Yi^ۮVSCpٱ$O-dp ⓥp9s8 𺬮 'T.*|Rp5Wm=s=b +@+1@p+,3cلS3g9Sػ_΂)iYVb#m ҆ yWrY]l)tR'?@Q?խG~*?|n-Y]`6{Dp K7jM*<?Ij TbR566H+6YWBz\ȧ47_y|-[Cm/b9q5ϐ> C%QQSv%.YM{Huk$UfA=pڰp`L@Oug85s0YλuAp!i )(Y Z :[AγTGJS)iS G\pAddef_t>Y}?W޾ӄfiN\h39pn~RGUx$"W"fNq<9*:YK"_{:֣#<~@u"9h-#ًV %_QfU=H()ovcNL]G୶I62B@^֗,ñ7|[|rit[ }yz AJɚ|363w&9w s77Hk7W@]Q/if^G1Ș 9d >@Rx~`53!S .)W@$)#E/7'pv@ݓ^ n"y8Nqjn-O@r+%#Fpd ^"?⡗l<"ءAП@rkXgw57=7mS>;XsH D5^7pDwH:jn1IwZ.䖫Ogic|U1}`3o ؈ (tWX k~X~iA"bg{,^tHAjV>VG"Xk|&+ONe2ZKRW~`GRwBof P(%ZO0%KU3Dxnps<穮җvW}[}䧵lEy5| 35vwι낃_WV{j4[| Ӭi:C~B/<.ם PǽOK̩y; v1h"9spV)H@MA߾BvxVn zE*P ڝzG\L.:\8@r[n< mp0j,33ld/J_Ӯ-r=t)`-pߨ @=k^; ǹ Bf:+Uu2B=O_Z k=VS3R[q%]nPod ߁^Y 5m >02,<) ZQ#sﯺgMo 634&^0xy+s1jٴF[͸{~,Omc2ƻ\YE]5Cgg+wVVW\W ]ܐpOY+]c`[xޕΣCNѽmߧC6Ƅʴ 6h[2Ey7DǬ'YVNxӝ @5p] H/{ >b m,b qkAl^i.VI_a|&׃v)Yj[KW nsݗ6W" dM;x_X `5.Hp\@6/sy:æO 8s:OsL. _UU-,nh%]:Ae(cO` z؛dXӁ08ۍ!>cmm"|@!ܦ>bS}3QpSW~ 7vcUVpێW;3Y3]9Զn +e kk1~^Ipj pFQ0pMh_OF}Qx8L/I,g {_ϣ$vA Cr}]:ύ2@KC;O T5d.wAdMc0CrK%`K;?7ût}m'9FM`D ݊ p{R,o`u"fJw\ :U.O!Z.xe |Mޕ~#Z6$r ջav^ r'*WF+ƻLofhߠɻΐ˟4U]/Xӳoq&|4!a`[~I6߆MITؤL~e] `Ҳ[O)@ BM(bEp(0FMJ߼ F]U-mϟV }u5 @2Id)`;%L^FqދӃ=|-A\AjwϛΣ p/ó}9ynV+< R5'Y3 ۹q)'_GY.uɚo`,'ڴ' SП~]Tyۻq hq*5CJ@p:T?V.ߘ=JuA8*guȤf`]xw%㗬,78YC ȱ %F{7q8;\.V.ͮ(J470:Yw M~q㌃Ư̓d wȱ+e`4#Ч.'2 C l$t CxT}-TBDJ&ڐIQ_"~}" '<0;vC+Bwp:懃G|;vB } Hq;&,ߞC-RK-RK-RK,u$IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/playback_forw.svg0000644000175000017500000000217113455362716022034 0ustar noahfxnoahfx image/svg+xml glueviz-1.0.1+dfsg.orig/glue/icons/playback_next.png0000644000175000017500000000261313455362716022023 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYsItEXtSoftwarewww.inkscape.org<IDATxMe)"P\"FJ "q+]JWR lAhZX [(((( JRH"a0c9sι5g|yܝЖEEEEEEEEEEEEf[:ef_k0t"|}c:_3wC:fT=V9FXD X=FFXB H=69GXLғ 3R,~1CjPJtOf6O=t%EX 7^cf6G=T%FX 5!%*5:R)MߛL`7!!:ఙ TM?1[33 zDjG[lzDjvG;lzDnj̶G䤅{lzD.Z6G䠥K֢(|6`ܨzBfůS~0aj=:lvC%'sHwخR\=+C-?i𳙭PWI[2(>);ձ"EϷ~zHE<ezH?E-cP"zg/T釈޹7I񯫇LWDTp JD;7p=%]ҋ޻c2ہyѭM5  8ѭ޻zzH"zoֺ!o*wu aj9}3w?2lFQ?>w#!*Ew`(}RPk)Vw?Vowh!Nw߫ڣv9^wߩZw1AwߪڢlV]MǁCrWK#GIW̊ȰJOW)I'OzHiJ~t_];!*-ҽCJVR%Rc?rQB OwH_r~zHMr~ʎE'R_'RCd= XDoPDoPDoPDoPDoPDoPDoPDoPDoPDoPDoPDo#(A*IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_back.png0000644000175000017500000002526513455362716021123 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<*BIDATx}{\ՙwnwϣ祙c#%DZdvEUnj+3 [MUH_Cgl6v΂$m@B  F{rs;#wnw9sۗko2222͹l?CWrh:e @Lu[ޫeaCPcbOgE_}iZ<ܡvz"~܀j'p`` b=KùxԻi, *~Mmzo5GF i?xTdsՔ˗ۆ]=kF6/ ^:r[.y]2 G}ęSX pm3ʸ-m;l{p3d@?.~8=>t3 r.Qs yhn9qJ3<1 rI/MA\)Vfnܷ+gusm%O_ z6[Op ~޾NUZ@mPFpY8Mr@$~ty;#*Pɱa(]h|`ff>/%#G)7WƎ.-0ǀ <[cu>>'azdL93R;})ҀRO c}}p3l!Qgtqk{jsK8mH@+*X_x|TL 1>v;^ 1Cj?l{2ݼv4N]ѵOP/?cD?+]C*ݗ&ڭ<3F{+DlA F8 |z.nczNJzR_"%}c~@WIGm iPBdL}?ƲP7_xDK}2}=#!+ =C$}bFE]~?~gy[t~'G:%TO@?091iqr8NSeȏ Yy#D?/*>+Bʋ@a-蜸Cv@ϪРxj>}ǿ+O@J?@b=:y/9L:|EO=!Y;ĘPQ`m=Ղ-nx]Qp"8sk}c':vZ_!8h x޷n8:lK_ynjhqd;a9X^! Vƛò{^w_qШC&ze( ]\FX5ro]C({ X#19'M=k˟&(?3ߝ۾ k% .\3S&Zk0|`Y{M[;z4jȐjmB3t8. >Λw|e~L7]=Eh.`D"%Р205\59 tN97l1 (\=(f8. YŊm%?k(e3E3{%ݳkzb'۵}Q}2]Zm8qܾz^h(1G$jd@rL5C;6~flΝzOa"K]+FCUcCb8 b6~6sUn;ߥ g VI !nnvZ?CES쌘Z"Zk5ȹN8%? T.{lg7mPv!MS/ [M7 . XucAȔ8ͶRO=u"ׇCPRt?I| Vziؾsyh,Oru['L9·D ">:(k@eҟ,a#ꃞ'pO~@2.?;ݾ-/Y7яM5xrG%*b;%q{b08 ?:gyq3rςVմvktkN+"*W.^]o RprG`ZR( M97ς{̀NW.#L,`- t_z(JIՊHpUF&ff4>JbsAT[Rl+=i8tM)Mdź TuA#VŪ4VffxT6㕛ES!/=x,x^uJЖ:diE]( yˁ"r/l<ѣӪTKYvW{5[Q2N;Ì8QL/Tc2~FJH0| kY*H ,*|- > )҅y@CR)?e x1-s>r5_y]~`C%#A'e XVYTX{0198a {z (8텳 O[Kq5E+z<6QcsE}=k?;4&ȕLt`;kΠ6<>l8CEloK%=N,,=35%EG%IBU"6.{n'ЖY)lJ uwLlT2Rz$uAxɤ (ۜϪ8̀!"W˿l Ql*q\[ k1xuz4/ښI'_j\yћkr \>& 3mۓxq\%e]@ Wǽ0j-$"6mo?Rxlz6;m!'#O&F;<jMZoR*P'.i:t[Ov6Wcpd|Ix,n#IS''6=pPp7a[&x``scy9u 6{}HDsNC{7iϸ=.``6 Q n%P'uyKe܄zU,f6 ?֞3Ϣ> 7}^\ ӕ}W=gdb:|cV1yL߂@8^g>#Mqڅ p LLo{3C'] fe}.,ڕАS{ksL/tC ,(#9Hjh#n}]sRs k/w=&Oձe`x 3>2{jP< 0{u}kV 37H؝ȗ>z*S2.)C8m=272g`U7}3m-Psf|jS4TXa1?kG:OSVҽ* M8I_uBwQ%3kJ 9g+oT&Rۙcf̭Lƴw}qkx5g-QR p,{eO4FV휄l=OwKv<"Cc-: ,gTWt\0 ۬A-\!.OLpoX^\2~=4 R7~1<0;ngz >йN"YN=v:r'v>Vmt{m k&,qYeae(09_3ϫmhx=gq;,-k`S'rjeod\v&kX}f|rX3t$:l2w$9:aF(Zc몿=)2'yL@f]w/cks>O#HG v"JZLۊH(IFYIasBJR13q]#%@,$ȲGMdҶ&_z/Vs<8^9>a vkCW8VpeD0:K oqnhcy.R\ҳy7?N(T}L%rj$UI"_h0eڹ{_J}(IcR]qM4dp=-7 JTB*d,=*`KX؝vX:ﱛ.ʤފO]- ).O.8'Trx~wqX{A ~q{pz.fAm{Ganeri9 `>^#Ѧ)k1M AC3}s*D=xv%ҋ'r4CuHےt&6v'2+JO=O TEmuUƆZ#_^_=yߡA* EKɒi6\ *.%̎rbtr呲纨dmN ֵj:J I>6s=`U;.0dـ={taHy G=[*6gJ`Y߶5y^뱁MEkGM+^ݘ1qD֝eO8Ҏk%AP*_II̷\%]7wߦ]8*"7$ 2zLY5!+ǚUS:n:ȁy0چepGiwFI;Mz }g3jFGj4̍S=o,Acb|"<ԝ p~b"_kFkS@Yy2tf'",&!ˮӖ9u|[+\f^ԹíPJkprEmyQqsƑ鱗䮸%LE[IL[ņi7,+w^}"y(K=g{34x|(h^蠇p/p^l%ufK2d| );_h T!l n-vv:Bq8Wo$7t'nG'z 㸙@>.:18~y::`6Y=-Zu⬹r ,L^H. ?8 IݚgWBŬN4Dh3hS;Q~)-K'U~&~FpCAW~QO^z9OK*pJC=`Qx#\pufXۆNE-t@')'@pݷ{^=qhv͆$?2|zdzbyjꠥe<!ݤ{8ii)`δ-iЉC*7x+|Nzb,>Y@8klT K5mXͺ3?#bYOl{gz;CK \/)kzGO񣫗z$pd8> ^`/iOe6Ϭ image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_yrange_select.png0000644000175000017500000000715113502206677023035 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYs.#.#x?vtEXtSoftwarewww.inkscape.org< IDATx{pTǿ^cDhU ؂:XjumJn%l3tkGikZW[8>(e)Bʣ<y'lpww9Ia{BP( BP( BGt]ך;0 h>6k@%=9`j0:eab~8 r__Пw\OzumDqBm ,D^_[n6 /^%H U`bA`] 5S" `VEՇëw\$ċ8 oǘ_ Bq셡9y[CCC=%2n*xh^ $'<fk_;ե;$-W ЌK9;.ԑ*';ϢL$,+BUU A3~DJz•v 6un6 2k&DHwbh Pf&9v.&@'Th#z &l:̄~ eKaWQѩKX`4]ۼԹ׮{Y"Nř~BEQDc].[]]O(. ,aA$Pw ׯ^q/h~X{ EuzB bЮvaIQ P("'r'L>`Rwr^ 3`}laƒ>' G0HEfӮF.%*BUk|B., aWт  *`l]VǺN;] &xėW=|n g$Q]]̟h^CKd9s Z'ޗQhF Xs~A~Kd220Fn0jmmmRə/'"ϸBCs!`08{=-@D 4VTV>X39tzOzִJkHw#T 1QdL!`zva.aLș7$w6޼y~ȝt]-Bd0DOVTT=laF$ǎ}Ŋ]KB+AtP9H$J G^.) %mSm{IGIO4ĈKYMcNۯ} ݉OQ_Vlb%?,N.6Փj@[F6mۦ">(Bf Fqa{lH!OV+qy1'% 6M.s Jqع3RRxZ!Vc:C~|؃GT_ճ՗W|hK'QؖAAX7k>>rNBi|pjW6Ylޥ+]y!1O Gy•l[y3W քYZk֮ld 6V6]֝GPz`8hՓ;WXpڕo“IOT(SM_r}pW8+sDۙMzy>OvtN')<uvrhzv!=?}&®OYKTrrD$qN.(ŀcl9-%t ILtZCtHIB`9b&LLb/m9-D'ibUFuXa@ԘZXtJ ͓+ 'iΘ.{] wYΤ8{'Eݤmd J`!:&M}Tˆ! uvL m˴[2|2kʊްbS9xW?r>f-v ZAw]US率ih&F~f˻vOֲkj W.ڶwƉQ 5Gh0rXG3D2&=f&1:;11]e͇艮f6`0#J>+!3Xrl4m+OML8Å[/m#Ձnc#]plzagFh:bYx}u{Pm{28$RZ c- >XEciUj;FO,7 =˾L|IbZ`;vɇ!B>Y(v\8i|^Iw\H|j䇃c ;{t% lbڗM") mW,KÅWľKoǷrL(\3$=6|IOcK`99KR~F~aFG?v+^!|KKN']bFplʞn'dvTp 6SmCJ;a \J{3kN9]E^UvvG0PWZĉq5Ŏp|FwkNsԦeOmdxR0ݘId^}e)@nM̞ցOL-4O hn|dzŲ@0b`!p>Z_YʇlIZY`$2'$2OLp`$'> ͽuUwTi*jNh+m_}nJlJÇHuL P( BF( M3 &}PRT?gIceAfcҺ׼ү0ǓL eh-6(: sOd`"jx1Kݶc_Vzc&_Vzҗ]AS6(2ͭU$)$f1||g E'ЯȌkbL2h1&0o&=,nBP( BP( BP( 3 RxIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_home.svg0000644000175000017500000004607113455362716021164 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_and.svg0000644000175000017500000000571013455362716020771 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_circle.png0000644000175000017500000001547713455362716021470 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx] pTUOw' Ix$.C\vR}l#ZFqvftAgkfܪrtg[2(C@ πywtA=U'ѷ;|Du˕U~/>~A_|t2ЕPN7yV(KDRCk?*jW=ԐM;X+^\մVNPマZ@WҺTP>0`W_GzZצT $,-p&Q Fk =eTP. } ..#Fa@f!|k[;NB(=(kSi U}|UOFya.XA%?p8?]݉թtnl ^לU &Aq!D m M`E"F1 l'b_IO@~}[[0"?-A#By1[< r!;@ bIk~! cF3ٖ΀OVancN@ǟÄSMX_9PC]^V|!| 'p.w(hcA{;mb+(^=>nNX$σYa4E6a pḄBGޯM{mXf́]'SMn,:%zܪ&> UXuL~AOF )w]ـOi޾zj@$Lg-rdBy_ÎYw2p9DE[; :nD>0x)=*ևߟ0XYKA)RA lϰSQrK$`̟|{=y k<&Ib_6 G۸KYӇ_ouG@ N_AN(CkǓsg xc8/ 1ߢ|zͪ=/C]T?MnM^ݟù@uXvl>t 6^@cl'0v!of #&5x) FnMY.?Ry+CR<7\"LŞ!5PE emA[ p IΌF܆O)'ڹ엡[3.֏.FP QD(Zf1 BȜ1o_ M+_4!,StH҄ |$.\ϲ_J9k.Xbчh&PA>~vjc@!N<;];5=9-Z cmj:vsk"A]8CG F1~/? ?&!!Ú?l_l_9@wcyӢpcn i ˱4Ԃp݅=Ķ/Ut^3mV̷k8. Dtz'GʗA- =,ן+ |ɥP[Zód:v?:Ļy :UOڀ8&y"rIidEP|pAh˨12\xn:6JkЅnaYq%siroסRuj‹(S7 C*x3)}F΀nwVeco蝺M$QWR`Aa025!i97y./APN΁Pa.ԝؘ^UWvڡWi v?mrm7О-X/),o )a |2> AUBzt͊W?YkLuţO;$E::4ɯkjk8d tꨪ (Tz]T ]o~xcC6I Fk3 ]!{π|6cPF (Z ^r3ƍm}V:n'bRrٰaK~;)*yC ] 9;5U+j!g?=gcҌ%pzi(޺T It ܤdW=T[N#}u(˄ܴz@@Ȓ՚Lqޅ nЕ]0vϡ<.W| U +鳷QCS <OZ!jjزJ^}aFka)A%<]A1KӤάu?=507]qpF6|ʅKu:ȇIst+"kMq9C)}:l]Dà._ܚ|ʳ.VE9!Τ~?cs8}U8zALw^2=t.l(6*q{ @[\#f]TE=9M ɒ*T l^y17u<1S`Awђbkj=MCZ(.eٳiȥOIS%C6k%0c}G[ҍ,tC)b8;|t>1:O#iB񈶋_@@Z;Fnox<n0ձ |t\.t"<j*,(^b8QǪx2CKUeckoWl0ԺXŷppcDzn|~i xrWh/G΁MO MHKo3?~. !5Q O?8ج3 sq]XX;@ w߫xQ8?}MO'rL) CwD =͸!p2~뒧 6DYyBL93[NW0L+މ pvHI"Y?@ vSRđL->@KƐkp09AAjݱ/uT!:L׸+CČXe*JTq=h*3cS+mu޾ l* B}_LQVqM<-1d{ M( ˜p\l36]`1*8W4eS4bpyiD^㈸c*hnKemC"sl/uj}S3LI{QWW'd.[ԭBdH@v}~‘:''Q7kQlۅh\K=$~'ت1#XBd 8%M|nfhwrZP1`jͽq#Ix^ٝڤ^TKO\ߩtj|N\s=cTuQxSػ舩O>OqJ>i Yr^byy"@kr8܃'ue6)g?~ !a .?/Y2nԨu}6<y"𝣦&댑r=.A]Ab osl7|gn6#"SD$ROUWTcJZ^e[ l,تKl,S>`x!ۚnW_5nqqٕ1+3qi&iN(:Z'0to zn?8fٟ;4>mkFL=L3xg R d^vWn,,ΎJ/'X^o5i&eg@Zyy_1i*=<\z!+J"ҭ{kk34޸| |q,o"md/؝nֹ!UM.j 6d+# xJ-vCg^:S7Ȉ6p .#D|87@ݖ:ܭ.b νGoa xpxStpM74{0"n.6qfkS)V6/~4@C$+kW$deee'49JDM >W?p78V'scц.Z 8k7+8b"cnf^ט]-xkoz_wQɰ6I1q׉X7&Xyٖ8:b1=߯Hn=\e)DOѸs{|5Xb{:f'-u^W\Wqً{R+hǨo .֏~f:TF|J&P#%my^akafFnxeBuv,&S t >#ws'S;q^qnX'?0V0G-g8mem\ByM\Η vq86)i͵ޭK'Dwc'OfA?岬`MIÏB7 vl9.lg~_ ܹ/!Y@ok9zc/nx{JG&E{ie$:4+-,C=^ma7Ì`=5SlAC=l\|j}\?Lުgp0mng/V]>yXqe`vu1*_׃R!qV|B{HE%]%B]`X,~@KwaNA/ISKLQjuy_eK7e}2?,Dn#A$ d VWuD<`1+!Nux/ݮ"|+H/>~A_|tt\)G."hB9U/_R$`P$.˺M&(_t 0Y"/i$oo*+dq?ilߴi{%߈/q]~ρ`/Z\> */=VWE0~~A_|\_V ]ľIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_move.png0000644000175000017500000000402413455362716021157 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx?o6){u.nN5| ==EAc7X*YhGQN|>pu%>z~#*fTd:Lǫo~/9,k/3xL#/ֿ %7օr0U5! Oy(鸰>5\w 'z>=/`P[+?1cpe]^+i/S;>Z:LxkzyњQ1|fI`3ޗafZ.9&raԉ'ֳ|pq` &k|v̈́f[b}`L֯ 奒*luÏ穏a{[/ü Gƺ__xc{l`~%i@.UO{xẅ́pH%P&ɤkX/ &-&nXE&[Ocɱ%dl3kQO-% 3q)~:L'$2D$2DtN"Id_~|b_(-!& v꙲#N1'*羶5+;6\ϴ7ץ#-w 唥{*9MKyպǀT{ ;n`?tҡznHw|]ߗ+iN`ãQMH+fҽnJ](S H%rx[a7வ¬ԫCeL}pq_K+cI8R]be=G]Yn.uq3ֲr?=z=Z,saZ ]02U2[KIk2dF4]]e:[tŎdz߸/).<ϧ\ Gp~0ru҅Ʃt芝#Mw7J} .\8)# o2 u: N's])д!2J>۞YHS_:tJ=ݐjdwm:`#uYY~2Bh^L.yηtls͜ 12 m\ /͞#v1(tӨh4ݪAp_KzgWM3qkn{m[Χ *2bh$َtJe ߀{ԾMahYEII*ee; evIŘِY} uY_.ЖԓL˗t޲6^h<6JӷD߻!dc 2 rO2RǬIe(2L'$2Dtzmgn{JTv㮩ŏʃ-?]ϳhCYۉ5ޝ& ]rGpN. |Rn\cCvmr}$S|iVyȝ0Hnԗ &:)rÿ.[:4T{_+*,XNܒwyTXǂw}0_<6Qa0󦍏:*+-. gtHJu䅆p&W>!6^}ؚjsl؉8ֳ2]1!%̋wΝh9&M`=KA#Ya=K+[zI[} R,_ t2`ߝ|;IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_subset.svg0000644000175000017500000001267313455362716021542 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/__init__.py0000644000175000017500000000160213605357235020574 0ustar noahfxnoahfximport os import pkg_resources __all__ = ['icon_path'] def icon_path(icon_name, icon_format='png'): """ Return the absolute path to an icon Parameters ---------- icon_name : str Name of icon, without extension or directory prefix icon_format : str, optional Can be either 'png' or 'svg' Returns ------- path : str Full path to icon """ icon_name += '.{0}'.format(icon_format) try: if pkg_resources.resource_exists('glue.icons', icon_name): return pkg_resources.resource_filename('glue.icons', icon_name) else: raise RuntimeError("Icon does not exist: %s" % icon_name) except NotImplementedError: # workaround for mac app result = os.path.dirname(__file__) return os.path.join(result.replace('site-packages.zip', 'glue'), icon_name) glueviz-1.0.1+dfsg.orig/glue/icons/glue_unlink.png0000644000175000017500000002426613455362716021523 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<(CIDATx} |TI&d%a aQA&uV^Ej{mօ`xoRkHlj(H&ː,|grwo93$s<}],- e e[O⊕,`+ i _ wjfAzAxbKc-!؄C^H%g/P{c'*Oj}STtv(NG(͚ULҟ{7#?07}_”Ӵ+j ?@9)GJ_4h1nseggSfyt]pjk9x>};ڿJΥ)/+t+yԼ+)+KW]5Zz$sw+p =z6?k^v*uu(K6{̔g/^ F^oKp㏨nC_/GaM(G~?>gOHͻְu9(w̝I%:榍nr\%QNj %F6ĀOWMN:}?32ԇȪx tPN9JSlHb ƆݢQ+OsiQiϜX(:C'2UErTMgloW+B>JJNNK >${} O ePfӥ?&7^XO;nc]m9'&S^Z %JxkG/uvR? C0e4+"1zq[{*ipw*J4V\d\4FQ뗮hv"nuW9|v=n[My4[4|%%lK?-G/6դуj$C/QgWytc>erN1v}LMqhb;nUUUTs2i|CM[*4іmn tv)/7?}"e:htv%h?TІևbܵL`KP俽-9PIۃ+/6mbɐdAӋ29sÇН }k.u[$ K \v*׉ 7]ʵYKY ?~l_\h_]q%L'^W5&6~2 gؼY~ʹvC'k;"z~fJ@ז g?~s½H[%x2bKf>>6ZBMɎ*!'ZORg1Kd/◄Z궭2g X?\H+YQu(uL\I ڽ] 8nIjj84ƖX(#ey7f񴗳*q[)q8U|q[PR7p:UIbxOjW ImޓQcB`mяV0! մI@ |>3u7'L9+Y$O+y=hf +:t(ݻ'QZađiOJ[kbƜ@bLNsnzA5/-}}+:BN\;Yg4wlX||?o4Wa`[Cpx C7WI}ǭAgo6#n-pgN(1ٸ* #0h4E _ `Ax^)>)pJ=v/~5}! Ol?#|Y9o~`jD*h`}_qb|L"{_ vtp_o5t|ҒARgBCgh}[@#&v޳p2'Nyu˲/}L:\' j/>ZߗvfG|5S8ŤF21c?ko/?!_H|qK!dyŕjǏJ(0_+#'U%>IXwD|&T[+*z|_?Y^F)Z=UOf@Hk&(z4ʍ9$И |X0ёbϨ.FSfYH}TmWRրJ@o+'OФZ3Ҽz K2k̊y7~ *kbMSyiA@P09' 7)2AgHRٳ}7/>ϝG$~ޓje1+ ֋+X#4wFV-#v_љm` -;0 o,s.?3lOa2_|S_ܪy7,<;:c YWK1( 0,9VPޮsv>i7LdJIIF'O0X8O&OyiKii:d'xPNUNk)hD?aK1=膲a*$A'HWGF{}nZDl3l.(,P⊊rӷ*!We7Ϡlvǻ(ٝ@ƇބެiK-_*!ܹsEW5O P)h`4p9k~}"p=;r^U~;UVY*lT̴{(jUX1 '{O=?C NX gJ5џ;&m$VgADg.2Р]miҸkrzM][k^q.zj\lWT}񰠳̚A222z"Sye![S7v+s uY #vk 8Ҩ` b-趶>o eb0K'wOovnIj?[#YWOL~3KΈDYi3edN餾,wOZߣgb;J94x`t~aD(ֹ́.c}pD7m]{?|F'$g '1 'v`_9Sa&>^9I"zRYfܿOM m1, ӶhnmIr]Χ$j&T :.]ܹ?xߐ:6yBWr :HjgOLS_㟪hhO*۶Wpt뭷-"㊸b /+h{cG_EonHVzrw=zb7o$d;1ru? v{]dE< _& QPr!wpա>qb`^#m"e:d{tM9x>7X˵uqռ"*:VkFb%wBfCgLOPM0Dgqƾh[SS#e8Z_;Jb`^ )źژi+/Zd4pBW7R׊-`߿3 X;u4<6b$QrOw Q%4y]tŞ:zC#!8 _Ԏ/c1)TЁ?Eca;$ =t1! 0p4ݮ/-{dI lVH$ܞu\ ۗJ_{7/24gET2Trį>󼚴.UQM4\8IVQ =|8P$Q֚DkZLUb:2M#fC뼠hzXYcpMMu'|n:w;'|H]ᕴJyh`5 5iӦ6* CխxkKӧL@F t_nEEUޑd1o-Zx*@ /L9kU"EgY'X?s];$(C~B@//_M4i ȥ@moPC۔Rd%k" gnAjq%zѭ |QF׳%A _&LInyv L(hfڴԵ1HZ| 6Bۆ6{ÎB2"{vùj[׋y]=wZY%L}*h([&Ijm| >)1h<0-w.\DnXn߽GCIzI&ڇj "55pUlqt"AQH\9QoM0Yƛ(:Sw BږK#_ycvNrJGڍH5Jڇi̘ɭ{HE Z2jVח&qXD_WVM2Z[ZMht%|6WMvk_āD=uV__~3me\c2lșB)qK 9o?}SەN]U!~+R&YN綅D,Ҳ >JyJX_Waf]8OJEq6۶n17dşEϐ G6+1a:Mp|"frT*Z`i:t*w#$aBXPExrdT %D#|uc-<)6`i-[ˊtaTw&>*ܜ^2L-p2p׎Ȣdjj ;qDHP "OowЃ/N4]3cdE׀ހ2>'Jy}i\yweJ}& g.ލ{EPF/q@y3%H&,cha[Ttbnd.O.YA x-sWw>XvSLlcuOO%@%Cf.zP&x]W//ԑ4V+̍ΛT-Z@N8A$* ;v /|ɛa+>¬SdFľwhTj5&C-o:g Qy8iJ)Eբ40|8(^e;M&>\SS'/qLRs<~sWp2%'>A~W1f mMKD_ RL6-Vy)^JvǫȻwiED:\SˡE~4hܹxgF`BO7b2#ƍ`Hu<\% ޲ZUjnA \LvD{˧EM)Q]D) Z~yH`t>'^3L<0{l!z5碂x㘏p%aD 44(2p13LV,8Zj[UF|!F__(޷K ]?p;d eY{{o)́noT'4~r`Rz0DKY e`[6"Zkヮ\ ;4yuDqy%HGtKa"wm;C\x\K9u:8h řh3R3pF&,ya;e7[[yoiМvsfjrZeQ?vEhDhE %HtzDZ_?7|9ꊠO;[aXWBo?J^ m1N !`py nK %F+.^SpsW( mFcnYw/wccALZpe}^䍌8>!q/ي~];IVQ=HvԧyFxĉ6wlLRݫ={[y<_FQ!7'76 vqO؍x|l eNX']QCm( uԠEAhL255 mÆb59cCn*A`'O L]QKAa lB6Ij t sFsvw3fzW/Ū`{# ܨ#GQ+@%5x 81A9Q@ZlGc :gl&gL%C|QqXiWDL_ncB~S82_-'GQ*IEek I $g,Iz ;5$򀃩G҉i-d޷MeA^H`C"\ZɴjCruY ~M1]}(v4A^/gnSP)`s[ (a}(q%޴Km˜FQѣ_^˱Ѡ^Fi 茩?,bNz%@6M̯<0VXU+TЪZ!0 k9EVECM1u )j$d}_. }N=ck¦v=OQe>&#zLcЙ4t$Ҟqq|%k) F;V/i(b~uZψ)C.$Tw?0u87dJiCd)]G$ \H:šg xY^k%H'e'S,Pa5=;?s7EZ 7 ;]9EL[bD@~Z$Xw2ORh3V6|4TۜT-Z93g\u^F"5"KF⚙B`NZ:2+Õc-ϛYaD@VZ~6pq̗ݵicRnWQG,+) ڣmK *F?`VMۂ1*T =(+/(7V,t=ܿ.c2Rbgg{h{0W5~,1ѰhD ;%qAnQ v'dtA Z(wf '\0g34_Jū92--U0+`=/pA<݁ ފAс4qu~b |:yR7ΦfYvJ$ Yw傠˘:cG9Ս-"O9)[Rʥ%\q W;֑"20z=|\?}SpRY'FӍ$) 3 AYݏ|<()\H5T4垫QN5-CXC0=ّe" [Z^%FUJb hXք,| .~Y.~Y 0J.aIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_contour.png0000644000175000017500000001465113455362716021711 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<6IDATx] xU$,$$a0ODTx4 :"D,D!!8$d!!Uu*`/I{r p/KAtAtAtA I?ҥ//tdJظ_>` c֝x?MHht 5l,aU,鈷x EAo:G?剧Hh{rɛ;rE˛ ʈ+wࣥoh=~$<<\zII)y뭕dN gxAn:Aa4[crdy3}T÷ӯ'%K_#ݺ]G 瓬u iC}iAuIOOFfddhs-Y*16pn >|Ɯȇ61xnT|\TO p2cm]MHHrRLVp۶?! _裠ho/Y27\E t-qSi&YEU̿ (>}F(ʱ :Ϥ9AZw56~'C33 I̚xKkp'}rO p mʬYiSN41LBaB*zkYד3ߑ.ڹ+iu@|Wv]yפtox֭ bT={OWHS/߼\ԗּ`D`~ x 6wfz"*ɟ@(':uէO@``C$rC/u'HkIi^e!@a$%!: J8 xȡÅIe9ʫe"zRbO@/nɼ4$qS_1ܹ*f=)H˅lBpi~p? ,➷ÌY$S'Lw2f&ɛ y ^p(޼<)),d1%Gtەݓ{ܕ>Da`ҵׇTvHpy1 ߶4;zR_|OEz c-$0ޒ7[RZJ侰BXqo!tlCJW ~Q3_̬çj9ih5Kw'͛7',Y%G"֮%1ZD: c"Ƭקv"Չ=[ 3HCUrXi7'L phwI;u"_4$$tMI?oDl亵Yism 撀CǓ+̼3ܨMx?Q"_Ok\ofC8-'2ٙ>ڪ೴5p-;C@>ڊ/G3c3LA kF)8;N58Qᤲs?dq.EK:vHfUG[aKd0v " SCp5%G432 茱bڑqk,[ylF۽qS A=,?4MKt葉S-jƭ>tv7;,NB{m^5a=6ͯdSh9~M[=4ÇF5@%jG!+[FYύן,SXMy\B*|+/Fmx ޚRJlھ}S(..&OHר~RzӞJ>)$\8S "Cΐƫ!ģ[w &͚i7xzfʻﬖЄ-pjq!sPgJ ma ["&Z, tC M5[C٣bX0259;ߜ퐡 \y~nh^KL%Z`.n:!eNni/m}vMqjК@cBKn*͚zD¨nt쌕:|īp(/8C<ĶAE A7#II!V_5+G@fBRZЏ?j+Iy1dv"gE89+||koSk֎KRH_>$i_0ZΛ NOv6iWMΨ++ڧhCxSKZ/Rb(lD[2mw(gly$dxӜC\CXĴIlDL ,"e|cqWl-oU_ʥӤ@~O,788ش9fA-x4t]]iouc>oh3],MӇ2{ӇEc؈4{:-AMYksh&CM;iɅS+Zbk7ۛeHx{։\2iBSBOw2ZsXp:aBD͢f\,м19s$y6ZNA#0TU%;9lp"tH·w c2lʗ&NzT^ aUj=!jYٗrvmF*{>mHD;$PInؐ͘xb 5KB9((g&6`4]K9!bHMZFBt/At=)־X4kVV |s;64+rabkFvX݃ЩG=̘N YcPEfE; Z!>7HkaaЕ"}۞k6ՌtEz#cŶj vZVz 3vI?8ex=V==u*o oi+lCrș)!M Y]_K#gˎ'*у UoK~ezKMOLyr57.ЙYf5atB}DjiAynڛEpX-ּ+p*PA0hAwͲח1>m"6.L_(x_Qt_,<aG+CyOZje%1x^c$ 3͜ϮE T!|, '1i[_]L7,^9cGS虵 ݈UqoWxmt+Qٖ%nQծ)+ʴbHk#Vۤ[d hwא]c n3QKw7Txnۉ\gd&&QD`RT:p@Wb}$4,& '9'`7WH~n,hd+i'nWGy 6}|˶=۳s#)}$dJwm7Z,n+ ]HrƏQ5\l [Z0z,v=h<4tbLz<$m&ީrGZl]!܌ MHb/ o&HϺyLSIwƳ>j غsfW&[ ra# 1r曂 ܐ%m4 :̛IC5I,*Wfc~gGAWjBySSR!` uӧ5oRqvݠ+#L}գ.iׂaw7B*Mtx< nCG%ƍH2XgGp9IG]t=S !C\a44XX'MOW1c\TV d,GfL7UkπeHIM\A` wth{@8+] (qqm&/ʉRRZZ"i1rڧ@瀏mD#~> :eӕ!+m;|5̺A}#ކ";R_M t ]qɥՕŅD<~?~?~/~r 0E&AIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/icon_preview.html0000644000175000017500000000641313502206677022046 0ustar noahfxnoahfx
Application icon:
Application toolbar:
Selection modes:
Viewer navigation:
Viewer-specific tools:
Selection tools:
Layer/Marker symbols:
Slicing:
Unused?:
glueviz-1.0.1+dfsg.orig/glue/icons/playback_stop.png0000644000175000017500000000076313455362716022036 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYsItEXtSoftwarewww.inkscape.org<pIDATxiP AB\N ag@-_}O уD=H уD=H уD~xιf!>y̚?v{A$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zA$zAtv9\ܻ{?Y|{=H уD=H уD=H у~}. image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/pencil.png0000644000175000017500000000225413502206677020446 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYsHtEXtSoftwarewww.inkscape.org<)IDATx=kAo VQQ6ب);46X(j"6ZYXؘFDHlmMX"{q=r3;Tml$3Zl8v'-ăcgc]kXZFJeb"p8Ju^$%KUw20ō-] /?M\3xg$+|BBW"|rߡ6+|SYT;;p;@᳥J1I83?x9~hٴJ>Cw#xMj{U6 7 ^MU`}]&?uK﷚r90&N#h= o\.ޤ?+-Ve›2.VuLx?1m|X28.3-.??48>l7\qu1ZJ|xm)#tayB~`+-3x ^H!x ^H)vR #R^gx/4!xФU+x]+x'mS2yr7e+- x]@+6\{hÇ65S}! @ߖmQ(xf<3 όgF3Q(xf<3 όgF3Q(xf<3.+t#q|JPC Q])/^Pҗ7Ia^e`j-_ OkWvL8NB.KlwOxT'^4WL-Tna}ἱ'^ _!^xgt^ +x*: BF+x!cƀoY3L|=>p~fϑ%g{˯[u5Ċx=i(f [MxbEWbDWBGW]ጉ568xTפ׹4ឥ]H)Jt( X A I Ù!`k}vIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/playback_forw.png0000644000175000017500000000426713455362716022031 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYsItEXtSoftwarewww.inkscape.org<4IDATxlU?Oo+! !+s2L9I20 7 NԱi\[Q -"ذ]p* Jmys~n\ͅ"ӗI& !(?."2P+GQ.8r &//L+I$$OV&WQ~Nt;G` hP~d$$ރ'F?ևC$ߑ30BhLdR "8V / W$#C,6 (!7L&U 5 )Q* 93A %;9 J )O%;4%+Y@+G+(:(2W9tRNS~2\y5ds6kk*q%b x/[4_vҵgo1of&u)_о{z̲ȗ`ɲ0ɿ&vlpc]8,!nhz7d݉YatZk\U4qƑRn[M;Y7]Aw[qu}!(r>B?̽.޸Syx]q<;΋AK)CL~_I_O<}o\79%@ /8y!\(EvH3x+>~@BJr4k<'tَ0Gβip w/,Q|7"-Ύg&f#zaBdj})va*VY(&CWظőU6.,5O_m 4bɛ_u(WoqX]zFEa>tݠO{O*}] g}HwR\ 4Wug2 ֧qW{\ .[,b>%tEXtdate:create2016-05-03T12:07:17+01:00*6%tEXtdate:modify2016-05-03T12:07:17+01:00tEXtSoftwarewww.inkscape.org<IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_andnot.png0000644000175000017500000000561513455362716021503 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe< IDATx[SEە/1d% Pxh1&>`bdy3>Y4Q,!!c v&F\$*[ADg:simfΙ9zNK 4t- ]2M'^O_V@1*Epo?kQyC5bF0d3C>(BLy c17._ϭ^NqKCܼz|?ͽun"Cȱ+u͏ӧ6m;ŸGj }I2peOy;sȎ=_|?vٲǖ9䑿.@qg;sh!O9s\ .ያi}CC 8WxvqOYwf=_}d웼.X+Wo,$> gU |ˬ17g98 l;ʁrȚKg̫7]_*3t;"OLC* W?U[^f]y%{y3+#;Gê߻o l:_Q xS |{<[0/󎙶 xчwk+qtQjAjn}:}mðLUYsڸF/(gm3QxaE6͸ 'Jkz\8VEʹ)0,3/t@j+t<ٚWѶtL8I/ZequYʙR'Ҫ*;H@%9|[Mi-_yՠk-uc25mqՋfiU4=G"ƸuWJ@TPU2 $6=RY薔+ԥk%oq5 qm>M۪Dru+|ZI˓-OenXL*JH}Z'ޓ, ٶ柍͕3ɿ.Q6).+Z]9-j d]NЕwCg'=G|NbF甆nYh2j'SSmu-e=JQ[@ÿt#-NExmP]3, |4}n>;OT9wm N4<rGBva[.e6εފZE֛7eL-.yX@!vtզ&4 'S5v`ٖ֕C p40|*lşQY9AmD%9voFFTmFDX C<[&"e=%mcz0E:AD19ДD8F6zaHa'FAʰ js 9_*pedZ6mMcY೑AgQmK 9_ 2s3d;+ zw/Dl7ֿ5sCaG+ xXk`f͑k(^K={ '!,w!LU-m;Sqov X}7n%!a*?m;ÿGD>;(aǢ|79Kl$T}3O# ak:aIFv՟0#!=AC8a)ۧkD=|bPڞ!6D~rTpJtб-wDذAr>Ǖ[,%5 %K^|LQ9\["i%º2BA׀.l3300čm.^?m9ڞh4͜;ôs" tf~`*Yi/kܾcr<n}yfM e#nW>MT?q͟im}Let[Zl9׿ڴ;KE(Nar&R*IzoЛemQ=7>;XDtJ!!SQ=s)d6~~a.P'@w,y'g ]2:-wtZFErM m}O$Mx2Bo hiEh- "> uk:PvF t-KZ5t^9;BzUwGI١6#M/Ce4oAQ^f\11kbBŸ84d+k5DNׁ\W@k%ox5E@r+z O^oSEtz\Q7SEUʇA6Ub-<D4HtćJWl[8Mh;qvx$clo7bKY::߼V5nXD:}lEқ0* Q[~^36'T5Vora5# 3?ŕ*Ag3_hh:j;owd6PȤ8ʊ\zjc.E\͐tmZӇ)oKQJK!Ӝt*T:5UYp~K vQ`Suy<$jyW,3|q-AI:AX 9RGEbC荆4t- ]EROBfx|IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_forward.png0000644000175000017500000002446713455362716021672 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<(IDATx}Yp]y}.. H\APZHd{$(1Kb+N򐲧z<)<5J%R劖X#٤qbl-J$E@ Nw^.8{q9_߿݄1oF6@}c}c}c}c؞ť}s[Oo~*{'߇M| i0f7@_ [._ůS y#}m7`83r{</wTwj%8WpMd[}3S-#>slLi<>8_PnoڢBuGkRݺi`ԮmCS[iϪ0Y RON5nPO7q<cu :pW]>85=W҈?每?`9uv9 z]^ Szobl Z|u];Ż_7se\OO/+݄EK@; *eF8E+N4Ty27I\6; ,6?pp'p=[[׎ h|\-z อ]]&9s n^NT&NxcByf3WN,6 7t )^ޕevvh=utE\o&DXux,aaj@ňl~sJgc@WoK/yy`sygH:VYI4a}]ţ:O<,+@;}s5y,Rk:\$Um/W|/l.vB] nnEK4 0S`!V75ד%^J._|GD/lw8GmA˹`~s35ōY55 paA?W ȷ1w__]ӒջcsZ|?mru\v]\[1]eny%*K}׍B7"?̏c w y~q?v~AW 吏;;3no R#DBߑ OAd̂oiSp%!$%I91O05dc#v~UA=X9GR^ڵZ3@G |dD2b. #l}/W@ap~ u:KJpw'mHDBE/\ |T 6XB8ߩl t,txaБpؾy:!Y="Peyȸ9QrLC@ rаC.kzEh~ZOWIA[#wp FS*[VBǢzQm5yw"?K.2xdӹU'$hen@IPGz`Ju!)O~Ӈ}\L,\wI>uopn{q6f`:蟞@j?5۫ʻ+q TT@w2;O;Q1w+^iPvq?nA瀋"6 jWJwQOS്2ёXh vŒW^7Y^d}י$Nkۮ\8tXM|O* .u'*uw-- p؋u-EqU{v)ym|b+׵ )J)b\ɼe<.C +d|EXܺXKkF/%uuq{!7ݖ6p\m;B@ /P |!JCQtCߟ} 'VضnJdBfoqmxN8Py0q5*2m6bnP(p`ج+E^D |s )f JH:E ~ufϟMnrSqx7A}5>驓`pL((xݐPv0Hcvks;\U"W/+W\j YXֶpKOw 0W`C@F9'L".;_[SЕv@{zgw˛Ѝ"թLXogM-TlnՍ4|vrB~|??%> pji}sga3ͭԓ}aRsyQ0 j]2&ѿWWjY/Y[G7Ơ'(CFLDNd͍Gx*Z%4DKAz'_ ۗ#gO[ǎ=_d TCsf7f9%xNCwv{s <`,)bqvsos:Bl?>v, ua=TsǞ:ۨ<1 vx+5ΟFjWGϽKIUJ}~ټ<]*33uƿ8Xz5AUMMb,SR%wFʔ~GGr,vYUH}>[7w!9_o9;Wvi*uIu27kOm~]wӍևY^6|\%v)QN=yu69x%MZ 3ek5;7ac}*]HE3 zz^l$䙂f:QtI HIdȶ n|vFU2ڍ^<~2n wzjʫVt%r:쿾in'ib&b.hDJILYbY+X,TI}},#4fA0~V!ۻ2/wQJ biÄRW֪Yw1JrliXI5˓X2A,.JPܽF){C泴U9xl%I7ֲ!\#6+Yi3AӛKn"˒D3 z+;h}zƬI8 Æe9)l㧯´>)%3IKH6+.{t9^ls0;|(ƑUBF-߉*\ - # cjx0=hkIʹPR:3 ӗ>qΓ}}qߗT[:31N]2#}XEAoNeq$T]z iypPU3cYig1Q$6Jwsg._08`Mװ.Kğ|QH@0aىK}/LoIfO΅1Ue*|(L~ s9 Ժ@K+JO2H{ KyTm 3'f> sY-Cb'fx^ɰ1VB$yZ{#'~#?l*p=Ԡi?~Oe9!QNgs]M("6PОi)"a֨7zfkmt0d(P=@2>;0ٝĞT1Y0a{(l -$O7rW}]1D,Nfu%nwkݸQpM1e}]k,ƨą6KWǻ1^Kj2?MN[o\8J{$I =I L ~.`Ҩ< -<{~[y\\j-$՞P1<6u?@]=G/1l\u]8Zsf8ԹO}{ˮXZ[8b\[ 9rfvSW_Y,?D5ff/Oќ))Fx w6 xZ 2y6P!c,& `<ϝ(mDw&'䁉,9hȉC Լ}276Ty7iٸ҂-/SkBzrg|F'ΐƔN>xߕtώ#pԈSܵ4Tscə( s<}RÉu Ї}HD{m? fZ&C4n-#j̀G3PIs?kD\$s e9A> 7}Ez7F#}ۛAfT^h^ϙyaX0fXM=>p S )OPߧ"s{$Ix VtlYEД^dʼy^-dJe n_ylpƻeg exݙ03?jɓL:͋IK+r,v*.+؞̍f^ֆ]'o7@xsikpv G E`q%(&^sǸ-ۑbL_}8rf}Ȳ*Ѝ l9a gfYk;Gv,\ig̕vllO9dޱ:nrhU$'YVs |!4CSz@vɑ jU((ƪee># wݝ.WǦ;Iv}Vw`}+g{O>(0ǻQjkC&O^?VBYevp/gɝÌU+ꤋOXoӭaƚZ*+r$*cdh-aóFeV^ͰaLrh't5tE{u5flrA־oľ[91RjB孵@nVVW< |oqX8 +z~O$uUSRqIb1 A7xɜMDH;fb՜{jAأ!s73ImYc3Xhi>_-yϷbJk]"ǟe # iں >~n~7i3o4ؑa9 MIQ!Y\XIr%93던gly zhiZ]4AT<-aG~]38\ (cgA(ĝ[$89`ǁS"erK8Ŷ9\(A*d'o9c.ɽ\qpINuۺak~E?:3ժy7l@%Y`M3%,"e%=7YOȽ?s\'_ܲ\hp'<-ip`A0OLC-9#$yC X=v}I\vO,<ղvbxĂsҮ{vY},qŠZvim&X%'8^pbkd+o8ӯnC,:DݪqkdF3rB2~>sNfzh-iOYjíz:0Aeq ƋlBc 1w5Pb~7]K<޺}\J:_95FNft#L#H6&Lo+LkNPEΜċJsp\Y7izZ6;隺u9-4 Vm(/֩]P\'^;vN3_Cvp.Ba*[$p=*.ZUe'F<;Ql~yaj}\Vt˄}w>#'CwP).p̛H$5@@r]Y8\.l8\w2[tdPR v\ nu"wmIwI,UOlW?EKQ% Ihg CƮZHh}'e8WݣԪ/1ꔅ#IݥZ<8ĵ&֖룢U4gӠ5\^NAI&kwi$>jqv$qvvNnÅ3N Y9}d Υ.Ju"}P^*pK:n ;8N81b7~7UFI "'##d U$H GAK9E$dtÉ"YOKŮ۾$?-!vL;F֨ODt<0*P)\b;Ų)K۽}ԤX}nܾNT'`scMy2wwҭZj5S֜(KvB223.˪pV 5hb7lG-j>:%|ՕqZ: ipaA1Ա+>Ib+\đu=LmAGNq- z)W!,4CVgtnIEqJV38jc\.˸M0ۈs7φcUdB~|hI +oܾ.l3t=s0c'9<Aܽ ]:m \oRҝvVK vg&IP}琀&v5c}7Q+a+Tc?J,w=z2j]jV,ήpjXͪ,J p{h.r;Rmg*UI&& qv/ǛM: x>\S?|^17/pTHrt.nzG`+jlCe3m9c;_]+;-DK-礍gxr)B+R`ujժgrp"r5mpVg:b@F,f1~>Jĭ]Rpw*N3ܲ: ٺ/JTju i (JCauUt/Zlܫ/̍;b5,<[ʥB(̦(iOTW1 s ëӖuW|[+}cN滋%;eY8yRghٞj|" E-!vrb~W}ryI Cw&c|{ 8v3wHH';९*zx Oߩ!iz>97KDg77cmëcg+4"յ|M@G?jN=?{ld;ଏڶ7%9tg8q@gXLk8{Ys\$D_ [L4OK=*bDM]5|N S|GmA C%NY9tZsk ~ /i^Ӓ.HM\*Z`vC"s􀅀nl&m U3ѵ`AG)oZ>/fC~D^>a=O7@[Vc x<+sgv J{БS,vlG{gG<+ gIA:YS} ,O!6QͺߟZ @GߪXߏ'cNpՉEl>Q~;`ff(wOE~L_XʸO")ۘb#9 muOF«Y5Ιd7F 2g4m~Z<ÀE\-fo ^J;?"l^JoWg ?/ü2lSR>^:A6JY;gbB1> ^`kߍe7FNm9Ȁt!4!-b64x2zGBsua2w&ԛ[7Mm:82DGkzuV>'띷e@9`k}T=v-k@U× `P.nր= U S)AG<gЧf?T[?d%ޮ2IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_contrast.svg0000644000175000017500000000374313455362716022070 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_and.png0000644000175000017500000000615213455362716020757 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe< IDATx_GnJDzV@ Uۨ *BH'Dܧ>p}@@|OЪ>EBEdO"PlPI5|Q4_zwwvfw~;ghČ & tc1ݘn@7f3Ѝ)cuyй\msuh7TGuJ`;WIo@W:bBqv@OvDEɷ8W @{yPn=X5dD<x+ pux*^nlP ܧi]X9X( vg&qU.YKVkllMQRˀJz s2_ Ķnnx@bP[zM641PӢ}wIGnjmedS Psڎ ׵͠NĪKXd x='})St ]GtAথ"wu(*A2]4Gumz'AWmm;%Yj= "l=C4?tLt2։ҩ MCfR֠Q#ڼUೣt*myS{/ѵ3}jTTBKY+&,5cp»v; ûx+ J1ȅy U"j7]ήt'iB^FeݡҧZ_~DP7ejpUe|m#.-tu fC:CϊժumyCV'.co8뒀FO9#x:5n|oN,'<¾]sc?ߍmdOs7Wn'O},DLsd2q5g^~9پK~vmg3Ϛo Ӽk3Wc񋼟})ɍlA,y&[EzMA?賫{E7?Ⱦ [_ǟ (e8"sZPj Eeㇾľyv xBvm߂DG Pez J`IR%*0KKþ*_ 6.L/B+}=ϥkl|:$Pl^*W{YndگgCp|ћ,' > t[;KHU=AƒU(}1_>>{T ""Mm ҹ޽2a~%⡨5*oA 7CߊvOȋ*#i;gUPӂNݸ.xlqqh𪕸BR'F ->pn+z?aJJkD;~%7_T9cC0`37Wwl56׉~y Er"[k V AtLca[F?z-zܞdR-B" |E%7W~;J&@8K9rފkv/XD֐tAi'G7~nPNgcIlinBJ['/ǝNt,}wڂTÃkyԁ_w9uo^dmZ>%iiGP=oʭZ&t#ޢB)u?e{)=&q;5,\Q^a`p@ :p2?3ilV*<=DiZ*~v/C4)4Z}"{,ZTOqծ;JPOv? ['Pu84jЧY/g Mz]^<&9 \AED:tWøpwK(ݐ!sj;O6SQDקx),k{'JlA.yRAUevވ@&ԁ n "e ֘͋ޞ쭳W7|וťHZo`gF1C~k7[?DvUW:J$~R :^JSپrؿ [/N.P,Ƕ֊+}^M"C炶&\/WqxlݩX-"s_xK|-nn&jyVJ4Qm{CvH},VZCdG-n2vϠ;t_F$T>ٔYQP'C!*ߒ1v7uiN9X1mÃFv{w Mz{e.=V/9ٽUd/w40e8t .(=Q8WйPCI7E-tW1`7>=/ ,lme & g`40yjwIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/playback_first.png0000644000175000017500000000334013455362716022172 0ustar noahfxnoahfxPNG  IHDR}}%gAMA a cHRMz&u0`:pQ<bKGD̿ pHYsItIME ,.IDATxYlTU- +*(4AnvBQ4&>/bhpA0BF$ QJ$(*#;-Bڡ3=ӷs~9LC7CzH!=CzH2Y5yJg2RکvƟM }N?J*x|f'*0N Ldt0[(sf3UQDt0,uf=yT^:MN+Oz`jYqr4LE)f`GluW'N,1>%N`E~:MF"4!w8M37o.9M3Y*dpi:Qo!v{z[LUdsi:`ZiFO鴙uti3k( jF۩N_L=(UI.gjQ1*3{zf2jn4)sE^EVs|֩${}8d6aJjїuslQIP-ti9L[T8 lSIF>^PbΏ*/$rˑҘ=DE%99M~W~w(`~ ?r3b`' L`t0ߑ:6Nlf{rkr9{B>sK{|4 m{\rќS{kF #ϽLQ.dźKR/5Kғqd!=GHOғqde%tEXtdate:create2016-05-03T12:07:17+01:00*6%tEXtdate:modify2016-05-03T12:07:17+01:00tEXtSoftwarewww.inkscape.org<IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_tree.png0000644000175000017500000000161313455362716021151 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx=OSQְ͑.2&L2&⨋7`W1q0:Xc/-[.?yr99h4zq% tA:H 3d%B#6Qv?U<{-݈cwBzlGxb=)H`Smj8A tA:H 2ň^M}2F}FQ]Lڿ=VYF(Jݥ//.ӷN)-\6#yl_n-ƍlL{'tA:H ţ%"{r#vLK!YD'Q=^"2]ҫfN#o< tI t\OHtAz8$ӃŐƩ Kx\okcH?g&~1˪2fZ1%Ҽmyub"P?M{NN!=tqnf޵w٭IݿsJwub6ȲJOp&;wA:f(:,{wҋ]XΣI۱y\܉c]ikҺ5Ԯяn<}w&Y/Xz&> H8ӸO:?Dƺ{ tA:H tA:H t_\;K d:H ^Ks~SĝQm ėvДIO+&|X;*ӳlOэhV@xjGCA:H tN: ?F|<IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_replace.png0000644000175000017500000000464713455362716021637 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe< 4IDATx͏SUOG$2 @ǂ a0$jbBʅt•) ~tB@4?gA(e RÜJ{N=/f(C<==Z`1D h:鄦Nh:鄦Nh:鄦N_?X*d#^^>*Zڗ˥U%״_e}B=4~$OÔɝ8:$tl}(yNxFOzJvիԺϻp#sK-ݸ}~8E}Tizt'v޺aܶ)+jU^_>FU׬S? W/ԗgT.B޵ctDm.|:WZ$ %ޙY-[ET| Q_uv#,F)O M7}UTyhX=uZs{}*MKӹ@fg2h<[תosZKO﹁7]/JRa/>AS#]Ï#~ 荗9| G|_Һ%vxf/_SDzHkݒgƃp,t fqC1ˣUa_eQs8uLL7;O;m&.Ч4]Oa/=Nt [zq: |Al7ݭI:=L8Gۡc}&:4'3 [ saBbAϜUuOsz"=|\xB(@#蚵i?GbAW;׋%?y]QC 1OYgN?^7]3M$bi#aR_cs: 2G#1$N M'4=0g0WI|VlI|7sttujj9-0yBtZ.6 =Oy]ȕAbAײDiAW#bA׊M}gꚣ1ۋt{71bԞ&6g|o}ˈ9=Q }%-ѭ Hti9Ցht^i:̘haK)ţ3!'d%ۈ(|Њt߼&):t/ + &M/=ʹ}s9t M FM FS(%Eɾmo,v4u΀N+8+uߍB0GY$dA=?Cwa4%-uO S9mx- s{V@Ntttީ0曳u" SZGځ1JMt͒e>8&u[>MRL9%~wӁ߭yJj姂?GsA5Esݩ>w,=?݁=3.<Mw1 =3܆験Rff#eGGv9ӻnΘ`|QxJC~jC<4܊zF~͊U /=ҝ0]_PulGXzWpDKo ti6=VI1wz8.D^O[ܶZw\{4A+JvXƄm"Ęp_7 Ѩ΅GdUlùӦk: hTe62}dx ufuv^qJcLo0-chcFq6 ӣxϖ/WwUH{=F4A@Gnd( (DtcQwߘ0ȗꥣ|TP eUyى2]XaۙFO'Jä.D?\'ÿ}|&O'!t$iOȾQ+@ތ82N|/uD;{QIj$${鄦Nh:鄦Nh:鄦Nh: .IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_spawn.svg0000644000175000017500000001023713502206677021353 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_move_y.png0000644000175000017500000002062213752534424021506 0ustar noahfxnoahfxPNG  IHDR}}l%^zTXtRaw profile type exifxڭu;# ބ?FuS+33ȦT;_|r|Qog}z>o>^<x ;ϧoxÍϛzׁ^/tf"y?ϻ:Py?ƻϙ/%|~"' 㳂t~2](zZ ryGk?Kw|.# .<7ğN>V俾+?\;gssu=f"_e;:7g2߅~7ve|Oל'+Ǹ},1ɉ~=WQ󓌑E}KhaJ&kqoʙq0Gſ6L+U,CoEBy-_鷟R%醹rݎ#onK<>-N,2` eg9XIPg>D?ȀK/cٛ׹Lq>4D"UM dŘ+5SH1SIդz9s.Y K(K)kkڛo L-jkwo:'3Ï0H#2hOgƙfef_~Lʪ Ŏ;ˮ~N:2^ +g2iYdRnr)tGefRe(uRc~%7cO*6}j_Xrd*c]՗; CBL䠰FnĊ7UZ 4[ 0\ nMs PirYvǹ>֡XkRɵR-&pxxvFsX$כ 8Ԕlh>B1=ymXMhVMxTu>Euz)3s!{G%@9C&c#lY01vg܋c6+ngj0Ƞ]Deje8\hV˳v\U&myۘiÇ4c ԫMk2lQȗ~LBnINi>:wu|Z",*Խf:)9PPȱwK6ʣbwTUbȊ'pMafm=K!Cn5߇$GotZ̲ܪ@À\%CۑY.0S 9NJHURڴCKDj;p~-rdzB SNISZŕAUP ]|ؠqLMk ؃D.)s¹re{fi1jՎ*,!QUU9[n7 ~:|\Pvm"\WCqX^*R7ˏ*kVq4F."jhiži`MRsm)kK\+ZB') ]EZMq|B!Fhi1ؾxl&ϋ@%<#@dk>(얊x?~29NU l`.=LNI+̭7rXDT*w`.0niF(-r<~ّ'>Uj6iwY,PPX2bvt Ӣ zN;X5{BQ,T]yn6B΂@0\:FyrUY&@t0[?L A<֡,=`SUI$O1L;A\MATV4xC-r{йK[z!o]R8с0 =Lax ZZEӇtA})fҲ5l6=pĸjf*e !SQMf+@~HӀHi0ݥh jT2DZxnr bv .M ܭ]R! 0GL:`jxT(j,)`gЖ洠 ޕj0Ԍ&XM)}ǞF/=@AB&1Zh@PM(a+-`ΰ&`R4gs۵U0p,(<Hp'IhqqS,F>`5Tu "&nDZ ^ uUCH4EDx|u+hx*M(V5u}3 aUDn}nY@]e oX/{h%XSuwM벘K0ݫ8iAp@/US]V &F: N*P[|'b ƧT\o45 I8堗aPZbxJVoJ(|ڠS9 4+p[Q D2I= ,4~\nȣǚ2(d"Z2q4^L-ƗZ||\ UM4(c!MDDYzmO' 10{j1EI/,˩+@+Af4O@H8kVedz|-sq36Kj_Mueځ;3k6;TRmݰ`\Xq MJ/(84CCs \ ǰ6C7U Ę$XBc/ U" ~󾔴 bK6fշs`hy"J&YE[)pB #JkB&F ğ8R퀵,[K6{2AABD'H)^PPK|QqP, v٨Ua{0%ttX(4E<=`+3 zJyKx$ƝdjcEάnB: "S4*47A,qoyRB1"&(HH |%Yʓ:ܘtF´t1ғi}xtwq"@;d~UK׃%Uxw?+ +E, n\`5*VYW'.$EXbax_!;΃b*Q.p Xc=IL|Vj!9mG%M} , '@? P<C`hek+,z9vحqIQ3zGivA1``5m\7ϐ=( Q0ӈKKC.9١n_iBQF6́O5)9wы_V-ZJF0mPuؚ $oHm({C֣MrnQ>iPy"+N# y;m!]^O!G^FQ{dF#H_CDO'F_FR'riTKՌmwgٍheϐE4o(V6 J9?ݼ>9Ąe#Wsxѣ=GC«!rZHBC཮}+ Fm" 4*{ f#8dyaSi8qpP7v*ʱl#8 ذqĖhB( 8eI8MWjHa3`1xqcŔӨ V);/vQ`7ՖZЦg!eٻ`/0T̀n5|H(L>bEt\"v̨ 7g:vt2Hg ϕmi{#FT*>.Kd 5 NYBG'Zn|L?7xp!Qc&6!l{qGJ&b"ߦ@ϋ Qͬ5a7o^Up+&1` isxf^9LJz#1zptsRR`k1XuuwP?SnLR b3dp D%4p_r!{GBG~TM{M 1!x'9ɢSFЎKս㇭r!W3MسVt#_ o4OWzgg;3hi5tw UԸO1eHl IdϮ#& ̭Aژ%Nv4b}dk( .Xwכ|E+(Xs~ }+:'a4rɢ"Ld& <<`c$T>;l5° 1KhK@+xJ~!Si*p9_JRB42i@{c3|ܛo^zm\V(:^h?;d 6*WG fxС6 sau+PIot ?uGN~Ng"R]4Md΃h1ajʦiUʚhs{BpZu*/6mQ=MzcSh[vtKIU4$$94RJK-Oޑd 9 ("ќn'F rKml]n#Kp7^toి-+yupWua~y'=jod)ެ{4"~5DD K35z. 6 &e!R$ ,͗Dp4.ۭq͆#9IMipYuxHHC\,]2ctES<M9.EóJλ}^f1eq!]B|BHb>'Ix=uRxwߟգLYs'tAG.q.8#' 6ۘ x83.+8*kޓ0VNk ,b "Ȩ2,hH1U#*P!9~?=[3?9&@m]QcngJk+u`ZKuK`I ɑ<~Fߔo5wns>iU88F PiArybKGD pHYs.#.#x?vtIME%K$IDATxMT=sU@B#$THp >Lbvn:;Ƹ Dg>+.83}sOEs_缵 sD~IIt@wWa4?FH0~p^~/=uB'h%ݏu 0ʿS+\nƺ[@w4U%ݍY9g]iXQnρ xHxO^gtn=ipSݽXKn/y<nTzVc3?O8ǁ bJļ닠r&v|2ֽy 㼚dQK_%݊*y~Zq*;zṢc~GO+.W˳Ut>[Rw8(q&pL]c,/9X>61@{t7bM:eˇu*b\g.X5^ࠥ]{uwb+d墌f.-[UF[(34ַj ,zC5I?ui1Y]/qG8]$E]$E]HHHT̓0˅N܄Fs4w0NA hrQ0gF`wr}dit}9~#3qw-=]ztwk#uXt<ӉOzpwጩ=5|b\ܪ<͊̀G#gl}wu$&Ol]~ǚ[_Y`?)^:f^\π\U]/\^&)>`S[nFs!YސG{۲9 7_;o$ɀl3Ga^5́{EՂrw?ƫ1U, +Cy0'kypKbTzZODZxO4j^>Fz8@}[בK #u#4-;{׮5l7,|ŝ;j `h{jáXeeMC[--Dw`a>DU6!nCqn؉vzwCGwiܴ;y؝ MBU]4b˭4</ʞ*=IC]j6SXrn=^٭Xk r}%Sͳǽ8ْA>q{.wz 0GqzE#ati[XGszzicL<@"."."~:*x~/BӍSP͏V Փ.m*5@Rq@U2eIpMr s6}(IlPx{}xח蕯҆_os܈=0f d7M2d_*@q;VP;d~01ct,Nź+^"pw*֝g>֝w#Nƺkw;NB/֭ƺnm 1t6xbY|q:֝^t1_7żzn^ĺu_lyob-mċa5X ႎE「7 \ѻk}v:i_`{^B0bޫX2ލ2z}*ϲI|X/4_mOu.yTY|wڮTvIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_down_arrow.svg0000644000175000017500000001232013455362716022403 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_settings.png0000644000175000017500000000512013502206677022043 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYs  VtEXtSoftwarewww.inkscape.org< IDATxm]EnKKJiyiRh|5&MLT5h &PDBňbUbEn]BFnݗu/ݽ̜33ϜK&{_ҐHӆ`^`nCn&x*{Si/\~#d/\~’ss/`L\1w%辻ao_N[ ;^i_Ex%ݎԽ[0-px XV\5u;p |q*T=}2k:Q e՘OFL+h N64(RA*L:0k]՚5v\C;(`'8ӑMCr[gisROs 7)6+`/[7xqvRQ3 Mn9i爉>w`" v5kZ\mˣׁR5*޽Di?pߨXBXՃ<}D0+zن+m=Qd_xQiNM2 pEk72 O!:>.n_젬Dcnvb ě,`@qQHVm{P9"9Iu]g:vv rB桧.  jZu"iKS3|2oQl]nhy-Bd9ɥN q.C/vz9w5qmr0Û+{D{[nކ,ChW4z3Wќ4^eސ]vQp22V6!Z]8d lO{Y؍k"9^ zHP/{D*z ȼMGޘv"۸bz֗##ivM/f<~H.ׁRrw;krЦN]8c~= |1ՓᇌC_6:JNhya :o##eJ]0eQov MRaA$Gۑ"N֥,Wp#0,Aߣ!/i;`BYvWCb dm e z;N~%賴0ݑSv"vJp(O!b{DvRc9t|z;% 8H2Um_c13>o[J,l"L m{`0RߝuX`_:;;Vo P?c#48,& tק^CK~Nc@Vގ*5twxS )dX[q^ ײ9ZpsVxQ kO;!~ۈglbGj|ysh<k^fn_)Ȏ8.'H2ƛHzS+cd\g26-]hᙇ i7Ts1iķj0py( נ(AMv`ǣVg! 1tۚ <`AsInՁ ZNѺ@EgO ]!P4BG[$ϩj7GPɤSmګB!V'-e'3M[ ppÃveDN1ae %55yAּ RǁUHte=S(# (,eI]X7@ݕhѧg]< HosTpXgOv92o'_Y":z wX|d5 ,phoU^Wn7f MGC{p#f:k`O p7+k}{y oiS(7L l |n螰X(X,Ar\~Cq{~y?H.ʣ PI6nĮ@v^ d+)vGDklTt;'q9QOMxYtywJO3wOsMfb.2^~<!K'F<6C? IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_pythonsave.svg0000644000175000017500000003532313502206677022426 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_down_arrow.png0000644000175000017500000000554413502206677022376 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYs.#.#x?vtEXtSoftwarewww.inkscape.org< IDATxylW?oַ#&M[ @EpJD@VHAr4gQ6$v;nb7Ys׻;fٝy㾏e{àh4Fh4Fh4Fh4Fh4DxyXu7 ^D,[VIVx@." Ȥd`bŬ"A_uSQm}6;W5W֛^CbOoj c"zZQSg[U,tOOfA5!GٶU6 EWpj7Cҙ^$iq:48tD-z`eVR5,tAVJɎhТAv+8;>ʶ0{F~.Y1 da9:aNW=Kd@:Ӌ {xWpNwZ"ݡ#*۶/V:'w]/lX}]2F=-Ex*ڑszջe8ptdP|9G%Wɠ2P89)9At寄Q^ _9%Wɠ2ûAt ok $#=9~%Wɠ20r9L/_,] rvF-zddWa'GgT} **stF-zb!ÜQ} *_ Z]gz1 Pwx}K's:8^Hd*Taq13@|~kNW}|LhCkZ4l&C 'ŸF>AvƔmIvv@svKJu%PfK:k^ssX}s5K,T̂d s1|8k7~*9Utۜ g;FW|8oDʟ/4Jɗ"O۸"yW3w 7( jCNuPVUpX!J!A]  LVU.Y]Si&//{T_ -$B Dtryϒƴ/Q|W)5HF@ɛ&j&e%qdxnT? _1- bكwܾ|d e\~"Xqv|I_?%(<07MqrlHD 64.UxV ۂKl&~)1 /"˹507t蝙bs2=d@8;LHXLp&/# Mxg|Tlij1kJ:Jr&<"vu,&x?d1wK 7(r~7hCƥfseeޝh`udEM$ }n.{ m4^~"pHUWm5>m[%g~Iєb k7k&đPQɇJpX5wZiMuRM!o3p;yqzlX FԢir.N e n?q7Y Ԅ蘚9WHdm`$l1KqR/>DdZ MM-撀_{tL=x޸]#K媣!~S\^U]PYW1%<~9vy1sp8$VVpWm}=,^ ŬUGdRPnүXNu;:$Lc ͋ҺK?^q*#rM6 n?Ļ3SlYd<%r7j[-FHnώ-Zm8cgWqjl8WI^lU-D(Ȑдluk% NMi (ŧKTz8nxBnZ,U_Ul*pyu3\!G^Һ}x[x*o)!NςPItIu19..NMp2%[õl2d }Wן "txDlV̺$d(T<qGm=P4bON%߁/!KE߳Etke3Mqdd0˺NyC 9d ms{ѨM 8[[W&mx L}!8rk YМXU]3' 0|ZEHLC' image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_replace.svg0000644000175000017500000000706113455362716021643 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_xor.png0000644000175000017500000000721513455362716021026 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATxklǯ&x %5Tlh eMR5d R[R66UUR[U)T5NU6x"E`-R(I4:`mm _;̑Fg5wϹ瞹ܚ|>OB D&B%J=z(!P*~s J6Ϗ%Rq2ޏ?}3d#tlРx="):#m~W^-zCNR[ZtnJ|Kнr(7_}ۖ|5Ǽ>Vt]f-%GI˼Y R?},?o6֒Edd^=''f+9a#I]S*:^+K>!JGoZ[4#oK.\K!JЫ-r$q50eEm[Nܨ-eϐw:{E ce# 9K^l*ZRڭ;^^ 7v1 c~v9ge޵g?FOn˕Smg>ts 8{ϦvWLY6S-q>!xG#ZӍ-/ɡc׽oHvŀ?G.iCxkҖq!b3Nt>.%p_>wir݁ttɰ^:I(ı:}a0{&_]3A3ipt? L .uӎ 4Kq /%$!H;t3y,ӲJB蘑ݜe\d9x9b=m._CcX5Ђ Hk gٷ68./_XyZ1n )!kViz'xD<2v_B7hߝ2}+P:] M$mHOnGF 03l:cɣu<..=tQYWBCK*0!q+^CǓдߕObƨFZg'Ejstw:&-te& b EdϖZ.-5!H&BƫH4+dҶ.(ڣQGxtfm<_iz`$]N-ÞzK,ubY 6VPFGO:K^-utf/JUIr@Y8YX|̽ʑyt59zd+YNg7)RF3Je,)-FktSsԔ>ME5)$pԕ{dcŴz (2`\8]tbAz\זȷ66ʆmO S[*Bz;6˫ؑ)cwQ7$8%/9;]}Q][yT0ku2MV,V0MBraiRLOcͬX55 xbU/?c%FӞ4zL٤4!OHW Z/p,_yi|C4˃ EoN}{W ]5F[9eQiz=n÷~Bi\*{ [Ft tp8!#ixle΃ i\iM74<:VE RNcA˭.g3y:2Ai;vhZMuhw۩ZT2Vב380VYԽ_d;]ttv>nۦx, $N]cZڢ9ֽdnhvh;-j:f+P.54<3?f{/Vp0p^NVnWu\gxU-.YmX 9K Uރ=5#{g3UѽcrRng[J=Bw<I ( O&aۓ܍ HްFذDb^0ozykj ] zNs=5k91$%a{`?]\8Ty'ss4┩t ]jcT4STR<wƒ4=x 2JubR8i#~TZW'j'\f] vʱGXl4爟5g@~p|2=}۲fɪ[ɲ+J_LЩo~udh=ҝ+7g<~VH-Չi Jx"lYb|mй1羱yb5{ / ]8)^Yiu?D_fעBs(wW"){P[-k'NE'r'qAN%ЩޜusAnW-LQiW5#`)ǴLٖ:MЙxNKNw:]V ®VB$AFΙ  ^PޅSTTk@͖RHxiġ`|;_:sXQ5pA/߱-ʙnh#AKz1vm# oSڽȻM#]yS[:v^#/P6g ZpoMvt6;AOҥNxN=}`_Z<+us:$ȝT8Utn6 &6 BI$lz( 04j5H1͞IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_square.svg0000644000175000017500000001324613455362716021532 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_delete.png0000644000175000017500000000445013455362716021456 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx_UEgn$]ݗ77jA!XCEDFA "vR!VSRW-vnο{f朙 \=g>wf~wtk)B:C:C:C:C:C*5_s sg\a-D|U @ϰfG~qzޚ8i5M_ 8-Gcҷ.֤?`Ϭh{e1gӛclZohf;p(G.jjQϽl?m\^;gzy<_fٿf-[btѺvӧ % m=>ٶўH x- wOO ?`{2m>h81|Ν|9?x2,]}Ny,cyw)'[G9AOsJo=)F"NvCSM:/\:v$OX.p28C^y)RE{x?x-}ǂ^"kΈ_ݵXJ־#έ_{U¡/ЇщE/lzOMwsf꼑yh'NKoiYv;M7&[9.pANB\0*u۴GE: "I18I|Ld` qWy 8EOf=P%ePs6XBud}Ѿ}e YYo>ʼ2FzVtDJP.J9>;W*E\tDJ&+;w*>JQJ]Xs6-@qwAj =k~|nծKi>ySArZvwQYr'ҩ>tQE d~!3pA^I+ o]gZluyRt4Jx)DWy4L(#ˑnr)khՒzR(nF~ʼv`z2e8Vib0"F[NAI>[Kk|NG'Tf>|nzGut, ^QA&l%so /YyFx'G.&5p +Y^(tSK*1 VdxZՅ]E!Tbt-FhrOЀ\~ۥ3n2/b0N^Na>'q%eԡriO2'XIO%u"sa~&NJqJSU Mf:Om#.xR:[&++22 xAI9>*裱5(TYa4EQ'} d}E)˒'V~Lr9$)ˣ>v?&,)io;z)pꠗ~MYu?m) `;$.m\umކqlm+laL;T;:@8n*xJi;Iϲ֗xix;Ljޑy5%f%j\c+c;d1KD&CQ:r9eeˑ}h.VgM=>E::G']q,RPAO0IГWTZI >as"q xdZ;ZjVx.鿝fgZ8~0"=<8.ߧGIԦ::E3\ix#Z}êr\lp/,7u 5ea)_+ƱIt M`Gz}H8=|e.rBT1u3xޢ*\~i;|zψO*_ewв@<ßl *[,[aUƩ.;y:SaB?A|޶ Ab"c u>#GNfh!`ֵ6C |>$n>$YLcj?b!C2G`5#Ђ.cBKjm˃,>[Y{й)jZ,3^ܭ7;B\=.oI!yUsfkT}z氱y28~RJwE2 Qg6fjc ^tI@xg>>91RK(P5Md?|_)xoz-kp>W_p;^~q}Nk8fCUzٝ-3kZ{XpvVg/;yv5Π**wYK-h4bCun .z8|5ciͪ4)O!u»:Ys[,Gӆ9w"1?i6QKWt!YM}5ʺI0#4i]j"U[y"KUk#!l洹K|4,M!:zժSw>d(ЪV.eo4ayID!ռ3PS,Vn-X+R{ƱJWC(E`Y~&S9 A_0oTۯ.9m'XZ/J@_.{HGvSUKlҰBGnXKk;t|nj&DN{_$~fu!@+Awt~sZc^j4WeKtm'F₽yr{~& sаP[ tKb)IF:l|1SW?<8^ͫxWήH7s}L/smZ=wS1$eMнcY~-RVe a2pئWlt1r@G#t(s noѰ-|+ؓoNVN"$N"$N"$N"$N"$O e31ĬIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_spectrum.svg0000644000175000017500000000332113455362716022065 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_unlink.svg0000644000175000017500000001173213455362716021530 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_triangle_up.svg0000644000175000017500000000334413455362716022541 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_move.svg0000644000175000017500000001654613455362716021206 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_spawn.png0000644000175000017500000001220213502206677021332 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYs.#.#x?vtEXtSoftwarewww.inkscape.org<IDATxyս?U= 0,#J$"%*v4$jỈ4%Fyq(@&FMYeƨ/a3Dazޗ({ꅱ>S~v}ֽ.dՅ.AiFyD2yDLx9Fsh dm-AEeFȾA2G2A2+«.hA -#`Oi tiaOBxt0( E}6 ",իf3@fE᥆!L&|UAb<%%<9rC޶6خk )M&`JqᅸZe Y,3:E2!`Vn%L5Z%%Oa"Np!/0dZ"DB-B'0ӟ#=inoG~Qz9%9լ(XSÂV'~ņ $M@HKYF*_;Q>v~|hJy.b`QS-ɥ kx3~LG~AČxJKY;,Ʀ& AMb9vL|B|JL*VE qMVE2$G~Z[/$ @\s ~'sq3 1ɕ%&fYn1bb$ <$l95Ģ&!ACf sy3?L]PoK/M~H 1X_ϜVq Au5~ <7oτb}<)SlW1D Wruf `ë7OpnQ52 ='V:wt `N5 ! ~ְcUu$)hs Y\]FŬ)^ؼ6nP ڎ Z[お >[Dx$5\Y[f*|$CETUi/Ͱd ` BaGuv(kH&Ib1<9 :ƍW?v^SȚm!b<[%&P 7<ƿ*8r%wkp oF L&.pK/,=i *UT{SS8[wމ$|.vn*nhѵWH᷵?%͛[:@gI14y%Z6w4ei.ػ%J΁[`[k}>P\w3$IT5y,Fk;!Q_L5D?!V[wS]/ѯGW"Il[[&8;8z n qoN|>x Df ,.`0}(5Av”7`2{u"~2wD\ pm n}ak%pe)I#d 89 b_]WUu:{.O8Q+fmc<vՈ#-bY(\_4jr~tD7Cx#6x9V|ozz`ee&?HSQ8^R n8~L0}za_L G(Gvsզ7KE/Nn>x!k n/55҂rSx\ˀm/);s>p{Ys$]^X..X._8s`"Y&`7pBĺ:Z. Ot>>wOe )ޏ 5E'x@p}?^~_CK ʸq̴X؈G5]B]rfdcSL0;]9yWiHp'w63"mnҊpvx}<.>v]͘%xav21=oWWv5o%E"x [N.eQ65K?9vC(gsIW/g*^5}*1#;ߛ8Ue:/>|!U/.n on:;a|]]5jͮ<m.a^N7,=[{mm0kUO> ZZ0sKFeK 2{t3IoT|fUإKب>njRICx=D_DXP=k9.zmmp9v5u'ɍ;җ`h6 ϊ(W&\XQ 1!USl&h\jT cѢ絵932m we#% ,Xw*8yoWk`IC:,nv ,bmXI~*lڻ* >Wl+WάYi64`zO(VћY2̦ds…{mm}67xmWIZs j$h꒧n j|I VѣjyyIV%ٹ#]'H+R"I4*|oC?9V%{~K{Anri~7 Ge>n-*k.81Hn 檂5Nѣ^Fz-u,~ժs5)=P?;^RHp3ѫ#jJ  :3} P[Z5.Wz$xfѣ\oV~Y0y/Gz""4d⡟w-ѣ2MP?nk@%^;$٩>S SG;Tv̭sMawN]*"Q !sk :~;o>?A!A-GMz6 5|]շ~Em9L}'wƣEwpn<O_0EL]1Tr&xu5)7qW)g%T=j^?ucͥhg<ܓԎ׀3AJ)|Q%cs ?=z26\2|D:#>GD FxDɏwPou 8|*^bti m1ȏۻ=x2eg C]Hf/"|Cm}J> !BIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_spectrum.png0000644000175000017500000000340313455362716022053 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx=oFire2.`D)@ nnunSЩϐ%O` ڪ|lOŗ;R`e?>ow;σ   Hp juחQ>j׸4 Gm̴f~|T4pjWTnp|pLzg|FkJko',勉}}܋,;'t:Q|Y͝ ooe-v~E/]UkuU?} ҭ|ٻw8tyɕs" *}sT]0;1?股f,9ݕUwHf:.*צz t{UqV7 6 5u/:q! N諰t:Ȩh ZZWב >t y2w%Mr=Oo\PX:ܻ_W.Fg'%ɶA@;7@Iyta )a\RLeD @/mGY,CnX mX@Ӫկ8eqw ir/hrﴰ%# tF@Ӝ@0C"D.x.=szvR8{z=^D辧LNR&{!;Bw(f2 @,zb'MCq @/yb^>k7!]k'נZ2ͫ`myЃ",f}7"ϙ:;=KtгVi7LXf3.xLV댗.d32aݔkV7πn k,طARczz rfy5=hwbud'+kzUڵdPtЋ ]w&- ]+ft.F`i1\ +uŻ!{@\1VcE]kJٰG],l~Y%׋̃uKD. z>AgybP gԛJ0Qqw٬a=qOrx w29= image/svg+xml glueviz-1.0.1+dfsg.orig/glue/icons/glue_settings.svg0000644000175000017500000000431313502206677022061 0ustar noahfxnoahfx image/svg+xml glueviz-1.0.1+dfsg.orig/glue/icons/glue_open.svg0000644000175000017500000000610313455362716021165 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_move_y.svg0000644000175000017500000001552713752534424021531 0ustar noahfxnoahfx image/svg+xml glueviz-1.0.1+dfsg.orig/glue/icons/glue_rainbow.svg0000644000175000017500000000637213455362716021675 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_circle.svg0000644000175000017500000001224513455362716021471 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_xor.svg0000644000175000017500000001027413455362716021040 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/app_icon.png0000644000175000017500000004634613455362716021002 0ustar noahfxnoahfxPNG  IHDR\rfiCCPICC Profile(c``RH,(a``+) rwRR`B > @Qk .,Ly+%8H䂢 [E vЁ@;>Va r?|I`6.t[Ć )I kZZZh$D;Teg(8C*U3/YOGЌ՟(v!9 KX Lz00OE2030웓\ZT5ɘR?Jq1 pHYsnu>YiTXtXML:com.adobe.xmp 1 L'Y@IDATx}űw])r@YBY('D D ylـL4$m@"$B O _kfvvfvggP]]U].&#< PSSȩ#Á@a߾}:uD}Y wDUXXH׿s=N>djlh uEm3~pQ64G%?wGˉnҲ+..mF]v}Q!H֭(N;:(72|ʁC `̙t%RCXl(ݻSEEt@aC M4!%/4kG|K\voOy(z:6V:PYI|UUUvU]?z(e{2 &qU ]&s+c0AZ}D^y*#Q1cŜ`7r l`Ϭw _[@}q b*"ͧ־ߞB0jy*1зV\_)-VBa6HqPvV$-LP~a_v-J4zɗ h_3ؾ#\0\~ﯰ*Mg݇s #J8A{裧Co\S{t5-C)իViࡴq˦Kh֤=oћ` Jf| <ʿoT{/u;|$GHaaQV%_nqX]cRǎd ~?,l@?| 1J$+ЗSq}~Lmsz?S^Rj߻'soHqwzf**nO}gL'O}{n|U{>ĥ$4uhY|B]yu(,ܜ'x 0- *ynهTXRBlǤ[kVR[4 hԫ{7ja17, "t3K[o5\^4jq~ g҈07u06:OqU߶c=ȣD+}œϖ*V^S1|2`+zÏk&ݥ丙̐oU $?%1͗r O(,[_z%:TYf-+m_QAq ×N;:w(-!{#I@ ?و&/-ōwޡ%K@糲ǭ㏧c~LJQ km.T^Ra@89qO>O0m\F9ɃWќ .SH{d0 syբ|^z: a-"~k6l&>~iy|{ &Kf2 L7dt#F/@)[loDBt[ɦGneyodYNJ7bl/2#F֬'XJF!hqztrE^6 U]C'\ye@fk pf.1n?W$ xMַi֜>N(rg1c~ŪU$j}5SٳioFFrS h|W-[gao<#$B7hY,s<1 @9♛C-uoK*>rG^~oFOECnm4+4m޸Jzkq0'ZC<8ؑL>#.=IFdXnnJcM<׭.x~?|/-h⑳{YY|YOgFsSvԧ(k]^};wA_Xg4:X 9QCJ@q&D>\63M6[f)pn Bnj[-ld}",py/OoF;zCZ#5~/iQ1< in,EqKb!,8^qU=n}CM㴞j+߼ֿqRmu+aL /~RO从k7"R _tN9cu.JW+hWèyyl H>܃:tb <3@R-m' 3{=t yw }yXlj'^^$Pʭ&v|a-Utȧ2_gxnc!@)-?2iu4 ̋XP*m_wlD;RА~}iٻWYܮD7 V.q>=pO c皻QqJ<w1 6q\>n.|+NTԅ\V V/oܰ4355M>Oi!T4^6=ѽ U['<` ?𚪤7[DڕB*^[噎?*nEŷ  ݻ`-|{Dw6ߑ?TɷJ^2L`@ p‚?,7o@Tkyqתhݺb>}zӰi„ lt-Ӑأ!U|뗴š~2Pvjyy8~ {1y-+.jǧ8!\0ש]lLdO.n=uTFMjV@t&8PIA+EKXÚ-#GCx nj:>y)ૼ;j0Ayt}G왳ߌi鋍[ͅoaчFKSv{ VQfN8 cPד&!}[Ņ^H:tȨpUi?u p2A"<\ϭ2&D7ߠ4xMkN27I$JyH|;vq3]t3g?/1y%+xgY ;xAO$!C#tCoޑ_> CtnuU5}Pެ3u|9rͳDs+' dC: حPMMq'<ȃB=_\}B|At$W>3QFnT0&O-Ac,@BE:Ho8iG; <|87i͚5x p;=3Ϥ'|n*@&OE; =5V勧ҭ8~z@4af_(@amߩ[ۜW@@kcN|F_1OӑsY2^xAk[nY ;YfQ'<mOEp_~t\-zm6u*ӱWݽL߽҅ 8T)eHrfm\<>C%9ĭ˼yWn{-@\dn Ke&zǩ}޾FPA? 3"-]wMgs{vxw2'v~oԻSUUq-D'N/ZnM2#oOeg֟Jw/Mo.nS [Ȣt))5rDr#Ka8|-A'X%[@/ם"Jt3ŸrF4O u/f5-A!Иho*;Lt@> zE0 ”(8m)?taܥ݄ىk>p ͩH!O9mSeaPS%ς!els?%_k-%ʡkpR~0p;/>xtQ] ,XR cF$l#J9i[R|yЊ~@G"xȂVx)QߖV{R8o04PtPD]@>BK%*fy5  `@x< ԠLpڢ|WhC|RR 07K/eZz{5H^$?p5W_-7&c)1t]0n8:ߤ}$~|>EB7?(l: TgE>S $vzQݹ{#Nƛn#FOyy%W3YBe#T;y ok0+0w?]1SݤAqu ?SKWXA?яFcJ^S%*[XCna=ly)#gr'lټ{]z̳~#}jMe=K|L9`&L -je -_(qèGң=Fq i/=hد?}Q|A _,]w|JOx!_t1|7OO ^wp}\, `۶mÃ?(uAөP뮻NptaġUUbC,G|16vy y6 .߿y+8JCXNPڻv-I347,G:%J4W/YADr ;n}:GdN!Æ˚~k KcU/(U1~_s~<*ܘ" x-4!;u'PvN{2T[lUˡ .He8~J+t=>܉ЈGV1 ꫉~P@JDwb@ ]ەb¹}Ia h4g9oҮͤxTĻGq?#X TAbGr s޹Bw*Y`cQJh Azl{kkrGlN)37MSR0V_k7-{k4?S%oUfޡ F4"ɷe,k$McӞ*MXC|" 23_UqBUi2i^JbUU`iWpPR)GԔ}<72O*jCI5L߼Inn 7Gk\'+*o@pSv ["ICM*:͇5*hgt0N"|rKrDY/*Z椗*Z,Jnxbl|x@P8% !Ǭ|ۣѻpHHJM9@N&'Ix1ix@78pBzoF:|8w`:@[IA()y5G0j`ܾǑ|UHYĦl\ K(u&+xwhahDmJ~[E EJHQ)˱cRCb/^V(f( %Ix^}:I:f FLh۪ybNt.ȀS0!S{y,S#Ne|L^| P І<ْfx>쯏=*GdpC1}\3xrSs9yn +4f 8h>O>gk^ ,]B}\'t7 @s3e͑tJD0]Tq}^|Q.̗y\4 TQ))Gx,s OGH.NgSO;M9?~?24#op ]H!O901$Вc3ϟg: kq"Ǚ9wC5O?*8F.2}@D ?ׅv{l+(H=taQLLd%EqB!Zz:t~8 a"@m/~s:Ӆs,t ׅ :522WQ˗|D^Sŗ%Hw]|~x&2gBd%`q%9L݋/WƝ+΂"◿WW/MOJwicΰxFLyK*`[qg%ڜZjveW!z>9-[^~%z%dr!~0v=/tUYi{3ϋMgN ? /.A>=."{8nZZzgmN4Iqq<~=s=w 6,c-f`Xv52QS#+3iK>\"OblEXjcYnE?C}L y)$nyR VfPg JL0}71&پ[Bcd?VwıUlM`vkh?|Y@-Qܒ=ͯ*ic>q-;FiR[ij>Kgq=ڟ>$(ThDh9ckXVNeܽ{7}gTe3q{#ETs'KGU]iJY 5I΀C40[2*UN-(h={%5lihTh B p+*W_})#6§T+qwV F s5픯锹siڴi[o/*Z5uhW*n(f%#;ץ< Z Ђ`>ǝe~L@Z9iLwƑ,7ӪNgΠOctx @p J݊y\!+WfGT'g$a>0{LAo89u Z6/iBB+-F>7!48@ЈM|VZOW"8}ꥋRٗ)NGqJ9V0 im54_@OB0cVYWxX?TUT5k]T[D!iyZ!.CH}g<Ƴ h}!i%)cR0kwӚ_NfLȨ>F&ny:"%ʲ/RZFU\֌EMU?E @\[ ͼ]oI;kTh_S/s0[9Y7Tx5jWmJFzH|-7.W]K54lx@dk~Jwz-W=vjjAjybQScێ/6 eR;_|Iȣg2|n" ׭*aw]yiB*6 *^ˁݴZqJ\u8^Jj˖h[2(`3"$~*6_%~.#y7; (/tƒ`cO 9GLba_)a(R*Il@Q!u5څX)uƽeGQ#[E 2>%WuN9n-tgwb0{)+i"Nԭ5[)Ja/RiT V{eVEQyZH/6mՂzo.JĈ K jPPءѶ)Wqn*?z}|q);'dBuBah.rOvxnlR/ fJ|%^]+p8 GYCt_FGuuTԹSs_ΤN"@e"yt\ e VLЧ2/i/|BH;Ax5aO(g%W%!g(fUC^oi#;LL}=[V%`ٳg=sEr sΡ!CXC&PvYKL{Z9T̳Ž pxu+~iY :ڶ/zr.~XQ" AԞW"4l]#rzgԆϧ+VИc;qf;s!Oua4 uAl-uQb:J1,m&5sYjч:"ʀ/XK˳$X!Խm^y/|GD=ڷg(Ν;Ӣwަ_?4rKGWnaP(y0P9J^v0O啶GռdA*u\&|O0 K>F#X6=${2P#JKF&]z-^ G IN|JOc^}*VkFp{G*8>^Jwyc@é_lƒo9U\,^=3ߠ#FPSqV~AE&ađ͛FTc-[~u<0w_*:5?U"Q Q \ҳ'<ؘ.]Jyb F=?.<|,5عc>:Q+.eI"5 ҡ˩^ 邋.ٱ};5rjW^ kҫ,ֆ@ 5,|xCYGb9µCkIr2?8m?;n8"n_a>gzi.}'?|̊g@8_81j ]H˧s14 pswMC]jqzԅ?iѦR)h|qZVJ4\Z4qY9̎a2rdX_p 8]{?'d*(~''?yhA V7yl6P\-+鷍c;!5<Unr6-{VN+.ZɒQ~SUii,PsN9uo(c) 4I}$?3]Kq XUf/񙒊|'9j^O&>܉Q|DyB94!].Hl!\AldhQ7oyW^IyWѼg`ҤItW;#2Epz-Lf[gd)mݻ]q%h֌H|!=ԥKwfcPPЮ\ JZ/-\({C;v_}5zwO)1" Fƭ$ 8;KN* t,6p#Ckdua 6pyf' h&ˁK 3*pV*%\  j ?48րo޴N8q, ,֓iӦINkY,))L⛜f@wz; yS1dNȂxiUWمfjS;mm^Zp> v~{kx@ o(U< bZ` 8 8 k"68H DNvhQ_+W1;]N3K5K/|/BXٻw/#y'!@etpq5ǍKEa$fWzs05v?kPjemeKl_sW-xY>bʦ3h*+fۮe]i9Ogu^[ءuϼɈ3C|5U 딟a4.be_Do[}?2ѾZ֣ 2q q@,‹ @4 plOj pQ'y_Kp?W쳂(p ^W17WCԫzAs.l.r-u&DYTcx2րEpxMun i.xǝVJ>!(ƑGГ=Au6Py:ʽ>u*͎QgKCYg1tl@An3(mFX/}aj"2M~6ťhYȋy.zᱧ~,<%R&+qy :`A+>^4\~l5&iҬAG-x5~Xe˖հqϸVvοf:$VZkF+صk]uK.,}GSNA>;iۍ4ۗ*MY |#YˋyK7gwdʑf;K.X@33%[K'kJϱT3JWqRlL<9)tLSb[*̰x~;z }wEGg~~Ѐ=J ;RE+Ei mE 8%z㯜PAs+(\4Js cVtO;d0uCDqF՜<54AT~}.W>>z?[puYu&+@* k1p˒1dt`aFm>ۼ7'i˙L™L;.S2 8O}|>_%Q-3#լHqfdک2xֺ~|'+ʞ|{NB$3 @\ТH0 @qI1} f_93g;27:5qLFĶFby)RBo+3o?+eDoPVq9M/QMY}zi8 rFDT  "QmdLn)3 @ A㊮'`y0F*ynٺP‹GPYY) ~/J޿8sFÝ%,+Ёng ;GX٭SAe O9eͱJ&żEQECmڸ=p5C} Nj V@ C.rR:wm2ض\nqpY'3Y C&s(/?z^re2eұpHXD,{z9`@zZe`4ȑl @9aQ07Y70 ԁ010v(qr4+C%2p>Gg Ȑg8%9` (pb3)3Eme֭|9ƔήQ9Vf%`x+5 ͻ5 ( WmP؉@zS4Lq2VnP1 Q3mgKjO&8Pi Z2AI[hٖE8]) F7l@}%|kFɜ|lxiG[[>۶mkRCCҋQHbG3߮s{)׭['q>X34 image/svg+xml glueviz-1.0.1+dfsg.orig/glue/icons/glue_or.svg0000644000175000017500000001025513455362716020647 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/arithmetic.png0000644000175000017500000000330213502206677021320 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYsHtEXtSoftwarewww.inkscape.org<?IDATx{ekniVjVMVtB J nfad=J*"**`w+$SBji鏷9ygܟiDMhÁ@;0h:R`gċaTU`=PJY~:b+=te>pPQK.ȞepTC5, hOFܥ  X; &bz'.RˁiTNh@!K^EnۀoIN9a33_-BοUg $^Nߝ)*[]XRG|>Eû7D夿1. Or5 r2 _ /4 a4 /4 0` $^hTNxpM$$'|J$$_/4 //4 k+b\X6R8l 9b IFOéLR HIo@Ecա!l K2̴SPp\DC;.yʈnƶӳƘfyj^^eXJ(} ڇBepH8FHicqli?Copc߅Fz5ۡ}?j{F^B<D@:hͣf90CpEhJcp{pZ;o5-akqnҰw1fR'I]튻 xͰvCjtGo0pm![ image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_box_point.png0000644000175000017500000000056113455362716022214 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATxձ 0A0 lG:WMk;zCUҫ=C/OtAtAtAtAtAtAtAtAt........]]]]]]]]С CtAtAtAtAtAtAtAtA.........]]]]]]v:K*+wu`Ǽ4tIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/playback_next.svg0000644000175000017500000000224313455362716022035 0ustar noahfxnoahfx image/svg+xml glueviz-1.0.1+dfsg.orig/glue/icons/glue_image.svg0000644000175000017500000001416013455362716021310 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_contrast.png0000644000175000017500000000336613455362716022056 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATxq0u\BAұ37O@0V jE #~Ag߈(ޖ :DAtDAtDAtDAtD*v;t[spA#KoX]jy{7Pۧ^{;xK!ޡޞCct]>a(&m2\֩_u+1%~?#~zh^_ l^뫴{G&ŵ6\C>5)Vۖ3O+jRpJY,Zb$x81>!(xx,Ē^9wpY._n{QxԹ!{Ԅ2ꆛgro^4}F/'O:v^.IP&4͝VThҼ!~t|tNH9Fq/XbF(?zھйp/ix'êsSoB/+č@M!bWs17@<G/@V8[8ͽ+d E) F"l ޷F u&ϥЄ ܽY=_7{chpDphpKDR*tb.t[ IvV&V-Sv8/z9]el|= Wn2-ݗF%χUr{.Oq={>nH˱|N/Xη~CtFt:w&;5YOKx0/`I:ҽ01yd[(`$r39H^;LuB >Vr05LI7 0u8'eSha/T0hS6F\;=y,+3h*&;-9`-͑10Rp9r0>] UH:FnC6&-[#c`h6d`ry܇T0rď` a`gT0ZwK0 E``\Xw%r00vEl0F+0,L]N`a݄&Q9ӥ`?`PH$yt@!Ӑ B)P,T juC;h : ]Laΰ X, aWpt8.ׄ[½ x9>_ B3BDx#b9*D7bE!H--2,F6#kKlFQmtJn9ߨHT*TTuT;ԧQ?.a1Iy%͠3K{'Ww/_`cpb`aab4aLbdǴ lĜIũ9Mǭǝ=#3{WׅgW_?N@F N`EEPA0Hp@O(HhJFXBOGG$HdFNTF萘Xؚ8xDĝ$dԘ4 ( 'Y6Y69>y2y#2#J8%1eeʄ*;5 5uu#* #MM7-2m1-; ],]=4;}>V8&MFbFEFr&$ SS?%3+)s"s K$K=˯T<~JȪZM-m]=}C#cӐ3󜋝˚+k-]ƽȃϣs7Oϛo_?C $ |z$Rh\CXQ8BMB_]RdCN\4GtZ OLC,A8L\Z\ƀa#cQ(!"K7&Ǧ1cffvf.=Jy4g`nbжȵش "`umdnӆ&ئϖ퍝]݆=}cOFgNNNμc...]q[=q\u\\26c< z6#V(65v'N> 2^7}2:58+q(%)2GprF[SIR}R^p+M.umizSQwX&WfbKY,l999ׯL_ e]7,޼6QD[QZ,_\^Z\򭔫4̠~Ł4쾍I&>#;LrVnaɜEͮ}/[ ZY}nQeKKaKG˶S?6F9-kwmo" lnnnmnm7p%ڍ߽7ߟ8P:h!aQٱiY9yEҵ?7B75Eww1D\+,| fh00|@VC!(_Q3ly1F0󱜱q8㸅xn"H2"by"-DrKhT)4ttS SY>8ggkg/t!as(@a/ǢbVfRFҺ2j2rL x ׊Jmʙ*.jHQ #MRi4m Nފ~!``bLk 46贌2fakWg_81+gWwϊğ>@>1(!?aH:ǥǷ'l$a' ?N|1vA)R?;;%ͫټBE%K˄VpaxKRVyndULJԚZulg ʛ[|?*Svw\wutLf7Q8wMS;Z@eZ+Yk|~-n oB<?<8:e>[( r[|΃H,Tz {B">!PQzPu? t"v!u#G@0AMq)̝|"R j:Fv.ސ'c+3KVuЗ)y+·aЎgBѧb&D'Re\enAUWQUSVRRwPԤ<. 5G06,0 1~d"iJk7}4in2*=#qiʹݥq'׌wOonp M2h?xIӊ'az2ՈՆ}gjonlYk5lIut`q)yօ%k=;{Gg~/mPEc ؄"$ȳt?g`@ %P `>" {6`X O@L2|Z`ysi B&/D 1A a$+[H+Ș(AzP;4 AG?ADHa0I.dX1 v% \ %OqAB( ~dD918D!bH;2d6 (((f((i(U((*%0>j2j%j:M --#::;*z,z9 8-FF F/&S3 .67V&VH4زǰOqHqqp9%Õĵō;{G'焗W7wπ߂L'`#"#%&EIGhLJXJ8HxDDB$AdG@TK4KtGDNCxDĊ$d۳>!-% L̗l씜\ܟ|B"%(3e6e?%+)UU!5!Ȇ'4444CpZq ?nәeMk'3 zИԘRƙMWYXXXXH?}0JjZz&6NnC#cӋ ː[;{LJJK>E xC?tz((.FpBFP0LXKHgQlQ#ע[b-8A/>(1 q>I, lܗž7o\{;R)RY]л W%V}WSSq-Lx]q=j[DPC^#h֔tЬ\ۂ2SڧVmhmm=T1K)]b]/%3zz^*^|\_P > -ڐPݑwQ11qq IɄɵ)̩_O_Tfau{-..}=dBB?R~ZXX]]Y>0ɊJ٪jZ _ r~oJooJZَܞa%umC3{wr `7oߍC#cGLJ'"''E[gg~gyEKKˢ++⫕kk?tAH|ٽe5M\ &"#_?Os8 `.?bT (j6~5BaAd K"GH/+(&tz  ͂vCg{0( SY@/æap8\w9 PD B&@"1Rd/r@AB@ EGiGrJ*jZ z:*+Z Z-$9:8%zfa˜Qo1G1ϱ(dRA'_Fbsabbcb_R*z~=£S+;ǧ/ǟ'`'0&&%X" &t L'&< $R% *#$['(LIImHH{Ht"jVeɽKȧ(Yה)]TTTqTTԴԺϨwiitiѴӶӱ=K룻wM?ˀ zSz=FFGLH&)@Z]f&fK)lhN|xjZȺFfȖ6Ύî>¡ ¸:ùx <jh;ÚdwZZYZڼڡC:d:: 0]B= D}6 ~RG&CC# K#ecQDE.0qSzS_ӯff.fmY=3G3ж(T̷<ʲڵNe#jbj+h`dokݞ>~΁!aƑ1qʉ)i™ Kcǁ]]\Ѻ SNLœݳNjW7#H8>>M~?cw~w:*y2ABAqA OGP?xӚPФaaa_ ­?DFdElDGGDQF9G5E#u_G<{l61;+;,mE|\Zx' v 5sƓi]?>||>%7 /.~s?I ΰͨ͸Tˈ#YYYlW^}E5-'7ϐ^^`TPZpT(YX8z""㢲cзJJK:J1JKKK$f=+* ++~{f-[-JʂwBލ| RQSCjj55i5˵q>}xR'UX7[P^٠Pа(8DdTt۬Ҝ%'Of*>J&δѷ}jGow<$ӥԕ޵ۃMYl|/_oߡ~:=P07(6?8߷!!꡻aёQQѯcDcvcuq ԉIɑ))iG̨̊&ϱϓ;7-,./W}}b?p5wL9²epfv X\_]Y]`_/68nl[mWm_(,aOP8V{z~yD)iYY9ֹyÅEeeJ*jZ:z?9n(nnroRܚߥݍcݫg@O 1@P]0 }!3  0 <8@ XR0@!* I@* ϐI* BiP. [h?ON`H9&39Ba7`:mp6  e0!G~\ZdBw_y ?(*Q"\^_TZ\zSfXVS^nWQAZU 7oyƿ]LZ]jK_WT`VmR]]1EԚV4ӯר] aQ1{{Ӧo.-----ǟ>%}oejjmkCimolinXdl}ZY׷nn^ޘM?֯Я*_S9UG 0( M۫oClCCðaQє1ZkǮe''('&&'%'c'ǦH*Ŧffgg~ ͙͆.`,-.}gu:ˏWKKK˥w ٹ\Q_I__Y_}z&ֻ>AaQ_7 GooalinmMolno/lswϵ_q`7o%RfNi}9ŎkwON,OrN&N1NO֟1ݟ >@(ļ,B^ ]9^e]]_3]_G^\/#?~ވݤ4,ߢr>-ݿÿ3 +ɟ^ǁo H?(p[rg`76$.O/P4 IDATxYy7W EBَd9UQH Hr;"7E?$زM,Y$Ewp}<93CUusm $H A $H A/4M@ $H A $H#H A $H A H$H A $H A/ $H A $HKd A $H A $ H A $H A H$H A $H A/ $H A $HKsss7ݮύ7h8.LӄlAӴ蟀)q]vSm`΃ȣA60|χ=XysQ4Vx3<&PũSha D&a%uQ|7g "3< P;g p.AVinll47o''}04Z}_][+_]#Cu}x "`h9;H$E@|K5@2yhP.Q._''h )}= ${g:M N#`G݃~^|_P־| $xG(W*ui¶mض]_ >h$f AGM)C`0Mr w] u]AZYgl _-k>QPVdfii䳬Lå;\Dž{$H@i5@t>kI y74 h@ZuСCxg044{0 tuuX[[j@uR)5-v iZDO>G}/N}ęZ RJ08pmrrݻFP,~?D!x5lllu]yttt`xxtZ*ӴsxM{d;Vauuoi0MA tH@6皦q,,,m:t\GfffiFGGaC>0 LOOT*?Q @Oo*|`33 .] o!Nƍr <6Y$Gӻ@2@=A~h@4]F}K}A ~mG>Pv?@ zA?gxxg2x dvwwc~~8uTk׮E+q@n+~LϢMR t?ߡ/ @?P*|ez ~jR<i"J3;UIb!^Md|ORG[? ;m\^@1҆G <_^=0H Į.pg077F;ɓa}yYkF]xuho|Z 333(X]] D4K~ݰ]ׯcuu~)pD8 FGGP1775NT qaqq8v,o鋎l&P}ض qڋNHVJ0::ި@u,,,`aaַp  8je¶mbd024lݻw۶l6GBuܿp 333ڂm4 ܮ2;;r.+#v y躎6]70v 4-|v:^ңc!}SSS(J}8tP4Cy82 tuuT*ajj XZZ J,FFF"]}644M #Gݍ e }"R:::8FFFa&0;;J~,8ׯcpp+++p ory`m"33(HY)+\iX^^,*4=- *Q000#G0 lll`ii ba`xx, XXX@gg'022A,,,`qqZ ===ub[u}IīEŀLҕHxV7$D |Á}x@V zp]=M AD!M:.VQVٻ]g-˂a8s  JŌ 2lj{w7|OFV/~ ͢\.}t]oX033Yܽ{T :^ye|GPVa> 7Ӆ_ٳ/cc}> ]]]x?ю((qߏ~#)x≆u]LOO?D\y׿utvvHGxyyibhhqZ4QTp mE&''===d2@R3gOܹs8q^~eX-g?wr9\t RO>/ӧOcss1n޼ntuuŞGniVR, 4M,//ڵk:;0Cӱwbm㣏>eY(0M ;@tRauuu ÇQVy;w077uq|[ߊVnElllԩSxa;^ MӰ!|߅isΝ;۶1<4* N8h[kSOa~~bǎ>3J%|Wplb"jX]]ų>!;w}~iܹsu_}LNN`ph~}O׽{S8sy::ڵkno_t.ġn| yN:7tRl{{1"z-ܻwFFFO7,m ĀLPapgWBg?.ݻwmcii 71::wy׮]׾5`zzN0]L&agyBZO~|x簱wbhhof4dKM;Bt\./Ǝk(Jkw}O?4^|E躎J^z `aa_7obrrR:8z{{>9y$_C<Qpq~ d2  mmm!ރ8x7022 .`nn0 |ᇸ>`tt/СC}`{{;:3Dl{9Эyݻ˗/^}ss~#: ͛7W^yO<_VF1ؐJ}+++\|q5X\\P,>~ 7n4x <(V$h'zt~Pmh[?e-VmI AჀt3 $H#gw[4f¤?٧]8 MO^@./,,`eeSSS R`hh==QH4;v q\x`cc#ZO'D6_}xpM\zkp]G*h2T*loou]jAn2 055Arm<B |+_Va,--ayymGooK/m|2* rz{{7ob}}aDppi Jall W\Aj&D:r4`b&y8q"`Hn7IY< Ξ= ˲P(099jHܹڊgH• |ݸtc`၎Zx3Ox^[Hdg$H xYvOI$H9C2}> @__:;;̈aN(XZZF>_` 4>|0N>^ 硣GGwXXXO>|>J%jJaUAOOJRZYT\Jq]aZTnOw2j[${{ Ѭٳg1>>`gEϗ"Uq0j?KGBCqV&FRATCt]:\-twwqenk(*.Cj0 J|VVVWWyB>ma'{rrn… ꫯn݊tc~~ϟ믿z .]][Ā m}]j4pU(PTOȑ#Bݖ n:]"P,tt ϣ 0CWdJ% lll@T»ヒqX\\իW a6O?I\r/188cǎEmK*mۘZ⏍p}Iヒl6k׮app:ݞE'۶0+΋8Ѭ# с߻1>>/… 55cԵ0 L:;q}8qn\M011`ǏGeEela}m T&1FKԅ\y}eX&49lmma}}Fݿ(f,|fmcss| *pm8q###ڊAsVVV`6ܽ^dR۷octt|ehp8혋άmћ}၃7o4\<"{j @۷,..===X^^.|I;w.]BRV6E|p ۶S}蚆Atuu… =+jxQ(ѣ{..]}}GE*+dy駟?mY)wfff`6P:~"%ěz{{-LfFAv : Os ƕ!2x P1: A <>c]z|~\a_L?M gsl:7]בd`X:99=g hZ|شJQ:t=b)iRgFA|> paT*j5=zQ'GEGGGd}mm ę3gp:u'N@WW* 6660881m :q9r$L7 IDAT099B)`rrl0M<677Q(}-Ӣi0t#,бәAӀyaaa[[[HRFGG*fggQ0:zrBg3"loo!3|r P(hGbssT ]]]}6޽bh5 ]W8b1ӿI~6Nmcuu5DxJRZMee}}}FT :0PVkˑ7<>,..< ,..bbbB<rm9ܹs++xꩧ09qd99͡T*gűc0 0 #:{{JB7o&&''qiX ܊ J%=z|A`mmaĉ8zh4#ӠAtUp}F? vkzPYa&K%ɿ/æO[a&r>; f @&iQ_겍Zfرwlr9zYD3]1;;·]P.WB]7 @CԆN躎j ۶fte5춮0 #OL聮fRF#~*IQw'Ѷm\t ?#rV#J!SEAkJϯϕRWUC1; 2 3ī|ޑBS4 |ӄLālA(&q*V!J!=|Ѡk\BiQ[S6oA qeNO A mhms ]`ڏ-eAsP,o?I`mmׯ_= tzi2 bJ%#,:8e¢yyW_ő#Gv'AΑhX/;o 6Q!m"?DtV[twڹ+mb?w__4auz( bH8Cӄ\=A#v3<[ LE{ڙ:?[@~W h z6.oQ#*[S~v9t }Ymۘ|ߏ:u OR4LdsYtuu#6+$ZT44 @|AC'Hde} bU,r9\ݍG44uA2<4 r<,3JRZؽFQtX+e F<~3A $Hpϵ^\.Tʂ㸨V+Rl6 ,% Z@,Eb<XP蚎T:l&L&<ZX*u:{`+,]8 ?𱻈`+>a@5{bã !f9b#/! ݀eZRaLq\cunYah_b Pd$% $H A5G$BIMV-I@mgFiW'1MM>z'Ov7>A:BrPV}9nR0L sQmx 4B4S&rLt_\AR_ ʑ2pC:c!@VṏWGDs}LeHACyZf\>(m w ؉n $H '+WTKhC zK(jMޤ%>iԭ,;-\׃^mS`"VMVu= D[Ǜ RhWC\eϵ¿fte ]oVdJdrZ[5}r Ÿg_b8f-|/Y}*kqv,CcUVU2_^okدli9Țrd[}gor[)vJ2=(>8Wj^mUٶvdъj֎8U*|zeszZO mviFZЊ*ҿ6zUSYbjRU9ye dಥg)e+'8oF(V'dcMǣVe 1!׀`rf%U~rko381.y\eWAʶlf:FIeլ\aʽAAY^öYJkL{ Zf1U9rv㬸.+?AIe,{Pq6fۉE\{~A{giyfU!}+ߩM`VV*N5]9Nb!k\\U'$H*f4%40{h*S%ʶY}rCY^fUKnJw:&kAɶWQu\{yxԎͪ{?V,oL?T4VLUjҹKT1'>T:0lˢݤԎlVn5~Om׎?~U 8׶8yx.ڍl#WNS;%~k'ƵV:ƼV,YmlPnsq}?rهeqӲZٝ]P_r AT n :n]h9A//]C8k[ Zᷬ>>'J>#Uٶf*d*7qm5qیvCin^hj]վڬ,۪Ac3A踺bfehWl;m7ζ+fqv/U>M6+O8+l Y"k_4flUe3r(~Ͳ{y~rc~^~}בfu ZA\ k}8~v⒬lյFl[͍ǯi޼PS!F#Aa}PDP,AkРZh`Onу נG:`;q2%wdWrψrjmtԼl;Nq)ftӶe#dFǗqFeyWd| @z?.MUgB\_|8='[Y+' NI@ȗnɖ۸1lQ߭l3)d*_Y%[^"8_u'ͼVmVfjlq:!ɖ^ǴN9hf5^w;1[eqU/ C ܁^ezeڬJ/UKm|e6+WqzeJLT7rjE129l;^ul6sq^r͍3qyqX%ms|"^>[#Zolyd25ˍUoGQ^fUC(Q~6Kitt]vT sN)\ ksU*uua'p t۩7  7W0s3,Ays B( ] J{7{ pݝ=824cӇ^r<ەTpFh !-CeB8TeU5; [U퍕9Gx\ydeCqPllTf|ʞUv 2^J&3,aZ+o xF(yҪl)=TO(-ʖg{WVlVs2_,hPzTtz^z.[};'CwU6+ =mR񙷏]36.ٌy+<鴴7w/qV%Шq$~r|=к?/,^ro~(e4Z2Pq,+Ge#جLFqXdp/UdP (6-7^Sl^,RlVvlq۔,YQ^?<jnf[{Ye*/幬mӬ~~]@DT\d< BsجvmV8|6+Q9N<)4$0 iL>2K8(Tkk3U1UV.Lyjl6NLNN׮V}JEOSel?V=EiTIEkoj'Ϊ*^Q[栿U ج.C\U6$Gr3ׯ{x.SO=gf1}[||]v P\ǽ#6og0_Decܾ!>! ‰gJ"ECqm ƞ:?u Y\Ο]}L=-H3u] y`}:哪>zc9fΚ(AO&($|3LxBS˞WJh-l?Yd|r .>?'.[[\`]^T[-只-T~ؑL:8:J6|^Uʮ8ݴ~> Kg(MAHm*u=vQqM(-1o8<1??I:t[[[y&z{{Ắt`mjY.OpAy=7YM+c'\1h!:|w23>Xv]?`L\2_PYNO&[zcY ?OYKrR̠L{?us>rcVz_8/&VGQuq<K=*Pٸ~-7N2q#ᏛƜ8޴bJ1tbK|jOrjp?@qg:;p=#S(LDЖSE9w}ҁ#c5TN,D_Ɵgqozv13+ '3̈́`CYt3.4' |VLA*:Fg10Ɲy;f%2E۹qx$OOWŝ,xFAS|ΜMUĽ|lyY*}s2يyr"iT}3^ia:lV%[^>+o ~6k_Oec/>^~;6*YeY?sssC<˲P* MӢ^ZEVC.apZ leRX,jq Aڬ(람TF*Ynߋr\Tse[:,.fy.%Yno*r[1fb}n}q4pk6l ur RZ($]A?PZő ¬脆׆]u]NXD÷ʚ >t݃rrEk]8rC3_w \ `e *}68vh*XXBo!-FGR'$ 01(}n< $`ƓaO䅷O%[NføxFuIhy 4ryv_@#wxeHZ/Y2lV0\5ve2>Qr,őR6KW xٴ(pB! }?:~y̙3ottt4ـKU!DS+6+2]]zo+1,qmgi=2}9fyy\,+\֜n3*[ |m+Cz.[Uerͪ|,=6Ͳ8+>F/2&nY:oC\q,5v8Tq2ڢ*YG%[x2Mml0nl;9T6KyD!~W} !0`3y 0,Vcód#f>3JA3S@#pLh@6Æ:ӳ VLY3ipU h@JHgA8h3zTh\7TWXOiV\ydTeZ.w.A _Ń76Q]֎fN+8U|NLD}2=G41ྀ%+W&WԶy`uTk2?gq '-Kfx^.ܹÇ7$]t~b+:| "j*Ǒ&¾e񑷁.eM\+5(emVف*>D}*;YgS<>yI*㡠w[P: f6}U4S]61}Ug4()x]?Or( x!h9T6+_3U*7Pϲ8+~4y%huۉڬ,7vG,Rي-f4~PT#R~9rKn}]ܘOu&myL6m]@G PkkA:;=ow"yHw b.KH33yf| GZOul>H)N5,ty蚁_ZUL[8J'mMp%xGdJ+72NO_Ҳ%dA@'w*Ni9Mk\f'[G\F\(2O%;z.Y>zY(U%g*93utFUlnk7&`w05qLJ顿q:hN Yx.\*cZiQgKs4qڬ@^.}!SlYYbL'|G&A>** 2 |GVï~+?Dwww ijj(@OOF1x(W|򅶁ˇ("Au)[f<)/vvgǔw|*o:wU^C?cdz-e~8UY ;nޡMB9dV.UJmgzT/W6M@PɖSԟߩ-hg-r~X3{Ty.W齪Yx]2_e}ݜnQw\%o^=l>&1.Lq,MO;Cgon *\ .dz߇* ;V6Fz˴Y)3Qt=}~X,|C4v^ׁeїSGÓ9Lpy:yĸm8G$sުdN?󄛗m;O(g4,>J/Y 7~A5F\2yriGRI]T<vg7e~92'(TZ*1qMMzH8۬j`CFeb;cǎƍő#Gf2)S*`a64MC>Ggg';1rj^Ȗ>oS_ 8;6O^8H9=~ 8NyF˕sWn'fyN q*Z#qﲜ6^`OU,ҸԪ de^Zɍez$ˍfmHYD%{O=+i{(8h(Dnr{d1VfT\J<*Tϫ#+Uzn'*]!j(޽ }m }=JV.cyn۞R%Bt]GekՍ)Dy\ZЇ| NYM8@OO'zzz`&-,.i,kA; I&LQI*@9VJ>;M Ul4L9x77fe%CB+k YRcTߨ#q'}H;KC\ qr_ne99N6R~lV 2^pyy'[i8q!7C{p]VJ*{䱊pd2uLmc{{gϞE&[PT/c}}zzz`&<ɓ'ۋ;w޽{8rRV9|U3>Vl<c]6;^ٳ4hyGqkfP>}q6+++.٬~n"tg\|Cg9Ox-2\Te 26q8UVfqqX>2)sYX"{^^ χibq){FefVzPns2_x߿z/3e<++WWlwӄم |D26PݜnA t( "1i88J,b azȧVt.T1(ipvA~1(ѪcR(Se၌CNztȂ 7N'|xF$@%KͲJwt6YJɂ,V'fɏ<{s|)wpDHRHUCu?-,2L_ ++Kz /)=tGȖp}ׅSCi, R> ~x"dq8,B&xyxp"Fy F r Z,d٬ :+ACc^tܸ,5M4@,YH":*q42xLmUd} i3G|'uW#ZP畾<S ?VbA<nߪ߹OhfC21-&.7>!g3u{ U2q!MIx?~%;3g{/,y+`:§R LsUB6|VŊPy0TeX [P2M{(w\Zu6ੁ8TOG'i1Ƶh0ho3#-b{1pͯ<~8A6`Iy]y ,Su?$K{qǔ{,:,ҽ Vy'o!s1~= _~OG܊R{ʌhm^k*m6VG;8B츿y֯]TGuCLC;Ӧw茷Hq+5Feu<>k.yxmy9-O;1yV':_ ZL:R:gURV*iCVBjwttPϊsRPu.:.!ճ[u_ZA8k΁By/ ,@uA 4@v_"W0A tKc&kjb{VvwuMT.dQ<_ g뒷YK,w bՃ6+ 6NyF~$>x,yn~X.ބjCa3\bQWFlwZy{IJXk{ұmcgxyS<,a`>KOe}:Ǘ])v, KyR6~GzV,Sc~(WR`B̊ǻ t>DLGP 'm`X(Pdt*7d#(b"wF 5+ǯ0B&;߱d fF V#c~Ȕ+b~a 5"&f<[~tS]^=i @UN#rbWb#~$Aְ`0noYXٱ A`܊>^6e4I~@ JsaB`d,=%+q<+]{@ṀAeV:Fy"\܆stV~o!L܊ieY`'RMLR{"&nvyV&qw>\80neW|J@g%M R^㞗1&t=g+7t/^%|y4/=b =y$1./v Nkr#ȯ%nx:+>Ak1<䃍#ogH~i7 ^_yq頼| _ĭt]3ӝRHV:U~YJ 8&Ckǽ,?H;FbYӍ gb8宬|'(^6e"]Wa_TOY%xpa5n#PKz wn or:R=k*Ik U')i g<k:$U|k ~aBG{'>*S5^C+oJUOõAw5G=|JpDuH玥Huy9p?mcGo(itK+D- Ƴ 4U~Si;$ojNt#!B Z[["-ߨT*z$%%A;82h?k !$bw'xGMM >J455>â/ NłB!111槷B$ :M X1 ؎i!p!|Bz8A+ţTBZ ,UPhB~@yy9z=z@"9܌2@Q{Bz<>¡酣Շ6@ vBH S!͠e`JPAFߏBHx xqfYja<&G IDAT˅$&&"999~@8@!./6cXz"΁|q@BQم'mhq`Nd9EX˲gx<Iq[ Fİtq hnnFQQfsw}oT*@!D \^m}xSJa[xӓjjuZ<><!$'r!11^n[0 hmm`8ٌo>/5 BzO{ϩs] %n"777A> iG\YYY}vށ^ !2"N>?T|vv6Xӧڊ hjjN {dzhZ=Z904@?B!=<%p=r,8b+Ai rAEP{q.|>{BHeYnǁ8`ذaAkk+V+v;Z[[/)u!v@58>o": àRj~‡yƌ#Ӊ\҂zCׇχ͛7cҤIZ$.$x_PYY χt HIIO3b86 NR!#KPg[ 55 ~Lyf[nAVV\.`2vqYl6zAK qaUURٌ!;; !#񠹹Y;0;v,F#'HKKCVVx£RPZZ ՊĠx^q@TWWĉ+0|px^T*9zp 466aj$%%aȐ!cǎy"99y@1pSS z4`H!8;<Պ\l6477#778uڢ"V}Z V @dBbb" !๎Sje 08}4::XV̟?6l@zz:f̘Ʉ?3g΄dš5kV. F۷oǞ={PXXӧ@?'v8b 8z(.-$'Ob͚5Ryر={tLrJ455yCmmoff&L& )J@믿Ƽyv駟"550͘7oRSS:ƍq׆ |g8pYYYX,hmm֭[iӦgΜݻ3f p|7HLLĉ'0w$AyfFz3_ $>M%AVZ0Lp8ɁV Y'ʏUWWݎ ~0t !x^/5㑞z#GDAA[l۶ ǏǸq_`Ĉ8y$\., VZ˅QFpݸF  !!---繂ax^x^x<ucc#a ͛s碤_5wNM7Ӊ> ^ĬY0vX3Eb8>oyx^ipvn1cfϞ>NV-޲e Z[[q#%%EJr~,T̙3Wkٳg hllŌ3hc2e ^/>#7zvjŤIB?SӉAV VKS$^ _'}Z шF:uop])Φq5jHH%; L!kg|^8Ȧ78ŰP͘6mV\Jܹ&MVʼn'PPPAPTT e466Bb+mZ JZIEVrfa̘1`YFZUUU _XSS|t:z  ++ iiiғy"|ctC F#233V#==]Z0g"0 L\O7h4JOa8`Up̛7iiihjjBUUMJ'B?׋LBG[X&Apa "@E_0l0;j+OW[l\"n/DMM 6mڄm۶!77fµ^M67n? @=Aj|O!%!!zIIIR=n@p\o=mmmɊ?H!$6LY](wxQfs3c`OQNH<|1X,7;Fbc64aMİT- a;qQTWW`0H~; @F#G"++%^4!IOgq3 ! III>|8YW5łldddPBzI+ U#ÄxeF!00L1b jjBEX{"Bq⢚nq`&BH?VhB} `00jL69!رcnq4ppZu:j0PT`^'N@mm-.":t(ZZZp!466999Xxq.{vǎy'F"~zI>sB!BR{!;;1u].85\[ommmĺu`ZqcҤIرcZ-Q[[V8Nx^TWWrСC5jS^^W^y%,Y3gBӁy@nn.nFw}x^wB!B.|0(,,i7AiӦ!770 pp8p8hnnݻaۑ F\a߾}ԩSQ^^łP__d 6 (ֆG8v'==y$|>*++vz epAdeeè8}4RSSVӇ7!B!ވFȑ#h꿜JVi@FNCnn. a0W_}5Nw}uuu1bRSSΓexpPT|xa00c CF,_gFMM !rj&M@kk. @3B!sU ;q9r$L&t:f͚3g``EEE8tj5/* 3f̀Ċ+ֆ|TTTuAah4$&&B ''yB!BULY](wxQfsэ3`█;z;K'zp:HJJ^ESSNffqN'o6,r)|ss3< ˅$  χp͆i*++qRRRzRzɓ'c a=:5$ea0[y5b 4xaMİT- a6Q?#J!B>_L ==߁[,X,}3L0Lg00|p}Dz!ST@FF:Wq9(!XZRRRB!BЍhO1ɹ`R7D!B!kB!B!dB!B!@B!B9!B!s@q(..KB!A3ł%!B!t@H?sqs=X`222rxB!2p Gn7^}UT*,^SN߇fÈ#ps=?~7pQRRKbʔ)8vΞ= ⢋.NÛof,ZeeexwQWW)S`ѢEPT~t:Xt)v;}QF鷝;wSNE~~>N>uaܹs= eQ\\ ՊE!??<ȑ#裏P__CZZ駟b߾}:t(/^4;Xv-RSSQ%O| `# ɿxAtW^0[i/z=nft'3<Պ7xH$'' R9rFB!B=aTWWcΝرc{ӧ"-- O>$L&0 gϞZ󑒒ŋn0HHHYdcF:۷\r ƌ 6رcRxeY477;.R&O <ϣ/Lb\XVx^dddAW՘2e }Q$%%_a o0i$̛7Ǐ_rl6jn:uJz\ӡ'ODjj* L&jkkx`2鐚4NojoD$,ۙj5\.xV~cY v#B!Ad@/(F qlHHHjGzQRR+W;v, `YZ,beYvs0 Ӊ&|'Rg////9YEaa!{1,[ +V@(bXd0(//DŽ pB,]qWc޼yp\hnnƶmpQ\.8F[ UٶEBB8C[[RRRR`2$mxJ!B!tՀ!d\Rj񠪪 MMMz!g Hj|âEo80 #=hiilt^dggCb ~,1vX:u h4>" q*++qV+xG^^~ilٲWlF~~>RRR0c ̙3o 8c54)QPP͆$%%HHH@NN4 SNI26 <#777.A!B<ѣGAL᫯BCCv &=w̌F#ӱg$$$eY x<:tUUUPTp:`. %%%HMM֭[rɓ'cݺuXt).rh4[Macǎ!;;& YYYG-[t:YӧQRReeL8YYYp88z( AAAz=0zhܹZYYYtBZZL&JKKf `~[@mmm0(--ȑ#a61k,[8tn:\r%HMMJE]^{ ˲8|0ƌx_VJ!B!t걟>ۆ6N7AAfJ GYYۇb߿ӧOhĮ]p1L2#F@jj*aX0tP$&&Ba֭z6mZ-aʔ)hmmNbAKK T*n Ӊ `̘1:$%%zcǎ޽{}viqk`MMMؽ{7+:tX`, % IDATRSSzm6aƌHNNѣa2PYY Պݻwc0͸jp8~z|hhhI0uThZnc۶mػw/ 77WZ`ݨu8P}!T#³g/Aeep%PE!1UWW?nqؽ{7Ґ ٌl߾{hĭ* ٌe˖-2:.e5Mp,, RUЪC$B!da*BË2n 9 "qvF_pUWB^3H/g8E@7VlcT*$$$G.9'}u!YoW}{vyL2 ?^չeQ?&b&LBVV60աO Oo鮞,)=G 4y;؛oEHOS+J$j52"gd -=, ҌF<wCz #Ē2 ?$+zߡ).5Ղ^z ~klh֯7bkxWjGzJOã  2 E_a1 DW_z1}Cp`UCnò,FFspN*muW*~Bqp8pa2%#=*Uq(_rz(}{xq ah0*f@UUYhwd_x=/Jc G<ӕCC :Vgd@yx2t?))w2-݆ 455`0lNAvN.pxp<j`q> -@ZZzc0gx_JjH} Bzp13~$? fsJΙ?O그aƮaONr- O5 &OYs¥3fByN8MWjlXtˆǙ1 <У}~W^z[6m5ހEF׭t_}>~96oZ}{v>e6Θ~['pH jkjP[SjT|,c .EVhhPфIw>Ī+TOo@ w}]lǾŰՇX3`Up͵#11t;PmM {ިsƁo-fNIżkGTL xvJXN:rQ9Ʉ.yyCuew"n3|(z=^e3gG=q ߿-;e(1vJl- AR~ѥu]>73J\Vm&L?ů+Z:}%d* ^ʯՐTe *;-H֭i+/]TiÏ{y߶6xeY Ψ{^ؾ;oAbb"x1\u2G?/B7 OE8cblߺHKψz ر} >~ػgIn4ر,4m-~ςJ}˲XxM5*TiHW۶7^C;݆%6#yG>Ŀ!;NK} .O>,;@?o49A{oށnmSc#֭Y֭ƜMRa9s4\xDgî|هQ֭Y5g^1)Oٽ/8Uv"6--(9t%bx@ш>w8=ƨ񛓗XJpU~uW ~/+UY28Þ+~l'$$ƴ}q׭?ュ rs£݋ꪈۖ>NGYwKhX&/=h];7_Hv{Z,E)ޅǗܯ09zL*]}N<%1u5567_K?xm<3i.KZe_ir^{iG62j/>6JzǗıRs"ڹW4P3Aʯ@$!3Lҟ|@Lǽr[C'LDon?gрy:ARYq[ܱ-w++⩟>Inq 7CRŲ//l]ۗ;ߴK\inj~Ke뜫桶Oe_iKس{gLvQ5N`U_ƅu%c=rq|fU@k ,z @R<HlʱLbUjkgZ)F̜}Vؼq=7v܎w7ulH>@_s/+^O>)KssQqLNt^Pԃ gX11wf6|q(jBIWjEGš9V+|>*=p)8( \{C°oI2McxaY }vFU/a玭J"w[lٜKf̌z%K~io̝}X `'JjoSo ~_`a]R,Nh4sb>gD}T趈 1Oh4J]Aپg#O1oFܟʐ8NWG5 +_Ue~8Uv2qhهKo[X4Ge 1<{8\|e((,BUe|)^7_x][n \⚱ŕ+ lu]D^uxqm%h<'koE\wZ9鳩rK.\m( MDۺ5"ܱ u~߲W23Ws=aǞsa{5U=EҞ Y~4Z(t3R:V}u_D}t}hDdp">RHx뽏qe3VW>oK !rF=6fenߣ8“h4ß.ѭTVC݉C!YYx鵷pD.7Aq4?,bh0-Oxi[lRfÏ=2N3V<3HII~^zZ%,`8^F|}k}ƍ1P%'W+^_d/ǟcTU=3 6 Erӕ%ӵ%edd*ڎ|hlx,6mɥǍ[ O!lN^|9+z30yJ)h `Oh4_{XQ Û~CuyȞR[cxO?ê/= ZSbI0ųL;_v2)ɍ͖4z苕JHnٴ!jϴZ-܋ 'OUtX>{@i MFEҕd e*Օ! J v M?A n!j g~ kf4sJjkF~P=A&ʚ,V Iy,}-,piy_Ɛliz"v0 yt۰ez̝>gonL<_[q[шg͉_saE搬뮈 @i MFD<+ G, ߻#FF >=pϬ[WWC]4Ng鱶G{0#>%UTb4po.-f[ֺ=\_כwi__u5AsL8_oψg/p٬epV}ðooq{ t:]1Lۜ7BOdpО [^ g(\f soEjk\.7(͟w)=%b?} |w1K͆x?iGཷ{|Rj?Yx\I<4.MidHVvoǘ)l-ޅ>x/t*n#_oڀ>(};+mp7 `0d4@ɣ5 K#2lߺ9vY99~KbEzR,"c, x+^*d%'BRy|n5X 71m tm55o^7^Wiis+2ݦ,Q?q 7G]aqwC~#9Ɯw> GB Sr;B{f0lm2z(p hi@$1caDmM;ס7 Hŗ\/o-J% *J7<$6EG;[ovݷFCptԎ?_x}x`0# qz.bxOݎ/ 3/)R7[| =^YYҔ Dݟ0 C "..^_{-0LbUX1H L_ihp;7I[逶t/ FEdQ *8DQr(BAAWmt/MڤM<[DT<ƫX2x{˸k,Lfuzr:/FPpe S8c-nAA N.T%4,)W˪O?Ahx;@Q?"%e^ŘjOxy븷q KײSѷ;...:?Xǐa_ :u :q88:~ݫ\wDC1[ o u(:w_j7k21 =fQ\]Bmz*kv &7ޞcǾ~-u\z/O{"ѦTTF5_`+ữ>^Xx$[֫&''aw_cxnPl`z^]pNguh,lu%*Cگ>Ӛ5rRS08V.5G#eID#"^}^1w!\K,b$'%" 0H99cӔ\^&v@bsrvƣ3u<ӆ:J1֦w~:˜>y\gvycx6k.Rđ!5d4,_=xРnn;XYw:fΘIόő:y5?cݜW_j_W]e%w'\WibFu0bUMׇ>2D!\?Nԭ!\Kl+/j|Q|Sѯ IDATT8D н&%Sع}+z]ˈr+>hּ9Μ>iTT nȭKW`K= dee"++e={]YO}?9O~i8ov؊sgNi^^Hױ%Mnn&O77w4usí[ols|%% 8m!*-BE|ڱ vl3X~u^efDQQէGڊ.55b:L&Y>3گ>ӺvW6#Fěz2]b$`bnY0p6mQ1 l?Nf3j{{}U0vPE? iRqj,JKRk&3H4nHQ[-ǫX/NyǏAn}nݺ @!;; )¿ۢ:!E]4`9Vu"V*fΘuG}DDB{z,g PII1v܆*ӪO:[b)23qiH KWL\NJ_-)5W  C(6Dǚgw@Qsk2S`@M {וr 5H7DR*C??*?w /ZՀG뽖͚XDطY)}}_KS&1q=G&Ms/6Ì6 +[wcCt?;w7AAdv}0CBbZDD2BBRhQܜl{o0acfkҋ`ۈQ^?<  ۳߃] u{>~]m͆~{w>|fjO%`dTkjW#j}~&C=:d8r^i_}Tkgc5F# &gP?o͚ B _p?xS16uChxBBlT\6o=ؽcΞ9%^>lnCO#=Do~-~ZJMZ>ףks hW|wfn8<7a >X̰F°{wctD 6߯f[[[XKzPS==o^sgNiݙӳz'F?~fK}^CL)G|V)K)E%nO9欯{ $E/[Fdm\ m;8k9777@zzzXOUVVBvV&''g8;;ͼ3FII1232tdggNNptt CLRZZ[)HIN4ׯ%|}[ֹ퍵!IPi\4S}tN 6 $'%"+3EEE(.*<<-|:UA~>bc/#;+ypttkS7@pHSȸ [&ie}|fBn ##h掠0iwpx=\| 9ˁ]xzz7#!Z~{}]ˁTm>5"kDTQQK1۶N'N2˱Ⱥp %[\$-9Yػ{@ވnצnpvvFEEn 5%IIطg' jsT$H][_J9sDgNa{5fN5qҋs8 7,5bg#A w9 P'Ƌڎ a r'[ P7|LwB*99Zkْ Έ Q%""CyyyW_DA~>ylM15L@z ez釟~NgNQCѣg/tpum \ܼ$%qQ$;99cQ0@z""õi?2;oZ&;; [6-L..T. E;m3(.e""Cl<`W&waCL~l"""^ |1@Fa"@"""""2 DDDDDD dp,%pQ}aҼn&sS*P*"""""hb\H$H$] """"dV" hmŝF """""7 ;(PVDDDDDD2Zv6˕ˁLHDDDDDT/ Hْydbp'"""""D@DDDDDT? ;rrYd-8Jˁ YdvID 7sm&dDc"@""""""cLFNLHDDDDDdq Yd2QRHD(.b9 Ȓ q W\" """""$Ȥ:KxDDDDDDTPq Ȓ 9-%0 0@&'g8 RlԸH ˺Q#w)h|nAM***pa:xii//jȨб \\]뻚$kV0f3Q] & "m'ݽSs<:&{m\Yj'͚{a.>Hz\^9k³FDDDu\R":%Dw"װxZ231eDw*VwssK=׈t 2ai 0EXUToM yxxbѱSWxvZ\~+鲏ӢzS$'% ZcMH,䚹Jвə!g{j>Y}uPhߡ# wfgNl9AhJJi~-Ҍ Ք\hP5!""""&:}y܇l6s{_-^@RMlf^2 N#6mIDo^o^=ۀL2qN1@(%`ko|F<>Z Z˦$' 9mqXjVZs ۰ Ȕ r$ u/ e.qʫ{n$\c9נ[%XvphYڱ Ȕ̢Mv6@YEJU9zHXrip?<:t"~mBԔ),,@^^.@" 11 FT불n#:3}NvΟ; ϠQۢKpоiSXh.sIt< Lܬ(}f|<\x@ _?CH$&yL]7!>Vj|˖goRMb\^okk-|h{38s掐pt͚{:}1S&uYH*űXS*Ы*EDq?Br>xɱzNNθs@egf %9YT*1cK٬9漷:v^ W}z-Bnh|{g /^M?SːߩsWhZW._ćje#F_'KMj}^>;IݹSyW 7g۔`wp`Za0TrDDDu2F\"Ǫ{z6C}zRR~FZen\v߳Ysۣ'"[E K 230_5Oѝ;9cZյ)4LRצG4'LVU~%/PǰE)-)g˗b3ckupR,_#`ì7_wptkuO{R ߚ|}w|D||?arR 9 m"""18̦kتxJʔ3TU"_tJFz:˫;:9=x.H?txY,,? +>/oo,^0: Ƭ9պ-r9o Kޟ/ӆnj#+3C/p^^?6|Vھg4xz6ӫ-ctSxo^G"0(Ăw@ܔ|mRS1光H&<6x߳7CB6,|/jCUkط?Py=0IL%&//Y?fy8{vl߂iT13 b̦S 2) ױ \^MF,kmCF)UזP:‚`x 4i<Ν9b|#[Psh+,>-2 [qiL{f2${ѷ@={ll*o=rrq7 |'B o=W+J:xhߡc4t DQAnQxT累*b:v䗧 ;w1OM7kDNN!m@DD$8K&P)vJJֈ5l˫PI'VjzZӼ9 D!mSЧNSןvl:f~kK||4yw'ggDj-԰AOa_2 >_VN#~np V^W);Ԧo܍mm-6AZ;S;`KS[]{ڀH@f-Rs2I",K9Y<~HJLn;b\RЁB? S "2 (.*\^$ 9)ϝefUz}ڭT:T{.%znoh]&s\k,Jڔj8%&"2 ͚#+3II7)=oQwc6 ""dV]#eXS^NPB [vPww^_^^?mn-;.B̌tRTmp7ě jeTvkSXv_5`.ݵT3"%9I`IuiZ.NGii iY{UPP(ȯlsG''zK <1)|rT1~H̚3_.8Ĵ "r'2\˜!􄣓<##a_돭jspU:xPv_lNND!ajVg"#doeJyaarssWj 'ggrssPXXͽޓ^/[&BIm{S7^e{,6觐.$kS_0~$HKLqi"""}1@f,AdK bu>vEQ=Riд}UrI8X ߯Fߤ=4V9~uԵX-Y_hXT}a|#ڨұOQwb >\uA_-|:Q3*zq}dTyyćCYi)J%^ 欹B9ScbڀH_LHf5R\X\Ddz/a56'5x ϊ~ݟ0p\6UNu}]ӹ/_ F] e m% tAz *N;*}SWuulU7 (8DxlۖBs DDDb̮krOdIYYd$KMMM :n۳Sm:6Ih{}7wL沪#Z* \p}N*jJKjJXF&M4So`*!2d{z$i-RG"F-65㋯6]LT ]HqYA1D@DϫxYi)v$G ) :Gobƫ/@xl[Ӛ@0%I ~W._P}Ug(8~EEEu=%%V9VM Cg2$ IDAT<~Lc 8vDmW]U֙۳ՕPU*Hkɱ`)W[QL{C?u6DDDbM oqS ]vDTK >^n^4V>Ғi#0FmvIx[} z l>h؄g'NQ"4</V.6wn_m\{,Ye7 Hkk_={(3Php挩j5 徿g/ ʬ\rydռ 5BIIpX,6Wc/cqXbi\Q>U=Ķ,(nrLb mFǓcL0iደ#5Q7~"&>y?Pv-᾽nB| \zEb:`qjǐJxyx6$;!aḝ `p,uOW[PLtG= xwc:"!>߭[k`: 0U,[>v9{$<5!\!W_S_cDuqqLf7aȰ(.*¿7gkP(a:/z.]#;; ?}PMhᶱ6 ""d?uN]WT &gi&" _~^ضOՈǞ0/NH|pm\ڭTJU6ݷcVTPeG4Jxjsx֊WܥF=9N过dMcfo鯹c_P\[y㯪ob _Y%-|ꊂ|JM 899c˯=f9OK"m-Dr. x x{:uf x_~={WLAx)Xf@}T]jϾQc^Kv1X+%[SR"S^1]/j)Igc4g&뗕_RWnox^s'-- nne?7q7aprv6dgeprvlqUTt>HR"4,A刏kWPPX`Os>X SvVRSiд|Z§/}ZhT=s$n\\^֭JS`ׯ_ː Ct3ӧ ta,惍eX\g? v/t@ sEشG䗦bSs. F˫)Jr;@"Mu}] 6b:KD Tk[NDDDD1@Vnf}JR-;Q]r~@DDDȢzwb HDv ryw5V Eh+n@zR nDDDDzc,*_ O!r ag#Af8Q . """""*[}An5 U neDDDDDD1@VF]sJ2. """"":b{Os !DT٭G H H *n7g/2JTB%0@Vi=U8 H$H """""0@V] ^:fHjMxSf QY!"/8 . CSt L&GDDDDD d_,\%:/7sm.Ȫ=Mlŕe2@"""""" (N2Qe#4acPVl9YDDDDDD0@V()|=$t""""""M 'J0d8}jb"> """""j)t~j`'{KXZ-0@ 2;+a9J """""5Ml%x~qn+q5""""""j8elo/7cMA gLT=H,""""""jP(  ϧ&cd 5[w [I,P3"FrMdd͚7Gpp(zWz[^^.zp.pqvoKj z_Cw ++0'ײkd׬Baa`̸gY5"jwjpdR e%^Sl:\ѽ%$j(Ο;o|Ǐ@мe 0pP4e:,\C uzKR>ap`O֠<\TTT`W_B.&<;kDx5"2 P4؈K% 5YY3 8vce/01df[R|^PƵ>~EEE]Õ'\ۧkd[B.\#Ʃ}waM;ܸār3׈2ݥkRSS0s4V%C0clLxv2z=Aɪ'UTT`?c˜tE$'Wk _?d"VJyi~h1!h,N;w >/H"CxaW. "5 _ī⥦$øz20pw9Y8,.?rDn.]e[ƬL]7!>VjƿG1e C4)޼7\\]璓qy#,"V7}EgI [ij} !?/ϝƹQZRVQЩν-vU黣 񈏿HOO<5ðФIsl9;}A9`sgp $''!aة 5y.#OV&#>WLp}׋HR%O+B:ٸ 6.+ϥ 񰃳Fiiipss}!Mvn߂ީO NaX`.[w#v $4 Vo~_Yڷtqu{ecIpi/A^}ۦߩu:u-Q.b?kE^(Jd2:<1Q( 9jzͪZ`ʴ_gɶ3 %%Xz9nlmmcJk_g96t/MŘ&vr0 F׻ 潃v*kgg!ا&jx<&-.@Q)_繦!Iu#\q7gVuA@@>\4vlBQ{OiI >[[J*j~CxzH?F233t^o1SnoHNJόmUjΣPtfΘvMTvrsspa&X0WkuTm!1ec?YmDV} @s'O?.b?k;F.8Hyy9֡͞vs-E?h|[팭k]w=1?nX5_KZc?dž#m /^мmbb\(K4v܀+j|>\@}lrT!mr<-:qd#G¡dn)*bΞ.Du V|!:m/RS1光lQgNa-vˢIɺ1@C:W^\ᘆ77w?bH2b rssjTGhv܎fͽ0i[O *G''_X꙱C}sG@Fz:n\Z]ڱUx}LGR23+ר={sT*!ˑ~; -q #E.Xs!0xڱΜokx.RE@\QN~:ԆۮƼmԔd<}۳Ʀr6ܛo_\.\.G| xz63]~Uر} i5mg!9hMJ;t|ϡwa÷_a-vU7_+>GYs*Z9Of^kؘsYs/xy v23~V7wx$VMӱ3&~$tXU=5:$tS}/KڪAʅ-_\Tӧe/^W UF\TP(pʑ.G'':ch+F>>Fؿz5UYUGc=ɧ_ ήZZcKO~Z=/6.ngNPyrZHM~!n_1jOVlGw prvFd۵CLUiUIiȿA?V躬^0Gqoju%غ1}wa췄_Qb7kbϱ,˗g|fpN LRoYgY1+_W_1h.2l;{@}xQOJJL$77뿭UTxI#33C-5^}թ "2J1B5!/)Q53vu]oG6hԭkUNbU_ćj(ޔi? V<yTI33ncOkjiPi\9k.5UF*Ք屢)_uyMnohUiqϚHd6+Ғa ې]L~o{%5%/21TGSjt,v>wlW!u1smt{߻ @2R'wNQlYHJ7Oyj}VFe;l;V 5;1-Y_$ ;&yv&My3$K Y}J^6ib(P4QcPSJrp 'ioOU UfRi&l\{蒙|>-|5Um1NO ě jeU׍jSXS' q_Z˪.P5 ]M^bU?tbd75Uvvv +P(v-mCT r㞞Xq4WBPe?//K)*Vc?1i3L96Q]~qI%ا' 1S1Oe34GĬ9M5. PRduR,`Á <;@D엟Wq2}zIo ...pmڴןc: ^^uҭV*@#>έ(J$Y-~S.k^$%B||5!aS֬_KLϡbٰRėFxq}:Xjղ7U9:V.E{w~_h._5]w'''3^}I*uW}7BLLew7צsK5Q؋Z\ Ue]ch\> fTRu*mfF2Ec֣uhXG{x`чsnZCx@)"GcU;5˩Ni5$A*OZN0v5{al$ $//o;{_CEL ת~ʧEHz^^2eeec}Ll;c몏 w.i`㞞X+9BBW.؁}{| ]a2Z×:)>Z9c,EJKJ믨%"RU&RǍihURضEk򪺨v-̛3ΜjO?|%%ED"Qˢ*PecTTTı##^@moBbBX@`D`к]UJq-mV蜪TDQT38~EEEpttz,,bطgge+ u%2v5չ"֑ӐŽjǽvYpY( pe}~hlKޱ yy*W]eɶ3b5 n<gTiG 7y 󑜜9ʴ7G_Cw#Fa۰əD@P`ޜ2 *I%SM50#++ڷ@NN6n&c0v0οO ,cST֨6֚]ꑏ+j~`挩jj'GWI&=o~/O} :u1Yj&91Eպ-B#Yb2h{vL:Y_QěXb)yYRm4v5{elϚ^dڀ ĶϚ&n7Dd'X_8,y_?vD3u}wCwU"jϱ)h֬9}s?ϐ{'KQZRZ{WdqxS 7/|}GUoIO^Tb[ˢu]V]UW]\W]t+X`EBRBI:}(2 )L\W.19d&>(In'UlǫeU{>Xe95{6bh6vܾwq6nְ#i8YʯpÊ}{ O>EO\*55m_tIzxuiL |%˥rOj+leEH%6MW]{|uŬg(wkwz;vhŲ5b:l̸Z?]uW_G~z}~Xѳ:vH4RUxj\5UNp(p%KM_kfu4W:ƁzN7!+u 7CNZpޘ]t׺1ZIrok;oOF:FwЎ۔Vs|cxlam^'O|?^NhT4]>Cݻ 4nOS|am]> M>ġ OpuU~^4.;g5Ǩ] C_ָA tK-_ߟ_Mx6VǞx[ZrvW=|9o{ҒoVa^ݳ[wQ95p?JW[Xk%loܟ}q̕ڷ}9AC}$I텗TϸwԴ4i\xyfg'rJTWUKnzf>bTW~9uuرҹǎ;J- oI'XޘՎn]1:uܥrS Uݨ{H))1>dp=ca 0t{NUTO:sF]5q2Xkvۧv@]vs/VϜNNt}effUzc>vkmRj^E>r&x=GRT_ߡ>>c5uoΏ%{z'ծ]J_usw[ iW[A=vZuiciUE/4u}ZUS񌺥MQA CzF0+r:fB5[^ӠZ2},W!gK) wءByyy Z a}>z+MAQa׭QժVz U{v{tgMilE"a 8D}Bڸ![֬VqIun{Tyvh۶ږU;wlWzzw:]UNJ~VTǵ窍zPϷkp~$w^]zE?4e֭]?p |Q+h]X8K|}t"222 l\*$]y : 4RX|~$׫7ޙC2hBP-ftC_N*D ΩzSSe_BSV57PeS M4vov֑nvæ gT6*4lx).eXe6{uDSLe^_oo#|@~OgO Xۋӟ)?41UV1OO l薳\zCVcask֊g> * ΘVla}mB=m m:9߅usgɆ޹˫ioٲ01mx&#-Izr& ׏[* L핀N_}\7o/[* (wybp!]vCQ s{.IHϬfY:NuP@K'MQ6m8@,U 107 7SR0i~TyR^S>d>룺_:hg7uOS剱Š[;#xJ50VljTmCVly/t Wyhm*+- ;t5׶JG3An'j[}ml6 [qD>>"_9p_z|vH}ҥ':Ny'Ŧ%+6EUxN uiS>O_$8MKnKٵԣo_nrsTqqA|1 q ks+"c7Nr5 eLSŠfp 4=.K=n ׂ_h/tw7W4Zc XPzyKGi->ZqRٹM3njƭ^T>mc8/~/w묣ʦz 5 $i.ݷm縴${=FƞϘW5I5N,|ޚ@׳WM ]y ҵ[GuK۵+_?8@uXpc5~9뫾jm3<ٻWWlhs^I|W{݆lPT/էq&YУWu>V<+ }xXHʨ>vz]o/u̠nWz2+IO9^sν@e(an Xi$/T;4io:'R$%7Y

[U(IGXK&9u$R'}.S8ػ ^ψbo5-=ɼc[G jTݽ#*ۦlUq9 yK?\}25퍠<j+O nӐ6J3vkT3>][?7{qXgа6JWarWգ;+wLhm͈{ooÚ8ʡo@HZBڴr(1b8 'ii3+Wͦ5^00aC juazDP{Kւ~ʘk6I'EeiB?})yMt*N745.lJK2ojK~TVG4终AjP:>=Į U$˗Ea 򫠸>&W5{/t[}*)l@+88|uxH?T'8劈>^RfTz^[)[ѼeUIJuj4u#~ CMkfo3wCtW .Up9rH\>]P+:/E$S_M-^SyEASuxWh>XPLҊ^#pi/o7M:ϥ=lfH(*U?̡_x =EX}ٻ>=Y:]gavm^Og!2$iHw.Q'XR{ 0EnMW^A,t}Wo[-066 icA@yEie==&)6iRSC;rqxayQ9R6J]Uh*o)_f@VYU_~wQXRjou)]z$J[:b7hN#zgaͮJ>׬;Jrse@ .mj>_ch@5ZnD^v]Z[JCBpj?k1MڧZ!WzԹuKkw1$P )Z5;M]_~BCҀ6M], H N:ԡ?\_ (%: >Gt˴: =r[=ռ($07!иzHK2y+fPPO;y]I|P/Jqq0]^ᖭvA=!GQwՠ{ך5LӔih @sS7Q˽z3a0 F-M ήNp~/`D@4;t{ߏúQ/iuڇ T#?@q: Z}WE`dh@ukҨڇLMۧE# 02VC40Wuڇ{Ky+!f:wG[ ꮗP@f3t\ڇ:.#aQ/4u]0ͯhGWw!竸XHDi4Y4 ;9zuҡzwOO"6M@P6l_5k֨Hafx 4QɆң.u)%)x=w$e)ݮ۷+''GmڴQRRJJJ6 MG8=^S"=Pai0 CPP;vT4`eeeQhD@3ХM ;e:2IHhc¡uΝ;+#=Sn[2 @#! 5֝ R:vLӔTjjL3@ v;ۣv߱暚_?ז|n4M ;pr ]6٥ݫcկS$[Uo%,h:7xUnͨ_A`Xz⽐ɧ7 1YZ8>߫ s M:~-]Bx }[oѠi!׵OO>#Za=zn?; #:f~9l + ]:ɩj!/H}֑7w*  ЂuleWy4Va[@_笐L/|R(L4߮ ]JvIwԄ[}8L ,f34uSLrg䙺N__cДd莩n͹׫c!rsTïsiŦ0M@#!,WG퍸sQ]̇Ywa0sn#,a͹׫?ƥTo|fGϽi{r |) mHNpZwOڵ|AHJ5sN=ܮx_ڦvOڵ Hticӿ.胿z5>N ;tATi@WG3nhDXSl`lmnƂʦ{$z\ kA}ú5eD۶O0Ү׾_osO|69æ:3٫ XNAS/?iOI|&5SG Cqw`Uoꅹ!NQG*!eSZ4v;:~HN]q5TVy?,ߍ6}K5edt @KG@qu?Dyk|Z֤O{*}K@Fm Nuݔ2[oz.OW\2~3uINꃿuݔR.u襯99-[4 <QQئڴ >[$)ixyuiʨBҳLFN4k)yDT 5LM?l-^KibM\H\47n):XZ%)Y _e:W&->X "p{zt덯3`mLo5 -N֢dI !E:~HZ*A>[i&CsW*b^Y 35Oit2) @13+Oإ[_WQз$e%ub?X3q=Th2#:nJD~. 6Cӧ=}*(S+jUyjGOm=t̀ҫL. @*%scj&,KɊD* 1(;YuE5OPa]}ǿ4PgZ,ÐFid4wE>^`|A>]OW*=)#%1 j%d$G uơZGҴp]‘ ʻ4]-MW_P<BR Cկ]*_,U[w; zmq^[m:f@_i;'p х:utVnhβT_`6ow|"K;ùW4/а@Ҡ~ ehޏ),Uv~\Ft%:gm $i2MS0"#t"֜bu[vCݐ$.>SzUJ k!0a4u _ǀ._/Vhβ4elUaIZ9IOm~ӻTcSPwpI.SkҰb8}: К/[K.՘>>n\jjDХUHGGk}&YKiNK9.8SSӻTczjp C ]|g];)_c%z8-Q |GNт* ܻzoi[OD*Әޥ'lI; >2M 5( ت,x0?l꺟Z@@ fnOm.=@]Z.I; @ .B2b3XӥCUͯve҉Ë| fGz 7U^CÀ9E+""_łUjpsh6<~ܴ m"jdJ6}!IlH^7`1>~L|n;e:d* hRwS+z|W˷ziِu/vҍ'itO_Ш;3@)+NY!MZlЊxbWg4`ݳ뜱{i#-V̰:f넟{zZÓV')V{g@3>#"=gɦ$]b'ep%z( hJ6,t&z9uӫDAhw= \Q eڕe#4D/uhx2SY ,h%`@Ft/n> Sl14FdwDú4{Gp9 0i4Mح06S:'|Ͳ~$aL-$;fMt)Ų~!4)ۦ"{g;mt,_?e6 Qd~@_ⓗeqEhpE>*LF4[D2N$ht@hp;rL s2j5 jЫWoɲ~D!4h`yD4<, , , , , ,  cLL0K 0a0D, , , , 0i4D c H0K , , , ,@˜)4= $a2 #, , , , $i2M3,@!0= , , , , , , , , $i2M3,@!0= , , , , , , ,$ZA#Z92}vvW_/hV\ CC{4"@U,#(sھ۬v=|CT4ݹ'"SYc|ӳT|&멫@sbKሩK҆=;y{:~}m$^C n,=Yr;PΆn9˥8b(,5u~ZqG!Y۟7OsU:ƊM9?TGеm:|]gVyr4绰n{.pa蝻FP-hK^1 Cߦ wj(Kh$ l(#EVdo3gR+NF鎩 +,5u~%Z`J}C@ԃpqn3%/D꼰ʭY!W_ *74Mi᪨ q=pK;.K 10v7 7SR~_obSM]d|A0v6xs~X V{@c@.蔷j|IR_z|vHn(o TLʗԧx*S+Cc*|tk'!}]ŶiI҃4^WNY1X9Ϯ\wGCYʹ++Iۮх+-YY@z^"QοL;iƭ^Ii+NЍgT0Hz/wkEV#ie wәuݩj 閳]9{*TϘ."!8[ qG#zUom|Z+6E9⪂TMu:rpy~6T 5ID m+j^94r%+J,g{֝^]ִ7Z{|atރ~}X+w_*߷?._SvUsz\+NcrY6q#0ܮ_o.ĸ, -\t +;mYANtNX*MT՟a:Piq%ƕϗ,0N=PMZ(:Xcm &`! J|ґ7IN]2ɩTo a1=nB};b3b[M nbO _$j5wb,rEKL?гsBzMDG7a<~V0wȬًZQ$jjwYC@Ac}V oͯB*U V<igCKaf2CսMRūEeiB?})yMt*ܸ;|lK)-PN-Q-Zќ"+ IDAT#ەʽ?(gbWVnjB[Dg;q@`6.8Ρ!=*_18ҮgbUe N}"T"wӔ-h޲os$g:R[\\_ȥwvvB3GpckwxT?`5Z5كIZ9ً"byDۢ;mtc_]4M i[J91q~uC3o* T>vztX?:ʡ۪RҟtC-e,iIҐ6]4YVؘܲ'ԧMHM(ljNS=zw4tJ{ayQ9R6J]Uh*o)_f@VYU/xwQXQ_{JkCztpߕdCGpæfCa{w\yw+@B"QCZIqx$Nb''3f:nZ8q:uN2I'3iv8&ؒkɢX(HQ {b#$ssy}^_H2+h~4vkߙ&Auҿ 8|:WvzrbI>s)-`Xbsğ~=g<Wգ|4u]f~)=io/ o4h|HXYA]-F[{KZ/IA$ce9MhsNέMCXjmҺ巭}_Mmٯtz #cqJ4k?I~c)\w?o/Dy|Y/uj$`5DNRCR|"GޖX]-?DJMُ>w%%e_|7СS9O~)V`6z_J#7N9wn;rҟrt_,Vˍ%|g3`1b;?̾)%}ik_պv/|Tg{́XZ3~T7#u4ĵe90!I,И4[ڲ+w)zݶIGF'ſηF6'y+'g Hhk}SkhlIYi}[qStg1X`ukwe={BMRt^adDS&PS6!k/=sNQ)wf<~.ҎAZ5ĀR8^}W:V&Yk8499sΩT*)3ӶtW1FZiuuu)l` V){}XQRwmVKKVWie:y3sv0gOHQXX6RKzSٺ)j 9ع)9>W_;];|fNbQBA\NJ澻XDS$㚚+Lj```A1FaX,jddDSSS&b%2M씟`A4883ghJ$xa@sNk;UN3Wտ:?vFbÍ19uT%*JOrhC: 4TS;ŒN>|DugǕ 4ɨ́Lfx"ZZYT:-O(yI5559g*"y'cZ+{ct"eciYk5<< BAAh˖-jnnnsS~FgErڶvm.o"[y0B[[3T*x␔nUkG_M' \lFmJA(.~#Yfj fdjhNX͘;dԌJqS*=TSN]}<]6$ȱ#*NdWjWEO9O:mл>ߴQT#~F1}+[fmٲsa(|߯9T*H$f\DZFFF499M65t|>qutt;A3W寁d+"ChzuFRI<{T8 媯E\N993(iiTKFkFFQ]%SEт,Rdv(v l$W9X0T:Қ(Uwܡ\.Wؽ{n6uvv^u<8p@'?ׯvjgddD/\,xjs+OCSN $4+Ne}6ȖKE TɀX.rIHeTf箆"q\I#8ځ|F|I*{;x/N_^1uOשdB-]:!Ec#ʬ[ܼZTP}K*.Tbe^ szqn?kttL==*nw붷CL ZKV__lm<*ˊXrYP(ZL&(4::SNI؇aExXTEٳgU,J08ڵKlVqT*Ig<%di\*GR;EHeyq|Q_ (bIq,&KxlSʱUYJŕ;rN梪gC4RONGG/MKgNR얝*w1 ꖟLW5u-MکvIT-CJ5hi}OGs:u{gt$@:WEΟ?r,眾/j``@ǎSP<cL&U*OxvkN<'xE*1FXkO\N?ui=ֳ>|>m۶]h5",٫KMOY_r22Q4%߻{''ztBWv |yK IaQS<: Ἦ]L$|&SlaJiMW%_Z)mYk{^GRIo{SִcMl2[ַK=[{N.yWKT0 +hllL۷O׎;ܬz/zK7VzT_P޽{U,wC1$Ivm{466}Zm޼YvR:V[[nm޼YΜ9S[#ß-0YbqB`֪.RV*0ER\Yj`X{U2ی|P XN.K,di3Cg%y⸲<9PbTTsg*Vh}ٌRFU*RKssk]$@:)^Fw7^~ZGT9QOW(RN8tںs۫=k;Z /?P!ZlZ]voWXw{ǎݻcm۶M|^Re9Ą9[;T*ҹs.0 k.:f mذ1c4::|>p`*מߚq:1:stzx9MdsJ V.) "'EN.(Sl=Mx9ٲ]eppՙ++jpbD>+ʕra 5└LK2a0 yެ^^,0:wνռi@ٶv32NzrMMZ%0lxn}9yL/'tvbRٴT96q-jniuOԹc +"EQ$Jj;'LNNJR}ITKx(488=cT*j?.{ɵc=fe2m۶MSSSU,S\RIk׮Շ?a}[җ%=#L7rڲe^x:uJ~:~* :uvڥ=z'uVɓz7aÆ3m1S“I+Xy֪?cBk5t?#9#Vagmeޫ|R`J6)+|#1bW|/l&RRMқKS+LNH*ϛm?k ey&IkoP2Ssk$* P-qjۤ<5kr}uƁIA05IT"rYwqG1__2?nA|#Zv$iÆ z֭[vZmذAwNZZZdsNR)a-[hxxXTJ6mҽ+c2 v^Fe B0=ӌzW^|Les&/OJj}__q\c+&gF3B?T)M*@o{ImFX5m~xjdM'yjY E8d'ϳn>1芠0 WDv5\2u.nf7sNz]jjiWkU< }zkÖm-f8G#י˿F7zT.Ku~mLWTYŕ*'IQbűSzjIKM1FDBBATJ9‘uqmVi~AVZ< KsJ&* jiiY[Ug /8UjooWTyyu[M;vekinQGGta͈_ۖ0H,˟9N8z\h5~ u7+*/ɳwS =b'E) Bcǵ~MF9qt:I+ն?kD9{~ fQg |^J$,#f`)Ug IDATϟ?8*}=zTqV>_mjokk5ٲ':uꔲ٬{JKҎC+ӶUuG/Z]v/R߽9e^tZُTӻ7S,jjQm{scN.{gF+n.j?W/.K]u=;K9m_ƾճo&7?;u+"۟j`&O*-xSW<4nwg' _ڟ?dj-ݱgX(6K1ҧk4sV~wܘճoSG+b[D`9*cy&hK]Nm/=tkAzKCc tX\KLFA<]74sU#؉'Rl?j#>2l|;r%s܀s*J {9̘27-_:PO뢎;5.'43 X,1F9EQD`MH{g/o:}'Wn%a_CwF,C 5D7ē*>pxu ߜ_o ='1kTO T[v>_6o+' ob{h,o:}g #F AcՔZTI'Y$~Wp@ }¸$ڜӯꞛWF>dc[~$+liUsJH$$ݹ!ֿ?Ԇ9oQ'_[`.*_ƗH5N?_2#FdCVk|̙g>?`3O+}o6r;b==ҮNI>W]9~t;=96+}[C.fya߼#sSN_L鞛c?^|GR14zճo9U(\mO TWra~ʡMqw/>_|QSSSDZ2N]/E_0u#uc:;;uM7) CAPW===Za>`j~$%I[NX9iuww+HҌC1FZutt6ѡZ? 7TJׯА&&&קfIW/ˬJ ]J&Q2TE4$qFuvvmˇhnU{{esn  `hb} y%fC奡mmX^s-I=S~^0?޽{ĩTJ?`ÓK?v `Xbt` *@U V$XH ` *@U npZȬ2ƐFem%߳g<r_V$XkE'6oLVAW_sNJR2Ͽ(cI|cV$XH ` *@U V$XH ` *@U V$XH ` *@U}!eIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_cross.svg0000644000175000017500000000333513455362716021361 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_patch.png0000644000175000017500000000277213455362716021320 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATxU0/;AL@:ff J'L0 H&(Lۊ?xՕ`^+HTC9 . ]\R<++]S8यܲpCuIt.t46NB݄n*zX5L!um9{m n Eݻ<@G&y]x/Jƀ.kGty/t"@3].OQfCMzлw 3؂8Pz7,7ލ6FЦ ?Ҿ5پJ.@߹yl7wY~ԍ{=]fS'ÌC FJsxJrguuB^*oԭפup[#f[G\.kn_& 2;N J0.aNujGy3"L霁fv'ԟ̣ 'PҺ7lnL*OZՆ^9i{De2!^'ҝPCP*rgu:QNIqz:tNgUG3ԟ 誽VBPwL8]` !vЦ t{Lt}E.O3ԙ:_G.0&ݣ^4"=^l:B7rդ~P*]R7KD' ˡ(Tnh!G=\Ƶs޴p!3oTs |r{_ ܱjN MMnڤG;ݜuקVTMwoO-4E3cqF>m?x4Sjj Նc!lƟtP\xW f)!s?VYᝄ^:l~d:ߝNzjY᝴wͥ@GC^fs:x@ Ջ$ 耎6 psVyk|h(eu|~sX{N>g& B޷]x?t[:{ciӥ-nYv]e쥇4}uIW F~7ҁKt:uߖpqN犙xtKus;[3ziwSb )7GQo$w:r7HC]rwcM앥;.VS+}]cnm.{yHIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_cross.png0000644000175000017500000000067513455362716021352 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<JIDATx 0aq7t3LݤnqǃFKMz"mZ;舎舎V,"7NSjE_s wDGtDGtDGtDGtDGtDGtDEGtDGtDGtDGtDGtDGtZmMqMGtyٳ v?|q{5\b !Ș3]tDGtDGtDGtDGtDGtDGtDGtDGtDiSiJO;#:#:#:`*~&.IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_star.svg0000644000175000017500000000221613455362716021176 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_lasso.png0000644000175000017500000001132713455362716021336 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<dIDATx tTE<:΋<  ŒP9@EfHW]wUgv=G3.s\LtV대\yvDDy%ᑄW!MyTǾ}ow}ow'pNu篾n -΅C¡sйp\8t.: ΅C¡sеg6n΢hG?6|H6?8<|lsmC3Gb^|yQ>-cj:0zAKFQX*n}ôfG0Md]}g31N ]Qn̤UA0 mŤ c@oՌia[SɃ2maX H!H40K`d{UA~bugeQq1NiѸEE B>@^r{?c{ȍ\3 &us&f X!PKFYqЙc݉#0yrJ(}< mx 41I!%[u\IL: OUn$Չo@ .Ɏ|Wb[d̰ՊKWK^D Wlt6 ͤOD.0=jMz9$FPw.So sAT[7^xWBiN҅d1VǶEIA2|3R{,sw:Jx5ZB11."u֮Kh9 㯵K-9kD”p[\9R9wzRv:}^z4SKW_z6'm.p*Y(ۮIAiӈe{zO( /*ʿE?4ᬻУ p|@'+2{hijeWr@ۋrfwc?|XIH`Ӳ|\kRE95j]PbBXǡtTe>CJɒ_S1FÙ^RPFm1D#EmaJwW#:ܱ`9|I~4Nkf ĆˈrQ2OBUEȽ?'IZ]>Nmc⽪,?';l99|=ߥ_^|ENB־vuףp8]{;PhSPҵ4"O{z.y|J=՛Afjxmx}>I9>煴y,*k桿'RHڮԼ %㎹sz^6S~onBZGEdrJi9bR)bU55 -ߵ0cAF9p&G0&ONoLMѕwWTtgk篈 uٙS颰`(i| H91'QהEIugԽV*:=[dڧE=`t˚. ׾xvҔF%+uX;ǒiWANG*8$Ĝ%*tA}!~*7JdU ;q&]ZÃ]i U lj\'"'lSܵBBZ.bc\)Rq}~ݷ -?\{GC}浖xd .HЫTT&Ԟ@ 8Y8).Ӱ_>\rx [Ҟ1Wyzohx;%?`>:w~d/M[ ^6X=Cu7d_P^́w_EEϞݪ<_]x3i½JzYJVt4|,z{F?&'.-$6޵ˎ';3euQ ʅ=5鎃FNj()ɱSG`~-˜wUB,B=iQ>/;|VNGQPK w+[;\},G̾ľYf}V,3)h&MW'wgrB4?q~?eV9]at>ݯ$_(.xɛ{mߗ{I*э|gi%RZ?G;UvFc48.I}u|cgkJ;wY :~$^QtK@aHMfdN7{&> Z2g;Т8}E{̜?"t6U N3.yT$O%ON^B[d\SI?Upsd?ms׎L6txӝ `93pwpcp$Hog7>JnZINVٸ$ㅇ#+RnìB"qw`z-zdV t>?pK8ו9x3`@IhJfTZ-c5\[ǎN5]<}X~3(6}s^" Fۺ⺼N\Q̪,a(1ctU[=4?iMݩ@1JmD)ID]β|1>*쭋DC/֠x2:et&{q`4mCmpO76#et-a੅QX%_#ނ~ RZY?t0 Ȓ bhq\ V[R3 ^]rAT 1&{B-N$A=Hy#T:E8rQXH*jv%]_HarT8t7o|4xbJE]JUH.Vh5R,]SشS)| |}B е_"Q{ʑ2|gh=5-h]r9tuV=oz^sp[yȦN8gųTE's+dzhy6=4F.[b0gkХW_471+5೘K~[wf$ˇ. &SX7 `zviFc,?PitUewR--;GRs3g3'*tbP6Uljw~:uyC5 i1e.nu$ ]NEcewx:"l зQgَ|CUu jB1 5nxj5)5^ RB`x.˛aǠ] *~C/8?_Ά^.]ٛ'%.x\TINXԺLzXj3Э[bO;/pAй: ΅C¡sйp\8t.: ΅C¡s_s&!:IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_patch.svg0000644000175000017500000000305313455362716021324 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_circle_point.png0000644000175000017500000000301013455362716022655 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATxq0 *Kp: PtTp#pdsd#ْݵ'1dGh4LjJԠysįgם~]k1ʠl䳧3`)3㓠+ g<_u^Ьxts} i]^A:^K8qOUkuxu9 |ۏs襻>͛N_/֝]wt㎮V»r 7@c.#u=} ^OG;Ś^b /G_R^=^.wnWS +-x!B3&*ӳΗ< ]5]zm lo;~v N?뿄= Rw]@Kt R>mV#tv!!.?t\XO>%< Cz{òְWjÖ~a3`><#\ktEg0,tx{x^\ :5z(g SB SAuu35$v9AFn7a+@:VXz#q0z5;Ijh6pRܡO߯.ml\a/X&?Ӗ[ް*T#umpNwַĚ޲'p#=s׿=cHڰ=E^c}p >y~!vڸ6 e %iK5{L}\ggq?vJ (U) ک S">%;尧UN#'sY(j;=ZtS'{]_Qv KCniXw!GEa)xZP-$~R^zeSv 5('WO#,ix7=uԧ/wO tG[\=5& -zk GHŒOJ9Ka@@\fʾ8qWp{VH:Y4;gvd}I:@KekunaB ̚]iOn>ӣ1²,`i߰likw:%+6V>˂)KgR :ܚj'eA5&]e(Qunw:3Ks^aU]CYǝ ~ K9Kѷx[`S3pB̤͂|37xנlDn*]g76uWk t*ݜys z=(oɃ]Twdd.gH2LQ ڸt!@ts裸RM-̀ޛ VTb":[hG7 ]\&~l! 'z'rҷJENntxNݰ޸Ip%=wp$tk6swxHvk7-;>w\N!f,ǭI&bR,Ot!襓'D6 nztе{t8%tـZÓA8E')%߾EʩTm+ :.NOq㩤+oʙCҋ#6a:Stkۤn~nw}Ƚ~sr>:44ɍC{GGW :PÛ\,Ji7wn=uF}z| &U>htHDuxү7l݋Z6pr#fܳyur_v 𒙸vեa٥{y p9cӜs^TXT,.,'rYTmCVQvBNY;*sΉ_<^mamّ(E,-qFZ@'ZtlA.e9!eJ{2 5&mE;Oұ^"3wkq1Ov{=Y7 /'AokQgse.Ig0@=)2Qy,=˧",?.ۊ0Eo`IvT&[hd vC5xiTJ|n~<ڟ9gWC{뭟bFF=BʍH@p image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_forward.svg0000644000175000017500000001511113455362716021667 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_star.png0000644000175000017500000000257513455362716021173 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe< IDATxq0m5`:RA| %H@:H NȻ3#`{?Zr[@G:AEYmŜn$&Aomp^V9-sevSY s;ÖVm7cMn#wa>Wtv,!>><L7b),gzӯ܌۳],7alWk r{4=mwܞLOhZ,g*{\۳]=Zvٮc RaSacʼ [ȼGZ/\z Yv`=,glĈ%6>uZ2ڼehUлj=h|y;w.+n:ؔD!N`sn ͩXC<X%o],=C딱z=q{|͹ ENEC*7#r-knE_s?YfKF695'ɱwbC?P,t" ^WC 3t. \b6K*ݰ0?)I"-?>@+tx>?,>/l[_`pgNgsϧ,p/ \ :n(YDQ Rz\kK簿9.uзX ѽ[-UTN=MBoi(tozczabC[_{&7ơX!i0iʸL^<: M/Xqx roCK6T3]9U9gfZ6>Mc;ɿgRC^}SrdzkBp38= sӮ3+SmdNz_k'ͤ;qWP^E3E:{'BpRHJ*s|"OU ҂^eDۓC^oٹe <1Ը-KdKE50^z+ǡ]j/k/}_^T:Z"~OuCЍjU<tkvF8wd`N9qݫ image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_triangle_up.png0000644000175000017500000000236213455362716022525 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATxm0i N`wd 47i;A lNT c>x HdGjs\ ԗ,C0L`:!t7f0/Çp2?9p<떛 7فtݔʿ>quj ]8%X|LK~8`z {pI"Z\Ow8v#\Dt:#ڒTG83hǕ/?N.8=o3nf2 N"ZN#c!7tޔmn)\}az&-%=bzx;XߌpFIi]eÍQTZ%i\pž5>G8i)0KjItiU@yO۷Siӑ폒KDNsu"奒 uhO>*<NTE8i/uPITwdcjx^RiҙD45N 鞹s ]gD$~^˜юLgzz/ХB%1^Q['dI&6q%}nFFy.*뎱L)$v2|R"܀[.gup")5f1,HoܥBXtp!+7|p%PD^Zt%PD^Ze :3#pp^$h|#\MGFQc"/h+P> p]y Y3cJ_*D8StPFb1%IGDነ&+•2.*鎱 `x3"d}\ 'Y9QEեU90Z=v%1aV image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_crosshair.png0000644000175000017500000000477313502206677022215 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYs.#.#x?vtEXtSoftwarewww.inkscape.org< xIDATx{PTǿ #"h6$1 >vliiv&i;}LhGq:u:M:!`FǦujMG[|²{,ea]>q9w=s]hRU & W7F;hѢ1 ,]AXta +KW ,]AXta +KW ,]AXta +KW ,]AXta +KW ,]AXta +KW ,]AXta +KW ,]AXta +5".8 Q0 3"bD]A:!@@XC`~/eaY)nW"Z>Th.9fv TKDL6fXzJCh:ү0ˆǢ"VHo!_^P`aƪihvz"!r>x<+e;дahi ~Rti|ܖ :KNqEYX'izQbh46ls׭%cw֝NJnO65e FktI^9tcA(; ^-ڵf_*(n#hev^ht oFOq #5_ӁGKAnF.c䍅\fq?$,]>Ep)DQR3{K ʙB p?ma%Nˇ`He6ut)\I ƪlf~kUK#?05 ?~$ ɸA3]K ǤW~zzas$|suےGn,]P'g;bf}Z.ylcTyk,0ݻLlaxc3k*Lˀ+1^޿B}BC#n{3<,]M \*fd;|zs̚ 4ƴ3xYWndSݻ¯ 0)(Ot qss)ס`|Y:AxwS6ً~a%{S&+gڢUCYXs^7&OMm:YHd.mͬ?>?*g]wI]¸H|p+wH^ųt g|r} B}ݦ@'m2|+[[93UO?mpX3rA;o-:Q9nOl[u L=Y(n "dMWfk_{rDK`Vma C.ng7nv==J{V'|x;b|@y P7o@%)ڋ/<4}xKbܭNhnEf3\N'\9p}fa&p;{A. uf$4P% image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_tree.svg0000644000175000017500000000507213455362716021167 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_square.png0000644000175000017500000001014213455362716021507 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx] p.DȃG"TG qpj[bc[gTJ1tbA@tD$ CkwO޻{ٳgͽ77ݻ9{#@J].E.E.E.E.E.E.E.E.E.E.E)Zhˤѕ+^8*AO]n uL?O_%AO=.[Ff)sRw$<@ zjh\6_({jk.)f4ny)%ߴ翤< p!Jfݞ_[ ~} i4Dǎy5%.Fq,KLA cG}Ѝn A .rѴ0m)k\Xlֵ.{7_epGKulYl" I#׳{ϡ">-±Q~(ΔF[z!O#j7?.-M=SEͅN.nS{N_1 %6?Am |w8re'TZ1}3'`S΋6&hgm4} צaո>o?gރ[Y 1*f<~͟B?} я[fcEoڛm}OZt=9+&?YL5Z}6|q&8B8nGG |1B-9ԩT}Y[/[V!Nk^z۽m݊wccSzs1֤gt @1J=a58,h gI誮QFg&^1&1=+ᅪi=lSBB!,cP|7a|{H?Аxπ^A@`YStXcuPIݫ!mWMsJj !kzg~ͻƁيC89=m9=d {#z"EO9-`oE3=vJ%uϫX<E3պec}[Ŝ,< sc4{NPFow_7>A_zEYa=>h4.nuh׶{G*בSyX39 bhAmEH~)L{6(~ " _:^b7.(j C|\}[_[s*` csl-rorNIp+?ENZΚu!WP4G +'@Wh92]n+C4Fs_gh9BN%W\9srIsev6]+g1LUc=&b're\5\>q5r*O]#ow9z0qM]:U@Lt͚+gC `"-J-K,̞*N_%SsKfX|ϑ6DLBr;ESD/ EM7v1v jW>W% M/&FQ'|ߪ2=WN?w{hrh)A3P &z&w!X?$61ʙr弅eaHT*gNxFO^oFt E|Y8yܠWbFlm9;-A_㎚2;McCh:Wg{r_wK|59h8ioKux_UX1)´TάEh fmx ݙIw5 415CE^zpˈۺ y[ V9FZA@ѸA忞%B1l `JaWm]/T ))Yάyh¸9sl9}?K#)zAgmk==aQ9rf;EY_F&l$5禮מtmړ\9Ox-w[욢1C/tL-e#0sv{ɬ83︚vhRߋr$@L4ю<~>05q!-'\EhrU=} xfGMu]N*+Rܚ\} z|wnARhy\4]>AuXe֢Gs,E3˙5yʙ[+_t}P\?lӤ<<$JPO;؂el\)Ec1^)-s(ޝ2?gKNIDIk2t㿍HN @ W"?zø5¾&!.nFvuI)o\Cx=KkoQ7v?NН:f\/԰ +@^@/mznA,CWmI݁zF2qB9|1 cGz`g2Snm>#3_[/bF t9P>wy{旪9RMuM͏Q]#ЙuݔێH>8}s.]{!-PU,* ME]Q_2i g;sMC|i=o :vo4*J QDoFJ>?ȂY^"=FA?zGfpt/s|Z9{_G>wm G O '/i^&tW'%?b^ t8g6m[1txbIpxo:~3d1mۭRD|1O8T2;iJcqMIv]27t6<Ϥ¤?[ní6|z4N]!(1ȉ7뾟 ^f~?Ish]-</ =Z!KPmX9>ɦ$̋nQQgEA]n E.GvG!<jxim9ڱ{+d[fکti5c]) D?BM@Z Y8Sw&ȵQߤ:tӺ0l==STޑtnf [Uˁnµ4'dI].ꛚ :/Sڽ[>v.Q;ҟBKKKoE' JZ.%Zu~CDYT0 pufR'Xw& .@S_ PvuƍmgwR_pb^*tLΘ:n[r~j69чY 6rߛHzVy'4+QhS ɡqBˁnڎQW*_?G4;WU%p1>>F|Mst3Q}!]9_JL$9 8!+pJΪ-r_n"V}kek26KǺך@7ex_*z똬t/C.^0酅;%iL֎w0L+)Xq%ۙZTx'J /cFeCgQE Ucl0iK>md+uFQNX-S$_iKJKY!I~4/꜇ά}|1Iz- VvYVT+I0QWV'"Yzθ!88sDVK"N@Whxk8ES^Drq <kԟf]>wNOg}"w_mbIY^-l5e^@<STŊC=rss\} _/ëc>"{OW3!]O,؛"$%&R:g#%@nQz!QN3.rkSAAKP SҋKA7 W:/A}:t {33Q :>}FpMn) %E|YΊ&f@a쨠?W:iq|7m+sKh}[Evʍ: LL.~soss[,ơ[nsgtѲ/܆B/NsvnہE=-1"fxkK:㊊tVž}%u!ORF'fEkD}sGRa ~m|斳 mA A/?{j ssw])5;F= V+qؕX`.`20Pq\t:'$N"$N"_q$YynIENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_point.png0000644000175000017500000001050513455362716021343 0ustar noahfxnoahfxPNG  IHDR}}l%sBIT|d pHYs.#.#x?vtEXtSoftwarewww.inkscape.org<IDATx{xǿ&`D)Bц"D.r豞J؃>% ̙pKm (><ԧӣn51@T D.I WٙcC7ٙI|;wwy̰.|lӣ(6= MBlӣ(6= MBlӣ(6= MC$gg`l 3|?*sXXvDYYY^[<T%? Dm.+(@{.Zv24}~ff2P!u09DEå-P)zH=?;{CQXFeN&WCTN--Yד$IgQsLE0=y%*w]E(&}[$IjvH0텋ռrtr\^!M?FF\'/5D$z1νM2?'/5D֬y GԜ::3ϟ ASK-Qi:33z @E֬٪'/r-iӇATk׮.s ߂n$% S VczDm7?.Yr˩Ojk+MzYW`.\[X8Ry#*U.i\[`ջ8ZB(׮]Unt<&AX"=ϛN7IbkNR L֬aVQ]G!Qmzb􂂂{9-(G΍kb(G7}OEQ䰺<5]Ӂ i999+ҙy4JA0''Ls; VWp(:(//~̽.?( $IힳxbEK̼Op _l/yyyO2K!dGª۴EѤf^B:@{sߺ$4kLU Uʳ>3D$Z`f-+[\r<: K1i7s(%Mg_a~"&"M^x-IR-&"IsIAX Jݲe˾%ZbE,˹p+zI(FEoXjĒ%DQ/dߴ#9 `\NN'afUM:$Y `|?֞2P"Iҍaƪ ˚999%.v"9 vxn;Ep-W֩~ `yG+V$OxEu:d猉P^^^3!#DQH OTeH4577wH5`nZ4GemS!aGK6̦]V۠< @7 IG vI75cn0]wI$E6`ϟZ=[[QlAڴSbd m)CȪA3gΜ-ltE9" .U䔘9c>$ITXJSM>= 1GyO2ҲeN$a7 _Aݑg-°y@.Q-⛆! g3Ǜ p9SΌ/ޟ vG<$i1bOdh$3؝2lI&.IRoJy(Ƭ;f-ùrWuӂND?v $[֩J@THIYj{pCLgt'iPuqaz'j5$ Kh4|IȚQ%c ګ1(=h!UgI"0OBX/bq聁=ݫn]8M:k N9!d'nwQ1aj٩3%ͽ],ogN1l 3yT?kҺ~@jBH@#sKDH}EQءD̃u:eK.=khb{ڀXOLIx)ᑶb<]=6CAnWq+M7%Ǿk^{]м> IQ)3YF됛Y(KDw; Ո4,9"//Y?oH ˗/mT t|3ryϤ$ؚ>cÄH.21V}H=wm9=> YA7ad K@6ފ g ©{ơܐxE=mHǤKںdɒ󑎡Y@q8P~'-4@aޙ>sewL衇dZF/7.xpŶ^$ G5qFħK(3s:)(]S_G,͜kzNorսFcLQ· TlHÇOXtF][ gLh'G{M#fmsraG&~%nuU>P6o)F s"SsڦGKcNKϩcqy@?K=ith).Z`AwO@? Ή7k] &c3'GPi{cO+x8v$x̮nlӬY::"llC;ݟx;1sWo ئ_=2=NN:z"}F129)В$ @)F!%7;1d!StĎXI_HM]C2 3N.X !tb聫^JV4SWFy.XODH]z0O+: F<NTיo1[U e-5E=aO 233BmmL$O-0 ][J^WC,e܂tAIu %`\pMMI ,=o.UmBWCẖ` ~s5`;wɷw\ENL\v3}b(0{C{ȣ0{ݱ;Kz<"ku|V Su&wEh U\w[mn$B+5gOk;v8]ljR3{^g7 F㾽{Ģj`MAF ]^ N&BWE*ZaK 'Nc{t|[$xiQCwCm:gnSܲ'ZNu7 Kߎ+I޺o gUrg%KJ ֛_נujpGm`p b1< ?\:lNDׯTs>TdckUSlxv]Yo6@DaׯYSu:Ri|']p^KkT[°ifCAӉx{s=iz I_Z]bj{e)S6wtvĈ؀ˋ+W HE1*+v/~zK[aeINm|Cg8[A}>yzh9@qa0dhpWn &_N8g٫&BW;>1=P3E}Ca7%}.*JccM}3__ӿ,(6= MBIʣ^IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_rainbow.png0000644000175000017500000000377513455362716021666 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATx=nFia{)}i"0}'Ҥ]MDt R*R r:3#}63}^- ^^^2t:C:C:C:CCzBoOQ;F8V_:X4!K-_bKB >/ű9\CK=v{qQ ;@TÎ0" 2OWm_z;t.Yt^w: *O/ axC-XMj/q|G7mv/9G ]~pOs,}X=QBr9 ƵP1_rl;VA4G]?h[J5tᙄXB}LWΉDSEݰJ !_:m h` >S6Lq^UC_s*.:b OOj'qnUku/Lh/B~\jbJV?&ӈ_CTg ?U!]l5h39|>.o.`5oS?_ ?= rsG>)D͡m篚G 7LZcT.X }Z8.0[ҕq]zb& TY{_QTpRWu'ʮTyOࢾ'Si,ӧP̸#pS5(B8|odo<O{ݔfNG&ۄt香MM'yۥӴqObB_(#N}t#V*{v^#R&r=)f ۺt4YTw: mAί ˓x)>C@WakSW9o$2apiZtEsq-s:SCrU"8}uU7M${$HKi#Tc.-{@p5.p x;w#Q[ŗ|"3tekʣeΣPv:8Nއp{#M(Yh.G %_t +E6 O^)㔮rBl#Q7]KEQWZ}(}^*wxf]1Zm<}R'L..w mݮ}WL.fKjKp˯=I@q]g]nkYORtIQ̰~]t,bpr,\׉\mݾr0Fp/tfYZ¢;k :vOl ]G%o7m^.[ VHrWLX E5߼}s y\=~,!y|ݴ Īv ֨3HyN^ Y.ʺj|Щw3)9ݻut ԡgx,cԎ!o<9 @NC=bhnf[^4lZ9i8BХuk KU{?tL;ͻ $Q@g\ęv0Q ԡw]_:x6&VlH]>8&UD% 9M(iF{er"7Or%w.#C"4t|.C9t]Kr~kIS>_L{v1}\EXr:jt)?HQyjcaQt:C:C:C:C::C:C:CWM&IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/IPythonConsole.png0000644000175000017500000003115513455362716022117 0ustar noahfxnoahfxPNG  IHDRq] pHYs  tEXtSoftwareAdobe ImageReadyqe<1IDATx t\ՙ%,K,!Y^ 8p 1q t t$v&N鸝4tI&f!z0L'p t&΄=-y-־[ɯ^իz8ET{qJ EǦM?G| CwwwSIICin*3_}_'Jܬ~.L'G["ňǔe" P8JKKa0"Gٔ  N.D cz{{a?\;::N@#'<00@^[9 3Gbd0 . {iU]iS"_>et>>peǿ*5e3'a ă2Z=n TRBOS?b@ۧqzb>9anܰe{(dS\B! 8/b@皵vV0h\pZ07h-v Ii!07@Ab9apD`Fhoㄩʛk`púnuS@e@ wpT82>תX r= q58anʀx8anܰe{).+b@皵vT)\" zoqbas¸aܰe{ȷ) S)$Y{ Sac>תS@a?לb8xP\+:anw|ܰmm0N { ^a\ h2 Yk'Le _µj-Ah*'a>]S { u/0؃' 4P2 Y{q;nt.FVEB;` ID١b8xP\+(9ant7ajqTʀx@cOyp)0<:a h` k SWpZ07@._ >nk S2 anܰne{U)A2 Yk'me e@~ תsbǐ4nk Se@<=h*¸@X2 k=[0P)7׌)0nOk.A2e@<=pÔ_GnˀP{8a*n(\+ S~Ҹa? py@#' &/7Lg/ ?yKv,Yo/X@}3 ])lsWuwOx` {V W)b`^~%tl5gԪXy>[uUZ,|ebҬ?Q5ĺD!ΦP QNt(1BJ{mP Ervbͷ a~}kQ{0@dQǚO~!/al:a1\F@eq*W_i<察WOԬs7젅 akˮTg\qg@%tÉ$\s6[*֭qoܸQuvv:tauJmìDtU’A[=.yh =RwVB#N8).o3g0ˡ<}j'u17-~f(! A77޾j*ϜGIJP77yHD[@{OE"%}o__|MKc >N20 ;vO|DBSƃ/<{EаZTuÒݼ5 GwFoܛx| *IJۛ8a]zp0oB,Kz1Pv "4B˺aSǏv 8YGov #*=iO햎׮]ᣡ>e~O %_ٿKWU' e/Q}F5qg-:wj3}:5d9 0@xx]>J"’)-lUkYPέQfHzBYM4N@AI2wLi˗ϲ/2e{=%JT p,Ŀ'9ul\i̳ޮhA]N07ezHZܰz0_yb @zsHZ'7OL 0x<ݞ|}$w+/M,a*Onq%h6XMԩJFӣZOJ>=89( 'R>Pagk5C{WZ߽ˎYh/3wؑ|͜-#AMj~Y\0ȨLBX}{"U/N5R=R[c67V{_seau I;IY[n5g7ɟn~Z}sk_DZ}kVR5uz:y]u=}\8/E+oW Z4_ON8Q\$p7~zn,WϨuncY2`sԆcPV]4V-'ZՉC|@BD"y99ԬThpuɣꭧ~Vx+~a@CѦ A'vb%cs}ɹ#ǎ.',ϨM@?BMp~cw5c3 !·27C$PƇJ+qO͛a*Hn| /’%- Z 6 aϛ?W]}kMȒ-9G ]'ZxԂ+ޯbVdJD酧O0tE*%1 UNєhs[A:a߁&t Z"-l ?u]2's~Mn_ iiPSx%$h?<ٜpS:ϿfфkLl. p)ֵOꊸڞNuK xf2PZQm {9PF +Xgϧ[d/ӎݚ ZSs^t1̙꺼'nw6y@rw:'C;eQI 7+ Z#:3T;sveQFA U[w}xݼrCG;MԎv7w5^>ڢ;h u*ڵ|]ܠC6wB ?i;U)G\0- O$lذaƋgf*>"}GR6CGm/V:^0YdZ9p )ZFS\d'.N> Ɉ6]%˴9a[?fW wU(s,zXCYUp%ų8_QNZHuڵyV"Ix{ -5{nXxIY'k:[[l3K6wВB 6\Vܸ'I ##A glB-ܒ|}+=CYe8ZܰS+t `f/Yԁ`6WE a"LuaՒ 5#v~IВ#$A+_nSrdaU0,4dHzl_}7Ie!"k׮Mˮ]֔"=p!x{pD~}o=2"*dJe#H`H |ڵo +çn؋#%Pd *lsTa)J}<[0\:@bƍ/O&aS!㎄KqPw%xqġeI֡֐ g[3!['L]~6evF4{6yW0!M3HrQ938(5daW6oݶ'# sA\hQReVp&{r`"&hm*Xj_M+s:=p7 >!sNTLƖ ]FURR!{,Wr:LOm*eIRi@leJ*3NG;Ig5X2T,YbQTo٫\n\̦Ǟt\܈pa rnp _-Sһ}!1 у г9''VDuCΑZΖXՀG\Yb\513:ua) A`&eGֵu 4D3 n- ZnYԡԑCqaVܹ+=եl!٨3[^@\JL(d8`7Cђ&·w_=o:z$h|j(n͵A>!N ;sbpT:a>oub~H%[@=]H™6rή9^N$ Z^uيma !L"Q@ɐP26ztnpo:/ AㆃyLm,EanY#6=#v:m]^:ԭ\ν洺yGjBu CuZhYM+ƞn̆yD'۴G U֨W|x6֯6.?sn"{PfEI"{_7l oӡ#o0Lp1w9bytLHBVk Wdf.VM|]2ijnAhpbN8 InGG'ܹ3 uX+uv}+kK+k"RK\ٸMKcSՌsN(ft'nj0 jQSg oh{WY54w}Irbu($Z9:dvϦϽH '&A !t1ikw9\pYաԡ53'&bteVS>}l{Q9#Vt!_%VcGfNτzk>[0Jm̔unXc~|1?͛'UFMVwҳ}3܆ѡD8jȼ$TIb }3@]ReM]$K̡ha \Μ#WOqR܇U9a2C݀4WIؠiLÓL9 zdh?jOߩnn޿gT8ERa,).M[eHG갆l3~ٔ"2_DX-qvE|fね7,9AF9&֒xfa Ⱦz{z5ݺ'\9,m@ mkw\'l]6]ȉ}*v:w-UH cO exk[B|npoNO_vYGrmpTkCqd#_Vzmץ[.pYC@ T*jSׁ*jc8ڍ5D2!kg#ut>b5ym#+OuZ@=Dw4fuz;\ߺ68 Yv%sg.mCTϨ9Je/H~]^?'GaHZĶi#Ɋfۛ rzh\$)258uȍ/?jf%✹YoϪ{^H~O"'#== mW' &4)wy$dEx(:Y/O<`b,Cӝoс~U0f(WC]eH#iƼ zA 'ֈ3VO4)S|@xG.5Hv.+.7n٧F15v7U5^fq heh\%dəa  uRґ"^I:r pZ9y㨛ߵ o%Ϩע>H\@pp85WaqnvȚ!9D 4VgJbul~vʼn> [aÆ# av.@AS θ?4Æ_\{klMȒ0dw1YƜ_Iĺ;F*Cٍ j̙ɯ*=7p<9{pXj`XG٘3ݸ7&]pwȂp K~=kfCB\DžU~VU9./(3׿}뎮ns{wήnjY㝎ў}T-]YqP; b eY؜ 9*f-0aGj'[e"+gAEqN8W;AZ;dJ= :9lI 0xm>tc ;d_qljɬU[[/_n]G߿_mذ!)ɦɟb1"1p7@EHx"E<>D' NlJ -9)###D c"Mp7@0" 3   3 ahNh0" EauAvˆ0O"p4N 20' d'p4@qE" )S ɦI!R(1H O.D c2h|k *OزeK-v)[%%%%n LEc+s> 031kMi!(M_P4WB-IENDB`glueviz-1.0.1+dfsg.orig/glue/icons/glue_contour.svg0000644000175000017500000001573513455362716021730 0ustar noahfxnoahfx image/svg+xmlglueviz-1.0.1+dfsg.orig/glue/icons/glue_open.png0000644000175000017500000000410413455362716021151 0ustar noahfxnoahfxPNG  IHDR}}l% pHYs.#.#x?vtEXtSoftwareAdobe ImageReadyqe<IDATxKoT3LfҤ\(4M (*  ۢ[@bNJQ X)UXԮ/*"ܚ?Ɠs|<3gW{ȏ>>`fB+t@Gt{y#1W_3\п|џp>9n耎 B |)bvOӷ?C#:cz}>s :t{tDF-@?uJzGt@G&.+ô<\M8KP !- ơtb?:z\H~WT9N^ʂNݗ4j `ar{VQwv_1ov^e]]wyčϝ<{DҫevO{8YTszw1&oyZXh~=_oi'aWƥ Ǒ8N#)P`ܒଂ&y?N4[' G}`>iz~UN^weY6uSM(`ݖ+| n@^6)~H7.JӇGкe}*ZύTF9T9#Zh=ʺJcyCZSvM_|mw\B#ckh{}V~O>|OZgֻѺ;tUq0#|øݐ/SNHH"}wk~yKRx&n]>BǕޕޗ(M~tX zz龽hkaҡuZ="k/_znZfhvҕkյnZORNεbڹ?[кe,_R xeZLivY(͊}uʔֽw/%Rr%#G+iΌCPn;+vznq+wCECZwZkNzF]A_ےUˆNzZ= M6]E@j=gUO\W|E)jzClW̻&'rLAA{3׶jv*]͌شuփQ^Vp6Nzu248 mϓJ ZOAn6 #h=QHCCnY-r A떵NzhݲSuΈ9Za;У:֙L$y}nJowh= 1, label='Subset 1') assert combo_labels(subset_combo) == ('Subset 1', ['Subset 1', 'None/Create New']) assert mode_label.text() == 'Mode:' sg2 = dc.new_subset_group(subset_state=dc[0].id['x'] < 1, label='Subset 2') assert combo_labels(subset_combo) == ('Subset 2', ['Subset 1', 'Subset 2', 'None/Create New']) assert mode_label.text() == 'Mode:' edit.edit_subset = [sg1, sg2] assert combo_labels(subset_combo) == ('Multiple subsets', ['Multiple subsets', 'Subset 1', 'Subset 2', 'None/Create New']) assert mode_label.text() == 'Mode:' edit.edit_subset = [sg1] assert combo_labels(subset_combo) == ('Subset 1', ['Subset 1', 'Subset 2', 'None/Create New']) assert mode_label.text() == 'Mode:' edit.edit_subset = [] assert combo_labels(subset_combo) == ('None/Create New', ['Subset 1', 'Subset 2', 'None/Create New']) assert mode_label.text() == '(the next selection will create a subset)' edit.edit_subset = [sg2] assert combo_labels(subset_combo) == ('Subset 2', ['Subset 1', 'Subset 2', 'None/Create New']) assert mode_label.text() == 'Mode:' glueviz-1.0.1+dfsg.orig/glue/app/qt/tests/test_preferences.py0000644000175000017500000003221213605357235023631 0ustar noahfxnoahfximport os import pytest import numpy as np from unittest.mock import patch, MagicMock from matplotlib.colors import ColorConverter from glue import custom_viewer from glue.tests.helpers import PYSIDE2_INSTALLED # noqa from glue.core import HubListener, Application, Data, DataCollection from glue.core.message import SettingsChangeMessage from qtpy import QtWidgets from glue.app.qt.preferences import PreferencesDialog from glue.app.qt import GlueApplication from glue.viewers.scatter.qt import ScatterViewer from glue.viewers.image.qt import ImageViewer from glue.viewers.histogram.qt import HistogramViewer from glue.plugins.dendro_viewer.qt import DendrogramViewer rgb = ColorConverter().to_rgb class TestPreferences(): def setup_method(self, method): self.app = Application() def test_no_change(self): # If we don't change anything, settings should be preserved with patch('glue.config.settings') as settings: settings.FOREGROUND_COLOR = 'red' settings.BACKGROUND_COLOR = (0, 0.5, 1) settings.DATA_COLOR = (1, 0.5, 0.25) settings.DATA_ALPHA = 0.3 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() assert dialog.theme == 'Custom' dialog.accept() assert rgb(settings.FOREGROUND_COLOR) == (1, 0, 0) assert rgb(settings.BACKGROUND_COLOR) == (0, 0.5, 1) assert rgb(settings.DATA_COLOR) == (1, 0.5, 0.25) assert settings.DATA_ALPHA == 0.3 assert settings.FONT_SIZE == 8.0 def test_theme_autodetect(self): # If we don't change anything, settings should be preserved with patch('glue.config.settings') as settings: settings.FOREGROUND_COLOR = 'white' settings.BACKGROUND_COLOR = 'black' settings.DATA_COLOR = '0.75' settings.DATA_ALPHA = 0.8 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() assert dialog.theme == 'White on Black' dialog.accept() settings.FOREGROUND_COLOR = 'black' settings.BACKGROUND_COLOR = 'white' settings.DATA_COLOR = '0.35' settings.DATA_ALPHA = 0.8 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() assert dialog.theme == 'Black on White' dialog.accept() def test_themes(self): # Check that themes work with patch('glue.config.settings') as settings: settings.FOREGROUND_COLOR = 'red' settings.BACKGROUND_COLOR = (0, 0.5, 1) settings.DATA_COLOR = (1, 0.5, 0.25) settings.DATA_ALPHA = 0.3 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() dialog.theme = 'White on Black' dialog.accept() assert rgb(settings.FOREGROUND_COLOR) == (1, 1, 1) assert rgb(settings.BACKGROUND_COLOR) == (0, 0, 0) assert rgb(settings.DATA_COLOR) == (0.75, 0.75, 0.75) assert settings.DATA_ALPHA == 0.8 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() dialog.theme = 'Black on White' dialog.accept() assert rgb(settings.FOREGROUND_COLOR) == (0, 0, 0) assert rgb(settings.BACKGROUND_COLOR) == (1, 1, 1) assert rgb(settings.DATA_COLOR) == (0.35, 0.35, 0.35) assert settings.DATA_ALPHA == 0.8 settings.FONT_SIZE = 8.0 def test_custom_changes(self): # Check that themes work with patch('glue.config.settings') as settings: settings.FOREGROUND_COLOR = 'red' settings.BACKGROUND_COLOR = (0, 0.5, 1) settings.DATA_COLOR = (1, 0.5, 0.25) settings.DATA_ALPHA = 0.3 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() dialog.foreground = (0, 1, 1) dialog.accept() assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) assert rgb(settings.BACKGROUND_COLOR) == (0, 0.5, 1) assert rgb(settings.DATA_COLOR) == (1, 0.5, 0.25) assert settings.DATA_ALPHA == 0.3 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() dialog.background = (1, 0, 1) dialog.accept() assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) assert rgb(settings.BACKGROUND_COLOR) == (1, 0, 1) assert rgb(settings.DATA_COLOR) == (1, 0.5, 0.25) assert settings.DATA_ALPHA == 0.3 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() dialog.data_color = (1, 1, 0.5) dialog.accept() assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) assert rgb(settings.BACKGROUND_COLOR) == (1, 0, 1) assert rgb(settings.DATA_COLOR) == (1, 1, 0.5) assert settings.DATA_ALPHA == 0.3 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() dialog.data_alpha = 0.4 dialog.accept() assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) assert rgb(settings.BACKGROUND_COLOR) == (1, 0, 1) assert rgb(settings.DATA_COLOR) == (1, 1, 0.5) assert settings.DATA_ALPHA == 0.4 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() dialog.font_size = 16.0 dialog.accept() assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) assert rgb(settings.BACKGROUND_COLOR) == (1, 0, 1) assert rgb(settings.DATA_COLOR) == (1, 1, 0.5) assert settings.DATA_ALPHA == 0.4 settings.FONT_SIZE = 16.0 def test_custom_pane(self): settings = MagicMock() class CustomPreferences(QtWidgets.QWidget): def __init__(self, parent=None): super(CustomPreferences, self).__init__(parent=parent) self.layout = QtWidgets.QFormLayout() self.option1 = QtWidgets.QLineEdit() self.option2 = QtWidgets.QLineEdit() self.layout.addRow("Option 1", self.option1) self.layout.addRow("Option 2", self.option2) self.setLayout(self.layout) def finalize(self): settings.OPTION1 = "Monty" settings.OPTION2 = "Python" preference_panes = [('Custom', CustomPreferences)] with patch('glue.config.preference_panes', preference_panes): dialog = PreferencesDialog(self.app) dialog.show() dialog.accept() assert settings.OPTION1 == "Monty" assert settings.OPTION2 == "Python" def test_settings_change_message(self): # Make sure that a SettingsChangeMessage gets emitted when settings # change in the dialog class TestListener(HubListener): def __init__(self, hub): hub.subscribe(self, SettingsChangeMessage, handler=self.receive_message) self.received = [] def receive_message(self, message): self.received.append(message) listener = TestListener(self.app._hub) with patch('glue.config.settings') as settings: settings.FOREGROUND_COLOR = 'red' settings.BACKGROUND_COLOR = (0, 0.5, 1) settings.DATA_COLOR = (1, 0.5, 0.25) settings.DATA_ALPHA = 0.3 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() dialog.foreground = (0, 1, 1) dialog.accept() assert len(listener.received) == 1 assert listener.received[0].settings == ('FOREGROUND_COLOR', 'BACKGROUND_COLOR', 'FONT_SIZE') def test_save_to_disk(self, tmpdir): with patch('glue.config.settings') as settings: with patch('glue.config.CFG_DIR', tmpdir.strpath): settings.FOREGROUND_COLOR = 'red' settings.BACKGROUND_COLOR = (0, 0.5, 1) settings.DATA_COLOR = (1, 0.5, 0.25) settings.DATA_ALPHA = 0.3 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(self.app) dialog.show() dialog.save_to_disk = False dialog.accept() assert not os.path.exists(os.path.join(tmpdir.strpath, 'settings.cfg')) dialog = PreferencesDialog(self.app) dialog.show() dialog.save_to_disk = True dialog.accept() assert os.path.exists(os.path.join(tmpdir.strpath, 'settings.cfg')) def assert_axes_background(axes, color): assert axes.patch.get_facecolor() == color assert axes.figure.get_facecolor() == color def assert_axes_foreground(axes, color): if hasattr(axes, 'coords'): # TODO: fix this in WCSAxes assert axes.coords.frame._color == color for coord in axes.coords: assert coord.ticks.get_color() == color assert coord.ticklabels.get_color() == color assert coord.axislabels.get_color() == color else: for spine in axes.spines.values(): assert spine.get_edgecolor() == color for tick in axes.xaxis.get_ticklines() + axes.yaxis.get_ticklines(): assert tick.get_color() == color for label in axes.xaxis.get_ticklabels() + axes.yaxis.get_ticklabels(): assert label.get_color() == color assert axes.xaxis.label.get_color() == color assert axes.yaxis.label.get_color() == color def _generate_custom_viewer(): example = custom_viewer('Test Plot', x='att(x)', y='att(y)') @example.plot_data def plot_data(axes, x, y, style): axes.plot(x, y) @example.plot_subset def plot_subset(axes, x, y, style): axes.plot(x, y) @example.setup def setup(axes): pass from glue.config import qt_client for viewer in qt_client.members: if viewer.LABEL == 'Test Plot': return viewer raise Exception("Failed to find custom viewer in qt_client") @pytest.mark.skipif('PYSIDE2_INSTALLED') def test_foreground_background_settings(): d_1d = Data(x=np.random.random(100), y=np.random.random(100), label='Data 1d') d_2d = Data(x=np.random.random((100, 100)), y=np.random.random((100, 100)), label='Data 2d') dc = DataCollection([d_1d, d_2d]) app = GlueApplication(dc) # Make sure that settings change existing viewers, so we create a bunch of # viewers here. scatter1 = app.new_data_viewer(ScatterViewer) scatter1.add_data(d_1d) image1 = app.new_data_viewer(ImageViewer) image1.add_data(d_2d) histogram1 = app.new_data_viewer(HistogramViewer) histogram1.add_data(d_1d) dendrogram1 = app.new_data_viewer(DendrogramViewer) example_custom = _generate_custom_viewer() custom1 = app.new_data_viewer(example_custom) RED = (1, 0, 0, 0.5) GREEN = (0, 1, 0, 0.6) app.show() with patch('glue.config.settings') as settings: settings.FOREGROUND_COLOR = 'black' settings.BACKGROUND_COLOR = 'white' settings.DATA_COLOR = '0.5' settings.DATA_ALPHA = 0.5 settings.FONT_SIZE = 8.0 dialog = PreferencesDialog(app) dialog.show() dialog.background = RED dialog.foreground = GREEN dialog.accept() assert_axes_background(scatter1.axes, RED) assert_axes_background(image1.axes, RED) assert_axes_background(histogram1.axes, RED) assert_axes_background(dendrogram1.axes, RED) assert_axes_background(custom1.axes, RED) assert_axes_foreground(scatter1.axes, GREEN) assert_axes_foreground(image1.axes, GREEN) assert_axes_foreground(histogram1.axes, GREEN) assert_axes_foreground(dendrogram1.axes, GREEN) assert_axes_foreground(custom1.axes, GREEN) # Now make sure that new viewers also inherit these settings scatter2 = app.new_data_viewer(ScatterViewer) scatter2.add_data(d_1d) image2 = app.new_data_viewer(ImageViewer) image2.add_data(d_2d) histogram2 = app.new_data_viewer(HistogramViewer) histogram2.add_data(d_1d) dendrogram2 = app.new_data_viewer(DendrogramViewer) custom2 = app.new_data_viewer(example_custom) assert_axes_background(scatter2.axes, RED) assert_axes_background(image2.axes, RED) assert_axes_background(histogram2.axes, RED) assert_axes_background(dendrogram2.axes, RED) assert_axes_background(custom2.axes, RED) assert_axes_foreground(scatter2.axes, GREEN) assert_axes_foreground(image2.axes, GREEN) assert_axes_foreground(histogram2.axes, GREEN) assert_axes_foreground(dendrogram2.axes, GREEN) assert_axes_foreground(custom2.axes, GREEN) app.close() glueviz-1.0.1+dfsg.orig/glue/app/qt/tests/test_terminal.py0000644000175000017500000000453713605357235023154 0ustar noahfxnoahfximport os import pytest from unittest.mock import MagicMock, patch from glue.tests.helpers import requires_ipython, IPYTHON_INSTALLED CIRCLECI = os.environ.get('CIRCLECI', 'false') == 'true' if IPYTHON_INSTALLED: from ..terminal import glue_terminal @requires_ipython @pytest.mark.skipif(CIRCLECI, reason='IPython terminal tests tend to hang on CircleCI') class TestTerminal(object): def test_mpl_non_interactive(self): """IPython v0.12 sometimes turns on mpl interactive. Ensure we catch that""" import matplotlib assert not matplotlib.is_interactive() gt = glue_terminal() assert not matplotlib.is_interactive() def test_update_namespace(self): """Test that top level namespace API works without error""" gt = glue_terminal() gt.update_namespace({'x': 3}) assert 'x' in gt.namespace def test_accepts_drops(self): gt = glue_terminal() assert gt.acceptDrops() def test_drops_update_namespace(self): """DnD adds variable name to namespace""" with patch('glue.app.qt.terminal.QtWidgets.QInputDialog') as dialog: dialog.getText.return_value = 'accept_var', True gt = glue_terminal() event = MagicMock() event.mimeData().data.return_value = [5] gt.dropEvent(event) assert gt.namespace.get('accept_var') == 5 def test_cancel_drop(self): """Drop not added if user cancels dialog box""" with patch('glue.app.qt.terminal.QtWidgets.QInputDialog') as dialog: dialog.getText.return_value = 'cancel_var', False gt = glue_terminal() event = MagicMock() event.mimeData().data.return_value = [5] gt.dropEvent(event) assert 'cancel_var' not in gt.namespace def test_ignore_drag_enter(self): event = MagicMock() event.mimeData().hasFormat.return_value = False gt = glue_terminal() gt.dragEnterEvent(event) event.ignore.assert_called_once_with() def test_accept_drag_enter(self): event = MagicMock() event.mimeData().hasFormat.return_value = True gt = glue_terminal() gt.dragEnterEvent(event) event.accept.assert_called_once_with() if __name__ == "__main__": import pytest pytest.main([__file__]) glueviz-1.0.1+dfsg.orig/glue/app/qt/tests/test_save_data.py0000644000175000017500000000763513605357235023272 0ustar noahfxnoahfxfrom collections import namedtuple from unittest.mock import MagicMock, patch from qtpy.QtCore import Qt from glue.core import DataCollection, Data from glue.utils.qt import get_qapp from glue.app.qt.save_data import SaveDataDialog def components(list_widget): enabled = [] disabled = [] for idx in range(list_widget.count()): item = list_widget.item(idx) if item.flags() & Qt.ItemIsSelectable: if item.checkState() == Qt.Checked: enabled.append(item.text()) else: disabled.append(item.text()) return disabled, enabled class TestSaveDataDialog: def setup_method(self, method): self.data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') self.data2 = Data(a=[1, 2, 3], b=[2, 3, 4], label='data2') self.dc = DataCollection([self.data1, self.data2]) self.dc.new_subset_group(label='my subset', subset_state=self.data1.id['x'] > 1.5) self.x = self.data1.id['x'] self.y = self.data1.id['y'] self.a = self.data2.id['a'] self.b = self.data2.id['b'] self.app = get_qapp() self.dialog = SaveDataDialog(data_collection=self.dc) def teardown_method(self, method): self.app = None def test_defaults(self): disabled, enabled = components(self.dialog.ui.list_component) assert enabled == ['x', 'y'] assert disabled == [] def test_defaults_derived(self): self.data1['z'] = self.data1.id['x'] + 1 disabled, enabled = components(self.dialog.ui.list_component) assert enabled == ['x', 'y', 'z'] assert disabled == [] def test_change_data(self): self.dialog.ui.combosel_data.setCurrentIndex(1) disabled, enabled = components(self.dialog.ui.list_component) assert enabled == ['a', 'b'] assert disabled == [] def test_select_buttons(self): self.dialog.button_select_none.click() disabled, enabled = components(self.dialog.ui.list_component) assert enabled == [] assert disabled == ['x', 'y'] self.dialog.button_select_all.click() disabled, enabled = components(self.dialog.ui.list_component) assert enabled == ['x', 'y'] assert disabled == [] def test_accept(self): func = self._accept() func.assert_called_once_with('test_file.fits', self.data1, components=[self.x, self.y]) def test_change_accept(self): self.dialog.ui.combosel_data.setCurrentIndex(1) func = self._accept() func.assert_called_once_with('test_file.fits', self.data2, components=[self.a, self.b]) def test_change_subset_accept(self): self.dialog.ui.combosel_subset.setCurrentIndex(1) func = self._accept() func.assert_called_once_with('test_file.fits', self.data1.subsets[0], components=[self.x, self.y]) def test_deselect_accept(self): self.dialog.ui.list_component.item(1).setCheckState(Qt.Unchecked) func = self._accept() func.assert_called_once_with('test_file.fits', self.data1, components=[self.x]) def test_deselect_all(self): self.dialog.select_none() assert not self.dialog.button_ok.isEnabled() self.dialog.select_all() assert self.dialog.button_ok.isEnabled() def _accept(self): mock = MagicMock() test_exporter_cls = namedtuple('exporter', 'function label extension') test_exporter = test_exporter_cls(function=mock, label='Test', extension='') with patch('qtpy.compat.getsavefilename') as dialog: with patch('glue.config.data_exporter') as data_exporter: def test_iter(x): yield test_exporter data_exporter.__iter__ = test_iter dialog.return_value = 'test_file.fits', None self.dialog.state._sync_data_exporters() self.dialog.accept() return test_exporter.function glueviz-1.0.1+dfsg.orig/glue/app/qt/tests/test_plugin_manager.py0000644000175000017500000000276713605357235024334 0ustar noahfxnoahfxfrom unittest.mock import patch from glue import _plugin_helpers as ph from glue.main import load_plugins from ..plugin_manager import QtPluginManager def setup_function(func): from glue import config func.CFG_DIR_ORIG = config.CFG_DIR def teardown_function(func): from glue import config config.CFG_DIR = func.CFG_DIR_ORIG def test_basic_empty(tmpdir): # Test that things work when the plugin cfg file is empty from glue import config config.CFG_DIR = tmpdir.join('.glue').strpath w = QtPluginManager() w.clear() w.update_list() w.finalize() def test_basic(tmpdir): # Test that things work when the plugin cfg file is populated from glue import config config.CFG_DIR = tmpdir.join('.glue').strpath load_plugins() config = ph.PluginConfig.load() config.plugins['spectrum_tool'] = False config.plugins['pv_slicer'] = False config.save() w = QtPluginManager() w.clear() w.update_list() w.finalize() config2 = ph.PluginConfig.load() assert config.plugins == config2.plugins def test_permission_fail(tmpdir): from glue import config config.CFG_DIR = tmpdir.join('.glue').strpath # Make a *file* at that location so that reading the plugin file will fail with open(config.CFG_DIR, 'w') as f: f.write("test") config2 = ph.PluginConfig.load() with patch('qtpy.QtWidgets.QMessageBox') as qmb: w = QtPluginManager() w.finalize() assert qmb.call_count == 1 glueviz-1.0.1+dfsg.orig/glue/app/qt/tests/test_actions.py0000644000175000017500000000116313605357235022771 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 from qtpy import QtWidgets, QtGui from ..actions import GlueActionButton def test_glue_action_button(): a = QtWidgets.QAction(None) a.setToolTip("testtooltip") a.setWhatsThis("testwhatsthis") a.setIcon(QtGui.QIcon("dummy_file")) a.setText('testtext') b = GlueActionButton() b.set_action(a) # assert b.icon() == a.icon() icons are copied, apparently assert b.text() == a.text() assert b.toolTip() == a.toolTip() assert b.whatsThis() == a.whatsThis() # stays in sync a.setText('test2') assert b.text() == 'test2' glueviz-1.0.1+dfsg.orig/glue/app/qt/tests/test_layer_tree_widget.py0000644000175000017500000002031213605357235025024 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 from unittest.mock import MagicMock, patch from qtpy import QtWidgets from glue import core from glue.tests import example_data from glue.core.session import Session from ..layer_tree_widget import LayerTreeWidget, Clipboard, PlotAction class TestLayerTree(object): """ Unit tests for the layer_tree_widget class """ def setup_method(self, method): self.data = example_data.test_data() self.collect = core.data_collection.DataCollection(list(self.data)) self.hub = self.collect.hub self.session = Session(data_collection=self.collect, hub=self.hub) self.widget = LayerTreeWidget(session=self.session) self.win = QtWidgets.QMainWindow() self.win.setCentralWidget(self.widget) self.widget.setup(self.collect) for key, value in self.widget._actions.items(): self.__setattr__("%s_action" % key, value) def teardown_method(self, method): self.win.close() def select_layers(self, *layers): self.widget.ui.layerTree.set_selected_layers(layers) def remove_layer(self, layer): """ Remove a layer via the widget remove button """ self.select_layers(layer) self.widget._actions['delete']._do_action() def add_layer(self, layer=None): """ Add a layer through a hub message """ layer = layer or core.Data() self.widget.data_collection.append(layer) return layer def layer_present(self, layer): """ Test that a layer exists in the data collection """ return layer in self.collect or \ getattr(layer, 'data', None) in self.collect def test_current_layer_method_correct(self): layer = self.add_layer() self.select_layers(layer) assert self.widget.current_layer() is layer def test_add(self): """ Test that a layer exists in widget once added """ data = core.Data() assert not self.layer_present(data) self.add_layer(data) assert self.layer_present(data) def test_remove_layer(self): """ Test that widget remove button works properly """ layer = self.add_layer() self.remove_layer(layer) assert not self.layer_present(layer) def test_remove_subset_triggers_selection_changed(self): layer = self.add_layer() grp = self.collect.new_subset_group() mock = MagicMock() self.widget.ui.layerTree.selection_changed.connect(mock) self.remove_layer(grp) assert mock.call_count > 0 def test_remove_subset_layer(self): """ Test that widget remove button works properly on subset groups""" layer = self.add_layer() grp = self.collect.new_subset_group() assert self.layer_present(grp) self.remove_layer(grp) assert not self.layer_present(grp) def test_empty_removal_does_nothing(self): """ Make sure widgets are only removed when selected """ layer = self.add_layer() self.widget.ui.layerTree.clearSelection() self.widget._actions['delete']._do_action() assert self.layer_present(layer) @patch('glue.app.qt.layer_tree_widget.LinkEditor') def test_link_data(self, le): layer = self.add_layer() self.select_layers(layer) self.link_action.trigger() assert le.update_links.call_count == 1 def test_new_subset_action(self): """ new action creates a new subset group """ layer = self.add_layer() self.new_action.trigger() assert len(self.collect.subset_groups) == 1 def test_maskify_action(self): d = core.Data(x=[1, 2, 3]) s = d.new_subset() selected = MagicMock() self.maskify_action.selected_layers = selected selected.return_value = [s] self.maskify_action.trigger() assert isinstance(s.subset_state, core.subset.MaskSubsetState) def test_copy_paste_subset_action(self): layer = self.add_layer() grp = self.collect.new_subset_group() self.select_layers(grp) self.copy_action.trigger() grp2 = self.collect.new_subset_group() self.select_layers(grp2) state0 = grp2.subset_state self.paste_action.trigger() assert grp2.subset_state is not state0 def setup_two_subset_selection(self): layer = self.add_layer() g1 = self.collect.new_subset_group() g2 = self.collect.new_subset_group() self.select_layers(g1, g2) return layer def test_invert(self): layer = self.add_layer() sub = self.collect.new_subset_group() self.select_layers(sub) self.invert_action.trigger() assert isinstance(sub.subset_state, core.subset.InvertState) def test_actions_enabled_single_subset_group_selection(self): Clipboard().contents = None layer = self.add_layer() grp = self.collect.new_subset_group() self.select_layers(grp) assert self.new_action.isEnabled() assert self.copy_action.isEnabled() assert not self.paste_action.isEnabled() assert self.invert_action.isEnabled() assert self.clear_action.isEnabled() def test_actions_enabled_single_data_selection(self): layer = self.add_layer() self.select_layers(layer) assert self.new_action.isEnabled() assert not self.copy_action.isEnabled() assert not self.paste_action.isEnabled() assert not self.invert_action.isEnabled() assert not self.clear_action.isEnabled() def test_actions_enabled_multi_subset_group_selection(self): layer = self.setup_two_subset_selection() assert self.new_action.isEnabled() assert not self.copy_action.isEnabled() assert not self.paste_action.isEnabled() assert not self.invert_action.isEnabled() assert not self.clear_action.isEnabled() def test_checkable_toggle(self): self.widget.set_checkable(True) assert self.widget.is_checkable() self.widget.set_checkable(False) assert not self.widget.is_checkable() def test_load_data(self): with patch('glue.app.qt.layer_tree_widget.data_wizard') as wizard: d = core.Data(x=[1]) assert not self.layer_present(d) wizard.return_value = [d] self.widget._load_data() assert self.layer_present(d) def test_clear_subset_group(self): layer = self.add_layer() sub = self.collect.new_subset_group() self.select_layers(sub) dummy_state = MagicMock() sub.subset_state = dummy_state self.clear_action.trigger() assert sub.subset_state is not dummy_state def test_single_selection_updates_editable(self): self.widget.bind_selection_to_edit_subset() self.add_layer() grp1 = self.collect.new_subset_group() grp2 = self.collect.new_subset_group() mode = self.session.edit_subset_mode assert mode.edit_subset[0] is not grp1 self.select_layers(grp1) assert mode.edit_subset[0] is grp1 def test_multi_selection_updates_editable(self): """Selection disables edit_subset for all other data""" self.widget.bind_selection_to_edit_subset() self.add_layer() self.add_layer() grps = [self.collect.new_subset_group() for _ in range(3)] self.select_layers(*grps[:2]) mode = self.session.edit_subset_mode assert grps[0] in mode.edit_subset assert grps[1] in mode.edit_subset assert grps[2] not in mode.edit_subset def test_selection_updates_on_data_add(self): layer = self.add_layer() assert self.widget.selected_layers() == [layer] def test_selection_updates_on_subset_group_add(self): layer = self.add_layer() grp = self.collect.new_subset_group() assert self.widget.selected_layers() == [grp] def test_plot_action(self): # regression test for #364 app = MagicMock() pa = PlotAction(self.widget, app) layer = self.add_layer() grp = self.collect.new_subset_group() self.select_layers(grp) assert not pa.isEnabled() self.select_layers(layer) assert pa.isEnabled() glueviz-1.0.1+dfsg.orig/glue/app/qt/metadata.py0000644000175000017500000000173213605357235020712 0ustar noahfxnoahfximport os from collections import OrderedDict from qtpy import QtWidgets from qtpy.QtCore import Qt from glue.utils.qt import load_ui, CenteredDialog __all__ = ['MetadataDialog'] class MetadataDialog(CenteredDialog): """ A dialog to view the metadata in a data object. """ def __init__(self, data, *args, **kwargs): super(MetadataDialog, self).__init__(*args, **kwargs) self.ui = load_ui('metadata.ui', self, directory=os.path.dirname(__file__)) self.resize(400, 500) self.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint) self._text = "" for name, value in OrderedDict(data.meta).items(): QtWidgets.QTreeWidgetItem(self.ui.meta_tree.invisibleRootItem(), [name, str(value)]) if data.label: self.setWindowTitle("Metadata for {0}".format(data.label)) self.ui.label_ndim.setText(str(data.ndim)) self.ui.label_shape.setText(str(data.shape)) self.center() glueviz-1.0.1+dfsg.orig/glue/app/qt/preferences.ui0000644000175000017500000002205713527542473021426 0ustar noahfxnoahfx Form 0 0 462 375 Preferences true Save preferences to disk true Qt::Horizontal 40 20 Cancel OK true 0 User interface Global Font Size Qt::Horizontal 40 20 Reset visibility of info messages and warnings Qt::Horizontal 40 20 Qt::Horizontal Default data color: Qt::Vertical 20 40 Apply to existing datasets: Qt::Vertical 20 40 Foreground color: 100 Qt::Horizontal true Default data transparency: QComboBox::AdjustToMinimumContentsLength Black on White White on Black Custom Qt::Horizontal Background color: Theme for viewers: 0 0 Font size for most widgets (Default: 8.0) 1 8.000000000000000 56.000000000000000 8.000000000000000 combo_theme label_2 label_3 label_4 color_default_data color_foreground line checkbox_apply label_5 slider_alpha label label_6 color_background line_2 verticalSpacer_2 label_7 spinner_font_size QColorBox QLabel

glue.utils.qt.colors
glueviz-1.0.1+dfsg.orig/glue/app/qt/actions.py0000644000175000017500000000162013605357235020566 0ustar noahfxnoahfxfrom qtpy import QtWidgets from glue.icons.qt import get_icon class GlueActionButton(QtWidgets.QPushButton): def set_action(self, action, text=True): self._text = text self._action = action self.clicked.connect(action.trigger) action.changed.connect(self._sync_to_action) self._sync_to_action() def _sync_to_action(self): self.setIcon(self._action.icon()) if self._text: self.setText(self._action.text()) self.setToolTip(self._action.toolTip()) self.setWhatsThis(self._action.whatsThis()) self.setEnabled(self._action.isEnabled()) def action(name, parent, tip='', icon=None, shortcut=None): """ Factory for making a new action """ a = QtWidgets.QAction(name, parent) a.setToolTip(tip) if icon: a.setIcon(get_icon(icon)) if shortcut: a.setShortcut(shortcut) return a glueviz-1.0.1+dfsg.orig/glue/app/qt/report_crash.ui0000644000175000017500000000616513455362716021622 0ustar noahfxnoahfx FeedbackForm 0 0 418 474 Crash Report true 8 16 2 2 Crash report: Submit Feedback Qt::NoTextInteraction If possible, please let us know what you were doing when the above crash happened: true Optionally provide your email address if you want to be notified when the issue is fixed: true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttons accepted() FeedbackForm accept() 248 254 157 274 buttons rejected() FeedbackForm reject() 316 260 286 274 glueviz-1.0.1+dfsg.orig/glue/app/qt/plugin_manager.ui0000644000175000017500000000602513455362716022112 0ustar noahfxnoahfx Dialog 0 0 405 478 Plugin Manager false Select plugins to enable them, de-select to disable Qt::AlignCenter Qt::ScrollBarAlwaysOff true true true Plugin Name Select Selections will take effect next time Glue is restarted Qt::AlignCenter Qt::Horizontal 40 20 Cancel Save Configuration true false glueviz-1.0.1+dfsg.orig/glue/app/qt/mdi_area.py0000644000175000017500000000766313605357235020704 0ustar noahfxnoahfximport weakref from qtpy.QtCore import Qt from qtpy import QtCore, QtGui, QtWidgets from glue import core from glue.core.qt.mime import LAYER_MIME_TYPE, LAYERS_MIME_TYPE class GlueMdiArea(QtWidgets.QMdiArea): """Glue's MdiArea implementation. Drop events with :class:`~glue.core.data.Data` objects in :class:`~glue.utils.qt.PyMimeData` load these objects into new data viewers """ def __init__(self, application, parent=None): """ :param application: The Glue application to which this is attached :type application: :class:`~glue.app.qt.application.GlueApplication` """ super(GlueMdiArea, self).__init__(parent) self._application = weakref.ref(application) self.setAcceptDrops(True) self.setAttribute(Qt.WA_DeleteOnClose) self.setBackground(QtGui.QBrush(QtGui.QColor(250, 250, 250))) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) def addSubWindow(self, sub): super(GlueMdiArea, self).addSubWindow(sub) self.repaint() def dragEnterEvent(self, event): """ Accept the event if it has an application/py_instance format """ if event.mimeData().hasFormat(LAYERS_MIME_TYPE): event.accept() elif event.mimeData().hasFormat(LAYER_MIME_TYPE): event.accept() else: event.ignore() def dropEvent(self, event): """ Load a new data viewer if the event has a glue Data object """ md = event.mimeData() def new_layer(layer): application = self._application() if application is None: return if isinstance(layer, (core.data.BaseData, core.subset.Subset)): application.choose_new_data_viewer(layer) else: raise TypeError("Expected a Data or Subset, got {0}".format(type(layer))) if md.hasFormat(LAYER_MIME_TYPE): new_layer(md.data(LAYER_MIME_TYPE)) assert md.hasFormat(LAYERS_MIME_TYPE) for layer in md.data(LAYERS_MIME_TYPE): new_layer(layer) event.accept() def close(self): self.closeAllSubWindows() super(GlueMdiArea, self).close() def paintEvent(self, event): super(GlueMdiArea, self).paintEvent(event) painter = QtGui.QPainter(self.viewport()) painter.setPen(QtGui.QColor(210, 210, 210)) font = painter.font() font.setPointSize(font.pointSize() * 4) font.setWeight(font.Black) painter.setFont(font) rect = self.contentsRect() painter.drawText(rect, Qt.AlignHCenter | Qt.AlignVCenter, "Drag Data To Plot") def wheelEvent(self, event): # NOTE: when a scroll wheel event happens on top of a GlueMdiSubWindow, # we need to ignore it in GlueMdiArea to prevent the canvas from moving # around. I couldn't find a clean way to do this with events, so instead # in GlueMdiSubWindow I set a flag, _wheel_event, to indicate that a # wheel event has happened in a subwindow, which means the next time # the GlueMdiArea.wheelEvent gets called, we should ignore the wheel # event. any_subwindow_wheel = False for window in self.subWindowList(): if getattr(window, '_wheel_event', None): any_subwindow_wheel = True window._wheel_event = None if any_subwindow_wheel: event.ignore() return super(GlueMdiArea, self).wheelEvent(event) class GlueMdiSubWindow(QtWidgets.QMdiSubWindow): closed = QtCore.Signal() def wheelEvent(self, event): # See NOTE in GlueMdiArea.wheelEvent self._wheel_event = True super(GlueMdiSubWindow, self).wheelEvent(event) def closeEvent(self, event): super(GlueMdiSubWindow, self).closeEvent(event) self.closed.emit() glueviz-1.0.1+dfsg.orig/glue/app/qt/feedback.py0000644000175000017500000000765213605357235020665 0ustar noahfxnoahfx""" Widgets for sending feedback reports """ import os from qtpy import QtGui, QtWidgets from urllib.parse import urlencode from urllib.request import Request, urlopen from glue.utils.qt import load_ui from glue._deps import get_status_as_odict __all__ = ['submit_bug_report', 'submit_feedback'] def diagnostics(): """ Return a some system informaton useful for debugging """ versions = "" for package, version in get_status_as_odict().items(): versions += "* {0}: {1}\n".format(package, version) return versions.strip() class BaseReportWidget(QtWidgets.QDialog): def accept(self): """ Send a report to bugs.glueviz.org """ # website expects a post request with a report and specific key url = 'http://bugs.glueviz.org' values = dict(report=self.content, key='72z29Q9BzM8sgATeQdu4') data = urlencode(values) req = Request(url, data.encode('utf-8')) urlopen(req) self.close() @property def comments(self): return self.ui.area_comments.document().toPlainText() or "No comments" @property def email(self): return self.ui.value_email.text() or "Not provided" FEEDBACK_TEMPLATE = """ Email address: {email} Comments -------- {comments} System information ------------------ {report} """ class FeedbackWidget(BaseReportWidget): """ A Dialog to enter and send feedback """ def __init__(self, parent=None): super(FeedbackWidget, self).__init__(parent=parent) self.ui = load_ui('report_feedback.ui', self, directory=os.path.dirname(__file__)) self.ui.area_comments.moveCursor(QtGui.QTextCursor.Start) @property def report(self): if self.ui.checkbox_system_info.isChecked(): return diagnostics() else: return "No version information provided" @property def content(self): """ The contents of the feedback window """ return FEEDBACK_TEMPLATE.format(email=self.email, comments=self.comments, report=self.report) REPORT_TEMPLATE = """ Email address: {email} Comments -------- {comments} Report ------ {report} """ class CrashReportWidget(BaseReportWidget): """ A dialog to report crashes/errors """ def __init__(self, crash_report='', parent=None): """ :param feedback: The default feedback report :type feedback: str Feedback will be supplemented with diagnostic system information. The user can modify or add to any of this """ super(CrashReportWidget, self).__init__(parent=parent) self.ui = load_ui('report_crash.ui', self, directory=os.path.dirname(__file__)) self.ui.area_report.insertPlainText(diagnostics() + "\n\n" + crash_report) self.ui.area_comments.moveCursor(QtGui.QTextCursor.Start) @property def report(self): return self.ui.area_report.document().toPlainText() or "No report" @property def content(self): """ The contents of the feedback window """ return REPORT_TEMPLATE.format(email=self.email, comments=self.comments, report=self.report) def submit_bug_report(report=''): """ Present a user interface for sending a crash report Parameters ---------- report : str The crash report/trackback """ widget = CrashReportWidget(crash_report=report) widget.exec_() def submit_feedback(): """ Present a user interface for modifying and sending a feedback message """ widget = FeedbackWidget() widget.exec_() if __name__ == "__main__": from glue.utils.qt import get_qapp app = get_qapp() submit_bug_report(report="Crash log here") submit_feedback() glueviz-1.0.1+dfsg.orig/glue/app/qt/splash_screen.py0000644000175000017500000000273513752534424021767 0ustar noahfxnoahfximport os from qtpy import QtWidgets, QtGui from qtpy.QtCore import Qt, QRect __all__ = ['QtSplashScreen'] class QtSplashScreen(QtWidgets.QWidget): def __init__(self, *args, **kwargs): super(QtSplashScreen, self).__init__(*args, **kwargs) self.resize(627, 310) self.setStyleSheet("background-color:white;") self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.center() self.progress = QtWidgets.QProgressBar() self.layout = QtWidgets.QVBoxLayout(self) self.layout.addStretch() self.layout.addWidget(self.progress) pth = os.path.join(os.path.dirname(__file__), '..', '..', 'logo.png') self.image = QtGui.QPixmap(pth) def set_progress(self, value): self.progress.setValue(value) QtWidgets.QApplication.processEvents() # update progress bar def paintEvent(self, event): painter = QtGui.QPainter(self) painter.drawPixmap(QRect(20, 20, 587, 229), self.image) def center(self): # Adapted from StackOverflow # https://stackoverflow.com/questions/20243637/pyqt4-center-window-on-active-screen frameGm = self.frameGeometry() screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos()) centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center() frameGm.moveCenter(centerPoint) self.move(frameGm.topLeft()) glueviz-1.0.1+dfsg.orig/glue/app/qt/edit_subset_mode_toolbar.py0000644000175000017500000001763413605357235024202 0ustar noahfxnoahfxfrom qtpy import QtCore, QtWidgets from glue.core.edit_subset_mode import (NewMode, OrMode, AndNotMode, AndMode, XorMode, ReplaceMode) from glue.app.qt.actions import action from glue.utils import nonpartial, avoid_circular from glue.utils.qt import update_combobox from glue.core.message import EditSubsetMessage, SubsetMessage from glue.core.hub import HubListener from glue.icons.qt import layer_icon class EditSubsetModeToolBar(QtWidgets.QToolBar, HubListener): def __init__(self, title="Subset mode toolbar", parent=None): super(EditSubsetModeToolBar, self).__init__(title, parent) self.subset_combo = QtWidgets.QComboBox() self.subset_combo.setMinimumContentsLength(10) spacer = QtWidgets.QWidget() spacer.setMinimumSize(10, 10) spacer.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) self.addWidget(spacer) self.addWidget(QtWidgets.QLabel("Active Subset:")) self.addWidget(self.subset_combo) self._label_subset_mode = QtWidgets.QLabel("Mode:") self.addWidget(self._label_subset_mode) self.setIconSize(QtCore.QSize(16, 16)) self._group = QtWidgets.QActionGroup(self) self._modes = {} self._add_actions() self._edit_subset_mode = self.parent()._session.edit_subset_mode self._modes[self._edit_subset_mode.mode].trigger() self._backup_mode = None spacer = QtWidgets.QWidget() spacer.setMinimumSize(20, 10) spacer.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) self.parent()._hub.subscribe(self, EditSubsetMessage, handler=self._update_mode) self.parent()._hub.subscribe(self, SubsetMessage, handler=self._update_subset_combo) self._data_collection = self.parent().data_collection self._update_subset_combo() self.subset_combo.currentIndexChanged.connect(self._on_subset_combo_change) self._update_mode_visibility() def _update_subset_combo(self, msg=None): """ Set up the combo listing the subsets. """ # Prepare contents of combo box - we include a 'Create subset' item as # the last item labeldata = [(subset.label, subset) for subset in self._data_collection.subset_groups] labeldata.append(('None/Create New', None)) # We now update the combo box, but we block the signals as we don't want # this to cause the current subset being edited to be modified. self.subset_combo.blockSignals(True) # The block_signals here is to prevent signals from being turned back # on inside update_combobox. update_combobox(self.subset_combo, labeldata, block_signals=False) self.subset_combo.setIconSize(QtCore.QSize(12, 12)) for index, subset in enumerate(self._data_collection.subset_groups): self.subset_combo.setItemIcon(index, layer_icon(subset)) self.subset_combo.blockSignals(False) def _update_mode_visibility(self): visible_modes = not self.subset_combo.currentIndex() == self.subset_combo.count() - 1 if visible_modes: self._label_subset_mode.setText("Mode:") else: self._label_subset_mode.setText("(the next selection will create a subset)") for mode in self._modes.values(): if isinstance(mode, QtWidgets.QAction): mode.setVisible(visible_modes) @avoid_circular def _on_subset_combo_change(self, event=None): """ Update the EditSubsetMode when the subset combo changes. """ subset = self.subset_combo.currentData() if subset is None: self._edit_subset_mode.edit_subset = [] else: self._edit_subset_mode.edit_subset = [self.subset_combo.currentData()] if self._edit_subset_mode.mode is NewMode: self._edit_subset_mode.mode = ReplaceMode self._update_mode_visibility() # We now force the combo to be refreshed in case it included the # temporary 'Multiple subsets' entry which needs to be removed. self.subset_combo.blockSignals(True) self._update_subset_combo() self.subset_combo.blockSignals(False) @avoid_circular def _on_edit_subset_mode_change(self): """ Update the subset combo when the EditSubsetMode changes. """ # We block signals since we don't want to trigger a change in EditSubsetMode. self.subset_combo.blockSignals(True) edit_subset = self._edit_subset_mode.edit_subset if self._edit_subset_mode.mode is NewMode: index = self.subset_combo.count() - 1 elif len(edit_subset) > 1: # We temporarily add an item - we remove this if the combo changes # again. self.subset_combo.insertItem(0, 'Multiple subsets') index = 0 else: self._update_subset_combo() # In some cases, this can be called from _update_subset_combo before # EditSubsetMode has been updated, so the edit subset may still be # the old one and not exist in the list of subset groups. if edit_subset: if edit_subset[0] in self._data_collection.subset_groups: index = self._data_collection.subset_groups.index(edit_subset[0]) else: index = self.subset_combo.count() - 1 elif len(edit_subset) == 0: index = self.subset_combo.count() - 1 self.subset_combo.setCurrentIndex(index) self.subset_combo.blockSignals(False) self._update_mode_visibility() def _make_mode(self, name, tip, icon, mode): def set_mode(mode): self._edit_subset_mode.mode = mode a = action(name, self, tip, icon) a.setCheckable(True) a.triggered.connect(nonpartial(set_mode, mode)) self._group.addAction(a) self.addAction(a) self._modes[mode] = a label = name.split()[0].lower().replace('&', '') self._modes[label] = mode def _add_actions(self): self._make_mode("&Replace Mode", "Replace selection", 'glue_replace', ReplaceMode) self._make_mode("&Or Mode", "Add to selection", 'glue_or', OrMode) self._make_mode("&And Mode", "Set selection as intersection", 'glue_and', AndMode) self._make_mode("&Xor Mode", "Set selection as exclusive intersection", 'glue_xor', XorMode) self._make_mode("&Not Mode", "Remove from selection", 'glue_andnot', AndNotMode) def _update_mode(self, message): self.set_mode(message.mode) self._on_edit_subset_mode_change() def set_mode(self, mode): """Temporarily set the edit mode to mode :param mode: Name of the mode (Or, Not, And, Xor, Replace) :type mode: str """ if mode == 'new' or mode is NewMode: if self._edit_subset_mode.mode is not NewMode: self._edit_subset_mode.mode = NewMode for act in self._modes.values(): if isinstance(act, QtWidgets.QAction): act.setChecked(False) return elif isinstance(mode, str): try: mode = self._modes[mode] # label to mode class except KeyError: raise KeyError("Unrecognized mode: %s" % mode) self._backup_mode = self._backup_mode or self._edit_subset_mode.mode self._modes[mode].trigger() # mode class to action def unset_mode(self): """Restore the mode to the state before set_mode was called""" mode = self._backup_mode self._backup_mode = None if mode: self._modes[mode].trigger() glueviz-1.0.1+dfsg.orig/glue/app/qt/__init__.py0000644000175000017500000000013213605357235020662 0ustar noahfxnoahfxfrom .application import GlueApplication # noqa from . import keyboard_shortcuts # noqa glueviz-1.0.1+dfsg.orig/glue/app/qt/report_feedback.ui0000644000175000017500000000563313455362716022245 0ustar noahfxnoahfx FeedbackForm 0 0 418 474 Submit Feedback true 8 16 2 2 Please include any feedback below! true Include information on glue and dependency versions true Optionally provide your email address if you want us to be able to respond to your feedback! true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttons accepted() FeedbackForm accept() 248 254 157 274 buttons rejected() FeedbackForm reject() 316 260 286 274 glueviz-1.0.1+dfsg.orig/glue/app/qt/keyboard_shortcuts.py0000644000175000017500000000312013605357235023041 0ustar noahfxnoahfx""" Jesse Averbukh October 12, 2017 The file where keyboard shortcuts are created. In the future, many of these values will be populated using a GUI. """ from qtpy import QtCore from glue.config import keyboard_shortcut from glue.config import viewer_tool from glue.viewers.scatter.qt.data_viewer import ScatterViewer from glue.viewers.histogram.qt.data_viewer import HistogramViewer from glue.viewers.image.qt.data_viewer import ImageViewer from glue.viewers.table.qt.data_viewer import DataTableModel def check_duplicate_shortcut(key_shortcut): """ Checks to make sure that a key_shortcut is not already used within the glue application somewhere else. This will become simpler with the implementation of a GUI """ list_of_shortcuts = [] for k in viewer_tool.__iter__(): list_of_shortcuts.append(viewer_tool.members[k].shortcut) if key_shortcut in list_of_shortcuts: return True return False @keyboard_shortcut(QtCore.Qt.Key_Tab, [ImageViewer, HistogramViewer, ScatterViewer, DataTableModel]) def cycle_through_windows(session): """ Cycle through all active windows within the current tab """ if check_duplicate_shortcut("tab"): return return session.application.current_tab.activateNextSubWindow() @keyboard_shortcut(QtCore.Qt.Key_Backspace, [ImageViewer, HistogramViewer, ScatterViewer, DataTableModel]) def delete_current_window(session): """ Deletes the currently active window """ if check_duplicate_shortcut("backspace"): return return session.application._viewer_in_focus.close(warn=True) glueviz-1.0.1+dfsg.orig/glue/app/qt/layer_tree_widget.py0000644000175000017500000005007613605357235022635 0ustar noahfxnoahfx""" Class which embellishes the DataCollectionView with buttons and actions for editing the data collection """ import os import weakref from qtpy import QtCore, QtWidgets, QtGui from qtpy.QtCore import Qt from glue.core.edit_subset_mode import AndMode, OrMode, XorMode, AndNotMode from glue.config import layer_action from glue import core from glue.dialogs.link_editor.qt import LinkEditor from glue.icons.qt import get_icon from glue.dialogs.component_arithmetic.qt import ArithmeticEditorWidget from glue.dialogs.component_manager.qt import ComponentManagerWidget from glue.dialogs.subset_facet.qt import SubsetFacetDialog from glue.dialogs.data_wizard.qt import data_wizard from glue.utils import nonpartial from glue.utils.decorators import avoid_circular from glue.utils.qt import load_ui from glue.core.message import EditSubsetMessage from glue.core.hub import HubListener from glue.app.qt.metadata import MetadataDialog @core.decorators.singleton class Clipboard(object): def __init__(self): self.contents = None class LayerAction(QtWidgets.QAction): _title = '' _icon = None _tooltip = None _enabled_on_init = False _shortcut = None _shortcut_context = Qt.WidgetShortcut def __init__(self, layer_tree_widget): self._parent = layer_tree_widget.ui.layerTree super(LayerAction, self).__init__(self._title, self._parent) self._layer_tree = layer_tree_widget if self._icon: self.setIcon(get_icon(self._icon)) if self._tooltip: self.setToolTip(self._tooltip) self.setEnabled(self._enabled_on_init) if self._shortcut_context is not None: self.setShortcutContext(self._shortcut_context) if self._shortcut: self.setShortcut(self._shortcut) self._parent.addAction(self) self._connect() self.setIconVisibleInMenu(False) def _connect(self): self._parent.selection_changed.connect(nonpartial(self.update_enabled)) self.triggered.connect(nonpartial(self._do_action)) def selected_layers(self): return self._layer_tree.selected_layers() @property def data_collection(self): return self._layer_tree.data_collection def update_enabled(self): enabled = self._can_trigger() self.setEnabled(enabled) self.setVisible(enabled) def single_selection(self): return len(self.selected_layers()) == 1 def single_selection_data(self): layers = self.selected_layers() if len(layers) != 1: return False return isinstance(layers[0], core.Data) def single_selection_subset(self): layers = self.selected_layers() if len(layers) != 1: return False return isinstance(layers[0], core.Subset) def single_selection_subset_group(self): layers = self.selected_layers() if len(layers) != 1: return False return isinstance(layers[0], core.SubsetGroup) def _can_trigger(self): raise NotImplementedError def _do_action(self): raise NotImplementedError class PlotAction(LayerAction): """Visualize the selection. Requires GlueApplication""" _title = "Create new viewer" _tooltip = "Create a new viewer from this object" def __init__(self, tree, app): super(PlotAction, self).__init__(tree) self.app = weakref.ref(app) def _can_trigger(self): if not self.single_selection(): return False return isinstance(self.selected_layers()[0], (core.Subset, core.Data)) def _do_action(self): assert self._can_trigger() data = self.selected_layers()[0].data app = self.app() if app is not None: app.choose_new_data_viewer(data) class FacetAction(LayerAction): """Add a sequence of subsets which facet a ComponentID""" _title = "Create faceted subsets" _tooltip = "Create faceted subsets" def _can_trigger(self): return len(self._layer_tree.data_collection) > 0 def _do_action(self): layers = self.selected_layers() try: default = layers[0].data except (AttributeError, TypeError, IndexError): default = None SubsetFacetDialog.facet(self._layer_tree.data_collection, parent=self._layer_tree, default=default) class MetadataAction(LayerAction): _title = "View metadata/header" _tooltip = "View metadata/header" _shortcut = QtGui.QKeySequence('Ctrl+I') def _can_trigger(self): return self.single_selection_data() or self.single_selection_subset() def _do_action(self): layers = self.selected_layers() if isinstance(layers[0], core.Subset): data = layers[0].data else: data = layers[0] MetadataDialog(data).exec_() class NewAction(LayerAction): _title = "New Subset" _tooltip = "Create a new subset" _shortcut = QtGui.QKeySequence('Ctrl+Shift+N') _icon = 'glue_subset' def _can_trigger(self): return len(self.data_collection) > 0 def _do_action(self): assert self._can_trigger() self.data_collection.new_subset_group() class ClearAction(LayerAction): _title = "Clear subset" _tooltip = "Clear current subset" _shortcut = QtGui.QKeySequence('Ctrl+K') def _can_trigger(self): return self.single_selection_subset_group() def _do_action(self): assert self._can_trigger() subset = self.selected_layers()[0] subset.subset_state = core.subset.SubsetState() class DeleteAction(LayerAction): _title = "Delete Layer" _tooltip = "Delete the selected data and/or subset Groups" _shortcut = QtGui.QKeySequence(Qt.Key_Backspace) def _can_trigger(self): selection = self.selected_layers() return all(isinstance(s, (core.Data, core.SubsetGroup)) for s in selection) def _do_action(self): assert self._can_trigger() selection = self.selected_layers() for s in selection: if isinstance(s, core.Data): self._layer_tree.data_collection.remove(s) else: assert isinstance(s, core.SubsetGroup) self._layer_tree.data_collection.remove_subset_group(s) class LinkAction(LayerAction): _title = "Link Data" _tooltip = "Define links between data sets" _data_link_message = "Define links between data sets" _icon = 'glue_link' def __init__(self, *args, **kwargs): super(LinkAction, self).__init__(*args, **kwargs) def _can_trigger(self): return len(self.data_collection) > 0 def _do_action(self): LinkEditor.update_links(self.data_collection) class MaskifySubsetAction(LayerAction): _title = "Transform subset to pixel mask" _tooltip = "Transform a subset to a pixel mask" def _can_trigger(self): return self.single_selection() and \ isinstance(self.selected_layers()[0], core.Subset) def _do_action(self): s = self.selected_layers()[0] s.subset_state = s.state_as_mask() class ExportDataAction(LayerAction): _title = "Export data values" _tooltip = "Save the data to a file" def _can_trigger(self): return self.single_selection_data() def _do_action(self): assert self._can_trigger() data = self.selected_layers()[0] from glue.core.data_exporters.qt.dialog import export_data export_data(data) class ArithmeticAction(LayerAction): _title = "Add/edit arithmetic attributes" _tooltip = "Add/edit attributes derived from existing ones" def _can_trigger(self): return self.single_selection_data() def _do_action(self): assert self._can_trigger() data = self.selected_layers()[0] print(data.label) dialog = ArithmeticEditorWidget(self._layer_tree.data_collection, initial_data=data) dialog.exec_() class ManageComponentsAction(LayerAction): _title = "Reorder/rename data attributes" _tooltip = "Reorder/rename data attributes" def _can_trigger(self): return self.single_selection_data() def _do_action(self): assert self._can_trigger() data = self.selected_layers()[0] print(data.label) dialog = ComponentManagerWidget(self._layer_tree.data_collection, initial_data=data) dialog.exec_() class ExportSubsetAction(ExportDataAction): _title = "Export subset values" _tooltip = "Save the data subset to a file" def _can_trigger(self): return self.single_selection_subset() class ImportSubsetMaskAction(LayerAction): _title = "Import subset mask(s)" _tooltip = "Import subset mask from a file" def _can_trigger(self): return self.single_selection_subset() or self.single_selection_data() def _do_action(self): assert self._can_trigger() data = self.selected_layers()[0] from glue.io.qt.subset_mask import QtSubsetMaskImporter QtSubsetMaskImporter().run(data, self._layer_tree._data_collection) class ExportSubsetMaskAction(LayerAction): _title = "Export subset mask(s)" _tooltip = "Export subset mask to a file" def _can_trigger(self): return (len(self.data_collection.subset_groups) > 0 and (self.single_selection_subset() or self.single_selection_data())) def _do_action(self): assert self._can_trigger() data = self.selected_layers()[0] from glue.io.qt.subset_mask import QtSubsetMaskExporter QtSubsetMaskExporter().run(data) class CopyAction(LayerAction): _title = "Copy subset" _tooltip = "Copy the definition for the selected subset" _shortcut = QtGui.QKeySequence.Copy def _can_trigger(self): return self.single_selection_subset_group() def _do_action(self): assert self._can_trigger() subset = self.selected_layers()[0] Clipboard().contents = subset.subset_state class PasteAction(LayerAction): _title = "Paste subset" _tooltip = "Overwrite selected subset with contents from clipboard" _shortcut = QtGui.QKeySequence.Paste def _can_trigger(self): if not self.single_selection_subset_group(): return False cnt = Clipboard().contents if not isinstance(cnt, core.subset.SubsetState): return False return True def _do_action(self): assert self._can_trigger() layer = self.selected_layers()[0] layer.paste(Clipboard().contents) class PasteSpecialAction(PasteAction): _title = "Paste Special..." _tooltip = "Paste with boolean logic" _shortcut = None def __init__(self, *args, **kwargs): super(PasteSpecialAction, self).__init__(*args, **kwargs) self.setMenu(self.menu()) def menu(self): m = QtWidgets.QMenu() a = QtWidgets.QAction("Or", m) a.setIcon(get_icon('glue_or')) a.triggered.connect(nonpartial(self._paste, OrMode)) m.addAction(a) a = QtWidgets.QAction("And", m) a.setIcon(get_icon('glue_and')) a.triggered.connect(nonpartial(self._paste, AndMode)) m.addAction(a) a = QtWidgets.QAction("XOR", m) a.setIcon(get_icon('glue_xor')) a.triggered.connect(nonpartial(self._paste, XorMode)) m.addAction(a) a = QtWidgets.QAction("Not", m) a.setIcon(get_icon('glue_andnot')) a.triggered.connect(nonpartial(self._paste, AndNotMode)) m.addAction(a) return m def _paste(self, mode): if not self._can_trigger(): return assert self._can_trigger() layer = self.selected_layers()[0] mode(layer, Clipboard().contents) def _do_action(self): pass class Inverter(LayerAction): _title = "Invert Subset" _tooltip = "Invert selected subset" def _can_trigger(self): """ Can trigger iff one subset is selected """ return self.single_selection_subset_group() def _do_action(self): """Replace selected subset with its inverse""" assert self._can_trigger() subset, = self.selected_layers() subset.subset_state = core.subset.InvertState(subset.subset_state) class MergeAction(LayerAction): _title = "Merge datasets" _tooltip = "Merge the selected datasets into a single dataset" def _can_trigger(self): layers = self.selected_layers() if len(layers) < 2: return False if not all(isinstance(l, core.Data) for l in layers): return False shp = layers[0].shape return all(d.shape == shp for d in layers[1:]) def _do_action(self): self.data_collection.merge(*self.selected_layers()) class UserAction(LayerAction): """ User-defined callback functions to expose. Users register new actions via the :member:`glue.config.layer_action` member Callback functions are passed the layer (or list of layers) and data collection """ def __init__(self, layer_tree_widget, callback=None, label='User Action', tooltip=None, icon=None, single=False, data=False, subset_group=False, subset=False): self._title = label self._tooltip = tooltip self._icon = icon self._single = single self._data = data self._subset_group = subset_group self._subset = subset self._callback = callback super(UserAction, self).__init__(layer_tree_widget) def _can_trigger(self): if self._single: if self.single_selection(): layer = self.selected_layers()[0] if isinstance(layer, core.Data) and self._data: return True elif isinstance(layer, core.SubsetGroup) and self._subset_group: return True elif isinstance(layer, core.Subset) and self._subset: return True else: return False else: return False else: return len(self.selected_layers()) > 0 def _do_action(self): if self._single: subset = self.selected_layers()[0] return self._callback(subset, self.data_collection) else: return self._callback(self.selected_layers(), self.data_collection) class LayerCommunicator(QtCore.QObject): layer_check_changed = QtCore.Signal(object, bool) class LayerTreeWidget(QtWidgets.QMainWindow, HubListener): """The layertree widget provides a way to visualize the various data and subset layers in a Glue session. This widget relies on sending/receiving messages to/from the hub to maintin synchronization with the data collection it manages. If it isn't attached to a hub, interactions may not propagate properly. """ def __init__(self, session=None, parent=None): super(LayerTreeWidget, self).__init__(parent) self.session = session self.ui = load_ui('layer_tree_widget.ui', None, directory=os.path.dirname(__file__)) self.setCentralWidget(self.ui) self._signals = LayerCommunicator() self._is_checkable = True self._layer_check_changed = self._signals.layer_check_changed self._layer_dict = {} self._actions = {} self._create_actions() self._data_collection = None self._hub = None self.ui.layerTree.setDragEnabled(True) self.setMinimumSize(300, 0) @property def data_collection(self): return self._data_collection def setup(self, collection): self._data_collection = collection self._hub = collection.hub self.ui.layerTree.set_data_collection(collection) self.bind_selection_to_edit_subset() def unregister(self, hub): """Unsubscribe from hub""" self.ui.layerTree.unregister(hub) def is_checkable(self): """ Return whether checkboxes appear next o layers""" return self.ui.layerTree.checkable def set_checkable(self, state): """ Setw hether checkboxes appear next o layers""" self.ui.layerTree.checkable = state def selected_layers(self): """ Return a list of selected layers (subsets and data objects) """ return self.ui.layerTree.selected_layers() def current_layer(self): """Return the layer if a single item is selected, else None """ layers = self.selected_layers() if len(layers) == 1: return layers[0] def actions(self): """ Return the list of actions attached to this widget """ return self.ui.layerTree.actions() def bind_selection_to_edit_subset(self): self.ui.layerTree.selection_changed.connect(self._update_edit_subset) self._data_collection.hub.subscribe(self, EditSubsetMessage, handler=self._update_subset_selection) @avoid_circular def _update_subset_selection(self, message): """ Update current selection to match edit subsets """ self.ui.layerTree.set_selected_layers(message.subset) @avoid_circular def _update_edit_subset(self): """ Update edit subsets to match current selection """ mode = self.session.edit_subset_mode mode.edit_subset = [s for s in self.selected_layers() if isinstance(s, core.SubsetGroup)] def _create_actions(self): tree = self.ui.layerTree sep = QtWidgets.QAction("", tree) sep.setSeparator(True) tree.addAction(sep) # Actions relating to I/O self._actions['save_data'] = ExportDataAction(self) self._actions['save_subset'] = ExportSubsetAction(self) self._actions['import_subset_mask'] = ImportSubsetMaskAction(self) self._actions['export_subset_mask'] = ExportSubsetMaskAction(self) self._actions['copy'] = CopyAction(self) self._actions['paste'] = PasteAction(self) self._actions['paste_special'] = PasteSpecialAction(self) self._actions['invert'] = Inverter(self) self._actions['new'] = NewAction(self) self._actions['clear'] = ClearAction(self) self._actions['delete'] = DeleteAction(self) self._actions['facet'] = FacetAction(self) self._actions['metadata'] = MetadataAction(self) self._actions['merge'] = MergeAction(self) self._actions['maskify'] = MaskifySubsetAction(self) self._actions['link'] = LinkAction(self) self._actions['new_component'] = ArithmeticAction(self) self._actions['manage_components'] = ManageComponentsAction(self) # Add user-defined layer actions. Note that _asdict is actually a public # method, but just has an underscore to prevent conflict with # namedtuple attributes. for item in layer_action: self._actions[item.label] = UserAction(self, **item._asdict()) # right click pulls up menu tree.setContextMenuPolicy(Qt.ActionsContextMenu) def _on_item_change(self, item, column): """emit check_state_changed signal when checkbox clicked""" if item is None or item not in self or column != 0: return is_checked = item.checkState(0) == Qt.Checked layer = self[item] self._layer_check_changed.emit(layer, is_checked) def _load_data(self): """ Interactively loads data from a data set. Adds as new layer """ layers = data_wizard() self.data_collection.extend(layers) def __getitem__(self, key): raise NotImplementedError() return self.ui.layerTree[key] def __setitem__(self, key, value): raise NotImplementedError() self.ui.layerTree[key] = value def __contains__(self, obj): return obj in self.ui.layerTree def __len__(self): return len(self.ui.layerTree) if __name__ == "__main__": from glue.core.data_collection import DataCollection collection = DataCollection() from glue.utils.qt import get_qapp app = get_qapp() widget = LayerTreeWidget() widget.setup(collection) widget.show() app.exec_() glueviz-1.0.1+dfsg.orig/glue/app/qt/merge.ui0000644000175000017500000000466613522025220020207 0ustar noahfxnoahfx MergeDialog 0 0 443 410 Merge Datasets true 8 Several of the datasets (including at least one that you have added) have the same shape. Do you want to merge them? Merging means that these separate datasets will then become attributes of a single dataset. false Qt::AlignJustify|Qt::AlignVCenter true Final label Do not merge Qt::Horizontal 40 20 Merge the selected datasets true glueviz-1.0.1+dfsg.orig/glue/app/qt/application.py0000644000175000017500000014151113657331513021433 0ustar noahfxnoahfx# pylint: disable=W0223 import os import sys import weakref import warnings import webbrowser from qtpy import QtCore, QtWidgets, QtGui, compat from qtpy.QtCore import Qt from glue.core.application_base import Application from glue.core.message import ApplicationClosedMessage, DataCollectionMessage, SettingsChangeMessage from glue.core import command, BaseData from glue.core.coordinates import WCSCoordinates from glue import env from glue.main import load_plugins from glue.icons.qt import get_icon from glue.utils.qt import get_qapp, update_global_font_size from glue.app.qt.actions import action from glue.dialogs.data_wizard.qt import data_wizard from glue.dialogs.link_editor.qt import LinkEditor from glue.dialogs.autolinker.qt import run_autolinker from glue.dialogs.component_arithmetic.qt import ArithmeticEditorWidget from glue.app.qt.edit_subset_mode_toolbar import EditSubsetModeToolBar from glue.app.qt.mdi_area import GlueMdiArea from glue.app.qt.layer_tree_widget import PlotAction, LayerTreeWidget from glue.app.qt.preferences import PreferencesDialog from glue.viewers.common.qt.data_viewer import DataViewer from glue.viewers.scatter.qt import ScatterViewer from glue.viewers.image.qt import ImageViewer from glue.utils import nonpartial, defer_draw from glue.utils.qt import (pick_class, GlueTabBar, qurl_to_path, set_cursor_cm, messagebox_on_error, load_ui) from glue.app.qt.feedback import submit_bug_report, submit_feedback from glue.app.qt.plugin_manager import QtPluginManager from glue.app.qt.versions import QVersionsDialog from glue.app.qt.terminal import glue_terminal, IPythonTerminalError from glue.config import qt_fixed_layout_tab, qt_client, startup_action, keyboard_shortcut from glue.app.qt.save_data import SaveDataDialog __all__ = ['GlueApplication'] DOCS_URL = 'http://www.glueviz.org' def _fix_ipython_pylab(): try: from IPython import get_ipython except ImportError: return shell = get_ipython() if shell is None: return from IPython.core.error import UsageError # UnknownBackend exists only in IPython 5.0 and above, so if it doesn't # exist we just set UnknownBackend to be a fake exception class try: from IPython.terminal.pt_inputhooks import UnknownBackend except ImportError: class UnknownBackend(Exception): pass try: shell.enable_pylab('agg', import_all=True) except ValueError: # if the shell is a normal terminal shell, we get here pass except UnknownBackend: # if the shell is a normal terminal shell, we can also get here pass except UsageError: pass except KeyError: # old versions of ipython pass # Make sure we disable interactive mode (where figures get redrawn for # every single Matplotlib command) import matplotlib matplotlib.interactive(False) class GlueLogger(QtWidgets.QWidget): """ A window to display error messages """ def __init__(self, button_console, parent=None): super(GlueLogger, self).__init__(parent) self.button_console = button_console self.button_stylesheet = button_console.styleSheet() self.button_console.clicked.connect(self._show) self._text = QtWidgets.QTextEdit() self._text.setTextInteractionFlags(Qt.TextSelectableByMouse) clear = QtWidgets.QPushButton("Clear") clear.clicked.connect(self._clear) report = QtWidgets.QPushButton("Send Bug Report") report.clicked.connect(self._send_report) if isinstance(sys.stderr, GlueLogger): if isinstance(sys.stderr._stderr_original, GlueLogger): raise Exception('Too many nested GlueLoggers') self._stderr_original = sys.stderr._stderr_original else: self._stderr_original = sys.stderr sys.stderr = self l = QtWidgets.QVBoxLayout() h = QtWidgets.QHBoxLayout() l.setContentsMargins(2, 2, 2, 2) l.setSpacing(2) h.setContentsMargins(0, 0, 0, 0) l.addWidget(self._text) h.insertStretch(0) h.addWidget(report) h.addWidget(clear) l.addLayout(h) self.setLayout(l) def _set_console_button(self, attention): if attention: style = 'color: red; text-decoration: underline;' else: style = self.button_stylesheet try: self.button_console.setStyleSheet(style) except RuntimeError: # Prevent RuntimeError: wrapped C/C++ object of type QToolButton has been deleted pass def write(self, message): """ Interface for sys.excepthook """ # On Windows, sys.stderr can sometimes be None, in which case we only # show the warnings/errors in the graphical glue logger. if self._stderr_original is not None: self._stderr_original.write(message) self._text.moveCursor(QtGui.QTextCursor.End) self._text.insertPlainText(message) self._set_console_button(attention=True) def flush(self): """ Interface for sys.excepthook """ pass def _send_report(self, *args): """ Send the contents of the log as a bug report """ text = self._text.document().toPlainText() submit_bug_report(text) def _clear(self, *args): """ Erase the log """ self._text.setText('') self._set_console_button(attention=False) self.close() def _show(self): """ Show the log """ self.show() self.raise_() def keyPressEvent(self, event): """ Hide window on escape key """ if event.key() == Qt.Key_Escape: self.hide() def closeEvent(self, event): if sys.stderr is self: sys.stderr = self._stderr_original class ExportHelper(object): """ This class is needed because setting up the callbacks requires using nonpartial but if the callback was a method on GlueApplication this would result in a circular reference - hence we use a helper object with a weak reference to the application. """ def __init__(self, app): self.app = weakref.ref(app) @messagebox_on_error("Failed to export session") def _choose_export_session(self, saver, checker, outmode): app = self.app() if app is None: return checker(app) if outmode is None: return saver(app) elif outmode in ['file', 'directory']: outfile, file_filter = compat.getsavefilename(parent=app) if not outfile: return return saver(app, outfile) else: assert outmode == 'label' label, ok = QtWidgets.QInputDialog.getText(app, 'Choose a label:', 'Choose a label:') if not ok: return return saver(app, label) class ImportHelper(object): def __init__(self, app): self.app = weakref.ref(app) def _choose_load_data_wizard(self, *args): self._choose_load_data(data_importer=data_wizard) def _choose_load_data(self, data_importer=None): app = self.app() if app is None: return if data_importer is None: app.add_datasets(data_wizard()) else: data = data_importer() if not isinstance(data, list): raise TypeError("Data loader should return list of " "Data objects") for item in data: if not isinstance(item, BaseData): raise TypeError("Data loader should return list of " "Data objects") app.add_datasets(data) class GlueApplication(Application, QtWidgets.QMainWindow): """ The main GUI application for the Qt frontend""" def __init__(self, data_collection=None, session=None): # At this point we need to check if a Qt application already exists - # this happens for example if using the %gui qt/qt5 mode in Jupyter. We # should keep a reference to the original icon so that we can restore it # later self._original_app = QtWidgets.QApplication.instance() if self._original_app is not None: self._original_icon = self._original_app.windowIcon() self._export_helper = ExportHelper(self) self._import_helper = ImportHelper(self) # Now we can get the application instance, which involves setting it # up if it doesn't already exist. self.app = get_qapp() QtWidgets.QMainWindow.__init__(self) Application.__init__(self, data_collection=data_collection, session=session) # Pull in any keybindings from an external file self.keybindings = keyboard_shortcut icon = get_icon('app_icon') self.app.setWindowIcon(icon) # Even though we loaded the plugins in start_glue, we re-load them here # in case glue was started directly by initializing this class. load_plugins(require_qt_plugins=True) self.set_window_title() self.setWindowIcon(icon) self.setAttribute(Qt.WA_DeleteOnClose) self._actions = {} self._terminal = None self._setup_ui() self.tab_widget.setMovable(True) self.tab_widget.setTabsClosable(True) # The following is a counter that never goes down, even if tabs are # deleted (this is by design, to avoid having two tabs called the # same if a tab is removed then a new one added again) self._total_tab_count = 0 lwidget = self._layer_widget a = PlotAction(lwidget, self) lwidget.ui.layerTree.addAction(a) self._tweak_geometry() self._create_actions() self._create_menu() self._connect() self.new_tab() self._update_viewer_in_focus() def _update_viewer_in_focus(self, *args): if not hasattr(self, '_viewer_in_focus'): self._viewer_in_focus = None mdi_area = self.current_tab active = mdi_area.activeSubWindow() # Disable any active tool in the viewer that was previously in focus. # Note that we want to do this even if active is None, which means that # the user may have switched application. if (self._viewer_in_focus is not None and (active is None or active.widget() is not self._viewer_in_focus)): try: self._viewer_in_focus.toolbar.active_tool = None except AttributeError: pass # not all viewers have toolbars if active is None: first_viewer = None for win in mdi_area.subWindowList(): if self._viewer_in_focus is win.widget(): break elif isinstance(win.widget(), DataViewer): first_viewer = win.widget() else: self._viewer_in_focus = first_viewer self._update_focus_decoration() self._update_plot_dashboard() else: self._viewer_in_focus = active.widget() self._update_focus_decoration() self._update_plot_dashboard() def run_startup_action(self, name): if name in startup_action.members: startup_action.members[name](self.session, self.data_collection) else: raise Exception("Unknown startup action: {0}".format(name)) def _setup_ui(self): self._ui = load_ui('application.ui', None, directory=os.path.dirname(__file__)) self.setCentralWidget(self._ui) self._ui.tabWidget.setTabBar(GlueTabBar()) lw = LayerTreeWidget(session=self._session) lw.set_checkable(False) self._vb = QtWidgets.QVBoxLayout() self._vb.setContentsMargins(0, 0, 0, 0) self._vb.addWidget(lw) self._ui.data_layers.setLayout(self._vb) self._layer_widget = lw # Data toolbar self._data_toolbar = QtWidgets.QToolBar("Session and Data toolbar") self._data_toolbar.setIconSize(QtCore.QSize(16, 16)) self._button_open_session = QtWidgets.QToolButton() self._button_open_session.setText("Open Session") self._button_open_session.setIcon(get_icon('glue_open')) self._button_open_session.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._button_open_session.clicked.connect(self._restore_session) self._data_toolbar.addWidget(self._button_open_session) self._button_save_session = QtWidgets.QToolButton() self._button_save_session.setText("Export Session") self._button_save_session.setIcon(get_icon('glue_filesave')) self._button_save_session.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._button_save_session.clicked.connect(self._choose_save_session) self._data_toolbar.addWidget(self._button_save_session) self._button_open_data = QtWidgets.QToolButton() self._button_open_data.setText("Import Data") self._button_open_data.setIcon(get_icon('glue_open')) self._button_open_data.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._button_open_data.clicked.connect(self._import_helper._choose_load_data_wizard) self._data_toolbar.addWidget(self._button_open_data) self._button_save_data = QtWidgets.QToolButton() self._button_save_data.setText("Export Data/Subsets") self._button_save_data.setIcon(get_icon('glue_filesave')) self._button_save_data.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._button_save_data.clicked.connect(self._choose_save_data) self._data_toolbar.addWidget(self._button_save_data) self._button_link_data = QtWidgets.QToolButton() self._button_link_data.setText("Link Data") self._button_link_data.setIcon(get_icon('glue_link')) self._button_link_data.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._button_link_data.clicked.connect(self._set_up_links) self._data_toolbar.addWidget(self._button_link_data) self._button_edit_components = QtWidgets.QToolButton() self._button_edit_components.setText("Arithmetic attributes") self._button_edit_components.setIcon(get_icon('arithmetic')) self._button_edit_components.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._button_edit_components.clicked.connect(self._artihmetic_dialog) self._data_toolbar.addWidget(self._button_edit_components) self.addToolBar(self._data_toolbar) self._on_data_collection_change() # Selection mode toolbar tbar = EditSubsetModeToolBar(parent=self) self._mode_toolbar = tbar self.addToolBar(self._mode_toolbar) # Error console toolbar self._console_toolbar = QtWidgets.QToolBar('Advanced toolbar') self._console_toolbar.setIconSize(QtCore.QSize(14, 14)) spacer = QtWidgets.QWidget() spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) self._console_toolbar.addWidget(spacer) self._button_ipython = QtWidgets.QToolButton() self._button_ipython.setCheckable(True) self._button_ipython.setText("Terminal") self._button_ipython.setIcon(get_icon('IPythonConsole')) self._button_ipython.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._button_ipython.clicked.connect(self._toggle_terminal) self._console_toolbar.addWidget(self._button_ipython) self._button_preferences = QtWidgets.QToolButton() self._button_preferences.setText("Preferences") self._button_preferences.setIcon(get_icon('glue_settings')) self._button_preferences.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._button_preferences.clicked.connect(self._edit_settings) self._console_toolbar.addWidget(self._button_preferences) self._button_console = QtWidgets.QToolButton() self._button_console.setText("Error Console") self._button_console.setToolButtonStyle(Qt.ToolButtonTextOnly) self._console_toolbar.addWidget(self._button_console) self.addToolBar(self._console_toolbar) self._log = GlueLogger(button_console=self._button_console) self._log.window().setWindowTitle("Console Log") self._log.resize(550, 550) self._log.hide() self._hub.subscribe(self, DataCollectionMessage, handler=self._on_data_collection_change) self._hub.subscribe(self, SettingsChangeMessage, handler=self._on_ui_settings_change) def _artihmetic_dialog(self, *event): dialog = ArithmeticEditorWidget(self.data_collection) dialog.exec_() def _on_data_collection_change(self, *event): self._button_save_data.setEnabled(len(self.data_collection) > 0) self._button_link_data.setEnabled(len(self.data_collection) > 1) self._button_edit_components.setEnabled(len(self.data_collection) > 0) def _on_ui_settings_change(self, *event): update_global_font_size() def keyPressEvent(self, event): if self.current_tab.activeSubWindow() and self.current_tab.activeSubWindow().widget(): active_window = self.current_tab.activeSubWindow().widget() else: active_window = None # keybindings is a data structure in the form of dict(dict) # which uses the DataViewer as the first key, the key pressed # as the second key, and the function associated with those two # as the value. if type(active_window) in self.keybindings: for k, func in self.keybindings.members[type(active_window)].items(): if event.key() == k: func(self.session) return # If key does not correspond with viewers, it might correspond # with the global application, thus, None if None in self.keybindings: for k, func in self.keybindings.members[None].items(): if event.key() == k: func(self.session) return return super(GlueApplication, self).keyPressEvent(event) def _set_up_links(self, event): LinkEditor.update_links(self.data_collection) def _tweak_geometry(self): """Maximize window by default.""" self._ui.main_splitter.setStretchFactor(0, 1) self._ui.main_splitter.setStretchFactor(1, 9) self._ui.data_plot_splitter.setStretchFactor(0, 1) self._ui.data_plot_splitter.setStretchFactor(1, 2) self._ui.data_plot_splitter.setStretchFactor(2, 1) @property def tab_widget(self): return self._ui.tabWidget @property def tab_bar(self): return self._ui.tabWidget.tabBar() @property def tab_count(self): """ The number of open tabs """ return self._ui.tabWidget.count() @property def current_tab(self): return self._ui.tabWidget.currentWidget() def get_tab_index(self, widget): for idx in range(self.tab_count): if self.tab(idx) == widget: return idx raise Exception("Tab not found") def tab(self, index=None): if index is None: return self.current_tab return self._ui.tabWidget.widget(index) def new_tab(self, *args): """Spawn a new tab page""" layout = QtWidgets.QGridLayout() layout.setSpacing(1) layout.setContentsMargins(0, 0, 0, 0) widget = GlueMdiArea(self) widget.setLayout(layout) tab = self.tab_widget self._total_tab_count += 1 tab.addTab(widget, str("Tab %i" % self._total_tab_count)) tab.setCurrentWidget(widget) widget.subWindowActivated.connect(self._update_viewer_in_focus) def close_tab(self, index, warn=True): """ Close a tab window and all associated data viewers """ # do not delete the last tab if self.tab_widget.count() == 1: return w = self.tab_widget.widget(index) if len(w.subWindowList()) > 0: if warn and not os.environ.get('GLUE_TESTING'): buttons = QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel dialog = QtWidgets.QMessageBox.warning( self, "Confirm Close", "Are you sure you want to close this tab? " "This will close all data viewers in the tab.", buttons=buttons, defaultButton=QtWidgets.QMessageBox.Cancel) if not dialog == QtWidgets.QMessageBox.Ok: return for window in w.subWindowList(): widget = window.widget() if isinstance(widget, DataViewer): widget.close(warn=False) w.close() self.tab_widget.removeTab(index) def add_widget(self, new_widget, label=None, tab=None, hold_position=False): """ Add a widget to one of the tabs. Returns the window that this widget is wrapped in. :param new_widget: new QtWidgets.QWidget to add :param label: label for the new window. Optional :type label: str :param tab: Tab to add to. Optional (default: current tab) :type tab: int :param hold_position: If True, then override Qt's default placement and retain the original position of new_widget :type hold_position: bool """ # Find first tab that supports addSubWindow if tab is None: if hasattr(self.current_tab, 'addSubWindow'): pass else: for tab in range(self.tab_count): page = self.tab(tab) if hasattr(page, 'addSubWindow'): break else: self.new_tab() tab = self.tab_count - 1 page = self.tab(tab) pos = getattr(new_widget, 'position', None) sub = new_widget.mdi_wrap() sub.closed.connect(self._update_viewer_in_focus) if label: sub.setWindowTitle(label) page.addSubWindow(sub) page.setActiveSubWindow(sub) if hold_position and pos is not None: new_widget.move(pos[0], pos[1]) self.tab_widget.setCurrentWidget(page) return sub def _edit_settings(self, *args): self._editor = PreferencesDialog(self, parent=self) self._editor.show() def gather_current_tab(self, *args): """Arrange windows in current tab via tiling""" self.current_tab.tileSubWindows() def _get_plot_dashboards(self, widget): if not isinstance(widget, DataViewer): return QtWidgets.QWidget(), QtWidgets.QWidget(), "" layer_view = widget.layer_view() options_widget = widget.options_widget() return layer_view, options_widget, str(widget) def _clear_dashboard(self): for widget, title in [(self._ui.plot_layers, "Plot Layers"), (self._ui.plot_options, "Plot Options")]: layout = widget.layout() if layout is None: layout = QtWidgets.QVBoxLayout() layout.setContentsMargins(4, 4, 4, 4) widget.setLayout(layout) while layout.count(): layout.takeAt(0).widget().hide() self._ui.plot_options_label.setText("Plot Options") self._ui.plot_layers_label.setText("Plot Layers") def _update_plot_dashboard(self, *args): self._clear_dashboard() if self._viewer_in_focus is None: return layer_view, options_widget, title = self._get_plot_dashboards(self._viewer_in_focus) layout = self._ui.plot_layers.layout() layout.addWidget(layer_view) layout = self._ui.plot_options.layout() layout.addWidget(options_widget) layer_view.show() options_widget.show() if title: self._ui.plot_options_label.setText("Plot Options - %s" % title) self._ui.plot_layers_label.setText("Plot Layers - %s" % title) else: self._ui.plot_options_label.setText("Plot Options") self._ui.plot_layers_label.setText("Plot Layers") self._update_focus_decoration() def _update_focus_decoration(self): mdi_area = self.current_tab for win in mdi_area.subWindowList(): widget = win.widget() if isinstance(widget, DataViewer): widget.set_focus(widget is self._viewer_in_focus) def _connect(self): self.setAcceptDrops(True) self._layer_widget.setup(self._data) self.tab_widget.tabCloseRequested.connect(self.close_tab) self.tab_widget.currentChanged.connect(self._update_viewer_in_focus) def _create_menu(self): mbar = self.menuBar() menu = QtWidgets.QMenu(mbar) menu.setTitle("&File") menu.addAction(self._actions['data_new']) if 'data_importers' in self._actions: submenu = menu.addMenu("I&mport data") for a in self._actions['data_importers']: submenu.addAction(a) # menu.addAction(self._actions['data_save']) # XXX add this menu.addAction(self._actions['session_reset']) menu.addAction(self._actions['session_restore']) menu.addAction(self._actions['session_save']) menu.addAction(self._actions['export_data']) if 'session_export' in self._actions: submenu = menu.addMenu("Advanced E&xporters") for a in self._actions['session_export']: submenu.addAction(a) menu.addSeparator() menu.addAction("Edit &Preferences", self._edit_settings) # Here we use close instead of self.app.quit because if we are launching # glue from an environment with a Qt event loop already existing, we # don't want to quit this. Using close here is safer, though it does # mean that any dialog we launch from glue has to be either modal (to # prevent quitting) or correctly define its parent so that it gets # closed too. menu.addAction("&Quit", self.close) mbar.addMenu(menu) menu = QtWidgets.QMenu(mbar) menu.setTitle("&Edit ") menu.addAction(self._actions['undo']) menu.addAction(self._actions['redo']) mbar.addMenu(menu) menu = QtWidgets.QMenu(mbar) menu.setTitle("&View ") a = QtWidgets.QAction("&Console Log", menu) a.triggered.connect(self._log._show) menu.addAction(a) mbar.addMenu(menu) menu = QtWidgets.QMenu(mbar) menu.setTitle("&Canvas") menu.addAction(self._actions['tab_new']) menu.addAction(self._actions['viewer_new']) menu.addAction(self._actions['fixed_layout_tab_new']) menu.addSeparator() menu.addAction(self._actions['gather']) menu.addAction(self._actions['tab_rename']) mbar.addMenu(menu) menu = QtWidgets.QMenu(mbar) menu.setTitle("Data &Manager") menu.addActions(self._layer_widget.actions()) mbar.addMenu(menu) menu = QtWidgets.QMenu(mbar) menu.setTitle("&Plugins") menu.addAction(self._actions['plugin_manager']) menu.addSeparator() if 'plugins' in self._actions: for plugin in self._actions['plugins']: menu.addAction(plugin) mbar.addMenu(menu) # trigger inclusion of Mac Native "Help" tool menu = mbar.addMenu("&Help") a = QtWidgets.QAction("&Online Documentation", menu) a.triggered.connect(nonpartial(webbrowser.open, DOCS_URL)) menu.addAction(a) a = QtWidgets.QAction("Send &Feedback", menu) a.triggered.connect(nonpartial(submit_feedback)) menu.addAction(a) menu.addSeparator() menu.addAction("Version information", self._show_glue_info) def _show_glue_info(self): window = QVersionsDialog(parent=self) window.show() window.exec_() def _choose_save_data(self, *args): dialog = SaveDataDialog(data_collection=self.data_collection, parent=self) dialog.exec_() def _create_actions(self): """ Create and connect actions, store in _actions dict """ self._actions = {} a = action("&New Data Viewer", self, tip="Open a new visualization window in the current tab", shortcut=QtGui.QKeySequence.New) a.triggered.connect(self._choose_new_data_viewer_nodata) self._actions['viewer_new'] = a if len(qt_client.members) == 0: a.setEnabled(False) a = action("New Fixed Layout Tab", self, tip="Create a new tab with a fixed layout") a.triggered.connect(self.choose_new_fixed_layout_tab) self._actions['fixed_layout_tab_new'] = a if len(qt_fixed_layout_tab.members) == 0: a.setEnabled(False) a = action('New &Tab', self, shortcut=QtGui.QKeySequence.AddTab, tip='Add a new tab') a.triggered.connect(self.new_tab) self._actions['tab_new'] = a a = action('&Rename Tab', self, shortcut="Ctrl+R", tip='Set a new label for the current tab') a.triggered.connect(nonpartial(self.tab_bar.choose_rename_tab)) self._actions['tab_rename'] = a a = action('&Gather Windows', self, tip='Gather plot windows side-by-side', shortcut='Ctrl+G') a.triggered.connect(self.gather_current_tab) self._actions['gather'] = a a = action('&Export Session', self, tip='Save the current session') a.triggered.connect(self._choose_save_session) self._actions['session_save'] = a # Add file loader as first item in File menu for convenience. We then # also add it again below in the Import menu for consistency. a = action("&Open Data Set", self, tip="Open a new data set", shortcut=QtGui.QKeySequence.Open) a.triggered.connect(self._import_helper._choose_load_data_wizard) self._actions['data_new'] = a # We now populate the "Import data" menu from glue.config import importer acts = [] # Add default file loader (later we can add this to the registry) a = action("Import from file", self, tip="Import from file") a.triggered.connect(self._import_helper._choose_load_data_wizard) acts.append(a) for label, data_importer in importer: a = action(label, self, tip=label) a.triggered.connect(nonpartial(self._import_helper._choose_load_data, data_importer)) acts.append(a) self._actions['data_importers'] = acts from glue.config import exporters if len(exporters) > 0: acts = [] for e in exporters: label, saver, checker, mode = e a = action(label, self, tip='Export the current session to %s format' % label) a.triggered.connect(nonpartial(self._export_helper._choose_export_session, saver, checker, mode)) acts.append(a) self._actions['session_export'] = acts a = action('Open S&ession', self, tip='Restore a saved session') a.triggered.connect(self._restore_session) self._actions['session_restore'] = a a = action('Reset S&ession', self, tip='Reset session to clean state') a.triggered.connect(self._reset_session) self._actions['session_reset'] = a a = action('Export D&ata/Subsets', self, tip='Export data to a file') a.triggered.connect(self._choose_save_data) self._actions['export_data'] = a a = action("Undo", self, tip='Undo last action', shortcut=QtGui.QKeySequence.Undo) a.triggered.connect(self.undo) a.setEnabled(False) self._actions['undo'] = a a = action("Redo", self, tip='Redo last action', shortcut=QtGui.QKeySequence.Redo) a.triggered.connect(self.redo) a.setEnabled(False) self._actions['redo'] = a # Create actions for menubar plugins from glue.config import menubar_plugin acts = [] for label, function in menubar_plugin: a = action(label, self, tip=label) a.triggered.connect(nonpartial(function, self.session, self.data_collection)) acts.append(a) self._actions['plugins'] = acts a = action('&Plugin Manager', self, tip='Open plugin manager') a.triggered.connect(self.plugin_manager) self._actions['plugin_manager'] = a def undo(self, *args): super(GlueApplication, self).undo() def redo(self, *args): super(GlueApplication, self).redo() def choose_new_fixed_layout_tab(self, *args): """ Creates a new tab with a fixed layout """ tab_cls = pick_class(list(qt_fixed_layout_tab.members), title='Fixed layout tab', label="Choose a new fixed layout tab", sort=True) return self.add_fixed_layout_tab(tab_cls) def add_fixed_layout_tab(self, tab_cls): tab = tab_cls(session=self.session) self._total_tab_count += 1 name = 'Tab {0}'.format(self._total_tab_count) if hasattr(tab, 'LABEL'): name += ': ' + tab.LABEL self.tab_widget.addTab(tab, name) self.tab_widget.setCurrentWidget(tab) tab.subWindowActivated.connect(self._update_viewer_in_focus) return tab def _choose_new_data_viewer_nodata(self): self.choose_new_data_viewer() def choose_new_data_viewer(self, data=None): """ Create a new visualization window in the current tab """ if data and data.ndim == 1 and ScatterViewer in qt_client.members: default = ScatterViewer elif data and data.ndim > 1 and ImageViewer in qt_client.members: default = ImageViewer else: default = None client = pick_class(list(qt_client.members), title='Data Viewer', label="Choose a new data viewer", default=default, sort=True) if client is None: return cmd = command.NewDataViewer(viewer=client, data=data) return self.do(cmd) @defer_draw def new_data_viewer(self, viewer_class, data=None, state=None): viewer = super(GlueApplication, self).new_data_viewer(viewer_class, data=data, state=state) if viewer is not None: viewer.show() return viewer def set_window_title(self, detail=None): """Set the window title""" if detail is None: title = "Glue" else: title = "Glue (" + detail + ")" self.setWindowTitle(title) def _on_session_changed(self, name): """Call when the session is changed""" self.set_window_title(name) def _choose_save_session(self, *args): """ Save the data collection and hub to file. Can be restored via restore_session """ # include file filter twice, so it shows up in Dialog outfile, file_filter = compat.getsavefilename( parent=self, basedir=getattr(self, '_last_session_name', 'session.glu'), filters=("Glue Session with absolute paths to data (*.glu);; " "Glue Session with relative paths to data (*.glu);; " "Glue Session including data (*.glu)"), selectedfilter=getattr(self, '_last_session_filter', 'Glue Session with relative paths to data (*.glu)')) # This indicates that the user cancelled if not outfile: return # Add extension if not specified if '.' not in outfile: outfile += '.glu' self._last_session_name = outfile self._last_session_filter = file_filter with set_cursor_cm(Qt.WaitCursor): self.save_session(outfile, include_data="including data" in file_filter, absolute_paths="absolute" in file_filter) self._on_session_changed(outfile) @messagebox_on_error("Failed to restore session") def _restore_session(self, *args): """ Load a previously-saved state, and restart the session """ fltr = "Glue sessions (*.glu)" file_name, file_filter = compat.getopenfilename( parent=self, filters=fltr) if not file_name: return ga = self.restore_session_and_close(file_name) return ga @property def is_empty(self): """ Returns `True` if there are no viewers and no data. """ return (len([viewer for tab in self.viewers for viewer in tab]) == 0 and len(self.data_collection) == 0) def _reset_session(self, *args, **kwargs): """ Reset session to clean state. """ warn = kwargs.pop('warn', False) if not os.environ.get('GLUE_TESTING') and warn and not self.is_empty: buttons = QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel dialog = QtWidgets.QMessageBox.warning( self, "Confirm Close", "Are you sure you want to reset the session? " "This will close all datasets, subsets, and data viewers", buttons=buttons, defaultButton=QtWidgets.QMessageBox.Cancel) if not dialog == QtWidgets.QMessageBox.Ok: return # Make sure the closeEvent gets executed to close the GlueLogger self._log.close() if self.app is not None: self.app.processEvents() ga = GlueApplication() ga.start(block=False) # NOTE: we need to keep a reference to this new application object # otherwise it will immediately garbage collect - this is a hack and # we should find a better solution in future. self._new_application = ga # We need to close this after we open the next application otherwise # Qt will quit since there are no actively open windows. self.close() return ga @staticmethod def restore_session(path, show=True): """ Reload a previously-saved session Parameters ---------- path : str Path to the file to load show : bool, optional If True (the default), immediately show the widget Returns ------- app : :class:`glue.app.qt.application.GlueApplication` The loaded application """ ga = Application.restore_session(path) if show: ga.start(block=False) ga._on_session_changed(path) return ga def has_terminal(self, create_if_not=True): """ Returns True if the IPython terminal is present. """ if self._terminal is None and create_if_not: self._create_terminal() return self._terminal is not None def _create_terminal(self): if self._terminal is not None: # already set up return try: widget = glue_terminal(data_collection=self._data, dc=self._data, hub=self._hub, session=self.session, application=self, **vars(env)) except IPythonTerminalError: self._button_ipython.setEnabled(False) else: self._terminal = self.add_widget(widget) self._terminal.closed.connect(self._on_terminal_close) self._hide_terminal() def _toggle_terminal(self): if self._terminal is None: self._create_terminal() if self._terminal.isVisible(): self._hide_terminal() if self._terminal.isVisible(): warnings.warn("An unexpected error occurred while " "trying to hide the terminal") else: self._show_terminal() if not self._terminal.isVisible(): warnings.warn("An unexpected error occurred while " "trying to show the terminal") def _on_terminal_close(self): if self._button_ipython.isChecked(): self._button_ipython.blockSignals(True) self._button_ipython.setChecked(False) self._button_ipython.blockSignals(False) def _hide_terminal(self): self._terminal.hide() def _show_terminal(self): self._terminal.show() self._terminal.widget().show() def start(self, size=None, position=None, block=True, maximized=True): """ Show the GUI and start the application. Parameters ---------- size : (int, int) Optional The default width/height of the application. If not provided, uses the full screen position : (int, int) Optional The default position of the application """ if maximized: self.showMaximized() else: self.show() if size is not None: self.resize(*size) if position is not None: self.move(*position) self.raise_() # bring window to front # at some point during all this, the MPL backend # switches. This call restores things, so # figures are still inlined in the notebook. # XXX find out a better place for this _fix_ipython_pylab() if block: return self.app.exec_() exec_ = start def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() else: event.ignore() @messagebox_on_error("Failed to load files") def dropEvent(self, event): urls = event.mimeData().urls() paths = [qurl_to_path(url) for url in urls] if any(path.endswith('.glu') for path in paths): if len(paths) != 1: raise Exception("When dragging and dropping files onto glue, " "only a single .glu session file can be " "dropped at a time, or multiple datasets, but " "not a mix of both.") else: self.restore_session_and_close(paths[0]) else: self.load_data(paths) event.accept() @messagebox_on_error("Failed to restore session") def restore_session_and_close(self, path, warn=True): if warn and not self.is_empty: buttons = QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel dialog = QtWidgets.QMessageBox.warning( self, "Confirm Close", "Loading a session file will close the existing session. Are you " "sure you want to continue?", buttons=buttons, defaultButton=QtWidgets.QMessageBox.Cancel) if not dialog == QtWidgets.QMessageBox.Ok: return with set_cursor_cm(Qt.WaitCursor): app = self.restore_session(path) app.setGeometry(self.geometry()) # NOTE: we need to keep a reference to this new application object # otherwise it will immediately garbage collect - this is a hack and # we should find a better solution in future. self._new_application = app self.close() return app def closeEvent(self, event): """Emit a message to hub before closing.""" # Clear the namespace in the terminal to avoid cicular references if self._terminal is not None: self._terminal.widget().clear_ns(['data_collection', 'dc', 'hub', 'session', 'application']) for tab in self.viewers: for viewer in tab: viewer.close(warn=False) self._viewer_in_focus = None self._clear_dashboard() self._log.close() self._hub.broadcast(ApplicationClosedMessage(None)) event.accept() if self._original_app is not None: self._original_app.setWindowIcon(self._original_icon) self._original_app = None self.app = None def report_error(self, message, detail): """ Display an error in a modal :param message: A short description of the error :type message: str :param detail: A longer description :type detail: str """ qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Error", message) qmb.setDetailedText(detail) qmb.resize(400, qmb.size().height()) qmb.exec_() def plugin_manager(self, *args): from glue.main import _installed_plugins pm = QtPluginManager(installed=_installed_plugins) pm.ui.exec_() def _update_undo_redo_enabled(self, *args): undo, redo = self._cmds.can_undo_redo() self._actions['undo'].setEnabled(undo) self._actions['redo'].setEnabled(redo) self._actions['undo'].setText('Undo ' + self._cmds.undo_label) self._actions['redo'].setText('Redo ' + self._cmds.redo_label) @property def viewers(self): """ A list of lists of open Data Viewers. Each inner list contains the viewers open on a particular tab. """ result = [] for t in range(self.tab_count): tab = self.tab(t) item = [] for subwindow in tab.subWindowList(): widget = subwindow.widget() if isinstance(widget, DataViewer): item.append(widget) result.append(tuple(item)) return tuple(result) @property def tab_names(self): """ The name of each tab A list of strings """ return [self.tab_bar.tabText(i) for i in range(self.tab_count)] @tab_names.setter def tab_names(self, values): for index, value in enumerate(values): self.tab_bar.setTabText(index, value) @staticmethod def _choose_merge(data, others, suggested_label): w = load_ui('merge.ui', None, directory=os.path.dirname(__file__)) w.button_yes.clicked.connect(w.accept) w.button_no.clicked.connect(w.reject) w.show() w.raise_() # Add the main dataset to the list. Some of the 'others' may also be # new ones, so it doesn't really make sense to distinguish between # the two here. The main point is that some datasets, including at # least one new one, have a common shape. others.append(data) others.sort(key=lambda x: x.label) for i, d in enumerate(others): if isinstance(d.coords, WCSCoordinates): if i == 0: break else: others[0], others[i] = others[i], others[0] break w.merged_label.setText(suggested_label) entries = [QtWidgets.QListWidgetItem(other.label) for other in others] for e in entries: e.setCheckState(Qt.Checked) for d, item in zip(others, entries): w.choices.addItem(item) if not w.exec_(): return None, None result = [layer for layer, entry in zip(others, entries) if entry.checkState() == Qt.Checked] if result: return result, str(w.merged_label.text()) return None, None def screenshot(self, filename): """ Save a screenshot of the current application window to a file. """ image = QtGui.QImage(self.size(), QtGui.QImage.Format_RGB32) painter = QtGui.QPainter(image) flags = self.IgnoreMask | self.DrawWindowBackground | self.DrawChildren self.render(painter, QtCore.QPoint(), QtGui.QRegion(), flags) image.save(filename) painter.end() def add_datasets(self, *args, **kwargs): result = super(GlueApplication, self).add_datasets(*args, **kwargs) run_autolinker(self.data_collection) return result def __gluestate__(self, context): state = super(GlueApplication, self).__gluestate__(context) state['tab_names'] = self.tab_names return state @classmethod def __setgluestate__(cls, rec, context): self = super(GlueApplication, cls).__setgluestate__(rec, context) if 'tab_names' in rec: self.tab_names = rec['tab_names'] return self glueviz-1.0.1+dfsg.orig/glue/app/qt/save_data.py0000644000175000017500000001211013657331513021047 0ustar noahfxnoahfximport os from qtpy.QtWidgets import QDialog, QListWidgetItem from qtpy.QtCore import Qt from glue import config from echo import SelectionCallbackProperty from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui from glue.core.state_objects import State from echo import ChoiceSeparator from glue.core.data_combo_helper import ComponentIDComboHelper, DataCollectionComboHelper from glue.core.data_exporters.qt.dialog import export_data __all__ = ['SaveDataDialog'] class SaveDataState(State): data = SelectionCallbackProperty() subset = SelectionCallbackProperty() component = SelectionCallbackProperty() exporter = SelectionCallbackProperty() def __init__(self, data_collection=None): super(SaveDataState, self).__init__() self.data_helper = DataCollectionComboHelper(self, 'data', data_collection) self.component_helper = ComponentIDComboHelper(self, 'component', data_collection=data_collection) self.add_callback('data', self._on_data_change) self._on_data_change() self._sync_data_exporters() def _sync_data_exporters(self): exporters = list(config.data_exporter) def display_func(exporter): if exporter.extension == '': return "{0} (*)".format(exporter.label) else: return "{0} ({1})".format(exporter.label, ' '.join('*.' + ext for ext in exporter.extension)) SaveDataState.exporter.set_choices(self, exporters) SaveDataState.exporter.set_display_func(self, display_func) def _on_data_change(self, event=None): self.component_helper.set_multiple_data([self.data]) self._sync_subsets() def _sync_subsets(self): def display_func(subset): if subset is None: return "All data (no subsets applied)" else: return subset.label subsets = [None] + list(self.data.subsets) SaveDataState.subset.set_choices(self, subsets) SaveDataState.subset.set_display_func(self, display_func) class SaveDataDialog(QDialog): def __init__(self, data_collection=None, parent=None): super(SaveDataDialog, self).__init__(parent=parent) self.state = SaveDataState(data_collection=data_collection) self.ui = load_ui('save_data.ui', parent=self, directory=os.path.dirname(__file__)) self._connections = autoconnect_callbacks_to_qt(self.state, self.ui) self.ui.button_cancel.clicked.connect(self.reject) self.ui.button_ok.clicked.connect(self.accept) self.ui.button_select_none.clicked.connect(self.select_none) self.ui.button_select_all.clicked.connect(self.select_all) self.ui.list_component.itemChanged.connect(self._on_check_change) self.state.add_callback('component', self._on_data_change) self._on_data_change() def _on_data_change(self, *event): components = getattr(type(self.state), 'component').get_choices(self.state) self.ui.list_component.clear() for component in components: if isinstance(component, ChoiceSeparator): item = QListWidgetItem(str(component)) item.setFlags(item.flags() & ~Qt.ItemIsSelectable) item.setForeground(Qt.gray) else: item = QListWidgetItem(component.label) item.setCheckState(Qt.Checked) self.ui.list_component.addItem(item) def _on_check_change(self, *event): any_checked = False for idx in range(self.ui.list_component.count()): item = self.ui.list_component.item(idx) if item.checkState() == Qt.Checked: any_checked = True break self.button_ok.setEnabled(any_checked) def select_none(self, *event): self._set_all_checked(False) def select_all(self, *event): self._set_all_checked(True) def _set_all_checked(self, check_state): for idx in range(self.ui.list_component.count()): item = self.ui.list_component.item(idx) item.setCheckState(Qt.Checked if check_state else Qt.Unchecked) def accept(self): components = [] for idx in range(self.ui.list_component.count()): item = self.ui.list_component.item(idx) if item.checkState() == Qt.Checked: components.append(self.state.data.id[item.text()]) if self.state.subset is None: data = self.state.data else: data = self.state.subset export_data(data, components=components, exporter=self.state.exporter.function) super(SaveDataDialog, self).accept() if __name__ == "__main__": from glue.core import DataCollection, Data from glue.utils.qt import get_qapp data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') data2 = Data(a=[1, 2, 3], b=[2, 3, 4], label='data2') dc = DataCollection([data1, data2]) app = get_qapp() dialog = SaveDataDialog(data_collection=dc) dialog.exec_() glueviz-1.0.1+dfsg.orig/glue/app/qt/versions.ui0000644000175000017500000000263613455362716020776 0ustar noahfxnoahfx Dialog 0 0 591 397 Version information false true QAbstractItemView::NoSelection Package Version Copy information to clipboard false glueviz-1.0.1+dfsg.orig/glue/app/qt/terminal.py0000644000175000017500000001611213605357235020743 0ustar noahfxnoahfx""" A GUI Ipython terminal window which can interact with Glue. Based on code from https://stackoverflow.com/a/9796491/1332492 and https://stackoverflow.com/a/11525205/1332492 Usage: new_widget = glue_terminal(**kwargs) """ # must import these first, to set up Qt properly from qtpy import QtWidgets from IPython import get_ipython from IPython.core.interactiveshell import InteractiveShell from IPython.core.completer import IPCompleter from ipykernel.inprocess.ipkernel import InProcessInteractiveShell from ipykernel.connect import get_connection_file from qtconsole.inprocess import QtInProcessKernelManager from qtconsole.rich_jupyter_widget import RichJupyterWidget from qtconsole.client import QtKernelClient from glue.app.qt.mdi_area import GlueMdiSubWindow from glue.utils import as_variable_name from glue.utils.qt import get_qapp # We need to do the following to make sure that the outputs in the IPython # terminal don't get cached. This is because if a user does e.g. # # In [1]: viewer # Out [1]: # # then there will be a remaining reference to the viewer in the IPython # namespace. InteractiveShell.cache_size.default_value = 0 # Make sure that tab competion only shows items returned by # _ipython_key_completions_ if hasattr(IPCompleter, 'dict_keys_only'): IPCompleter.dict_keys_only.default_value = True kernel_manager = None kernel_client = None class IPythonTerminalError(Exception): pass def start_in_process_kernel(): global kernel_manager, kernel_client kernel_manager = QtInProcessKernelManager() kernel_manager.start_kernel() kernel_client = kernel_manager.client() kernel_client.start_channels() def in_process_console(console_class=RichJupyterWidget, **kwargs): """ Create a console widget, connected to an in-process Kernel Keyword arguments will be added to the namespace of the shell. Parameters ---------- console_class : `type` The class of the console widget to create """ global kernel_manager, kernel_client if kernel_manager is None: start_in_process_kernel() app = get_qapp() def stop(): kernel_client.stop_channels() kernel_manager.shutdown_kernel() app.exit() control = console_class() control._display_banner = False control.kernel_manager = kernel_manager control.kernel_client = kernel_client control.exit_requested.connect(stop) control.shell = kernel_manager.kernel.shell control.shell.user_ns.update(**kwargs) control.setWindowTitle('IPython Terminal - type howto() for instructions') return control def connected_console(console_class=RichJupyterWidget, **kwargs): """ Create a console widget, connected to another kernel running in the current process. This is used for instance if glue has been started from IPython. Keyword arguments will be added to the namespace of the shell. Parameters ---------- console_class : `type` The class of the console widget to create """ shell = get_ipython() if shell is None: raise RuntimeError("There is no IPython kernel in this process") client = QtKernelClient(connection_file=get_connection_file()) client.load_connection_file() client.start_channels() control = console_class() control.kernel_client = client control.shell = shell control.shell.user_ns.update(**kwargs) return control glue_banner = """ This is the built-in IPython terminal. You can type any valid Python code here, and you also have access to the following pre-defined variables: * data_collection (aliased to dc) * application * hub In addition, you can drag and drop any dataset or subset onto the terminal to create a new variable, and you will be prompted for a name. """ def howto(): print(glue_banner.strip()) class DragAndDropTerminal(RichJupyterWidget): def __init__(self, **kwargs): super(DragAndDropTerminal, self).__init__(**kwargs) self.setAcceptDrops(True) self.shell = None def mdi_wrap(self): sub = GlueMdiSubWindow() sub.setWidget(self) self.destroyed.connect(sub.close) sub.resize(self.size()) self._mdi_wrapper = sub return sub @property def namespace(self): return self.shell.user_ns if self.shell is not None else None def dragEnterEvent(self, event): fmt = 'application/py_instance' if self.shell is not None and event.mimeData().hasFormat(fmt): event.accept() else: event.ignore() def update_namespace(self, kwargs): if self.shell is not None: self.shell.push(kwargs) def dropEvent(self, event): obj = event.mimeData().data('application/py_instance') try: lbl = obj[0].label except (IndexError, AttributeError): lbl = 'x' lbl = as_variable_name(lbl) var, ok = QtWidgets.QInputDialog.getText(self, "Choose a variable name", "Choose a variable name", text=lbl) if ok: # unpack single-item lists for convenience if isinstance(obj, list) and len(obj) == 1: obj = obj[0] var = {as_variable_name(str(var)): obj} self.update_namespace(var) event.accept() else: event.ignore() def clear_ns(self, names): if self.shell is not None: for name in names: self.shell.user_ns.pop(name) FROM_TERMINAL_MESSAGE = """ Due to a limitation in IPython, the IPython terminal in glue does not work well or at all if glue was started from an IPython terminal, or a Jupyter qtconsole or notebook. However, you can use the original Python session you started glue from instead to interact with glue objects. For example, if you started the application with: In [2]: app = qglue(...) or In [2]: app = GlueApplication(...) you can then access e.g. the viewers with app.viewers, the data collection with app.data_collection, and so on. If the IPython shell hangs and doesn't allow you to type anything else until glue is closed, make sure you type: In [1]: %gui qt before starting glue to avoid this kind of issue. """.strip() def glue_terminal(**kwargs): """ Return a qt widget which embed an IPython interpreter. Keyword arguments will be added to the namespace of the shell. """ kwargs['howto'] = howto shell = get_ipython() if shell is None or isinstance(shell, InProcessInteractiveShell): return in_process_console(console_class=DragAndDropTerminal, **kwargs) else: raise IPythonTerminalError(FROM_TERMINAL_MESSAGE) # TODO: if glue is launched from a qtconsole or a notebook, we should in # principle be able to use the connected_console function above, but this # doesn't behave quite right and requires further investigation. # Note however that if we do this, then we need to avoid polluting the # namespace of the original IPython console, as described in this issue: # https://github.com/glue-viz/glue/issues/1209 glueviz-1.0.1+dfsg.orig/glue/app/qt/layer_tree_widget.ui0000644000175000017500000000222413502206677022611 0ustar noahfxnoahfx LayerTree 0 0 266 185 Form 2 5 0 10 QAbstractItemView::ExtendedSelection DataCollectionView QTreeView
glue.core.qt.data_collection_model
glueviz-1.0.1+dfsg.orig/glue/app/qt/preferences.py0000644000175000017500000001451513605357235021436 0ustar noahfxnoahfximport os import weakref import platform from collections import OrderedDict import numpy as np from matplotlib.colors import ColorConverter from qtpy import QtWidgets from glue.core.message import SettingsChangeMessage from glue.utils.qt import load_ui, ColorProperty, get_qapp from glue.utils.qt.widget_properties import (CurrentComboTextProperty, ValueProperty, ButtonProperty) from glue._settings_helpers import save_settings __all__ = ["PreferencesDialog"] rgb = ColorConverter().to_rgb AUTOLINK_OPTIONS = OrderedDict([ ('always_show', 'Always show suggestions'), ('always_accept', 'Always accept suggestions'), ('always_ignore', 'Always ignore suggestions') ]) class AutolinkPreferencesPane(QtWidgets.QWidget): def __init__(self, parent=None): super(AutolinkPreferencesPane, self).__init__(parent=parent) from glue.config import settings, autolinker # noqa layout = QtWidgets.QGridLayout() self.combos = {} if len(autolinker) > 0: for i, (label, _) in enumerate(autolinker): combo = QtWidgets.QComboBox() for short, display in AUTOLINK_OPTIONS.items(): combo.addItem(display, userData=short) if label in settings.AUTOLINK: index = list(AUTOLINK_OPTIONS.keys()).index(settings.AUTOLINK[label]) else: index = 0 combo.setCurrentIndex(index) layout.addWidget(QtWidgets.QLabel(label), i, 0) layout.addWidget(combo, i, 1) self.combos[label] = combo layout.addWidget(QtWidgets.QWidget(), i + 1, 0) self.setLayout(layout) def finalize(self): from glue.config import settings for label, combo in self.combos.items(): settings.AUTOLINK[label] = combo.currentData() class PreferencesDialog(QtWidgets.QDialog): theme = CurrentComboTextProperty('ui.combo_theme') background = ColorProperty('ui.color_background') foreground = ColorProperty('ui.color_foreground') data_color = ColorProperty('ui.color_default_data') data_alpha = ValueProperty('ui.slider_alpha', value_range=(0, 1)) data_apply = ButtonProperty('ui.checkbox_apply') save_to_disk = ButtonProperty('ui.checkbox_save') font_size = ValueProperty('ui.spinner_font_size') def __init__(self, application, parent=None): super(PreferencesDialog, self).__init__(parent=parent) self._app = weakref.ref(application) self.ui = load_ui('preferences.ui', self, directory=os.path.dirname(__file__)) self.ui.cancel.clicked.connect(self.reject) self.ui.ok.clicked.connect(self.accept) self.ui.combo_theme.currentIndexChanged.connect(self._update_colors_from_theme) self.ui.button_reset_dialogs.clicked.connect(self._reset_dialogs) # The following is needed because of a bug in Qt which means that # tab titles don't get scaled right. if platform.system() == 'Darwin': app = get_qapp() app_font = app.font() self.ui.tab_widget.setStyleSheet('font-size: {0}px'.format(app_font.pointSize())) from glue.config import settings self.background = settings.BACKGROUND_COLOR self.foreground = settings.FOREGROUND_COLOR self.data_color = settings.DATA_COLOR self.data_alpha = settings.DATA_ALPHA self.font_size = settings.FONT_SIZE self._update_theme_from_colors() self._autolink_pane = AutolinkPreferencesPane() self.ui.tab_widget.addTab(self._autolink_pane, 'Autolinking') self.panes = [] from glue.config import preference_panes for label, widget_cls in sorted(preference_panes): pane = widget_cls() self.ui.tab_widget.addTab(pane, label) self.panes.append(pane) def _update_theme_from_colors(self, *args): if (rgb(self.background) == (1, 1, 1) and rgb(self.foreground) == (0, 0, 0) and rgb(self.data_color) == (0.35, 0.35, 0.35) and np.allclose(self.data_alpha, 0.8)): self.theme = 'Black on White' elif (rgb(self.background) == (0, 0, 0) and rgb(self.foreground) == (1, 1, 1) and rgb(self.data_color) == (0.75, 0.75, 0.75) and np.allclose(self.data_alpha, 0.8)): self.theme = 'White on Black' else: self.theme = 'Custom' def _update_colors_from_theme(self, *args): if self.theme == 'Black on White': self.foreground = 'black' self.background = 'white' self.data_color = '0.35' self.data_alpha = 0.8 elif self.theme == 'White on Black': self.foreground = 'white' self.background = 'black' self.data_color = '0.75' self.data_alpha = 0.8 elif self.theme != 'Custom': raise ValueError("Unknown theme: {0}".format(self.theme)) def _reset_dialogs(self, *args): from glue.config import settings for key, _, _ in settings: if key.lower().startswith(('show_info', 'show_warn', 'show_large')): setattr(settings, key, True) def accept(self): # Update default settings from glue.config import settings settings.FOREGROUND_COLOR = self.foreground settings.BACKGROUND_COLOR = self.background settings.DATA_COLOR = self.data_color settings.DATA_ALPHA = self.data_alpha settings.FONT_SIZE = self.font_size self._autolink_pane.finalize() for pane in self.panes: pane.finalize() # Save to disk if requested if self.save_to_disk: save_settings() else: settings._save_to_disk = True # Trigger viewers to update defaults app = self._app() if app is not None: app._hub.broadcast(SettingsChangeMessage(self, ('FOREGROUND_COLOR', 'BACKGROUND_COLOR', 'FONT_SIZE'))) if self.data_apply: # If requested, trigger data to update color app.set_data_color(settings.DATA_COLOR, settings.DATA_ALPHA) super(PreferencesDialog, self).accept() if __name__ == "__main__": app = get_qapp() widget = PreferencesDialog() widget.show() widget.raise_() app.exec_() glueviz-1.0.1+dfsg.orig/glue/app/qt/application.ui0000644000175000017500000001301213502206677021413 0ustar noahfxnoahfx GlueApplication 0 0 1024 557 Form 3 3 3 2 Qt::Horizontal Qt::Vertical 0 75 true Data Collection 0 0 Qt::LeftToRight false 0 75 true Plot Layers 0 0 Qt::LeftToRight false 0 75 true Plot Options 0 0 Qt::LeftToRight false true 1 0 400 0 QTabWidget::Rounded -1 Qt::ElideRight false false false true glueviz-1.0.1+dfsg.orig/glue/app/qt/save_data.ui0000644000175000017500000001115713522025220021030 0ustar noahfxnoahfx Dialog 0 0 420 472 0 0 Dialog 12 12 12 5 7 Qt::Horizontal 40 20 Select the file format to save to: Qt::Horizontal 40 20 false Select All Choose what part of the data you want to save: Qt::Horizontal 40 20 Cancel Export true Select None Qt::Horizontal 40 20 Choose the dataset and attribute you want to save: Once you click on "Export" you will be prompted to choose the filename. true glueviz-1.0.1+dfsg.orig/glue/app/qt/metadata.ui0000644000175000017500000000640213502206677020675 0ustar noahfxnoahfx Dialog 0 0 362 397 Metadata viewer false 5 ndim shape Qt::Horizontal 40 5 Data shape: Number of dimensions: Qt::Horizontal 40 5 Qt::Vertical QSizePolicy::Fixed 5 5 true QAbstractItemView::NoSelection true true false false Keyword Value glueviz-1.0.1+dfsg.orig/glue/app/qt/versions.py0000644000175000017500000000251113605357235020776 0ustar noahfxnoahfximport os from glue import __version__ from qtpy import QtWidgets from qtpy.QtCore import Qt from glue.utils import nonpartial from glue.utils.qt import load_ui, CenteredDialog from glue._deps import get_status_as_odict __all__ = ['QVersionsDialog'] class QVersionsDialog(CenteredDialog): def __init__(self, *args, **kwargs): super(QVersionsDialog, self).__init__(*args, **kwargs) self.ui = load_ui('versions.ui', self, directory=os.path.dirname(__file__)) self.resize(400, 500) self.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint) self.center() self._update_deps() self._clipboard = QtWidgets.QApplication.clipboard() self.ui.button_copy.clicked.connect(nonpartial(self._copy)) def _update_deps(self): status = get_status_as_odict() self._text = "" for name, version in [('Glue', __version__)] + list(status.items()): QtWidgets.QTreeWidgetItem(self.ui.version_tree.invisibleRootItem(), [name, version]) self._text += "{0}: {1}\n".format(name, version) def _copy(self): self._clipboard.setText(self._text) if __name__ == "__main__": from glue.utils.qt import get_qapp app = get_qapp() window = QVersionsDialog() window.show() window.exec_() glueviz-1.0.1+dfsg.orig/glue/app/qt/plugin_manager.py0000644000175000017500000000402313605357235022116 0ustar noahfxnoahfximport os from qtpy.QtCore import Qt from qtpy import QtWidgets from glue._plugin_helpers import PluginConfig from glue.utils.qt import load_ui __all__ = ["QtPluginManager"] class QtPluginManager(object): def __init__(self, installed=None): self.ui = load_ui('plugin_manager.ui', None, directory=os.path.dirname(__file__)) self.ui.cancel.clicked.connect(self.reject) self.ui.confirm.clicked.connect(self.finalize) self._checkboxes = {} self.update_list(installed=installed) def clear(self): self._checkboxes.clear() self.ui.tree.clear() def update_list(self, installed=None): self.clear() config = PluginConfig.load() if installed is not None: config.filter(installed) for plugin in sorted(config.plugins): check = QtWidgets.QTreeWidgetItem(self.ui.tree.invisibleRootItem(), ["", plugin]) check.setFlags(check.flags() | Qt.ItemIsUserCheckable) if config.plugins[plugin]: check.setCheckState(0, Qt.Checked) else: check.setCheckState(0, Qt.Unchecked) self._checkboxes[plugin] = check self.ui.tree.resizeColumnToContents(0) self.ui.tree.resizeColumnToContents(1) def reject(self): self.ui.reject() def finalize(self): config = PluginConfig.load() for name in self._checkboxes: config.plugins[name] = self._checkboxes[name].checkState(0) > 0 try: config.save() except Exception: import traceback detail = str(traceback.format_exc()) message = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Error", "Could not save plugin configuration") message.setDetailedText(detail) message.exec_() return self.ui.accept() glueviz-1.0.1+dfsg.orig/glue/app/__init__.py0000644000175000017500000000000013455362716020233 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/0000755000175000017500000000000013752535025015756 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/tests/0000755000175000017500000000000013752535025017120 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/tests/test_subset_mask.py0000644000175000017500000001222413605357235023054 0ustar noahfxnoahfxfrom collections import OrderedDict from unittest.mock import MagicMock import pytest import numpy as np from numpy.testing import assert_equal from glue.core import DataCollection, Data from ..subset_mask import SubsetMaskImporter, SubsetMaskExporter class MySubsetMaskImporter(SubsetMaskImporter): filename = None reader = None def get_filename_and_reader(self): return self.filename, self.reader class MySubsetMaskExporter(SubsetMaskExporter): filename = None writer = None def get_filename_and_writer(self): return self.filename, self.writer class TestImporter(): def setup_method(self, method): self.importer = MySubsetMaskImporter() self.importer.filename = 'test-filename' self.importer.reader = MagicMock() self.data = Data(x=[1, 2, 3]) self.data_collection = DataCollection([self.data]) def test_single_valid(self): self.importer.reader.return_value = OrderedDict([('subset 1', np.array([0, 1, 0]))]) self.importer.run(self.data, self.data_collection) assert len(self.data_collection.subset_groups) == 1 assert_equal(self.data.subsets[0].to_mask(), [0, 1, 0]) def test_multiple_valid(self): self.importer.reader.return_value = OrderedDict([('subset 1', np.array([0, 1, 0])), ('subset 2', np.array([1, 1, 0]))]) self.importer.run(self.data, self.data_collection) assert len(self.data_collection.subset_groups) == 2 assert_equal(self.data.subsets[0].to_mask(), [0, 1, 0]) assert_equal(self.data.subsets[1].to_mask(), [1, 1, 0]) def test_missing_masks(self): self.importer.reader.return_value = OrderedDict() with pytest.raises(ValueError) as exc: self.importer.run(self.data, self.data_collection) assert exc.value.args[0] == "No subset masks were returned" def test_single_invalid_shape(self): self.importer.reader.return_value = OrderedDict([('subset 1', np.array([0, 1, 0, 1]))]) with pytest.raises(ValueError) as exc: self.importer.run(self.data, self.data_collection) assert exc.value.args[0].replace('L', '') == "Mask shape (4,) does not match data shape (3,)" def test_multiple_inconsistent_shapes(self): self.importer.reader.return_value = OrderedDict([('subset 1', np.array([0, 1, 0])), ('subset 2', np.array([0, 1, 0, 1]))]) with pytest.raises(ValueError) as exc: self.importer.run(self.data, self.data_collection) assert exc.value.args[0] == "Not all subsets have the same shape" def test_subset_single(self): self.importer.reader.return_value = OrderedDict([('subset 1', np.array([0, 1, 0]))]) subset = self.data.new_subset() assert_equal(self.data.subsets[0].to_mask(), [0, 0, 0]) self.importer.run(subset, self.data_collection) assert_equal(self.data.subsets[0].to_mask(), [0, 1, 0]) def test_subset_multiple(self): self.importer.reader.return_value = OrderedDict([('subset 1', np.array([0, 1, 0])), ('subset 2', np.array([1, 1, 0]))]) subset = self.data.new_subset() with pytest.raises(ValueError) as exc: self.importer.run(subset, self.data_collection) assert exc.value.args[0] == 'Can only read in a single subset when importing into a subset' class TestExporter(): def setup_method(self, method): self.exporter = MySubsetMaskExporter() self.exporter.filename = 'test-filename' self.exporter.writer = MagicMock() self.data = Data(x=[1, 2, 3]) self.data_collection = DataCollection([self.data]) def test_no_subsets(self): with pytest.raises(ValueError) as exc: self.exporter.run(self.data) assert exc.value.args[0] == 'Data has no subsets' def test_multiple_valid(self): self.subset1 = self.data_collection.new_subset_group(subset_state=self.data.id['x'] >= 2, label='subset a') self.subset2 = self.data_collection.new_subset_group(subset_state=self.data.id['x'] >= 3, label='subset b') self.exporter.run(self.data) assert self.exporter.writer.call_count == 1 assert self.exporter.writer.call_args[0][0] == 'test-filename' masks = self.exporter.writer.call_args[0][1] assert len(masks) == 2 assert_equal(masks['subset a'], [0, 1, 1]) assert_equal(masks['subset b'], [0, 0, 1]) def test_single_subset_valid(self): self.subset = self.data_collection.new_subset_group(subset_state=self.data.id['x'] >= 2, label='subset a') self.exporter.run(self.data.subsets[0]) assert self.exporter.writer.call_count == 1 assert self.exporter.writer.call_args[0][0] == 'test-filename' masks = self.exporter.writer.call_args[0][1] assert len(masks) == 1 assert_equal(masks['subset a'], [0, 1, 1]) glueviz-1.0.1+dfsg.orig/glue/io/tests/__init__.py0000644000175000017500000000000013502206677021220 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/subset_mask.py0000644000175000017500000000445513605357235020662 0ustar noahfxnoahfxfrom collections import OrderedDict from glue.core import Subset from glue.core.subset import MaskSubsetState __all__ = ['SubsetMaskImporter', 'SubsetMaskExporter'] class SubsetMaskImporter(object): def get_filename_and_reader(self): raise NotImplementedError def run(self, data_or_subset, data_collection): filename, reader = self.get_filename_and_reader() if filename is None: return # Read in the masks masks = reader(filename) # Make sure shape is unique shapes = set(mask.shape for mask in masks.values()) if len(shapes) == 0: raise ValueError("No subset masks were returned") elif len(shapes) > 1: raise ValueError("Not all subsets have the same shape") if list(shapes)[0] != data_or_subset.shape: raise ValueError("Mask shape {0} does not match data shape {1}".format(list(shapes)[0], data_or_subset.shape)) if isinstance(data_or_subset, Subset): subset = data_or_subset if len(masks) != 1: raise ValueError("Can only read in a single subset when importing into a subset") mask = list(masks.values())[0] subset_state = MaskSubsetState(mask, subset.pixel_component_ids) subset.subset_state = subset_state else: data = data_or_subset for label, mask in masks.items(): subset_state = MaskSubsetState(mask, data.pixel_component_ids) data_collection.new_subset_group(label=label, subset_state=subset_state) class SubsetMaskExporter(object): def get_filename_and_writer(self): raise NotImplementedError def run(self, data_or_subset): filename, writer = self.get_filename_and_writer() if filename is None: return # Prepare dictionary of masks masks = OrderedDict() if isinstance(data_or_subset, Subset): subset = data_or_subset masks[subset.label] = subset.to_mask() else: data = data_or_subset if len(data.subsets) == 0: raise ValueError("Data has no subsets") for subset in data.subsets: masks[subset.label] = subset.to_mask() writer(filename, masks) glueviz-1.0.1+dfsg.orig/glue/io/qt/0000755000175000017500000000000013752535025016402 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/qt/tests/0000755000175000017500000000000013752535025017544 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/qt/tests/test_subset_mask.py0000644000175000017500000000445313605357235023505 0ustar noahfxnoahfximport os from unittest.mock import patch from numpy.testing import assert_equal from astropy.io import fits from glue.core import DataCollection, Data from glue.io.qt.subset_mask import QtSubsetMaskImporter, QtSubsetMaskExporter def test_importer(tmpdir): filename = tmpdir.join('test.fits').strpath hdu = fits.PrimaryHDU(data=[0, 1, 1]) hdu.writeto(filename) data = Data(x=[1, 2, 3]) data_collection = DataCollection([data]) with patch('qtpy.compat.getopenfilename') as o: o.return_value = filename, 'FITS (*.fits *.fit *.fits.gz *.fit.gz)' importer = QtSubsetMaskImporter() importer.run(data, data_collection) assert_equal(data.subsets[0].to_mask(), [0, 1, 1]) def test_importer_cancel(tmpdir): filename = tmpdir.join('test.fits').strpath hdu = fits.PrimaryHDU(data=[0, 1, 1]) hdu.writeto(filename) data = Data(x=[1, 2, 3]) data_collection = DataCollection([data]) with patch('qtpy.compat.getopenfilename') as o: o.return_value = '', '' # simulates cancelling importer = QtSubsetMaskImporter() importer.run(data, data_collection) assert len(data_collection.subset_groups) == 0 assert len(data.subsets) == 0 def test_exporter(tmpdir): filename = tmpdir.join('test.fits').strpath data = Data(x=[1, 2, 3]) data_collection = DataCollection([data]) data_collection.new_subset_group(subset_state=data.id['x'] >= 2, label='subset a') with patch('qtpy.compat.getsavefilename') as o: o.return_value = filename, 'FITS (*.fits *.fit *.fits.gz *.fit.gz)' exporter = QtSubsetMaskExporter() exporter.run(data) with fits.open(filename) as hdulist: assert len(hdulist) == 2 assert hdulist[0].data is None assert hdulist[1].name == 'SUBSET A' assert_equal(hdulist[1].data, [0, 1, 1]) def test_exporter_cancel(tmpdir): filename = tmpdir.join('test.fits').strpath data = Data(x=[1, 2, 3]) data_collection = DataCollection([data]) data_collection.new_subset_group(subset_state=data.id['x'] >= 2, label='subset a') with patch('qtpy.compat.getsavefilename') as o: o.return_value = '', '' # simulates cancelling exporter = QtSubsetMaskExporter() exporter.run(data) assert not os.path.exists(filename) glueviz-1.0.1+dfsg.orig/glue/io/qt/tests/__init__.py0000644000175000017500000000000013502206677021644 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/qt/subset_mask.py0000644000175000017500000000375513605357235021310 0ustar noahfxnoahfxfrom qtpy import compat from glue import config from glue.utils.qt import messagebox_on_error from glue.io.subset_mask import SubsetMaskImporter, SubsetMaskExporter __all__ = ['QtSubsetMaskImporter', 'QtSubsetMaskExporter'] def _make_filters_dict(registry): filters = {} for e in registry: if e.extension == '': fltr = "{0} (*)".format(e.label) else: fltr = "{0} ({1})".format(e.label, ' '.join('*.' + ext for ext in e.extension)) filters[fltr] = e.function return filters class QtSubsetMaskImporter(SubsetMaskImporter): def get_filename_and_reader(self): subset_mask_importers = _make_filters_dict(config.subset_mask_importer) filters = ';;'.join(sorted(subset_mask_importers)) filename, fltr = compat.getopenfilename(caption="Choose a subset mask file", filters=filters) filename = str(filename) if filename: return filename, subset_mask_importers[fltr] else: return None, None @messagebox_on_error('An error occurred when importing the subset mask file:', sep=' ') def run(self, data_or_subset, data_collection): super(QtSubsetMaskImporter, self).run(data_or_subset, data_collection) class QtSubsetMaskExporter(SubsetMaskExporter): def get_filename_and_writer(self): subset_mask_exporters = _make_filters_dict(config.subset_mask_exporter) filters = ';;'.join(sorted(subset_mask_exporters)) filename, fltr = compat.getsavefilename(caption="Choose a subset mask filename", filters=filters) filename = str(filename) if filename: return filename, subset_mask_exporters[fltr] else: return None, None @messagebox_on_error('An error occurred when exporting the subset mask:', sep=' ') def run(self, data_or_subset): super(QtSubsetMaskExporter, self).run(data_or_subset) glueviz-1.0.1+dfsg.orig/glue/io/qt/directory_importer/0000755000175000017500000000000013752535025022327 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/qt/directory_importer/__init__.py0000644000175000017500000000007213614040126024425 0ustar noahfxnoahfxdef setup(): from . import directory_importer # noqa glueviz-1.0.1+dfsg.orig/glue/io/qt/directory_importer/directory_importer.py0000644000175000017500000000027713614041255026626 0ustar noahfxnoahfxfrom glue.config import importer from glue.dialogs.data_wizard.qt import data_wizard @importer("Import from directory") def directory_importer(): return data_wizard(mode='directories') glueviz-1.0.1+dfsg.orig/glue/io/qt/__init__.py0000644000175000017500000000000013502206677020502 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/__init__.py0000644000175000017500000000000013502206677020056 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/formats/0000755000175000017500000000000013752535025017431 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/formats/tests/0000755000175000017500000000000013752535025020573 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/formats/tests/__init__.py0000644000175000017500000000000013502206677022673 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/formats/__init__.py0000644000175000017500000000000013502206677021531 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/formats/fits/0000755000175000017500000000000013752535025020376 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/formats/fits/tests/0000755000175000017500000000000013752535025021540 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/formats/fits/tests/test_subset_mask.py0000644000175000017500000000513213605357235025474 0ustar noahfxnoahfxfrom collections import OrderedDict import pytest from numpy.testing import assert_equal from astropy.io import fits from glue.io.formats.fits.subset_mask import fits_subset_mask_importer, fits_subset_mask_exporter def test_reader(tmpdir): mask_filename = tmpdir.join('subset_mask.fits').strpath original_mask = [[0, 1], [1, 0]] hdu = fits.PrimaryHDU(data=original_mask) hdu.writeto(mask_filename) masks = fits_subset_mask_importer(mask_filename) assert len(masks) == 1 label, mask = list(masks.items())[0] assert label == 'subset_mask' assert_equal(mask, original_mask) def test_reader_extensions(tmpdir): mask_filename = tmpdir.join('subset_mask.fits').strpath original_mask1 = [[0, 1], [1, 0]] hdu1 = fits.ImageHDU(data=original_mask1, name='Subset A') original_mask2 = [[1, 0], [1, 0]] hdu2 = fits.ImageHDU(data=original_mask2) hdulist = fits.HDUList([fits.PrimaryHDU(), hdu1, hdu2]) hdulist.writeto(mask_filename) masks = fits_subset_mask_importer(mask_filename) assert len(masks) == 2 mask_items = list(masks.items()) label1, mask1 = mask_items[0] label2, mask2 = mask_items[1] assert label1 == 'SUBSET A' assert_equal(mask1, original_mask1) assert label2 == 'subset_mask[2]' assert_equal(mask2, original_mask2) def test_reader_invalid_hdus(tmpdir): mask_filename = tmpdir.join('subset_mask.fits').strpath hdu = fits.PrimaryHDU(data=[1.2, 3.2, 4.5]) hdu.writeto(mask_filename) with pytest.raises(ValueError) as exc: fits_subset_mask_importer(mask_filename) assert exc.value.args[0] == 'No HDUs with integer values (which would normally indicate a mask) were found in file' def test_reader_invalid_format(tmpdir): mask_filename = tmpdir.join('subset_mask.fits').strpath with open(mask_filename, 'w') as f: f.write('qwdiwqoidqwjdijwq') with pytest.raises(IOError) as exc: fits_subset_mask_importer(mask_filename) assert exc.value.args[0].endswith('is not a valid FITS file') def test_writer(tmpdir): mask_filename = tmpdir.join('subset_mask.fits').strpath masks = OrderedDict() masks['subset 1'] = [[0, 1], [1, 0]] masks['subset 2'] = [[0, 1, 3], [1, 0, 2]] fits_subset_mask_exporter(mask_filename, masks) with fits.open(mask_filename) as hdulist: assert len(hdulist) == 3 assert hdulist[0].data is None assert hdulist[1].name == 'SUBSET 1' assert_equal(hdulist[1].data, masks['subset 1']) assert hdulist[2].name == 'SUBSET 2' assert_equal(hdulist[2].data, masks['subset 2']) glueviz-1.0.1+dfsg.orig/glue/io/formats/fits/tests/__init__.py0000644000175000017500000000000013502206677023640 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/io/formats/fits/subset_mask.py0000644000175000017500000000316213605357235023274 0ustar noahfxnoahfximport os from collections import OrderedDict import numpy as np from astropy.io import fits from glue.config import subset_mask_importer, subset_mask_exporter from glue.core.data_factories.fits import is_fits @subset_mask_importer(label='FITS', extension=['fits', 'fit', 'fits.gz', 'fit.gz']) def fits_subset_mask_importer(filename): if not is_fits(filename): raise IOError("File {0} is not a valid FITS file".format(filename)) masks = OrderedDict() label = os.path.basename(filename).rpartition('.')[0] with fits.open(filename) as hdulist: for ihdu, hdu in enumerate(hdulist): if hdu.data is not None and hdu.data.dtype.kind == 'i': if not hdu.name: name = '{0}[{1}]'.format(label, ihdu) elif ihdu == 0: name = label else: name = hdu.name masks[name] = hdu.data > 0 if len(masks) == 0: raise ValueError('No HDUs with integer values (which would normally indicate a mask) were found in file') return masks @subset_mask_exporter(label='FITS', extension=['fits', 'fit', 'fits.gz', 'fit.gz']) def fits_subset_mask_exporter(filename, masks): hdulist = fits.HDUList() hdulist.append(fits.PrimaryHDU()) # We store the subset masks in the extensions to make sure we can give # then a name. for label, mask in masks.items(): hdulist.append(fits.ImageHDU(np.asarray(mask, int), name=label)) hdulist.writeto(filename, overwrite=True) glueviz-1.0.1+dfsg.orig/glue/io/formats/fits/__init__.py0000644000175000017500000000024013502206677022504 0ustar noahfxnoahfx# Data factories are already defined in glue.core.data_factories, but they will # be moved here in future. def setup(): from . import subset_mask # noqa glueviz-1.0.1+dfsg.orig/glue/config_gen.py0000755000175000017500000000213313605357235020023 0ustar noahfxnoahfx#!/usr/bin/env python """ Script used to create template config.py files for Glue """ import os import sys from shutil import copyfile import glue def get_clobber(): result = None result = input("\nDestination file exists. Overwrite? [y/n] ") while result not in ['y', 'n']: print("\tPlease choose one of [y/n]") result = input("\nDestination file exists. Overwrite? [y/n] ") return result == 'y' def main(): # Import at runtime because some tests change this value. We also don't # just import the function directly otherwise it is cached. from glue import config dest = config.CFG_DIR if not os.path.exists(dest): print("Creating directory %s" % dest) os.makedirs(dest) infile = os.path.join(glue.__path__[0], 'default_config.py') outfile = os.path.join(dest, 'config.py') print("Creating file %s" % outfile) if os.path.exists(outfile): clobber = get_clobber() if not clobber: print("Exiting") sys.exit(1) copyfile(infile, outfile) if __name__ == "__main__": main() glueviz-1.0.1+dfsg.orig/glue/_settings_helpers.py0000644000175000017500000000304613605357235021447 0ustar noahfxnoahfximport os import json import configparser from glue.logger import logger def save_settings(): from glue.config import settings, CFG_DIR if not getattr(settings, '_save_to_disk', True): return settings_cfg = os.path.join(CFG_DIR, 'settings.cfg') config = configparser.ConfigParser() config.add_section('main') for name, value, _ in sorted(settings): config.set('main', name, value=json.dumps(value, sort_keys=True)) if not os.path.exists(CFG_DIR): os.mkdir(CFG_DIR) with open(settings_cfg, 'w') as fout: config.write(fout) def load_settings(force=False): """ Load the settings from disk. By default, only settings not already defined in memory are read in, but by setting ``force=True``, all settings will be read in. """ from glue.config import settings, CFG_DIR settings_cfg = os.path.join(CFG_DIR, 'settings.cfg') logger.info("Loading settings from {0}".format(settings_cfg)) config = configparser.ConfigParser() read = config.read(settings_cfg) if len(read) == 0 or not config.has_section('main'): return for name, value in config.items('main'): name = name.upper() if name in settings: if settings.is_default(name) or force: setattr(settings, name, json.loads(value)) elif not settings.is_default(name): logger.info("Setting {0} already initialized - skipping".format(name)) else: logger.info("Unknown setting {0} - skipping".format(name)) glueviz-1.0.1+dfsg.orig/glue/core/0000755000175000017500000000000013752535025016277 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/tests/0000755000175000017500000000000013752535025017441 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/tests/test_links.py0000644000175000017500000000367113612622074022175 0ustar noahfxnoahfx"""This file contains tests concerning linking data and accessing linked components""" import numpy as np from numpy.random import random as r from glue.core.coordinates import IdentityCoordinates from .. import Data, DataCollection from ..link_helpers import LinkSame def test_1d_world_link(): x, y = r(10), r(10) d1 = Data(label='d1', x=x) d2 = Data(label='d2', y=y, coords=IdentityCoordinates(n_dim=1)) dc = DataCollection([d1, d2]) dc.add_link(LinkSame(d2.world_component_ids[0], d1.id['x'])) assert d2.world_component_ids[0] in d1.externally_derivable_components np.testing.assert_array_equal(d1[d2.world_component_ids[0]], x) np.testing.assert_array_equal(d1[d2.pixel_component_ids[0]], x) def test_3d_world_link(): """Should be able to grab pixel coords after linking world""" x, y, z = r(10), r(10), r(10) cat = Data(label='cat', x=x, y=y, z=z) im = Data(label='im', inten=r((3, 3, 3)), coords=IdentityCoordinates(n_dim=3)) dc = DataCollection([cat, im]) dc.add_link(LinkSame(im.world_component_ids[2], cat.id['x'])) dc.add_link(LinkSame(im.world_component_ids[1], cat.id['y'])) dc.add_link(LinkSame(im.world_component_ids[0], cat.id['z'])) np.testing.assert_array_equal(cat[im.pixel_component_ids[2]], x) np.testing.assert_array_equal(cat[im.pixel_component_ids[1]], y) np.testing.assert_array_equal(cat[im.pixel_component_ids[0]], z) def test_2d_world_link(): """Should be able to grab pixel coords after linking world""" x, y = r(10), r(10) cat = Data(label='cat', x=x, y=y) im = Data(label='im', inten=r((3, 3)), coords=IdentityCoordinates(n_dim=2)) dc = DataCollection([cat, im]) dc.add_link(LinkSame(im.world_component_ids[0], cat.id['x'])) dc.add_link(LinkSame(im.world_component_ids[1], cat.id['y'])) np.testing.assert_array_equal(cat[im.pixel_component_ids[0]], x) np.testing.assert_array_equal(cat[im.pixel_component_ids[1]], y) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_component.py0000644000175000017500000003033213612622074023051 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import operator import pytest import numpy as np from unittest.mock import MagicMock from glue import core from glue.tests.helpers import requires_astropy from ..coordinates import Coordinates from ..component import (Component, DerivedComponent, CoordinateComponent, CategoricalComponent) from ..component_id import ComponentID from ..data import Data from ..parse import ParsedCommand, ParsedComponentLink from ..data_collection import DataCollection VIEWS = (np.s_[:], np.s_[1], np.s_[::-1], np.s_[0, :]) class TestComponent(object): def setup_method(self, method): self.data = MagicMock() self.data.shape = [1, 2] self.component = Component(self.data) def test_data(self): assert self.component.data is self.data def test_shape(self): assert self.component.shape is self.data.shape def test_ndim(self): assert self.component.ndim is len(self.data.shape) class TestComponentID(object): def setup_method(self, method): self.cid = ComponentID('test') def test_label(self): assert self.cid.label == 'test' def test_str(self): """ str should return """ str(self.cid) def test_repr(self): """ str should return """ repr(self.cid) class TestDerivedComponent(object): def setup_method(self, method): data = MagicMock() link = MagicMock() self.cid = DerivedComponent(data, link) self.link = link self.data = data def test_data(self): """ data property should wrap to links compute method """ self.cid.data self.link.compute.assert_called_once_with(self.data) def test_link(self): assert self.cid.link == self.link class TestCategoricalComponent(object): def setup_method(self, method): self.list_data = ['a', 'a', 'b', 'b'] self.array_data = np.array(self.list_data) def test_autodetection(self): assert Component.autotyped(self.array_data).categorical assert Component.autotyped(self.list_data).categorical x = np.array([True, False, True, False]) assert not Component.autotyped(x).categorical x = np.array([1, 2, 3, 4]) assert not Component.autotyped(x).categorical x = np.array(['1', '2', '3', '4']) assert not Component.autotyped(x).categorical d = Data(x=['a', 'b', 'c']) assert d.get_component('x').categorical def test_basic_properties(self): data = ['a', 'b', 'c', 'b', 'b', 'c', 'a', 'c', 'd'] cat_comp = CategoricalComponent(data) np.testing.assert_equal(cat_comp.data, data) np.testing.assert_equal(cat_comp.labels, data) np.testing.assert_equal(cat_comp.codes, [0, 1, 2, 1, 1, 2, 0, 2, 3]) np.testing.assert_equal(cat_comp.categories, ['a', 'b', 'c', 'd']) def test_accepts_numpy(self): cat_comp = CategoricalComponent(self.array_data) assert cat_comp.data.shape == (4,) def test_accepts_pandas(self): # Regression test for a bug that caused read-only # pandas DataFrame columns to cause issues from pandas import DataFrame df = DataFrame() df['a'] = ['a', 'b', 'c'] print(df['a'].fillna(''), type) CategoricalComponent(df['a'].fillna('')) def test_accepts_list(self): """Should accept a list and convert to numpy!""" cat_comp = CategoricalComponent(self.list_data) np.testing.assert_equal(cat_comp.data, self.array_data) def test_multi_nans(self): cat_comp = CategoricalComponent(['', '', 'a', 'b', 'c', 'zanthia']) np.testing.assert_equal(cat_comp.codes, np.array([0, 0, 1, 2, 3, 4])) np.testing.assert_equal(cat_comp.categories, np.asarray(['', 'a', 'b', 'c', 'zanthia'])) def test_calculate_grouping(self): cat_comp = CategoricalComponent(self.array_data) np.testing.assert_equal(cat_comp.categories, np.asarray(['a', 'b'])) np.testing.assert_equal(cat_comp.codes, np.array([0, 0, 1, 1])) assert cat_comp.codes.dtype == np.float def test_accepts_provided_grouping(self): ncategories = ['b', 'c'] cat_data = list('aaabbbcccddd') cat_comp = CategoricalComponent(cat_data, categories=ncategories) assert cat_comp.categories == ncategories assert np.all(np.isnan(cat_comp.codes[:3])) assert np.all(cat_comp.codes[3:6] == 0) assert np.all(cat_comp.codes[6:9] == 1) assert np.all(np.isnan(cat_comp.codes[9:])) def test_uniform_jitter(self): cat_comp = CategoricalComponent(self.array_data) second_comp = CategoricalComponent(self.array_data) cat_comp.jitter(method='uniform') assert np.all(cat_comp.codes != second_comp.codes), "Didn't jitter data!" def test_unjitter_data(self): cat_comp = CategoricalComponent(self.array_data) second_comp = CategoricalComponent(self.array_data) cat_comp.jitter(method='uniform') delta = np.abs(cat_comp.codes - second_comp.codes).sum() assert delta > 0 cat_comp.jitter(method=None) np.testing.assert_equal(cat_comp.codes, second_comp.codes, "Didn't un-jitter data!") def test_jitter_on_init(self): cat_comp = CategoricalComponent(self.array_data, jitter='uniform') second_comp = CategoricalComponent(self.array_data) assert np.all(cat_comp.codes != second_comp.codes) def test_object_dtype(self): d = np.array([1, 3, 3, 1, 'a', 'b', 'a'], dtype=object) c = CategoricalComponent(d) np.testing.assert_array_equal(c.categories, np.array([1, 3, 'a', 'b'], dtype=object)) np.testing.assert_array_equal(c.codes, [0, 1, 1, 0, 2, 3, 2]) def test_valueerror_on_bad_jitter(self): with pytest.raises(ValueError): cat_comp = CategoricalComponent(self.array_data) cat_comp.jitter(method='this will never be a jitter method') class TestCoordinateComponent(object): def setup_method(self, method): class TestCoords(Coordinates): def __init__(self): super().__init__(pixel_n_dim=3, world_n_dim=3) def pixel_to_world_values(self, *args): return [a * (i + 1) for i, a in enumerate(args)] def world_to_pixel_values(self, *args): return [a / (i + 1) for i, a in enumerate(args)] @property def axis_correlation_matrix(self): return np.identity(3).astype(bool) data = core.Data() data.add_component(Component(np.zeros((3, 3, 3))), 'test') data.coords = TestCoords() self.data = data self.px = CoordinateComponent(data, 2) self.py = CoordinateComponent(data, 1) self.pz = CoordinateComponent(data, 0) self.wx = CoordinateComponent(data, 2, world=True) self.wy = CoordinateComponent(data, 1, world=True) self.wz = CoordinateComponent(data, 0, world=True) def test_data(self): z, y, x = np.mgrid[0:3, 0:3, 0:3] np.testing.assert_array_equal(self.px.data, x) np.testing.assert_array_equal(self.py.data, y) np.testing.assert_array_equal(self.pz.data, z) np.testing.assert_array_equal(self.wx.data, x * 1) np.testing.assert_array_equal(self.wy.data, y * 2) np.testing.assert_array_equal(self.wz.data, z * 3) @pytest.mark.parametrize(('view'), VIEWS) def test_view(self, view): z, y, x = np.mgrid[0:3, 0:3, 0:3] np.testing.assert_array_equal(self.px[view], x[view]) np.testing.assert_array_equal(self.py[view], y[view]) np.testing.assert_array_equal(self.pz[view], z[view]) np.testing.assert_array_equal(self.wx[view], x[view] * 1) np.testing.assert_array_equal(self.wy[view], y[view] * 2) np.testing.assert_array_equal(self.wz[view], z[view] * 3) def check_binary(result, left, right, op): assert isinstance(result, core.subset.InequalitySubsetState) assert result.left is left assert result.right is right assert result.operator is op def check_link(result, left, right): assert isinstance(result, core.component_link.ComponentLink) if isinstance(left, ComponentID): assert left in result.get_from_ids() if isinstance(right, ComponentID): assert right in result.get_from_ids() # componentID overload COMPARE_OPS = (operator.gt, operator.ge, operator.lt, operator.le) NUMBER_OPS = (operator.add, operator.mul, operator.truediv, operator.sub) @pytest.mark.parametrize(('op'), COMPARE_OPS) def test_inequality_scalar(op): cid = ComponentID('test') result = op(cid, 3) check_binary(result, cid, 3, op) @pytest.mark.parametrize(('op'), COMPARE_OPS) def test_inequality_id(op): cid = ComponentID('test') cid2 = ComponentID('test2') result = op(cid, cid2) check_binary(result, cid, cid2, op) @pytest.mark.parametrize(('op'), NUMBER_OPS) def test_arithmetic_scalar(op): cid = ComponentID('test') result = op(cid, 3) check_link(result, cid, 3) @pytest.mark.parametrize(('op'), NUMBER_OPS) def test_arithmetic_scalar_right(op): cid = ComponentID('test') result = op(3, cid) check_link(result, 3, cid) @pytest.mark.parametrize(('op'), NUMBER_OPS) def test_arithmetic_cid(op): cid = ComponentID('test') cid2 = ComponentID('test2') result = op(cid, cid2) check_link(result, cid, cid2) def test_pow_scalar(): cid = ComponentID('test') result = cid ** 3 check_link(result, cid, 3) @pytest.mark.parametrize(('view'), VIEWS) def test_view(view): comp = Component(np.array([[1, 2, 3], [2, 3, 4]])) np.testing.assert_array_equal(comp[view], comp.data[view]) @pytest.mark.parametrize(('view'), VIEWS) def test_view_derived(view): comp = Component(np.array([[1, 2, 3], [2, 3, 4]])) d = core.Data() cid = d.add_component(comp, 'primary') cid2 = ComponentID("derived") link = core.ComponentLink([cid], cid2, using=lambda x: x * 3) dc = DerivedComponent(d, link) np.testing.assert_array_equal(dc[view], comp.data[view] * 3) @requires_astropy def test_units(): # Make sure that units get converted to strings. At the moment if these # are set to Astropy units for example, things can go wrong for example # when writing out the datasets. Once we settle on a units framework, we # can then use that instead of converting units to strings. from astropy import units as u comp = Component([1, 2, 3], units='m') assert comp.units == 'm' assert isinstance(comp.units, str) comp = Component([1, 2, 3], units=u.m) assert comp.units == 'm' assert isinstance(comp.units, str) def test_scalar_derived(): # Regression test for a bug that caused expressions without components to # fail. Note that the data collection is needed here as the bug occurs in # the link manager set up by the data collection. data = Data(a=[3, 4, 1]) dc = DataCollection([data]) # noqa pc = ParsedCommand('3.5', {}) link = ParsedComponentLink(ComponentID('b'), pc) data.add_component_link(link) np.testing.assert_equal(data['b'], [3.5, 3.5, 3.5]) def test_rename_cid_used_in_derived(): # Unit test to make sure expressions still work when renaming components data = Data(a=[3, 4, 1]) dc = DataCollection([data]) # noqa pc = ParsedCommand('1 + {a}', {'a': data.id['a']}) link = ParsedComponentLink(ComponentID('b'), pc) data.add_component_link(link) np.testing.assert_equal(data['b'], [4, 5, 2]) data.id['a'].label = 'x' np.testing.assert_equal(data['b'], [4, 5, 2]) @pytest.mark.xfail def test_update_cid_used_in_derived(): # Unit test to make sure expressions still work when updating the component # id for components data = Data(a=[3, 4, 1]) dc = DataCollection([data]) # noqa pc = ParsedCommand('1 + {a}', {'a': data.id['a']}) link = ParsedComponentLink(ComponentID('b'), pc) data.add_component_link(link) np.testing.assert_equal(data['b'], [4, 5, 2]) data.update_id(data.id['a'], ComponentID('x')) np.testing.assert_equal(data['b'], [4, 5, 2]) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_hub.py0000644000175000017500000001521213605357235021633 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import pytest from unittest.mock import MagicMock from ..data import Data from ..data_collection import DataCollection from ..exceptions import InvalidSubscriber, InvalidMessage from ..hub import Hub, HubListener from ..message import SubsetMessage, Message from ..subset import Subset class TestHub(object): def setup_method(self, method): self.hub = Hub() def get_subscription(self): msg = Message handler = MagicMock() subscriber = MagicMock(spec_set=HubListener) return msg, handler, subscriber def test_subscribe(self): msg, handler, subscriber = self.get_subscription() self.hub.subscribe(subscriber, msg, handler) assert self.hub.is_subscribed(subscriber, msg) assert self.hub.get_handler(subscriber, msg) == handler def test_get_handler(self): msg, handler, subscriber = self.get_subscription() self.hub.subscribe(subscriber, msg, handler) assert self.hub.get_handler(subscriber, msg) == handler assert self.hub.get_handler(subscriber, None) is None assert self.hub.get_handler(None, msg) is None def test_unsubscribe(self): msg, handler, subscriber = self.get_subscription() self.hub.subscribe(subscriber, msg, handler) self.hub.unsubscribe(subscriber, msg) assert not self.hub.is_subscribed(subscriber, msg) assert self.hub.get_handler(subscriber, msg) is None def test_unsubscribe_all(self): msg, handler, subscriber = self.get_subscription() msg2 = SubsetMessage self.hub.subscribe(subscriber, msg, handler) self.hub.subscribe(subscriber, msg2, handler) self.hub.unsubscribe_all(subscriber) assert not self.hub.is_subscribed(subscriber, msg) assert not self.hub.is_subscribed(subscriber, msg2) def test_unsubscribe_specific_to_message(self): msg, handler, subscriber = self.get_subscription() msg2 = SubsetMessage self.hub.subscribe(subscriber, msg, handler) self.hub.subscribe(subscriber, msg2, handler) self.hub.unsubscribe(subscriber, msg) assert not self.hub.is_subscribed(subscriber, msg) assert self.hub.is_subscribed(subscriber, msg2) def test_broadcast(self): msg, handler, subscriber = self.get_subscription() self.hub.subscribe(subscriber, msg, handler) msg_instance = msg("Test") self.hub.broadcast(msg_instance) handler.assert_called_once_with(msg_instance) def test_unsubscribe_halts_broadcast(self): msg, handler, subscriber = self.get_subscription() self.hub.subscribe(subscriber, msg, handler) self.hub.unsubscribe(subscriber, msg) msg_instance = msg("Test") self.hub.broadcast(msg_instance) assert handler.call_count == 0 def test_unsubscribe_spec_setific_to_message(self): msg, handler, subscriber = self.get_subscription() msg2 = SubsetMessage self.hub.subscribe(subscriber, msg2, handler) msg_instance = msg("Test") self.hub.broadcast(msg_instance) assert handler.call_count == 0 def test_subscription_catches_message_subclasses(self): msg, handler, subscriber = self.get_subscription() msg2 = SubsetMessage self.hub.subscribe(subscriber, msg, handler) msg_instance = msg2(MagicMock(spec_set=Subset)) self.hub.broadcast(msg_instance) handler.assert_called_once_with(msg_instance) def test_handler_ignored_if_subset_handler_present(self): msg, handler, subscriber = self.get_subscription() handler2 = MagicMock() msg2 = SubsetMessage self.hub.subscribe(subscriber, msg, handler) self.hub.subscribe(subscriber, msg2, handler2) msg_instance = SubsetMessage(Subset(None)) self.hub.broadcast(msg_instance) handler2.assert_called_once_with(msg_instance) assert handler.call_count == 0 def test_filter(self): msg, handler, subscriber = self.get_subscription() filter = lambda x: False self.hub.subscribe(subscriber, msg, handler) msg_instance = msg("Test") self.hub.broadcast(msg) assert handler.call_count == 0 def test_broadcast_sends_to_all_subsribers(self): msg, handler, subscriber = self.get_subscription() msg, handler2, subscriber2 = self.get_subscription() self.hub.subscribe(subscriber, msg, handler) self.hub.subscribe(subscriber2, msg, handler2) msg_instance = msg("Test") self.hub.broadcast(msg_instance) handler.assert_called_once_with(msg_instance) handler2.assert_called_once_with(msg_instance) def test_invalid_unsubscribe_ignored(self): msg, handler, subscriber = self.get_subscription() self.hub.unsubscribe(handler, subscriber) def test_invalid_subscribe(self): msg, handler, subscriber = self.get_subscription() with pytest.raises(InvalidSubscriber) as exc: self.hub.subscribe(None, msg, handler) assert exc.value.args[0].startswith("Subscriber must be a HubListener") with pytest.raises(InvalidMessage) as exc: self.hub.subscribe(subscriber, None, handler) assert exc.value.args[0].startswith("message class must be " "a subclass of glue.Message") def test_default_handler(self): msg, handler, subscriber = self.get_subscription() self.hub.subscribe(subscriber, msg) msg_instance = msg("Test") self.hub.broadcast(msg_instance) subscriber.notify.assert_called_once_with(msg_instance) def test_autosubscribe(self): l = MagicMock(spec_set=HubListener) d = MagicMock(spec_set=Data) s = MagicMock(spec_set=Subset) dc = MagicMock(spec_set=DataCollection) hub = Hub(l, d, s, dc) l.register_to_hub.assert_called_once_with(hub) d.register_to_hub.assert_called_once_with(hub) dc.register_to_hub.assert_called_once_with(hub) s.register.assert_called_once_with() def test_invalid_init(self): with pytest.raises(TypeError) as exc: Hub(None) assert exc.value.args[0] == ("Inputs must be HubListener, data, " "subset, or data collection objects") class TestHubListener(object): """This is a dumb test, I know. Fixated on code coverage""" def test_unimplemented(self): hl = HubListener() with pytest.raises(NotImplementedError): hl.register_to_hub(None) with pytest.raises(NotImplementedError): hl.notify(None) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_subset_group.py0000644000175000017500000001744613605357235023611 0ustar noahfxnoahfximport numpy as np from unittest.mock import MagicMock, patch from ...config import settings from .. import DataCollection, Data, SubsetGroup from .. import subset from ..subset import SubsetState from ..subset_group import coerce_subset_groups from .test_state import clone def restore_settings(func): def wrapper(*args, **kwargs): settings.reset_defaults() results = func(*args, **kwargs) settings.reset_defaults() return results return wrapper class TestSubsetGroup(object): def setup_method(self, method): x = Data(label='x', x=[1, 2, 3]) y = Data(label='y', y=[2, 4, 8]) self.dc = DataCollection([x, y]) self.sg = SubsetGroup() def test_creation(self): self.sg.register(self.dc) sg = self.sg for sub, data in zip(sg.subsets, self.dc): assert sub is data.subsets[0] def test_attributes_matched_to_group(self): self.sg.register(self.dc) sg = self.sg for sub in sg.subsets: assert sub.subset_state is sg.subset_state assert sub.label is sg.label def test_attributes_synced_to_group(self): self.sg.register(self.dc) sg = self.sg sg.subsets[0].subset_state = SubsetState() sg.subsets[0].label = 'testing' for sub in sg.subsets: assert sub.subset_state is sg.subset_state assert sub.label is sg.label def test_new_subset_group_syncs_style(self): sg = self.dc.new_subset_group() for sub in sg.subsets: assert sub.style == sg.style def test_changing_subset_style_changes_group(self): # Test to make sure that if a subset's visual properties are changed, # the visual properties of all subsets in the same subset group are changed d1 = Data(x=[1, 2, 3], label='d1') d2 = Data(y=[2, 3, 4], label='d2') d3 = Data(y=[2, 3, 4], label='d3') dc = DataCollection([d1, d2, d3]) sg = dc.new_subset_group(subset_state=d1.id['x'] > 1, label='A') # Changing d1 subset properties changes group and other subsets d1.subsets[0].style.color = '#c0b4a1' assert sg.style.color == '#c0b4a1' assert d2.subsets[0].style.color == '#c0b4a1' assert d3.subsets[0].style.color == '#c0b4a1' d2.subsets[0].style.alpha = 0.2 assert sg.style.alpha == 0.2 assert d1.subsets[0].style.alpha == 0.2 assert d3.subsets[0].style.alpha == 0.2 d3.subsets[0].style.markersize = 16 assert sg.style.markersize == 16 assert d1.subsets[0].style.markersize == 16 assert d2.subsets[0].style.markersize == 16 # Changing subset group changes subsets sg.style.color = '#abcdef' assert d1.subsets[0].style.color == '#abcdef' assert d2.subsets[0].style.color == '#abcdef' assert d3.subsets[0].style.color == '#abcdef' sg.style.linewidth = 12 assert d1.subsets[0].style.linewidth == 12 assert d2.subsets[0].style.linewidth == 12 assert d3.subsets[0].style.linewidth == 12 def test_new_data_creates_subset(self): sg = self.dc.new_subset_group() d = Data(label='z', z=[10, 20, 30]) self.dc.append(d) assert d.subsets[0] in sg.subsets def test_remove_data_deletes_subset(self): sg = self.dc.new_subset_group() sub = self.dc[0].subsets[0] self.dc.remove(self.dc[0]) assert sub not in sg.subsets def test_subsets_given_data_reference(self): sg = self.dc.new_subset_group() assert sg.subsets[0].data is self.dc[0] def test_data_collection_subset(self): sg = self.dc.new_subset_group() assert tuple(self.dc.subset_groups) == (sg,) sg2 = self.dc.new_subset_group() assert tuple(self.dc.subset_groups) == (sg, sg2) def test_remove_subset(self): sg = self.dc.new_subset_group() n = len(self.dc[0].subsets) self.dc.remove_subset_group(sg) assert len(self.dc[0].subsets) == n - 1 def test_edit_broadcasts(self): sg = self.dc.new_subset_group() bcast = MagicMock() sg.subsets[0].broadcast = bcast bcast.reset_mock() sg.subsets[0].style.color = 'red' assert bcast.call_count == 1 def test_braodcast(self): sg = self.dc.new_subset_group() bcast = MagicMock() sg.subsets[0].broadcast = bcast bcast.reset_mock() sg.subset_state = SubsetState() assert bcast.call_count == 1 sg.style.color = '#123456' assert bcast.call_count == 2 sg.label = 'new label' assert bcast.call_count == 3 def test_auto_labeled(self): sg = self.dc.new_subset_group() assert sg.label is not None def test_label_color_cycle(self): sg1 = self.dc.new_subset_group() sg2 = self.dc.new_subset_group() assert sg1.label != sg2.label assert sg1.style.color != sg2.style.color def test_new_label(self): sg = self.dc.new_subset_group(label='test') assert sg.label == 'test' def test_new_state(self): state = SubsetState() sg = self.dc.new_subset_group(subset_state=state) assert sg.subset_state is state def test_deleted_subsets_dont_respawn(self): # regression test sg1 = self.dc.new_subset_group() self.dc.remove_subset_group(sg1) d = Data(label='z', z=[1, 2, 3]) self.dc.append(d) assert len(d.subsets) == 0 class TestSerialze(TestSubsetGroup): def test_save_group(self): sg = self.dc.new_subset_group() sg2 = clone(sg) assert sg.style == sg2.style assert sg.label == sg2.label def test_save_subset(self): sg = self.dc.new_subset_group() sg.subset_state = self.dc[0].id['x'] > 1 sub = sg.subsets[0] dc = clone(self.dc) sub2 = dc[0].subsets[0] np.testing.assert_array_equal(sub2.to_mask(), [False, True, True]) assert sub2.style == sg.style assert sub2.label == sg.label def test_save_override(self): sg = self.dc.new_subset_group() sg.subsets[0].style.color = 'blue' dc = clone(self.dc) assert dc.subset_groups[0].style == sg.style assert dc.subset_groups[0].subsets[0].style.color == 'blue' class TestCombination(object): def check_type_and_children(self, s1, s2, s3, statetype): assert isinstance(s3, statetype) assert s3.state1 is s1.subset_state assert s3.state2 is s2.subset_state def test_and(self): s1, s2 = SubsetGroup(), SubsetGroup() assert isinstance(s1 & s2, subset.AndState) def test_or(self): s1, s2 = SubsetGroup(), SubsetGroup() assert isinstance(s1 | s2, subset.OrState) def test_xor(self): s1, s2 = SubsetGroup(), SubsetGroup() assert isinstance(s1 ^ s2, subset.XorState) def test_invert(self): s1 = SubsetGroup() assert isinstance(~s1, subset.InvertState) class TestCoerce(object): def setup_method(self, method): self.x = Data(label='x', x=[1, 2, 3]) self.y = Data(label='y', y=[1, 2, 3]) self.dc = DataCollection([self.x, self.y]) def test_noop_on_good_setup(self): with patch('glue.core.subset_group.warn') as warn: coerce_subset_groups(self.dc) assert warn.call_count == 0 def test_reassign_non_grouped_subsets(self): s = self.x.new_subset() dc = self.dc with patch('glue.core.subset_group.warn') as warn: coerce_subset_groups(dc) assert len(dc.subset_groups) == 1 assert dc.subset_groups[0].subset_state is s.subset_state assert dc.subset_groups[0].style == s.style assert dc.subset_groups[0].label == s.label assert warn.call_count == 1 glueviz-1.0.1+dfsg.orig/glue/core/tests/test_data_translation.py0000644000175000017500000002707513605360514024410 0ustar noahfxnoahfx# Tests for the translation infrastrcture between glue Data/Subset/SubsetState # objects and other data containers registered with glue. import pytest import numpy as np from numpy.testing import assert_equal from ..component_id import ComponentID from ..data import Data from ..data_collection import DataCollection from ..registry import Registry from glue.config import data_translator, subset_state_translator from ..subset import InequalitySubsetState class FakeDataObject(object): array = None name = None class AnotherFakeDataObject(object): pass class CustomSelectionObject(object): serialized = None def setup_module(self): @data_translator(FakeDataObject) class FakeDataObjectHandler: def to_data(self, obj): data = Data() data[obj.name] = obj.array return data def to_object(self, data_or_subset): cid = data_or_subset.main_components[0] obj = FakeDataObject() obj.array = data_or_subset[cid] obj.name = cid.label return obj @subset_state_translator('my_subset_translator') class FakeSubsetDefinitionTranslator: """ A simple subset translator that only knows how to handle InequalitySubsetState and translates it to a custom serialization. We include a keyword argument for the translation to make sure it gets passed through. """ def to_object(self, subset): subset_state = subset.subset_state if isinstance(subset_state, InequalitySubsetState): if isinstance(subset_state.left, ComponentID): left = '{' + subset_state.left.label + '}' else: left = subset_state.left if isinstance(subset_state.right, ComponentID): right = '{' + subset_state.right.label + '}' else: right = subset_state.right c = CustomSelectionObject() c.serialized = '{0} {1} {2}'.format(left, subset_state.operator.__name__, right) return c else: raise TypeError("my_subset_translator could not translate " "subset state of type {0}".format(subset_state.__class__.__name__)) def teardown_module(self): data_translator.remove(FakeDataObject) subset_state_translator.remove('my_subset_translator') class TestTranslationData: def teardown_method(self, method): Registry().clear() def test_get_object_basic(self): data = Data(spam=np.arange(10)) obj = data.get_object(cls=FakeDataObject) assert isinstance(obj, FakeDataObject) assert_equal(obj.array, np.arange(10)) assert_equal(obj.name, 'spam') def test_get_object_invalid(self): data = Data(spam=np.arange(10)) with pytest.raises(TypeError) as exc: data.get_object(cls=AnotherFakeDataObject) assert exc.value.args[0] == 'Could not find a class to translate objects of type Data to AnotherFakeDataObject' def test_get_object_explicit_class(self): data = Data(x=[1, 2, 3], label='myobj') with pytest.raises(ValueError) as exc: data.get_object() assert exc.value.args[0] == ('Specify the object class to use with cls= - ' 'supported classes are:\n\n* pandas.core.frame.DataFrame\n' '* glue.core.tests.test_data_translation.FakeDataObject') obj = data.get_object(cls=FakeDataObject) assert isinstance(obj, FakeDataObject) assert_equal(obj.array, [1, 2, 3]) assert_equal(obj.name, 'x') def test_get_subset_object(self): data = Data(spam=np.arange(10)) data.add_subset(data.id['spam'] > 4.5, label='subset 1') # Check that the following three are equivalent for subset_id in [None, 0, 'subset 1']: subset = data.get_subset_object(subset_id=subset_id, cls=FakeDataObject) assert isinstance(subset, FakeDataObject) assert_equal(subset.array, np.arange(5, 10)) assert subset.name == 'spam' def test_get_subset_object_explicit_class(self): data = Data(spam=np.arange(10)) data.add_subset(data.id['spam'] > 4.5, label='subset 1') with pytest.raises(ValueError) as exc: data.get_subset_object() assert exc.value.args[0] == ('Specify the object class to use with cls= - ' 'supported classes are:\n\n* pandas.core.frame.DataFrame\n' '* glue.core.tests.test_data_translation.FakeDataObject') subset = data.get_subset_object(subset_id=0, cls=FakeDataObject) assert isinstance(subset, FakeDataObject) assert_equal(subset.array, np.arange(5, 10)) assert subset.name == 'spam' def test_get_subset_object_invalid(self): data = Data(x=np.arange(10), label='myobj') with pytest.raises(ValueError) as exc: data.get_subset_object(subset_id='subset 2', cls=FakeDataObject) assert exc.value.args[0] == "Dataset does not contain any subsets" data.add_subset(data.id['x'] > 4.5, label='subset 1') data.add_subset(data.id['x'] > 3.5, label='subset 1') with pytest.raises(ValueError) as exc: data.get_subset_object(subset_id='subset 2', cls=FakeDataObject) assert exc.value.args[0] == "No subset found with the label 'subset 2'" with pytest.raises(ValueError) as exc: data.get_subset_object(cls=FakeDataObject) assert exc.value.args[0] == "Several subsets are present, specify which one to retrieve with subset_id= - valid options are:\n\n* 0 or 'subset 1'\n* 1 or 'subset 1_01'" # FIXME: currently we disambiguate subset names, but would maybe be # better to just raise an error. In any case the test below never # works because of the auto-disambiguation. # with pytest.raises(ValueError) as exc: # data.get_subset_object(subset_id='subset 1', cls=FakeDataObject) # assert exc.value.args[0] == "Several subsets were found with the label 'subset 1', use a numerical index instead" subset = data.get_subset_object(subset_id=0, cls=FakeDataObject) assert isinstance(subset, FakeDataObject) assert_equal(subset.array, np.arange(5, 10)) assert subset.name == 'x' def test_get_selection_basic(self): data = Data(x=[1, 2, 3], label='basic') data.add_subset(data.id['x'] > 1, label='subset 1') # Check that the following three are equivalent for subset_id in [None, 0, 'subset 1']: result = data.get_selection_definition(subset_id=subset_id, format='my_subset_translator') assert isinstance(result, CustomSelectionObject) assert result.serialized == '{x} gt 1' def test_get_selection_invalid(self): data = Data(x=[1, 2, 3], label='basic') with pytest.raises(ValueError) as exc: data.get_selection_definition(subset_id=0) assert exc.value.args[0] == "Dataset does not contain any subsets" data.add_subset((data.id['x'] > 1) & (data.id['x'] < 3), label='subset 1') with pytest.raises(TypeError) as exc: data.get_selection_definition(subset_id=0, format='my_subset_translator') assert exc.value.args[0] == 'my_subset_translator could not translate subset state of type AndState' with pytest.raises(ValueError) as exc: data.get_selection_definition(subset_id=0) assert exc.value.args[0] == ("Subset state handler format not set - should be one " "of:\n\n* 'my_subset_translator'") with pytest.raises(ValueError) as exc: data.get_selection_definition(subset_id=0, format='invalid_translator') assert exc.value.args[0] == ("Invalid subset state handler format 'invalid_translator' " "- should be one of:\n\n* 'my_subset_translator'") data.add_subset((data.id['x'] > 1) & (data.id['x'] < 3), label='subset 1') data.add_subset((data.id['x'] > 1) & (data.id['x'] < 3), label='subset 3') with pytest.raises(ValueError) as exc: data.get_selection_definition(subset_id='subset 2', format='invalid_translator') assert exc.value.args[0] == "No subset found with the label 'subset 2'" with pytest.raises(ValueError) as exc: data.get_selection_definition(format='invalid_translator') assert exc.value.args[0] == ("Several subsets are present, specify which " "one to retrieve with subset_id= - valid options " "are:\n\n* 0 or 'subset 1'\n* 1 or 'subset 1_01'\n* 2 or 'subset 3'") # with pytest.raises(ValueError) as exc: # data.get_selection_definition(subset_id='subset 1', format='invalid_translator') # assert exc.value.args[0] == "Several subsets were found with the label 'subset 1', use a numerical index instead" class TestTranslationDataCollection: # The main purpose of these tests is to make sure that glue remembers the # original class from objects that are added to the data collection. def setup_method(self, method): self.dc = DataCollection() def test_set_and_get_object_basic(self): obj = FakeDataObject() obj.array = np.arange(10) obj.name = 'spam' assert len(self.dc) == 0 self.dc['myobj'] = obj assert len(self.dc) == 1 assert self.dc['myobj'] is self.dc[0] data = self.dc['myobj'] assert isinstance(data, Data) assert_equal(data['spam'], np.arange(10)) # Make sure preferred class is used obj_out = self.dc['myobj'].get_object() assert isinstance(obj_out, FakeDataObject) assert obj_out is not obj assert_equal(obj_out.array, np.arange(10)) assert_equal(obj_out.name, 'spam') def test_set_object_invalid(self): obj = AnotherFakeDataObject() with pytest.raises(TypeError) as exc: self.dc['myobj'] = obj assert exc.value.args[0] == 'Could not find a class to translate objects of type AnotherFakeDataObject to Data' def test_get_subset_object(self): obj = FakeDataObject() obj.array = np.arange(10) obj.name = 'spam' self.dc['myobj'] = obj self.dc.new_subset_group(subset_state=self.dc['myobj'].id['spam'] > 4.5, label='subset 1') subset = self.dc['myobj'].get_subset_object(subset_id=0) # Make sure preferred class is used assert isinstance(subset, FakeDataObject) assert_equal(subset.array, np.arange(5, 10)) assert subset.name == 'spam' def test_set_duplicate(self): # Make sure data gets replaced when re-using a data collection key assert len(self.dc) == 0 self.dc['myobj'] = Data(x=[1, 2, 3]) assert len(self.dc) == 1 assert_equal(self.dc[0]['x'], [1, 2, 3]) self.dc['myobj'] = Data(x=[4, 5, 6]) assert len(self.dc) == 1 assert_equal(self.dc[0]['x'], [4, 5, 6]) # And make sure if there are multiple datasets with the key # pre-existing then we get rid of them all self.dc.append(Data(x=[7, 8, 9], label='myobj')) assert len(self.dc) == 2 self.dc['myobj'] = Data(x=[1, 2, 3]) assert len(self.dc) == 1 assert_equal(self.dc[0]['x'], [1, 2, 3]) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_fixed_resolution_buffer.py0000644000175000017500000001167613605357235026002 0ustar noahfxnoahfximport pytest import numpy as np from numpy.testing import assert_equal from glue.core import Data, DataCollection from glue.core.link_helpers import LinkSame ARRAY = np.arange(3024).reshape((6, 7, 8, 9)).astype(float) class TestFixedResolutionBuffer(): def setup_method(self, method): self.data_collection = DataCollection() # The reference dataset. Shape is (6, 7, 8, 9). self.data1 = Data(x=ARRAY) self.data_collection.append(self.data1) # A dataset with the same shape but not linked. Shape is (6, 7, 8, 9). self.data2 = Data(x=ARRAY) self.data_collection.append(self.data2) # A dataset with the same number of dimensions but in a different # order, linked to the first. Shape is (9, 7, 6, 8). self.data3 = Data(x=np.moveaxis(ARRAY, (3, 1, 0, 2), (0, 1, 2, 3))) self.data_collection.append(self.data3) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[0], self.data3.pixel_component_ids[2])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[1], self.data3.pixel_component_ids[1])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[2], self.data3.pixel_component_ids[3])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[3], self.data3.pixel_component_ids[0])) # A dataset with fewer dimensions, linked to the first one. Shape is # (8, 7, 6) self.data4 = Data(x=ARRAY[:, :, :, 0].transpose()) self.data_collection.append(self.data4) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[0], self.data4.pixel_component_ids[2])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[1], self.data4.pixel_component_ids[1])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[2], self.data4.pixel_component_ids[0])) # A dataset with even fewer dimensions, linked to the first one. Shape # is (8, 6) self.data5 = Data(x=ARRAY[:, 0, :, 0].transpose()) self.data_collection.append(self.data5) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[0], self.data5.pixel_component_ids[1])) self.data_collection.add_link(LinkSame(self.data1.pixel_component_ids[2], self.data5.pixel_component_ids[0])) # A dataset that is not on the same pixel grid and requires reprojection # self.data6 = Data() # self.data6.coords = SimpleCoordinates() # self.array_nonaligned = np.arange(60).reshape((5, 3, 4)) # self.data6['x'] = np.array(self.array_nonaligned) # self.data_collection.append(self.data6) # self.data_collection.add_link(LinkSame(self.data1.world_component_ids[0], # self.data6.world_component_ids[1])) # self.data_collection.add_link(LinkSame(self.data1.world_component_ids[1], # self.data6.world_component_ids[2])) # self.data_collection.add_link(LinkSame(self.data1.world_component_ids[2], # self.data6.world_component_ids[0])) # Start off with the cases where the data is the target data. Enumerate # the different cases for the bounds and the expected result. DATA_IS_TARGET_CASES = [ # Bounds are full extent of data ([(0, 5, 6), (0, 6, 7), (0, 7, 8), (0, 8, 9)], ARRAY), # Bounds are inside data ([(2, 3, 2), (3, 3, 1), (0, 7, 8), (0, 7, 8)], ARRAY[2:4, 3:4, :, :8]), # Bounds are outside data along some dimensions ([(-5, 9, 15), (3, 5, 3), (0, 9, 10), (5, 6, 2)], np.pad(ARRAY[:, 3:6, :, 5:7], [(5, 4), (0, 0), (0, 2), (0, 0)], mode='constant', constant_values=-np.inf)), # No overlap ([(2, 3, 2), (3, 3, 1), (-5, -4, 2), (0, 7, 8)], -np.inf * np.ones((2, 1, 2, 8))) ] @pytest.mark.parametrize(('bounds', 'expected'), DATA_IS_TARGET_CASES) def test_data_is_target_full_bounds(self, bounds, expected): buffer = self.data1.compute_fixed_resolution_buffer(target_data=self.data1, bounds=bounds, target_cid=self.data1.id['x']) assert_equal(buffer, expected) buffer = self.data3.compute_fixed_resolution_buffer(target_data=self.data1, bounds=bounds, target_cid=self.data3.id['x']) assert_equal(buffer, expected) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_roi_transfoms.py0000644000175000017500000001177613752534424023755 0ustar noahfxnoahfximport numpy as np from numpy.testing import assert_allclose from glue.core.roi_pretransforms import ProjectionMplTransform from glue.core.state import GlueSerializer, GlueUnSerializer def roundtrip_transform(transform): gs = GlueSerializer(transform) out_str = gs.dumps() obj = GlueUnSerializer.loads(out_str) return obj.object('__main__') def test_simple_polar_mpl_transform(): angles = np.deg2rad(np.array([0, 45, 90, 180, 300, 810, 0, 0])) radii = np.array([0, 5, 4, 1, 0, 2, -10, 10]) transform = ProjectionMplTransform('polar', [0, 2 * np.pi], [-5, 5], 'linear', 'linear') x, y = transform(angles, radii) expected_x = np.array([0.75, 0.8535533905932736, 0.5, 0.2, .625, 0.5, np.nan, 1.25]) expected_y = np.array([0.5, 0.8535533905932736, 0.95, 0.5, 0.28349364905389035, 0.85, np.nan, 0.5]) assert_allclose(x, expected_x) assert_allclose(y, expected_y) new_transform = roundtrip_transform(transform) new_x, new_y = new_transform(angles, radii) assert_allclose(new_x, x, rtol=1e-14) assert_allclose(new_y, y, rtol=1e-14) def test_log_polar_mpl_transform(): angles = np.deg2rad(np.array([0, 90, 180])) radii = np.array([10, 100, 1000]) transform = ProjectionMplTransform('polar', [0, 2 * np.pi], [1, 10000], 'linear', 'log') x, y = transform(angles, radii) expected_x = np.array([0.5, 0.5, 0.25]) expected_y = np.array([0.5, 0.625, 0.5]) assert_allclose(x, expected_x) assert_allclose(y, expected_y) new_transform = roundtrip_transform(transform) new_x, new_y = new_transform(angles, radii) assert_allclose(new_x, x, rtol=1e-14) assert_allclose(new_y, y, rtol=1e-14) def test_wedge_polar_mpl_transform(): angles = np.deg2rad(np.array([0, 45, 90, 135, 180])) radii = np.array([0.1, 0.2, 0.3, 0.4, 0.5]) transform = ProjectionMplTransform('polar', [0, np.pi], [0, 0.5], 'linear', 'linear') x, y = transform(angles, radii) assert_allclose(x, np.array([0.6, 0.64142136, 0.5, 0.21715729, 0])) # For just the upper half, y is between 0.25 and 0.75 assert_allclose(y, np. array([0.25, 0.39142136, 0.55, 0.53284271, 0.25])) new_transform = roundtrip_transform(transform) new_x, new_y = new_transform(angles, radii) assert_allclose(new_x, x, rtol=1e-14) assert_allclose(new_y, y, rtol=1e-14) def test_aitoff_mpl_transform(): transform = ProjectionMplTransform('aitoff', [-np.pi, np.pi], [-np.pi / 2, np.pi / 2], 'linear', 'linear') long = np.deg2rad(np.array([0, -90, 0, 45])) lat = np.deg2rad(np.array([0, 0, 45, -45])) x, y = transform(long, lat) expected_x = np.array([0.5, 0.25, 0.5, 0.59771208]) expected_y = np.array([0.5, 0.5, 0.75, 0.24466602]) assert_allclose(x, expected_x) assert_allclose(y, expected_y) new_transform = roundtrip_transform(transform) new_x, new_y = new_transform(long, lat) assert_allclose(new_x, x, rtol=1e-14) assert_allclose(new_y, y, rtol=1e-14) def test_hammer_mpl_transform(): transform = ProjectionMplTransform('hammer', [-np.pi, np.pi], [-np.pi / 2, np.pi / 2], 'linear', 'linear') long = np.deg2rad(np.array([0, -90, 0, 45])) lat = np.deg2rad(np.array([0, 0, 45, -45])) x, y = transform(long, lat) expected_x = np.array([0.5, 0.22940195, 0.5, 0.60522557]) expected_y = np.array([0.5, 0.5, 0.77059805, 0.22503235]) assert_allclose(x, expected_x) assert_allclose(y, expected_y) new_transform = roundtrip_transform(transform) new_x, new_y = new_transform(long, lat) assert_allclose(new_x, x, rtol=1e-14) assert_allclose(new_y, y, rtol=1e-14) def test_lambert_mpl_projection(): transform = ProjectionMplTransform('lambert', [-np.pi, np.pi], [-np.pi / 2, np.pi / 2], 'linear', 'linear') long = np.deg2rad(np.array([0, -90, 0, 45])) lat = np.deg2rad(np.array([0, 0, 45, -45])) x, y = transform(long, lat) expected_x = np.array([0.5, 0.14644661, 0.5, 0.64433757]) expected_y = np.array([0.5, 0.5, 0.69134172, 0.29587585]) assert_allclose(x, expected_x) assert_allclose(y, expected_y) new_transform = roundtrip_transform(transform) new_x, new_y = new_transform(long, lat) assert_allclose(new_x, x, rtol=1e-14) assert_allclose(new_y, y, rtol=1e-14) def test_mollweide_mpl_projection(): transform = ProjectionMplTransform('mollweide', [-np.pi, np.pi], [-np.pi / 2, np.pi / 2], 'linear', 'linear') long = np.deg2rad(np.array([0, -90, 0, 45])) lat = np.deg2rad(np.array([0, 0, 45, -45])) x, y = transform(long, lat) expected_x = np.array([0.5, 0.25, 0.5, 0.60076564]) expected_y = np.array([0.5, 0.5, 0.79587254, 0.20412746]) assert_allclose(x, expected_x) assert_allclose(y, expected_y) new_transform = roundtrip_transform(transform) new_x, new_y = new_transform(long, lat) assert_allclose(new_x, x, rtol=1e-14) assert_allclose(new_y, y, rtol=1e-14) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_pandas.py0000644000175000017500000000424113605357235022323 0ustar noahfxnoahfximport numpy as np import pandas as pd from unittest.mock import MagicMock from pandas.util.testing import (assert_series_equal, assert_frame_equal) from ..component import Component, DerivedComponent, CategoricalComponent from ..data import Data class TestPandasConversion(object): def test_Component_conversion(self): comp = Component(np.arange(5)) series = pd.Series(np.arange(5)) assert_series_equal(series, comp.to_series()) def test_DerivedComponent_conversion(self): data = MagicMock() link = MagicMock() link.compute.return_value = np.arange(5) comp = DerivedComponent(data, link) series = pd.Series(np.arange(5)) assert_series_equal(series, comp.to_series()) def test_CategoricalComponent_conversion(self): comp = CategoricalComponent(np.array(['a', 'b', 'c', 'd'])) series = pd.Series(['a', 'b', 'c', 'd']) assert_series_equal(series, comp.to_series()) def test_CoordinateComponent_conversion(self): d = Data(x=[1, 2, 3]) series = pd.Series(np.array([0, 1, 2])) comp = d.get_component(d.pixel_component_ids[0]) assert_series_equal(series, comp.to_series()) def test_Data_conversion(self): d = Data(n=np.array([4, 5, 6, 7])) cat_comp = CategoricalComponent(np.array(['a', 'b', 'c', 'd'])) d.add_component(cat_comp, 'c') link = MagicMock() link.compute.return_value = np.arange(4) deriv_comp = DerivedComponent(d, link) d.add_component(deriv_comp, 'd') order = [comp.label for comp in d.components] frame = pd.DataFrame() frame['Pixel Axis 0 [x]'] = np.ogrid[0:4] frame['n'] = np.array([4, 5, 6, 7]) frame['c'] = ['a', 'b', 'c', 'd'] frame['d'] = np.arange(4) out_frame = d.to_dataframe() assert_frame_equal(out_frame, frame) assert list(out_frame.columns) == order def test_multi_dimensional(self): a = np.array([[2, 3], [5, 4], [6, 7]]) comp = Component(a) series = pd.Series(a.ravel()) assert_series_equal(series, comp.to_series()) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_parse.py0000644000175000017500000001746413605357235022202 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import pytest import numpy as np from unittest.mock import MagicMock, patch from .. import parse from ..data import ComponentID, Component, Data from ..subset import Subset class TestParse(object): def test_re_matches_valid_names(self): reg = parse.TAG_RE valid = ['{a}', '{ a }', '{A}', '{a }', '{ a}', '{a_}', '{abc_1}', '{_abc_1}', '{1}', '{1_}'] invalid = ['', '{}', '{a '] for v in valid: assert reg.match(v) is not None for i in invalid: assert reg.match(i) is None def test_group(self): reg = parse.TAG_RE assert reg.match('{a}').group('tag') == 'a' assert reg.match('{ a }').group('tag') == 'a' assert reg.match('{ A }').group('tag') == 'A' assert reg.match('{ Abc_ }').group('tag') == 'Abc_' def test_reference_list(self): cmd = '{a} - {b} + {c}' refs = {'a': 1, 'b': 2, 'c': 3, 'd': 4} expected = set([1, 2, 3]) result = set(parse._reference_list(cmd, refs)) assert expected == result def test_reference_list_invalid_cmd(self): with pytest.raises(KeyError) as exc: parse._reference_list('{a}', {}) assert exc.value.args[0] == ("Tags from command not in " "reference mapping") def test_dereference(self): c1 = ComponentID('c1') c2 = ComponentID('c2') s1 = Subset(None, label='s1') s2 = Subset(None, label='s2') refs = dict([('c1', c1), ('c2', c2), ('s1', s1), ('s2', s2)]) cmd = '({c1} > 10) and {s1}' expected = ('(data[references["c1"], __view] > 10) and ' 'references["s1"].to_mask(__view)') result = parse._dereference(cmd, refs) assert expected == result def test_validate(self): ref = {'a': ComponentID('ca'), 'b': ComponentID('cb')} parse._validate('{a} + {b}', ref) parse._validate('{a}', ref) parse._validate('3 + 4', ref) with pytest.raises(parse.InvalidTagError) as exc: parse._validate('{c}', ref) assert exc.value.args[0] == ("Tag c not in reference mapping: " "['a', 'b']") def test_ensure_only_component_references(self): ref = {'a': 1, 'b': ComponentID('b')} F = parse._ensure_only_component_references F('{b} + 5', ref) with pytest.raises(TypeError) as exc: F('{b} + {a}', ref) assert exc.value.args[0] == ("Reference to a, which is not a " "ComponentID") with pytest.raises(TypeError) as exc: F('{b} + {d}', ref) assert exc.value.args[0] == ("Reference to d, which is not a " "ComponentID") class TestParsedCommand(object): def test_evaluate_component(self): data = MagicMock() c1 = ComponentID('c1') data.__getitem__.return_value = 5 cmd = '{comp1} * 5' refs = {'comp1': c1} pc = parse.ParsedCommand(cmd, refs) assert pc.evaluate(data) == 25 data.__getitem__.assert_called_once_with((c1, None)) def test_evaluate_subset(self): data = Data(x=[1, 2, 3]) sub1 = data.new_subset(data.id['x'] > 1) sub2 = data.new_subset(data.id['x'] < 3) cmd = '{s1} & {s2}' refs = {'s1': sub1, 's2': sub2} pc = parse.ParsedCommand(cmd, refs) np.testing.assert_equal(pc.evaluate(data), [0, 1, 0]) def test_evaluate_function(self): data = MagicMock() c1 = ComponentID('c1') data.__getitem__.return_value = 5 cmd = 'max({comp1}, 100)' refs = {'comp1': c1} pc = parse.ParsedCommand(cmd, refs) assert pc.evaluate(data) == 100 data.__getitem__.assert_called_once_with((c1, None)) def test_evaluate_math(self): # If numpy, np, and math aren't defined in the config.py file, they # are added to the local variables available. data = MagicMock() c1 = ComponentID('c1') data.__getitem__.return_value = 10 refs = {'comp1': c1} cmd = 'numpy.log10({comp1})' pc = parse.ParsedCommand(cmd, refs) assert pc.evaluate(data) == 1 cmd = 'np.log10({comp1})' pc = parse.ParsedCommand(cmd, refs) assert pc.evaluate(data) == 1 cmd = 'math.log10({comp1})' pc = parse.ParsedCommand(cmd, refs) assert pc.evaluate(data) == 1 def test_evaluate_test(self): data = MagicMock() c1 = ComponentID('c1') data.__getitem__.return_value = 10 refs = {'comp1': c1} cmd = 'numpy.log10({comp1}) + 3.4 - {comp1}' pc = parse.ParsedCommand(cmd, refs) pc.evaluate_test() cmd = 'nump.log10({comp1}) + 3.4 - {comp1}' pc = parse.ParsedCommand(cmd, refs) with pytest.raises(NameError) as exc: pc.evaluate_test() assert exc.value.args[0] == "name 'nump' is not defined" def test_globals(self): with patch('glue.env') as patched_env: import numpy as np patched_env.np = np data = Data() pc = parse.ParsedCommand('np.sin(3.4)', {}) pc.evaluate(data) class TestParsedComponentLink(object): def make_link(self): data = Data() comp = Component(np.array([1, 2, 3])) c1 = ComponentID('c1') c2 = ComponentID('c2') data.add_component(comp, c1) cmd = '{comp1} * 100' refs = {'comp1': c1} pc = parse.ParsedCommand(cmd, refs) cl = parse.ParsedComponentLink(c2, pc) data.add_component_link(cl) return data, c2 def test(self): data, cid = self.make_link() result = data[cid] expected = np.array([100, 200, 300]) np.testing.assert_array_equal(result, expected) def test_not_identity(self): # regression test d = Data(x=[1, 2, 3]) c2 = ComponentID('c2') cmd = '{x}' refs = {'x': d.id['x']} pc = parse.ParsedCommand(cmd, refs) link = parse.ParsedComponentLink(c2, pc) assert not link.identity def test_slice(self): data, cid = self.make_link() result = data[cid, ::2] np.testing.assert_array_equal(result, [100, 300]) def test_save_load(self): from .test_state import clone d = Data(x=[1, 2, 3]) c2 = ComponentID('c2') cmd = '{x} + 1' refs = {'x': d.id['x']} pc = parse.ParsedCommand(cmd, refs) link = parse.ParsedComponentLink(c2, pc) d.add_component_link(link) d2 = clone(d) np.testing.assert_array_equal(d2['c2'], [2, 3, 4]) class TestParsedSubsetState(object): def setup_method(self, method): data = Data(g=[2, 4, 6, 8]) s1 = data.new_subset() s2 = data.new_subset() s1.subset_state = np.array([1, 1, 1, 0], dtype=bool) s2.subset_state = np.array([0, 1, 1, 1], dtype=bool) self.refs = {'s1': s1, 's2': s2, 'g': data.id['g']} self.data = data def test_two_subset(self): cmd = '{s1} & {s2}' s = self.data.new_subset() p = parse.ParsedCommand(cmd, self.refs) state = parse.ParsedSubsetState(p) s.subset_state = state result = s.to_mask() expected = np.array([0, 1, 1, 0], dtype=bool) np.testing.assert_array_equal(result, expected) def test_two_subset_and_component(self): cmd = '{s1} & {s2} & ({g} < 6)' s = self.data.new_subset() p = parse.ParsedCommand(cmd, self.refs) state = parse.ParsedSubsetState(p) s.subset_state = state result = s.to_mask() expected = np.array([0, 1, 0, 0], dtype=bool) np.testing.assert_array_equal(result, expected) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_edit_subset_mode.py0000644000175000017500000000546313605357235024402 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import numpy as np from ..data import Component, Data from ..data_collection import DataCollection from ..edit_subset_mode import (EditSubsetMode, ReplaceMode, OrMode, AndMode, XorMode, AndNotMode) from ..subset import ElementSubsetState class TestEditSubsetMode(object): def setup_method(self, method): data = Data() comp = Component(np.array([1, 2, 3])) ind1 = np.array([0, 1]) ind2 = np.array([1, 2]) cid = data.add_component(comp, 'test') state1 = ElementSubsetState(ind1) state2 = ElementSubsetState(ind2) self.edit_mode = EditSubsetMode() self.edit_mode.edit_subset = data.new_subset() self.edit_mode.edit_subset.subset_state = state1 self.data = data self.cid = cid self.state1 = state1 self.state2 = state2 def check_mode(self, mode, expected): self.edit_mode.mode = mode self.edit_mode.update(self.data, self.state2) np.testing.assert_array_equal(self.edit_mode.edit_subset.to_mask(), expected) def test_replace(self): self.check_mode(ReplaceMode, [False, True, True]) def test_or(self): self.check_mode(OrMode, [True, True, True]) def test_and(self): self.check_mode(AndMode, [False, True, False]) def test_xor(self): self.check_mode(XorMode, [True, False, True]) def test_and_not(self): self.check_mode(AndNotMode, [True, False, False]) def test_combine_maps_over_multiselection(self): """If data has many edit subsets, act on all of them""" self.edit_mode.mode = ReplaceMode for i in range(5): self.data.new_subset() self.edit_mode.edit_subset = list(self.data.subsets) self.edit_mode.update(self.data, self.state2) expected = np.array([False, True, True]) for s in self.data.subsets: np.testing.assert_array_equal(s.to_mask(), expected) def test_combine_with_collection(self): """A data collection input works on each data object""" self.edit_mode.mode = ReplaceMode for i in range(5): self.data.new_subset() self.edit_mode.edit_subset = list(self.data.subsets) dc = DataCollection([self.data]) self.edit_mode.update(dc, self.state2) expected = np.array([False, True, True]) for s in self.data.subsets: np.testing.assert_array_equal(s.to_mask(), expected) def test_combines_make_copy(self): self.edit_mode.mode = ReplaceMode self.edit_mode.edit_subset = self.data.new_subset() self.edit_mode.update(self.data, self.state2) assert self.edit_mode.edit_subset.subset_state is not self.state2 glueviz-1.0.1+dfsg.orig/glue/core/tests/test_data_combo_helper.py0000644000175000017500000002456613657331513024516 0ustar noahfxnoahfximport pytest from unittest.mock import MagicMock from glue.core import Data, DataCollection from glue.core.component_id import ComponentID from echo.selection import SelectionCallbackProperty from glue.core.state_objects import State from glue.core.coordinates import IdentityCoordinates from ..data_combo_helper import (ComponentIDComboHelper, ManualDataComboHelper, DataCollectionComboHelper) def selection_choices(state, property): items = getattr(type(state), property).get_choice_labels(state) return ":".join(items).replace('Coordinate components', 'coord').replace('Main components', 'main').replace('Derived components', 'derived') class ExampleState(State): combo = SelectionCallbackProperty() def test_component_id_combo_helper(): state = ExampleState() dc = DataCollection([]) helper = ComponentIDComboHelper(state, 'combo', dc) assert selection_choices(state, 'combo') == "" data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') dc.append(data1) helper.append_data(data1) assert selection_choices(state, 'combo') == "x:y" data2 = Data(a=[1, 2, 3], b=['a', 'b', 'c'], label='data2', coords=IdentityCoordinates(n_dim=1)) dc.append(data2) helper.append_data(data2) assert selection_choices(state, 'combo') == "data1:x:y:data2:a:b" helper.categorical = False assert selection_choices(state, 'combo') == "data1:x:y:data2:a" helper.numeric = False assert selection_choices(state, 'combo') == "data1:data2" helper.categorical = True helper.numeric = True helper.pixel_coord = True assert selection_choices(state, 'combo') == "data1:main:x:y:coord:Pixel Axis 0 [x]:data2:main:a:b:coord:Pixel Axis 0 [x]" helper.world_coord = True assert selection_choices(state, 'combo') == "data1:main:x:y:coord:Pixel Axis 0 [x]:data2:main:a:b:coord:Pixel Axis 0 [x]:World 0" helper.pixel_coord = False assert selection_choices(state, 'combo') == "data1:main:x:y:data2:main:a:b:coord:World 0" helper.world_coord = False dc.remove(data2) assert selection_choices(state, 'combo') == "x:y" data1['z'] = data1.id['x'] + 1 assert selection_choices(state, 'combo') == "main:x:y:derived:z" helper.derived = False assert selection_choices(state, 'combo') == "x:y" data1.id['x'].label = 'z' assert selection_choices(state, 'combo') == "z:y" helper.remove_data(data1) assert selection_choices(state, 'combo') == "" def test_component_id_combo_helper_nocollection(): # Make sure that we can use use ComponentIDComboHelper without any # data collection. state = ExampleState() data = Data(x=[1, 2, 3], y=[2, 3, 4], z=['a', 'b', 'c'], label='data1') helper = ComponentIDComboHelper(state, 'combo', data=data) assert selection_choices(state, 'combo') == "x:y:z" helper.categorical = False assert selection_choices(state, 'combo') == "x:y" helper.numeric = False assert selection_choices(state, 'combo') == "" helper.categorical = True assert selection_choices(state, 'combo') == "z" helper.numeric = True assert selection_choices(state, 'combo') == "x:y:z" data2 = Data(a=[1, 2, 3], b=['a', 'b', 'c'], label='data2') with pytest.raises(Exception) as exc: helper.append_data(data2) assert exc.value.args[0] == ("Cannot change data in ComponentIDComboHelper " "initialized from a single dataset") with pytest.raises(Exception) as exc: helper.remove_data(data2) assert exc.value.args[0] == ("Cannot change data in ComponentIDComboHelper " "initialized from a single dataset") with pytest.raises(Exception) as exc: helper.set_multiple_data([data2]) assert exc.value.args[0] == ("Cannot change data in ComponentIDComboHelper " "initialized from a single dataset") def test_component_id_combo_helper_init(): # Regression test to make sure that the numeric and categorical options # in the __init__ are taken into account properly state = ExampleState() dc = DataCollection([]) data = Data(a=[1, 2, 3], b=['a', 'b', 'c'], label='data2') dc.append(data) helper = ComponentIDComboHelper(state, 'combo', dc) helper.append_data(data) assert selection_choices(state, 'combo') == "a:b" helper = ComponentIDComboHelper(state, 'combo', dc, numeric=False) helper.append_data(data) assert selection_choices(state, 'combo') == "b" helper = ComponentIDComboHelper(state, 'combo', dc, categorical=False) helper.append_data(data) assert selection_choices(state, 'combo') == "a" helper = ComponentIDComboHelper(state, 'combo', dc, numeric=False, categorical=False) helper.append_data(data) assert selection_choices(state, 'combo') == "" def test_component_id_combo_helper_replaced(): # Make sure that when components are replaced, the equivalent combo index # remains selected and an event is broadcast so that any attached callback # properties can be sure to pull the latest text/userData. callback = MagicMock() state = ExampleState() state.add_callback('combo', callback) dc = DataCollection([]) helper = ComponentIDComboHelper(state, 'combo', dc) assert selection_choices(state, 'combo') == "" data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') callback.reset_mock() dc.append(data1) helper.append_data(data1) callback.assert_called_once_with(0) callback.reset_mock() assert selection_choices(state, 'combo') == "x:y" new_id = ComponentID(label='new') data1.update_id(data1.id['x'], new_id) callback.assert_called_once_with(0) callback.reset_mock() assert selection_choices(state, 'combo') == "new:y" def test_component_id_combo_helper_add(): # Make sure that when adding a component, and if a data collection is not # present, the choices still get updated callback = MagicMock() state = ExampleState() state.add_callback('combo', callback) dc = DataCollection([]) helper = ComponentIDComboHelper(state, 'combo') assert selection_choices(state, 'combo') == "" data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') callback.reset_mock() dc.append(data1) helper.append_data(data1) callback.assert_called_once_with(0) callback.reset_mock() assert selection_choices(state, 'combo') == "x:y" data1.add_component([7, 8, 9], 'z') # Should get notification since choices have changed callback.assert_called_once_with(0) callback.reset_mock() assert selection_choices(state, 'combo') == "x:y:z" @pytest.mark.parametrize('initialize_data_collection', [False, True]) def test_manual_data_combo_helper(initialize_data_collection): # The case with initialize_data_collection=False is a regression test for a # bug which meant that when a ManualDataComboHelper was initialized without # a data collection, it did not change when a data object added later has a # label changed. callback = MagicMock() state = ExampleState() state.add_callback('combo', callback) dc = DataCollection([]) if initialize_data_collection: helper = ManualDataComboHelper(state, 'combo', dc) else: helper = ManualDataComboHelper(state, 'combo') data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') dc.append(data1) assert callback.call_count == 0 assert selection_choices(state, 'combo') == "" helper.append_data(data1) assert callback.call_count == 1 assert selection_choices(state, 'combo') == "data1" data1.label = 'mydata1' assert selection_choices(state, 'combo') == "mydata1" assert callback.call_count == 2 if initialize_data_collection: dc.remove(data1) assert selection_choices(state, 'combo') == "" assert callback.call_count == 3 def test_data_collection_combo_helper(): callback = MagicMock() state = ExampleState() state.add_callback('combo', callback) dc = DataCollection([]) helper = DataCollectionComboHelper(state, 'combo', dc) # noqa data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') assert callback.call_count == 0 dc.append(data1) assert callback.call_count == 1 assert selection_choices(state, 'combo') == "data1" data1.label = 'mydata1' assert selection_choices(state, 'combo') == "mydata1" assert callback.call_count == 2 dc.remove(data1) assert callback.call_count == 3 assert selection_choices(state, 'combo') == "" def test_component_id_combo_helper_rename(): # Make sure that renaming component IDs now propagates to the combo options state = ExampleState() data = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') dc = DataCollection([data]) helper = ComponentIDComboHelper(state, 'combo', dc) # noqa helper.append_data(data) assert selection_choices(state, 'combo') == "x:y" data.id['x'].label = 'renamed' assert selection_choices(state, 'combo') == "renamed:y" def test_component_id_combo_helper_reorder(): # Make sure that renaming component IDs now propagates to the combo options state = ExampleState() data = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') dc = DataCollection([data]) helper = ComponentIDComboHelper(state, 'combo', dc) # noqa helper.append_data(data) assert selection_choices(state, 'combo') == "x:y" data.reorder_components(data.components[::-1]) assert selection_choices(state, 'combo') == "y:x" def test_component_id_combo_helper_none(): # Make sure the none=True option works state = ExampleState() data = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') dc = DataCollection([data]) helper = ComponentIDComboHelper(state, 'combo', dc) helper.append_data(data) assert selection_choices(state, 'combo') == "x:y" helper.none = True assert selection_choices(state, 'combo') == ":x:y" helper.none = 'banana' assert selection_choices(state, 'combo') == "banana:x:y" # Try with initializing none=... from the start helper = ComponentIDComboHelper(state, 'combo', dc, none=True) helper.append_data(data) assert selection_choices(state, 'combo') == ":x:y" helper = ComponentIDComboHelper(state, 'combo', dc, none='banana') helper.append_data(data) assert selection_choices(state, 'combo') == "banana:x:y" glueviz-1.0.1+dfsg.orig/glue/core/tests/util.py0000644000175000017500000000136613605357235021000 0ustar noahfxnoahfxfrom contextlib import contextmanager from unittest.mock import MagicMock from glue import core from glue.core.application_base import Application from glue.tests.helpers import make_file @contextmanager def simple_catalog(): """Context manager to create a temporary data file :param suffix: File suffix. string """ with make_file(b'#a, b\n1, 2\n3, 4', '.csv') as result: yield result def simple_session(): collect = core.data_collection.DataCollection() hub = core.hub.Hub() result = core.Session(data_collection=collect, hub=hub, application=MagicMock(Application), command_stack=core.CommandStack()) result.command_stack.session = result return result glueviz-1.0.1+dfsg.orig/glue/core/tests/test_coordinates.py0000644000175000017500000003664613641414126023376 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import pytest import numpy as np from numpy.testing import assert_allclose, assert_equal from glue.core.tests.test_state import clone from glue.tests.helpers import requires_astropy from ..coordinate_helpers import (axis_label, world_axis, pixel2world_single_axis, dependent_axes) from ..coordinates import (coordinates_from_header, IdentityCoordinates, WCSCoordinates, AffineCoordinates, header_from_string) @requires_astropy class TestWcsCoordinates(object): def default_header(self): from astropy.io import fits hdr = fits.Header() hdr['NAXIS'] = 2 hdr['CRVAL1'] = 0 hdr['CRVAL2'] = 5 hdr['CRPIX1'] = 250 hdr['CRPIX2'] = 187.5 hdr['CTYPE1'] = 'GLON-TAN' hdr['CTYPE2'] = 'GLAT-TAN' hdr['CD1_1'] = -0.0166666666667 hdr['CD1_2'] = 0. hdr['CD2_1'] = 0. hdr['CD2_2'] = 0.01666666666667 return hdr def test_pixel2world_scalar(self): hdr = self.default_header() coord = WCSCoordinates(hdr) x, y = 250., 187.5 result = coord.pixel_to_world_values(x, y) expected = 359.9832692105993601, 5.0166664867400375 assert_allclose(result[0], expected[0]) assert_allclose(result[1], expected[1]) def test_pixel2world_different_input_types(self): hdr = self.default_header() coord = WCSCoordinates(hdr) x, y = 250, 187.5 result = coord.pixel_to_world_values(x, y) expected = 359.9832692105993601, 5.0166664867400375 assert_allclose(result[0], expected[0]) assert_allclose(result[1], expected[1]) def test_pixel2world_list(self): hdr = self.default_header() coord = WCSCoordinates(hdr) x, y = [250, 250], [187.5, 187.5] result = coord.pixel_to_world_values(x, y) expected = ([359.9832692105993601, 359.9832692105993601], [5.0166664867400375, 5.0166664867400375]) for i in range(0, 1): for r, e in zip(result[i], expected[i]): assert_allclose(r, e) def test_pixel2world_numpy(self): hdr = self.default_header() coord = WCSCoordinates(hdr) x, y = np.array([250, 250]), np.array([187.5, 187.5]) result = coord.pixel_to_world_values(x, y) expected = (np.array([359.9832692105993601, 359.9832692105993601]), np.array([5.0166664867400375, 5.0166664867400375])) np.testing.assert_array_almost_equal(result[0], expected[0]) np.testing.assert_array_almost_equal(result[1], expected[1]) def test_world2pixel_numpy(self): hdr = self.default_header() coord = WCSCoordinates(hdr) x, y = np.array([0, 0]), np.array([0, 0]) expected = (np.array([249.0000000000000284, 249.0000000000000284]), np.array([-114.2632689899972434, -114.2632689899972434])) result = coord.world_to_pixel_values(x, y) np.testing.assert_array_almost_equal(result[0], expected[0], 3) np.testing.assert_array_almost_equal(result[1], expected[1], 3) def test_world2pixel_list(self): hdr = self.default_header() coord = WCSCoordinates(hdr) x, y = [0, 0], [0, 0] expected = ([249.0000000000000284, 249.0000000000000284], [-114.2632689899972434, -114.2632689899972434]) result = coord.world_to_pixel_values(x, y) for i in range(0, 1): for r, e in zip(result[i], expected[i]): assert_allclose(r, e) def test_world2pixel_scalar(self): hdr = self.default_header() coord = WCSCoordinates(hdr) expected = 249.0000000000000284, -114.2632689899972434 x, y = 0, 0 result = coord.world_to_pixel_values(x, y) assert_allclose(result[0], expected[0], 3) assert_allclose(result[1], expected[1], 3) def test_world2pixel_mismatched_input(self): coord = WCSCoordinates(self.default_header()) x, y = 0., [0.] expected = coord.world_to_pixel_values(x, y[0]) result = coord.world_to_pixel_values(x, y) assert_allclose(result[0], expected[0]) assert_allclose(result[1], expected[1]) def test_pixel2world_mismatched_input(self): coord = WCSCoordinates(self.default_header()) x, y = [250.], 187.5 expected = coord.pixel_to_world_values(x[0], y) result = coord.pixel_to_world_values(x, y) assert_allclose(result[0], expected[0]) assert_allclose(result[1], expected[1]) def test_axis_label(self): hdr = self.default_header() coord = WCSCoordinates(hdr) assert axis_label(coord, 0) == 'Galactic Latitude' assert axis_label(coord, 1) == 'Galactic Longitude' @requires_astropy def test_world_axis_wcs(): from astropy.io import fits hdr = fits.Header() hdr['NAXIS'] = 2 hdr['CRVAL1'] = 0 hdr['CRVAL2'] = 5 hdr['CRPIX1'] = 2 hdr['CRPIX2'] = 1 hdr['CTYPE1'] = 'XOFFSET' hdr['CTYPE2'] = 'YOFFSET' hdr['CD1_1'] = -2. hdr['CD1_2'] = 0. hdr['CD2_1'] = 0. hdr['CD2_2'] = 2. data = np.ones((3, 4)) coord = WCSCoordinates(hdr) # pixel_axis and world_axis are in WCS order assert_allclose(world_axis(coord, data, pixel_axis=0, world_axis=0), [2, 0, -2, -4]) assert_allclose(world_axis(coord, data, pixel_axis=1, world_axis=1), [5, 7, 9]) class TestCoordinatesFromHeader(object): def test_2d_nowcs(self): hdr = {"NAXIS": 2} coord = coordinates_from_header(hdr) assert type(coord) == IdentityCoordinates assert coord.pixel_n_dim == 2 assert coord.world_n_dim == 2 def test_2d(self): hdr = header_from_string(HDR_2D_VALID) coord = coordinates_from_header(hdr) assert type(coord) == WCSCoordinates def test_3d_nowcs(self): hdr = HDR_3D_VALID_NOWCS coord = coordinates_from_header(header_from_string(hdr)) assert type(coord) == IdentityCoordinates assert coord.pixel_n_dim == 3 assert coord.world_n_dim == 3 def test_3d(self): hdr = header_from_string(HDR_3D_VALID_WCS) coord = coordinates_from_header(hdr) assert type(coord) == WCSCoordinates def test_nod(self): hdr = 0 coord = coordinates_from_header(hdr) assert type(coord) == IdentityCoordinates HDR_2D_VALID = """ SIMPLE = T / Written by IDL: Wed Jul 27 10:01:47 2011 BITPIX = -32 / number of bits per data pixel NAXIS = 2 / number of data axes NAXIS1 = 501 / length of data axis 1 NAXIS2 = 376 / length of data axis 2 EXTEND = T / FITS dataset may contain extensions RADESYS = 'FK5 ' / Frame of reference CRVAL1 = 0. / World coordinate 1 at reference point CRVAL2 = 5. / World coordinate 2 at reference point CRPIX1 = 250.000 / Pixel coordinate 1 at reference point CRPIX2 = 187.500 / Pixel coordinate 2 at reference point CTYPE1 = 'GLON-TAN' / Projection type CTYPE2 = 'GLAT-TAN' / Projection type CUNIT1 = 'deg ' / Unit used for axis 1 CUNIT2 = 'deg ' / Unit used for axis 2 CD1_1 = -0.016666667 / Pixel trasformation matrix CD1_2 = 0. CD2_1 = 0. CD2_2 = 0.016666667 """ HDR_3D_VALID_NOWCS = """SIMPLE = T / Written by IDL: Fri Mar 18 11:58:30 2011 BITPIX = -32 / Number of bits per data pixel NAXIS = 3 / Number of data axes NAXIS1 = 128 / NAXIS2 = 128 / NAXIS3 = 128 / """ HDR_3D_VALID_WCS = """SIMPLE = T / Written by IDL: Thu Jul 7 15:37:21 2011 BITPIX = -32 / Number of bits per data pixel NAXIS = 3 / Number of data axes NAXIS1 = 82 / NAXIS2 = 82 / NAXIS3 = 248 / DATE = '2011-07-07' / Creation UTC (CCCC-MM-DD) date of FITS header COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy COMMENT and Astrophysics', volume 376, page 359; bibcode 2001A&A...376..359H CTYPE1 = 'RA---CAR' / CTYPE2 = 'DEC--CAR' / CTYPE3 = 'VELO-LSR' / CRVAL1 = 55.3500 / CRPIX1 = 41.5000 / CDELT1 = -0.00638888900000 / CRVAL2 = 31.8944 / CRPIX2 = 41.5000 / CDELT2 = 0.00638888900000 / CRVAL3 = -9960.07902777 / CRPIX3 = -102.000 / CDELT3 = 66.4236100000 / """ @requires_astropy def test_coords_preserve_shape_2d(): coord = coordinates_from_header(header_from_string(HDR_2D_VALID)) x = np.zeros(12) y = np.zeros(12) result = coord.pixel_to_world_values(x, y) for r in result: assert r.shape == x.shape result = coord.world_to_pixel_values(x, y) for r in result: assert r.shape == x.shape x.shape = (4, 3) y.shape = (4, 3) result = coord.pixel_to_world_values(x, y) for r in result: assert r.shape == x.shape result = coord.world_to_pixel_values(x, y) for r in result: assert r.shape == x.shape x.shape = (2, 2, 3) y.shape = (2, 2, 3) result = coord.pixel_to_world_values(x, y) for r in result: assert r.shape == x.shape result = coord.world_to_pixel_values(x, y) for r in result: assert r.shape == x.shape @requires_astropy def test_coords_preserve_shape_3d(): coord = coordinates_from_header(header_from_string(HDR_3D_VALID_NOWCS)) x = np.zeros(12) y = np.zeros(12) z = np.zeros(12) result = coord.pixel_to_world_values(x, y, z) for r in result: assert r.shape == x.shape result = coord.world_to_pixel_values(x, y, z) for r in result: assert r.shape == x.shape x.shape = (4, 3) y.shape = (4, 3) z.shape = (4, 3) result = coord.pixel_to_world_values(x, y, z) for r in result: assert r.shape == x.shape result = coord.world_to_pixel_values(x, y, z) for r in result: assert r.shape == x.shape x.shape = (2, 2, 3) y.shape = (2, 2, 3) z.shape = (2, 2, 3) result = coord.pixel_to_world_values(x, y, z) for r in result: assert r.shape == x.shape result = coord.world_to_pixel_values(x, y, z) for r in result: assert r.shape == x.shape def test_world_axis_units(): coord = coordinates_from_header(header_from_string(HDR_3D_VALID_WCS)) assert coord.world_axis_units[0] == 'deg' assert coord.world_axis_units[1] == 'deg' assert coord.world_axis_units[2] == 'm s-1' def test_dependent_axes_non_diagonal_pc(): # Fix a bug that occurred when non-diagonal PC elements # were present in the WCS - in that case all other axes # were returned as dependent axes even if this wasn't # the case. coord = WCSCoordinates(naxis=3) coord.wcs.ctype = 'HPLN-TAN', 'HPLT-TAN', 'Time' coord.wcs.crval = 1, 1, 1 coord.wcs.crpix = 1, 1, 1 coord.wcs.cd = [[0.9, 0.1, 0], [-0.1, 0.9, 0], [0, 0, 1]] # Remember that the axes numbers below are reversed compared # to the WCS order above. assert_equal(dependent_axes(coord, 0), [0]) assert_equal(dependent_axes(coord, 1), [1, 2]) assert_equal(dependent_axes(coord, 2), [1, 2]) def test_pixel2world_single_axis(): # Regression test for a bug in pixel2world_single_axis which was due to # incorrect indexing order (WCS vs Numpy) coord = WCSCoordinates(naxis=3) coord.wcs.ctype = 'HPLN-TAN', 'HPLT-TAN', 'Time' coord.wcs.crval = 1, 1, 1 coord.wcs.crpix = 1, 1, 1 coord.wcs.cd = [[0.9, 0.1, 0], [-0.1, 0.9, 0], [0, 0, 1]] x = np.array([0.2, 0.4, 0.6]) y = np.array([0.3, 0.6, 0.9]) z = np.array([0.5, 0.5, 0.5]) assert_allclose(pixel2world_single_axis(coord, x, y, z, world_axis=0), [1.21004705, 1.42012044, 1.63021455]) assert_allclose(pixel2world_single_axis(coord, x, y, z, world_axis=1), [1.24999002, 1.499947, 1.74985138]) assert_allclose(pixel2world_single_axis(coord, x, y, z, world_axis=2), [1.5, 1.5, 1.5]) def test_affine(): matrix = np.array([[2, 3, -1], [1, 2, 2], [0, 0, 1]]) coords = AffineCoordinates(matrix) assert axis_label(coords, 1) == 'World 1' assert axis_label(coords, 0) == 'World 0' assert coords.world_axis_units[1] == '' assert coords.world_axis_units[0] == '' # First the scalar case xp, yp = 1, 2 xw, yw = coords.pixel_to_world_values(xp, yp) assert_allclose(xw, 2 * 1 + 3 * 2 - 1) assert_allclose(yw, 1 * 1 + 2 * 2 + 2) assert np.ndim(xw) == 0 assert np.ndim(yw) == 0 xpc, ypc = coords.world_to_pixel_values(xw, yw) assert_allclose(xpc, 1) assert_allclose(ypc, 2) assert np.ndim(xpc) == 0 assert np.ndim(ypc) == 0 # Next the array case xp = np.array([1, 2, 3]) yp = np.array([2, 3, 4]) xw, yw = coords.pixel_to_world_values(xp, yp) assert_allclose(xw, 2 * xp + 3 * yp - 1) assert_allclose(yw, 1 * xp + 2 * yp + 2) xpc, ypc = coords.world_to_pixel_values(xw, yw) assert_allclose(xpc, [1, 2, 3]) assert_allclose(ypc, [2, 3, 4]) # Check that serialization/deserialization works coords2 = clone(coords) xw, yw = coords2.pixel_to_world_values(xp, yp) assert_allclose(xw, 2 * xp + 3 * yp - 1) assert_allclose(yw, 1 * xp + 2 * yp + 2) xpc, ypc = coords.world_to_pixel_values(xw, yw) assert_allclose(xpc, [1, 2, 3]) assert_allclose(ypc, [2, 3, 4]) def test_affine_labels_units(): matrix = np.array([[2, 3, -1], [1, 2, 2], [0, 0, 1]]) coords = AffineCoordinates(matrix, units=['km', 'km'], labels=['xw', 'yw']) assert axis_label(coords, 1) == 'Xw' assert axis_label(coords, 0) == 'Yw' assert coords.world_axis_units[1] == 'km' assert coords.world_axis_units[0] == 'km' coords2 = clone(coords) assert axis_label(coords2, 1) == 'Xw' assert axis_label(coords2, 0) == 'Yw' assert coords2.world_axis_units[1] == 'km' assert coords2.world_axis_units[0] == 'km' def test_affine_invalid(): matrix = np.array([[2, 3, -1], [1, 2, 2], [0, 0, 1]]) with pytest.raises(ValueError) as exc: AffineCoordinates(matrix[0]) assert exc.value.args[0] == 'Affine matrix should be two-dimensional' with pytest.raises(ValueError) as exc: AffineCoordinates(matrix[:-1]) assert exc.value.args[0] == 'Affine matrix should be square' with pytest.raises(ValueError) as exc: AffineCoordinates(matrix, labels=['a', 'b', 'c']) assert exc.value.args[0] == 'Expected 2 labels, got 3' with pytest.raises(ValueError) as exc: AffineCoordinates(matrix, units=['km', 'km', 'km']) assert exc.value.args[0] == 'Expected 2 units, got 3' matrix[-1] = 1 with pytest.raises(ValueError) as exc: AffineCoordinates(matrix) assert exc.value.args[0] == 'Last row of matrix should be zeros and a one' def test_affine_highd(): # Test AffineCoordinates when higher dimensional objects are transformed matrix = np.array([[2, 3, -1], [1, 2, 2], [0, 0, 1]]) coords = AffineCoordinates(matrix) xp = np.ones((2, 4, 1, 2, 5)) yp = np.ones((2, 4, 1, 2, 5)) xw, yw = coords.pixel_to_world_values(xp, yp) xpc, ypc = coords.world_to_pixel_values(xw, yw) assert_allclose(xp, xpc) assert_allclose(yp, ypc) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_link_helpers.py0000644000175000017500000000627213605357235023542 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import pytest import numpy as np from glue.core import ComponentID, Data, DataCollection from .. import link_helpers as lh from ..link_helpers import (LinkTwoWay, MultiLink, LinkSame, LinkAligned) R, D, L, B = (ComponentID('ra'), ComponentID('dec'), ComponentID('lon'), ComponentID('lat')) def forwards(x): print('forwards input', x) return x * 3 def backwards(x): print('backwards input', x) return x / 3 def forwards_xy(x, y): print('forwards inputs', x, y) return x * 3, y * 5 def backwards_xy(x, y): print('backwards inputs', x, y) return x / 3, y / 5 def check_link(link, from_, to, using=None): assert link.get_from_ids() == from_ assert link.get_to_id() == to if using: assert link.get_using() == using def check_using(link, inp, out): np.testing.assert_array_almost_equal(link.get_using()(*inp), out) def test_LinkTwoWay(): result = list(LinkTwoWay(R, D, forwards, backwards)) check_link(result[0], [R], D, forwards) check_link(result[1], [D], R, backwards) def test_multilink_forwards(): result = MultiLink([R, D], [L, B], forwards_xy, labels2=['x', 'y']) assert len(result) == 2 check_link(result[0], [R, D], L) check_link(result[1], [R, D], B) check_using(result[0], (3, 4), 9) check_using(result[1], (3, 4), 20) def test_multilink_backwards(): result = MultiLink([R, D], [L, B], backwards=backwards_xy, labels1=['x', 'y']) assert len(result) == 2 check_link(result[0], [L, B], R) check_link(result[1], [L, B], D) check_using(result[0], (9, 20), 3) check_using(result[1], (9, 20), 4) def test_multilink_forwards_backwards(): result = MultiLink([R, D], [L, B], forwards_xy, backwards_xy) assert len(result) == 4 check_link(result[0], [R, D], L) check_link(result[1], [R, D], B) check_link(result[2], [L, B], R) check_link(result[3], [L, B], D) check_using(result[0], (3, 4), 9) check_using(result[1], (3, 4), 20) check_using(result[2], (9, 20), 3) check_using(result[3], (9, 20), 4) def test_multilink_nofunc(): with pytest.raises(TypeError) as exc: MultiLink([R, D], [L, B]) assert exc.value.args[0] == "Must supply either forwards or backwards" def test_linksame_string(): """String inputs auto-converted to component IDs""" # ComponentLink does type checking to ensure conversion happens links = LinkSame('a', 'b') def test_identity(): assert lh.identity('3') == '3' def test_toid(): assert lh._toid('test').label == 'test' cid = ComponentID('test2') assert lh._toid(cid) is cid with pytest.raises(TypeError) as exc: lh._toid(None) @pytest.mark.parametrize('ndim', [0, 1, 2]) def test_link_aligned(ndim): shp = tuple([2] * ndim) data1 = Data(test=np.random.random(shp)) data2 = Data(test=np.random.random(shp)) links = LinkAligned(data1, data2) dc = DataCollection([data1, data2]) dc.add_link(links) for i in range(ndim): id0 = data1.pixel_component_ids[i] id1 = data2.pixel_component_ids[i] np.testing.assert_array_equal(data1[id0], data2[id1]) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_registry.py0000644000175000017500000000321613605357235022726 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 from ..registry import Registry def setup_function(function): Registry().clear() def test_singleton(): assert Registry() is Registry() def test_unique(): r = Registry() assert r.register(3, "test") == "test" assert r.register(4, "test2") == "test2" def test_disambiguate(): r = Registry() assert r.register(3, "test") == "test" assert r.register(4, "test") == "test_01" def test_rename(): r = Registry() assert r.register(3, "test") == "test" assert r.register(4, "test2") == "test2" assert r.register(3, "test") == "test" def test_rename_then_new(): r = Registry() assert r.register(3, "test") == "test" assert r.register(3, "test2") == "test2" assert r.register(4, "test") == "test" def test_cross_class(): r = Registry() assert r.register(3, "test") == "test" assert r.register(3.5, "test") == "test" assert r.register(4.5, "test") == "test_01" def test_group_override(): r = Registry() assert r.register(3, "test") == "test" assert r.register(3.5, "test", group=int) == "test_01" assert r.register(4, "test", group=float) == "test" def test_unregister(): r = Registry() assert r.register(3, "test") == "test" r.unregister(3) assert r.register(4, "test") == "test" def test_relabel_to_self(): r = Registry() assert r.register(3, "test") == "test" assert r.register(3, "test") == "test" def test_lowest_disambiguation(): r = Registry() assert r.register(3, "test") == "test" assert r.register(4, "test") == "test_01" assert r.register(4, "test") == "test_01" glueviz-1.0.1+dfsg.orig/glue/core/tests/test_link_manager.py0000644000175000017500000004327713617324354023517 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 from itertools import product import numpy as np from numpy.testing import assert_array_equal from ..component_link import ComponentLink from ..data import ComponentID, DerivedComponent, Data, Component from ..coordinates import IdentityCoordinates from ..data_collection import DataCollection from ..link_manager import (LinkManager, accessible_links, discover_links, find_dependents, is_convertible_to_single_pixel_cid, equivalent_pixel_cids, pixel_cid_to_pixel_cid_matrix) from ..link_helpers import LinkSame, MultiLink comp = Component(data=np.array([1, 2, 3])) def example_components(self, add_derived=True): """ Link Topology --- c1---c3--\ data --| --c5,c6 (c7,c8 disconnected) --- c2---c4--/ """ self.data = Data() c1 = ComponentID('c1') c2 = ComponentID('c2') c3 = ComponentID('c3') c4 = ComponentID('c4') c5 = ComponentID('c5') c6 = ComponentID('c6') c7 = ComponentID('c7') c8 = ComponentID('c8') dummy_using = lambda x, y: (x, y) self.cs = [c1, c2, c3, c4, c5, c6, c7, c8] self.links = [ComponentLink([c1], c3, lambda x:x), ComponentLink([c2], c4, lambda x:x), ComponentLink([c3], c1, lambda x:x), ComponentLink([c4], c2, lambda x:x), ComponentLink([c3, c4], c5, dummy_using), ComponentLink([c3, c4], c6, dummy_using)] self.data.add_component(comp, c1) self.data.add_component(comp, c2) if add_derived: for i in [0, 1, 4, 5]: dc = DerivedComponent(self.data, self.links[i]) self.data.add_component(dc, dc.link.get_to_id()) self.primary = [c1, c2] self.direct = [c3, c4] self.derived = [] self.externally_derived = [c5, c6] self.inaccessible = [c7, c8] class TestAccessibleLinks(object): def setup_method(self, method): self.cs = [ComponentID("%i" % i) for i in range(10)] def test_returned_if_available(self): cids = self.cs[0:5] links = [ComponentLink([self.cs[0]], self.cs[1])] assert links[0] in accessible_links(cids, links) def test_returned_if_reachable(self): cids = self.cs[0:5] links = [ComponentLink([self.cs[0]], self.cs[6])] assert links[0] in accessible_links(cids, links) def test_not_returned_if_not_reachable(self): cids = self.cs[0:5] links = [ComponentLink([self.cs[6]], self.cs[7])] assert not links[0] in accessible_links(cids, links) class TestDiscoverLinks(object): def setup_method(self, method): example_components(self) def test_correct_discover(self): """discover_links finds the correct links""" links = discover_links(self.data, self.links) for i in self.inaccessible: assert i not in links for d in self.direct: assert d in links for d in self.derived: assert d in links for p in self.primary: assert p not in links def test_links_point_to_proper_ids(self): """ Dictionary values are ComponentLinks which point to the keys """ links = discover_links(self.data, self.links) for cid in links: assert cid == links[cid].get_to_id() def test_shortest_path(self): """ Shortcircuit c5 to c1, yielding 2 ways to get to c5. Ensure that the shortest path is chosen """ self.links.append(ComponentLink([self.cs[0]], self.cs[4])) links = discover_links(self.data, self.links) assert links[self.cs[4]] is self.links[-1] class TestFindDependents(object): def setup_method(self, method): example_components(self) def test_propagated(self): to_remove = self.links[0] result = find_dependents(self.data, to_remove) expected = set([self.cs[2], self.cs[4], self.cs[5]]) assert expected == result def test_basic(self): to_remove = self.links[4] result = find_dependents(self.data, to_remove) expected = set([self.cs[4]]) assert expected == result class TestLinkManager(object): def test_add_links(self): id1 = ComponentID('id1') id2 = ComponentID('id2') id3 = ComponentID('id3') lm = LinkManager() using = lambda x, y: 0 link = ComponentLink([id1, id2], id3, using) lm.add_link(link) links = lm.links assert links == [link] def test_remove_link(self): id1 = ComponentID('id1') id2 = ComponentID('id2') id3 = ComponentID('id3') lm = LinkManager() using = lambda x, y: 0 link = ComponentLink([id1, id2], id3, using) lm.add_link(link) lm.remove_link(link) links = lm.links assert links == [] def test_setup(self): example_components(self, add_derived=False) expected = set() assert set(self.data.derived_components) == expected def test_update_externally_derivable_components_adds_correctly(self): example_components(self, add_derived=False) lm = LinkManager() list(map(lm.add_link, self.links)) lm.update_externally_derivable_components(self.data) derived = set(self.data.externally_derivable_components) expected = set(self.externally_derived + self.direct) assert derived == expected def test_update_externally_derivable_components_removes_correctly(self): # add all but last link to manager example_components(self, add_derived=False) lm = LinkManager() list(map(lm.add_link, self.links[:-1])) lm.update_externally_derivable_components(self.data) # manually add last link as derived component dc = DerivedComponent(self.data, self.links[-1]) self.data._externally_derivable_components.update({dc.link.get_to_id(): dc}) removed = set([dc.link.get_to_id()]) assert dc.link.get_to_id() in self.data.externally_derivable_components # this link should be removed upon update_components lm.update_externally_derivable_components(self.data) derived = set(self.data.externally_derivable_components) expected = set(self.direct + self.externally_derived) - removed assert derived == expected def test_derived_links_correctwith_mergers(self): """When the link manager merges components, links that depend on the merged components remain functional""" d1 = Data(x=[[1, 2], [3, 4]], coords=IdentityCoordinates(n_dim=2)) d2 = Data(u=[[5, 6], [7, 8]], coords=IdentityCoordinates(n_dim=2)) dc = DataCollection([d1, d2]) # link world coordinates... dc.add_link(LinkSame( d1.world_component_ids[0], d2.world_component_ids[0])) dc.add_link(LinkSame( d1.world_component_ids[1], d2.world_component_ids[1])) # and then retrieve pixel coordinates assert_array_equal(d2[d1.pixel_component_ids[0]], [[0, 0], [1, 1]]) assert_array_equal(d1[d2.pixel_component_ids[1]], [[0, 1], [0, 1]]) def test_binary_links_correct_with_mergers(self): """Regression test. BinaryComponentLinks should work after mergers""" d1 = Data(x=[1, 2, 3], y=[2, 3, 4]) d2 = Data(u=[2, 3, 4], v=[3, 4, 5]) z = d1.id['x'] + d1.id['y'] d1.add_component_link(z, 'z') dc = DataCollection([d1, d2]) dc.add_link(LinkSame(d2.id['u'], d1.id['x'])) assert_array_equal(d1['z'], [3, 5, 7]) def test_complex_links_correct_with_mergers(self): """Regression test. multi-level links should work after mergers""" d1 = Data(x=[1, 2, 3], y=[2, 3, 4]) d2 = Data(u=[2, 3, 4], v=[3, 4, 5]) x = d1.id['x'] z = d1.id['x'] + d1.id['y'] + 5 d1.add_component_link(z, 'z') dc = DataCollection([d1, d2]) dc.add_link(LinkSame(d2.id['u'], d1.id['x'])) # NOTE: the behavior tested here is not desirable anymore, so the # following assert is no longer True # assert x not in d1.components assert_array_equal(d1['z'], [8, 10, 12]) def test_remove_data_removes_links(self): d1 = Data(x=[[1, 2], [3, 4]], label='image', coords=IdentityCoordinates(n_dim=2)) d2 = Data(a=[1, 2, 3], b=[4, 5, 6], label='catalog', coords=IdentityCoordinates(n_dim=3)) dc = DataCollection([d1, d2]) assert len(dc.links) == 6 dc.add_link(LinkSame(d1.id['x'], d2.id['a'])) assert len(dc.links) == 7 # Removing dataset should remove related links dc.remove(d1) assert len(dc.links) == 2 def test_remove_component_removes_links(self): d1 = Data(x=[[1, 2], [3, 4]], label='image', coords=IdentityCoordinates(n_dim=2)) d2 = Data(a=[1, 2, 3], b=[4, 5, 6], label='catalog', coords=IdentityCoordinates(n_dim=3)) dc = DataCollection([d1, d2]) assert len(dc.links) == 6 assert len(d1.components) == 5 assert len(d2.components) == 4 assert len(d1.externally_derivable_components) == 0 assert len(d2.externally_derivable_components) == 0 dc.add_link(LinkSame(d1.id['x'], d2.id['a'])) assert len(dc.links) == 7 assert len(d1.components) == 5 assert len(d2.components) == 4 assert len(d1.externally_derivable_components) == 1 assert len(d2.externally_derivable_components) == 1 assert d1.id['x'] in d2.externally_derivable_components assert d2.id['a'] in d1.externally_derivable_components # Removing component a from d2 should remove related links and # derived components. d2.remove_component(d2.id['a']) assert len(dc.links) == 6 assert len(d1.components) == 5 assert len(d2.components) == 3 assert len(d1.externally_derivable_components) == 0 assert len(d2.externally_derivable_components) == 0 def test_is_convertible_to_single_pixel_cid(): # This tests the function is_convertible_to_single_pixel_cid, which gives # for a given dataset the pixel component ID that can be uniquely # transformed into the requested component ID. # Set up a coordinate object which has an independent first axis and # has the second and third axes depend on each other. The transformation # itself is irrelevant since for this function to work we only care about # whether or not an axis is independent. class CustomCoordinates(IdentityCoordinates): @property def axis_correlation_matrix(self): matrix = np.zeros((self.world_n_dim, self.pixel_n_dim), dtype=bool) matrix[2, 2] = True matrix[0:2, 0:2] = True return matrix data1 = Data() data1.coords = CustomCoordinates(n_dim=3) data1['x'] = np.ones((4, 3, 4)) px1, py1, pz1 = data1.pixel_component_ids wx1, wy1, wz1 = data1.world_component_ids data1['a'] = px1 * 2 data1['b'] = wx1 * 2 data1['c'] = wy1 * 2 data1['d'] = wx1 * 2 + px1 data1['e'] = wx1 * 2 + wy1 # Pixel component IDs should just be returned directly for cid in data1.pixel_component_ids: assert is_convertible_to_single_pixel_cid(data1, cid) is cid # Only the first world component should return a valid pixel component # ID since the two other world components are interlinked assert is_convertible_to_single_pixel_cid(data1, wx1) is px1 assert is_convertible_to_single_pixel_cid(data1, wy1) is None assert is_convertible_to_single_pixel_cid(data1, wz1) is None # a and b are ultimately linked to the first pixel coordinate, whereas c # depends on the second world coordinate which is interlinked with the third # Finally, d is ok because it really only depends on px1 assert is_convertible_to_single_pixel_cid(data1, data1.id['a']) is px1 assert is_convertible_to_single_pixel_cid(data1, data1.id['b']) is px1 assert is_convertible_to_single_pixel_cid(data1, data1.id['c']) is None assert is_convertible_to_single_pixel_cid(data1, data1.id['d']) is px1 assert is_convertible_to_single_pixel_cid(data1, data1.id['e']) is None # We now create a second dataset and set up links data2 = Data(y=np.ones((4, 5, 6, 7)), z=np.zeros((4, 5, 6, 7))) dc = DataCollection([data1, data2]) dc.add_link(ComponentLink([data1.id['a'], px1], data2.id['y'], using=lambda x: 2 * x)) dc.add_link(ComponentLink([wy1], data2.id['z'], using=lambda x: 2 * x)) assert is_convertible_to_single_pixel_cid(data1, data2.id['y']) is px1 assert is_convertible_to_single_pixel_cid(data1, data2.id['z']) is None def test_equivalent_pixel_cids(): # This tests the equivalent_pixel_cids function which checks whether all # pixel component IDs in a dataset are equivalent to pixel component IDs # in another dataset. data1 = Data(x=np.ones((2, 4, 3))) data2 = Data(y=np.ones((4, 3, 2))) data3 = Data(z=np.ones((2, 3))) dc = DataCollection([data1, data2, data3]) # Checking data against itself should give the normal axis order assert equivalent_pixel_cids(data1, data1) == [0, 1, 2] assert equivalent_pixel_cids(data3, data3) == [0, 1] # Checking data against unlinked data should give None for d1, d2 in product(dc, dc): if d1 is not d2: assert equivalent_pixel_cids(d1, d2) is None # Add one set of links, which shouldn't change anything dc.add_link(LinkSame(data1.pixel_component_ids[0], data2.pixel_component_ids[2])) dc.add_link(LinkSame(data1.pixel_component_ids[0], data3.pixel_component_ids[0])) for d1, d2 in product(dc, dc): if d1 is not d2: assert equivalent_pixel_cids(d1, d2) is None # Add links between a second set of axes dc.add_link(LinkSame(data1.pixel_component_ids[2], data2.pixel_component_ids[1])) dc.add_link(LinkSame(data1.pixel_component_ids[2], data3.pixel_component_ids[1])) # At this point, data3 has all its axes contained in data1 and data2 assert equivalent_pixel_cids(data1, data2) is None assert equivalent_pixel_cids(data2, data1) is None assert equivalent_pixel_cids(data1, data3) == [0, 2] assert equivalent_pixel_cids(data3, data1) is None assert equivalent_pixel_cids(data2, data3) == [2, 1] assert equivalent_pixel_cids(data3, data2) is None # Finally we can link the third set of axes dc.add_link(LinkSame(data1.pixel_component_ids[1], data2.pixel_component_ids[0])) # At this point, data1 and data2 should now be linked. Note that in cases # where the target has more dimensions than the reference, we always get None assert equivalent_pixel_cids(data1, data2) == [1, 2, 0] assert equivalent_pixel_cids(data2, data1) == [2, 0, 1] assert equivalent_pixel_cids(data1, data3) == [0, 2] assert equivalent_pixel_cids(data3, data1) is None assert equivalent_pixel_cids(data2, data3) == [2, 1] assert equivalent_pixel_cids(data3, data2) is None def test_pixel_cid_to_pixel_cid_matrix(): # This tests the pixel_cid_to_pixel_cid_matrix function which returns # a correlation matrix between the pixel components from two dataset. data1 = Data(x=np.ones((2, 4, 3))) data2 = Data(y=np.ones((4, 3, 2))) data3 = Data(z=np.ones((2, 3))) dc = DataCollection([data1, data2, data3]) # Checking data against itself should give an identity matrix assert_array_equal(pixel_cid_to_pixel_cid_matrix(data1, data1), np.identity(3)) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data2, data2), np.identity(3)) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data3, data3), np.identity(2)) # Checking data against unlinked data should give None for d1, d2 in product(dc, dc): if d1 is not d2: assert_array_equal(pixel_cid_to_pixel_cid_matrix(d1, d2), np.zeros((d1.ndim, d2.ndim))) # Add one set of links dc.add_link(LinkSame(data1.pixel_component_ids[0], data2.pixel_component_ids[2])) dc.add_link(LinkSame(data1.pixel_component_ids[0], data3.pixel_component_ids[0])) expected = np.array([[0, 0, 1], [0, 0, 0], [0, 0, 0]], dtype=bool) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data1, data2), expected) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data2, data1), expected.T) expected = np.array([[1, 0], [0, 0], [0, 0]], dtype=bool) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data1, data3), expected) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data3, data1), expected.T) expected = np.array([[0, 0], [0, 0], [1, 0]], dtype=bool) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data2, data3), expected) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data3, data2), expected.T) # Add links with multiple components, in this case linking two cids with two cids def forwards(x, y): return x + y, x - y def backwards(x, y): return 0.5 * (x + y), 0.5 * (x - y) dc.add_link(MultiLink(data1.pixel_component_ids[1:], data2.pixel_component_ids[:2], forwards=forwards, backwards=backwards)) expected = np.array([[0, 0, 1], [1, 1, 0], [1, 1, 0]], dtype=bool) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data1, data2), expected) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data2, data1), expected.T) expected = np.array([[1, 0], [0, 0], [0, 0]], dtype=bool) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data1, data3), expected) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data3, data1), expected.T) expected = np.array([[0, 0], [0, 0], [1, 0]], dtype=bool) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data2, data3), expected) assert_array_equal(pixel_cid_to_pixel_cid_matrix(data3, data2), expected.T) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_simpleforms.py0000644000175000017500000000246713605357235023425 0ustar noahfxnoahfximport pytest from ..simpleforms import IntOption, FloatOption, BoolOption class Stub(object): int_opt = IntOption(min=0, max=10, default=3) float_opt = FloatOption(min=1, max=2, default=1.5) bool_opt = BoolOption() class TestSimpleForms(object): def test_get_set_int(self): assert Stub.int_opt.min == 0 assert Stub.int_opt.max == 10 assert Stub().int_opt == 3 def test_get_set_bool(self): s = Stub() assert s.bool_opt is False s.bool_opt = True assert s.bool_opt def test_get_set_float(self): s = Stub() assert s.float_opt == 1.5 s.float_opt = 1 assert s.float_opt == 1.0 assert isinstance(s.float_opt, float) def test_invalid_int(self): s = Stub() s.int_opt = 4 with pytest.raises(ValueError): s.int_opt = -1 with pytest.raises(ValueError): s.int_opt = 11 with pytest.raises(ValueError): s.int_opt = 2.5 def test_invalid_float(self): s = Stub() with pytest.raises(ValueError): s.float_opt = -0.1 with pytest.raises(ValueError): s.float_opt = 10.1 def test_invalid(self): s = Stub() with pytest.raises(ValueError): s.bool_opt = 3 glueviz-1.0.1+dfsg.orig/glue/core/tests/__init__.py0000644000175000017500000000000013455362716021545 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/tests/test_layout.py0000644000175000017500000000323313605357235022372 0ustar noahfxnoahfxfrom ..layout import Rectangle, snap_to_grid class TestSnap(object): @staticmethod def check(input, expected, **kwargs): result = snap_to_grid(input, **kwargs) for i, e in zip(input, expected): assert result[i] == e def test_2x2(self): rs = [Rectangle(-.2, -.1, .45, .52), Rectangle(.52, -.23, .49, .49), Rectangle(0, .45, .51, .53), Rectangle(.50, .45, .51, .53)] ex = [Rectangle(0, 0, .5, .5), Rectangle(.5, 0, .5, .5), Rectangle(0, .5, .5, .5), Rectangle(.5, .5, .5, .5)] self.check(rs, ex) def test_1x2(self): rs = [Rectangle(-.2, -.2, .95, .48), Rectangle(0, .45, .51, .53), Rectangle(.50, .45, .51, .53)] ex = [Rectangle(0, 0, 1, .5), Rectangle(0, .5, .5, .5), Rectangle(.5, .5, .5, .5)] self.check(rs, ex) def test_1x3(self): rs = [Rectangle(-.02, -.2, .95, .48), Rectangle(0.1, .51, 0.32, .53), Rectangle(0.32, .49, .30, .53), Rectangle(0.7, .52, .40, .53)] ex = [Rectangle(0, 0, 1, .5), Rectangle(0, .5, 1 / 3., .5), Rectangle(1 / 3., .5, 1 / 3., .5), Rectangle(2 / 3., .5, 1 / 3., .5)] self.check(rs, ex) def test_padding_1x2(self): rs = [Rectangle(0, 0, 1, .5), Rectangle(0, .5, .5, .5), Rectangle(.5, .5, .5, .5)] ex = [Rectangle(.1, .1, .8, .3), Rectangle(.1, .6, .3, .3), Rectangle(.6, .6, .3, .3)] self.check(rs, ex, padding=0.1) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_util.py0000644000175000017500000001017313605357235022033 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import numpy as np from matplotlib.cm import gray from .. import Data, DataCollection from ..util import facet_subsets, colorize_subsets class TestRelim(object): pass class TestFacetSubsets(object): def setup_method(self, method): from .. import Data, DataCollection self.data = Data(label='data', x=[1, 2, 3, 4, 5, 6, 7]) self.collect = DataCollection([self.data]) def test_facet_fully_specified(self): grps = facet_subsets(self.collect, self.data.id['x'], lo=3, hi=6, steps=3) assert len(grps) == 3 np.testing.assert_array_equal(grps[0].subsets[0].to_mask(), [False, False, True, False, False, False, False]) np.testing.assert_array_equal(grps[1].subsets[0].to_mask(), [False, False, False, True, False, False, False]) np.testing.assert_array_equal(grps[2].subsets[0].to_mask(), [False, False, False, False, True, True, False]) def test_default_lo_value(self): grps = facet_subsets(self.collect, self.data.id['x'], hi=7, steps=2) assert len(grps) == 2 np.testing.assert_array_equal(grps[0].subsets[0].to_mask(), [True, True, True, False, False, False, False]) np.testing.assert_array_equal(grps[1].subsets[0].to_mask(), [False, False, False, True, True, True, True]) def test_default_hi_value(self): grps = facet_subsets(self.collect, self.data.id['x'], lo=3, steps=2) assert len(grps) == 2 np.testing.assert_array_equal(grps[0].subsets[0].to_mask(), [False, False, True, True, False, False, False]) np.testing.assert_array_equal(grps[1].subsets[0].to_mask(), [False, False, False, False, True, True, True]) def test_default_steps(self): grps = facet_subsets(self.collect, self.data.id['x']) assert len(grps) == 5 def test_label(self): grps = facet_subsets(self.collect, self.data.id['x']) lbls = ['1.0<=x<2.2', '2.2<=x<3.4', '3.4<=x<4.6', '4.6<=x<5.8', '5.8<=x<=7.0', None] for s, lbl in zip(grps, lbls): assert s.label == lbl grps = facet_subsets(self.collect, self.data.id['x'], prefix='test_') for i, s in enumerate(grps, start=1): assert s.label.startswith('test_') def test_facet_reversed(self): grps = facet_subsets(self.collect, self.data.id['x'], lo=3, hi=1, steps=2) assert len(grps) == 2 # ranges should be (2, 3] and (1, 2] np.testing.assert_array_equal(grps[0].subsets[0].to_mask(), [False, False, True, False, False, False, False]) np.testing.assert_array_equal(grps[1].subsets[0].to_mask(), [True, True, False, False, False, False, False]) def test_colorize_subsets(): data = Data(label='test', x=[1, 2, 3]) dc = DataCollection(data) grps = facet_subsets(dc, data.id['x'], steps=2) colorize_subsets(grps, gray) assert grps[0].style.color == '#000000' assert grps[1].style.color == '#ffffff' def test_colorize_subsets_clip(): data = Data(label='test', x=[1, 2, 3]) grps = facet_subsets(DataCollection(data), data.id['x'], steps=2) colorize_subsets(grps, gray, hi=0.5) assert grps[0].style.color == '#000000' assert grps[1].style.color == '#808080' colorize_subsets(grps, gray, lo=0.5) assert grps[0].style.color == '#808080' assert grps[1].style.color == '#ffffff' glueviz-1.0.1+dfsg.orig/glue/core/tests/test_component_link.py0000644000175000017500000002561213612622074024073 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103,W0612 import pytest import numpy as np from numpy.testing import assert_array_equal from glue.utils import unbroadcast from ..coordinates import IdentityCoordinates from ..component import DerivedComponent from ..component_link import ComponentLink, BinaryComponentLink from ..data import ComponentID, Data, Component from ..subset import InequalitySubsetState class TestComponentLink(object): def toy_data(self): data = Data() from_comp = Component(np.array([1, 2, 3])) to_comp = Component(np.array([4, 5, 6])) return data, from_comp, to_comp def test_valid_init(self): ComponentLink([ComponentID('from')], ComponentID('to')) def test_valid_init_using(self): data, from_, to_ = self.toy_data() using = lambda x: x ComponentLink([ComponentID('from')], ComponentID('to'), using) def test_invalid_init_multi_from_no_using(self): with pytest.raises(TypeError) as exc: ComponentLink([ComponentID('a'), ComponentID('b')], ComponentID('c')) assert exc.value.args[0] == ("comp_from must have only 1 element, " "or a 'using' function must be provided") def test_invalid_init_scalar_from(self): with pytest.raises(TypeError) as exc: ComponentLink(ComponentID('from'), ComponentID('to')) assert exc.value.args[0].startswith("comp_from must be a list") def test_compute_direct(self): data, from_, to_ = self.toy_data() from_id = data.add_component(from_, 'from_label') to_id = ComponentID('to_label') link = ComponentLink([from_id], to_id) result = link.compute(data) expected = from_.data assert_array_equal(result, expected) def test_compute_using(self): data, from_, to_ = self.toy_data() from_id = data.add_component(from_, 'from_label') to_id = ComponentID('to_label') using = lambda x: 3 * x link = ComponentLink([from_id], to_id, using) result = link.compute(data) expected = from_.data * 3 assert_array_equal(result, expected) def test_getters(self): data, from_, to_ = self.toy_data() from_id = data.add_component(from_, 'from_label') to_id = ComponentID('to_label') using = lambda x: 3 * x link = ComponentLink([from_id], to_id, using) assert link.get_from_ids()[0] is from_id assert link.get_to_id() is to_id assert link.get_using() is using def test_str(self): """ str method returns without error """ data, from_, to_ = self.toy_data() from_id = data.add_component(from_, 'from_label') to_id = ComponentID('to_label') link = ComponentLink([from_id], to_id) str(link) link = ComponentLink([from_id], to_id, using=lambda x: 3 * x) str(link) def test_repr(self): """ repr method returns without error """ data, from_, to_ = self.toy_data() from_id = data.add_component(from_, 'from_label') to_id = ComponentID('to_label') link = ComponentLink([from_id], to_id) repr(link) def test_type_check(self): """Should raise an exception if non ComponentIDs are passed as input""" cid = ComponentID('test') with pytest.raises(TypeError) as exc: ComponentLink([None], cid) assert exc.value.args[0].startswith('from argument is not a list ' 'of ComponentIDs') with pytest.raises(TypeError) as exc: ComponentLink([cid], None) assert exc.value.args[0].startswith('to argument is not a ComponentID') with pytest.raises(TypeError) as exc: ComponentLink([cid, None], None, using=lambda x, y: None) assert exc.value.args[0].startswith('from argument is not a list ' 'of ComponentIDs') def test_compute_scalar(self): # Regression test - in some cases, link functions can return Python # scalars rather than Numpy objects if a scalar is passed in, so we need # to make sure that works properly. data, from_, to_ = self.toy_data() from_id = data.add_component(from_, 'from_label') to_id = ComponentID('to_label') def using(x): if np.isscalar(x): return float(x) * 2 else: return x * 2 link = ComponentLink([from_id], to_id, using) result = link.compute(data, view=(0,)) assert_array_equal(result, 2) data.add_component(DerivedComponent(data, link), to_id) assert data.get_component(to_id).numeric l = ComponentLink([ComponentID('a')], ComponentID('b')) cid = ComponentID('a') scalar = 3 @pytest.mark.parametrize(('a', 'b'), [(l, l), (l, cid), (l, scalar)]) def test_arithmetic_overload(a, b): for x in [a + b, a - b, a * b, a / b, a ** b]: assert isinstance(x, BinaryComponentLink) for x in [b + a, b - a, b * a, b / a, b ** a]: assert isinstance(x, BinaryComponentLink) @pytest.mark.parametrize(('a', 'b'), [(l, l), (l, cid), (l, scalar)]) def test_inequality_overload(a, b): for x in [a < b, a <= b, a > b, a >= b]: assert isinstance(x, InequalitySubsetState) for x in [b < a, b <= a, b > a, b >= a]: assert isinstance(x, InequalitySubsetState) def test_link_bad_input(): with pytest.raises(TypeError) as exc: BinaryComponentLink(ComponentID('x'), None, None) assert exc.value.args[0] == 'Cannot create BinaryComponentLink using None' with pytest.raises(TypeError) as exc: BinaryComponentLink(None, ComponentID('x'), None) assert exc.value.args[0] == 'Cannot create BinaryComponentLink using None' def test_arithmetic_id_scalar(): d = Data(x=[1, 2, 3, 4], y=[10, 20, 10, 20]) assert_array_equal(d[d.id['x'] + 3], [4, 5, 6, 7]) assert_array_equal(d[d.id['x'] - 3], [-2, -1, 0, 1]) assert_array_equal(d[d.id['x'] * 3], [3, 6, 9, 12]) assert_array_equal(d[d.id['y'] / 10], [1, 2, 1, 2]) assert_array_equal(d[d.id['x'] ** 2], [1, 4, 9, 16]) assert_array_equal(d[3 + d.id['x']], [4, 5, 6, 7]) assert_array_equal(d[3 - d.id['x']], [2, 1, 0, -1]) assert_array_equal(d[3 * d.id['x']], [3, 6, 9, 12]) assert_array_equal(d[24 / d.id['x']], [24, 12, 8, 6]) assert_array_equal(d[2 ** d.id['x']], [2, 4, 8, 16]) def test_arithmetic_id_id(): d = Data(x=[1, 2, 3, 4], y=[10, 20, 10, 20]) assert_array_equal(d[d.id['x'] + d.id['y']], [11, 22, 13, 24]) assert_array_equal(d[d.id['x'] - d.id['y']], [-9, -18, -7, -16]) assert_array_equal(d[d.id['x'] * d.id['y']], [10, 40, 30, 80]) assert_array_equal( d[d.id['y'] / d.id['x']], [10, 10, 10 / 3, 5]) assert_array_equal(d[d.id['y'] ** d.id['x']], [10, 400, 1000, 20 ** 4]) def test_arithmetic_id_link(): d = Data(x=[1, 2, 3, 4], y=[10, 20, 10, 20]) y10 = d.id['y'] / 10 assert_array_equal(d[d.id['x'] + y10], [2, 4, 4, 6]) assert_array_equal(d[d.id['x'] - y10], [0, 0, 2, 2]) assert_array_equal(d[d.id['x'] * y10], [1, 4, 3, 8]) assert_array_equal(d[d.id['x'] / y10], [1, 1, 3, 2]) assert_array_equal(d[d.id['x'] ** y10], [1, 4, 3, 16]) assert_array_equal(d[y10 + d.id['x']], [2, 4, 4, 6]) assert_array_equal(d[y10 - d.id['x']], [0, 0, -2, -2]) assert_array_equal(d[y10 * d.id['x']], [1, 4, 3, 8]) assert_array_equal(d[y10 / d.id['x']], [1, 1, 1 / 3., 1 / 2.]) assert_array_equal(d[y10 ** d.id['x']], [1, 4, 1, 16]) def test_arithmetic_link_link(): d = Data(x=[1, 2, 3, 4], y=[10, 20, 10, 20]) x = d[d.id['x']] y = d[d.id['y']] xpy = d.id['x'] + d.id['y'] xt3 = d.id['x'] * 3 assert_array_equal(d[xpy + xt3], x + y + x * 3) assert_array_equal(d[xpy - xt3], x + y - x * 3) assert_array_equal(d[xpy * xt3], (x + y) * x * 3) assert_array_equal(d[xpy / xt3], (x + y) / (x * 3)) assert_array_equal(d[xpy ** xt3], (x + y) ** (x * 3)) def test_inequality(): d = Data(x=[1, 2, 3, 4], y=[10, 20, 10, 20]) s = d.new_subset() xpy = d.id['x'] + d.id['y'] twentytwo = xpy * 0 + 22 x = d[d.id['x']] y = d[d.id['y']] s.subset_state = xpy < 22 assert_array_equal(s.to_mask(), (x + y) < 22) s.subset_state = xpy <= 22 assert_array_equal(s.to_mask(), (x + y) <= 22) s.subset_state = xpy >= 22 assert_array_equal(s.to_mask(), (x + y) >= 22) s.subset_state = xpy > 22 assert_array_equal(s.to_mask(), (x + y) > 22) s.subset_state = 22 < xpy assert_array_equal(s.to_mask(), 22 < (x + y)) s.subset_state = 22 <= xpy assert_array_equal(s.to_mask(), 22 <= (x + y)) s.subset_state = 22 > xpy assert_array_equal(s.to_mask(), 22 > (x + y)) s.subset_state = 22 >= xpy assert_array_equal(s.to_mask(), 22 >= (x + y)) s.subset_state = twentytwo < xpy assert_array_equal(s.to_mask(), 22 < (x + y)) s.subset_state = twentytwo <= xpy assert_array_equal(s.to_mask(), 22 <= (x + y)) s.subset_state = twentytwo > xpy assert_array_equal(s.to_mask(), 22 > (x + y)) s.subset_state = twentytwo >= xpy assert_array_equal(s.to_mask(), 22 >= (x + y)) def test_link_fixes_shape(): def double(x): return (x * 2).reshape((2, 2)) d = Data(x=[1, 2, 3, 4]) y = ComponentID('y') link = ComponentLink([d.id['x']], y, using=double) assert_array_equal(d[link], [2, 4, 6, 8]) def test_link_str(): """Links should have sensible names""" d = Data(x=[1, 2, 3], y=[2, 3, 4]) x = d.id['x'] y = d.id['y'] assert str(x + y) == ('(x + y)') assert str(x - y) == ('(x - y)') assert str(x * y) == ('(x * y)') assert str(x / y) == ('(x / y)') assert str(x ** y) == ('(x ** y)') assert str(x ** 3) == ('(x ** 3)') assert str(3 + x * y) == ('(3 + (x * y))') assert str(x + x + y) == ('((x + x) + y)') assert repr(x + y) == '' def test_efficiency(): def add(x, y): # Make sure we don't benefit from broadcasting here return x.copy() + y.copy() data = Data(x=np.ones((2, 3, 4, 5)), y=np.ones((2, 3, 4, 5)), coords=IdentityCoordinates(n_dim=4)) for i, from_ids in enumerate(([data.id['x'], data.id['y']], data.world_component_ids[:2], data.pixel_component_ids[:2])): if i == 0: expected_shape = (2, 3, 4, 5) else: expected_shape = (2, 3, 1, 1) for cls in [ComponentLink, BinaryComponentLink]: if cls is ComponentLink: link = ComponentLink(from_ids, ComponentID('test'), using=add) else: link = BinaryComponentLink(from_ids[0], from_ids[1], add) result = link.compute(data) assert result.shape == (2, 3, 4, 5) assert unbroadcast(result).shape == expected_shape glueviz-1.0.1+dfsg.orig/glue/core/tests/test_joins.py0000644000175000017500000001334713605357235022206 0ustar noahfxnoahfximport pytest from numpy.testing import assert_array_equal from .. import Data, DataCollection from ..exceptions import IncompatibleAttribute from .test_state import clone class TestSubsets(object): def test_basic(self): x = Data(id=[0, 1, 2]) y = Data(id=[0, 1, 2, 3], x=[1, 2, 3, 4]) x.join_on_key(y, 'id', 'id') s = x.new_subset() s.subset_state = y.id['x'] > 1 assert_array_equal(s.to_mask(), [False, True, True]) # Make sure this also works if we use the Data.get_mask API assert_array_equal(x.get_mask(s.subset_state), [False, True, True]) def test_basic_to_index_list(self): x = Data(id=[0, 1, 2]) y = Data(id=[0, 1, 2, 3], x=[1, 2, 3, 4]) x.join_on_key(y, 'id', 'id') s = x.new_subset() s.subset_state = y.id['x'] > 1 assert_array_equal(s.to_index_list(), [1, 2]) def test_permute(self): x = Data(id=[1, 2, 1]) y = Data(id=[2, 0, 1, 5], x=[1, 2, 3, 2]) x.join_on_key(y, 'id', 'id') s = x.new_subset() s.subset_state = y.id['x'] < 3 assert_array_equal(s.to_mask(), [False, True, False]) s.subset_state = y.id['x'] > 1 assert_array_equal(s.to_mask(), [True, False, True]) def test_multidim(self): x = Data(id=[[0, 0], [1, 2]]) y = Data(id=[2, 0, 1], x=[1, 2, 3]) x.join_on_key(y, 'id', 'id') s = x.new_subset() s.subset_state = y.id['x'] > 1 assert_array_equal(s.to_mask(), [[True, True], [True, False]]) def test_mismatch(self): x = Data(id=[3, 4, 5]) y = Data(id=[0, 0, 0, 0], x=[1, 2, 3, 4]) x.join_on_key(y, 'id', 'id') s = x.new_subset() s.subset_state = y.id['x'] > 1 assert_array_equal(s.to_mask(), [False, False, False]) def test_inverse_match(self): x = Data(id=[0, 1, 2, 3], x=[5, 6, 7, 8]) y = Data(id=[2, 1, 0], y=[1, 2, 3]) x.join_on_key(y, 'id', 'id') s = y.new_subset() s.subset_state = x.id['x'] > 6 assert_array_equal(s.to_mask(), [True, False, False]) def test_join_chain(self): x = Data(id1=[0, 1, 2], label='x') y = Data(id1=[2, 1, 0, 3], id2=[3, 4, 5, 6], label='y') z = Data(id2=[5, 4, 5], z=[1, 2, 3], label='z') x.join_on_key(y, 'id1', 'id1') y.join_on_key(z, 'id2', 'id2') s = x.new_subset() s.subset_state = z.id['z'] > 2 assert_array_equal(s.to_mask(), [True, False, False]) with pytest.raises(IncompatibleAttribute): w = Data(w=[1, 2]) s.subset_state = w.id['w'] > 1 s.to_mask() def test_incompatible_attibute_without_join(self): x = Data(id1=[0, 1, 2], label='x') y = Data(y=[1, 2, 3, 4]) s = x.new_subset() s.subset_state = y.id['y'] > 2 with pytest.raises(IncompatibleAttribute): s.to_mask() def test_bad_join_key(self): x = Data(id1=[0, 1, 2], label='x') y = Data(id1=[1, 2, 3, 4], label='y') with pytest.raises(ValueError) as exc: x.join_on_key(y, 'bad_key', 'id1') assert exc.value.args[0] == 'ComponentID not found in x: bad_key' with pytest.raises(ValueError) as exc: x.join_on_key(y, 'id1', 'bad_key') assert exc.value.args[0] == 'ComponentID not found in y: bad_key' def test_clone(self): x = Data(id=[0, 1, 2], label='data_x') y = Data(id=[0, 1, 2, 3], x=[1, 2, 3, 4], label='data_y') x.join_on_key(y, 'id', 'id') dc = DataCollection([x, y]) dc = clone(dc) x, y = dc s = x.new_subset() s.subset_state = y.id['x'] > 1 assert_array_equal(s.to_mask(), [False, True, True]) def test_many_to_many(): """ Test the use of multiple keys to denote that combinations of components have to match. """ d1 = Data(x=[1, 2, 3, 5, 5], y=[0, 0, 1, 1, 2], label='d1') d2 = Data(a=[2, 5, 5, 8, 4, 9], b=[1, 3, 2, 2, 3, 9], label='d2') d2.join_on_key(d1, ('a', 'b'), ('x', 'y')) s = d1.new_subset() s.subset_state = d1.id['x'] == 5 assert_array_equal(s.to_mask(), [0, 0, 0, 1, 1]) s = d2.new_subset() s.subset_state = d1.id['x'] == 5 assert_array_equal(s.to_mask(), [0, 0, 1, 0, 0, 0]) def test_one_and_many(): """ Test the use of one-to-many keys or many-to-one key to indicate that any of the components can match the other. """ d1 = Data(x=[1, 2, 3], label='d1') d2 = Data(a=[1, 1, 2, 5], b=[2, 3, 3, 5], label='d2') d1.join_on_key(d2, 'x', ('a', 'b')) s = d2.new_subset() s.subset_state = d2.id['a'] == 2 assert_array_equal(s.to_mask(), [0, 0, 1, 0]) s = d1.new_subset() s.subset_state = d2.id['a'] == 2 assert_array_equal(s.to_mask(), [0, 1, 1]) d1 = Data(x=[1, 2, 3], label='d1') d2 = Data(a=[1, 1, 2, 5], b=[2, 3, 3, 5], label='d2') d2.join_on_key(d1, ('a', 'b'), 'x') s = d1.new_subset() s.subset_state = d1.id['x'] == 1 assert_array_equal(s.to_mask(), [1, 0, 0]) s = d2.new_subset() s.subset_state = d1.id['x'] == 1 assert_array_equal(s.to_mask(), [1, 1, 0, 0]) def test_mismatch(): d1 = Data(x=[1, 1, 2], y=[2, 3, 3], z=[2, 3, 3], label='d1') d2 = Data(a=[1, 1, 2], b=[2, 3, 3], label='d2') with pytest.raises(Exception) as exc: d1.join_on_key(d2, ('x', 'y', 'z'), ('a', 'b')) assert exc.value.args[0] == ("Either the number of components in the key " "join sets should match, or one of the " "component sets should contain a single " "component.") glueviz-1.0.1+dfsg.orig/glue/core/tests/test_state_objects.py0000644000175000017500000003374413657331513023716 0ustar noahfxnoahfximport numpy as np from numpy.testing import assert_allclose from echo import CallbackProperty, ListCallbackProperty from glue.core import Data, DataCollection from glue.utils import nanmedian from .test_state import clone from ..state_objects import (State, StateAttributeLimitsHelper, StateAttributeSingleValueHelper, StateAttributeHistogramHelper) class SimpleTestState(State): a = CallbackProperty() b = CallbackProperty() flat = ListCallbackProperty() nested = ListCallbackProperty() def test_state_serialization(): state1 = SimpleTestState() state1.a = 2 state1.b = 'hello' state1.flat = [1, 3, 4] sub_state = SimpleTestState() sub_state.a = 3 sub_state.b = 'blah' sub_state.flat = [1, 2] sub_state.nested = [] state1.nested = [1, 3, sub_state] state2 = clone(state1) assert state2.a == 2 assert state2.b == 'hello' assert state2.flat == [1, 3, 4] assert state2.nested[0:2] == [1, 3] assert state2.nested[2].a == 3 assert state2.nested[2].b == 'blah' assert state2.nested[2].flat == [1, 2] assert state2.nested[2].nested == [] EXPECTED_STR = """ a: 2 b: hello flat: nested: """ EXPECTED_REPR = """ nested: > """ def test_state_str_repr(): state1 = SimpleTestState() state1.a = 2 state1.b = 'hello' state1.flat = [1, 3, 4] sub_state = SimpleTestState() state1.nested = [1, 3, sub_state] assert str(state1) == EXPECTED_STR.strip() assert repr(state1) == EXPECTED_REPR.strip() class TestStateAttributeLimitsHelper(): def setup_method(self, method): self.data = Data(x=np.linspace(-100, 100, 10000), y=np.linspace(2, 3, 10000), label='test_data') self.data_collection = DataCollection([self.data]) class SimpleState(State): layer = CallbackProperty() comp = CallbackProperty() lower = CallbackProperty() upper = CallbackProperty() log = CallbackProperty(False) scale = CallbackProperty(100) self.state = SimpleState() self.helper = StateAttributeLimitsHelper(self.state, attribute='comp', lower='lower', upper='upper', percentile='scale', log='log') self.state.data = self.data self.state.comp = self.data.id['x'] self.x_id = self.data.main_components[0] self.y_id = self.data.main_components[1] def test_minmax(self): assert self.helper.lower == -100 assert self.helper.upper == +100 def test_change_attribute(self): self.helper.attribute = self.y_id assert self.helper.lower == 2 assert self.helper.upper == 3 self.helper.attribute = self.x_id assert self.helper.lower == -100 assert self.helper.upper == +100 def test_change_percentile(self): # Changing scale mode updates the limits self.helper.percentile = 99.5 assert_allclose(self.helper.lower, -99.5) assert_allclose(self.helper.upper, +99.5) self.helper.percentile = 99 assert_allclose(self.helper.lower, -99) assert_allclose(self.helper.upper, +99) self.helper.percentile = 90 assert_allclose(self.helper.lower, -90) assert_allclose(self.helper.upper, +90) # When switching to custom, the last limits are retained self.helper.percentile = "Custom" assert_allclose(self.helper.lower, -90) assert_allclose(self.helper.upper, +90) def test_percentile_cached(self): # Make sure that if we change scale and change attribute, the scale # modes are cached on a per-attribute basis. self.helper.percentile = 99.5 self.state.comp = self.y_id assert self.helper.percentile == 100 self.helper.percentile = 99 self.state.comp = self.x_id assert self.helper.percentile == 99.5 self.state.comp = self.y_id assert self.helper.percentile == 99 def test_flip_button(self): self.helper.flip_limits() assert self.helper.lower == +100 assert self.helper.upper == -100 # Make sure that values were re-cached when flipping self.state.comp = self.y_id assert self.helper.lower == 2 assert self.helper.upper == 3 self.state.comp = self.x_id assert self.helper.lower == +100 assert self.helper.upper == -100 def test_manual_edit(self): # Make sure that values are re-cached when edited manually self.helper.percentile = "Custom" self.state.lower = -122 self.state.upper = 234 self.helper.log = True assert self.helper.lower == -122 assert self.helper.upper == 234 assert self.helper.log self.state.comp = self.y_id assert self.helper.lower == 2 assert self.helper.upper == 3 assert not self.helper.log self.state.comp = self.x_id assert self.helper.lower == -122 assert self.helper.upper == 234 assert self.helper.log class TestStateAttributeSingleValueHelper(): def setup_method(self, method): self.data = Data(x=np.linspace(-100, 30, 9999), y=np.linspace(2, 3, 9999), label='test_data') self.data_collection = DataCollection([self.data]) class SimpleState(State): layer = CallbackProperty() comp = CallbackProperty() val = CallbackProperty() self.state = SimpleState() self.helper = StateAttributeSingleValueHelper(self.state, attribute='comp', function=nanmedian, value='val') self.state.data = self.data self.state.comp = self.data.id['x'] self.x_id = self.data.main_components[0] self.y_id = self.data.main_components[1] def test_value(self): assert self.helper.value == -35. def test_change_attribute(self): self.helper.attribute = self.y_id assert self.helper.value == 2.5 self.helper.attribute = self.x_id assert self.helper.value == -35 def test_manual_edit(self): self.state.val = 42. assert self.helper.value == 42 self.state.comp = self.y_id assert self.helper.value == 2.5 self.state.comp = self.x_id assert self.helper.value == 42 class TestStateAttributeHistogramHelper(): def setup_method(self, method): self.data = Data(x=[-3.2, 4.3, 2.2, 5.4, 7.2, -1.1, 2.3], y=['a', 'f', 'd', 'e', 'f', 'f', 'a'], label='test_data') self.data_collection = DataCollection([self.data]) class SimpleState(State): layer = CallbackProperty() comp = CallbackProperty() x_min = CallbackProperty() x_max = CallbackProperty() n_bin = CallbackProperty() self.state = SimpleState() self.helper = StateAttributeHistogramHelper(self.state, attribute='comp', lower='x_min', upper='x_max', n_bin='n_bin') self.state.data = self.data def test_default_numerical(self): self.state.comp = self.data.id['x'] assert self.state.x_min == -3.2 assert self.state.x_max == 7.2 assert self.state.n_bin == 15 def test_default_categorical(self): self.state.comp = self.data.id['y'] assert self.state.x_min == -0.5 assert self.state.x_max == 3.5 assert self.state.n_bin == 4 def test_hitting_limits(self): # FIXME: here we modify the internal defaults rather than making a new # state helper, but this could be improved self.helper._default_n_bin = 4 self.helper._max_n_bin = 3 self.state.comp = self.data.id['x'] assert self.state.x_min == -3.2 assert self.state.x_max == 7.2 assert self.state.n_bin == 4 self.state.comp = self.data.id['y'] assert self.state.x_min == -0.5 assert self.state.x_max == 3.5 assert self.state.n_bin == 3 def test_caching(self): self.state.comp = self.data.id['x'] self.state.x_min = 2 self.state.x_max = 7 self.state.n_bin = 8 self.state.comp = self.data.id['y'] self.state.x_min = 1.5 self.state.x_max = 3.5 self.state.n_bin = 3 self.state.comp = self.data.id['x'] assert self.state.x_min == 2 assert self.state.x_max == 7 assert self.state.n_bin == 8 self.state.comp = self.data.id['y'] assert self.state.x_min == 1.5 assert self.state.x_max == 3.5 assert self.state.n_bin == 3 def test_histogram_helper_common_n_bin(): data = Data(x=[-3.2, 4.3, 2.2], y=['a', 'f', 'd'], z=[1.1, 2.3, 1.2], label='test_data') class SimpleState(State): layer = CallbackProperty() comp = CallbackProperty() x_min = CallbackProperty() x_max = CallbackProperty() n_bin = CallbackProperty() common = CallbackProperty() state = SimpleState() helper = StateAttributeHistogramHelper(state, attribute='comp', lower='x_min', upper='x_max', n_bin='n_bin', common_n_bin='common') state.data = data state.comp = data.id['x'] state.n_bin = 9 state.comp = data.id['y'] assert state.n_bin == 3 state.comp = data.id['z'] assert state.n_bin == 15 state.n_bin = 12 state.common = True state.comp = data.id['x'] assert state.n_bin == 12 state.n_bin = 11 state.comp = data.id['y'] assert state.n_bin == 3 state.comp = data.id['z'] assert state.n_bin == 11 state.common = False state.n_bin = 13 state.comp = data.id['x'] assert state.n_bin == 11 def test_histogram_helper_common_n_bin_active(): # Make sure that common_n_bin works as expected if True from start data = Data(x=[-3.2, 4.3, 2.2], y=['a', 'f', 'd'], z=[1.1, 2.3, 1.2], label='test_data') class SimpleState(State): layer = CallbackProperty() comp = CallbackProperty() x_min = CallbackProperty() x_max = CallbackProperty() n_bin = CallbackProperty() common = CallbackProperty(True) state = SimpleState() helper = StateAttributeHistogramHelper(state, attribute='comp', lower='x_min', upper='x_max', n_bin='n_bin', common_n_bin='common') state.data = data state.comp = data.id['x'] state.n_bin = 9 state.comp = data.id['z'] assert state.n_bin == 9 state.n_bin = 12 state.common = True state.comp = data.id['x'] assert state.n_bin == 12 state.n_bin = 11 state.comp = data.id['y'] assert state.n_bin == 3 state.comp = data.id['z'] assert state.n_bin == 11 state.common = False state.n_bin = 13 state.comp = data.id['x'] assert state.n_bin == 11 def test_limits_helper_initial_values(): # Regression test for a bug that occurred if the limits cache was empty # but some attributes were set to values - in this case we don't want to # override the existing values. data = Data(x=np.linspace(-100, 100, 10000), y=np.linspace(2, 3, 10000), label='test_data') class SimpleState(State): layer = CallbackProperty() comp = CallbackProperty() lower = CallbackProperty() upper = CallbackProperty() state = SimpleState() state.lower = 1 state.upper = 2 state.comp = data.id['x'] helper = StateAttributeLimitsHelper(state, attribute='comp', lower='lower', upper='upper') assert helper.lower == 1 assert helper.upper == 2 class DatetimeState(State): a = CallbackProperty() def test_state_serialization_datetime64(): state1 = DatetimeState() state1.a = np.datetime64(100, 'D') state2 = clone(state1) assert state2.a == np.datetime64(100, 'D') def test_nan_inf_minmax(): data = Data(x=[3, 1, -2, np.inf, np.nan], label='test_data') class SimpleState(State): layer = CallbackProperty() comp = CallbackProperty() lower = CallbackProperty() upper = CallbackProperty() percentile = CallbackProperty() log = CallbackProperty() state = SimpleState() helper = StateAttributeLimitsHelper(state, attribute='comp', # noqa lower='lower', upper='upper', percentile='percentile', log='log') state.data = data state.comp = data.id['x'] assert state.lower == -2 assert state.upper == +3 state.log = True assert state.lower == +1 assert state.upper == +3 state.log = False state.percentile = 99 assert_allclose(state.lower, -1.97) assert_allclose(state.upper, +2.98) def test_percentile_no_log(): # Regression test for a bug that caused a crash if the state class had a # percentile attribute but no log. data = Data(x=np.linspace(-100, 100, 10000), y=np.linspace(2, 3, 10000), label='test_data') class SimpleState(State): layer = CallbackProperty() comp = CallbackProperty() lower = CallbackProperty() upper = CallbackProperty() scale = CallbackProperty() state = SimpleState() state.comp = data.id['x'] state.lower = 2 state.upper = 4 helper = StateAttributeLimitsHelper(state, attribute='comp', lower='lower', upper='upper', percentile='scale') state.scale = 90 glueviz-1.0.1+dfsg.orig/glue/core/tests/test_roi.py0000644000175000017500000011162113752534424021647 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 from collections import namedtuple import pytest import numpy as np from numpy.testing import assert_allclose import matplotlib.pyplot as plt from unittest.mock import MagicMock from matplotlib.figure import Figure from numpy.testing import assert_almost_equal from .. import roi as r from ..component import CategoricalComponent from ..roi import (RectangularROI, UndefinedROI, CircularROI, PolygonalROI, CategoricalROI, MplCircularROI, MplRectangularROI, MplPolygonalROI, MplPickROI, PointROI, XRangeROI, MplXRangeROI, YRangeROI, MplYRangeROI, RangeROI, Projected3dROI, EllipticalROI) from ..state import GlueSerializer, GlueUnSerializer FIG = Figure() AXES = FIG.add_subplot(111) def roundtrip_roi(roi): gs = GlueSerializer(roi) out_str = gs.dumps() obj = GlueUnSerializer.loads(out_str) return obj.object('__main__') class TestPoint(object): def setup_method(self, method): self.roi = PointROI(1, 2) def test_contains(self): assert not self.roi.contains(3, 3) assert not self.roi.contains(1, 2) def test_move_to(self): self.roi.move_to(4, 5) assert self.roi.x == 4 assert self.roi.y == 5 def test_defined(self): assert self.roi.defined() def test_not_defined(self): self.roi.reset() assert not self.roi.defined() def test_center(self): assert self.roi.center() == (1, 2) class TestRectangle(object): def setup_method(self, method): self.roi = RectangularROI() def test_empty_roi_contains_raises(self): with pytest.raises(UndefinedROI): self.roi.contains(1, 2) def test_scalar_contains(self): self.roi.update_limits(0, 0, 10, 10) assert self.roi.contains(5, 5) assert not self.roi.contains(11, 11) def test_reset(self): assert not self.roi.defined() self.roi.update_limits(0, 0, 10, 10) assert self.roi.defined() self.roi.reset() assert not self.roi.defined() with pytest.raises(UndefinedROI): self.roi.contains(5, 5) def test_empty_to_polygon(self): x, y = self.roi.to_polygon() assert x == [] assert y == [] def test_to_polygon(self): self.roi.update_limits(0, 0, 10, 10) x, y = self.roi.to_polygon() poly = PolygonalROI(vx=x, vy=y) assert poly.contains(5, 5) def test_ndarray(self): self.roi.update_limits(0, 0, 10, 10) x = np.array([5, 6, 2, 11]) y = np.array([5, 11, 2, 11]) result = self.roi.contains(x, y) assert result[0] assert not result[1] assert result[2] assert not result[3] def test_corner(self): self.roi.update_limits(6, 7, 10, 10) assert self.roi.corner() == (6, 7) def test_width(self): self.roi.update_limits(2, 2, 10, 12) assert self.roi.width() == 8 def test_height(self): self.roi.update_limits(2, 2, 10, 12) assert self.roi.height() == 10 def test_multidim_ndarray(self): self.roi.update_limits(0, 0, 10, 10) x = np.array([1, 2, 3, 4]).reshape(2, 2) y = np.array([1, 2, 3, 4]).reshape(2, 2) assert self.roi.contains(x, y).all() assert not self.roi.contains(x + 10, y).any() assert self.roi.contains(x, y).shape == x.shape def test_str_undefined(self): """ str method should not crash """ assert type(str(self.roi)) == str def test_str_defined(self): """ str method should not crash """ self.roi.update_limits(1, 2, 3, 4) assert type(str(self.roi)) == str def test_serialization(self): self.roi.update_limits(1, 2, 3, 4) new_roi = roundtrip_roi(self.roi) assert_almost_equal(new_roi.xmin, 1) assert_almost_equal(new_roi.ymin, 2) assert_almost_equal(new_roi.xmax, 3) assert_almost_equal(new_roi.ymax, 4) class TestRange(object): def test_wrong_orientation(self): with pytest.raises(ValueError): RangeROI(orientation='a') class TestXRange(object): def test_undefined_on_init(self): assert not XRangeROI().defined() def test_str(self): roi = XRangeROI() assert str(roi) == "Undefined XRangeROI" roi.set_range(1, 2) assert str(roi) == "1.000 < x < 2.000" def test_reset(self): roi = XRangeROI() roi.set_range(1, 2) assert roi.defined() roi.reset() assert not roi.defined() def test_contains(self): roi = XRangeROI() roi.set_range(1, 3) x = np.array([0, 1, 2, 3]) y = np.array([-np.inf, 100, 200, 0]) np.testing.assert_array_equal(roi.contains(x, y), [False, False, True, False]) def test_contains_undefined(self): roi = XRangeROI() with pytest.raises(UndefinedROI): roi.contains(1, 2) def test_to_polygon(self): roi = XRangeROI() assert roi.to_polygon() == ([], []) roi.set_range(1, 2) x, y = roi.to_polygon() np.testing.assert_array_equal(x, [1, 2, 2, 1, 1]) np.testing.assert_array_equal(y, [-1e100, -1e100, 1e100, 1e100, -1e100]) def test_serialization(self): roi = XRangeROI() roi.set_range(7, 8) new_roi = roundtrip_roi(roi) assert_almost_equal(new_roi.min, 7) assert_almost_equal(new_roi.max, 8) assert new_roi.ori == 'x' class TestYRange(object): def test_undefined_on_init(self): assert not YRangeROI().defined() def test_str(self): roi = YRangeROI() assert str(roi) == "Undefined YRangeROI" roi.set_range(1, 2) assert str(roi) == "1.000 < y < 2.000" def test_reset(self): roi = YRangeROI() roi.set_range(1, 2) assert roi.defined() roi.reset() assert not roi.defined() def test_contains(self): roi = YRangeROI() roi.set_range(1, 3) y = np.array([0, 1, 2, 3]) x = np.array([-np.inf, 100, 200, 0]) np.testing.assert_array_equal(roi.contains(x, y), [False, False, True, False]) def test_contains_undefined(self): roi = YRangeROI() with pytest.raises(UndefinedROI): roi.contains(1, 2) def test_to_polygon(self): roi = YRangeROI() assert roi.to_polygon() == ([], []) roi.set_range(1, 2) x, y = roi.to_polygon() np.testing.assert_array_equal(y, [1, 2, 2, 1, 1]) np.testing.assert_array_equal(x, [-1e100, -1e100, 1e100, 1e100, -1e100]) def test_serialization(self): roi = YRangeROI() roi.set_range(4, 5) new_roi = roundtrip_roi(roi) assert_almost_equal(new_roi.min, 4) assert_almost_equal(new_roi.max, 5) assert new_roi.ori == 'y' class TestCircle(object): def setup_method(self, method): self.roi = CircularROI() def test_undefined_on_creation(self): assert not self.roi.defined() def test_contains_on_undefined_contains_raises(self): with pytest.raises(UndefinedROI): self.roi.contains(1, 1) def test_set_center(self): self.roi.set_center(0, 0) self.roi.set_radius(1) assert self.roi.contains(0, 0) assert not self.roi.contains(2, 2) self.roi.set_center(2, 2) assert not self.roi.contains(0, 0) assert self.roi.contains(2, 2) def test_set_radius(self): self.roi.set_center(0, 0) self.roi.set_radius(1) assert not self.roi.contains(1.5, 0) self.roi.set_radius(5) assert self.roi.contains(1.5, 0) def test_contains_many(self): x = [0, 0, 0, 0, 0] y = [0, 0, 0, 0, 0] self.roi.set_center(0, 0) self.roi.set_radius(1) assert all(self.roi.contains(x, y)) assert all(self.roi.contains(np.asarray(x), np.asarray(y))) assert not any(self.roi.contains(np.asarray(x) + 10, y)) def test_poly(self): self.roi.set_center(0, 0) self.roi.set_radius(1) x, y = self.roi.to_polygon() poly = PolygonalROI(vx=x, vy=y) assert poly.contains(0, 0) assert not poly.contains(10, 0) def test_poly_undefined(self): x, y = self.roi.to_polygon() assert x == [] assert y == [] def test_reset(self): assert not self.roi.defined() self.roi.set_center(0, 0) assert not self.roi.defined() self.roi.set_radius(2) assert self.roi.defined() self.roi.reset() assert not self.roi.defined() def test_multidim(self): self.roi.set_center(0, 0) self.roi.set_radius(1) x = np.array([.1, .2, .3, .4]).reshape(2, 2) y = np.array([-.1, -.2, -.3, -.4]).reshape(2, 2) assert self.roi.contains(x, y).all() assert not self.roi.contains(x + 1, y).any() assert self.roi.contains(x, y).shape == (2, 2) def test_serialization(self): self.roi.set_center(3, 4) self.roi.set_radius(5) new_roi = roundtrip_roi(self.roi) assert_almost_equal(new_roi.xc, 3) assert_almost_equal(new_roi.yc, 4) assert_almost_equal(new_roi.radius, 5) class TestEllipse(object): def setup_method(self, method): self.roi_empty = EllipticalROI() self.roi = EllipticalROI(1, 2, 3, 4) def test_undefined_on_creation(self): assert not self.roi_empty.defined() assert self.roi.defined() def test_contains_on_undefined_contains_raises(self): with pytest.raises(UndefinedROI): self.roi_empty.contains(1, 1) self.roi.contains(1, 1) def test_set_center(self): assert self.roi.contains(0, 0) assert not self.roi.contains(12, 12) self.roi.xc = 11 self.roi.yc = 12 assert not self.roi.contains(0, 0) assert self.roi.contains(12, 12) def test_set_radius(self): assert self.roi.contains(0, 0) assert not self.roi.contains(12, 12) self.roi.radius_y = 100 assert self.roi.contains(0, 0) assert not self.roi.contains(12, 12) self.roi.radius_x = 100 assert self.roi.contains(0, 0) assert self.roi.contains(12, 12) def test_contains_many(self): x = [0, 0, 0, 0, 0] y = [0, 0, 0, 0, 0] assert all(self.roi.contains(x, y)) assert all(self.roi.contains(np.asarray(x), np.asarray(y))) assert not any(self.roi.contains(np.asarray(x) + 10, y)) def test_poly(self): x, y = self.roi.to_polygon() poly = PolygonalROI(vx=x, vy=y) assert poly.contains(0, 0) assert not poly.contains(10, 0) def test_poly_undefined(self): x, y = self.roi_empty.to_polygon() assert x == [] assert y == [] def test_reset(self): assert not self.roi_empty.defined() self.roi_empty.xc = 1 assert not self.roi_empty.defined() self.roi_empty.yc = 2 assert not self.roi_empty.defined() self.roi_empty.radius_x = 3 assert not self.roi_empty.defined() self.roi_empty.radius_y = 4 assert self.roi_empty.defined() self.roi_empty.reset() assert not self.roi_empty.defined() def test_multidim(self): x = np.array([.1, .2, .3, .4]).reshape(2, 2) y = np.array([-.1, -.2, -.3, -.4]).reshape(2, 2) assert self.roi.contains(x, y).all() assert not self.roi.contains(x + 10, y).any() assert self.roi.contains(x, y).shape == (2, 2) def test_serialization(self): new_roi = roundtrip_roi(self.roi) assert_almost_equal(new_roi.xc, 1) assert_almost_equal(new_roi.yc, 2) assert_almost_equal(new_roi.radius_x, 3) assert_almost_equal(new_roi.radius_y, 4) class TestPolygon(object): def setup_method(self, method): self.roi = PolygonalROI() def define_as_square(self): self.roi.reset() assert not self.roi.defined() self.roi.add_point(0, 0) self.roi.add_point(0, 1) self.roi.add_point(1, 1) self.roi.add_point(1, 0) assert self.roi.defined() def test_contains_on_empty_raises(self): with pytest.raises(UndefinedROI): self.roi.contains(1, 2) def test_remove_empty(self): self.roi.remove_point(1, 0) def test_replace(self): self.define_as_square() assert self.roi.contains(.9, .02) self.roi.replace_last_point(0, 0) assert not self.roi.contains(.9, .01) def test_remove_successful(self): self.define_as_square() assert self.roi.contains(.9, .02) self.roi.remove_point(1, 0) assert not self.roi.contains(.9, .01) def test_nan(self): self.define_as_square() assert not self.roi.contains(np.nan, .5) def test_remove_unsuccessful(self): self.define_as_square() assert self.roi.contains(.9, .02) self.roi.remove_point(1.5, 0, thresh=.49) assert self.roi.contains(.9, .01) def test_to_poly(self): self.define_as_square() x, y = self.roi.to_polygon() assert x == [0, 0, 1, 1] assert y == [0, 1, 1, 0] def test_to_poly_undefined(self): assert self.roi.to_polygon() == ([], []) def test_rect(self): self.roi.reset() self.roi.add_point(4.95164474584, 0.136922625654) self.roi.add_point(6.08256580223, 0.136922625654) self.roi.add_point(6.08256580223, 0.423771697842) self.roi.add_point(4.95164474584, 0.423771697842) self.roi.add_point(4.95164474584, 0.136922625654) x = np.array([4.4, 3.18, 4.49, 4.49]) y = np.array([.2, .2, .2, .2]) assert not self.roi.contains(4.4000001, 0.2) assert not self.roi.contains(3.1800001, 0.2) assert not self.roi.contains(4.4899998, 0.2) assert not self.roi.contains(x, y).any() x.shape = (2, 2) y.shape = (2, 2) assert not self.roi.contains(x, y).any() assert self.roi.contains(x, y).shape == (2, 2) def test_empty(self): assert not self.roi.defined() with pytest.raises(UndefinedROI): self.roi.contains(0, 0) def test_contains_scalar(self): self.define_as_square() assert self.roi.contains(.5, .5) assert not self.roi.contains(1.5, 1.5) def test_contains_list(self): self.define_as_square() assert self.roi.contains([.5, .4], [.5, .4]).all() assert not self.roi.contains([1.5, 1.5], [0, 0]).any() def test_contains_numpy(self): self.define_as_square() x = np.array([.4, .5, .4]) y = np.array([.4, .5, .4]) assert self.roi.contains(x, y).all() assert not self.roi.contains(x + 5, y).any() def test_str(self): """ __str__ returns a string """ assert type(str(self.roi)) == str def test_serialization(self): self.define_as_square() new_roi = roundtrip_roi(self.roi) assert_almost_equal(new_roi.vx, np.array([0, 0, 1, 1])) assert_almost_equal(new_roi.vy, np.array([0, 1, 1, 0])) class TestProjected3dROI(object): # matrix that converts xyzw to yxzw xyzw2yxzw = np.array([[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) x = [1, 2, 3] y = [2, 3, 4] z = [5, 6, 7] # repeat the arrays, 'rolled over' by 1 x_nd = [[1, 3], [2, 1], [3, 2]] y_nd = [[2, 3], [3, 2], [4, 3]] z_nd = [[5, 7], [6, 5], [7, 6]] def test_contains2d(self): roi_2d = PolygonalROI(vx=[0.5, 2.5, 2.5, 0.5], vy=[1, 1, 3.5, 3.5]) roi = Projected3dROI(roi_2d=roi_2d, projection_matrix=np.eye(4)) assert roi.contains(self.x, self.y).tolist() == [True, True, False] def test_contains3d(self): roi_2d = PolygonalROI(vx=[1.5, 3.5, 3.5, 1.5], vy=[4, 4, 6.5, 6.5]) roi = Projected3dROI(roi_2d=roi_2d, projection_matrix=self.xyzw2yxzw) assert roi.contains3d(self.x, self.y, self.z).tolist() == [True, True, False] assert roi.contains3d(self.x_nd, self.y_nd, self.z_nd).tolist() == [[True, False], [True, True], [False, True]] def test_forward(self): # testing the calls that should be forwarded to roi_2d roi_2d = PolygonalROI(vx=[0.5, 2.5, 2.5, 0.5], vy=[1, 1, 3.5, 3.5]) roi = Projected3dROI(roi_2d=roi_2d, projection_matrix=np.eye(4)) assert roi.contains(self.x, self.y).tolist() == roi_2d.contains(self.x, self.y).tolist() assert roi.to_polygon() == roi_2d.to_polygon() assert roi.defined() == roi_2d.defined() class TestCategorical(object): def test_empty(self): roi = CategoricalROI() assert not roi.defined() def test_defined(self): nroi = CategoricalROI(categories=['a', 'b', 'c']) assert nroi.defined() nroi.reset() assert not nroi.defined() def test_loads_from_components(self): roi = CategoricalROI() comp = CategoricalComponent(np.array(['a', 'a', 'b'])) roi.update_categories(comp) np.testing.assert_array_equal(roi.categories, np.array(['a', 'b'])) roi = CategoricalROI(categories=comp) np.testing.assert_array_equal(roi.categories, np.array(['a', 'b'])) def test_applies_components(self): roi = CategoricalROI() comp = CategoricalComponent(np.array(['a', 'b', 'c'])) roi.update_categories(CategoricalComponent(np.array(['a', 'b']))) contained = roi.contains(comp, None) np.testing.assert_array_equal(contained, np.array([True, True, False])) def test_from_range(self): comp = CategoricalComponent(np.array(list('abcdefghijklmnopqrstuvwxyz') * 2)) roi = CategoricalROI.from_range(comp, 6, 10) np.testing.assert_array_equal(roi.categories, np.array(list('ghij'))) def test_empty_categories(self): roi = CategoricalROI() contains = roi.contains(np.array(['a', 'b', 'c']), None) np.testing.assert_array_equal(contains, [0, 0, 0]) class DummyEvent(object): def __init__(self, x, y, inaxes=True, key=None): self.inaxes = inaxes self.xdata = x self.ydata = y self.key = key class MockAxes(object): def __init__(self): self.figure = MagicMock() self.figure.canvas = MagicMock() def add_patch(self, patch): pass class TestMpl(object): def setup_method(self, method): self.axes = MagicMock() self.roi = self._roi_factory() def _roi_factory(self): raise NotImplementedError def test_undefined_on_creation(self): assert not self.roi._roi.defined() def test_large_zorder(self): assert self.roi._patch.get_zorder() >= 100 def test_proper_roi(self): raise NotImplementedError def test_start_ignored_if_not_inaxes(self): ev = DummyEvent(0, 0, inaxes=None) self.roi.start_selection(ev) assert not self.roi._roi.defined() def test_canvas_syncs_properly(self): assert self.axes.figure.canvas.draw_idle.call_count == 0 assert self.axes.figure.canvas.blit.call_count == 0 event = DummyEvent(5, 5, inaxes=self.axes) self.roi.start_selection(event) assert self.axes.figure.canvas.draw_idle.call_count == 0 assert self.axes.figure.canvas.blit.call_count == 1 self.roi.update_selection(event) assert self.axes.figure.canvas.draw_idle.call_count == 0 assert self.axes.figure.canvas.blit.call_count == 2 self.roi.update_selection(event) assert self.axes.figure.canvas.draw_idle.call_count == 0 assert self.axes.figure.canvas.blit.call_count == 3 self.roi.finalize_selection(event) assert self.axes.figure.canvas.draw_idle.call_count == 0 assert self.axes.figure.canvas.blit.call_count == 4 def test_patch_shown_on_start(self): assert not self.roi._patch.get_visible() event = DummyEvent(5, 5, inaxes=self.axes) self.roi.start_selection(event) assert self.roi._patch.get_visible() def test_patch_hidden_on_finalise(self): event = DummyEvent(5, 5, inaxes=self.axes) self.roi.start_selection(event) self.roi.finalize_selection(event) assert not self.roi._patch.get_visible() def test_update_before_start_ignored(self): self.roi.update_selection(None) assert not self.roi._roi.defined() def test_finalize_before_start_ignored(self): self.roi.finalize_selection(None) assert not self.roi._roi.defined() def test_roi_defined_after_start(self): event = DummyEvent(5, 5, inaxes=self.axes) self.roi.start_selection(event) assert self.roi._roi.defined() def test_roi_undefined_before_start(self): assert not self.roi._roi.defined() def scrub(self, roi=None, abort=False, outside=False): if roi is None: roi = self._roi_factory() event = DummyEvent(5, 5, inaxes=self.axes) roi.start_selection(event) event = DummyEvent(10, 10, inaxes=self.axes) roi.update_selection(event) roi.finalize_selection(DummyEvent(10, 10)) if outside: roi.start_selection(DummyEvent(16, 16, inaxes=self.axes, key='control')) roi.update_selection(DummyEvent(17, 18, inaxes=self.axes, key='control')) else: roi.start_selection(DummyEvent(6, 6, inaxes=self.axes, key='control')) roi.update_selection(DummyEvent(7, 8, inaxes=self.axes, key='control')) if abort: roi.abort_selection(None) return roi class TestRectangleMpl(TestMpl): def _roi_factory(self): return MplRectangularROI(self.axes) def test_scrub(self): roi = self.scrub() assert roi._roi.xmin == 6 assert roi._roi.xmax == 11 assert roi._roi.ymin == 7 assert roi._roi.ymax == 12 def test_abort(self): roi = self.scrub(abort=True) assert roi._roi.xmin == 5 assert roi._roi.xmax == 10 assert roi._roi.ymin == 5 assert roi._roi.ymax == 10 def test_outside(self): roi = self.scrub(outside=True) assert roi._roi.xmin == 5 assert roi._roi.xmax == 10 assert roi._roi.ymin == 5 assert roi._roi.ymax == 10 def assert_roi_correct(self, x0, x1, y0, y1): corner = self.roi.roi().corner() height = self.roi.roi().height() width = self.roi.roi().width() assert self.roi.roi().defined() assert_almost_equal(corner[0], min(x0, x1), 4) assert_almost_equal(corner[1], min(y0, y1), 4) assert_almost_equal(abs(y1 - y0), height, 4) assert_almost_equal(abs(x1 - x0), width, 4) def assert_patch_correct(self, x0, x1, y0, y1): corner = self.roi._patch.get_xy() width = self.roi._patch.get_width() height = self.roi._patch.get_height() assert_almost_equal(corner, (min(x0, x1), min(y0, y1)), 4) assert_almost_equal(width, abs(x0 - x1)) assert_almost_equal(height, abs(y0 - y1)) def test_str(self): assert type(str(self.roi)) == str def test_proper_roi(self): assert isinstance(self.roi._roi, RectangularROI) def test_roi_on_start_selection(self): event = DummyEvent(5, 5, inaxes=self.axes) self.roi.start_selection(event) self.assert_roi_correct(5, 5, 5, 5) def test_patch_on_start_selection(self): event = DummyEvent(5, 5, inaxes=self.axes) self.roi.start_selection(event) self.assert_patch_correct(5, 5, 5, 5) def test_update_selection(self): event = DummyEvent(5, 6, inaxes=self.axes) self.roi.start_selection(event) event = DummyEvent(10, 11, inaxes=self.axes) self.roi.update_selection(event) self.assert_roi_correct(5, 10, 6, 11) self.assert_patch_correct(5, 10, 6, 11) def test_finalize_selection(self): event = DummyEvent(5, 6, inaxes=self.axes) self.roi.start_selection(event) event = DummyEvent(10, 11, inaxes=self.axes) self.roi.update_selection(event) self.roi.finalize_selection(event) self.assert_roi_correct(5, 10, 6, 11) self.assert_patch_correct(5, 10, 6, 11) def test_define_roi_to_right(self): ev0 = DummyEvent(0.5, 0.5, inaxes=self.axes) ev1 = DummyEvent(1, 1, inaxes=self.axes) self.roi.start_selection(ev0) self.roi.update_selection(ev1) self.roi.finalize_selection(ev1) self.assert_roi_correct(.5, 1, .5, 1) self.assert_patch_correct(.5, 1, .5, 1) def test_define_roi_to_left(self): ev0 = DummyEvent(1, 1, inaxes=self.axes) ev1 = DummyEvent(0.5, 0.5, inaxes=self.axes) self.roi.start_selection(ev0) self.roi.update_selection(ev1) self.roi.finalize_selection(ev1) self.assert_roi_correct(.5, 1, .5, 1) self.assert_patch_correct(.5, 1, .5, 1) class TestXRangeMpl(TestMpl): def _roi_factory(self): return MplXRangeROI(self.axes) def test_proper_roi(self): assert isinstance(self.roi._roi, XRangeROI) def test_start_selection(self): event = DummyEvent(1, 1, inaxes=self.axes) self.roi.start_selection(event) assert self.roi._roi.defined() def test_update_selection(self): ev0 = DummyEvent(1, 1, inaxes=self.axes) ev1 = DummyEvent(2, 1, inaxes=self.axes) self.roi.start_selection(ev0) self.roi.update_selection(ev1) assert self.roi._roi.defined() assert self.roi._roi.range() == (1, 2) def test_finalize_selection(self): ev0 = DummyEvent(1, 1, inaxes=self.axes) ev1 = DummyEvent(2, 1, inaxes=self.axes) self.roi.start_selection(ev0) self.roi.update_selection(ev1) self.roi.finalize_selection(ev1) assert self.roi._roi.defined() assert self.roi._roi.range() == (1, 2) assert not self.roi._patch.get_visible() def test_scrub(self): roi = self.scrub() assert_almost_equal(roi._roi.min, 3.0) assert_almost_equal(roi._roi.max, 8.0) def test_abort(self): roi = self.scrub(abort=True) assert_almost_equal(roi._roi.min, 5.0) assert_almost_equal(roi._roi.max, 10.0) def test_outside(self): roi = self.scrub(outside=True) assert_almost_equal(roi._roi.min, 5.0) assert_almost_equal(roi._roi.max, 10.0) class TestYRangeMpl(TestMpl): def _roi_factory(self): return MplYRangeROI(self.axes) def test_proper_roi(self): assert isinstance(self.roi._roi, YRangeROI) def test_start_selection(self): event = DummyEvent(1, 1, inaxes=self.axes) self.roi.start_selection(event) assert self.roi._roi.defined() def test_update_selection(self): ev0 = DummyEvent(1, 1, inaxes=self.axes) ev1 = DummyEvent(1, 2, inaxes=self.axes) self.roi.start_selection(ev0) self.roi.update_selection(ev1) assert self.roi._roi.defined() assert self.roi._roi.range() == (1, 2) def test_finalize_selection(self): ev0 = DummyEvent(1, 1, inaxes=self.axes) ev1 = DummyEvent(1, 2, inaxes=self.axes) self.roi.start_selection(ev0) self.roi.update_selection(ev1) self.roi.finalize_selection(ev1) assert self.roi._roi.defined() assert self.roi._roi.range() == (1, 2) assert not self.roi._patch.get_visible() def test_scrub(self): roi = self.scrub() assert_almost_equal(roi._roi.min, 4.0) assert_almost_equal(roi._roi.max, 9.0) def test_abort(self): roi = self.scrub(abort=True) assert_almost_equal(roi._roi.min, 5.0) assert_almost_equal(roi._roi.max, 10.0) def test_outside(self): roi = self.scrub(outside=True) assert_almost_equal(roi._roi.min, 5.0) assert_almost_equal(roi._roi.max, 10.0) def test_circular_roi_representation(): # Test cases where drawn circular ROIs are converted to circles, ellipses, # and polygons. Here we don't override pixel_to_data and data_to_pixel # since these are important. event = namedtuple('Event', ['inaxes', 'xdata', 'ydata', 'key']) fig = plt.figure(figsize=(6, 4)) ax = fig.add_subplot(1, 1, 1) # Case 1: log-linear axes ax.set_xlim(10, 100) ax.set_ylim(-2, 5) ax.set_xscale('log') ax.set_yscale('linear') fig.canvas.draw() mpl_roi = MplCircularROI(ax) mpl_roi.start_selection(event(inaxes=ax, xdata=50, ydata=1, key=None)) mpl_roi.update_selection(event(inaxes=ax, xdata=100, ydata=1, key=None)) roi = mpl_roi.roi() assert isinstance(roi, PolygonalROI) # Case 2: linear-linear axes with non-square aspect ratio ax.set_xlim(-40, 100) ax.set_ylim(-2, 5) ax.set_xscale('linear') ax.set_yscale('linear') fig.canvas.draw() mpl_roi = MplCircularROI(ax) mpl_roi.start_selection(event(inaxes=ax, xdata=30, ydata=1, key=None)) mpl_roi.update_selection(event(inaxes=ax, xdata=80, ydata=1, key=None)) roi = mpl_roi.roi() assert isinstance(roi, EllipticalROI) assert_allclose(roi.xc, 30) assert_allclose(roi.yc, 1) assert_allclose(roi.radius_x, 50) assert_allclose(roi.radius_y, 5.871212) # Case 3: linear-linear axes with square aspect ratio ax.set_aspect('equal') ax.set_xlim(-40, 100) fig.canvas.draw() mpl_roi = MplCircularROI(ax) mpl_roi.start_selection(event(inaxes=ax, xdata=50, ydata=1, key=None)) mpl_roi.update_selection(event(inaxes=ax, xdata=80, ydata=1, key=None)) roi = mpl_roi.roi() assert isinstance(roi, CircularROI) assert_allclose(roi.xc, 50) assert_allclose(roi.yc, 1) assert_allclose(roi.radius, 30) class TestCircleMpl(TestMpl): def _roi_factory(self): return MplCircularROI(self.axes) def setup_method(self, method): super(TestCircleMpl, self).setup_method(method) self.pixel_to_data = r.pixel_to_data self.data_to_pixel = r.data_to_pixel r.pixel_to_data = lambda x, y, z: np.column_stack((y, z)) r.data_to_pixel = lambda x, y, z: np.column_stack((y, z)) def teardown_method(self, method): # restore methods r.pixel_to_data = self.pixel_to_data r.data_to_pixel = self.data_to_pixel def test_proper_roi(self): assert isinstance(self.roi._roi, CircularROI) def test_to_polygon_undefined(self): """to_polygon() result should be undefined before defining polygon""" roi = self.roi.roi() assert not roi.defined() def test_roi_defined_correctly(self): ev0 = DummyEvent(0, 0, inaxes=self.axes) ev1 = DummyEvent(5, 0, inaxes=self.axes) self.roi.start_selection(ev0) self.roi.update_selection(ev1) self.roi.finalize_selection(ev1) self.assert_roi_correct(0, 0, 5) def assert_roi_correct(self, x, y, r): roi = self.roi.roi() assert roi.defined() assert roi.contains(x, y) assert roi.contains(x + .95 * r, y) assert not roi.contains(x + 1.05 * r, y) assert not roi.contains(x + .8 * r, y + .8 * r) def test_scrub(self): roi = self.scrub() assert roi._roi.xc == 6 assert roi._roi.yc == 7 assert_almost_equal(roi._roi.radius, 7.0, decimal=0) def test_abort(self): roi = self.scrub(abort=True) assert roi._roi.xc == 5 assert roi._roi.yc == 5 assert_almost_equal(roi._roi.radius, 7.0, decimal=0) def test_outside(self): roi = self.scrub(outside=True) assert roi._roi.xc == 5 assert roi._roi.yc == 5 assert_almost_equal(roi._roi.radius, 7.0, decimal=0) class TestPolyMpl(TestMpl): def _roi_factory(self): return MplPolygonalROI(self.axes) def test_proper_roi(self): return isinstance(self.roi._roi, PolygonalROI) def send_events(self): ev0 = DummyEvent(5, 5, inaxes=self.axes) ev1 = DummyEvent(5, 10, inaxes=self.axes) ev2 = DummyEvent(10, 10, inaxes=self.axes) ev3 = DummyEvent(10, 5, inaxes=self.axes) self.roi.start_selection(ev0) self.roi.update_selection(ev1) self.roi.update_selection(ev2) self.roi.update_selection(ev3) self.roi.finalize_selection(ev3) def assert_roi_correct(self): roi = self.roi.roi() assert roi.contains(7.0, 7.0) assert not roi.contains(12, 7.0) def test_define(self): self.send_events() self.assert_roi_correct() def test_scrub(self): self.send_events() roi = self.scrub(roi=self.roi) assert roi._roi.vx[0] == 6 assert roi._roi.vy[0] == 7 def test_abort(self): self.send_events() roi = self.scrub(roi=self.roi, abort=True) assert roi._roi.vx[0] == 5 assert roi._roi.vy[0] == 5 def test_outside(self): self.send_events() roi = self.scrub(roi=self.roi, outside=True) assert roi._roi.vx[0] == 5 assert roi._roi.vy[0] == 5 class TestPickMpl(TestMpl): def _roi_factory(self): return MplPickROI(self.axes) def test_proper_roi(self): return isinstance(self.roi._roi, PointROI) def test_start_ignored_if_not_inaxes(self): ev = DummyEvent(0, 0, inaxes=None) self.roi.start_selection(ev) assert self.roi._roi.defined() def test_update_before_start_ignored(self): ev = DummyEvent(0, 0, inaxes=None) self.roi.update_selection(ev) assert self.roi._roi.defined() def test_finalize_before_start_ignored(self): ev = DummyEvent(0, 0, inaxes=None) self.roi.finalize_selection(ev) assert self.roi._roi.defined() def test_large_zorder(self): """No patch to test for.""" def test_patch_shown_on_start(self): """No patch to test for.""" def test_patch_hidden_on_finalise(self): """No patch to test for.""" def test_canvas_syncs_properly(self): """No patch to test for.""" class TestUtil(object): def setup_method(self, method): self.axes = AXES def test_aspect_ratio(self): self.axes.figure.set_figheight(5) self.axes.figure.set_figwidth(5) self.axes.set_ylim([0, 10]) self.axes.set_xlim([0, 10]) ax0 = r.aspect_ratio(self.axes) self.axes.set_ylim(0, 20) assert_almost_equal(r.aspect_ratio(self.axes), ax0 / 2, 4) self.axes.set_ylim(0, 5) assert_almost_equal(r.aspect_ratio(self.axes), ax0 * 2, 4) self.axes.set_xlim(0, 5) assert_almost_equal(r.aspect_ratio(self.axes), ax0, 4) self.axes.set_xlim(0, 10) assert_almost_equal(r.aspect_ratio(self.axes), ax0 * 2, 4) def test_data_to_norm_with_scalars(self): # assume data that gets submitted to axes is valid. # testing to see if we can get there self.axes.set_xlim(0, 10) self.axes.set_ylim(0, 10) func = r.data_to_norm assert_almost_equal(func(self.axes, 0, 0)[0, 0], 0, 3) assert_almost_equal(func(self.axes, 0, 0)[0, 1], 0, 3) assert_almost_equal(func(self.axes, 0, 10)[0, 0], 0, 3) assert_almost_equal(func(self.axes, 0, 10)[0, 1], 1, 3) assert_almost_equal(func(self.axes, 10, 10)[0, 0], 1, 3) assert_almost_equal(func(self.axes, 10, 10)[0, 1], 1, 3) assert_almost_equal(func(self.axes, 10, 0)[0, 0], 1, 3) assert_almost_equal(func(self.axes, 10, 0)[0, 1], 0, 3) x = np.array([0, 0, 10, 10]) y = np.array([0, 10, 0, 10]) xans = [0, 0, 1, 1] yans = [0, 1, 0, 1] norm = func(self.axes, x, y) assert_almost_equal(norm[0, 0], xans[0], 3) assert_almost_equal(norm[1, 0], xans[1], 3) assert_almost_equal(norm[2, 0], xans[2], 3) assert_almost_equal(norm[3, 0], xans[3], 3) assert_almost_equal(norm[0, 1], yans[0], 3) assert_almost_equal(norm[1, 1], yans[1], 3) assert_almost_equal(norm[2, 1], yans[2], 3) assert_almost_equal(norm[3, 1], yans[3], 3) x = [0, 0, 10, 10] y = [0, 10, 0, 10] assert_almost_equal(norm[0, 0], xans[0], 3) assert_almost_equal(norm[1, 0], xans[1], 3) assert_almost_equal(norm[2, 0], xans[2], 3) assert_almost_equal(norm[3, 0], xans[3], 3) assert_almost_equal(norm[0, 1], yans[0], 3) assert_almost_equal(norm[1, 1], yans[1], 3) assert_almost_equal(norm[2, 1], yans[2], 3) assert_almost_equal(norm[3, 1], yans[3], 3) def test_data_to_pixel(self): xp = 100 yp = 100 data = r.pixel_to_data(self.axes, xp, yp) pixel = r.data_to_pixel(self.axes, data[:, 0], data[:, 1]) assert_almost_equal(pixel[0, 0], xp, 3) assert_almost_equal(pixel[0, 1], yp, 3) del TestMpl # prevents unittest discovery from running abstract base class glueviz-1.0.1+dfsg.orig/glue/core/tests/test_command.py0000644000175000017500000000674313605357235022504 0ustar noahfxnoahfximport pytest import numpy as np from unittest.mock import MagicMock from glue import core from .. import command as c from .. import roi from ..data_factories import tabular_data from .util import simple_session, simple_catalog class TestCommandStack(object): def setup_method(self, method): self.session = simple_session() self.stack = self.session.command_stack def make_command(self): return MagicMock(c.Command) def make_data(self): with simple_catalog() as path: cmd = c.LoadData(path=path, factory=tabular_data) data = self.stack.do(cmd) return data def test_do(self): c1 = self.make_command() self.stack.do(c1) c1.do.assert_called_once_with(self.session) def test_undo(self): c1, c2 = self.make_command(), self.make_command() self.stack.do(c1) self.stack.do(c2) self.stack.undo() c2.undo.assert_called_once_with(self.session) self.stack.undo() c1.undo.assert_called_once_with(self.session) def test_redo(self): c1, c2 = self.make_command(), self.make_command() self.stack.do(c1) self.stack.do(c2) self.stack.undo() self.stack.redo() c2.undo.assert_called_once_with(self.session) assert c2.do.call_count == 2 assert c2.undo.call_count == 1 assert c1.do.call_count == 1 assert c1.undo.call_count == 0 def test_max_undo(self): cmds = [self.make_command() for _ in range(c.MAX_UNDO + 1)] for cmd in cmds: self.stack.do(cmd) for cmd in cmds[:-1]: self.stack.undo() with pytest.raises(IndexError): self.stack.undo() def test_invalid_redo(self): with pytest.raises(IndexError) as exc: self.stack.redo() assert exc.value.args[0] == 'No commands to redo' def test_load_data(self): data = self.make_data() np.testing.assert_array_equal(data['a'], [1, 3]) def test_add_data(self): data = self.make_data() cmd = c.AddData(data=data) self.stack.do(cmd) assert len(self.session.data_collection) == 1 self.stack.undo() assert len(self.session.data_collection) == 0 def test_remove_data(self): data = self.make_data() add = c.AddData(data=data) remove = c.RemoveData(data=data) self.stack.do(add) assert len(self.session.data_collection) == 1 self.stack.do(remove) assert len(self.session.data_collection) == 0 self.stack.undo() assert len(self.session.data_collection) == 1 def test_new_data_viewer(self): cmd = c.NewDataViewer(viewer=None, data=None) v = self.stack.do(cmd) self.session.application.new_data_viewer.assert_called_once_with(None, None) self.stack.undo() v.close.assert_called_once_with(warn=False) def test_apply_roi(self): x = core.Data(x=[1, 2, 3]) s = x.new_subset() dc = self.session.data_collection dc.append(x) r = MagicMock(roi.Roi) apply_roi = MagicMock() cmd = c.ApplyROI(data_collection=dc, roi=r, apply_func=apply_roi) self.stack.do(cmd) apply_roi.assert_called_once_with(r) old_state = s.subset_state s.subset_state = MagicMock(spec_set=core.subset.SubsetState) self.stack.undo() assert s.subset_state is old_state glueviz-1.0.1+dfsg.orig/glue/core/tests/test_data_retrieval.py0000644000175000017500000000210313605357235024036 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import numpy as np from ..data import Data, Component class TestDataRetrieval(object): def setup_method(self, method): data1 = Data() comp1 = Component(np.arange(5)) id1 = data1.add_component(comp1, 'comp_1') comp2 = Component(np.arange(5) * 2) id2 = data1.add_component(comp2, 'comp_2') data2 = Data() comp3 = Component(np.arange(5) * 3) id3 = data2.add_component(comp3, 'comp_3') comp4 = Component(np.arange(5) * 4) id4 = data2.add_component(comp4, 'comp_4') self.data = [data1, data2] self.components = [comp1, comp2, comp3, comp4] self.component_ids = [id1, id2, id3, id4] def test_direct_get(self): assert self.data[0][self.component_ids[0]] is self.components[0].data assert self.data[0][self.component_ids[1]] is self.components[1].data assert self.data[1][self.component_ids[2]] is self.components[2].data assert self.data[1][self.component_ids[3]] is self.components[3].data glueviz-1.0.1+dfsg.orig/glue/core/tests/test_application_base.py0000644000175000017500000000762013610101121024327 0ustar noahfxnoahfximport os import json from unittest.mock import MagicMock from .. import Data from ..application_base import Application class MockApplication(Application): def __init__(self, data_collection=None, session=None): super(MockApplication, self).__init__(data_collection=data_collection, session=session) self.tab = MagicMock() self.errors = MagicMock() def report_error(self, message, detail): self.errors.report(message, detail) def new_tab(self): self.tab.tab() def add_widget(self, widget, label=None, tab=None): self.tab.add(widget, label) def close_tab(self): self.tab.close() def _load_settings(self): pass def test_session(tmpdir): session_file = tmpdir.join('test.glu').strpath app = MockApplication() app.save_session(session_file) MockApplication.restore_session(session_file) def test_set_data_color(): x = Data(x=[1, 2, 3]) y = Data(y=[1, 2, 3, 4]) x.style.color = 'blue' x.style.alpha = 0.4 y.style.color = 'purple' y.style.alpha = 0.5 app = Application() app.data_collection.append(x) app.data_collection.append(y) app.set_data_color('red', alpha=0.3) assert x.style.color == 'red' assert x.style.alpha == 0.3 assert y.style.color == 'red' assert y.style.alpha == 0.3 def test_nested_data(): # Regression test that caused add_datasets to crash if datasets included # lists of lists of data, which is possible if a data factory returns more # than one data object. x = Data(x=[1, 2]) y = Data(x=[1, 2, 3]) z = Data(y=[1, 2, 3, 4]) a = Data(y=[1, 2, 3, 4, 5]) b = Data(y=[1, 2, 3, 4, 5, 6]) c = Data(y=[1, 2, 3, 4, 5, 6, 7]) datasets = [x, [[[y, z]], a], [[[[b, c]]]]] app = Application() app.add_datasets(datasets) assert x in app.data_collection assert y in app.data_collection assert z in app.data_collection assert a in app.data_collection assert b in app.data_collection assert c in app.data_collection def test_session_paths(tmpdir): os.chdir(tmpdir.strpath) tmpdir.mkdir('data') with open(os.path.join('data', 'data.csv'), 'w') as f: f.write('a, b\n1, 2\n3, 4\n') app = MockApplication() app.load_data(os.path.join('data', 'data.csv')) # Make sure the paths work whether the session file is saved in the current # directory or in a sub-directory (this is important for relative links) for save_dir in ('.', 'data'): for absolute in (True, False): session_file = tmpdir.join(save_dir).join('test.glu').strpath app.save_session(session_file, absolute_paths=absolute) with open(session_file) as f: data = json.load(f) for key, value in data.items(): if value.get('_type') == 'glue.core.data_factories.helpers.LoadLog': assert os.path.isabs(value['path']) is absolute MockApplication.restore_session(session_file) def test_load_data(tmpdir): os.chdir(tmpdir.strpath) with open('data1.csv', 'w') as f: f.write('a, b\n1, 2\n3, 4\n') with open('data2.csv', 'w') as f: f.write('a, b\n1, 2\n3, 4\n') app1 = Application() data = app1.load_data('data1.csv') assert len(app1.data_collection) == 1 assert isinstance(data, Data) app2 = Application() datasets = app2.load_data(['data1.csv', 'data2.csv'], skip_merge=True) assert len(app2.data_collection) == 2 assert len(datasets) == 2 assert isinstance(datasets[0], Data) assert isinstance(datasets[1], Data) app3 = Application() data = app3.load_data(['data1.csv', 'data2.csv'], auto_merge=True) assert len(app3.data_collection) == 1 # NOTE: for now this still returns the individual datasets assert len(datasets) == 2 assert isinstance(datasets[0], Data) assert isinstance(datasets[1], Data) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_message.py0000644000175000017500000000111213605357235022473 0ustar noahfxnoahfximport pytest from .. import message as msg def test_invalid_subset_msg(): with pytest.raises(TypeError) as exc: msg.SubsetMessage(None) assert exc.value.args[0].startswith('Sender must be a subset') def test_invalid_data_msg(): with pytest.raises(TypeError) as exc: msg.DataMessage(None) assert exc.value.args[0].startswith('Sender must be a data') def test_invalid_data_collection_msg(): with pytest.raises(TypeError) as exc: msg.DataCollectionMessage(None) assert exc.value.args[0].startswith('Sender must be a DataCollection') glueviz-1.0.1+dfsg.orig/glue/core/tests/test_fitters.py0000644000175000017500000001506413605357235022542 0ustar noahfxnoahfx import pytest import numpy as np from unittest.mock import MagicMock from glue.tests.helpers import requires_scipy, requires_astropy, ASTROPY_INSTALLED from ..fitters import (PolynomialFitter, IntOption, BasicGaussianFitter) needs_modeling = pytest.mark.skipif("False", reason='') if ASTROPY_INSTALLED: from astropy.modeling.models import Gaussian1D try: from astropy.modeling.fitting import NonLinearLSQFitter except ImportError: from astropy.modeling.fitting import LevMarLSQFitter as NonLinearLSQFitter from ..fitters import SimpleAstropyGaussianFitter @requires_astropy @requires_scipy class TestAstropyFitter(object): def test_fit(self): f = SimpleAstropyGaussianFitter(amplitude=1, mean=2, stddev=3) f.fitting_cls = fitter = MagicMock() f.build_and_fit([1, 2, 3], [2, 3, 4]) (model, x, y), kwargs = fitter().call_args assert model.amplitude == 1 assert model.mean == 2 assert model.stddev == 3 np.testing.assert_array_equal(x, [1, 2, 3]) np.testing.assert_array_equal(y, [2, 3, 4]) def test_fit_converts_errors_to_weights(self): f = SimpleAstropyGaussianFitter(amplitude=1, mean=2, stddev=3) f.fitting_cls = fitter = MagicMock() f.build_and_fit([1, 2, 3], [2, 3, 4], [3, 4, 5]) args, kwargs = fitter().call_args np.testing.assert_array_equal(kwargs['weights'], 1. / np.array([3., 4., 5]) ** 2) def test_fit_returns_model_and_fitter(self): f = SimpleAstropyGaussianFitter(amplitude=1, mean=2, stddev=3) model, fitter = f.build_and_fit([1, 2, 3], [2, 3, 4]) assert isinstance(model, Gaussian1D) assert isinstance(fitter, NonLinearLSQFitter) def test_predict_uses_model(self): f = SimpleAstropyGaussianFitter(amplitude=1, mean=2, stddev=3) result = f.build_and_fit([1, 2, 3], [2, 3, 4]) model, _ = result np.testing.assert_array_equal(model([3]), f.predict(result, [3])) def test_summarize(self): f = SimpleAstropyGaussianFitter(amplitude=1, mean=2, stddev=3) model, fitter = f.build_and_fit([1, 2, 3], [2, 3, 4]) expected = ["Converged in %i iterations" % fitter.fit_info['nfev'], "", "amplitude = %e" % model.amplitude.value, "mean = %e" % model.mean.value, "stddev = %e" % model.stddev.value ] expected = '\n'.join(expected) actual = f.summarize((model, fitter), [1, 2, 3], [2, 3, 4]) assert expected == actual def test_range_constraints(self): f = SimpleAstropyGaussianFitter(amplitude=1, mean=0, stddev=1) x = np.linspace(0, 10, 10) y = np.exp(-x ** 2 / 2) f.set_constraint('mean', limits=[1, 2]) model, fitter = f.build_and_fit(x, y) np.testing.assert_almost_equal(model.mean.value, 1) def test_fixed_constraints(self): f = SimpleAstropyGaussianFitter(amplitude=1.5, mean=0, stddev=1) x = np.linspace(-5, 5, 10) y = np.exp(-x ** 2 / 2) f.set_constraint('amplitude', fixed=True) model, fitter = f.build_and_fit(x, y) np.testing.assert_almost_equal(model.amplitude.value, 1.5) class TestPolynomialFitter(object): def test_fit(self): f = PolynomialFitter() result = f.build_and_fit([1, 2, 3, 4, 5, 6], [2, 3, 4, 4, 3, 4]) expected = np.polyfit([1, 2, 3, 4, 5, 6], [2, 3, 4, 4, 3, 4], 3) np.testing.assert_array_equal(result, expected) def test_predict(self): f = PolynomialFitter(degree=1) fit = f.build_and_fit([1, 2, 3], [2, 3, 4]) result = f.predict(fit, [4]) expected = [5] np.testing.assert_almost_equal(result, expected) def test_summarize(self): f = PolynomialFitter(degree=1) fit = f.build_and_fit([1, 2, 3], [2, 3, 4]) assert f.summarize( fit, [1, 2, 3], [2, 3, 4]) == "Coefficients:\n%e\n%e" % (1, 1) class TestOptions(object): def test_options(self): p = PolynomialFitter(degree=3) assert p.options == {'degree': 3} def test_inherited(self): class Inherit(PolynomialFitter): test = IntOption(default=5) assert Inherit().options == dict(degree=3, test=5) def test_options_passed_to_fit(self): p = PolynomialFitter(degree=4) p.fit = MagicMock() p.build_and_fit([1, 2, 3], [2, 3, 4]) assert p.fit.call_args[1]['degree'] == 4 @requires_astropy class TestFitWrapper(object): def setup_method(self, method): f = SimpleAstropyGaussianFitter(amplitude=1, mean=2, stddev=3) f.fit = MagicMock() self.x = [1, 2, 3] self.y = [2, 3, 4] self.f = f def call_info(self): return self.f.fit.call_args def test_basic(self): self.f.build_and_fit(self.x, self.y) (x, y), kwargs = self.call_info() assert kwargs['constraints'] == {'amplitude': dict(value=1, fixed=False, limits=None), 'mean': dict(value=2, fixed=False, limits=None), 'stddev': dict(value=3, fixed=False, limits=None)} np.testing.assert_array_equal(x, self.x) np.testing.assert_array_equal(y, self.y) @requires_astropy class TestSetConstraints(object): def test(self): f = SimpleAstropyGaussianFitter(amplitude=1, mean=2, stddev=3) f.set_constraint('amplitude', value=3, fixed=True) f.set_constraint('mean', limits=[1, 2]) assert f.constraints == { 'amplitude': dict(value=3, fixed=True, limits=None), 'mean': dict(value=2, fixed=False, limits=[1, 2]), 'stddev': dict(value=3, fixed=False, limits=None) } @requires_scipy class TestBasicGaussianFitter(object): def test(self): f = BasicGaussianFitter() x = np.linspace(-10, 10) y = np.exp(-x ** 2) r = f.build_and_fit(x, y) expected = [3.67879441e-01, 1.83156389e-02, 1.23409804e-04] np.testing.assert_array_almost_equal(f.predict(r, [1, 2, 3]), expected) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_coordinate_links.py0000644000175000017500000002117113612622074024377 0ustar noahfxnoahfximport numpy as np from numpy.testing import assert_allclose from glue.tests.helpers import requires_astropy, ASTROPY_INSTALLED from .. import Data, DataCollection from ..coordinates import IdentityCoordinates, coordinates_from_header from ..link_helpers import LinkSame from glue.core.coordinate_helpers import dependent_axes from glue.tests.helpers import make_file if ASTROPY_INSTALLED: from astropy.io import fits @requires_astropy def test_wcs_3d_to_2d(): """ For a "normal" XYV cube, linking XY world should be enough to propagate XY pixel """ d = Data(label='D1') with make_file(test_fits, suffix='.fits', decompress=True) as file: header = fits.getheader(file) d.coords = coordinates_from_header(header) d.add_component(np.zeros((3, 2, 1)), label='test') d2 = Data(label='D2') d2.coords = coordinates_from_header(header) d2.add_component(np.zeros((3, 2, 1)), label='test2') dc = DataCollection([d, d2]) dc.add_link(LinkSame(d.world_component_ids[1], d2.world_component_ids[1])) dc.add_link(LinkSame(d.world_component_ids[2], d2.world_component_ids[2])) py = d.pixel_component_ids[1] px = d.pixel_component_ids[2] py2 = d2.pixel_component_ids[1] px2 = d2.pixel_component_ids[2] np.testing.assert_array_almost_equal(d2[px], d2[px2]) np.testing.assert_array_almost_equal(d2[py], d2[py2]) @requires_astropy def test_link_velocity(): """ For a normal PPV cube, linking velocity world should be enough to get pixel V""" d = Data(label='D1') with make_file(test_fits, suffix='.fits', decompress=True) as file: header = fits.getheader(file) d.coords = coordinates_from_header(header) d.add_component(np.zeros((3, 2, 1)), label='test') d2 = Data(label='D2') d2.coords = coordinates_from_header(header) d2.add_component(np.zeros((3, 2, 1)), label='test2') dc = DataCollection([d, d2]) dc.add_link(LinkSame(d.world_component_ids[0], d2.world_component_ids[0])) pz = d.pixel_component_ids[0] pz2 = d2.pixel_component_ids[0] np.testing.assert_array_almost_equal(d2[pz], d2[pz2]) @requires_astropy class TestDependentAxes(object): def test_base(self): d = Data(x=[1, 2, 3], coords=IdentityCoordinates(n_dim=1)) assert dependent_axes(d.coords, 0) == (0,) d = Data(x=[[1, 2], [3, 4]], coords=IdentityCoordinates(n_dim=2)) assert dependent_axes(d.coords, 0) == (0,) assert dependent_axes(d.coords, 1) == (1,) def header2(self, proj='SIN'): result = fits.Header() result['NAXIS'] = 2 result['NAXIS1'] = 100 result['NAXIS2'] = 100 result['CRPIX1'] = 1 result['CRPIX2'] = 1 result['CDELT1'] = 1 result['CDELT2'] = 1 result['CTYPE1'] = 'RA---%s' % proj result['CTYPE2'] = 'DEC--%s' % proj result['CRVAL1'] = 1 result['CRVAL2'] = 1 return result def header3(self, proj='SIN'): result = self.header2(proj) result.update(NAXIS=3, NAXIS3=1, CDELT3=1, CRPIX3=3, CTYPE3='VOPT') return result def header4(self): result = fits.Header() result.update(WCSAXES=4, CRPIX1=513, CRPIX2=513, CRPIX3=1, CRPIX4=1, CDELT1=-6.94444444444E-05, CDELT2=6.94444444444E-05, CDELT3=10000.1667626, CDELT4=1, CTYPE1='RA---SIN', CTYPE2='DEC--SIN', CTYPE3='VOPT', CTYPE4='STOKES', CRVAL1=56.7021416715, CRVAL2=68.0961055596, CRVAL3=-280000.000241, CRVAL4=1, PV2_1=0, PV2_2=0, LONPOLE=180, LATPOLE=68.0961055596, RESTFRQ=34596380000, RADESYS='FK5', EQUINOX=2000, SPECSYS='BARYCENT') return result def test_wcs_ppv(self): header = self.header3() d = Data(label='D1') d.coords = coordinates_from_header(header) d.add_component(np.zeros((3, 2, 1)), label='test') assert dependent_axes(d.coords, 0) == (0,) assert dependent_axes(d.coords, 1) == (1, 2) assert dependent_axes(d.coords, 2) == (1, 2) def test_wcs_alma(self): header = self.header4() d = Data(label='D1') d.coords = coordinates_from_header(header) d.add_component(np.zeros((3, 2, 1, 1)), label='test') assert dependent_axes(d.coords, 0) == (0,) assert dependent_axes(d.coords, 1) == (1,) assert dependent_axes(d.coords, 2) == (2, 3) assert dependent_axes(d.coords, 3) == (2, 3) def test_wcs_nan(self): # Regression test for a bug that caused world to pixel conversion to # return NaN when it shouldn't header = self.header4() header['CRVAL1'] = 246 header['CRVAL2'] = -24 d1 = Data(label='D1') d1.coords = coordinates_from_header(header) d1.add_component(np.zeros((3, 2, 1, 1)), label='test') d2 = Data(label='D2') d2.coords = coordinates_from_header(header) d2.add_component(np.zeros((3, 2, 1, 1)), label='test') v1 = d1.world_component_ids[1] v2 = d2.world_component_ids[1] p1 = d1.pixel_component_ids[1] p2 = d2.pixel_component_ids[1] dc = DataCollection([d1, d2]) dc.add_link(LinkSame(v1, v2)) assert_allclose(d1[p1, (0, 0, 0, 0)], 0) assert_allclose(d2[p1, (0, 0, 0, 0)], 0) assert_allclose(d1[p2, (0, 0, 0, 0)], 0) assert_allclose(d2[p2, (0, 0, 0, 0)], 0) test_fits = b'x\x9c\xed\x97Qs\xa2H\x14\x85\xf7\xa7\xdc\xa75I\x05B\x83\xa0\xb8\x95\x07\xd462\x11q\xa0\xcdL\xe6%\x85\xd21T!X\x80\x93\xf1\xdf\xef\x05uuv\xccN\xc0O\x9f\xee\xa6o\xbb\xa65\x19Q\x80[8!\x0670\x8f\xa3\xe78Y\xa6\x90\xc500\x99\x0bi\xe6E\xbe\x97\xf8\xa7\x1e\x00\xe8\x9alb~=\xc9\x13\xb4&\xf2\xbc$\xf16\xe0{\x99\x07\xd9f\xc5OS\x0e\x1a\x1b_M\x17\xde\xf0\xa7 /Z/g<\x81\xf8yO\x0e\x96\x8e\xd7\xb3-\x8b\x8e\x19\x9e\x15\x9dw1\x08\xf9\x8f`\x16r0\x97\xde\x82\x03K\xbc(]\xc5I\x06\xee&\xcd\xf8\xf2\x12\xf2\xce\xf62\x08R\xf0\xf9s\x10q\x1f\x82\x08\x1aF\x9a%q\x14/7\x07\x1e\x8e\x02(.\xaf^6i0O\x1b\xd7\xf0=\x0e\xd7K\x0eJK\xbb\x86U\x8eWT\xfd/\x98\x05\xb3y\xec\xf3\x0e\xc8\x92D\x8c?\rQ\x14\xf1\x0e\xfcP\xf5!\xf4\rFs\x9f\xb7\xd0\xc0\x9f\x9b\x82\xa4\x08De2\xe9\xc8\xed\x8e,7\xb0\x83\x9f\x03t;O\xb8\x97a\xa7\xe6\x03\x87\xc3\xc5#J\xb0,\xa1\xdfg//\x9d\xe5\xb2\x93\xa60e\x97\xc8\xb1\xbb\x9fh\x8f\x15\xbc\tu\\:u\x8b\x18\x1a\xbb8n\xca\xe6\xc7\xe8\x88\xba={\x82\xbcA\xcf1l\x814\xad\xc6\xe1\xe7\xd2\xbds(}\xb7C\x1c.\x0f\x063\xed-O\xd6DM\xd1\x9b\xedv\x13\x91\x9a\xa2\x95\xe7=\x8c\\\'?\x9ex\x1f\x90\xa2\xbd\xf7\xd6M\x89\xf8\xa0;\x1d\x9b\xac\xe0\xe5\xc3\xee>\xbf\xf4\xf3\xf8c\xc6U\t\x1c\xb8\xf7\x8fO\x03\x87~\xde\xfa#\xe8\x89\xb4u\x89\xb4U]\x95\x9b*\xf2\xee\x86\xdf\xca\xf0F\xe6\x98\x1ex;\xe5XY"J\xab<\xaf\xfbe{<\x91\x9f\xac\xe6\xf9Y\xe5\xfd\x8d\x8db\xfe\x12\xa5g?\x11A:k\xfe\xf6\xd8\xe3\x84\x16\xebQ\xc31\x04Ap\x07\xa3\xf3x\xce\x831:^\xdf\xd4\x96\xa8\xa9\xfaN\x15x}:b{\x9e \x89\x92\xa4)m\x94.\xe5\xaa\xe2\x0f\xcb\x83c\x7fJ\xce\xdc\xabb~\xc5\xfa\xdb\xe8\xd3\xde\x07\xe5\xf7\xebz\xbe3Y1\xbf\x7fx\x1f\x94\xdf\x91?\xa1\xa9\xe9MQ\'\xca9\xf9\x15\xf5F\xe3\x81\x8el\x01_7\xe7\xe7wT\xbf\x08\xba\xaea\xab[\xba$\xb7Z\xad\x8a\xf9\x1d\xd7C\x9a&6eE#\xe7\xe4w\xaa\xbeB\xa3\xa2T\x96\x0604]f;\x8fp\xc7#\x9e`m\xe2\xc3l\x03E\xa5\x00\x0e_$\x81\xef\x07\xd1\x02&I\xbcH\xbc%\xe0\xda\xfc\x9b\xff\xd8\xf3\xba^\xcaC,\xbf\xc0]\xcf\xb2\xc4\x9b\xe7\xe4*\xda\xf3\n\xdd\x85\xf1\xcc\x0b\x0f\xec\x89\x87\xa6x\xc6\x93\xb4\x83\xb5e\x9c\xf8XH\xaf\xe2p\x83\x85^\x80\xf7\xfd\x17\xefK\x10\xf9\xf1+0,\xe1o\xbf\xf30\x9e\x07\xd9\xe6l\x7fE}\x9b\xcf\x11\xc8\xd7\xf3\xed\xb1"o\x1c\x07)\x87W\x1e,^\xb2\xbc\x07\xc8\xcd*~\xbdp\xcd;\xcb\xb8\x96/\xab\xf0`\x10b]<\x08xXt)n8\xfa\xf9&\xa6\xa2?w\x85\xf5,f<\x08B\xcc\xbf\x03\x9f\x82h~\xb5\xf0\xd6i\x1ax\xd1U\xfe\xad\x1c\xcf\x8cV\xeb\x0cl6\x00w\x8e%}\xa7\xac\xaf\x7f\xf3@ST-\xdf\xf3\xe1I\xab\xc2\xbec/:\xeeW\x7f\xb8V\xadZ\xb5j\xd5\xaa\xf5\xbf\xd4\x1f\xb5j\xd5\xaaU\xabV\xadZ\xb5j\xd5z\xb7\xfe\x06\xb6\x02\x94\xfe' glueviz-1.0.1+dfsg.orig/glue/core/tests/test_data.py0000644000175000017500000010622313663225250021764 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103,R0903,R0904 import pytest import numpy as np from numpy.testing import assert_equal, assert_allclose from unittest.mock import MagicMock from astropy.utils import NumpyRNGContext from glue import core from glue.utils import broadcast_to from ..component import Component, DerivedComponent, CategoricalComponent, DateTimeComponent from ..component_id import ComponentID from ..component_link import ComponentLink, CoordinateComponentLink, BinaryComponentLink from ..coordinates import Coordinates, IdentityCoordinates from ..data import Data, pixel_label, BaseCartesianData from ..link_helpers import LinkSame from ..data_collection import DataCollection from ..exceptions import IncompatibleAttribute from ..hub import Hub from ..registry import Registry from ..subset import (Subset, CategoricalROISubsetState, SubsetState, RoiSubsetState, RangeSubsetState, SliceSubsetState, CategoricalMultiRangeSubsetState, MaskSubsetState, CategoricalROISubsetState2D, AndState, roi_to_subset_state) from ..roi import PolygonalROI, CategoricalROI, RangeROI, RectangularROI from .test_state import clone class _TestCoordinates(Coordinates): def __init__(self): super().__init__(pixel_n_dim=2, world_n_dim=2) def pixel_to_world_values(self, *args): return [(i + 2.) * a for i, a in enumerate(args)] def world_to_pixel_values(self, *args): return [a / (i + 2.) for i, a in enumerate(args)] class TestData(object): def setup_method(self, method): self.data = Data(label="Test Data") Registry().clear() comp = Component(np.random.random((2, 3))) self.comp = comp self.data.coords = _TestCoordinates() self.comp_id = self.data.add_component(comp, 'Test Component') def test_2d_component_print(self): assert str(self.comp) == 'Component with shape (2, 3)' def test_shape_empty(self): d = Data() assert d.shape == () def test_ndim_empty(self): d = Data() assert d.ndim == 0 def test_shape(self): assert self.data.shape == (2, 3) def test_ndim(self): assert self.data.ndim == 2 def test_size(self): assert self.data.size == 6 def test_label(self): d = Data() assert d.label == '' assert self.data.label == "Test Data" def test_set_label(self): d = Data() d.label = 'test_set_label' assert d.label == 'test_set_label' def test_add_component_with_id(self): cid = ComponentID("test") comp = Component(np.random.random((2, 3))) cid2 = self.data.add_component(comp, cid) assert cid2 is cid def test_add_component_via_setitem(self): d = Data(x=[1, 2, 3]) d['y'] = d['x'] * 2 np.testing.assert_array_equal(d['y'], [2, 4, 6]) def test_add_component_incompatible_shape(self): comp = MagicMock() comp.data.shape = (3, 2) with pytest.raises(TypeError) as exc: self.data.add_component(comp("junk label")) assert exc.value.args[0] == ("add_component() missing 1 required " "positional argument: 'label'") def test_get_getitem_incompatible_attribute(self): cid = ComponentID('bad') with pytest.raises(IncompatibleAttribute) as exc: self.data.__getitem__(cid) assert exc.value.args[0] is cid def test_get_component_incompatible_attribute(self): cid = ComponentID('bad') with pytest.raises(IncompatibleAttribute) as exc: self.data.get_component(cid) assert exc.value.args[0] is cid def test_get_component_name(self): d = Data(x=[1, 2, 3]) assert isinstance(d.get_component('x'), Component) def test_component_ids(self): cid = self.data.component_ids() assert self.comp_id in cid def test_new_subset(self): sub = self.data.new_subset() assert sub in self.data.subsets def test_data_not_created_with_subsets(self): assert len(self.data.subsets) == 0 def test_register(self): hub = MagicMock(spec_set=Hub) self.data.register_to_hub(hub) assert hub is self.data.hub def test_component_order(self): """Components should be returned in the order they were specified""" data = Data() comp = Component(np.array([1, 2, 3])) labels = 'asldfkjaAREGWoibasiwnsldkgajsldkgslkg' for label in labels: data.add_component(comp, label) ids = data.main_components assert [cid.label for cid in ids] == list(labels) def test_broadcast(self): hub = MagicMock(spec_set=Hub) # make sure broadcasting with no hub is ok self.data.broadcast('testing') # make sure broadcast with hub gets relayed self.data.register_to_hub(hub) self.data.broadcast('testing') assert hub.broadcast.call_count == 1 def test_double_hub_add(self): hub = MagicMock(spec_set=Hub) hub2 = MagicMock(spec_set=Hub) self.data.register_to_hub(hub) with pytest.raises(AttributeError) as exc: self.data.__setattr__('hub', hub2) assert exc.value.args[0] == ("Data has already been assigned " "to a different hub") def test_main_components(self): compid = ComponentID('virtual') link = MagicMock(spec_set=ComponentLink) comp = DerivedComponent(self.data, link) self.data.add_component(comp, compid) main_comps = self.data.main_components assert self.comp_id in main_comps assert compid not in main_comps def test_add_component_invalid_component(self): comp = Component(np.array([1])) with pytest.raises(ValueError) as exc: self.data.add_component(comp, label='bad') assert exc.value.args[0].startswith("The dimensions of component bad") def test_add_component_link(self): link = MagicMock(spec_set=ComponentLink) cid = ComponentID("new id") link.get_to_id.return_value = cid self.data.add_component_link(link) assert cid in self.data.derived_components def test_derived_components(self): compid = ComponentID('virtual') link = MagicMock(spec_set=ComponentLink) comp = DerivedComponent(self.data, link) self.data.add_component(comp, compid) pricomps = self.data.derived_components assert self.comp_id not in pricomps assert compid in pricomps def test_str_empty(self): d = Data() str(d) def test_str_(self): str(self.data) def test_add_derived_component(self): compid = ComponentID('virtual') link = MagicMock(spec_set=ComponentLink) comp = DerivedComponent(self.data, link) comp.data.shape = self.data.shape self.data.add_component(comp, compid) result = self.data[compid] link.compute.assert_called_with(self.data) def test_find_component_id(self): cid = self.data.find_component_id('Test Component') assert cid == self.comp_id assert self.data.find_component_id('does not exist') is None def test_add_subset(self): s = Subset(Data()) self.data.add_subset(s) assert s in self.data.subsets def test_add_subset_with_subset_state(self): """Passing a subset state auto-wraps into a subset object""" state = SubsetState() self.data.add_subset(state) added = self.data.subsets[-1] assert added.subset_state is state assert added.data is self.data def test_add_subset_reparents_subset(self): """add_subset method updates subset.data reference""" s = Subset(None) self.data.add_subset(s) assert s.data is self.data def test_add_subset_disambiguates_label(self): """adding subset should disambiguate label if needed""" s1 = Subset(None) self.data.add_subset(s1) s1.label = "test_subset_label" s2 = Subset(None) s2.label = "test_subset_label" assert s2.label == "test_subset_label" self.data.add_subset(s2) assert s2.label != "test_subset_label" def test_add_subset_with_hub(self): s = Subset(None) hub = MagicMock(spec_set=Hub) self.data.register_to_hub(hub) self.data.add_subset(s) assert s in self.data.subsets assert hub.broadcast.call_count == 1 def test_remove_component(self): hub = MagicMock(spec_set=Hub) self.data.register_to_hub(hub) self.data.remove_component(self.comp_id) assert self.comp_id not in self.data.components assert hub.broadcast.call_count == 2 def test_get_component(self): assert self.data.get_component(self.comp_id) is self.comp def test_get_None_component(self): with pytest.raises(IncompatibleAttribute): self.data.get_component(None) def test_get_item(self): assert self.data[self.comp_id] is self.comp.data def test_coordinate_links(self): links = self.data.coordinate_links w0 = self.data[self.data.world_component_ids[0]] w1 = self.data[self.data.world_component_ids[1]] p0 = self.data[self.data.pixel_component_ids[0]] p1 = self.data[self.data.pixel_component_ids[1]] w0prime = links[0].compute(self.data) p0prime = links[1].compute(self.data) w1prime = links[2].compute(self.data) p1prime = links[3].compute(self.data) np.testing.assert_array_equal(w0, w0prime) np.testing.assert_array_equal(w1, w1prime) np.testing.assert_array_equal(p0, p0prime) np.testing.assert_array_equal(p1, p1prime) def test_coordinate_links_empty_data(self): d = Data() d.coords = None assert d.coordinate_links == [] def test_coordinate_links_idempotent(self): """Should only calculate links once, and return the same objects every time""" links = self.data.coordinate_links links2 = self.data.coordinate_links assert links == links2 def test_fancy_view(self): result = self.data[self.comp_id, :, 2] np.testing.assert_array_equal(result, self.data[self.comp_id][:, 2]) def test_get_by_string(self): result = self.data['Test Component'] assert result is self.comp.data def test_get_by_missing_string(self): with pytest.raises(IncompatibleAttribute) as exc: result = self.data['xyz'] assert exc.value.args[0] == 'xyz' def test_immutable(self): d = Data(x=[1, 2, 3]) with pytest.raises(ValueError) as exc: d['x'][:] = 5 assert 'read-only' in exc.value.args[0] assert not d['x'].flags['WRITEABLE'] @pytest.mark.xfail def test_categorical_immutable(self): d = Data() c = CategoricalComponent(['M', 'M', 'F'], categories=['M', 'F']) d.add_component(c, label='gender') with pytest.raises(ValueError) as exc: d['gender'][:] = 5 assert 'read-only' in exc.value.args[0] assert not d['gender'].flags['WRITEABLE'] def test_update_clears_subset_cache(self): from ..roi import RectangularROI d = Data(x=[1, 2, 3], y=[1, 2, 3]) s = d.new_subset() state = core.subset.RoiSubsetState() state.xatt = d.id['x'] state.yatt = d.id['y'] state.roi = RectangularROI(xmin=1.5, xmax=2.5, ymin=1.5, ymax=2.5) s.subset_state = state np.testing.assert_array_equal(s.to_mask(), [False, True, False]) d.update_components({d.id['x']: [10, 20, 30]}) np.testing.assert_array_equal(s.to_mask(), [False, False, False]) def test_add_derived_implicit(self): # Regression test for a bug that caused derived components added via # the data[...] = ... syntax to have links that did not include a 'to' # argument, leading the link manager to add a ghost component to the # data. from ..data_collection import DataCollection dc = DataCollection([]) data = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') dc.append(data) data['z'] = data.id['x'] + 1 # There should be four components: x, y, z, and pixel assert len(data.components) == 4 def test_remove_derived_dependency(self): # Regression test for a bug that occurred when removing a component # used in a derived component, which should also remove the derived # component itself. To make things more fun, we set up a chain of # derived components to make sure they are all removed. data = Data(a=[1, 2, 3], b=[2, 3, 4], label='data1') data['c'] = data.id['a'] + 1 data['d'] = data.id['c'] + 1 data['e'] = data.id['d'] + 1 data['f'] = data.id['e'] + 1 a_id = data.id['a'] b_id = data.id['b'] c_id = data.id['c'] d_id = data.id['d'] e_id = data.id['e'] f_id = data.id['f'] # There should be seven components: pixel, a, b, c, d, e, f assert len(data.components) == 7 data.remove_component(data.id['d']) # This should also remove e and f since they depend on d assert len(data.components) == 4 assert a_id in data.components assert b_id in data.components assert c_id in data.components assert d_id not in data.components assert e_id not in data.components assert f_id not in data.components def test_links_property(self): data = Data(a=[1, 2, 3], b=[2, 3, 4], label='data1', coords=IdentityCoordinates(n_dim=1)) assert len(data.links) == 2 assert isinstance(data.links[0], CoordinateComponentLink) assert isinstance(data.links[1], CoordinateComponentLink) data['c'] = data.id['a'] + 1 assert len(data.links) == 3 assert isinstance(data.links[2], BinaryComponentLink) class TestROICreation(object): def test_range_roi(self): d = Data(xdata=[1, 2, 3], ydata=[1, 2, 3]) comp = d.get_component(d.id['xdata']) roi = RangeROI('x', min=2, max=3) s = roi_to_subset_state(roi, x_att='xdata') assert isinstance(s, RangeSubsetState) np.testing.assert_array_equal((s.lo, s.hi), [2, 3]) roi = RangeROI('y', min=2, max=3) s = roi_to_subset_state(roi, x_att='xdata', y_att='ydata') assert isinstance(s, RangeSubsetState) assert s.att == 'ydata' def test_range_roi_categorical(self): d = Data(x=['a', 'b', 'c'], y=[1, 2, 3]) comp = d.get_component(d.id['x']) roi = CategoricalROI(['b', 'c']) s = roi_to_subset_state(roi, x_att=d.id['x'], x_categories=comp.categories) assert isinstance(s, CategoricalROISubsetState) np.testing.assert_array_equal((s.roi.contains(['a', 'b', 'c'], None)), [False, True, True]) roi = RangeROI('x', min=1, max=3) s = roi_to_subset_state(roi, x_att='x', x_categories=comp.categories) assert isinstance(s, CategoricalROISubsetState) np.testing.assert_array_equal((s.roi.contains(['a', 'b', 'c'], None)), [False, True, True]) def test_polygon_roi(self): d = Data(x=[1, 1.3, 3, 10], y=[1, 1.5, 3, 10]) roi = PolygonalROI([0, 0, 2, 2], [0, 2, 2, 0]) s = roi_to_subset_state(roi, x_att=d.id['x'], y_att=d.id['y']) assert isinstance(s, RoiSubsetState) np.testing.assert_array_equal(s.to_mask(d), [True, True, False, False]) def test_polygon_categorical_rectangular(self): d = Data(x=[1, 1.3, 3, 10], y=['a', 'b', 'c', 'd']) y_comp = d.get_component(d.id['y']) roi = PolygonalROI([0, 0, 2, 2], [0, 2, 2, 0]) s = roi_to_subset_state(roi, x_att='x', y_att='y', y_categories=y_comp.categories) assert isinstance(s, CategoricalMultiRangeSubsetState) np.testing.assert_array_equal(s.to_mask(d), [True, True, False, False]) def test_polygon_categorical_arbitrary(self): d = Data(x=[1, 1.3, 3, 10], y=['a', 'b', 'c', 'd']) y_comp = d.get_component(d.id['y']) roi = PolygonalROI([0, 4, 4, 1, 0], [-0.5, 3.5, 0, -1, -0.5]) s = roi_to_subset_state(roi, x_att='x', y_att='y', y_categories=y_comp.categories) assert isinstance(s, CategoricalMultiRangeSubsetState) np.testing.assert_array_equal(s.to_mask(d), [True, False, True, False]) def test_rectangular_categorical(self): d = Data(x=[1, 1.3, 3, 10], y=['a', 'b', 'c', 'd']) x_comp = d.get_component(d.id['x']) y_comp = d.get_component(d.id['y']) roi = RectangularROI(xmin=-0.1, xmax=2.1, ymin=-0.1, ymax=2.1) s = roi_to_subset_state(roi, x_att='x', y_att='y', y_categories=y_comp.categories) assert isinstance(s, AndState) np.testing.assert_array_equal(s.to_mask(d), [True, True, False, False]) s = roi_to_subset_state(roi, x_att='x', y_att='y', y_categories=y_comp.categories) assert isinstance(s, AndState) np.testing.assert_array_equal(s.to_mask(d), [True, True, False, False]) def test_polygon_both_categorical_arbitrary(self): d = Data(x=['a', 'b', 'c', 'd', 'b', 'c'], y=['p', 'q', 'r', 's', 's', 'q']) x_comp = d.get_component(d.id['x']) y_comp = d.get_component(d.id['y']) roi = PolygonalROI([0.5, 1.5, 2.5, 1, 0.5], [0.5, 0.5, 2.5, 3.5, 0.5]) s = roi_to_subset_state(roi, x_att='x', x_categories=x_comp.categories, y_att='y', y_categories=y_comp.categories) assert isinstance(s, CategoricalROISubsetState2D) np.testing.assert_array_equal(s.to_mask(d), [False, True, True, False, True, False]) def test_polygon_both_categorical_empty(self): d = Data(x=['a', 'b', 'c', 'd', 'b', 'c'], y=['p', 'q', 'r', 's', 's', 'q']) x_comp = d.get_component(d.id['x']) y_comp = d.get_component(d.id['y']) roi = PolygonalROI([0.5, 0.6, 0.6, 0.5], [0.5, 0.5, 0.6, 0.5]) s = roi_to_subset_state(roi, x_att='x', x_categories=x_comp.categories, y_att='y', y_categories=y_comp.categories) assert isinstance(s, CategoricalROISubsetState2D) np.testing.assert_array_equal(s.to_mask(d), [False, False, False, False, False, False]) def test_component_id_item_access(): data = Data() c1 = Component(np.array([1, 2, 3])) data.add_component(c1, 'values') c2 = Component(np.array([4., 5., 6.])) data.add_component(c2, 'Flux') assert data.id['values'] == data.find_component_id('values') assert data.id['Flux'] == data.find_component_id('Flux') def test_component_id_item_access_missing(): """id attribute should raise KeyError if requesting a bad ComponentID""" data = Data() with pytest.raises(KeyError): data.id['not found'] class TestPixelLabel(object): def test(self): assert pixel_label(0, 2) == "0 [y]" assert pixel_label(1, 2) == "1 [x]" assert pixel_label(0, 3) == "0 [z]" assert pixel_label(1, 3) == "1 [y]" assert pixel_label(2, 3) == "2 [x]" assert pixel_label(1, 0) == "1" assert pixel_label(1, 4) == "1" @pytest.mark.parametrize(('kwargs'), [{'x': [1, 2, 3]}, {'x': np.array([1, 2, 3])}, {'x': [[1, 2, 3], [2, 3, 4]]}, {'x': [1, 2], 'y': [2, 3]}]) def test_init_with_inputs(kwargs): """Passing array-like objects as keywords to Data auto-populates Components with label names = keywords""" d = Data(**kwargs) for label, data in kwargs.items(): np.testing.assert_array_equal(d[d.id[label]], data) def test_init_with_invalid_kwargs(): with pytest.raises(ValueError) as exc: d = Data(x=[1, 2], y=[1, 2, 3]) assert exc.value.args[0].startswith('The dimensions of component') def test_getitem_with_component_link(): d = Data(x=[1, 2, 3, 4]) y = d.id['x'] * 5 np.testing.assert_array_equal(d[y], [5, 10, 15, 20]) def test_getitem_with_component_link_and_slice(): d = Data(x=[1, 2, 3, 4]) y = d.id['x'] * 5 np.testing.assert_array_equal(d[y, ::2], [5, 15]) def test_add_link_with_binary_link(): d = Data(x=[1, 2, 3, 4], y=[4, 5, 6, 7]) z = d.id['x'] + d.id['y'] d.add_component_link(z, 'z') np.testing.assert_array_equal(d[d.id['z']], [5, 7, 9, 11]) def test_foreign_pixel_components_not_in_visible(): """Pixel components from other data should not be visible""" # currently, this is trivially satisfied since all coordinates are hidden d1 = Data(x=[1], y=[2], coords=IdentityCoordinates(n_dim=1)) d2 = Data(w=[3], v=[4], coords=IdentityCoordinates(n_dim=1)) dc = DataCollection([d1, d2]) dc.add_link(LinkSame(d1.id['x'], d2.id['w'])) dc.add_link(LinkSame(d1.world_component_ids[0], d2.world_component_ids[0])) assert d2.pixel_component_ids[0] not in d1.main_components np.testing.assert_array_equal(d1[d2.pixel_component_ids[0]], [0]) def test_add_binary_component(): d = Data(x=[1, 2, 3], y=[2, 3, 4]) z = d.id['x'] + d.id['y'] d.add_component(z, label='z') np.testing.assert_array_equal(d['z'], [3, 5, 7]) EXPECTED_STR = """ Data Set: mydata Number of dimensions: 1 Shape: 3 Main components: - x - y Coordinate components: - Pixel Axis 0 [x] """.strip() def test_data_str(): # Regression test for Data.__str__ d = Data(x=[1, 2, 3], y=[2, 3, 4], label='mydata') assert str(d) == EXPECTED_STR EXPECTED_STR_WITH_DERIVED = """ Data Set: mydata Number of dimensions: 1 Shape: 3 Main components: - x - y Derived components: - z Coordinate components: - Pixel Axis 0 [x] """.strip() def test_data_str_with_derived(): d = Data(x=[1, 2, 3], y=[2, 3, 4], label='mydata') d['z'] = d.id['x'] + 1 assert str(d) == EXPECTED_STR_WITH_DERIVED def test_update_values_from_data(): d1 = Data(a=[1, 2, 3], b=[4, 5, 6], label='banana') d2 = Data(b=[1, 2, 3, 4], c=[5, 6, 7, 8], label='apple') d1a = d1.id['a'] d1b = d1.id['b'] d2b = d2.id['b'] d2c = d2.id['c'] d1.update_values_from_data(d2) assert d1a not in d1.components assert d1b in d1.components assert d2b not in d1.components assert d2c not in d1.components assert [cid.label for cid in d1.main_components] == ['b', 'c'] assert d1.shape == (4,) def test_update_values_from_data_invalid(): d1 = Data(a=[1, 2, 3], label='banana') d1.add_component([3, 4, 5], 'a') d2 = Data(b=[1, 2, 3, 4], c=[5, 6, 7, 8], label='apple') with pytest.raises(ValueError) as exc: d1.update_values_from_data(d2) assert exc.value.args[0] == "Non-unique component labels in original data" d1 = Data(a=[1, 2, 3], b=[4, 5, 6], label='banana') d2 = Data(b=[1, 2, 3, 4], label='apple') d2.add_component([5, 6, 7, 8], 'b') with pytest.raises(ValueError) as exc: d1.update_values_from_data(d2) assert exc.value.args[0] == "Non-unique component labels in new data" def test_update_values_from_data_order(): # Make sure that the order of components is preserved when calling # Data.update_values_from_data. The final order should be first # components that existed before, followed by new components d1 = Data() d1['c'] = [1, 2, 3] d1['b'] = [2, 3, 4] d1['j'] = [0, 1, 2] d1['a'] = [4, 4, 4] d1['f'] = [4, 5, 6] d2 = Data() d2['h'] = [4, 4, 4] d2['j'] = [0, 1, 2] d2['a'] = [4, 4, 4] d2.update_values_from_data(d1) assert [cid.label for cid in d2.main_components] == ['j', 'a', 'c', 'b', 'f'] def test_find_component_id_with_cid(): # Regression test for a bug that caused Data.find_component_id to return # True erroneously when passing a component ID. d1 = Data() d1['a'] = ['a', 'b', 'c'] d1['b'] = [1, 2, 3] assert d1.find_component_id(d1.id['a']) is d1.id['a'] assert d1.find_component_id(d1.id['b']) is d1.id['b'] def test_parent_preserved_session(): # Regression test for a bug that caused ComponentID parents to not be # preserved when saving and restoring a session. from ..link_helpers import LinkSame from ..data_collection import DataCollection d1 = Data(x=[1], y=[2], label='test1') d2 = Data(w=[3], v=[4], label='test2') dc = DataCollection([d1, d2]) dc.add_link(LinkSame(d1.id['x'], d2.id['w'])) assert d1.id['x'].parent is d1 assert d1.id['y'].parent is d1 assert d2.id['w'].parent is d2 assert d2.id['v'].parent is d2 dc2 = clone(dc) assert dc2[0].id['x'].parent.label == 'test1' assert dc2[0].id['y'].parent.label == 'test1' assert dc2[1].id['w'].parent.label == 'test2' assert dc2[1].id['v'].parent.label == 'test2' def test_preserve_datetime(): # Make sure that we recognize and preserve the Numpy datetime64 format dates = np.array([1, 2, 3], dtype='M8[D]') data = Data(dates=dates) assert isinstance(data.get_component('dates'), DateTimeComponent) def test_clone_meta(): # Regression test for a bug that caused metadata to not be preserved # when saving/loading sessions. class CustomObject(object): pass data1 = Data(x=[1, 2, 3]) data1.meta['a'] = 1 data1.meta['b'] = 'test' data1.meta['c'] = CustomObject() data2 = clone(data1) assert data2.meta['a'] == 1 assert data2.meta['b'] == 'test' assert 'c' not in data2.meta def test_update_coords(): # Make sure that when overriding coords, the world coordinate components # are updated. data1 = Data(x=[1, 2, 3], coords=IdentityCoordinates(n_dim=1)) assert len(data1.components) == 3 assert_equal(data1[data1.world_component_ids[0]], [0, 1, 2]) data2 = Data(x=[1, 2, 3], coords=IdentityCoordinates(n_dim=1)) assert len(data1.links) == 2 assert len(data2.links) == 2 data_collection = DataCollection([data1, data2]) assert len(data_collection.links) == 4 data_collection.add_link(LinkSame(data1.world_component_ids[0], data2.world_component_ids[0])) assert len(data_collection.links) == 5 class CustomCoordinates(Coordinates): def __init__(self): super().__init__(pixel_n_dim=1, world_n_dim=1) @property def world_axis_names(self): return ['Custom {0}'.format(axis) for axis in range(3)] def world_to_pixel_values(self, *world): return tuple([0.4 * w for w in world]) def pixel_to_world_values(self, *pixel): return tuple([2.5 * p for p in pixel]) data1.coords = CustomCoordinates() assert len(data1.components) == 3 assert_equal(data1[data1.world_component_ids[0]], [0, 2.5, 5]) assert sorted(cid.label for cid in data1.world_component_ids) == ['Custom 0'] # The link between the two world coordinates should be remove assert len(data_collection.links) == 4 def test_compute_statistic_subset(): data = Data(x=list(range(10))) result = data.compute_statistic('mean', data.id['x'], subset_state=data.id['x'] > 5) assert_allclose(result, 7.5) subset_state = SliceSubsetState(data, [slice(5)]) result = data.compute_statistic('mean', data.id['x'], subset_state=subset_state) assert_allclose(result, 2.0) @pytest.mark.parametrize('shape', (100, (30, 10), (500, 1, 30))) def test_compute_statistic_chunks(shape): # Make sure that when using chunks, the result is the same as without. data = Data(x=np.random.random(shape)) axis = tuple(range(data.ndim - 1)) assert_allclose(data.compute_statistic('mean', data.id['x'], axis=axis), data.compute_statistic('mean', data.id['x'], axis=axis, n_chunk_max=10)) def test_compute_statistic_random_subset(): data = Data(x=list(range(10))) with NumpyRNGContext(12345): result = data.compute_statistic('mean', data.id['x'], random_subset=5) assert_allclose(result, 4.2) result = data.compute_statistic('mean', data.id['x'], random_subset=5, subset_state=MaskSubsetState([0, 1, 0, 1, 1, 1, 0, 1, 0, 1], data.pixel_component_ids)) assert_allclose(result, 5) def test_compute_statistic_empty_subset(): data = Data(x=np.empty((30, 20, 40))) # A default subset state should be empty subset_state = SubsetState() result = data.compute_statistic('mean', data.id['x'], subset_state=subset_state) assert_equal(result, np.nan) result = data.compute_statistic('maximum', data.id['x'], subset_state=subset_state, axis=1) assert_equal(result, broadcast_to(np.nan, (30, 40))) result = data.compute_statistic('median', data.id['x'], subset_state=subset_state, axis=(1, 2)) assert_equal(result, broadcast_to(np.nan, (30))) result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, axis=(0, 1, 2)) assert_equal(result, np.nan) def test_compute_statistic_efficient(): # Unit test to test the logic for dealing with accessing only a minimal # region from the data based on the smallest array that covers a given # subset state. array = np.ones(10 * 20 * 30 * 40).reshape((10, 20, 40, 30)) array[3:5, 6:14, :, 10:21:2] += 1 class CustomData(Data): def get_data(self, cid, view=None): if cid.label == 'x': self.elements_accessed = np.ones(self.shape)[view].sum() else: self.elements_accessed = 0 return super().get_data(cid, view=view) data = CustomData(x=array, y=array) subset_state = data.id['y'] > 1.5 # First test without view result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state) assert_allclose(result, 7680) assert data.elements_accessed == 7040 # Now apply a view which includes just one slice that covers the original area result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, view=[slice(0, 5)]) assert_allclose(result, 7680) assert data.elements_accessed == 7040 # Make it so that the slice doesn't fully overlap with the subset result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, view=[slice(0, 4)]) assert_allclose(result, 3840) assert data.elements_accessed == 3520 # And now make it so there is no overlap # TODO: should this result be 0 instead of nan? result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, view=[slice(0, 3)]) assert_allclose(result, np.nan) assert data.elements_accessed == 0 # Check what happens if we use an integer index that overlaps... result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, view=[3]) assert_allclose(result, 3840) assert data.elements_accessed == 3520 # ... and one that doesn't # TODO: should this result be 0 instead of nan? result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, view=[2]) assert_allclose(result, np.nan) assert data.elements_accessed == 0 # Now try using a slice that has a step>1 - this should actually then # bypass the efficient algorithm result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, view=[slice(0, 5, 2)]) assert_allclose(result, 3840) assert data.elements_accessed == 72000 # Finally we can do a complex mix of options result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, view=(slice(0, 5), slice(3, 10), 20, slice(None))) assert_allclose(result, 96) assert data.elements_accessed == 88 def test_compute_statistic_shape(): # The compute_statistic method has some code that helps it be more efficient # with subsets, but we need to make sure that the final result has the same # shape as if we didn't have those optimizations. array = np.ones(10 * 20 * 30).reshape((10, 20, 30)) array[3:5, 6:14, 10:21] += 1 data = Data(x=array, y=array) subset_state = data.id['y'] > 1.5 result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state) assert np.isscalar(result) result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, axis=1) assert result.shape == (10, 30) result = data.compute_statistic('sum', data.id['x'], subset_state=subset_state, axis=(0, 2)) assert result.shape == (20,) def test_compute_histogram_log(): # Make sure that the returned histogram is NaN everywhere if either of the # limits are negative in log mode data = Data(x=np.ones(10), y=np.ones(10)) result = data.compute_histogram([data.id['x']], range=[[0.5, 2.5]], bins=[2]) assert_allclose(result, [10, 0]) data = Data(x=np.ones(10), y=np.ones(10)) result = data.compute_histogram([data.id['x']], range=[[0.5, 2.5]], bins=[2], log=[True]) assert_allclose(result, [10, 0]) data = Data(x=np.ones(10), y=np.ones(10)) result = data.compute_histogram([data.id['x']], range=[[-0.5, 2.5]], bins=[2], log=[True]) assert result.shape == (2,) and np.sum(result) == 0 data = Data(x=np.ones(10), y=np.ones(10)) result = data.compute_histogram([data.id['x'], data.id['y']], range=[[-0.5, 3], [-3, 5]], bins=[2, 3], log=[True, True]) assert result.shape == (2, 3) and np.sum(result) == 0 data = Data(x=np.ones(10), y=np.ones(10)) result = data.compute_histogram([data.id['x'], data.id['y']], range=[[1, 3], [-3, 5]], bins=[2, 3], log=[True, True]) assert result.shape == (2, 3) and np.sum(result) == 0 def test_compute_histogram_dask(): # Make sure that compute_histogram works for dask arrays da = pytest.importorskip('dask.array') data = Data(x=da.arange(10)) result = data.compute_histogram([data.id['x']], range=[[-0.5, 11.75]], bins=[2]) assert_allclose(result, [6, 4]) result = data.compute_histogram([data.id['x']], range=[[-0.5, 11.25]], bins=[2], subset_state=data.id['x'] > 4.5) assert_allclose(result, [1, 4]) def test_base_cartesian_data_coords(): # Make sure that world_component_ids works in both the case where # coords is not defined and when it is defined. class CustomData(BaseCartesianData): def get_kind(self): pass def compute_histogram(self): pass def compute_statistic(self): pass def get_mask(self): pass @property def shape(self): return (1, 4, 3) @property def main_components(self): return [] data1 = CustomData() assert len(data1.pixel_component_ids) == 3 assert len(data1.world_component_ids) == 0 data2 = CustomData(coords=IdentityCoordinates(n_dim=3)) assert len(data2.pixel_component_ids) == 3 assert len(data2.world_component_ids) == 3 glueviz-1.0.1+dfsg.orig/glue/core/tests/test_decorators.py0000644000175000017500000000405313605357235023223 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103, R0903 from ..decorators import singleton, memoize, memoize_attr_check @singleton class SingletonOne(object): """test docstring""" pass @singleton class SingletonTwo(object): pass class MemoAtt(object): def __init__(self): self.target = 1 self.trigger = 0 @memoize_attr_check('trigger') def test(self): return self.target @memoize_attr_check('trigger') def test_kwarg(self, x=0): return self.target + x def test_singleton(): f = SingletonOne() g = SingletonOne() h = SingletonTwo() k = SingletonTwo() assert f is g assert h is k assert f is not h def test_memoize(): class Bar(object): pass @memoize def func(x): return x.att b = Bar() b.att = 5 assert func(b) == 5 b.att = 7 assert func(b) == 5 # should return memoized func def test_memoize_unhashable(): @memoize def func(x, view=None): return 2 * x assert func(1, view=slice(1, 2, 3)) == 2 assert func(1, view=slice(1, 2, 3)) == 2 def test_memoize_attribute(): f = MemoAtt() assert f.test() == 1 f.target = 2 assert f.test() == 1 f.trigger = 1 assert f.test() == 2 def test_decorators_maintain_docstrings(): assert SingletonOne.__doc__ == "test docstring" @memoize def test(): """test docstring""" assert test.__doc__ == "test docstring" class MemoClass(object): @memoize_attr_check('test') def test(self): """123""" pass assert MemoClass.test.__doc__ == "123" def test_memoize_kwargs(): @memoize def memoadd(x, y=0): return x + y assert memoadd(3) == 3 assert memoadd(3, 2) == 5 assert memoadd(3, y=3) == 6 def test_memoize_attribute_kwargs(): f = MemoAtt() assert f.test_kwarg() == 1 assert f.test_kwarg(x=5) == 6 f.target = 2 assert f.test_kwarg() == 1 f.trigger = 1 assert f.test_kwarg() == 2 assert f.test_kwarg(x=6) == 8 glueviz-1.0.1+dfsg.orig/glue/core/tests/test_data_collection.py0000644000175000017500000004260613613550536024206 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import pytest import numpy as np from unittest.mock import MagicMock from numpy.testing import assert_array_equal, assert_equal from ..coordinates import IdentityCoordinates from ..component_link import ComponentLink from ..link_helpers import LinkSame from ..data import Data, Component, ComponentID, DerivedComponent from ..data_collection import DataCollection from ..hub import HubListener from ..message import (Message, DataCollectionAddMessage, DataRemoveComponentMessage, DataCollectionDeleteMessage, DataAddComponentMessage, ComponentsChangedMessage, PixelAlignedDataChangedMessage) from ..exceptions import IncompatibleAttribute from .test_state import clone class HubLog(HubListener): def __init__(self): self.messages = [] def register_to_hub(self, hub): hub.subscribe(self, Message) def notify(self, message): self.messages.append(message) def clear(self): self.messages[:] = [] def messages_by_type(self, klass): return [msg for msg in self.messages if isinstance(msg, klass)] class TestDataCollection(object): def setup_method(self, method): self.dc = DataCollection() self.data = MagicMock(spec_set=Data) self.hub = self.dc.hub self.log = HubLog() self.log.register_to_hub(self.hub) def test_init_scalar(self): """Single data object passed to init adds to collection""" d = Data() dc = DataCollection(d) assert d in dc def test_init_list(self): """List of data objects passed to init auto-added to collection""" d1 = Data() d2 = Data() dc = DataCollection([d1, d2]) assert d1 in dc assert d2 in dc def test_data(self): """ data attribute is a list of all appended data""" self.dc.append(self.data) assert self.dc.data == [self.data] def test_append(self): """ append method adds to collection """ self.dc.append(self.data) assert self.data in self.dc def test_multi_append(self): """ append method works with lists """ d = Data('test1', x=[1, 2, 3]) d2 = Data('test2', y=[2, 3, 4]) self.dc.append([d, d2]) assert d in self.dc assert d2 in self.dc def test_ignore_multi_add(self): """ data only added once, even after multiple calls to append """ self.dc.append(self.data) self.dc.append(self.data) assert len(self.dc) == 1 def test_remove(self): self.dc.append(self.data) self.dc.remove(self.data) assert self.data not in self.dc def test_clear(self): self.dc.append(self.data) self.dc.clear() assert self.data not in self.dc def test_ignore_multi_remove(self): self.dc.append(self.data) self.dc.remove(self.data) self.dc.remove(self.data) assert self.data not in self.dc def test_append_broadcast(self): """ Call to append generates a DataCollectionAddMessage """ self.dc.append(self.data) msg = self.log.messages[-1] assert msg.sender == self.dc assert isinstance(msg, DataCollectionAddMessage) assert msg.data is self.data def test_remove_broadcast(self): """ call to remove generates a DataCollectionDeleteMessage """ self.dc.append(self.data) self.dc.remove(self.data) msg = self.log.messages[-1] assert msg.sender == self.dc assert isinstance(msg, DataCollectionDeleteMessage) assert msg.data is self.data def test_register_assigns_hub_of_data(self): self.dc.append(self.data) self.data.register_to_hub.assert_called_once_with(self.hub) def test_get_item(self): self.dc.append(self.data) assert self.dc[0] is self.data def test_get_item_str(self): data = Data(label='test') self.dc.append(data) assert self.dc['test'] is data with pytest.raises(ValueError) as exc: self.dc['spam'] assert exc.value.args[0] == "No data found with the label 'spam'" data2 = Data(label='test') self.dc.append(data2) with pytest.raises(ValueError) as exc: self.dc['test'] assert exc.value.args[0] == "Several datasets were found with the label 'test'" def test_iter(self): self.dc.append(self.data) assert set(self.dc) == set([self.data]) def test_len(self): assert len(self.dc) == 0 self.dc.append(self.data) assert len(self.dc) == 1 self.dc.append(self.data) assert len(self.dc) == 1 self.dc.remove(self.data) assert len(self.dc) == 0 def test_derived_links_autoadd(self): """ When appending a data set, its DerivedComponents should be ingested into the LinkManager """ d = Data() id1 = ComponentID("id1") id2 = ComponentID("id2") link = ComponentLink([id1], id2) dc = DerivedComponent(d, link) d.add_component(Component(np.array([1, 2, 3])), id1) d.add_component(dc, id2) dc = DataCollection() dc.append(d) assert link in dc._link_manager def test_catch_data_add_component_message(self): """ DerviedAttributes added to a dataset in a collection should generate messages that the collection catches. """ d = Data() id1 = ComponentID("id1") id2 = ComponentID("id2") link = ComponentLink([id1], id2) dc = DerivedComponent(d, link) self.dc.append(d) d.add_component(Component(np.array([1, 2, 3])), id1) assert link not in self.dc._link_manager self.log.clear() d.add_component(dc, id2) assert link in self.dc._link_manager msgs = sorted(self.log.messages, key=lambda x: str(type(x))) assert isinstance(msgs[0], ComponentsChangedMessage) assert isinstance(msgs[1], DataAddComponentMessage) def test_links_auto_added(self): id1 = ComponentID("id1") id2 = ComponentID("id2") link = ComponentLink([id1], id2) self.data.links = [link] self.dc.append(self.data) assert link in self.dc.links def test_add_links(self): """ links attribute behaves like an editable list """ id1 = ComponentID("id1") id2 = ComponentID("id2") link = ComponentLink([id1], id2) self.dc.set_links([link]) assert link in self.dc.links def test_add_links_updates_components(self): """ Setting links attribute automatically adds components to data """ d = Data() comp = Component(np.array([1, 2, 3])) id1 = ComponentID("id1") d.add_component(comp, id1) id2 = ComponentID("id2") self.dc.append(d) link = ComponentLink([id1], id2) self.dc.set_links([link]) assert_equal(d[id2], d[id1]) def test_links_propagated(self): """Web of links is grown and applied to data automatically""" from ..component_link import ComponentLink d = Data() dc = DataCollection([d]) cid1 = d.add_component(np.array([1, 2, 3]), 'a') cid2 = ComponentID('b') cid3 = ComponentID('c') links1 = ComponentLink([cid1], cid2) dc.add_link(links1) assert_equal(d[cid2], d[cid1]) links2 = ComponentLink([cid2], cid3) dc.add_link(links2) assert_equal(d[cid3], d[cid2]) dc.remove_link(links2) with pytest.raises(IncompatibleAttribute): d[cid3] assert_equal(d[cid2], d[cid1]) dc.remove_link(links1) with pytest.raises(IncompatibleAttribute): d[cid2] def test_merge_links(self): """Trivial links should be merged, discarding the duplicate ID""" d1 = Data(x=[1, 2, 3]) d2 = Data(x=[2, 3, 4]) dc = DataCollection([d1, d2]) original_id = d2.id['x'] link = ComponentLink([d1.id['x']], d2.id['x']) dc.add_link(link) # NOTE: the behavior tested here is not desirable anymore, so the relevant # parts that are no longer true have been commented out and replaced # by the new behavior. # assert d1.id['x'] is not original_id # assert duplicated_id not in d2.components assert d1.id['x'] is not original_id assert d2.id['x'] is original_id assert original_id in d2.components assert_array_equal(d1[d1.id['x']], [1, 2, 3]) assert_array_equal(d2[d1.id['x']], [2, 3, 4]) def test_merge(self): x = Data(x=[1, 2, 3]) y = Data(y=[2, 3, 4]) dc = DataCollection([x, y]) dc.merge(x, y) assert x not in dc assert y not in dc assert_array_equal(dc[0]['x'], [1, 2, 3]) assert_array_equal(dc[0]['y'], [2, 3, 4]) def test_merge_discards_duplicate_pixel_components(self): x = Data(x=[1, 2, 3]) y = Data(y=[2, 3, 4]) dc = DataCollection([x, y]) dc.merge(x, y) merged = dc[0] assert len(merged.coordinate_components) == 1 assert y.pixel_component_ids[0] not in merged.components def test_merge_forbids_single_argument(self): x = Data(x=[1, 2, 3]) y = Data(y=[2, 3, 4]) dc = DataCollection([x, y]) with pytest.raises(ValueError) as exc: dc.merge(x) assert exc.value.args[0] == 'merge requires 2 or more arguments' def test_merge_requires_same_shapes(self): x = Data(x=[1, 2, 3]) y = Data(y=[2, 3, 4, 5]) dc = DataCollection([x, y]) with pytest.raises(ValueError) as exc: dc.merge(x, y) assert exc.value.args[0] == 'All arguments must have the same shape' def test_merge_disambiguates_components(self): x = Data(x=[1, 2, 3]) old = set(x.components) y = Data(x=[2, 3, 4]) dc = DataCollection([x, y]) dc.merge(x, y) z = dc[0] new = list(set(z.components) - old)[0] assert new.label != 'x' def test_merge_multiargument(self): dc = DataCollection([Data(x=[1, 2, 3]), Data(y=[2, 3, 4]), Data(z=[3, 4, 5])]) dc.merge(*list(dc)) assert len(dc) == 1 d = dc[0] assert_array_equal(d['y'], [2, 3, 4]) assert_array_equal(d['z'], [3, 4, 5]) def test_merging_preserves_links_forwards(self): a = Data(a=[1, 2, 3]) b = Data(b=[2, 3, 4]) c = Data(c=[3, 4, 5]) dc = DataCollection([a, b, c]) dc.add_link(ComponentLink([a.id['a']], b.id['b'], lambda x: x)) dc.add_link(ComponentLink([b.id['b']], c.id['c'], lambda x: x)) assert_array_equal(a['c'], [1, 2, 3]) dc.merge(a, b) assert_array_equal(a['c'], [1, 2, 3]) def test_merging_preserves_links_backwards(self): a = Data(a=[1, 2, 3], label='Data A') b = Data(b=[2, 3, 4], label='Data B') c = Data(c=[3, 4, 5], label='Data C') dc = DataCollection([a, b, c]) dc.add_link(ComponentLink([c.id['c']], b.id['b'], lambda x: x)) dc.add_link(ComponentLink([b.id['b']], a.id['a'], lambda x: x)) assert_array_equal(c['a'], [3, 4, 5]) dc.merge(a, b) assert_array_equal(c['a'], [3, 4, 5]) def test_merge_coordinates(self): # Regression test to make sure the coordinates from the first dataset # are preserved. x = Data(x=[1, 2, 3]) y = Data(y=[2, 3, 4]) dc = DataCollection([x, y]) x.coords = IdentityCoordinates(n_dim=1) y.coords = IdentityCoordinates(n_dim=1) dc.merge(x, y) assert dc[0].coords is x.coords def test_merge_coordinates_preserve_labels(self): # Regression test to make sure that axis labels are preserved after # merging. x = Data(x=[1, 2, 3]) y = Data(y=[2, 3, 4]) dc = DataCollection([x, y]) class CustomCoordinates(IdentityCoordinates): @property def world_axis_names(self): return ['Custom {0}'.format(axis) for axis in range(self.world_n_dim)] x.coords = CustomCoordinates(n_dim=1) y.coords = CustomCoordinates(n_dim=1) dc.merge(x, y) assert sorted(cid.label for cid in dc[0].world_component_ids) == ['Custom 0'] def test_merge_visible_components(self): # Regression test for a bug that caused visible_components to be empty # for a dataset made from merging other datasets. x = Data(x=[1, 2, 3], label='dx') y = Data(y=[2, 3, 4], label='dy') dc = DataCollection([x, y]) dc.merge(x, y) assert dc[0].main_components[0] is x.id['x'] assert dc[0].main_components[1] is y.id['y'] def test_remove_component_message(self): # Regression test to make sure that removing a component emits the # appropriate messages. data = Data(x=[1, 2, 3], y=[4, 5, 6]) self.dc.append(data) remove_id = data.id['y'] self.log.clear() data.remove_component(remove_id) msgs = sorted(self.log.messages, key=lambda x: str(type(x))) print([type(msg) for msg in msgs]) assert isinstance(msgs[0], ComponentsChangedMessage) assert isinstance(msgs[1], DataRemoveComponentMessage) assert msgs[1].component_id is remove_id def test_links_preserved_session(self): # This tests that the separation of internal vs external links is # preserved in session files. d1 = Data(a=[1, 2, 3], coords=IdentityCoordinates(n_dim=1)) d2 = Data(b=[2, 3, 4], coords=IdentityCoordinates(n_dim=1)) dc = DataCollection([d1, d2]) dc.add_link(ComponentLink([d2.id['b']], d1.id['a'])) d1['x'] = d1.id['a'] + 1 assert len(d1.coordinate_links) == 2 assert len(d1.derived_links) == 1 assert len(dc._link_manager._external_links) == 1 dc2 = clone(dc) assert len(dc2[0].coordinate_links) == 2 assert len(dc2[0].derived_links) == 1 assert len(dc2._link_manager._external_links) == 1 def test_pixel_aligned(self): data1 = Data(x=np.ones((2, 4, 3))) data2 = Data(y=np.ones((4, 3, 2))) data3 = Data(z=np.ones((2, 3))) self.dc.extend([data1, data2, data3]) # Add one set of links, which shouldn't change anything self.dc.add_link(LinkSame(data1.pixel_component_ids[0], data2.pixel_component_ids[2])) self.dc.add_link(LinkSame(data1.pixel_component_ids[0], data3.pixel_component_ids[0])) assert len(data1.pixel_aligned_data) == 0 assert len(data2.pixel_aligned_data) == 0 assert len(data3.pixel_aligned_data) == 0 assert len(self.log.messages_by_type(PixelAlignedDataChangedMessage)) == 0 # Add links between a second set of axes self.dc.add_link(LinkSame(data1.pixel_component_ids[2], data2.pixel_component_ids[1])) self.dc.add_link(LinkSame(data1.pixel_component_ids[2], data3.pixel_component_ids[1])) # At this point, data3 has all its axes contained in data1 and data2 assert len(data1.pixel_aligned_data) == 0 assert len(data2.pixel_aligned_data) == 0 assert len(data3.pixel_aligned_data) == 2 assert data3.pixel_aligned_data[data1] == [0, 2] assert data3.pixel_aligned_data[data2] == [2, 1] messages = self.log.messages_by_type(PixelAlignedDataChangedMessage) assert len(messages) == 1 assert messages[0].data is data3 # Finally we can link the third set of axes self.dc.add_link(LinkSame(data1.pixel_component_ids[1], data2.pixel_component_ids[0])) # At this point, data1 and data2 should now be linked assert len(data1.pixel_aligned_data) == 1 assert data1.pixel_aligned_data[data2] == [2, 0, 1] assert len(data2.pixel_aligned_data) == 1 assert data2.pixel_aligned_data[data1] == [1, 2, 0] assert len(data3.pixel_aligned_data) == 2 assert data3.pixel_aligned_data[data1] == [0, 2] assert data3.pixel_aligned_data[data2] == [2, 1] messages = self.log.messages_by_type(PixelAlignedDataChangedMessage) assert len(messages) == 3 assert messages[1].data is data1 assert messages[2].data is data2 def test_merge_duplicate(self): x = Data(x=[1, 2, 3], label='d1') y = Data(x=[2, 3, 4], label='d2') z = Data(y=[2, 3, 4], label='d3') dc = DataCollection([x, y, z]) dc.merge(x, y, z) assert dc[0].main_components[0].label == 'x [d1]' assert dc[0].main_components[1].label == 'x [d2]' assert dc[0].main_components[2].label == 'y' def test_suggest_merge_label(self): x = Data(x=[1, 2, 3], label='abc_1') y = Data(x=[2, 3, 4], label='abc_2') z = Data(y=[2, 3, 4], label='abc_3') w = Data(y=[2, 3, 4], label='banana') dc = DataCollection([x, y, z]) assert dc.suggest_merge_label(x, y) == 'abc' assert dc.suggest_merge_label(x, y, z) == 'abc' assert dc.suggest_merge_label(x, y, z, w) == 'Merged data' u = Data(y=[2, 3, 4], label='abc') dc.append(u) assert dc.suggest_merge_label(x, y) == 'abc [2]' v = Data(y=[2, 3, 4], label='Merged data') dc.append(v) assert dc.suggest_merge_label(x, w) == 'Merged data [2]' glueviz-1.0.1+dfsg.orig/glue/core/tests/test_data_derived.py0000644000175000017500000001720613641414126023466 0ustar noahfxnoahfximport pytest import numpy as np from numpy.testing import assert_equal from glue.core.hub import HubListener from glue.core.message import NumericalDataChangedMessage from glue.core.data import Data from glue.core.data_collection import DataCollection from glue.core.data_derived import IndexedData from glue.core.coordinates import AffineCoordinates class TestIndexedData: def setup_class(self): x = +np.arange(2520).reshape((3, 4, 5, 6, 7)) y = -np.arange(2520).reshape((3, 4, 5, 6, 7)) self.data = Data(x=x, y=y, label='Test data') self.x_id, self.y_id = self.data.main_components matrix = np.random.random((6, 6)) - 0.5 matrix[-1] = [0, 0, 0, 0, 0, 1] self.data_with_coords = Data(x=x, y=y, label='Test data', coords=AffineCoordinates(matrix=matrix)) self.subset_state = self.x_id >= 1200 def test_identity(self): # In this test, we don't actually slice any dimensions derived = IndexedData(self.data, (None,) * 5) assert derived.label == 'Test data[:,:,:,:,:]' assert derived.shape == self.data.shape assert [str(c) for c in derived.main_components] == [str(c) for c in self.data.main_components] assert derived.get_kind(self.x_id) == self.data.get_kind(self.x_id) for view in [None, (1, slice(None), slice(None), slice(1, 4), slice(0, 7, 2))]: assert_equal(derived.get_data(self.x_id, view=view), self.data.get_data(self.x_id, view=view)) assert_equal(derived.get_mask(self.subset_state, view=view), self.data.get_mask(self.subset_state, view=view)) bounds = [2, (-5, 5, 10), 3, 4, (-3, 3, 10)] assert_equal(derived.compute_fixed_resolution_buffer(bounds=bounds, target_cid=self.x_id), self.data.compute_fixed_resolution_buffer(bounds=bounds, target_cid=self.x_id)) assert_equal(derived.compute_statistic('mean', self.x_id), self.data.compute_statistic('mean', self.x_id)) assert_equal(derived.compute_statistic('mean', self.x_id, axis=2), self.data.compute_statistic('mean', self.x_id, axis=2)) assert_equal(derived.compute_statistic('mean', self.x_id, subset_state=self.subset_state), self.data.compute_statistic('mean', self.x_id, subset_state=self.subset_state)) assert_equal(derived.compute_histogram([self.x_id], range=[(0, 1000)], bins=[30]), self.data.compute_histogram([self.x_id], range=[(0, 1000)], bins=[30])) assert_equal(derived.compute_histogram([self.x_id], range=[(0, 1000)], bins=[30], subset_state=self.subset_state), self.data.compute_histogram([self.x_id], range=[(0, 1000)], bins=[30], subset_state=self.subset_state)) def test_indexed(self): # Here we slice two of the dimensions and then compare the results to a # manually sliced dataset. derived = IndexedData(self.data, (None, 2, None, 4, None)) manual = Data() manual.add_component(self.data[self.x_id][:, 2, :, 4, :], label=self.x_id) manual.add_component(self.data[self.y_id][:, 2, :, 4, :], label=self.y_id) assert derived.label == 'Test data[:,2,:,4,:]' assert derived.shape == manual.shape assert [str(c) for c in derived.main_components] == [str(c) for c in manual.main_components] assert derived.get_kind(self.x_id) == manual.get_kind(self.x_id) for view in [None, (1, slice(None), slice(1, 4))]: assert_equal(derived.get_data(self.x_id, view=view), manual.get_data(self.x_id, view=view)) assert_equal(derived.get_mask(self.subset_state, view=view), manual.get_mask(self.subset_state, view=view)) bounds = [2, (-5, 5, 10), (-3, 3, 10)] assert_equal(derived.compute_fixed_resolution_buffer(bounds=bounds, target_cid=self.x_id), manual.compute_fixed_resolution_buffer(bounds=bounds, target_cid=self.x_id)) assert_equal(derived.compute_statistic('mean', self.x_id), manual.compute_statistic('mean', self.x_id)) assert_equal(derived.compute_statistic('mean', self.x_id, axis=2), manual.compute_statistic('mean', self.x_id, axis=2)) assert_equal(derived.compute_statistic('mean', self.x_id, subset_state=self.subset_state), manual.compute_statistic('mean', self.x_id, subset_state=self.subset_state)) assert_equal(derived.compute_histogram([self.x_id], range=[(0, 1000)], bins=[30]), manual.compute_histogram([self.x_id], range=[(0, 1000)], bins=[30])) assert_equal(derived.compute_histogram([self.x_id], range=[(0, 1000)], bins=[30], subset_state=self.subset_state), manual.compute_histogram([self.x_id], range=[(0, 1000)], bins=[30], subset_state=self.subset_state)) def test_numerical_values_changed(self): # Here we slice two of the dimensions and then compare the results to a # manually sliced dataset. derived = IndexedData(self.data, (None, 2, None, 4, None)) data_collection = DataCollection([self.data, derived]) class CustomListener(HubListener): def __init__(self, hub): self.received = 0 hub.subscribe(self, NumericalDataChangedMessage, handler=self.receive_message) def receive_message(self, message): self.received += 1 listener = CustomListener(data_collection.hub) assert listener.received == 0 derived.indices = (None, 3, None, 5, None) assert listener.received == 1 derived.indices = (None, 3, None, 5, None) assert listener.received == 1 def test_invalid_indices_init(self): with pytest.raises(ValueError) as exc: derived = IndexedData(self.data, (2, None, 4, None)) assert exc.value.args[0] == "The 'indices' tuple should have length 5" def test_invalid_indices_changed(self): derived = IndexedData(self.data, (None, 2, None, 4, None)) with pytest.raises(TypeError) as exc: derived.indices = (2, None, 4, None, 5) assert exc.value.args[0] == "Can't change where the ``None`` values are in indices" with pytest.raises(ValueError) as exc: derived.indices = (2, None, 4, None) assert exc.value.args[0] == "The 'indices' tuple should have length 5" def test_pixel_component_ids(self): derived = IndexedData(self.data, (None, 2, None, 4, None)) assert_equal(derived.get_data(derived.pixel_component_ids[1]), self.data.get_data(self.data.pixel_component_ids[2])[:, 2, :, 4, :]) def test_world_component_ids(self): derived = IndexedData(self.data, (None, 2, None, 4, None)) assert derived.world_component_ids == [] derived_with_coords = IndexedData(self.data_with_coords, (None, 2, None, 4, None)) assert_equal(derived_with_coords.get_data(derived_with_coords.world_component_ids[0]), self.data_with_coords.get_data(self.data_with_coords.world_component_ids[0])[:, 2, :, 4, :]) assert_equal(derived_with_coords.get_data(derived_with_coords.world_component_ids[1]), self.data_with_coords.get_data(self.data_with_coords.world_component_ids[2])[:, 2, :, 4, :]) assert_equal(derived_with_coords.get_data(derived_with_coords.world_component_ids[2]), self.data_with_coords.get_data(self.data_with_coords.world_component_ids[4])[:, 2, :, 4, :]) glueviz-1.0.1+dfsg.orig/glue/core/tests/test_state.py0000644000175000017500000002430313661230662022172 0ustar noahfxnoahfximport json from io import BytesIO import pytest import numpy as np from numpy.testing import assert_equal from glue import core from glue.core.component import CategoricalComponent, DateTimeComponent from glue.tests.helpers import requires_astropy, make_file from glue.config import session_patch from ..data_factories import load_data from ..data_factories.tests.test_fits import TEST_FITS_DATA from ..state import (GlueSerializer, GlueUnSerializer, saver, loader, VersionedDict) def clone(object, include_data=False): gs = GlueSerializer(object, include_data=include_data) oid = gs.id(object) dump = gs.dumps() gu = GlueUnSerializer.loads(dump) result = gu.object(oid) return result def doubler(x): return 2 * x def containers_equal(c1, c2): """Check that two container-like items have the same contents, ignoring differences relating to the type of container """ if isinstance(c1, str): return c1 == c2 try: for a, b in zip(c1, c2): if not containers_equal(a, b): return False if isinstance(c1, dict) and isinstance(c2, dict): if not containers_equal(c1[a], c2[b]): return False except TypeError: pass return True class Cloner(object): def __init__(self, obj): self.s = GlueSerializer(obj) self.us = GlueUnSerializer.loads(self.s.dumps()) def get(self, o): return self.us.object(self.s.id(o)) class Circular(object): def __gluestate__(self, context): return dict(other=context.id(self.other)) @classmethod def __setgluestate__(cls, rec, context): result = cls() yield result result.other = context.object(rec['other']) def test_generator_loaders(): f = Circular() b = Circular() f.other = b b.other = f f2 = clone(f) assert f2.other.other is f2 def test_none(): assert clone(None) is None def test_data(): d = core.Data(x=[1, 2, 3], label='testing') d2 = clone(d) assert d2.label == 'testing' np.testing.assert_array_equal(d2['x'], [1, 2, 3]) np.testing.assert_array_equal(d2['Pixel Axis 0 [x]'], [0, 1, 2]) def test_data_style(): d = core.Data(x=[1, 2, 3]) d.style.color = 'blue' d2 = clone(d) assert d2.style.color == 'blue' def test_user_patch_is_called(): @session_patch() def a_patch(session): session["__main__"]["label"] = 'has_changed' d = core.Data(x=[1, 2, 3], label='testing') d2 = clone(d) session_patch._members.pop(-1) # clean registry assert d2.label == 'has_changed' @requires_astropy def test_data_factory(): with make_file(TEST_FITS_DATA, '.fits', decompress=True) as infile: d = load_data(infile) d2 = clone(d) np.testing.assert_array_equal(d['PRIMARY'], d2['PRIMARY']) @requires_astropy def test_data_factory_include_data(): with make_file(TEST_FITS_DATA, '.fits', decompress=True) as infile: d = load_data(infile) d2 = clone(d, include_data=True) np.testing.assert_array_equal(d['PRIMARY'], d2['PRIMARY']) def test_save_numpy_scalar(): assert clone(np.float32(5)) == 5 @requires_astropy def tests_data_factory_double(): # ensure double-cloning doesn't somehow lose lightweight references from astropy.io import fits d = np.random.normal(0, 1, (100, 100, 100)) s = BytesIO() fits.writeto(s, d) with make_file(s.getvalue(), '.fits') as infile: d = load_data(infile) d2 = clone(d) assert len(GlueSerializer(d).dumps()) < \ 1.1 * len(GlueSerializer(d2).dumps()) def test_inequality_subset(): d = core.Data(x=[1, 2, 3], label='testing') s = d.new_subset(label='abc') s.subset_state = d.id['x'] > 1 d2 = clone(d) s2 = d2.subsets[0] assert s.label == s2.label np.testing.assert_array_equal(s2.to_mask(), [False, True, True]) assert s.style == s2.style def test_compound_state(): d = core.Data(x=[1, 2, 3]) s = d.new_subset(label='abc') s.subset_state = (d.id['x'] > 2) | (d.id['x'] < 1.5) d2 = clone(d) np.testing.assert_array_equal(d2.subsets[0].to_mask(), [True, False, True]) def test_empty_subset(): d = core.Data(x=[1, 2, 3], label='testing') s = d.new_subset(label='abc') s.style.color = 'blue' s2 = clone(s) assert s.style == s2.style assert s2.style.color == 'blue' def test_box_roi_subset(): d = core.Data(x=[1, 2, 3], y=[2, 4, 8]) s = d.new_subset(label='box') roi = core.roi.RectangularROI(xmin=1.1, xmax=2.1, ymin=2.2, ymax=4.2) s.subset_state = core.subset.RoiSubsetState(xatt=d.id['x'], yatt=d.id['y'], roi=roi) d2 = clone(d) np.testing.assert_array_equal( d2.subsets[0].to_mask(), [False, True, False]) def test_range_subset(): d = core.Data(x=[1, 2, 3]) s = d.new_subset(label='range') s.subset_state = core.subset.RangeSubsetState(0.5, 2.5, att=d.id['x']) d2 = clone(d) np.testing.assert_array_equal( d2.subsets[0].to_mask(), [True, True, False]) def test_complex_state(): d = core.Data(x=[1, 2, 3], y=[2, 4, 8]) s = d.new_subset(label='test') s.subset_state = (d.id['x'] > 2) | (d.id['y'] < 4) s.subset_state = s.subset_state & (d.id['x'] < 4) d2 = clone(d) s2 = d2.subsets[0] np.testing.assert_array_equal(s2.to_mask(), [True, False, True]) def test_range_roi(): roi = core.roi.RangeROI('x', min=1, max=2) r2 = clone(roi) assert r2.ori == 'x' assert r2.min == 1 assert r2.max == 2 def test_circular_roi(): roi = core.roi.CircularROI(xc=0, yc=1, radius=2) r2 = clone(roi) assert r2.xc == 0 assert r2.yc == 1 assert r2.radius == 2 def test_polygonal_roi(): roi = core.roi.PolygonalROI() roi.add_point(0, 0) roi.add_point(0, 1) roi.add_point(1, 0) r2 = clone(roi) assert_equal(r2.vx, [0, 0, 1]) assert_equal(r2.vy, [0, 1, 0]) def test_projected3d_roi(): roi_2d = core.roi.PolygonalROI(vx=[0.5, 2.5, 2.5, 0.5], vy=[1, 1, 3.5, 3.5]) roi = core.roi.Projected3dROI(roi_2d=roi_2d, projection_matrix=np.eye(4)) roi_clone = clone(roi) x = [1, 2, 3] y = [2, 3, 4] z = [5, 6, 7] assert roi.contains(x, y).tolist() == roi_clone.contains(x, y).tolist() def test_matplotlib_cmap(): from matplotlib import cm assert clone(cm.gist_heat) is cm.gist_heat def test_binary_component_link(): d1 = core.Data(x=[1, 2, 3]) d1['y'] = d1.id['x'] + 1 assert_equal(d1['y'], [2, 3, 4]) d2 = clone(d1) assert_equal(d2['y'], [2, 3, 4]) class Spam(object): pass @saver(Spam) def _save_spam(spam, context): return {'spam': spam} @loader(Spam) def _load_spam(rec, context): pass def test_no_circular(): # If a saver/loader is implemented incorrectly, this used to lead to # non-informative circular references, so we check that the error message # is more useful now with pytest.raises(TypeError) as exc: clone(Spam()) assert "is not JSON serializable" in exc.value.args[0] def test_categorical_component(): c = CategoricalComponent(['a', 'b', 'c', 'a', 'b'], categories=['a', 'b', 'c']) c2 = clone(c) assert isinstance(c2, CategoricalComponent) np.testing.assert_array_equal(c.codes, [0, 1, 2, 0, 1]) np.testing.assert_array_equal(c.labels, ['a', 'b', 'c', 'a', 'b']) np.testing.assert_array_equal(c.categories, ['a', 'b', 'c']) def test_datetime_component(): c = DateTimeComponent(np.array([100, 200, 300], dtype='M8[D]')) c2 = clone(c) assert isinstance(c2, DateTimeComponent) np.testing.assert_array_equal(c.data, c2.data) assert isinstance(c2.data[0], np.datetime64) class DummyClass(object): pass class TestVersioning(object): def setup_method(self, method): @saver(DummyClass, version=1) def s1(d, context): return dict(v=3) @loader(DummyClass, version=1) def l1(d, context): return 3 @saver(DummyClass, version=2) def s2(d, context): return dict(v=4) @loader(DummyClass, version=2) def l2(rec, context): return 4 def teardown_method(self, method): GlueSerializer.dispatch._data[DummyClass].pop(1) GlueSerializer.dispatch._data[DummyClass].pop(2) GlueUnSerializer.dispatch._data[DummyClass].pop(1) GlueUnSerializer.dispatch._data[DummyClass].pop(2) def test_default_latest_save(self): assert list(GlueSerializer(DummyClass()).dumpo().values())[0]['v'] == 4 assert list(GlueSerializer(DummyClass()).dumpo().values())[0]['_protocol'] == 2 def test_legacy_load(self): data = json.dumps({'': {'_type': 'glue.core.tests.test_state.DummyClass', '_protocol': 1, 'v': 2}}) assert GlueUnSerializer(data).object('') == 3 def test_default_earliest_load(self): data = json.dumps({'': {'_type': 'glue.core.tests.test_state.DummyClass'}}) assert GlueUnSerializer(data).object('') == 3 class TestVersionedDict(object): def test_bad_add(self): d = VersionedDict() with pytest.raises(KeyError): d['nonsequential', 2] = 5 def test_get(self): d = VersionedDict() d['key', 1] = 5 d['key', 2] = 6 d['key', 3] = 7 assert d['key'] == (7, 3) assert d.get_version('key', 1) == 5 assert d.get_version('key', 2) == 6 assert d.get_version('key', 3) == 7 with pytest.raises(KeyError) as exc: d['missing'] def test_get_missing(self): d = VersionedDict() d['key', 1] = 5 with pytest.raises(KeyError) as exc: d.get_version('key', 2) assert exc.value.args[0] == 'No value associated with version 2 of key' def test_contains(self): d = VersionedDict() assert 'key' not in d d['key', 1] = 3 assert 'key' in d def test_overwrite_forbidden(self): d = VersionedDict() d['key', 1] = 3 with pytest.raises(KeyError) as exc: d['key', 1] = 3 def test_noninteger_version(self): d = VersionedDict() with pytest.raises(ValueError) as exc: d['key', 'bad'] = 4 glueviz-1.0.1+dfsg.orig/glue/core/tests/test_aggregate.py0000644000175000017500000000000013605357235022770 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/tests/test_subset.py0000644000175000017500000007644013752534424022374 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import tempfile import operator as op import pytest import numpy as np from numpy.testing import assert_equal, assert_allclose from unittest.mock import MagicMock from glue.tests.helpers import requires_astropy, requires_scipy, SCIPY_INSTALLED from ..exceptions import IncompatibleAttribute from .. import DataCollection, ComponentLink from ..data import Data, Component from ..roi import CategoricalROI, RectangularROI, Projected3dROI, CircularROI from ..message import SubsetDeleteMessage from ..registry import Registry from ..link_helpers import LinkSame from ..subset import (Subset, SubsetState, ElementSubsetState, RoiSubsetState, RangeSubsetState, CategoricalROISubsetState, InequalitySubsetState, CategorySubsetState, MaskSubsetState, CategoricalROISubsetState2D, RoiSubsetState3d, CategoricalMultiRangeSubsetState, FloodFillSubsetState, SliceSubsetState, MultiOrState) from ..subset import AndState from ..subset import InvertState from ..subset import OrState from ..subset import XorState from .test_state import clone def example_transform(x, y): return x + y, x - y class TestSubset(object): def setup_method(self, method): self.data = Data() self.data.hub = MagicMock() self.data.label = "data" Registry().clear() def test_subset_mask_wraps_state(self): s = Subset(self.data) state = MagicMock(spec_set=SubsetState) state.to_mask.return_value = np.array([True]) assert state.to_mask.call_count == 0 s.subset_state = state s.to_mask() state.to_mask.assert_called_once_with(self.data, view=None) def test_subset_index_wraps_state(self): s = Subset(self.data) state = MagicMock(spec=SubsetState) state.to_index_list.return_value = np.array([1, 2, 3]) s.subset_state = state s.to_index_list() state.to_index_list.assert_called_once_with(self.data) def test_set_label(self): s = Subset(self.data, label='hi') assert s.label == 'hi' def test_str(self): s = Subset(self.data, label="hi") assert str(s) == "Subset: hi (data: data)" s = Subset(None, label="hi") assert str(s) == "Subset: hi (no data)" s = Subset(None) assert str(s) == "Subset: (no label) (no data)" s = Subset(self.data) assert str(s) == "Subset: (no label) (data: data)" def test_set_color(self): s = Subset(self.data, color='blue') assert s.style.color == 'blue' def test_paste_returns_copy_of_state(self): s = Subset(self.data) state1 = MagicMock(spec=SubsetState) state1_copy = MagicMock(spec=SubsetState) state1.copy.return_value = state1_copy s.subset_state = state1 s2 = Subset(self.data) s2.paste(s) assert s2.subset_state is state1_copy def test_register_enables_braodcasting(self): s = Subset(self.data) s.register() assert s._broadcasting def test_register_adds_subset_to_data(self): self.data = MagicMock() s = Subset(self.data) s.register() s.data.add_subset.assert_called_once_with(s) def test_delete_without_hub(self): self.data = MagicMock() self.data.hub = None s = Subset(self.data) s.register() s.delete() assert not s._broadcasting def test_delete_disables_broadcasting(self): """Subset no longer broadcasts after delete""" s = Subset(self.data) s.register() s.delete() assert not s._broadcasting def test_delete_sends_message_if_hub_present(self): """delete() broadcasts a SubsetDelteMessage""" s = Subset(self.data) s.register() s.data.hub.broadcast.reset_mock() s.delete() assert s.data.hub.broadcast.call_count == 1 args = s.data.hub.broadcast.call_args[0] msg = args[0] assert isinstance(msg, SubsetDeleteMessage) def test_delete_removes_from_data(self): """delete method removes reference from data.subsets""" data = Data() s = data.new_subset() assert s in data.subsets s.delete() assert s not in data.subsets def test_delete_with_no_data(self): """delete method doesnt crash if subset has no data""" s = Subset(None) assert s.data is None s.delete() def test_double_delete_ignored(self): """calling delete twice doesnt crash""" data = Data() s = data.new_subset() assert s in data.subsets s.delete() s.delete() assert s not in data.subsets def test_broadcast_ignore(self): """subset doesn't broadcast until do_broadcast(True)""" s = Subset(self.data) s.data.hub.broadcast.reset_mock() s.broadcast('style') assert s.data.hub.broadcast.call_count == 0 def test_broadcast_processed(self): """subset broadcasts after do_broadcast(True)""" s = Subset(self.data) s.do_broadcast(True) s.data.hub.broadcast.reset_mock() s.broadcast('style') assert s.data.hub.broadcast.call_count == 1 def test_del(self): s = Subset(self.data) s.__del__() def test_getitem_empty(self): self.data = MagicMock() s = Subset(self.data) s.to_index_list = MagicMock() s.to_index_list.return_value = [] get = s['test'] assert list(get) == [] def test_state_with_array(self): d = Data(x=[1, 2, 3]) s = d.new_subset() s.subset_state = np.array([True, False, False]) np.testing.assert_array_equal(s.to_mask(), [True, False, False]) def test_state_array_bad_shape(self): d = Data(x=[1, 2, 3]) s = d.new_subset() with pytest.raises(ValueError): s.subset_state = np.array([True]) def test_state_bad_type(self): s = Subset(Data()) with pytest.raises(TypeError): s.subset_state = 5 target_states = ((op.and_, AndState), (op.or_, OrState), (op.xor, XorState)) @pytest.mark.parametrize(("x"), target_states) def test_binary_subset_combination(x): operator, target = x s1 = Subset(None) s2 = Subset(None) newsub = operator(s1, s2) assert isinstance(newsub, Subset) assert isinstance(newsub.subset_state, target) class TestSubsetStateCombinations(object): def setup_method(self, method): self.data = None def test_or(self): s1 = Subset(self.data) s2 = Subset(self.data) s3 = s1.subset_state | s2.subset_state assert isinstance(s3, OrState) def test_and(self): s1 = Subset(self.data) s2 = Subset(self.data) s3 = s1.subset_state & s2.subset_state assert isinstance(s3, AndState) def test_invert(self): s1 = Subset(self.data) s3 = ~s1.subset_state assert isinstance(s3, InvertState) def test_xor(self): s1 = Subset(self.data) s2 = Subset(self.data) s3 = s1.subset_state ^ s2.subset_state assert isinstance(s3, XorState) class TestCompositeSubsetStates(object): class DummyState(SubsetState): def __init__(self, mask): self._mask = mask def to_mask(self, data, view): return self._mask def copy(self): return TestCompositeSubsetStates.DummyState(self._mask) def setup_method(self, method): self.sub1 = self.DummyState(np.array([1, 1, 0, 0], dtype='bool')) self.sub2 = self.DummyState(np.array([1, 0, 1, 0], dtype='bool')) self.data = Data(x=[1, 2, 3, 4]) def test_or(self): s3 = OrState(self.sub1, self.sub2) answer = s3.to_mask(self.data) expected = np.array([True, True, True, False]) np.testing.assert_array_equal(answer, expected) def test_and(self): s3 = AndState(self.sub1, self.sub2) answer = s3.to_mask(self.data) expected = np.array([True, False, False, False]) np.testing.assert_array_equal(answer, expected) def test_xor(self): s3 = XorState(self.sub1, self.sub2) answer = s3.to_mask(self.data) expected = np.array([False, True, True, False]) np.testing.assert_array_equal(answer, expected) def test_invert(self): s3 = InvertState(self.sub1) answer = s3.to_mask(self.data) expected = np.array([False, False, True, True]) np.testing.assert_array_equal(answer, expected) def test_multicomposite(self): s3 = AndState(self.sub1, self.sub2) s4 = XorState(s3, self.sub1) answer = s4.to_mask(self.data) expected = np.array([False, True, False, False]) np.testing.assert_array_equal(answer, expected) class TestElementSubsetState(object): def setup_method(self, method): self.state = ElementSubsetState() self.data = Data(x=[[1], [2]]) def test_empty_mask(self): mask = self.state.to_mask(self.data) np.testing.assert_array_equal(mask, np.array([[False], [False]])) def test_empty_index_list(self): ilist = self.state.to_index_list(self.data) np.testing.assert_array_equal(ilist, np.array([])) def test_nonempty_index_list(self): self.state._indices = [0] ilist = self.state.to_index_list(self.data) np.testing.assert_array_equal(ilist, np.array([0])) def test_nonempty_mask(self): self.state._indices = [0] mask = self.state.to_mask(self.data) np.testing.assert_array_equal(mask, np.array([[True], [False]])) def test_define_on_init(self): ind = np.array([0, 1]) state = ElementSubsetState(indices=ind) np.testing.assert_array_equal(ind, state._indices) class TestSubsetIo(object): def setup_method(self, method): self.data = Data() self.data['a'] = np.arange(16).reshape((4, 4)) self.data.uuid = 'abcde' self.subset = Subset(self.data) inds = np.array([1, 2, 3]) self.subset.subset_state = ElementSubsetState(indices=inds) @requires_astropy def test_write(self): fobj, tmp = tempfile.mkstemp() self.subset.write_mask(tmp) from astropy.io import fits with fits.open(tmp) as hdulist: data = hdulist[0].data expected = np.array([[0, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], dtype=np.int16) np.testing.assert_array_equal(data, expected) @requires_astropy def test_read(self): fobj, tmp = tempfile.mkstemp() self.subset.write_mask(tmp) sub2 = Subset(self.data) sub2.read_mask(tmp) mask1 = self.subset.to_mask() mask2 = sub2.to_mask() np.testing.assert_array_equal(mask1, mask2) @requires_astropy def test_read_error(self): with pytest.raises(IOError) as exc: self.subset.read_mask('file_does_not_exist') assert exc.value.args[0] == ("Could not read file_does_not_exist " "(not a fits file?)") def test_write_unsupported_format(self): with pytest.raises(AttributeError) as exc: self.subset.write_mask('file_will_fail', format='.hd5') assert exc.value.args[0] == "format not supported: .hd5" class TestSubsetState(object): def setup_method(self, method): self.state = SubsetState() def mask_check(self, mask, answer): self.state.to_mask = MagicMock() self.state.to_mask.return_value = mask np.testing.assert_array_equal(self.state.to_index_list(Data()), answer) def test_to_index_list_1d(self): mask = np.array([False, True]) answer = np.array([1]) self.mask_check(mask, answer) def test_to_index_list_2d(self): mask = np.array([[False, True], [False, True]]) answer = np.array([1, 3]) self.mask_check(mask, answer) def test_empty_to_index_1d(self): mask = np.array([False, False]) answer = np.array([]) self.mask_check(mask, answer) def test_empty_to_index_2d(self): mask = np.array([[False, False], [False, False]]) answer = np.array([]) self.mask_check(mask, answer) class TestCompositeSubsetStateCopy(object): def assert_composite_copy(self, cls): """Copying composite state should create new state with same type, with copies of sub states""" state1 = MagicMock() state2 = MagicMock() s1 = cls(state1, state2) s2 = s1.copy() assert type(s1) == type(s2) assert s1.state1.copy() is s2.state1 assert s1.state2.copy() is s2.state2 def test_invert(self): self.assert_composite_copy(InvertState) def test_and(self): self.assert_composite_copy(AndState) def test_or(self): self.assert_composite_copy(OrState) def test_xor(self): self.assert_composite_copy(XorState) class DummySubsetState(SubsetState): def to_mask(self, data, view=None): result = np.ones(data.shape, dtype=bool) if view is not None: result = result[view] return result class TestSubsetViews(object): def setup_method(self, method): d = Data() c = Component(np.array([1, 2, 3, 4])) self.cid = d.add_component(c, 'test') self.s = d.new_subset() self.c = c self.s.subset_state = DummySubsetState() def test_cid_get(self): np.testing.assert_array_equal(self.s[self.cid], self.c.data) def test_label_get(self): np.testing.assert_array_equal(self.s['test'], self.c.data) def test_cid_slice(self): np.testing.assert_array_equal(self.s[self.cid, ::2], self.c.data[::2]) def test_label_slice(self): np.testing.assert_array_equal(self.s['test', ::-1], self.c.data[::-1]) # Test Fancy Indexing into the various subset states def roifac(comp, cid): result = RoiSubsetState() result.xatt = cid result.yatt = cid roi = RectangularROI() roi.update_limits(0.5, 0.5, 1.5, 1.5) result.roi = roi return result def roifac3d(comp, cid): roi_2d = RectangularROI(0.5, 0.5, 1.5, 1.5) roi_3d = Projected3dROI(roi_2d=roi_2d, projection_matrix=np.arange(16).reshape((4, 4))) result = RoiSubsetState3d(xatt=cid, yatt=cid, zatt=cid, roi=roi_3d) return result def rangefac(comp, cid): return RangeSubsetState(.5, 2.5, att=cid) def compfac(comp, cid, oper): s1 = roifac(comp, cid) s2 = rangefac(comp, cid) return oper(s1, s2) def orfac(comp, cid): return compfac(comp, cid, op.or_) def andfac(comp, cid): return compfac(comp, cid, op.and_) def xorfac(comp, cid): return compfac(comp, cid, op.xor) def invertfac(comp, cid): return ~rangefac(comp, cid) def elementfac(comp, cid): return ElementSubsetState(np.array([0, 1])) def inequalityfac(comp, cid): return cid > 2.5 def basefac(comp, cid): return SubsetState() def maskfac(comp, cid): return MaskSubsetState([[0, 1], [1, 1]], cid.parent.pixel_component_ids) def floodfac(comp, cid): return FloodFillSubsetState(cid.parent, cid, [0, 0], 1.2) def slicefac(comp, cid): return SliceSubsetState(cid.parent, [slice(None), slice(0, 1)]) views = (np.s_[:], np.s_[::-1, 0], np.s_[0, :], np.s_[:, 0], np.array([[True, False], [False, True]]), np.where(np.array([[True, False], [False, True]])), np.zeros((2, 2), dtype=bool), ) facs = [roifac, roifac3d, rangefac, orfac, andfac, xorfac, invertfac, elementfac, inequalityfac, basefac, maskfac, slicefac] if SCIPY_INSTALLED: facs.append(floodfac) @pytest.mark.parametrize(('statefac', 'view'), [(f, v) for f in facs for v in views]) def test_mask_of_view_is_view_of_mask(statefac, view): print(statefac, view) d = Data() d.edit_subset = d.new_subset() c = Component(np.array([[1, 2], [3, 4]])) cid = d.add_component(c, 'test') s = d.edit_subset s.subset_state = statefac(c, cid) v1 = s.to_mask(view) v2 = s.to_mask()[view] np.testing.assert_array_equal(v1, v2) v1 = s[cid, view] v2 = c.data[view][s.to_mask(view)] np.testing.assert_array_equal(v1, v2) def test_inequality_state_str(): d = Data(x=[1, 2, 3], y=[2, 3, 4]) x = d.id['x'] y = d.id['y'] assert str(x == 'a') == '(x == a)' assert str(x > 3) == '(x > 3)' assert str(x < 2) == '(x < 2)' assert str(x < y) == '(x < y)' assert str((3 * x) < 5) == '((3 * x) < 5)' assert str((x < y) & (x < 2)) == '((x < y) & (x < 2))' assert str((x < y) | (x < 2)) == '((x < y) | (x < 2))' assert str(~(x < y)) == '(~(x < y))' assert repr(x < 5) == ('') def test_to_mask_state(): d = Data(x=[1, 2, 3]) sub = d.new_subset() sub.subset_state = d.id['x'] > 1 sub.subset_state = sub.state_as_mask() np.testing.assert_array_equal(sub.to_mask(), [False, True, True]) def test_to_mask_state_across_data(): d = Data(x=[1, 2, 3]) d2 = Data(x=[2, 3, 4]) dc = DataCollection([d, d2]) link = ComponentLink(d2.pixel_component_ids, d.pixel_component_ids[0], lambda x: x - 1) dc.add_link(link) sub = d.new_subset() sub.subset_state = d.id['x'] > 1 sub.subset_state = sub.state_as_mask() sub2 = d2.new_subset() sub2.subset_state = sub.subset_state np.testing.assert_array_equal(sub2.to_mask(), [False, False, True]) def test_mask_clone(): d = Data(x=[1, 2, 3]) sub = d.new_subset() sub.subset_state = d.id['x'] > 1 sub.subset_state = sub.state_as_mask() d = clone(d) sub = d.subsets[0] np.testing.assert_array_equal(sub.to_mask(), [False, True, True]) class TestAttributes(object): def test_empty(self): assert SubsetState().attributes == tuple() def test_roi(self): d = Data(x=[1], y=[2]) s = RoiSubsetState(xatt=d.id['x'], yatt=d.id['y']) assert s.attributes == (d.id['x'], d.id['y']) def test_range(self): d = Data(x=[1]) s = RangeSubsetState(0, 1, att=d.id['x']) assert s.attributes == (d.id['x'],) def test_composite(self): d = Data(x=[1]) s = RangeSubsetState(0, 1, att=d.id['x']) assert (s & s).attributes == (d.id['x'],) def test_not(self): d = Data(x=[1]) s = RangeSubsetState(0, 1, att=d.id['x']) assert (~s).attributes == (d.id['x'],) def test_subset(self): d = Data(x=[1]) s = d.new_subset() s.subset_state = RangeSubsetState(0, 1, att=d.id['x']) assert s.attributes == (d.id['x'],) def test_save_element_subset_state(): # Regression test to make sure that element subset states are saved # correctly. state1 = ElementSubsetState(indices=[1, 3, 4]) state2 = clone(state1) assert state2._indices == [1, 3, 4] def test_inequality_subset_state_string(): d = Data(x=['a', 'b', 'c', 'b']) state = d.id['x'] == 'b' np.testing.assert_equal(state.to_mask(d), np.array([False, True, False, True])) def test_inherited_properties(): d = Data(x=np.random.random((3, 2, 4)).astype(np.float32)) sub = d.new_subset() sub.subset_state = d.id['x'] > 0.5 assert sub.component_ids() == d.component_ids() assert sub.components == d.components assert sub.main_components == d.main_components assert sub.derived_components == d.derived_components assert sub.pixel_component_ids == d.pixel_component_ids assert sub.world_component_ids == d.world_component_ids assert sub.ndim == 3 assert sub.shape == (3, 2, 4) assert sub.hub is d.hub class TestCloneSubsetStates(): def setup_method(self, method): self.data = Data(a=[-3, 2, 4, 1], b=['a', 'b', 'a', 'c'], c=[1.2, 1.3, 1.5, 1.9], d=['x', 'y', 'z', 'y']) def test_element_subset_state(self): subset = self.data.new_subset() subset.subset_state = ElementSubsetState(indices=[1, 2]) assert_equal(self.data.subsets[0].to_mask(), [0, 1, 1, 0]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [0, 1, 1, 0]) def test_categorical_roi_subset_state(self): roi = CategoricalROI(['a', 'c']) subset = self.data.new_subset() subset.subset_state = CategoricalROISubsetState(att=self.data.id['b'], roi=roi) assert_equal(self.data.subsets[0].to_mask(), [1, 0, 1, 1]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [1, 0, 1, 1]) def test_categorical_roi_2d_subset_state(self): selection = {'a': ['x'], 'b': ['x'], 'c': ['y']} subset = self.data.new_subset() subset.subset_state = CategoricalROISubsetState2D(selection, self.data.id['b'], self.data.id['d']) assert_equal(self.data.subsets[0].to_mask(), [1, 0, 0, 1]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [1, 0, 0, 1]) def test_category_subset_state(self): subset = self.data.new_subset() subset.subset_state = CategorySubsetState(self.data.id['b'], [0, 2]) assert_equal(self.data.subsets[0].to_mask(), [1, 0, 1, 1]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [1, 0, 1, 1]) def test_category_multi_range_subset_state(self): ranges = {'a': [(1.0, 1.1), (1.3, 1.6)], 'b': [(1.1, 1.4), (1.7, 1.8)], 'c': [(1.1, 1.2)]} subset = self.data.new_subset() subset.subset_state = CategoricalMultiRangeSubsetState(ranges, self.data.id['b'], self.data.id['c']) assert_equal(self.data.subsets[0].to_mask(), [0, 1, 1, 0]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [0, 1, 1, 0]) def test_inequality_roi_subset_state(self): subset = self.data.new_subset() subset.subset_state = self.data.id['a'] > 1.5 assert isinstance(subset.subset_state, InequalitySubsetState) assert_equal(self.data.subsets[0].to_mask(), [0, 1, 1, 0]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [0, 1, 1, 0]) def test_mask_subset_state(self): subset = self.data.new_subset() subset.subset_state = MaskSubsetState([0, 1, 0, 1], self.data.pixel_component_ids) assert_equal(self.data.subsets[0].to_mask(), [0, 1, 0, 1]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [0, 1, 0, 1]) def test_range_subset_state(self): subset = self.data.new_subset() subset.subset_state = RangeSubsetState(1.1, 1.4, self.data.id['c']) assert_equal(self.data.subsets[0].to_mask(), [1, 1, 0, 0]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [1, 1, 0, 0]) def test_and_subset_state(self): subset = self.data.new_subset() subset.subset_state = (self.data.id['a'] > 1) & (self.data.id['c'] < 1.5) assert isinstance(subset.subset_state, AndState) assert_equal(self.data.subsets[0].to_mask(), [0, 1, 0, 0]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [0, 1, 0, 0]) def test_or_subset_state(self): subset = self.data.new_subset() subset.subset_state = (self.data.id['a'] > 1) | (self.data.id['c'] < 1.5) assert isinstance(subset.subset_state, OrState) assert_equal(self.data.subsets[0].to_mask(), [1, 1, 1, 0]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [1, 1, 1, 0]) def test_not_subset_state(self): subset = self.data.new_subset() subset.subset_state = ~(self.data.id['a'] > 1) assert isinstance(subset.subset_state, InvertState) assert_equal(self.data.subsets[0].to_mask(), [1, 0, 0, 1]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [1, 0, 0, 1]) def test_xor_subset_state(self): subset = self.data.new_subset() subset.subset_state = (self.data.id['a'] > 1) ^ (self.data.id['c'] > 1.3) assert isinstance(subset.subset_state, XorState) assert_equal(self.data.subsets[0].to_mask(), [0, 1, 0, 1]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [0, 1, 0, 1]) def test_roi_subset_state(self): roi = RectangularROI(xmin=0, xmax=3, ymin=1.1, ymax=1.4) subset = self.data.new_subset() subset.subset_state = RoiSubsetState(xatt=self.data.id['a'], yatt=self.data.id['c'], roi=roi) assert_equal(self.data.subsets[0].to_mask(), [0, 1, 0, 0]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [0, 1, 0, 0]) def test_roi_subset_state_with_pretransform(self): roi = RectangularROI(xmin=2, xmax=4, ymin=0, ymax=1) subset = self.data.new_subset() subset.subset_state = RoiSubsetState(xatt=self.data.id['a'], yatt=self.data.id['c'], roi=roi, pretransform=example_transform) # Post transform outputs are x=[-1.8, 3.3, 5.5, 2.9], y=[-4.2, 0.7, 2.5, -0.9] assert_equal(self.data.subsets[0].to_mask(), [0, 1, 0, 0]) data_clone = clone(self.data) assert_equal(data_clone.subsets[0].to_mask(), [0, 1, 0, 0]) @requires_scipy def test_floodfill_subset_state(): data = np.array([[9, 6, 2, 3], [4, 5, 2, 5], [2, 4, 1, 0], [5, 6, 0, -1]]) expected = np.array([[0, 0, 0, 0], [1, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0]]) data1 = Data(x=data) data2 = Data(x=4 - data1['x']) dc = DataCollection([data1, data2]) subset_state = FloodFillSubsetState(data1, data1.id['x'], (1, 0), 1.3) dc.new_subset_group(label='subset', subset_state=subset_state) result = data1.subsets[0].to_mask() assert_equal(result, expected) with pytest.raises(IncompatibleAttribute): data2.subsets[0].to_mask() # Check that setting up pixel links works dc.add_link(LinkSame(data1.pixel_component_ids[0], data2.pixel_component_ids[0])) dc.add_link(LinkSame(data1.pixel_component_ids[1], data2.pixel_component_ids[1])) result = data2.subsets[0].to_mask() assert_equal(result, expected) dc._link_manager.clear_links() dc.add_link(LinkSame(data1.pixel_component_ids[1], data2.pixel_component_ids[0])) dc.add_link(LinkSame(data1.pixel_component_ids[0], data2.pixel_component_ids[1])) result = data2.subsets[0].to_mask() assert_equal(result, expected.T) # Check that (de)serialization works dc_new = clone(dc) result = dc_new[0].subsets[0].to_mask() assert_equal(result, expected) # Check that changing parameters invalidates the cache dc[0].subsets[0].subset_state.threshold = 10 result = data1.subsets[0].to_mask() assert_equal(result, 1) def test_projected_3d_clone(): d = Data(x=[1, 2, 3], y=[2, 3, 4], z=[4, 3, 2]) roi_2d = CircularROI(2, 3, 4) projection_matrix = np.random.uniform(-1, 1, 16).reshape((4, 4)) roi_3d = Projected3dROI(roi_2d=roi_2d, projection_matrix=projection_matrix) subset_state = RoiSubsetState3d(d.id['y'], d.id['x'], d.id['z'], roi_3d) subset_state_new = clone(subset_state) assert subset_state_new.xatt.label == 'y' assert subset_state_new.yatt.label == 'x' assert subset_state_new.zatt.label == 'z' assert isinstance(subset_state_new.roi, Projected3dROI) assert_allclose(subset_state_new.roi.projection_matrix, projection_matrix) def test_slice_subset_state(): data1 = Data(x=np.arange(24).reshape((2, 3, 4))) slices = (slice(None), slice(1, 3), slice(None, None, 2)) subset_state = SliceSubsetState(data1, slices) expected_mask = np.zeros((2, 3, 4)) expected_mask[slices] = 1 assert_equal(subset_state.to_mask(data1), expected_mask) view = (slice(0, 1), slice(None), slice(None)) assert_equal(subset_state.to_mask(data1, view=view), expected_mask[view]) data2 = Data(x=np.arange(24).reshape((3, 4, 2))) data_collection = DataCollection([data1, data2]) assert_equal(subset_state.to_mask(data2), 0) data_collection.add_link(LinkSame(data1.pixel_component_ids[0], data2.pixel_component_ids[2])) assert_equal(subset_state.to_mask(data2), 0) data_collection.add_link(LinkSame(data1.pixel_component_ids[1], data2.pixel_component_ids[0])) data_collection.add_link(LinkSame(data1.pixel_component_ids[2], data2.pixel_component_ids[1])) assert_equal(subset_state.to_mask(data2), expected_mask.transpose().swapaxes(0, 1)) view = (slice(None), slice(1, 3), slice(None)) assert_equal(subset_state.to_mask(data2, view=view), expected_mask.transpose().swapaxes(0, 1)[view]) def test_slice_subset_state_clone(): data1 = Data(x=np.arange(24).reshape((2, 3, 4))) slices = (slice(None), slice(1, 3), slice(None, None, 2)) subset_state = SliceSubsetState(data1, slices) subset = data1.new_subset() subset.subset_state = subset_state expected_mask = np.zeros((2, 3, 4)) expected_mask[slices] = 1 assert_equal(data1.subsets[0].to_mask(), expected_mask) data2 = clone(data1) assert_equal(data2.subsets[0].to_mask(), expected_mask) def test_multi_or_state(): data = Data(x=[1, 2, 3, 4, 5]) cids = data.pixel_component_ids sub1 = MaskSubsetState(np.array([1, 1, 0, 0, 0], dtype='bool'), cids) sub2 = MaskSubsetState(np.array([1, 0, 1, 0, 0], dtype='bool'), cids) sub3 = MaskSubsetState(np.array([0, 0, 0, 0, 1], dtype='bool'), cids) with pytest.raises(ValueError) as exc: MultiOrState([]) assert exc.value.args[0] == 'states should contain at least one subset state' state1 = MultiOrState([sub1]) assert_equal(state1.to_mask(data), [1, 1, 0, 0, 0]) state2 = MultiOrState([sub1, sub2]) assert_equal(state2.to_mask(data), [1, 1, 1, 0, 0]) state3 = MultiOrState([sub1, sub2, sub3]) assert_equal(state3.to_mask(data), [1, 1, 1, 0, 1]) # Test view assert_equal(state3.to_mask(data, view=slice(1, 4)), [1, 1, 0]) # Test attributes assert len(state3.attributes) == 1 and state3.attributes[0] is cids[0] # Test copy assert_equal(state3.to_mask(data).copy(), [1, 1, 1, 0, 1]) # Test str assert str(state3) == "('or' combination of 3 individual states)" def test_roi_reduction(): # This test checks that the ROI dimensionality shortcut works as expected data4d = Data(val=np.zeros((3, 4, 2, 2))) roi = RectangularROI(0.9, 2.1, 1.9, 3.1) state = RoiSubsetState(xatt=data4d.pixel_component_ids[0], yatt=data4d.pixel_component_ids[1], roi=roi) out = state.to_mask(data4d) expected_slice = np.array([[0, 0, 0, 0], [0, 0, 1, 1], [0, 0, 1, 1]]) assert_equal(out[:, :, 0, 0], expected_slice) assert_equal(out[:, :, 0, 1], expected_slice) assert_equal(out[:, :, 1, 0], expected_slice) assert_equal(out[:, :, 1, 1], expected_slice) glueviz-1.0.1+dfsg.orig/glue/core/qt/0000755000175000017500000000000013752535025016723 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/qt/tests/0000755000175000017500000000000013752535025020065 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/qt/tests/test_style_dialog.py0000644000175000017500000000131313605357235024155 0ustar noahfxnoahfximport time from qtpy import QtCore from glue.core import Data from glue.core.tests.util import simple_session from ..style_dialog import StyleDialog class NonBlockingStyleDialog(StyleDialog): def exec_(self, *args): self.show() time.sleep(0.1) self.accept() def test_style_dialog(): # This is in part a regression test for a bug in Python 3. It is not a # full test of StyleDialog. session = simple_session() hub = session.hub collect = session.data_collection image = Data(label='im', x=[[1, 2], [3, 4]], y=[[2, 3], [4, 5]]) pos = QtCore.QPoint(10, 10) st = NonBlockingStyleDialog.dropdown_editor(image, pos) glueviz-1.0.1+dfsg.orig/glue/core/qt/tests/test_simpleforms.py0000644000175000017500000000166313605357235024046 0ustar noahfxnoahfxfrom ..simpleforms import build_form_item, FloatOption, IntOption, BoolOption class Stub(object): i = IntOption(label="int", min=0, max=3, default=2) f = FloatOption(label="x", min=0, max=1, default=0.5) b = BoolOption(label="y", default=True) class TestBuildFormItem(object): def test_int(self): s = Stub() w = build_form_item(s, 'i') assert w.label == "int" assert w.widget.value() == 2 assert w.widget.minimum() == 0 assert w.widget.maximum() == 3 assert w.value == 2 def test_float(self): s = Stub() w = build_form_item(s, 'f') assert w.label == "x" assert w.value == 0.5 assert w.widget.minimum() == 0 assert w.widget.maximum() == 1 def test_bool(self): s = Stub() w = build_form_item(s, 'b') assert w.label == 'y' assert w.value is True assert w.widget.isChecked() glueviz-1.0.1+dfsg.orig/glue/core/qt/tests/__init__.py0000644000175000017500000000000013455362716022171 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/qt/tests/test_message_widget.py0000644000175000017500000000057213605357235024473 0ustar noahfxnoahfxfrom glue.core.hub import Hub from glue.core.message import Message from ..message_widget import MessageWidget def test_message_widget_runs(): hub = Hub() widget = MessageWidget() widget.register_to_hub(hub) widget.show() message = Message('test_message_widget_runs', tag='1234') hub.broadcast(message) # TODO: check content of widget window glueviz-1.0.1+dfsg.orig/glue/core/qt/tests/test_mime.py0000644000175000017500000000126013605357235022426 0ustar noahfxnoahfxfrom ..mime import GlueMimeListWidget, LAYERS_MIME_TYPE class TestGlueMimeListWidget(object): def setup_method(self, method): self.w = GlueMimeListWidget() def test_mime_type(self): assert self.w.mimeTypes() == [LAYERS_MIME_TYPE] def test_mime_data(self): self.w.set_data(3, 'test data') self.w.set_data(4, 'do not pick') mime = self.w.mimeData([3]) mime.data(LAYERS_MIME_TYPE) == ['test data'] def test_mime_data_multiselect(self): self.w.set_data(3, 'test data') self.w.set_data(4, 'also pick') mime = self.w.mimeData([3, 4]) mime.data(LAYERS_MIME_TYPE) == ['test data', 'also pick'] glueviz-1.0.1+dfsg.orig/glue/core/qt/tests/test_data_collection_model.py0000644000175000017500000001107313605357235026006 0ustar noahfxnoahfxfrom qtpy.QtCore import Qt from glue.core import DataCollection, Data from ..data_collection_model import DataCollectionModel from glue.core.qt.mime import LAYERS_MIME_TYPE class TestDataCollectionModel(object): def make_model(self, n_data=1, n_subsets=0): dc = DataCollection([Data(x=[1, 2, 3]) for _ in range(n_data)]) for _ in range(n_subsets): dc.new_subset_group() return DataCollectionModel(dc) def test_row_count_empty_index(self): model = self.make_model(1, 0) assert model.rowCount() == 2 def test_row_count_data_row(self): model = self.make_model(1, 0) assert model.rowCount(model.data_index()) == 1 model = self.make_model(2, 0) assert model.rowCount(model.data_index()) == 2 def test_row_count_subset_row(self): model = self.make_model(1, 0) assert model.rowCount(model.subsets_index()) == 0 model = self.make_model(1, 5) assert model.rowCount(model.subsets_index()) == 5 def test_row_count_single_subset1(self): model = self.make_model(2, 1) assert model.rowCount(model.subsets_index(0)) == 2 def test_row_count_single_subset2(self): model = self.make_model(2, 1) s = model.subsets_index(0) idx = model.index(0, 0, s) assert model.rowCount(idx) == 0 idx = model.index(1, 0, s) assert model.rowCount(s) == 2 def test_invalid_indices(self): model = self.make_model(1, 2) index = model.index(0, 1) assert not index.isValid() index = model.index(2, 0) assert not index.isValid() index = model.index(2, 0, model.index(0, 0)) assert not index.isValid() def test_heading_labels(self): model = self.make_model() assert model.data(model.data_index(), Qt.DisplayRole) == 'Data' assert model.data(model.subsets_index(), Qt.DisplayRole) == 'Subsets' def test_dc_labels(self): model = self.make_model(1, 2) dc = model.data_collection dc[0].label = 'test1' dc[0].subsets[0].label = 'subset1' dc[0].subsets[1].label = 'subset2' assert model.data(model.data_index(0), Qt.DisplayRole) == 'test1' assert model.data(model.subsets_index(0), Qt.DisplayRole) == 'subset1' assert model.data(model.subsets_index(1), Qt.DisplayRole) == 'subset2' assert model.data(model.index(0, 0, model.subsets_index(0)), Qt.DisplayRole) == 'subset1 (test1)' def test_column_count(self): model = self.make_model(1, 2) assert model.columnCount(model.data_index()) == 1 assert model.columnCount(model.data_index(0)) == 1 assert model.columnCount(model.subsets_index()) == 1 assert model.columnCount(model.subsets_index(0)) == 1 assert model.columnCount(model.subsets_index(1)) == 1 def test_header_data(self): model = self.make_model() assert model.headerData(0, Qt.Vertical) == '' assert model.headerData(0, Qt.Horizontal) == '' def test_font_role(self): model = self.make_model(1, 2) assert model.data(model.data_index(), Qt.FontRole).bold() assert model.data(model.subsets_index(), Qt.FontRole).bold() def test_drag_flags(self): model = self.make_model(1, 2) sg = model.subsets_index(0) subset = model.index(0, 0, sg) assert model.flags(model.data_index(0)) & Qt.ItemIsDragEnabled assert model.flags(subset) & Qt.ItemIsDragEnabled assert not model.flags(model.data_index()) & Qt.ItemIsDragEnabled assert not model.flags(model.subsets_index()) & Qt.ItemIsDragEnabled assert not model.flags(sg) & Qt.ItemIsDragEnabled def test_selectable_flags(self): model = self.make_model(1, 2) assert not model.flags(model.data_index()) & Qt.ItemIsSelectable assert not model.flags(model.subsets_index()) & Qt.ItemIsSelectable def test_layers_mime_type_data(self): model = self.make_model(1, 2) index = model.data_index(0) expected = [model.data_collection[0]] assert model.mimeData([index]).data(LAYERS_MIME_TYPE) == expected def test_layers_mime_type_multiselection(self): model = self.make_model(1, 2) idxs = [model.data_index(0), model.subsets_index(0), model.index(0, 0, model.subsets_index(0))] dc = model.data_collection expected = [dc[0], dc.subset_groups[0], dc.subset_groups[0].subsets[0]] assert model.mimeData(idxs).data(LAYERS_MIME_TYPE) == expected glueviz-1.0.1+dfsg.orig/glue/core/qt/tests/test_fitters.py0000644000175000017500000000307313605357235023163 0ustar noahfxnoahfxfrom unittest.mock import MagicMock from glue.core.fitters import SimpleAstropyGaussianFitter, PolynomialFitter from ..fitters import ConstraintsWidget, FitSettingsWidget class TestConstraintsWidget(object): def setup_method(self, method): self.constraints = dict(a=dict(fixed=True, value=1, limits=None)) self.widget = ConstraintsWidget(self.constraints) def test_settings(self): assert self.widget.settings('a') == dict(fixed=True, value=1, limits=None) def test_update_settings(self): self.widget._widgets['a'][2].setChecked(False) assert self.widget.settings('a')['fixed'] is False def test_update_constraints(self): self.widget._widgets['a'][2].setChecked(False) fitter = MagicMock() self.widget.update_constraints(fitter) fitter.set_constraint.assert_called_once_with('a', fixed=False, value=1, limits=None) class TestFitSettingsWidget(object): def test_option(self): f = PolynomialFitter() f.degree = 1 w = FitSettingsWidget(f) w.widgets['degree'].setValue(5) w.update_fitter_from_settings() assert f.degree == 5 def test_set_constraints(self): f = SimpleAstropyGaussianFitter() w = FitSettingsWidget(f) w.constraints._widgets['amplitude'][2].setChecked(True) w.update_fitter_from_settings() assert f.constraints['amplitude']['fixed'] glueviz-1.0.1+dfsg.orig/glue/core/qt/tests/test_layer_artist_model.py0000644000175000017500000001246013605357235025365 0ustar noahfxnoahfxfrom unittest.mock import MagicMock from qtpy.QtCore import Qt from glue.core import Data, Hub from glue.core.layer_artist import LayerArtistBase as _LayerArtist from ..layer_artist_model import LayerArtistModel, LayerArtistView class LayerArtist(_LayerArtist): update_count = 0 clear_count = 0 redraw_count = 0 def update(self): self.update_count += 1 def clear(self): self.clear_count += 1 def redraw(self): self.redraw_count += 1 def setup_model(num): mgrs = [LayerArtist(Data(label=str(i))) for i in range(num)] model = LayerArtistModel(mgrs) return model, mgrs def test_row_count(): for n in range(4): assert setup_model(n)[0].rowCount() == n def test_row_label(): model, mgrs = setup_model(5) for i in range(5): assert model.row_label(i) == mgrs[i].layer.label def test_add_artist_updates_row_count(): mgrs = [LayerArtist(Data(label='A'))] model = LayerArtistModel(mgrs) model.add_artist(0, LayerArtist(Data(label='B'))) assert model.rowCount() == 2 def test_add_artist_updates_artist_list(): mgrs = [LayerArtist(Data(label='A'))] model = LayerArtistModel(mgrs) model.add_artist(0, LayerArtist(Data(label='B'))) assert len(mgrs) == 2 def test_valid_remove(): mgr = MagicMock(spec_set=LayerArtist) mgrs = [mgr] model = LayerArtistModel(mgrs) remove = model.removeRow(0) assert remove assert mgr not in mgrs def test_invalid_remove(): mgr = MagicMock(spec_set=LayerArtist) mgrs = [mgr] model = LayerArtistModel(mgrs) remove = model.removeRow(1) assert not remove assert mgr in mgrs def test_artist_cleared_on_remove(): mgr = LayerArtist(None) mgrs = [mgr] model = LayerArtistModel(mgrs) model.removeRow(0) assert mgr.clear_count == 1 def test_change_label(): model, (mgr,) = setup_model(1) lbl = mgr.layer.label model.change_label(0, 'new label') assert mgr.layer.label != lbl def test_change_label_invalid_row(): model, (mgr,) = setup_model(1) lbl = mgr.layer.label model.change_label(1, 'new label') assert mgr.layer.label == lbl def test_flags(): model, layer_artists = setup_model(1) expected = (Qt.ItemIsEditable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemNeverHasChildren) assert model.flags(model.index(0)) == expected def test_move_artist_empty(): mgrs = [] model = LayerArtistModel(mgrs) model.move_artist(None, 0) assert mgrs == [] def test_move_artist_single(): m0 = LayerArtist(Data(label="test 0")) mgrs = [m0] model = LayerArtistModel(mgrs) model.move_artist(m0, 0) assert mgrs == [m0] model.move_artist(m0, -1) assert mgrs == [m0] model.move_artist(m0, 1) assert mgrs == [m0] model.move_artist(m0, 2) assert mgrs == [m0] def test_move_artist_two(): model, mgrs = setup_model(2) m0, m1 = mgrs model.move_artist(m0, 0) assert mgrs == [m0, m1] model.move_artist(m0, 1) assert mgrs == [m0, m1] model.move_artist(m0, 2) assert mgrs == [m1, m0] model.move_artist(m0, 0) assert mgrs == [m0, m1] def test_move_artist_three(): model, mgrs = setup_model(3) m0, m1, m2 = mgrs model.move_artist(m0, 0) assert mgrs == [m0, m1, m2] model.move_artist(m0, 1) assert mgrs == [m0, m1, m2] model.move_artist(m0, 2) assert mgrs == [m1, m0, m2] model.move_artist(m0, 0) model.move_artist(m0, 3) assert mgrs == [m1, m2, m0] model.move_artist(m0, 0) model.move_artist(m2, 0) assert mgrs == [m2, m0, m1] def test_move_updates_zorder(): m0 = LayerArtist(Data(label='test 0')) m1 = LayerArtist(Data(label='test 1')) m2 = LayerArtist(Data(label='test 2')) m0.zorder = 10 m1.zorder = 20 m2.zorder = 30 mgrs = [m0, m1, m2] model = LayerArtistModel(mgrs) model.move_artist(m2, 0) assert m2.zorder == 30 assert m0.zorder == 20 assert m1.zorder == 10 def test_check_syncs_to_visible(): m0 = LayerArtist(Data(label='test 0')) m0.artists = [MagicMock()] mgrs = [m0] model = LayerArtistModel(mgrs) m0.visible = True assert m0.visible assert model.data(model.index(0), Qt.CheckStateRole) == Qt.Checked m0.visible = False assert not m0.visible assert model.data(model.index(0), Qt.CheckStateRole) == Qt.Unchecked model.setData(model.index(0), Qt.Checked, Qt.CheckStateRole) assert m0.visible def test_data(): model, mgrs = setup_model(3) idx = model.index(3) assert model.data(idx, Qt.DisplayRole) is None idx = model.index(1) assert model.data(idx, Qt.DisplayRole) == model.row_label(1) assert model.data(idx, Qt.EditRole) == model.row_label(1) class TestLayerArtistView(object): def setup_method(self, method): self.model, self.artists = setup_model(2) self.hub = Hub() self.view = LayerArtistView(hub=self.hub) self.view.setModel(self.model) def test_current_row(self): for row in [0, 1]: idx = self.model.index(row) self.view.setCurrentIndex(idx) self.view.current_row() == row assert self.view.current_artist() is self.artists[row] glueviz-1.0.1+dfsg.orig/glue/core/qt/layer_artist_model.py0000644000175000017500000003634713657331477023205 0ustar noahfxnoahfx""" This module provides two classes for managing LayerArtists with Qt. The LayerArtistModel implements a QtModel to interface with a list of LayerManagers. The LayerArtistView is a list widget that displays these layers, and provides GUI access to the model """ # pylint: disable=I0011, W0613, R0913, R0904, W0611 import textwrap from weakref import WeakKeyDictionary from qtpy.QtCore import Qt from qtpy import QtCore, QtWidgets, QtGui from glue.core.layer_artist import LayerArtistBase, LayerArtistContainer from glue.core.qt.style_dialog import StyleDialog from glue.icons.qt import layer_artist_icon from glue.core.qt.mime import LAYERS_MIME_TYPE from glue.utils import nonpartial from glue.utils.qt import PythonListModel, PyMimeData from glue.core.hub import HubListener from glue.core.message import (LayerArtistEnabledMessage, LayerArtistUpdatedMessage, LayerArtistDisabledMessage, LayerArtistVisibilityMessage) class LayerArtistModel(PythonListModel): """A Qt model to manage a list of LayerArtists. Multiple views into this model should stay in sync, thanks to Qt. To properly maintain sync, any client that uses this list of LayerArtists should always edit the list in-place (so that the list managed by this model and the client are the same object) """ def __init__(self, artists, parent=None): super(LayerArtistModel, self).__init__(artists, parent) self.artists = artists def data(self, index, role): """Retrieve data at each index""" if not index.isValid() or index.row() >= len(self.artists): return None if role == Qt.DecorationRole: art = self.artists[index.row()] result = layer_artist_icon(art) return result if role == Qt.CheckStateRole: art = self.artists[index.row()] result = Qt.Checked if art.visible and art.enabled else Qt.Unchecked return result if role == Qt.ToolTipRole: art = self.artists[index.row()] if not art.enabled: wrapped = textwrap.fill(art.disabled_message, break_long_words=False) return wrapped return super(LayerArtistModel, self).data(index, role) def flags(self, index): result = super(LayerArtistModel, self).flags(index) if index.isValid() and index.row() < len(self.artists): art = self.artists[index.row()] if art.enabled: result = (result | Qt.ItemIsEditable | Qt.ItemIsDragEnabled | Qt.ItemIsUserCheckable) else: result = int(result & Qt.ItemIsUserCheckable) ^ int(result) else: # only drop between rows, where index isn't valid result = result | Qt.ItemIsDropEnabled return Qt.ItemFlags(int(result)) def setData(self, index, value, role): if not index.isValid(): return False if role == Qt.EditRole: self.change_label(index.row(), str(value)) if role == Qt.CheckStateRole: vis = value == Qt.Checked self.artists[index.row()].visible = vis self.artists[index.row()].redraw() self.dataChanged.emit(index, index) return True def _remove_row(self, row): art = self.artists.pop(row) art.remove() art.redraw() def mimeTypes(self): return [PyMimeData.MIME_TYPE, LAYERS_MIME_TYPE] def mimeData(self, indexes): arts = [self.artists[index.row()] for index in indexes] layers = [a.layer for a in arts] if len(indexes) == 0: return 0 return PyMimeData(arts, **{LAYERS_MIME_TYPE: layers}) def supportedDropActions(self): return Qt.MoveAction def dropMimeData(self, data, action, row, column, index): data = data.data(PyMimeData.MIME_TYPE) # list of a single artist. Move if isinstance(data, list) and len(data) == 1 and \ isinstance(data[0], LayerArtistBase) and \ data[0] in self.artists: self.move_artist(data[0], row) return True return False def move_artist(self, artist, row): """Move an artist before the entry in row Row could be the end of the list (-> put it at the end) """ if len(self.artists) < 2: # can't rearrange lenght 0 or 1 list return try: loc = self.artists.index(artist) except ValueError: return dest = row if not self.beginMoveRows(QtCore.QModelIndex(), loc, loc, QtCore.QModelIndex(), dest): return if dest >= loc: row -= 1 self.artists.pop(loc) self.artists.insert(row, artist) self._update_zorder() self.endMoveRows() def _update_zorder(self): """Redistribute zorders to match location in the list""" zs = [m.zorder for m in self.artists] zs = reversed(sorted(zs)) for z, m in zip(zs, self.artists): m.zorder = z if len(self.artists) > 0: self.artists[0].redraw() def row_label(self, row): """ The textual label for the row""" artist = self.artists[row] if hasattr(artist, 'label'): return artist.label layer = artist.layer if hasattr(layer, 'verbose_label'): return layer.verbose_label return layer.label def change_label(self, row, label): """ Reassign the labeel for whatever layer the artist manages""" try: art = self.artists[row] art.layer.label = label except IndexError: pass def add_artist(self, row, artist): """Add a new artist""" self.beginInsertRows(QtCore.QModelIndex(), row, row) self.artists.insert(row, artist) self.endInsertRows() self.rowsInserted.emit(self.index(row), row, row) def row_artist(self, row): return self.artists[row] def artist_row(self, artist): return self.artists.index(artist) class LayerArtistView(QtWidgets.QListView, HubListener): """A list view into an artist model. The zorder of each artist can be shuffled by dragging and dropping items. Right-clicking brings up a menu to edit style or delete""" def __init__(self, parent=None, hub=None): super(LayerArtistView, self).__init__(parent) self.setDragEnabled(True) self.setAcceptDrops(True) self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) self.setIconSize(QtCore.QSize(15, 15)) self.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) self.setContextMenuPolicy(Qt.ActionsContextMenu) self.setEditTriggers(self.NoEditTriggers) self.setMinimumSize(200, 100) self._actions = {} self._create_actions() # Update when any message is emitted which would indicate a change in # data collection content, colors, labels, etc. It's easier to simply # listen to all events since the viewport update is fast. self.hub = hub self.hub.subscribe(self, LayerArtistUpdatedMessage, self._update_viewport) self.hub.subscribe(self, LayerArtistEnabledMessage, self._layer_enabled_or_disabled) self.hub.subscribe(self, LayerArtistDisabledMessage, self._layer_enabled_or_disabled) self.hub.subscribe(self, LayerArtistVisibilityMessage, self._layer_enabled_or_disabled) def _update_viewport(self, *args): # This forces the widget containing the list view to update/redraw, # reflecting any changes in color/labels/content self.viewport().update() def _layer_enabled_or_disabled(self, *args): # This forces the widget containing the list view to update/redraw, # reflecting any changes in disabled/enabled layers. If a layer is # disabled, it will become unselected. self.viewport().update() # If a layer artist becomes unselected as a result of the update above, # a selection change event is not emitted for some reason, so we force # a manual update. If a layer artist was deselected, current_artist() # will be None and the options will be hidden for that layer. parent = self.parent() if parent is not None: parent.on_selection_change(self.current_artist()) def rowsInserted(self, index, start, end): super(LayerArtistView, self).rowsInserted(index, start, end) # If no rows are currently selected, make sure we select one. We do # this to make sure the layer style editor is visible to users straight # away. parent = self.parent() if parent is not None: parent.on_artist_add(self.model().artists) if self.current_row() is None: self.setCurrentIndex(self.model().index(0)) def selectionChanged(self, selected, deselected): super(LayerArtistView, self).selectionChanged(selected, deselected) parent = self.parent() if parent is not None: parent.on_selection_change(self.current_artist()) def current_artist(self): model = self.selectionModel() if model is None: return rows = model.selectedRows() if len(rows) != 1: return return self.model().row_artist(rows[0].row()) def select_artist(self, artist): model = self.selectionModel() row = self.model().artist_row(artist) model.select(model.model().index(row), model.ClearAndSelect) def single_selection(self): return self.current_artist() is not None def current_row(self): model = self.selectionModel() if model is None: return rows = model.selectedRows() if len(rows) != 1: return return rows[0].row() def _bottom_left_of_current_index(self): idx = self.currentIndex() if not idx.isValid(): return rect = self.visualRect(idx) pos = self.mapToGlobal(rect.bottomLeft()) pos.setY(pos.y() + 1) return pos def _edit_style(self): pos = self._bottom_left_of_current_index() if pos is None: return item = self.current_artist().layer StyleDialog.dropdown_editor(item, pos, edit_label=False) def _create_actions(self): act = QtWidgets.QAction('Edit style', self) act.triggered.connect(nonpartial(self._edit_style)) self.addAction(act) act = QtWidgets.QAction('Remove', self) act.setShortcut(QtGui.QKeySequence(Qt.Key_Backspace)) act.setShortcutContext(Qt.WidgetShortcut) act.triggered.connect( lambda *args: self.model().removeRow(self.current_row())) self.addAction(act) class LayerArtistWidget(QtWidgets.QWidget): """ A widget that includes a list of artists (LayerArtistView) and the visual options for the layer artists. """ def __init__(self, parent=None, layer_style_widget_cls=None, hub=None): super(LayerArtistWidget, self).__init__(parent=parent) self.hub = None self.layout = QtWidgets.QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.layer_style_widget_cls = layer_style_widget_cls self.layer_list = LayerArtistView(parent=self, hub=hub) self.layout.addWidget(self.layer_list) self.layer_options = QtWidgets.QWidget() self.layer_options_layout = QtWidgets.QStackedLayout() self.layer_options.setLayout(self.layer_options_layout) self.layout.addWidget(self.layer_options) self.setLayout(self.layout) self.layout_style_widgets = WeakKeyDictionary() self.empty = QtWidgets.QWidget() self.layer_options_layout.addWidget(self.empty) self.disabled_warning = QtWidgets.QLabel() self.disabled_warning.setWordWrap(True) self.padded_warning = QtWidgets.QWidget() warning_layout = QtWidgets.QVBoxLayout() warning_layout.setContentsMargins(20, 20, 20, 20) warning_layout.addWidget(self.disabled_warning) self.padded_warning.setLayout(warning_layout) self.layer_options_layout.addWidget(self.padded_warning) def on_artist_add(self, layer_artists): if self.layer_style_widget_cls is None: return for layer_artist in layer_artists: if layer_artist not in self.layout_style_widgets: if isinstance(self.layer_style_widget_cls, dict): layer_artist_cls = layer_artist.__class__ if layer_artist_cls in self.layer_style_widget_cls: layer_style_widget_cls = self.layer_style_widget_cls[layer_artist_cls] else: return else: layer_style_widget_cls = self.layer_style_widget_cls self.layout_style_widgets[layer_artist] = layer_style_widget_cls(layer_artist) self.layer_options_layout.addWidget(self.layout_style_widgets[layer_artist]) def on_selection_change(self, layer_artist): if layer_artist in self.layout_style_widgets: if layer_artist.enabled: if layer_artist.visible: self.disabled_warning.setText('') self.layer_options_layout.setCurrentWidget(self.layout_style_widgets[layer_artist]) else: self.disabled_warning.setText('Layer is not currently visible. ' 'Click on the checkbox for this ' 'layer to make it visible') self.disabled_warning.setAlignment(Qt.AlignLeft) self.layer_options_layout.setCurrentWidget(self.padded_warning) else: self.disabled_warning.setText(layer_artist.disabled_message) self.disabled_warning.setAlignment(Qt.AlignJustify) self.layer_options_layout.setCurrentWidget(self.padded_warning) else: self.layer_options_layout.setCurrentWidget(self.empty) self.disabled_warning.setText('') class QtLayerArtistContainer(LayerArtistContainer): """A subclass of LayerArtistContainer that dispatches to a LayerArtistModel""" def __init__(self): super(QtLayerArtistContainer, self).__init__() self.model = LayerArtistModel(self.artists) self.model.rowsInserted.connect(nonpartial(self._notify)) self.model.rowsRemoved.connect(nonpartial(self._notify)) self.model.modelReset.connect(nonpartial(self._notify)) def append(self, artist): self.model.add_artist(0, artist) artist.zorder = max(a.zorder for a in self.artists) + 1 assert self.artists[0] is artist self._notify() def remove(self, artist): if artist in self.artists: index = self.artists.index(artist) self.model.removeRow(index) assert artist not in self.artists artist.remove() self._notify() def __nonzero__(self): return True __bool__ = __nonzero__ glueviz-1.0.1+dfsg.orig/glue/core/qt/message_widget.py0000644000175000017500000000270313605357235022270 0ustar noahfxnoahfximport os from time import ctime from qtpy import QtWidgets from glue import core from glue.utils.qt import load_ui class MessageWidget(QtWidgets.QWidget, core.hub.HubListener): """ This simple class displays all messages broadcast by a hub. It is mainly intended for debugging """ def __init__(self): QtWidgets.QWidget.__init__(self) self.ui = load_ui('message_widget.ui', self, directory=os.path.dirname(__file__)) self.ui.messageTable.setColumnCount(3) labels = ['Time', 'Message', 'Sender'] self.ui.messageTable.setHorizontalHeaderLabels(labels) def register_to_hub(self, hub): # catch all messages hub.subscribe(self, core.message.Message, handler=self.process_message, filter=lambda x: True) def process_message(self, message): row = self.ui.messageTable.rowCount() * 0 self.ui.messageTable.insertRow(0) tm = QtWidgets.QTableWidgetItem(ctime().split()[3]) typ = str(type(message)).split("'")[-2].split('.')[-1] mtyp = QtWidgets.QTableWidgetItem(typ) typ = str(type(message.sender)).split("'")[-2].split('.')[-1] sender = QtWidgets.QTableWidgetItem(typ) self.ui.messageTable.setItem(row, 0, tm) self.ui.messageTable.setItem(row, 1, mtyp) self.ui.messageTable.setItem(row, 2, sender) self.ui.messageTable.resizeColumnsToContents() glueviz-1.0.1+dfsg.orig/glue/core/qt/fitters.py0000644000175000017500000001177313605357235020770 0ustar noahfxnoahfxfrom qtpy import QtWidgets, QtGui from glue.core.qt.simpleforms import build_form_item __all__ = ['ConstraintsWidget', 'FitSettingsWidget'] class ConstraintsWidget(QtWidgets.QWidget): """ A widget to display and tweak the constraints of a :class:`~glue.core.fitters.BaseFitter1D` """ def __init__(self, constraints, parent=None): """ Parameters ---------- constraints : dict The `contstraints` property of a :class:`~glue.core.fitters.BaseFitter1D` object parent : QtWidgets.QWidget (optional) The parent of this widget """ super(ConstraintsWidget, self).__init__(parent) self.constraints = constraints self.layout = QtWidgets.QGridLayout() self.layout.setContentsMargins(2, 2, 2, 2) self.layout.setSpacing(4) self.setLayout(self.layout) self.layout.addWidget(QtWidgets.QLabel("Estimate"), 0, 1) self.layout.addWidget(QtWidgets.QLabel("Fixed"), 0, 2) self.layout.addWidget(QtWidgets.QLabel("Bounded"), 0, 3) self.layout.addWidget(QtWidgets.QLabel("Lower Bound"), 0, 4) self.layout.addWidget(QtWidgets.QLabel("Upper Bound"), 0, 5) self._widgets = {} names = sorted(list(self.constraints.keys())) for k in names: row = [] w = QtWidgets.QLabel(k) row.append(w) v = QtGui.QDoubleValidator() e = QtWidgets.QLineEdit() e.setValidator(v) e.setText(str(constraints[k]['value'] or '')) row.append(e) w = QtWidgets.QCheckBox() w.setChecked(constraints[k]['fixed']) fix = w row.append(w) w = QtWidgets.QCheckBox() limits = constraints[k]['limits'] w.setChecked(limits is not None) bound = w row.append(w) e = QtWidgets.QLineEdit() e.setValidator(v) if limits is not None: e.setText(str(limits[0])) row.append(e) e = QtWidgets.QLineEdit() e.setValidator(v) if limits is not None: e.setText(str(limits[1])) row.append(e) def unset(w): def result(active): if active: w.setChecked(False) return result fix.toggled.connect(unset(bound)) bound.toggled.connect(unset(fix)) self._widgets[k] = row for i, row in enumerate(names, 1): for j, widget in enumerate(self._widgets[row]): self.layout.addWidget(widget, i, j) def settings(self, name): """ Return the constraints for a single model parameter """ row = self._widgets[name] name, value, fixed, limited, lo, hi = row value = float(value.text()) if value.text() else None fixed = fixed.isChecked() limited = limited.isChecked() lo = lo.text() hi = hi.text() limited = limited and not ((not lo) or (not hi)) limits = None if not limited else [float(lo), float(hi)] return dict(value=value, fixed=fixed, limits=limits) def update_constraints(self, fitter): """ Update the constraints in a :class:`~glue.core.fitters.BaseFitter1D` based on the settings in this widget """ for name in self._widgets: s = self.settings(name) fitter.set_constraint(name, **s) class FitSettingsWidget(QtWidgets.QDialog): def __init__(self, fitter, parent=None): super(FitSettingsWidget, self).__init__(parent) self.fitter = fitter self._build_form() self._connect() self.setModal(True) def _build_form(self): fitter = self.fitter l = QtWidgets.QFormLayout() options = fitter.options self.widgets = {} self.forms = {} for k in sorted(options): item = build_form_item(fitter, k) l.addRow(item.label, item.widget) self.widgets[k] = item.widget self.forms[k] = item # need to prevent garbage collection constraints = fitter.constraints if constraints: self.constraints = ConstraintsWidget(constraints) l.addRow(self.constraints) else: self.constraints = None self.okcancel = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) l.addRow(self.okcancel) self.setLayout(l) def _connect(self): self.okcancel.accepted.connect(self.accept) self.okcancel.rejected.connect(self.reject) self.accepted.connect(self.update_fitter_from_settings) def update_fitter_from_settings(self): for k, v in self.widgets.items(): setattr(self.fitter, k, v.value()) if self.constraints is not None: self.constraints.update_constraints(self.fitter) glueviz-1.0.1+dfsg.orig/glue/core/qt/message_widget.ui0000644000175000017500000000105513455362716022257 0ustar noahfxnoahfx MessageWidget 0 0 700 400 Message Widget glueviz-1.0.1+dfsg.orig/glue/core/qt/data_collection_model.py0000644000175000017500000003671013605357235023612 0ustar noahfxnoahfx# pylint: disable=E1101,F0401 from qtpy import QtCore, QtGui, QtWidgets from qtpy.QtCore import Qt from glue.core.hub import HubListener from glue.core import message as m from glue.core.decorators import memoize from glue import core from glue.core.qt.mime import LAYERS_MIME_TYPE from glue.icons.qt import layer_icon from glue.core.qt.style_dialog import StyleDialog from glue.utils.qt import PyMimeData from glue.core.message import Message DATA_IDX = 0 SUBSET_IDX = 1 def full_edit_factory(item, pos): StyleDialog.dropdown_editor(item, pos) def restricted_edit_factory(item, pos): StyleDialog.dropdown_editor(item, pos, edit_label=False) class Item(object): edit_factory = None glue_data = None flags = Qt.ItemIsEnabled tooltip = None def font(self): return QtGui.QFont() def icon(self): return None @property def label(self): return self._label class DataCollectionItem(Item): def __init__(self, dc): self.dc = dc self.row = 0 self.column = 0 self.parent = None self._label = '' self.children_count = 2 @memoize def child(self, row): if row == DATA_IDX: return DataListItem(self.dc, self) if row == SUBSET_IDX: return SubsetListItem(self.dc, self) return None class DataListItem(Item): def __init__(self, dc, parent): self.dc = dc self.parent = parent self.row = DATA_IDX self.column = 0 self._label = 'Data' @memoize def child(self, row): if row < len(self.dc): return DataItem(self.dc, row, self) @property def children_count(self): return len(self.dc) def font(self): result = QtGui.QFont() result.setBold(True) return result class DataItem(Item): edit_factory = full_edit_factory flags = (Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled) def __init__(self, dc, row, parent): self.dc = dc self.row = row self.parent = parent self.column = 0 self.children_count = 0 @property def data(self): return self.dc[self.row] @property def glue_data(self): return self.data @property def label(self): return self.data.label @label.setter def label(self, value): self.data.label = value @property def tooltip(self): # Return the label as the tooltip - this is useful if filenames are # really long and don't fit in the window. return self.label @property def style(self): return self.data.style def icon(self): return layer_icon(self.data) class SubsetListItem(Item): def __init__(self, dc, parent): self.dc = dc self.parent = parent self.row = SUBSET_IDX self._label = 'Subsets' self.column = 0 @memoize def child(self, row): if row < len(self.dc.subset_groups): return SubsetGroupItem(self.dc, row, self) @property def children_count(self): return len(self.dc.subset_groups) def font(self): result = QtGui.QFont() result.setBold(True) return result class SubsetGroupItem(Item): edit_factory = full_edit_factory flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable def __init__(self, dc, row, parent): self.parent = parent self.dc = dc self.row = row self.column = 0 @property def subset_group(self): return self.dc.subset_groups[self.row] @property def glue_data(self): return self.subset_group @property def label(self): return self.subset_group.label @label.setter def label(self, value): self.subset_group.label = value @property def tooltip(self): if type(self.subset_group.subset_state) == core.subset.SubsetState: return "Empty subset" atts = self.subset_group.subset_state.attributes atts = [a for a in atts if isinstance(a, core.ComponentID)] if len(atts) > 0: lbl = ', '.join(a.label for a in atts) return "Selection on %s" % lbl @property def style(self): return self.subset_group.style @property def children_count(self): return len(self.subset_group.subsets) @memoize def child(self, row): return SubsetItem(self.dc, self.subset_group, row, self) def icon(self): return layer_icon(self.subset_group) class SubsetItem(Item): edit_factory = restricted_edit_factory flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled def __init__(self, dc, subset_group, subset_idx, parent): self.parent = parent self.subset_group = subset_group self.row = subset_idx self.parent = parent self.children_count = 0 self.column = 0 @property def subset(self): return self.subset_group.subsets[self.row] @property def label(self): return self.subset.verbose_label def icon(self): return layer_icon(self.subset) @property def style(self): return self.subset.style @property def glue_data(self): return self.subset class DataCollectionModel(QtCore.QAbstractItemModel, HubListener): new_item = QtCore.Signal(QtCore.QModelIndex) def __init__(self, data_collection, parent=None): QtCore.QAbstractItemModel.__init__(self, parent) HubListener.__init__(self) self.data_collection = data_collection self.root = DataCollectionItem(data_collection) self._items = {} # map hashes of Model pointers to model items # without this reference, PySide clobbers instance # data of model items self.register_to_hub(self.data_collection.hub) def supportedDragActions(self): return Qt.CopyAction def index(self, row, column, parent=QtCore.QModelIndex()): if column != 0: return QtCore.QModelIndex() if not parent.isValid(): parent_item = self.root else: parent_item = self._get_item(parent) if parent_item is None: return QtCore.QModelIndex() child_item = parent_item.child(row) if child_item: return self._make_index(row, column, child_item) else: return QtCore.QModelIndex() def _get_item(self, index): if not index.isValid(): return None return self._items.get(id(index.internalPointer()), None) def _make_index(self, row, column, item): if item is not None: result = self.createIndex(row, column, item) self._items[id(result.internalPointer())] = item assert result.internalPointer() is item return result return self.createIndex(row, column) def to_indices(self, items): """Translate a list of Data, Subset, or SubsetGroups to a list of indices""" result = [] for item in items: if isinstance(item, core.Data): idx = self.data_index(list(self.data_collection).index(item)) elif isinstance(item, core.SubsetGroup): idx = self.subsets_index( self.data_collection.subset_groups.index(item)) elif isinstance(item, core.subset_group.GroupedSubset): grp = item.group idx = self.subsets_index( self.data_collection.subset_groups.index(grp)) row = list(self.data_collection).index(item.data) idx = self.index(row, idx) else: raise NotImplementedError(type(item)) result.append(idx) return result def flags(self, index=QtCore.QModelIndex()): item = self._get_item(index) if item is None: return Qt.NoItemFlags else: return item.flags def data(self, index, role): if not index.isValid(): return dispatch = { Qt.DisplayRole: self._display_data, Qt.FontRole: self._font_data, Qt.DecorationRole: self._icon_data, Qt.UserRole: self._user_data, Qt.ToolTipRole: self._tooltip_data} if role in dispatch: return dispatch[role](index) def setData(self, index, value, role=Qt.EditRole): if role != Qt.EditRole: return False try: self._get_item(index).label = value return True except AttributeError: return False def _tooltip_data(self, index): tooltip = self._get_item(index).tooltip return tooltip def _user_data(self, index): return self._get_item(index) def _display_data(self, index): return self._get_item(index).label def _font_data(self, index): item = self._get_item(index) return item.font() def _icon_data(self, index): return self._get_item(index).icon() def headerData(self, section, orientation, role=Qt.DisplayRole): return '' def data_index(self, data_number=None): """ Fetch the QtCore.QModelIndex for a given data index, or the index for the parent data item :param data_number: position of data set to fetch, or None """ base = self.index(DATA_IDX, 0) if data_number is None: return base return self.index(data_number, 0, base) def subsets_index(self, subset_number=None): """ Fetch the QtCore.QModelIndex for a given subset, or the index for the parent subset item :param data_number: position of subset group to fetch, or None """ base = self.index(SUBSET_IDX, 0) assert isinstance(self._get_item(base), SubsetListItem) if subset_number is None: return base return self.index(subset_number, 0, base) def rowCount(self, index=QtCore.QModelIndex()): item = self._get_item(index) if item is None: return self.root.children_count return item.children_count def parent(self, index=None): if index is None: # overloaded QtCore.QObject.parent() return QtCore.QObject.parent(self) item = self._get_item(index) if item is None: return QtCore.QModelIndex() return self._make_index(item.row, item.column, item.parent) def columnCount(self, index): return 1 def register_to_hub(self, hub): for msg in [m.DataCollectionDeleteMessage, m.SubsetDeleteMessage]: hub.subscribe(self, msg, self.invalidate) hub.subscribe(self, m.DataCollectionAddMessage, self._on_add_data) hub.subscribe(self, m.SubsetCreateMessage, self._on_add_subset) def _on_add_data(self, message): self.invalidate() idx = self.data_index(len(self.data_collection) - 1) self.new_item.emit(idx) def _on_add_subset(self, message): self.invalidate() idx = self.subsets_index(len(self.data_collection.subset_groups) - 1) self.new_item.emit(idx) def invalidate(self, *args): self.root = DataCollectionItem(self.data_collection) self._items.clear() self.layoutChanged.emit() def glue_data(self, indices): """ Given a list of indices, return a list of all selected Data, Subset, and SubsetGroup objects. """ items = [self._get_item(idx) for idx in indices] items = [item.glue_data for item in items] return items def mimeData(self, indices): data = self.glue_data(indices) result = PyMimeData(data, **{LAYERS_MIME_TYPE: data}) self._mime = result # hold reference to prevent segfault return result def mimeTypes(self): return [LAYERS_MIME_TYPE] class DataCollectionView(QtWidgets.QTreeView, HubListener): selection_changed = QtCore.Signal() def __init__(self, parent=None): super(DataCollectionView, self).__init__(parent) self.doubleClicked.connect(self._edit) # only edit label on model.new_item self.setItemDelegate(LabeledDelegate()) self.setEditTriggers(self.NoEditTriggers) self.setIconSize(QtCore.QSize(16, 16)) def selected_layers(self): idxs = self.selectedIndexes() return self._model.glue_data(idxs) def set_selected_layers(self, layers): sm = self.selectionModel() idxs = self._model.to_indices(layers) self.select_indices(*idxs) def select_indices(self, *indices): sm = self.selectionModel() sm.clearSelection() for idx in indices: sm.select(idx, sm.Select) def set_data_collection(self, data_collection): self._model = DataCollectionModel(data_collection) self.setModel(self._model) sm = QtCore.QItemSelectionModel(self._model) sm.selectionChanged.connect(lambda *args: self.selection_changed.emit()) self.setSelectionModel(sm) self.setRootIsDecorated(False) self.setExpandsOnDoubleClick(False) self.expandToDepth(0) self._model.layoutChanged.connect(lambda: self.expandToDepth(0)) self._model.layoutChanged.connect(self.selection_changed.emit) self._model.new_item.connect(self.select_indices) self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.setDragEnabled(True) self.setDropIndicatorShown(True) self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly) # Update when any message is emitted which would indicate a change in # data collection content, colors, labels, etc. It's easier to simply # listen to all events since the viewport update is fast. data_collection.hub.subscribe(self, Message, handler=self._update_viewport) def _update_viewport(self, *args, **kwargs): # This forces the widget containing the list view to update/redraw, # reflecting any changes in color/labels/content try: self.viewport().update() except RuntimeError: pass def edit_label(self, index): if not (self._model.flags(index) & Qt.ItemIsEditable): return self.edit(index) def _edit(self, index): item = self._model.data(index, role=Qt.UserRole) if item is None or item.edit_factory is None: return rect = self.visualRect(index) pos = self.mapToGlobal(rect.bottomLeft()) pos.setY(pos.y() + 1) item.edit_factory(pos) class LabeledDelegate(QtWidgets.QStyledItemDelegate): """ Add placeholder text to default delegate """ def setEditorData(self, editor, index): super(LabeledDelegate, self).setEditorData(editor, index) label = index.model().data(index, role=Qt.DisplayRole) editor.selectAll() editor.setText(label) if __name__ == "__main__": from glue.utils.qt import get_qapp from qtpy import QtWidgets from glue.core import Data, DataCollection app = get_qapp() dc = DataCollection() dc.append(Data(label='w')) view = DataCollectionView() view.set_data_collection(dc) view.show() view.raise_() dc.extend([Data(label='x', x=[1, 2, 3]), Data(label='y', y=[1, 2, 3]), Data(label='z', z=[1, 2, 3])]) app.exec_() glueviz-1.0.1+dfsg.orig/glue/core/qt/dialogs.py0000644000175000017500000000266013605357235020725 0ustar noahfxnoahfx# Infrastructure for helpful warnings/dialogs that can be hidden if needed # (and therefore connect to the settings). from qtpy.QtWidgets import QMessageBox, QCheckBox from glue.config import settings from glue._settings_helpers import save_settings __all__ = ['info', 'warn'] def info(title, text, setting=None, default=None): return dialog(title, text, QMessageBox.Information, setting=setting, default=default) def warn(title, text, setting=None, default=None): return dialog(title, text, QMessageBox.Warning, setting=setting, default=default) def dialog(title, text, icon, setting=None, default=None): if not getattr(settings, setting.upper()): return True check = QCheckBox() check.setText('Dont show this message again (can be reset via the preferences)') info = QMessageBox() info.setIcon(icon) info.setText(title) info.setInformativeText(text) info.setCheckBox(check) info.setStandardButtons(info.Cancel | info.Ok) if default == 'Cancel': info.setDefaultButton(info.Cancel) result = info.exec_() if result == info.Cancel: return False if check.isChecked(): setattr(settings, setting.upper(), False) save_settings() return True if __name__ == "__main__": from glue.utils.qt import get_qapp app = get_qapp() info('What happens next?', 'These are instructions on what happens next', setting='show_info_profile_open') glueviz-1.0.1+dfsg.orig/glue/core/qt/__init__.py0000644000175000017500000000000013455362716021027 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/qt/simpleforms.py0000644000175000017500000000342313605357235021641 0ustar noahfxnoahfxfrom qtpy import QtCore, QtWidgets from glue.core.simpleforms import IntOption, FloatOption, BoolOption from glue.utils import nonpartial _dispatch = {} class FormItem(QtCore.QObject): changed = QtCore.Signal() def __init__(self, instance, option): super(FormItem, self).__init__() self.option = option self.instance = instance @property def label(self): return self.option.label class NumberFormItem(FormItem): widget_cls = None def __init__(self, instance, option): super(NumberFormItem, self).__init__(instance, option) value = option.__get__(instance) w = self.widget_cls() w.setRange(option.min, option.max) w.setValue(value) w.valueChanged.connect(nonpartial(self.changed.emit)) self.widget = w @property def value(self): return self.widget.value() class IntFormItem(NumberFormItem): widget_cls = QtWidgets.QSpinBox class FloatFormItem(NumberFormItem): widget_cls = QtWidgets.QDoubleSpinBox class BoolFormItem(FormItem): def __init__(self, instance, option): super(BoolFormItem, self).__init__(instance, option) value = option.__get__(instance) self.widget = QtWidgets.QCheckBox() self.widget.setChecked(value) self.widget.clicked.connect(nonpartial(self.changed.emit)) @property def value(self): return self.widget.isChecked() def build_form_item(instance, option_name): option = getattr(type(instance), option_name) option_type = type(option) return _dispatch[option_type](instance, option) def register(option_cls, form_cls): _dispatch[option_cls] = form_cls register(IntOption, IntFormItem) register(FloatOption, FloatFormItem) register(BoolOption, BoolFormItem) glueviz-1.0.1+dfsg.orig/glue/core/qt/style_dialog.py0000644000175000017500000001034713605357235021763 0ustar noahfxnoahfxfrom qtpy.QtCore import Qt from qtpy import QtCore, QtWidgets from glue.icons.qt import POINT_ICONS, symbol_icon from glue.utils.qt import mpl_to_qt_color, qt_to_mpl_color class ColorWidget(QtWidgets.QLabel): mousePressed = QtCore.Signal() def mousePressEvent(self, event): self.mousePressed.emit() event.accept() class StyleDialog(QtWidgets.QDialog): """Dialog which edits the style of a layer (Data or Subset) Use via StyleDialog.edit_style(layer) """ def __init__(self, layer, parent=None, edit_label=True): super(StyleDialog, self).__init__(parent) self.setWindowTitle("Style Editor") self.layer = layer self._edit_label = edit_label self._symbols = list(POINT_ICONS.keys()) self._setup_widgets() self._connect() def _setup_widgets(self): self.layout = QtWidgets.QFormLayout() self.size_widget = QtWidgets.QSpinBox() self.size_widget.setMinimum(1) self.size_widget.setMaximum(40) self.size_widget.setValue(self.layer.style.markersize) self.label_widget = QtWidgets.QLineEdit() self.label_widget.setText(self.layer.label) self.label_widget.selectAll() self.color_widget = ColorWidget() self.color_widget.setStyleSheet('ColorWidget {border: 1px solid;}') color = self.layer.style.color color = mpl_to_qt_color(color, alpha=self.layer.style.alpha) self.set_color(color) self.okcancel = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) if self._edit_label: self.layout.addRow("Label", self.label_widget) self.layout.addRow("Color", self.color_widget) self.layout.addRow("Size", self.size_widget) self.layout.addWidget(self.okcancel) self.setLayout(self.layout) self.layout.setContentsMargins(6, 6, 6, 6) def _connect(self): self.color_widget.mousePressed.connect(self.query_color) self.okcancel.accepted.connect(self.accept) self.okcancel.rejected.connect(self.reject) self.setFocusPolicy(Qt.StrongFocus) def query_color(self, *args): color = QtWidgets.QColorDialog.getColor(self._color, self.color_widget, "", QtWidgets.QColorDialog.ShowAlphaChannel) if color.isValid(): self.set_color(color) def color(self): return self._color def set_color(self, color): self._color = color pm = symbol_icon('o', color).pixmap(30, 30) self.color_widget.setPixmap(pm) def size(self): return self.size_widget.value() def label(self): return str(self.label_widget.text()) def update_style(self): if self._edit_label: self.layer.label = self.label() self.layer.style.color = qt_to_mpl_color(self.color()) self.layer.style.alpha = self.color().alpha() / 255. self.layer.style.markersize = self.size() @classmethod def edit_style(cls, layer): self = cls(layer) result = self.exec_() if result == self.Accepted: self.update_style() @classmethod def dropdown_editor(cls, item, pos, **kwargs): """ Create a dropdown-style modal editor to edit the style of a given item :param item: Item with a .label and .style to edit :param pos: A QPoint to anchor the top-left corner of the dropdown at :param kwargs: Extra keywords to pass to StyleDialogs's constructor """ self = cls(item, **kwargs) self.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint) pos = self.mapFromGlobal(pos) self.move(pos) if self.exec_() == self.Accepted: self.update_style() if __name__ == "__main__": from glue.utils.qt import get_qapp from glue.core import Data app = get_qapp() d = Data(label='data label', x=[1, 2, 3, 4]) StyleDialog.edit_style(d) print("New layer properties") print(d.label) print('color: ', d.style.color) print('marker size: ', d.style.markersize) print('alpha ', d.style.alpha) glueviz-1.0.1+dfsg.orig/glue/core/qt/mime.py0000644000175000017500000000051113605357235020223 0ustar noahfxnoahfxfrom qtpy import QtWidgets from glue.utils.qt import PyMimeData, GlueItemWidget # some standard glue mime types LAYER_MIME_TYPE = 'glue/layer' LAYERS_MIME_TYPE = 'glue/layers' INSTANCE_MIME_TYPE = PyMimeData.MIME_TYPE class GlueMimeListWidget(GlueItemWidget, QtWidgets.QListWidget): SUPPORTED_MIME_TYPE = LAYERS_MIME_TYPE glueviz-1.0.1+dfsg.orig/glue/core/edit_subset_mode.py0000644000175000017500000001137413605357235022177 0ustar noahfxnoahfx""" These classes define the behavior of how new subset states affect the edit_subset of a Data object. """ import logging from glue.core.contracts import contract from glue.core.message import EditSubsetMessage from glue.core.data_collection import DataCollection from glue.core.data import Data from glue.utils import as_list __all__ = ['EditSubsetMode', 'NewMode', 'ReplaceMode', 'AndMode', 'OrMode', 'XorMode', 'AndNotMode'] class EditSubsetMode(object): """ Implements how new SubsetStates modify the edit_subset state """ def __init__(self): self.data_collection = None self._mode = ReplaceMode self._edit_subset = [] @property def mode(self): return self._mode @mode.setter def mode(self, value): if value is not self._mode: self._mode = value self._broadcast() @property def edit_subset(self): return self._edit_subset @edit_subset.setter def edit_subset(self, value): if value is self._edit_subset: return elif value is None: value = [] self._edit_subset = value self._broadcast() def _broadcast(self): if self.data_collection is not None: self.data_collection.hub.broadcast(EditSubsetMessage(self, self.edit_subset, self.mode)) def _combine_data(self, new_state, override_mode=None): """ Dispatches to the combine method of mode attribute. The behavior is dependent on the mode it dispatches to. By default, the method uses ReplaceMode, which overwrites the edit_subsets' subset_state with new_state :param edit_subset: The current edit_subset :param new_state: The new SubsetState :param override_mode: Mode to use instead of EditSubsetMode.mode """ mode = override_mode or self.mode if not self._edit_subset or mode is NewMode: if self.data_collection is None: raise RuntimeError("Must set data_collection before " "calling update") self.edit_subset = [self.data_collection.new_subset_group()] subs = self._edit_subset for s in as_list(subs): mode(s, new_state) @contract(d='inst($DataCollection, $Data)', new_state='isinstance(SubsetState)', focus_data='inst($Data)|None') def update(self, d, new_state, focus_data=None, override_mode=None): """ Apply a new subset state to editable subsets within a :class:`~glue.core.data.Data` or :class:`~glue.core.data_collection.DataCollection` instance :param d: Data or Collection to act upon :type d: Data or DataCollection :param new_state: Subset state to combine with :type new_state: :class:`~glue.core.subset.SubsetState` :param focus_data: The main data set in focus by the client, if relevant. If a data set is in focus and has no subsets, a new one will be created using new_state. :param override_mode: Mode to use instead of EditSubsetMode.mode :type override_mode: bool """ logging.getLogger(__name__).debug("Update subset for %s", d) if isinstance(d, (Data, DataCollection)): self._combine_data(new_state, override_mode=override_mode) else: raise TypeError("input must be a Data or DataCollection: %s" % type(d)) def NewMode(edit_subset, new_state): """ Replaces edit_subset.subset_state with new_state """ logging.getLogger(__name__).debug("New %s", edit_subset) edit_subset.subset_state = new_state.copy() def ReplaceMode(edit_subset, new_state): """ Replaces edit_subset.subset_state with new_state """ logging.getLogger(__name__).debug("Replace %s", edit_subset) edit_subset.subset_state = new_state.copy() def AndMode(edit_subset, new_state): """ Edit_subset.subset state is and-combined with new_state """ new_state.parent = edit_subset state = new_state & edit_subset.subset_state edit_subset.subset_state = state def OrMode(edit_subset, new_state): """ Edit_subset.subset state is or-combined with new_state """ new_state.parent = edit_subset state = new_state | edit_subset.subset_state edit_subset.subset_state = state def XorMode(edit_subset, new_state): """ Edit_subset.subset state is xor-combined with new_state """ new_state.parent = edit_subset state = new_state ^ edit_subset.subset_state edit_subset.subset_state = state def AndNotMode(edit_subset, new_state): """ Edit_subset.subset state is and-not-combined with new_state """ new_state.parent = edit_subset state = edit_subset.subset_state & (~new_state) edit_subset.subset_state = state glueviz-1.0.1+dfsg.orig/glue/core/state_path_patches.txt0000644000175000017500000000562013612623676022713 0ustar noahfxnoahfxglue.clients.ds9norm.DS9Normalize -> glue.viewers.image.ds9norm.DS9Normalize glue.clients.layer_artist.HistogramLayerArtist -> glue.viewers.histogram.qt.layer_artist.QThreadedHistogramLayerArtist glue.viewers.histogram.layer_artist.HistogramLayerArtist -> glue.viewers.histogram.qt.layer_artist.QThreadedHistogramLayerArtist glue.clients.layer_artist.ImageLayerArtist -> glue.viewers.image.layer_artist.ImageLayerArtist glue.clients.layer_artist.ScatterLayerArtist -> glue.viewers.scatter.layer_artist.ScatterLayerArtist glue.viewers.profile.layer_artist.ProfileLayerArtist -> glue.viewers.profile.qt.layer_artist.QThreadedProfileLayerArtist glue.core.data_factories.gridded_data -> glue.core.data_factories.deprecated.gridded_data glue.core.data_factories.tables.astropy_tabular_data -> glue.core.data_factories.astropy_table.astropy_tabular_data glue.core.link_helpers.lb2dec -> glue.plugins.coordinate_helpers.deprecated.lb2dec glue.core.link_helpers.lb2ra -> glue.plugins.coordinate_helpers.deprecated.lb2ra glue.core.link_helpers.radec2glat -> glue.plugins.coordinate_helpers.deprecated.radec2glat glue.core.link_helpers.radec2glon -> glue.plugins.coordinate_helpers.deprecated.radec2glon glue.external.aplpy.fk52gal -> glue.plugins.coordinate_helpers.deprecated.fk52gal glue.external.aplpy.gal2fk5 -> glue.plugins.coordinate_helpers.deprecated.gal2fk5 glue.qt.glue_application.GlueApplication -> glue.app.qt.application.GlueApplication glue.qt.widgets.histogram_widget.HistogramWidget -> glue.viewers.histogram.qt.HistogramViewer glue.qt.widgets.image_widget.ImageWidget -> glue.viewers.image.qt.ImageViewer glue.qt.widgets.scatter_widget.ScatterWidget -> glue.viewers.scatter.qt.ScatterViewer glue.viewers.histogram.qt.viewer_widget.HistogramWidget -> glue.viewers.histogram.qt.HistogramViewer glue.viewers.image.ds9norm.DS9Normalize -> glue.viewers.image.compat.DS9Compat glue.viewers.image.layer_artist.SubsetImageLayerArtist -> glue.viewers.image.layer_artist.ImageSubsetLayerArtist glue.viewers.image.qt.viewer_widget.ImageWidget -> glue.viewers.image.qt.ImageViewer glue.viewers.scatter.qt.viewer_widget.ScatterWidget -> glue.viewers.scatter.qt.ScatterViewer glue.viewers.table.qt.viewer_widget.TableLayerArtist -> glue.viewers.table.qt.TableLayerArtist glue.viewers.table.qt.viewer_widget.TableWidget -> glue.viewers.table.qt.TableViewer glue_vispy_viewers.common.toolbar.PatchedElementSubsetState -> glue.core.subset.ElementSubsetState glue.plugins.dendro_viewer.qt.viewer_widget.DendroWidget -> glue.plugins.dendro_viewer.qt.DendrogramViewer glue.plugins.dendro_viewer.layer_artist.DendroLayerArtist -> glue.plugins.dendro_viewer.layer_artist.DendrogramLayerArtist glue.core.component_link.identity -> glue.core.link_helpers.identity builtins.builtin_function_or_method -> types.BuiltinFunctionType __builtin__.builtin_function_or_method -> types.BuiltinFunctionType glue.core.coordinates.Coordinates -> glue.core.coordinates.LegacyCoordinates glueviz-1.0.1+dfsg.orig/glue/core/data_collection.py0000644000175000017500000003175513613550401021777 0ustar noahfxnoahfxfrom contextlib import contextmanager from glue.core.message import (DataCollectionAddMessage, DataCollectionDeleteMessage, ComponentsChangedMessage) from glue.core.registry import Registry from glue.core.link_manager import LinkManager from glue.core.data import Data, BaseCartesianData from glue.core.hub import Hub, HubListener from glue.core.coordinates import WCSCoordinates from glue.config import settings, data_translator from glue.utils import as_list, common_prefix __all__ = ['DataCollection'] class DataCollection(HubListener): """The top-level object for interacting with datasets in Glue. DataCollections have the following responsibilities: * Providing a way to retrieve and store data * Broadcasting messages when data are added or removed * Keeping each managed data set's list of :class:`~glue.core.component.DerivedComponent` instances up-to-date * Creating the hub that all other objects should use to communicate with one another (stored in ``self.hub``) """ def __init__(self, data=None): """ :param data: :class:`~glue.core.data.Data` object, or list of such objects """ super(DataCollection, self).__init__() self._link_manager = LinkManager(self) self._data = [] self.hub = None self._subset_groups = [] self.register_to_hub(Hub()) self.extend(as_list(data or [])) self._sg_count = 0 self._link_manager.register_to_hub(self.hub) @property def data(self): """ The :class:`~glue.core.data.Data` objects in the collection """ return self._data def append(self, data): """ Add a new dataset to this collection. Appending emits a DataCollectionAddMessage. It also updates the list of DerivedComponents that each data set can work with. :param data: :class:`~glue.core.data.BaseCartesianData` object to add """ if isinstance(data, list): self.extend(data) return if data in self: return if not isinstance(data, BaseCartesianData): raise TypeError("Only BaseCartesianData subclasses can be used at this time") self._data.append(data) if self.hub: data.register_to_hub(self.hub) for s in data.subsets: s.register() msg = DataCollectionAddMessage(self, data) self.hub.broadcast(msg) self._sync_link_manager() def extend(self, data): """Add several new datasets to this collection See :meth:`append` for more information :param data: List of data objects to add """ # Wait until all datasets are added to sync the link manager with self._no_sync_link_manager(): for d in data: self.append(d) self._sync_link_manager() def remove(self, data): """ Remove a data set from the collection Emits a DataCollectionDeleteMessage :param data: the object to remove :type data: :class:`~glue.core.data.Data` """ if data not in self._data: return self._data.remove(data) Registry().unregister(data, Data) if self.hub: msg = DataCollectionDeleteMessage(self, data) self.hub.broadcast(msg) def clear(self): with self._no_sync_link_manager(): for data in list(self): self.remove(data) def _sync_link_manager(self): """ update the LinkManager, so all the DerivedComponents for each data set are up-to-date """ if getattr(self, '_disable_sync_link_manager', False): return # Avoid circular calls with self._no_sync_link_manager(): self._link_manager.update_externally_derivable_components() @contextmanager def _no_sync_link_manager(self): self._disable_sync_link_manager = True yield self._disable_sync_link_manager = False @property def links(self): """ Tuple of :class:`~glue.core.component_link.ComponentLink` objects. """ return tuple(self._link_manager.links) @property def external_links(self): """ Tuple of :class:`~glue.core.component_link.ComponentLink` objects. """ return tuple(self._link_manager.external_links) def add_link(self, links): """Add one or more links to the data collection. This will auto-update the components in each data set :param links: The links to add. A scalar or list of :class:`~glue.core.component_link.ComponentLink` instances, or a :class:`~glue.core.link_helpers.LinkCollection` """ self._link_manager.add_link(links) def remove_link(self, links): """ Remove one or more links from the data collection. This will auto-update the components in each data set :param links: The links to remove. A scalar or list of :class:`~glue.core.component_link.ComponentLink` instances, or a :class:`~glue.core.link_helpers.LinkCollection` """ self._link_manager.remove_link(links) def _merge_link(self, link): pass def set_links(self, links): """ Override the links in the collection, and update data objects as necessary. :param links: The new links. An iterable of :class:`~glue.core.component_link.ComponentLink` instances """ self._link_manager.clear_links() self._link_manager.add_link(links) def register_to_hub(self, hub): """ Register managed data objects to a hub. :param hub: The hub to register with :type hub: :class:`~glue.core.hub.Hub` """ if self.hub is hub: return if self.hub is not None: raise RuntimeError("Data Collection already registered " "to a different Hub") if not isinstance(hub, Hub): raise TypeError("Input is not a Hub object: %s" % type(hub)) self.hub = hub # re-assign all data, subset hub instances to this hub for d in self._data: d.register_to_hub(hub) for s in d.subsets: s.register() hub.subscribe(self, ComponentsChangedMessage, lambda msg: self._sync_link_manager(), filter=lambda x: x.sender in self._data) def new_subset_group(self, label=None, subset_state=None): """ Create and return a new Subset Group. :param label: The label to assign to the group :type label: str :param subset_state: The state to initialize the group with :type subset_state: :class:`~glue.core.subset.SubsetState` :returns: A new :class:`~glue.core.subset_group.SubsetGroup` """ from glue.core.subset_group import SubsetGroup color = settings.SUBSET_COLORS[self._sg_count % len(settings.SUBSET_COLORS)] self._sg_count += 1 label = label or 'Subset %i' % self._sg_count result = SubsetGroup(color=color, label=label, subset_state=subset_state) self._subset_groups.append(result) result.register(self) return result def remove_subset_group(self, subset_grp): """ Remove an existing :class:`~glue.core.subset_group.SubsetGroup` """ if subset_grp not in self._subset_groups: return # remove from list first, so that group appears deleted # by the time the first SubsetDelete message is broadcast self._subset_groups.remove(subset_grp) for s in subset_grp.subsets: s.delete() subset_grp.unregister(self.hub) def suggest_merge_label(self, *data): """ Determine what merge label to suggest given datasets """ # Find longest common prefix for data suggestion = common_prefix([d.label for d in data]) if len(suggestion) < 3: suggestion = 'Merged data' # Now check if the suggestion already exists, and if so add a suffix labels = self.labels if suggestion in labels: suffix = 2 while "{0} [{1}]".format(suggestion, suffix) in labels: suffix += 1 suggestion = "{0} [{1}]".format(suggestion, suffix) return suggestion @property def labels(self): return [d.label for d in self] def merge(self, *data, **kwargs): """ Merge two or more datasets into a single dataset. This has the following effects: All components from all datasets are added to the first argument All datasets except the first argument are removed from the collection Any component name conflicts are disambiguated The pixel and world components apart from the first argument are discarded :note: All arguments must have the same shape :param data: One or more :class:`~glue.core.data.Data` instances. :returns: self """ if len(data) < 2: raise ValueError("merge requires 2 or more arguments") shp = data[0].shape for d in data: if d.shape != shp: raise ValueError("All arguments must have the same shape") label = kwargs.get('label', data[0].label) master = Data(label=label) self.append(master) master.coords = data[0].coords for i, d in enumerate(data): if isinstance(d.coords, WCSCoordinates): master.coords = d.coords break # Find ambiguous components (ones which have labels in more than one # dataset from collections import Counter clabel_count = Counter([c.label for d in data for c in d.main_components + d.derived_components]) for d in data: for c in d.components: if c in master.components: # already present (via a link) continue # Don't include coordinate components here as they will be # recomputed separately once the first non-coordinate component # is added. if c in d.coordinate_components: continue lbl = c.label if clabel_count[lbl] > 1: lbl = lbl + " [{0}]".format(d.label) c._label = lbl c.parent = master master.add_component(d.get_component(c), c) self.remove(d) return master @property def subset_groups(self): """ tuple of current :class:`Subset Groups ` """ return tuple(self._subset_groups) def __contains__(self, obj): return (obj in self._data or obj in self.subset_groups or any([data.label == obj for data in self._data])) def __getitem__(self, key): if isinstance(key, str): matches = [data for data in self._data if data.label == key] if len(matches) == 0: raise ValueError("No data found with the label '{0}'".format(key)) elif len(matches) > 1: raise ValueError("Several datasets were found with the label '{0}'".format(key)) else: return matches[0] else: return self._data[key] def __setitem__(self, key, data): """ Add a dataset to the data collection. This can be either a :class:`~glue.core.data.Data` object, which will then have its label set to the specified key, or another kind of object which will be automatically translated into a :class:`~glue.core.data.Data` object. """ if not isinstance(key, str): raise TypeError("item key should be a string, but got {0}".format(type(key))) if not isinstance(data, Data): handler, preferred = data_translator.get_handler_for(data) data = handler.to_data(data) data._preferred_translation = preferred data.label = key for existing_data in self._data[:]: if existing_data.label == key: self.remove(existing_data) self.append(data) def __iter__(self): return iter(self._data) def __len__(self): return len(self._data) def index(self, item): return self._data.index(item) def __str__(self): if len(self) == 1: result = "DataCollection (1 data set)\n\t" else: result = "DataCollection (%i data sets)\n\t" % len(self) result += '\n\t'.join("%3i: %s" % (i, d.label) for i, d in enumerate(self)) return result def __repr__(self): return self.__str__() def __bool__(self): return True def __nonzero__(self): return True glueviz-1.0.1+dfsg.orig/glue/core/decorators.py0000644000175000017500000000416513605357235021026 0ustar noahfxnoahfxfrom functools import wraps __all__ = ['memoize', 'singleton', 'memoize_attr_check'] def _make_key(args, kwargs): return args, frozenset(kwargs.items()) def memoize(func): """Save results of function calls to avoid repeated calculation""" memo = {} @wraps(func) def wrapper(*args, **kwargs): # Note that here we have two separate try...except statements, because # we want to make sure that we catch only TypeError on the first # statement, and both TypeError and KeyError on the second. try: key = _make_key(args, kwargs) except TypeError: # unhashable input return func(*args, **kwargs) try: return memo[key] except KeyError: result = func(*args, **kwargs) memo[key] = result return result except TypeError: # unhashable input return func(*args, **kwargs) wrapper.__memoize_cache = memo return wrapper def clear_cache(func): """ Clear the cache of a function that has potentially been decorated by memoize. Safely ignores non-decorated functions """ try: func.__memoize_cache.clear() except AttributeError: pass def memoize_attr_check(attr): """ Memoize a method call, cached both on arguments and given attribute of first argument (which is presumably self) Has the effect of re-calculating results if a specific attribute changes """ def decorator(func): # must return a decorator function @wraps(func) def result(*args, **kwargs): first_arg = getattr(args[0], attr) return memo(first_arg, *args, **kwargs) @memoize def memo(*args, **kwargs): return func(*args[1:], **kwargs) return result return decorator def singleton(cls): """Turn a class into a singleton, such that new objects in this class share the same instance""" instances = {} @wraps(cls) def getinstance(): if cls not in instances: instances[cls] = cls() return instances[cls] return getinstance glueviz-1.0.1+dfsg.orig/glue/core/data_derived.py0000644000175000017500000001704613641414126021267 0ustar noahfxnoahfx# Base classes for various types of derived datasets - these are basically # wrapper classes around data objects that modify the data in some way # on-the-fly. These work by exposting the common data API described in # http://docs.glueviz.org/en/stable/developer_guide/data.html and transparently # applying changes. from glue.core.hub import HubListener from glue.core.data import BaseCartesianData from glue.core.message import NumericalDataChangedMessage from glue.core.subset import SliceSubsetState from glue.core.component_id import ComponentID from astropy.wcs.wcsapi import SlicedLowLevelWCS class DerivedData(BaseCartesianData): """ Base class for all derived data classes. """ class IndexedData(BaseCartesianData, HubListener): """ A dataset where some dimensions have been removed via indexing. The indices can be modified after the data has been created using the ``indices`` property. Parameters ---------- original_data : `~glue.core.data.BaseCartesianData` The original data to reduce the dimension of indices : tuple of `int` or `None` The indices to apply to the data, or `None` if a dimension should be preserved. This tuple should have as many elements as there are dimensions in ``original_data``. """ def __init__(self, original_data, indices): super(IndexedData, self).__init__() if len(indices) != original_data.ndim: raise ValueError("The 'indices' tuple should have length {0}" .format(original_data.ndim)) if hasattr(original_data, 'coords'): if original_data.coords is None: self._coords = None else: slices = [slice(None) if idx is None else idx for idx in indices] self._coords = SlicedLowLevelWCS(original_data.coords, slices) self._original_data = original_data self._cid_to_original_cid = {} self._original_cid_to_cid = {} self.indices = indices @property def indices(self): return self._indices @indices.setter def indices(self, value): if len(value) != self._original_data.ndim: raise ValueError("The 'indices' tuple should have length {0}" .format(self._original_data.ndim)) # For now we require the indices to be in the same position, i.e. we # don't allow changes in dimensionality of the derived dataset. if hasattr(self, '_indices'): changed = False for idim in range(self._original_data.ndim): before, after = self._indices[idim], value[idim] if type(before) != type(after): raise TypeError("Can't change where the ``None`` values are in indices") elif before != after: changed = True else: changed = False self._indices = value # Compute a subset state that represents the indexing - this is used # for compute_statistic and compute_histogram slices = [slice(x) if x is None else x for x in self._indices] self._indices_subset_state = SliceSubsetState(self._original_data, slices) # Construct a list of original pixel component IDs self._original_pixel_cids = [] for idim in range(self._original_data.ndim): if self._indices[idim] is None: self._original_pixel_cids.append(self._original_data.pixel_component_ids[idim]) # Construct a list of original world component IDs self._original_world_cids = [] if len(self._original_data.world_component_ids) > 0: idim_new = 0 for idim in range(self._original_data.ndim): if self._indices[idim] is None: self._cid_to_original_cid[self.world_component_ids[idim_new]] = self._original_data.world_component_ids[idim] idim_new += 1 # Tell glue that the data has changed if changed and self.hub is not None: msg = NumericalDataChangedMessage(self) self.hub.broadcast(msg) @property def label(self): slice = '[' + ','.join([':' if x is None else str(x) for x in self._indices]) + ']' return self._original_data.label + slice @property def shape(self): shape = [] for idim in range(self._original_data.ndim): if self.indices[idim] is None: shape.append(self._original_data.shape[idim]) return tuple(shape) @property def main_components(self): main = [] for cid in self._original_data.main_components: if cid not in self._original_cid_to_cid: cid_new = ComponentID(label=cid.label, parent=self) self._original_cid_to_cid[cid] = cid_new self._cid_to_original_cid[cid_new] = cid main.append(self._original_cid_to_cid[cid]) return main def get_kind(self, cid): cid = self._translate_cid(cid) return self._original_data.get_kind(cid) def _to_original_view(self, view): if view is None: view = [slice(None)] * self.ndim original_view = list(self.indices) idim_reduced = 0 for idim in range(self._original_data.ndim): if original_view[idim] is None: if view is None: original_view[idim] = slice(None) else: original_view[idim] = view[idim_reduced] idim_reduced += 1 return tuple(original_view) def register_to_hub(self, hub): hub.subscribe(self, NumericalDataChangedMessage, handler=self._check_for_original_changes) super(IndexedData, self).register_to_hub(hub) def _check_for_original_changes(self, message): # If the parent's values are changed, we should assume the values # of the current dataset have changed too if message.data is self._original_data: msg = NumericalDataChangedMessage(self) self.hub.broadcast(msg) def _translate_cid(self, cid): if cid in self.pixel_component_ids: cid = self._original_pixel_cids[cid.axis] elif cid in self._cid_to_original_cid: cid = self._cid_to_original_cid[cid] return cid def get_data(self, cid, view=None): cid = self._translate_cid(cid) original_view = self._to_original_view(view) return self._original_data.get_data(cid, view=original_view) def get_mask(self, subset_state, view=None): original_view = self._to_original_view(view) return self._original_data.get_mask(subset_state, view=original_view) def compute_fixed_resolution_buffer(self, *args, **kwargs): if 'target_cid' in kwargs: kwargs['target_cid'] = self._translate_cid(kwargs['target_cid']) from glue.core.fixed_resolution_buffer import compute_fixed_resolution_buffer return compute_fixed_resolution_buffer(self, *args, **kwargs) def compute_statistic(self, statistic, cid, **kwargs): cid = self._translate_cid(cid) kwargs['view'] = self._to_original_view(kwargs.get('view')) return self._original_data.compute_statistic(statistic, cid, **kwargs) def compute_histogram(self, *args, **kwargs): if kwargs.get('subset_state') is None: kwargs['subset_state'] = self._indices_subset_state else: kwargs['subset_state'] &= self._indices_subset_state return self._original_data.compute_histogram(*args, **kwargs) glueviz-1.0.1+dfsg.orig/glue/core/coordinate_helpers.py0000644000175000017500000001343013613547211022517 0ustar noahfxnoahfximport numpy as np from astropy.wcs import WCS from glue.utils import unbroadcast, broadcast_to from glue.core.coordinates import LegacyCoordinates def default_world_coords(wcs): if isinstance(wcs, WCS): return wcs.wcs.crval else: return np.zeros(wcs.world_n_dim, dtype=float) def pixel2world_single_axis(wcs, *pixel, world_axis=None): """ Convert pixel to world coordinates, preserving input type/shape. This is a wrapper around pixel_to_world_values which returns the result for just one axis, and also determines whether the calculation can be sped up if broadcasting is present in the input arrays. Parameters ---------- *pixel : scalars lists, or Numpy arrays The pixel coordinates (0-based) to convert world_axis : int, optional The index of the world coordinate that is needed. Returns ------- world : `numpy.ndarray` The world coordinates for the requested axis """ if world_axis is None: raise ValueError("world_axis needs to be set") if np.size(pixel[0]) == 0: return np.array([], dtype=float) original_shape = pixel[0].shape pixel_new = [] # Now find all the pixel coordinates that are needed to calculate this # world coordinate, using the axis correlation matrix pixel_dep = wcs.axis_correlation_matrix[world_axis, :] for ip, p in enumerate(pixel): if pixel_dep[ip]: pixel_new.append(unbroadcast(p)) else: pixel_new.append(p.flat[0]) pixel = np.broadcast_arrays(*pixel_new) result = wcs.pixel_to_world_values(*pixel) return broadcast_to(result[world_axis], original_shape) def world2pixel_single_axis(wcs, *world, pixel_axis=None): """ Convert world to pixel coordinates, preserving input type/shape. This is a wrapper around world_to_pixel_values which returns the result for just one axis, and also determines whether the calculation can be sped up if broadcasting is present in the input arrays. Parameters ---------- *world : scalars lists, or Numpy arrays The world coordinates to convert pixel_axis : int, optional The index of the pixel coordinate that is needed. Returns ------- pixel : `numpy.ndarray` The pixel coordinates for the requested axis """ if pixel_axis is None: raise ValueError("pixel_axis needs to be set") if np.size(world[0]) == 0: return np.array([], dtype=float) original_shape = world[0].shape world_new = [] # Now find all the world coordinates that are needed to calculate this # world coordinate, using the axis correlation matrix world_dep = wcs.axis_correlation_matrix[:, pixel_axis] for iw, w in enumerate(world): if world_dep[iw]: world_new.append(unbroadcast(w)) else: world_new.append(w.flat[0]) world = np.broadcast_arrays(*world_new) result = wcs.world_to_pixel_values(*world) return broadcast_to(result[pixel_axis], original_shape) def world_axis(wcs, data, *, pixel_axis=None, world_axis=None): """ Find the world coordinates along a given pixel dimension, and which for now we center on the pixel origin. Parameters ---------- data : `~glue.core.data.Data` The data to compute the coordinate axis for (this is used to determine the size of the axis) pixel_axis : int The pixel axis along which to compute the world coordinate, in coordinate order. world_axis : int The world axis to compute. Notes ----- This method computes the axis values using pixel positions at the center of the data along all other axes. This will therefore only give the correct result for non-dependent axes (which can be checked using the ``dependent_axes`` method). """ pixel = [] pixel_axis = wcs.pixel_n_dim - 1 - pixel_axis for i, s in enumerate(data.shape): if i == pixel_axis: pixel.append(np.arange(data.shape[pixel_axis])) else: pixel.append(np.repeat((s - 1) / 2, data.shape[pixel_axis])) return pixel2world_single_axis(wcs, *pixel[::-1], world_axis=world_axis) def dependent_axes(wcs, axis): """ Return a tuple of which world-axes are non-independent from a given pixel axis The axis index is given in numpy ordering convention (note that opposite the fits convention) """ if isinstance(wcs, LegacyCoordinates): return (axis,) matrix = wcs.axis_correlation_matrix[::-1, ::-1] world_dep = matrix[:, axis:axis + 1] return tuple(np.nonzero((world_dep & matrix).any(axis=0))[0]) def _get_ndim(header): if 'NAXIS' in header: return header['NAXIS'] if 'WCSAXES' in header: return header['WCSAXES'] return None def axis_label(wcs, axis): if wcs.world_axis_names[axis] != '': return wcs.world_axis_names[wcs.world_n_dim - 1 - axis].title() if isinstance(wcs, WCS): header = wcs.to_header() num = _get_ndim(header) - axis # number orientation reversed ax = header.get('CTYPE%i' % num) if ax is not None: if len(ax) == 8 or '-' in ax: # assume standard format ax = ax[:5].split('-')[0].title() else: ax = ax.title() translate = dict( Glon='Galactic Longitude', Glat='Galactic Latitude', Ra='Right Ascension', Dec='Declination', Velo='Velocity', Freq='Frequency' ) return translate.get(ax, ax) unit = header.get('CUNIT%i' % num) if unit is not None: return "World {} ({})".format(axis, unit) return "World {}".format(axis) glueviz-1.0.1+dfsg.orig/glue/core/component_id.py0000644000175000017500000001170013605357235021330 0ustar noahfxnoahfximport uuid import operator import numbers from glue.core.component_link import BinaryComponentLink from glue.core.subset import InequalitySubsetState from glue.core.message import DataRenameComponentMessage __all__ = ['ComponentID', 'PixelComponentID', 'ComponentIDDict', 'ComponentIDList'] # access to ComponentIDs via .item[name] class ComponentIDList(list): def __contains__(self, cid): if isinstance(cid, str): for c in self: if cid == c.label: return True else: return False else: return list.__contains__(self, cid) class ComponentIDDict(object): def __init__(self, data, **kwargs): self.data = data def __getitem__(self, key): result = self.data.find_component_id(key) if result is None: raise KeyError("ComponentID not found or not unique: %s" % key) return result class ComponentID(object): """ References a :class:`glue.core.component.Component` object within a :class:`~glue.core.data.Data` object. ComponentIDs behave as keys:: component_id = data.id[name] data[component_id] -> numpy array Parameters ---------- label : str Name for the component ID """ def __init__(self, label, parent=None): self._label = str(label) self.parent = parent # We assign a UUID which can then be used for example in equations # for derived components - the idea is that this doesn't change over # the life cycle of glue, so it is a more reliable way to refer to # components in strings than using labels self._uuid = str(uuid.uuid4()) @property def uuid(self): return self._uuid @property def label(self): return self._label @label.setter def label(self, value): """Change label. .. warning:: Label changes are not currently tracked by client classes. Label's should only be changd before creating other client objects """ self._label = str(value) if self.parent is not None and self.parent.hub: msg = DataRenameComponentMessage(self.parent, self) self.parent.hub.broadcast(msg) def __str__(self): return str(self._label) def __repr__(self): return str(self._label) def to_html(self): if self.parent is None: return str(self._label) else: return "[{1}].{0}".format(self._label, self.parent._label) def __eq__(self, other): if isinstance(other, (numbers.Number, str)): return InequalitySubsetState(self, other, operator.eq) return other is self # If __eq__ is defined, then __hash__ has to be re-defined __hash__ = object.__hash__ def __ne__(self, other): if isinstance(other, (numbers.Number, str)): return InequalitySubsetState(self, other, operator.ne) return other is not self def __gt__(self, other): return InequalitySubsetState(self, other, operator.gt) def __ge__(self, other): return InequalitySubsetState(self, other, operator.ge) def __lt__(self, other): return InequalitySubsetState(self, other, operator.lt) def __le__(self, other): return InequalitySubsetState(self, other, operator.le) def __add__(self, other): return BinaryComponentLink(self, other, operator.add) def __radd__(self, other): return BinaryComponentLink(other, self, operator.add) def __sub__(self, other): return BinaryComponentLink(self, other, operator.sub) def __rsub__(self, other): return BinaryComponentLink(other, self, operator.sub) def __mul__(self, other): return BinaryComponentLink(self, other, operator.mul) def __rmul__(self, other): return BinaryComponentLink(other, self, operator.mul) def __div__(self, other): return BinaryComponentLink(self, other, operator.div) def __rdiv__(self, other): return BinaryComponentLink(other, self, operator.div) def __truediv__(self, other): return BinaryComponentLink(self, other, operator.truediv) def __rtruediv__(self, other): return BinaryComponentLink(other, self, operator.truediv) def __pow__(self, other): return BinaryComponentLink(self, other, operator.pow) def __rpow__(self, other): return BinaryComponentLink(other, self, operator.pow) class PixelComponentID(ComponentID): """ The ID of a component which is a pixel position in the data - this allows us to make assumptions in certain places. For example when a polygon selection is done in pixel space, it can easily be broadcast along dimensions. """ def __init__(self, axis, label, parent=None): self.axis = axis super(PixelComponentID, self).__init__(label, parent=parent) glueviz-1.0.1+dfsg.orig/glue/core/state_objects.py0000644000175000017500000004436313657331513021514 0ustar noahfxnoahfxfrom textwrap import indent from collections import defaultdict import numpy as np from glue.core import Subset from echo import (delay_callback, CallbackProperty, HasCallbackProperties, CallbackList) from glue.core.state import saver, loader from glue.core.component_id import PixelComponentID __all__ = ['State', 'StateAttributeCacheHelper', 'StateAttributeLimitsHelper', 'StateAttributeSingleValueHelper'] @saver(CallbackList) def _save_callback_list(items, context): return {'values': [context.id(item) for item in items]} @loader(CallbackList) def _load_callback_list(rec, context): return [context.object(obj) for obj in rec['values']] class State(HasCallbackProperties): """ A class to represent the state of a UI element. Initially this doesn't add anything compared to HasCallbackProperties, but functionality will be added over time. """ def __init__(self, **kwargs): super(State, self).__init__() self.update_from_dict(kwargs) def update_from_state(self, state): """ Update this state using the values from another state. Parameters ---------- state : `~glue.core.state_objects.State` The state to use the values from """ self.update_from_dict(state.as_dict()) def update_from_dict(self, properties): """ Update this state using the values from a dictionary of attributes. Parameters ---------- properties : dict The dictionary containing attribute/value pairs. """ if len(properties) == 0: return # Group properties into priority so as to be able to update them in # chunks and not fire off callback events between every single one. groups = defaultdict(list) for name in properties: if self.is_callback_property(name): groups[self._update_priority(name)].append(name) for priority in sorted(groups, reverse=True): with delay_callback(self, *groups[priority]): for name in groups[priority]: setattr(self, name, properties[name]) def as_dict(self): """ Return the current state as a dictionary of attribute/value pairs. """ properties = {} for name in dir(self): if not name.startswith('_') and self.is_callback_property(name): properties[name] = getattr(self, name) return properties def __str__(self): s = "" state_dict = self.as_dict() for key in sorted(state_dict): value = state_dict[key] if np.isscalar(value): s += "{0}: {1}\n".format(key, value) else: s += "{0}: {1}\n".format(key, repr(value)) return s.strip() def __repr__(self): return "<{0}\n{1}\n>".format(self.__class__.__name__, indent(str(self), ' ')) def __gluestate__(self, context): return {'values': dict((key, context.id(value)) for key, value in self.as_dict().items())} def _update_priority(self, name): return 0 @classmethod def __setgluestate__(cls, rec, context): properties = dict((key, context.object(value)) for key, value in rec['values'].items()) result = cls(**properties) return result class StateAttributeCacheHelper(object): """ Generic class to help with caching values on a per-attribute basis Parameters ---------- state : :class:`glue.core.state_objects.State` The state object with the callback properties to cache attribute : str The attribute name - this will be populated once a dataset is assigned to the helper cache : dict, optional A dictionary that can be used to hold the cache. This option can be used if a common cache should be shared between different helpers. kwargs Additional keyword arguments are taken to be values that should be used/cached. The key should be the name to be understood by sub-classes of this base class, and the value should be the name of the attribute in the state. """ def __init__(self, state, attribute, cache=None, **kwargs): self._state = state self._attribute = attribute self._values = dict((key, kwargs[key]) for key in self.values_names if key in kwargs) self._modifiers = dict((key, kwargs[key]) for key in self.modifiers_names if key in kwargs) self._attribute_lookup = {'attribute': self._attribute} self._attribute_lookup.update(self._values) self._attribute_lookup.update(self._modifiers) self._attribute_lookup_inv = {v: k for k, v in self._attribute_lookup.items()} self._state.add_callback(self._attribute, self._update_attribute) self._state.add_global_callback(self._update_values) # NOTE: don't use self._cache = cache or {} here since if the initial # cache is empty it will evaluate as False! if cache is None: self._cache = {} else: self._cache = cache @property def data_values(self): return self.data[self.component_id] def invalidate_cache(self): self._cache.clear() @property def data(self): if self.attribute is None: return None else: # For subsets in 'data' mode, we want to compute the limits based on # the full dataset, not just the subset. if isinstance(self.attribute.parent, Subset): return self.attribute.parent.data else: return self.attribute.parent @property def component_id(self): if self.attribute is None: return None else: return self.attribute def set_cache(self, cache): self._cache = cache self._update_attribute() def _update_attribute(self, *args): if self.component_id in self._cache: # The component ID already exists in the cache, so just revert to # that version of the values/settings. self.set(cache=False, **self._cache[self.component_id]) else: # We need to compute the values for the first time self.update_values(attribute=self.component_id, use_default_modifiers=True) def _update_values(self, **properties): if hasattr(self, '_in_set'): if self._in_set: return if self.attribute is None: return properties = dict((self._attribute_lookup_inv[key], value) for key, value in properties.items() if key in self._attribute_lookup_inv and self._attribute_lookup_inv[key] != 'attribute') if len(properties) > 0: self.update_values(**properties) def _modifiers_as_dict(self): return dict((prop, getattr(self, prop)) for prop in self.modifiers_names if prop in self._modifiers) def _values_as_dict(self): return dict((prop, getattr(self, prop)) for prop in self.values_names if prop in self._values) def _update_cache(self): if self.component_id is not None: self._cache[self.component_id] = {} self._cache[self.component_id].update(self._modifiers_as_dict()) self._cache[self.component_id].update(self._values_as_dict()) def __getattr__(self, attribute): if attribute in self._attribute_lookup: return getattr(self._state, self._attribute_lookup[attribute]) else: raise AttributeError(attribute) def __setattr__(self, attribute, value): if attribute.startswith('_') or attribute not in self._attribute_lookup: return object.__setattr__(self, attribute, value) else: return setattr(self._state, self._attribute_lookup[attribute], value) def set(self, cache=True, **kwargs): self._in_set = True extra_kwargs = set(kwargs.keys()) - set(self.values_names) - set(self.modifiers_names) if len(extra_kwargs) > 0: raise ValueError("Invalid properties: {0}".format(extra_kwargs)) with delay_callback(self._state, *self._attribute_lookup.values()): for prop, value in kwargs.items(): setattr(self, prop, value) if cache: self._update_cache() self._in_set = False class StateAttributeLimitsHelper(StateAttributeCacheHelper): """ This class is a helper for attribute-dependent min/max level values. It is equivalent to AttributeLimitsHelper but operates on State objects and is GUI-independent. Parameters ---------- attribute : str The attribute name - this will be populated once a dataset is assigned to the helper. random_subset : int How many points to use at most for the percentile calculation (using all values is highly inefficient and not needed) margin : float Whether to add a margin to the range of values determined. This should be given as a floating point value that will be multiplied by the range of values to give the margin to add to the limits. lower, upper : str The fields for the lower/upper levels percentile : ``QComboBox`` instance, optional The scale mode combo - this will be populated by presets such as Min/Max, various percentile levels, and Custom. log : bool Whether the limits are in log mode (in which case only positive values are used when finding the limits) Notes ----- Once the helper is instantiated, the data associated with the helper can be set/changed with: >>> helper = AttributeLimitsHelper(...) >>> helper.data = data The data can also be passed to the initializer as described in the list of parameters above. """ values_names = ('lower', 'upper') modifiers_names = ('log', 'percentile') def __init__(self, state, attribute, random_subset=10000, margin=0, **kwargs): super(StateAttributeLimitsHelper, self).__init__(state, attribute, **kwargs) self.margin = margin self.random_subset = random_subset self.subset_indices = None if self.attribute is not None: if (self.lower is not None and self.upper is not None and getattr(self, 'percentile', None) is None): # If the lower and upper limits are already set, we need to make # sure we don't override them, so we set the percentile mode to # custom if it isn't already set. self.set(percentile='Custom') else: # Otherwise, we force the recalculation or the fetching from # cache of the limits based on the current attribute self._update_attribute() def update_values(self, force=False, use_default_modifiers=False, **properties): if not force and not any(prop in properties for prop in ('attribute', 'percentile', 'log')): self.set(percentile='Custom') return if use_default_modifiers: percentile = 100 log = False else: percentile = getattr(self, 'percentile', None) or 100 log = getattr(self, 'log', None) or False if not force and (percentile == 'Custom' or not hasattr(self, 'data') or self.data is None): self.set(percentile=percentile, log=log) else: # Shortcut if the component ID is a pixel component ID if isinstance(self.component_id, PixelComponentID) and percentile == 100 and not log: lower = -0.5 upper = self.data.shape[self.component_id.axis] - 0.5 self.set(lower=lower, upper=upper, percentile=percentile, log=log) return exclude = (100 - percentile) / 2. if percentile == 100: lower = self.data.compute_statistic('minimum', cid=self.component_id, finite=True, positive=log, random_subset=self.random_subset) upper = self.data.compute_statistic('maximum', cid=self.component_id, finite=True, positive=log, random_subset=self.random_subset) else: lower = self.data.compute_statistic('percentile', cid=self.component_id, percentile=exclude, positive=log, random_subset=self.random_subset) upper = self.data.compute_statistic('percentile', cid=self.component_id, percentile=100 - exclude, positive=log, random_subset=self.random_subset) if not isinstance(lower, np.datetime64) and np.isnan(lower): lower, upper = 0, 1 else: if self.data.get_kind(self.component_id) == 'categorical': lower = np.floor(lower - 0.5) + 0.5 upper = np.ceil(upper + 0.5) - 0.5 if log: value_range = np.log10(upper / lower) lower /= 10.**(value_range * self.margin) upper *= 10.**(value_range * self.margin) else: value_range = upper - lower lower -= value_range * self.margin upper += value_range * self.margin self.set(lower=lower, upper=upper, percentile=percentile, log=log) def flip_limits(self): self.set(lower=self.upper, upper=self.lower) class StateAttributeSingleValueHelper(StateAttributeCacheHelper): values_names = ('value',) modifiers_names = () def __init__(self, state, attribute, function, mode='values', **kwargs): self._function = function super(StateAttributeSingleValueHelper, self).__init__(state, attribute, **kwargs) if self.attribute is not None: self._update_attribute() if mode in ('values', 'component'): self.mode = mode else: raise ValueError('mode should be one of "values" or "component"') def update_values(self, use_default_modifiers=False, **properties): if not any(prop in properties for prop in ('attribute',)) or self.data is None: self.set() else: if self.mode == 'values': arg = self.data_values else: arg = self.data.get_component(self.component_id) self.set(value=self._function(arg)) class StateAttributeHistogramHelper(StateAttributeCacheHelper): values_names = ('lower', 'upper', 'n_bin') modifiers_names = () def __init__(self, state, attribute, random_subset=10000, max_n_bin=30, default_n_bin=15, common_n_bin=None, **kwargs): self._max_n_bin = max_n_bin self._default_n_bin = default_n_bin common_n_bin_att = common_n_bin super(StateAttributeHistogramHelper, self).__init__(state, attribute, **kwargs) self.random_subset = random_subset if common_n_bin_att is not None: if getattr(self._state, common_n_bin_att): self._common_n_bin = self._default_n_bin else: self._common_n_bin = None self._state.add_callback(common_n_bin_att, self._update_common_n_bin) else: self._common_n_bin = None def _apply_common_n_bin(self): for att in self._cache: if not self.data.get_kind(att) == 'categorical': self._cache[att]['n_bin'] = self._common_n_bin def _update_common_n_bin(self, common_n_bin): if common_n_bin: if self.data.get_kind(self.component_id) == 'categorical': self._common_n_bin = self._default_n_bin else: self._common_n_bin = self.n_bin self._apply_common_n_bin() else: self._common_n_bin = None def update_values(self, force=False, use_default_modifiers=False, **properties): if not force and not any(prop in properties for prop in ('attribute', 'n_bin')) or self.data is None: self.set() return categorical = self.data.get_kind(self.component_id) == 'categorical' if 'n_bin' in properties: self.set() if self._common_n_bin is not None and not categorical: self._common_n_bin = properties['n_bin'] self._apply_common_n_bin() if 'attribute' in properties or force: if categorical: n_categories = self.data_values.categories.size n_bin = max(1, min(n_categories, self._max_n_bin)) lower = -0.5 upper = lower + n_categories else: if self._common_n_bin is None: n_bin = self._default_n_bin else: n_bin = self._common_n_bin lower = self.data.compute_statistic('minimum', cid=self.component_id, finite=True, random_subset=self.random_subset) upper = self.data.compute_statistic('maximum', cid=self.component_id, finite=True, random_subset=self.random_subset) if not isinstance(lower, np.datetime64) and np.isnan(lower): lower, upper = 0, 1 self.set(lower=lower, upper=upper, n_bin=n_bin) if __name__ == "__main__": from glue.core import Data class TestState(object): layer = CallbackProperty() comp = CallbackProperty(1) lower = CallbackProperty() higher = CallbackProperty() log = CallbackProperty() scale = CallbackProperty() state = TestState() state.layer = Data(x=np.arange(10), y=np.arange(10) / 3., z=np.arange(10) - 5) helper = StateAttributeLimitsHelper(state, 'layer', 'comp', 'lower', 'higher', percentile='scale', log='log') helper.component_id = state.layer.id['x'] glueviz-1.0.1+dfsg.orig/glue/core/fitters.py0000644000175000017500000002703213605357235020337 0ustar noahfxnoahfx""" Glue's fitting classes are designed to be easily subclassed for performing custom model fitting in Glue. See the guide on :ref:`writing custom fit plugins ` for help with using custom fitting utilities in Glue. """ import numpy as np from glue.core.simpleforms import IntOption, Option __all__ = ['BaseFitter1D', 'PolynomialFitter', 'AstropyFitter1D', 'SimpleAstropyGaussianFitter', 'BasicGaussianFitter'] class BaseFitter1D(object): """ Base class for 1D fitters. This abstract class must be overwritten. """ label = "Fitter" """A short label for the fit, used by the GUI""" param_names = [] """list of parameter names that support restrictions""" def __init__(self, **params): self._constraints = {} for k, v in params.items(): if k in self.param_names: self.set_constraint(k, value=v) else: setattr(self, k, v) def plot(self, fit_result, axes, x, linewidth=None, alpha=None, color=None, normalize=None): """ Plot the result of a fit. :param fit_result: The output from fit :param axes: The Matplotlib axes to add the fit to :param x: The values of X at which to visualize the model :returns: A list of matplotlib artists. **This is important:** plots will not be properly cleared if this isn't provided """ y = self.predict(fit_result, x) if normalize is not None: y = normalize(y) result = axes.plot(x, y, color, lw=linewidth, alpha=alpha, scalex=False, scaley=False) return result def _sigma_to_weights(self, dy): if dy is not None: return 1. / np.asarray(dy) ** 2 @property def options(self): """ A dictionary of the current setting of each model hyperparameter. Hyperparameters are defined in subclasses by creating class-level :mod:`Option ` attributes. This attribute dict maps ``{hyperparameter_name: current_value}`` """ result = [] for typ in type(self).mro(): result.extend(k for k, v in typ.__dict__.items() if isinstance(v, Option)) return dict((o, getattr(self, o)) for o in result) def summarize(self, fit_result, x, y, dy=None): """ Return a textual summary of the fit. :param fit_result: The return value from :meth:`fit` :param x: The x values passed to :meth:`fit` :returns: A description of the fit result :rtype: str """ return str(fit_result) @property def constraints(self): """ A dict of the constraints on each parameter in :attr:`param_names`. Each value is itself a dict with 3 items: :key value: The default value :key fixed: True / False, indicating whether the parameter is fixed :key bounds: [min, max] or None, indicating lower/upper limits """ result = {} for p in self.param_names: result[p] = dict(value=None, fixed=False, limits=None) result[p].update(self._constraints.get(p, {})) return result def set_constraint(self, parameter_name, value=None, fixed=None, limits=None): """ Update a constraint. :param parameter_name: name of the parameter to update :type parameter_name: str :param value: Set the default value (optional) :param limits: Set the limits to[min, max] (optional) :param fixed: Set whether the parameter is fixed (optional) """ c = self._constraints.setdefault(parameter_name, {}) if value is not None: c['value'] = value if fixed is not None: c['fixed'] = fixed if limits is not None: c['limits'] = limits def build_and_fit(self, x, y, dy=None): """ Method which builds the arguments to fit, and calls that method """ x = np.asarray(x).ravel() y = np.asarray(y).ravel() if dy is not None: dy = np.asarray(dy).ravel() return self.fit(x, y, dy=dy, constraints=self.constraints, **self.options) def fit(self, x, y, dy, constraints, **options): """ Fit the model to data. *This must be overriden by a subclass.* :param x: The x values of the data :type x: :class:`numpy.ndarray` :param y: The y values of the data :type y: :class:`numpy.ndarray` :param dy: 1 sigma uncertainties on each datum (optional) :type dy: :class:`numpy.ndarray` :param constraints: The current value of the ``constraints`` property :param options: kwargs for model hyperparameters. :returns: An object representing the fit result. """ raise NotImplementedError() def predict(self, fit_result, x): """ Evaluate the model at a set of locations. **This must be overridden in a subclass.** :param fit_result: The result from the fit method :param x: Locations to evaluate model at :type x: :class:`numpy.ndarray` :returns: model(x) :rtype: :class:`numpy.ndarray` """ raise NotImplementedError() class AstropyFitter1D(BaseFitter1D): """ A base class for wrapping :mod:`astropy.modeling`. Subclasses must override :attr:`model_cls` :attr:`fitting_cls` to point to the desired Astropy :mod:`model ` and :mod:`fitter ` classes. In addition, they should override :attr:`label` with a better label, and :meth:`parameter_guesses` to generate initial guesses """ model_cls = None """class describing the model""" fitting_cls = None """class to fit the model""" label = "Base Astropy Fitter" """UI Label""" @property def param_names(self): return self.model_cls.param_names def predict(self, fit_result, x): model, _ = fit_result return model(x) def summarize(self, fit_result, x, y, dy=None): model, fitter = fit_result result = [_report_fitter(fitter), ""] pnames = list(sorted(model.param_names)) maxlen = max(map(len, pnames)) result.extend("%s = %e" % (p.ljust(maxlen), getattr(model, p).value) for p in pnames) return "\n".join(result) def fit(self, x, y, dy, constraints): m, f = self._get_model_fitter(x, y, dy, constraints) dy = self._sigma_to_weights(dy) return f(m, x, y, weights=dy), f def _get_model_fitter(self, x, y, dy, constraints): if self.model_cls is None or self.fitting_cls is None: raise NotImplementedError("Model or fitting class is unspecified.") params = dict((k, v['value']) for k, v in constraints.items()) # update unset parameters with guesses from data for k, v in self.parameter_guesses(x, y, dy).items(): if params[k] is not None or constraints[k]['fixed']: continue params[k] = v m = self.model_cls(**params) f = self.fitting_cls() for param_name, constraint in constraints.items(): param = getattr(m, param_name) if constraint['fixed']: param.fixed = True if constraint['limits']: param.min, param.max = constraint['limits'] return m, f def parameter_guesses(self, x, y, dy): """ Provide initial guesses for each model parameter. **The base implementation does nothing, and should be overridden** :param x: X - values of the data :type x: :class:`numpy.ndarray` :param y: Y - values of the data :type y: :class:`numpy.ndarray` :param dy: uncertainties on Y(assumed to be 1 sigma) :type dy: :class:`numpy.ndarray` :returns: A dict mapping ``{parameter_name: value guess}`` for each parameter """ return {} def _gaussian_parameter_estimates(x, y, dy): amplitude = np.percentile(y, 95) y = np.maximum(y / y.sum(), 0) mean = (x * y).sum() stddev = np.sqrt((y * (x - mean) ** 2).sum()) return dict(mean=mean, stddev=stddev, amplitude=amplitude) class BasicGaussianFitter(BaseFitter1D): """ Fallback Gaussian fitter, for astropy < 0.3. If :mod:`astropy.modeling` is installed, this class is replaced by :class:`SimpleAstropyGaussianFitter` """ label = "Gaussian" def _errorfunc(self, params, x, y, dy): yp = self.eval(x, *params) result = (yp - y) if dy is not None: result /= dy return result @staticmethod def eval(x, amplitude, mean, stddev): return np.exp(-(x - mean) ** 2 / (2 * stddev ** 2)) * amplitude @staticmethod def fit_deriv(x, amplitude, mean, stddev): """ Gaussian1D model function derivatives. """ d_amplitude = np.exp(-0.5 / stddev ** 2 * (x - mean) ** 2) d_mean = amplitude * d_amplitude * (x - mean) / stddev ** 2 d_stddev = amplitude * d_amplitude * (x - mean) ** 2 / stddev ** 3 return [d_amplitude, d_mean, d_stddev] def fit(self, x, y, dy, constraints): from scipy import optimize init_values = _gaussian_parameter_estimates(x, y, dy) init_values = [init_values[p] for p in ['amplitude', 'mean', 'stddev']] farg = (x, y, dy) dfunc = None fitparams, status, dinfo, mess, ierr = optimize.leastsq( self._errorfunc, init_values, args=farg, Dfun=dfunc, full_output=True) return fitparams def predict(self, fit_result, x): return self.eval(x, *fit_result) def summarize(self, fit_result, x, y, dy=None): return ("amplitude = %e\n" "mean = %e\n" "stddev = %e" % tuple(fit_result)) GaussianFitter = BasicGaussianFitter try: from astropy.modeling import models, fitting class SimpleAstropyGaussianFitter(AstropyFitter1D): """ Gaussian fitter using astropy.modeling. """ model_cls = models.Gaussian1D try: fitting_cls = fitting.LevMarLSQFitter except AttributeError: # astropy v0.3 fitting_cls = fitting.NonLinearLSQFitter label = "Gaussian" parameter_guesses = staticmethod(_gaussian_parameter_estimates) GaussianFitter = SimpleAstropyGaussianFitter except ImportError: pass class PolynomialFitter(BaseFitter1D): """ A polynomial model. The degree of the polynomial is specified by :attr:`degree` """ label = "Polynomial" degree = IntOption(min=0, max=5, default=3, label="Polynomial Degree") def fit(self, x, y, dy, constraints, degree=2): """ Fit a polynomial of order ``degree`` to the data. """ w = self._sigma_to_weights(dy) return np.polyfit(x, y, degree, w=w) def predict(self, fit_result, x): return np.polyval(fit_result, x) def summarize(self, fit_result, x, y, dy=None): return "Coefficients:\n" + "\n".join("%e" % coeff for coeff in fit_result.tolist()) def _report_fitter(fitter): if "nfev" in fitter.fit_info: return "Converged in %i iterations" % fitter.fit_info['nfev'] return 'Converged' __FITTERS__ = [PolynomialFitter, GaussianFitter] glueviz-1.0.1+dfsg.orig/glue/core/callback_property.py0000644000175000017500000000031713657331513022352 0ustar noahfxnoahfxfrom echo import (CallbackProperty, add_callback, # noqa delay_callback, ignore_callback, # noqa remove_callback, callback_property) # noqa glueviz-1.0.1+dfsg.orig/glue/core/component_link.py0000644000175000017500000004374513641130663021701 0ustar noahfxnoahfximport numbers import operator from inspect import getfullargspec import numpy as np from glue.core.contracts import contract, ContractsMeta from glue.core.coordinate_helpers import (dependent_axes, default_world_coords, pixel2world_single_axis, world2pixel_single_axis) from glue.core.subset import InequalitySubsetState from glue.core.util import join_component_view from glue.utils import unbroadcast, broadcast_to from glue.logger import logger __all__ = ['ComponentLink', 'BinaryComponentLink', 'CoordinateComponentLink'] def null(*args): return None OPSYM = {operator.add: '+', operator.sub: '-', operator.truediv: '/', operator.mul: '*', operator.pow: '**'} class ComponentLink(object, metaclass=ContractsMeta): """ ComponentLinks represent transformation logic between ComponentIDs Parameters ---------- comp_from : list of :class:`~glue.core.component_id.ComponentID` The input ComponentIDs comp_to : :class:`~glue.core.component_id.ComponentID` The target component ID using : func, optional The translation function which maps data from ``comp_from`` to ``comp_to``. The using function should satisfy ``using(data[comp_from[0]],...,data[comp_from[-1]]) = desired data``. If not specifies, this defaults to an identity function. inverse : func, optional The inverse translation function, if exists description : str A short description for the link. This is used e.g. in the link editor. input_names : list of str, optional The names to use for the inputs to the ``using`` function. By default this is determined by inspecting the function signature. This is used e.g. in the link editor. output_name : str, optional The name to use for the output of the ``using`` function. This is used e.g. in the link editor. Notes ----- Both ``inverse`` and ``using`` should accept and return numpy arrays. Examples -------- :: def hours_to_minutes(hours): return hours * 60 d = Data(hour=[1, 2, 3]) hour = d.id['hour'] minute = ComponentID('minute') link = ComponentLink( [hour], minute, using=hours_to_minutes) link.compute(d) # array([ 60, 120, 180]) d.add_component_link(link) d['minute'] # array([ 60, 120, 180]) """ @contract(using='callable|None', inverse='callable|None') def __init__(self, comp_from, comp_to, using=None, inverse=None, inverse_component_link=None, description=None, input_names=None, output_name=None): from glue.core.data import ComponentID from glue.core.link_helpers import identity self._from = comp_from self._to = comp_to if using is None: using = identity if using is identity: if inverse is None: inverse = identity elif inverse is identity: pass else: raise ValueError("Cannot specify inverse if using is identity") self._using = using self._inverse = inverse self.identity = self._using is identity # NOTE: the getattr(using, 'func', using) in the following code # is to make sure that things work properly if the functions are # PartialResult objects. self.description = description or '' self.input_names = input_names or getfullargspec(getattr(using, 'func', using))[0] self.output_name = output_name or 'output' if not isinstance(comp_from, list): raise TypeError("comp_from must be a list: %s" % type(comp_from)) if not all(isinstance(f, ComponentID) for f in self._from): raise TypeError("from argument is not a list of ComponentIDs: %s" % self._from) if not isinstance(self._to, ComponentID): raise TypeError("to argument is not a ComponentID: %s" % type(self._to)) if using is identity: if len(comp_from) != 1: raise TypeError("comp_from must have only 1 element, " "or a 'using' function must be provided") if inverse_component_link is None: if inverse is not None: if len(comp_from) == 1: self._inverse_component_link = ComponentLink([self._to], self._from[0], using=self._inverse, inverse=self._using, inverse_component_link=self) else: raise ValueError("Can only use an inverse with one comp_from link") else: self._inverse_component_link = None else: self._inverse_component_link = inverse_component_link @contract(data='isinstance(Data)', view='array_view') def compute(self, data, view=None): """ For a given data set, compute the component comp_to given the data associated with each comp_from and the ``using`` function This raises an :class:`glue.core.exceptions.IncompatibleAttribute` if the data set doesn't have all the ComponentIDs needed for the transformation Parameters ---------- data : `~glue.core.data.Data` The data set to use view : `None` or `slice` or `tuple` Optional view (e.g. slice) through the data to use Returns ------- result The data associated with comp_to component """ # First we get the values of all the 'from' components. args = [data[join_component_view(f, view)] for f in self._from] # We keep track of the original shape of the arguments original_shape = args[0].shape logger.debug("shape of first argument: %s", original_shape) # We now unbroadcast the arrays to only compute the link with the # smallest number of values we can. This can help for cases where # the link depends only on e.g. pixel components or world coordinates # that themselves only depend on a subset of pixel components. # Unbroadcasting is the act of returning the smallest array that # contains all the information needed to be broadcasted back to its # full value args = [unbroadcast(arg) for arg in args] # We now broadcast these to the smallest common shape in case the # linking functions don't know how to broadcast arrays with different # shapes. args = np.broadcast_arrays(*args) # We call the actual linking function result = self._using(*args) # We call asarray since link functions may return Python scalars in some cases result = np.asarray(result) # In some cases, linking functions return ravelled arrays, so we # fix this here. logger.debug("shape of result: %s", result.shape) if result.shape != args[0].shape: logger.debug("ComponentLink function %s changed shape. Fixing", self._using.__name__) result.shape = args[0].shape # Finally we broadcast the final result to desired shape result = broadcast_to(result, original_shape) return result def __contains__(self, cid): return cid in self._from or cid is self._to def get_from_ids(self): """ The list of input ComponentIDs """ return self._from @contract(old='isinstance(ComponentID)', new='isinstance(ComponentID)') def replace_ids(self, old, new): """Replace all references to an old ComponentID with references to new :parma old: ComponentID to replace :param new: ComponentID to replace with """ for i, f in enumerate(self._from): if f is old: self._from[i] = new if self._to is old: self._to = new @contract(_from='list(isinstance(ComponentID))') def set_from_ids(self, _from): if len(_from) != len(self._from): raise ValueError("New ID list has the wrong length.") self._from = _from def get_to_id(self): """ The target ComponentID """ return self._to def get_to_ids(self): return [self.get_to_id()] def set_to_id(self, to): self._to = to def get_using(self): """ The transformation function """ return self._using @property def inverse(self): if self._inverse is None: return None else: return self._inverse_component_link def get_inverse(self): """ The inverse transformation, or None """ return self._inverse def __str__(self): from glue.core.link_helpers import identity args = ", ".join([t.label for t in self._from]) if self._using is identity: result = "%s <-> %s" % (self._to, self._from[0]) else: if self._inverse is None: result = "%s <- %s(%s)" % (self._to, self._using.__name__, args) else: result = "%s <-> %s(%s)" % (self._to, self._using.__name__, args) return result def to_html(self): from glue.core.link_helpers import identity args = ", ".join([t.to_html() for t in self._from]) if self._using is identity: result = "%s ↔ %s" % (self._to.to_html(), self._from[0].to_html()) else: if self._inverse is None: result = "%s ← %s(%s)" % (self._to.to_html(), self._using.__name__, args) else: result = "%s ↔ %s(%s)" % (self._to.to_html(), self._using.__name__, args) return result def __repr__(self): return str(self) @contract(other='isinstance(ComponentID)|component_like|float|int') def __add__(self, other): return BinaryComponentLink(self, other, operator.add) @contract(other='isinstance(ComponentID)|component_like|float|int') def __radd__(self, other): return BinaryComponentLink(other, self, operator.add) @contract(other='isinstance(ComponentID)|component_like|float|int') def __sub__(self, other): return BinaryComponentLink(self, other, operator.sub) @contract(other='isinstance(ComponentID)|component_like|float|int') def __rsub__(self, other): return BinaryComponentLink(other, self, operator.sub) @contract(other='isinstance(ComponentID)|component_like|float|int') def __mul__(self, other): return BinaryComponentLink(self, other, operator.mul) @contract(other='isinstance(ComponentID)|component_like|float|int') def __rmul__(self, other): return BinaryComponentLink(other, self, operator.mul) @contract(other='isinstance(ComponentID)|component_like|float|int') def __div__(self, other): return BinaryComponentLink(self, other, operator.div) @contract(other='isinstance(ComponentID)|component_like|float|int') def __rdiv__(self, other): return BinaryComponentLink(other, self, operator.div) @contract(other='isinstance(ComponentID)|component_like|float|int') def __truediv__(self, other): return BinaryComponentLink(self, other, operator.truediv) @contract(other='isinstance(ComponentID)|component_like|float|int') def __rtruediv__(self, other): return BinaryComponentLink(other, self, operator.truediv) @contract(other='isinstance(ComponentID)|component_like|float|int') def __pow__(self, other): return BinaryComponentLink(self, other, operator.pow) @contract(other='isinstance(ComponentID)|component_like|float|int') def __rpow__(self, other): return BinaryComponentLink(other, self, operator.pow) @contract(other='isinstance(ComponentID)|component_like|float|int') def __lt__(self, other): return InequalitySubsetState(self, other, operator.lt) @contract(other='isinstance(ComponentID)|component_like|float|int') def __le__(self, other): return InequalitySubsetState(self, other, operator.le) @contract(other='isinstance(ComponentID)|component_like|float|int') def __gt__(self, other): return InequalitySubsetState(self, other, operator.gt) @contract(other='isinstance(ComponentID)|component_like|float|int') def __ge__(self, other): return InequalitySubsetState(self, other, operator.ge) class CoordinateComponentLink(ComponentLink): @contract(comp_from='list(isinstance(ComponentID))', comp_to='isinstance(ComponentID)', coords='isinstance(Coordinates)', index=int, pixel2world=bool) def __init__(self, comp_from, comp_to, coords, index, pixel2world=True): self.coords = coords self.index = index self.pixel2world = pixel2world # Some coords don't need all pixel coords # to compute a given world coord, and vice versa # (e.g., spectral data cubes) self.ndim = len(comp_from) self.from_needed = dependent_axes(coords, index) self._from_all = comp_from comp_from = [comp_from[i] for i in self.from_needed] super(CoordinateComponentLink, self).__init__( comp_from, comp_to, self.using) def using(self, *args): # NOTE: in the past, we set any non-specified arguemnts to 0 for the # input coordinates, but this caused issues because in astropy.wcs # if one specifies e.g. (0, 0, 3000.) for (ra, dec, velocity), and if # (0, 0) for RA/Dec would return (nan, nan) normally, the velocity # is also NaN even though it is decoupled from the other coordinates. default = default_world_coords(self.coords) args2 = [None] * self.ndim for f, a in zip(self.from_needed, args): args2[f] = a for i in range(self.ndim): if args2[i] is None: args2[i] = broadcast_to(default[self.ndim - 1 - i], args[0].shape) args2 = tuple(args2) if self.pixel2world: return pixel2world_single_axis(self.coords, *args2[::-1], world_axis=self.ndim - 1 - self.index) else: return world2pixel_single_axis(self.coords, *args2[::-1], pixel_axis=self.ndim - 1 - self.index) def __str__(self): rep = 'pix2world' if self.pixel2world else 'world2pix' sup = super(CoordinateComponentLink, self).__str__() return sup.replace('using', rep) class BinaryComponentLink(ComponentLink): """ A ComponentLink that combines two inputs with a binary function :param left: The first input argument. ComponentID, ComponentLink, or number :param right: The second input argument. ComponentID, ComponentLink, or number :param op: A function with two inputs that works on numpy arrays The CompoentLink represents the logic of applying `op` to the data associated with the inputs `left` and `right`. """ def __init__(self, left, right, op): from glue.core.data import ComponentID self._left = left self._right = right self._op = op from_ = [] if isinstance(left, ComponentID): from_.append(left) elif isinstance(left, ComponentLink): from_.extend(left.get_from_ids()) elif not isinstance(left, numbers.Number): raise TypeError("Cannot create BinaryComponentLink using %s" % left) if isinstance(right, ComponentID): from_.append(right) elif isinstance(right, ComponentLink): from_.extend(right.get_from_ids()) elif not isinstance(right, numbers.Number): raise TypeError("Cannot create BinaryComponentLink using %s" % right) to = ComponentID("") super(BinaryComponentLink, self).__init__(from_, to, null) def replace_ids(self, old, new): super(BinaryComponentLink, self).replace_ids(old, new) if self._left is old: self._left = new elif isinstance(self._left, ComponentLink): self._left.replace_ids(old, new) if self._right is old: self._right = new elif isinstance(self._right, ComponentLink): self._right.replace_ids(old, new) def compute(self, data, view=None): left = self._left right = self._right if not isinstance(self._left, numbers.Number): left = data[self._left, view] if not isinstance(self._right, numbers.Number): right = data[self._right, view] # As described in more detail in ComponentLink.compute, we can # 'unbroadcast' the arrays to ensure a minimal operation original_shape = None if isinstance(left, np.ndarray): original_shape = left.shape left = unbroadcast(left) if isinstance(right, np.ndarray): original_shape = right.shape right = unbroadcast(right) if original_shape is not None: left, right = np.broadcast_arrays(left, right) result = self._op(left, right) if original_shape is None: return result else: return broadcast_to(result, original_shape) def __gluestate__(self, context): left = context.id(self._left) right = context.id(self._right) operator = context.do(self._op) return dict(left=left, right=right, operator=operator) @classmethod def __setgluestate__(cls, rec, context): left = context.object(rec['left']) right = context.object(rec['right']) operator = context.object(rec['operator']) return cls(left, right, operator) def __str__(self): sym = OPSYM.get(self._op, self._op.__name__) return '(%s %s %s)' % (self._left, sym, self._right) def __repr__(self): return "" % self glueviz-1.0.1+dfsg.orig/glue/core/fixed_resolution_buffer.py0000644000175000017500000003064213660521343023565 0ustar noahfxnoahfximport numpy as np from glue.core import Data from glue.core.exceptions import IncompatibleDataException from glue.core.component import CoordinateComponent, DaskComponent from glue.core.coordinate_helpers import dependent_axes from glue.utils import unbroadcast, broadcast_to, broadcast_arrays_minimal # TODO: cache needs to be updated when links are removed/changed __all__ = ['compute_fixed_resolution_buffer'] ARRAY_CACHE = {} PIXEL_CACHE = {} def translate_pixel(data, pixel_coords, target_cid): """ Given a dataset and pixel coordinates in that dataset, compute a corresponding pixel coordinate for another dataset. Parameters ---------- data : `~glue.core.data.Data` The main data object in which the original pixel coordinates are defined. pixel_coords : tuple or list List of pixel coordinates in the original data object. This should contain as many Numpy arrays as there are dimensions in ``data``. target_cid : `~glue.core.component_id.ComponentID` The component to compute - this can be for a different dataset. Returns ------- coords : `~numpy.ndarray` The values of the translated coordinates dimensions : tuple The dimensions in ``data`` for which pixel coordinates were used in the translation. """ if not len(pixel_coords) == data.ndim: raise ValueError('The number of coordinates in pixel_coords does not ' 'match the number of dimensions in data') if target_cid in data.pixel_component_ids: return pixel_coords[target_cid.axis], [target_cid.axis] # TODO: check that things are efficient if the PixelComponentID is in a # pixel-aligned dataset. component = data.get_component(target_cid) if hasattr(component, 'link'): link = component.link values_all = [] dimensions_all = [] for cid in link._from: values, dimensions = translate_pixel(data, pixel_coords, cid) values_all.append(values) dimensions_all.extend(dimensions) # Unbroadcast arrays to smallest common shape for performance if len(values_all) > 0: shape = values_all[0].shape values_all = broadcast_arrays_minimal(*values_all) results = link._using(*values_all) result = broadcast_to(results, shape) else: result = None return result, sorted(set(dimensions_all)) elif isinstance(component, CoordinateComponent): # FIXME: Hack for now - if we pass arrays in the view, it's interpreted return component._calculate(view=pixel_coords), dependent_axes(data.coords, component.axis) else: raise Exception("Dependency on non-pixel component", target_cid) class AnyScalar(object): def __eq__(self, other): return np.isscalar(other) def bounds_for_cache(bounds, dimensions): cache_bounds = [] for i in range(len(bounds)): if i not in dimensions and np.isscalar(bounds[i]): cache_bounds.append(AnyScalar()) else: cache_bounds.append(bounds[i]) return cache_bounds def compute_fixed_resolution_buffer(data, bounds, target_data=None, target_cid=None, subset_state=None, broadcast=True, cache_id=None): """ Get a fixed-resolution buffer for a dataset. Parameters ---------- data : `~glue.core.Data` The dataset from which to extract a fixed resolution buffer bounds : list The list of bounds for the fixed resolution buffer. This list should have as many items as there are dimensions in ``target_data``. Each item should either be a scalar value, or a tuple of ``(min, max, nsteps)``. target_data : `~glue.core.Data`, optional The data in whose frame of reference the bounds are defined. Defaults to ``data``. target_cid : `~glue.core.component_id.ComponentID`, optional If specified, gives the component ID giving the component to use for the data values. Alternatively, use ``subset_state`` to get a subset mask. subset_state : `~glue.core.subset.SubsetState`, optional If specified, gives the subset state for which to compute a mask. Alternatively, use ``target_cid`` if you want to get data values. broadcast : bool, optional If `True`, then if a dimension in ``target_data`` for which ``bounds`` is not a scalar does not affect any of the dimensions in ``data``, then the final array will be effectively broadcast along this dimension, otherwise an error will be raised. """ if target_data is None: target_data = data if target_cid is None and subset_state is None: raise ValueError("Either target_cid or subset_state should be specified") if target_cid is not None and subset_state is not None: raise ValueError("Either target_cid or subset_state should be specified (not both)") for bound in bounds: if isinstance(bound, tuple) and bound[2] < 1: raise ValueError("Number of steps in bounds should be >=1") # If cache_id is specified, we keep a cached version of the resulting array # indexed by cache_id as well as a hash formed of the call arguments to this # function. We then check if the resulting array already exists in the cache. if cache_id is not None: if subset_state is None: # Use uuid for component ID since otherwise component IDs don't return # False when comparing two different CIDs (instead they return a subset state). # For bounds we use a special wrapper that can identify wildcards. current_array_hash = (data, bounds, target_data, target_cid.uuid, broadcast) else: current_array_hash = (data, bounds, target_data, subset_state, broadcast) current_pixel_hash = (data, target_data) if cache_id in ARRAY_CACHE: if ARRAY_CACHE[cache_id]['hash'] == current_array_hash: return ARRAY_CACHE[cache_id]['array'] # To save time later, if the pixel cache doesn't match at the level of the # data and target_data, we just reset the cache. if cache_id in PIXEL_CACHE: if PIXEL_CACHE[cache_id]['hash'] != current_pixel_hash: PIXEL_CACHE.pop(cache_id) # Start off by generating arrays of coordinates in the original dataset pixel_coords = [np.linspace(*bound) if isinstance(bound, tuple) else bound for bound in bounds] pixel_coords = np.meshgrid(*pixel_coords, indexing='ij', copy=False) # Keep track of the original shape of these arrays original_shape = pixel_coords[0].shape # Now loop through the dimensions of 'data' to find the corresponding # coordinates in the frame of view of this dataset. translated_coords = [] dimensions_all = [] invalid_all = np.zeros(original_shape, dtype=bool) for ipix, pix in enumerate(data.pixel_component_ids): # At this point, if cache_id is in PIXEL_CACHE, we know that data and # target_data match so we just check the bounds. Note that the bounds # include the AnyScalar wildcard for any dimensions that don't impact # the pixel coordinates here. We do this so that we don't have to # recompute the pixel coordinates when e.g. slicing through cubes. if cache_id in PIXEL_CACHE and ipix in PIXEL_CACHE[cache_id] and PIXEL_CACHE[cache_id][ipix]['bounds'] == bounds: translated_coord = PIXEL_CACHE[cache_id][ipix]['translated_coord'] dimensions = PIXEL_CACHE[cache_id][ipix]['dimensions'] invalid = PIXEL_CACHE[cache_id][ipix]['invalid'] else: translated_coord, dimensions = translate_pixel(target_data, pixel_coords, pix) # The returned coordinates may often be a broadcasted array. To convert # the coordinates to integers and check which ones are within bounds, we # thus operate on the un-broadcasted array, before broadcasting it back # to the original shape. translated_coord = np.round(unbroadcast(translated_coord)).astype(int) invalid = (translated_coord < 0) | (translated_coord >= data.shape[ipix]) # Since we are going to be using these coordinates later on to index an # array, we need the coordinates to be within the array, so we reset # any invalid coordinates and keep track of which pixels are invalid # to reset them later. translated_coord[invalid] = 0 # We now populate the cache if cache_id is not None: if cache_id not in PIXEL_CACHE: PIXEL_CACHE[cache_id] = {'hash': current_pixel_hash} PIXEL_CACHE[cache_id][ipix] = {'translated_coord': translated_coord, 'dimensions': dimensions, 'invalid': invalid, 'bounds': bounds_for_cache(bounds, dimensions)} invalid_all |= invalid # Broadcast back to the original shape and add to the list translated_coords.append(broadcast_to(translated_coord, original_shape)) # Also keep track of all the dimensions that contributed to this coordinate dimensions_all.extend(dimensions) translated_coords = tuple(translated_coords) # If a dimension from the target data for which bounds was set to an interval # did not actually contribute to any of the coordinates in data, then if # broadcast is set to False we raise an error, otherwise we proceed and # implicitly broadcast values along that dimension of the target data. if data is not target_data and not broadcast: for i in range(target_data.ndim): if isinstance(bounds[i], tuple) and i not in dimensions_all: raise IncompatibleDataException() # Ideally, we would use fancy indexing to extract the data, but this can # be slow on non-SSD drives if using memory-mapped arrays, and also doesn't # work at all for dask arrays. For now, we therefore extract a sub-region # from the data before applying fancy indexing if the data is a dask # array. This won't be very efficient when dealing with 3d fixed # resolution buffers, but it will at least work as opposed to not. if target_cid is not None and isinstance(data, Data) and isinstance(data.get_component(target_cid), DaskComponent): # Extract sub-region of data first, then fetch exact coordinate values subregion = tuple([slice(np.nanmin(coord), np.nanmax(coord) + 1) for coord in translated_coords]) # Take subset_state into account, if present if subset_state is None: array = data.get_data(target_cid, view=subregion).astype(float) invalid_value = -np.inf else: array = data.get_mask(subset_state, view=subregion) invalid_value = False translated_coords = tuple([coord - np.nanmin(coord) for coord in translated_coords]) array = array[translated_coords] else: # Take subset_state into account, if present if subset_state is None: array = data.get_data(target_cid, view=translated_coords).astype(float) invalid_value = -np.inf else: array = data.get_mask(subset_state, view=translated_coords) invalid_value = False if np.any(invalid_all): if not array.flags.writeable: array = np.array(array, dtype=type(invalid_value)) array[invalid_all] = invalid_value # Drop dimensions for which bounds were scalars slices = [] for bound in bounds: if isinstance(bound, tuple): slices.append(slice(None)) else: slices.append(0) array = array[tuple(slices)] if cache_id is not None: # For the bounds, we use a special wildcard for bounds that don't affect # the result. This will allow the cache to match regardless of the # value for those bounds. However, we only do this for scalar bounds. cache_bounds = bounds_for_cache(bounds, dimensions_all) current_array_hash = current_array_hash[:1] + (cache_bounds,) + current_array_hash[2:] if subset_state is None: ARRAY_CACHE[cache_id] = {'hash': current_array_hash, 'array': array} else: ARRAY_CACHE[cache_id] = {'hash': current_array_hash, 'array': array} return array glueviz-1.0.1+dfsg.orig/glue/core/state.py0000644000175000017500000011253213752534424017777 0ustar noahfxnoahfx""" Module to convert Glue objects to and from JSON descriptions Example Usage: s = GlueSerializer(object) s.dumpo() -> a JSON-serializeable dict s.dumps() -> a JSON string s.dump(file) -> dump to a file object varname = s.id(x) -> string identifier that uniquely labels an object in the Serialized state u = GlueUnSerializer.load(file) u = GlueUnSerializer.loads(str) u.object(varname) -> A reconstituted version of `x` u.object('__main__') -> The object passed to the GlueSerializer constructor Developer Notes: Custom methods to serialize a class of objects can be registered either by: - wrapping a serialization function in the @saver decorator:: @saver(TypeToSave) def save(object, context): ... - Defining a __gluestate__(self, context) method These methods should return a JSON-serializable dict representing the object. context is a GlueSerializer instance. The `context.id` and `context.do` methods are helpful for referencing or serializing or dependencies Unserializer methods can be registered either via: - wrapping the method in the @loader decorator:: @loader(TypeToLoad) def load(rec, context) `rec` is the JSON dict created from the saver, and `context` is a GlueUnserializer object. context.object() is useful for unserializing dependencies. Versions: Both the @saver and @loader take an optional version keyword. Whenever you modify the serialization format for an object, you should register a new saver and loader version. This ensures Glue can still load old serialization protocols. Versions must be sequential integers, starting from 1. """ import os import json import uuid import types import logging from io import BytesIO from itertools import count from collections import defaultdict, OrderedDict from base64 import b64encode, b64decode from inspect import isgeneratorfunction import numpy as np from matplotlib.colors import Colormap from matplotlib import cm from astropy.wcs import WCS from glue import core from glue.core.data import Data from glue.core.component_id import ComponentID, PixelComponentID from glue.core.component import (Component, CategoricalComponent, DerivedComponent, CoordinateComponent) from glue.core.subset import (OPSYM, SYMOP, CompositeSubsetState, SubsetState, Subset, RoiSubsetState, InequalitySubsetState, RangeSubsetState) from glue.core import (VisualAttributes, ComponentLink, DataCollection) from glue.core.component_link import CoordinateComponentLink from glue.core.roi import Roi from glue.core import glue_pickle as gp from glue.core.subset_group import coerce_subset_groups from glue.utils import lookup_class from glue.config import session_patch literals = tuple([type(None), float, int, bytes, bool]) literals += tuple(s for s in np.ScalarType if s not in (np.datetime64, np.timedelta64)) builtin_iterables = (tuple, list, set) JSON_ENCODER = json.JSONEncoder() # We need to make sure that we don't break backward-compatibility when we move # classes/functions around in Glue, so we have a file that maps the old paths to # the new location, and we read this in to PATH_PATCHES. PATCH_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), 'state_path_patches.txt')) # For Mac app, need to get file from source directory if not os.path.exists(PATCH_FILE) and 'site-packages.zip' in PATCH_FILE: PATCH_FILE = PATCH_FILE.replace('site-packages.zip', 'glue') PATH_PATCHES = {} with open(PATCH_FILE) as fp: for line in fp: before, after = line.strip().split(' -> ') PATH_PATCHES[before.strip()] = after.strip() def save(filename, obj): s = GlueSerializer(obj) with open(filename, 'w') as f: s.dump(f) def load(filename): with open(filename, 'r') as f: s = GlueUnSerializer.load(f) return s.object('__main__') def lookup_class_with_patches(name): """ A wrapper to lookup_class that also patches paths to ensure backward-compatibility when functions/classes are moved around. """ while name in PATH_PATCHES: name = PATH_PATCHES[name] return lookup_class(name) class GlueSerializeError(RuntimeError): pass class VersionedDict(object): """ A dict-like object which associates (key, version_int) pairs with an object. Bracket syntax (d[key]) returns the highest-version value stored with a key. Versions must be sequential integers starting with 1, and must be added in order Examples -------- v = VersionedDict() v['key', 1] = 'v1' v['key', 2] = 'v2' v['key'] -> 'v2', 2 v.get_version('key', 2) -> 'v2' v.get_version('key', 1) -> 'v1' 'key' in v -> True Not allowed: v['key', 4] = 'cannot skip versions' v['key', 2] = 'cannot overwrite versions' v['key', 'bad'] = 'versions must be integers' """ def __init__(self): self._data = defaultdict(dict) def __contains__(self, key): return key in self._data def get_version(self, key, version=None): """ Get a specific version of a value stored with a key :param key: The key to fetch :param value: the version of the value to fetch. Defaults to latest """ if version is None: if key not in self._data: raise KeyError("No value associated with any version of %s" % key) vs = self._data[key] return vs[max(vs)] try: return self._data[key][version] except KeyError: raise KeyError("No value associated with version %s of %s" % (version, key)) def __getitem__(self, key): """Retrieve the highest-version value stored with a key Returns a tuple of the value, and the version it is associated with """ if key not in self._data: raise KeyError(key) versions = self._data[key] return versions[max(versions)], max(versions) def __delitem__(self, key): raise ValueError("Cannot remove items from VersionedDict") def __len__(self): return len(self._data) def __setitem__(self, key, value): """ Assign a new value with a particular key and version :param key: a tuple of (key, version) version must be an integer, equal to the previous version + 1 (or 1) Overwriting versions is not permitted, and will raise a KeyError :param value: The value to associate with the (key, version) pair """ if len(key) != 2: raise ValueError("Key must be a (item, version) pair") item, version = key try: version = int(version) except ValueError: raise ValueError("Version must be an integer: %s" % version) if version > 1 and (version - 1) not in self._data[item]: raise KeyError("Cannot assign version %i of item before adding " "version %i" % (version, version - 1)) if version in self._data[item]: raise KeyError("Cannot overwrite version %i of %s" % (version, item)) self._data[item][version] = value def as_nested_lists(obj): items = [] for item in obj: if type(item) in builtin_iterables: item = as_nested_lists(item) items.append(item) return items def flattened(obj): items = [] for item in obj: if type(item) in builtin_iterables: items += as_nested_lists(item) else: items.append(item) return items class GlueSerializer(object): """ Serialize an object graph """ dispatch = VersionedDict() def __init__(self, obj, include_data=False, absolute_paths=True): self._names = {} # map id(object) -> name self._objs = {} # map name -> object self._working = set() self._main = obj self.id(obj) self.include_data = include_data self.absolute_paths = absolute_paths @classmethod def serializes(cls, obj, version=1): def decorator(func): cls.dispatch[(obj, version)] = func return func return decorator def _label(self, obj): if obj is self._main: return '__main__' elif hasattr(obj, 'label'): return self._disambiguate(obj.label) else: return self._disambiguate(type(obj).__name__) def id(self, obj): """ Return a unique name for an object, and add it to the ID registry if necessary. """ if isinstance(obj, str): return 'st__%s' % obj if type(obj) in literals: return obj # Now check for list, set, and tuple, and skip if they don't contain # any non-literals. if type(obj) in builtin_iterables: if all(isinstance(x, literals) for x in flattened(obj)): return as_nested_lists(obj) oid = id(obj) if oid in self._names: return self._names[oid] name = self._label(obj) assert name not in self._objs logging.debug("Registering %r as %s", obj, name) self._objs[name] = obj self._names[oid] = name return name def object(self, name): return self._objs[name] def do_all(self): sz = -1 while sz != len(self._objs): sz = len(self._objs) # we need to construct this in two steps otherwise we get a # 'dictionary changed size during iteration' error. result = [(oid, self.do(obj)) for oid, obj in list(self._objs.items())] result = dict(result) return result def do(self, obj): """ Serialize an object, but do not add it to the ID registry """ if isinstance(obj, str): return 'st__' + obj if type(obj) in literals: return obj # Now check for list, set, and tuple, and skip if they don't contain # any non-literals if type(obj) in builtin_iterables: if all(isinstance(x, literals) for x in flattened(obj)): return as_nested_lists(obj) oid = id(obj) if oid in self._working: raise GlueSerializeError("Circular reference detected") self._working.add(oid) fun, version = self._dispatch(obj) logging.debug("Serializing %s with %s", obj, fun) result = fun(obj, self) if isinstance(obj, types.FunctionType): result['_type'] = 'types.FunctionType' elif isinstance(obj, types.MethodType): result['_type'] = 'types.MethodType' else: result['_type'] = "%s.%s" % (type(obj).__module__, type(obj).__name__) if version > 1: result['_protocol'] = version self._working.remove(oid) return result def _dispatch(self, obj): if hasattr(obj, '__gluestate__'): return type(obj).__gluestate__, 1 try: for typ in type(obj).mro(): if typ in self.dispatch: return self.dispatch[typ] except TypeError: # no mro pass raise GlueSerializeError("Don't know how to serialize" " %r of type %s" % (obj, type(obj))) def _disambiguate(self, name): if name not in self._objs: return name for i in count(0): newname = "%s_%i" % (name, i) if newname not in self._objs: return newname def dumpo(self): """ Dump an object (with needed dependencies) into a JSON Serializable data structure. Note: If eventually dumping to a string or file, dumps or dump are more robust """ return self.do_all() @staticmethod def json_default(o): """Default JSON enconding, to handle some special cases In particular, coerces numpy scalars to the equivalent python types Can be used as default kwarg in json.dumps/json.dump """ if np.isscalar(o) and isinstance(o, np.generic): return np.asscalar(o) # coerce numpy number to pure-python type elif isinstance(o, (tuple, set)): return list(o) else: # Dispatch to JSONEncoder so that we see errors straight away # if an object can't be serialized. return JSON_ENCODER.default(o) def dumps(self, indent=None): result = self.dumpo() return json.dumps(result, default=self.json_default, indent=indent, sort_keys=True) def dump(self, outfile, indent=None): result = self.dumpo() return json.dump(result, outfile, default=self.json_default, indent=indent, sort_keys=True) class GlueUnSerializer(object): dispatch = VersionedDict() def __init__(self, string=None, fobj=None): if string is None and fobj is None: raise ValueError("Most provide either a string or a file") self._names = {} # map id(object) -> name self._objs = {} # map name -> object self._working = set() self._rec = json.loads(string) if string else json.load(fobj) self._callbacks = [] # apply Glue defined patches apply_inplace_patches(self._rec) # apply user-defined patches for patcher in session_patch: patcher.function(self._rec) @classmethod def loads(cls, string): return cls(string=string) @classmethod def load(cls, fobj): return cls(fobj=fobj) @classmethod def unserializes(cls, obj, version=1): def decorator(func): cls.dispatch[(obj, version)] = func return func return decorator def _dispatch(self, rec): typ = lookup_class_with_patches(rec['_type']) if typ is None: raise GlueSerializeError("Unkonwn type %s" % rec['_type']) version = rec.get('_protocol', 1) if hasattr(typ, '__setgluestate__'): return typ.__setgluestate__ for t in typ.mro(): try: return self.dispatch.get_version(t, version) except KeyError: continue raise GlueSerializeError("Don't know how to load" " objects of type %s" % typ) def register_object(self, obj_id, obj): self._objs[obj_id] = obj @core.registry.disable def object(self, obj_id): if isinstance(obj_id, str): if obj_id.startswith('st__'): # a string literal return obj_id[4:] if obj_id in self._objs: return self._objs[obj_id] if obj_id not in self._rec: raise GlueSerializeError("Unrecognized object %s" % obj_id) if obj_id in self._working: raise GlueSerializeError("Circular Reference detected: %s" % obj_id) self._working.add(obj_id) rec = self._rec[obj_id] elif isinstance(obj_id, literals) or isinstance(obj_id, (tuple, list)): return obj_id else: rec = obj_id func = self._dispatch(rec) try: obj = func(rec, self) if hasattr(obj, '__setgluestate_callback__'): self._callbacks.append(obj.__setgluestate_callback__) # loader functions might yield the constructed value, # and then futher populate it. This deals with circular # dependencies. if isgeneratorfunction(func): gen, obj = obj, next(obj) # get the partially-constructed value... if isinstance(obj_id, str): # ... add it to the registry ... self._objs[obj_id] = obj self._working.remove(obj_id) if isgeneratorfunction(func): for _ in gen: # ... and finish constructing it pass finally: # If anything in the try: block above fails, we need to remove the # obj_id from te list of IDs we are currently working on, as we # may want to try again (this happens when using the callbacks below) if isinstance(obj_id, str) and obj_id in self._working: self._working.remove(obj_id) self._try_callbacks() return obj def _try_callbacks(self): for callback in self._callbacks[:]: try: callback(self) except Exception: pass else: # In some cases (unclear how to trigger this) callback is no # longer in the list by the time we try and remove it, hence # why we need this try...except. try: self._callbacks.remove(callback) except ValueError: pass saver = GlueSerializer.serializes loader = GlueUnSerializer.unserializes @saver(dict) def _save_dict(state, context): return dict(contents=dict((context.id(key), context.id(value)) for key, value in state.items())) @loader(dict) def _load_dict(rec, context): return dict((context.object(key), context.object(value)) for key, value in rec['contents'].items()) @saver(tuple) def _save_tuple(state, context): return dict(contents=[context.do(item) for item in state]) @loader(tuple) def _load_tuple(rec, context): return tuple(_load_list(rec, context)) @saver(list) def _save_list(state, context): return dict(contents=[context.id(item) for item in state]) @loader(list) def _load_list(rec, context): return [context.object(item) for item in rec['contents']] @saver(set) def _save_set(state, context): return dict(contents=[context.do(item) for item in state]) @loader(set) def _load_set(rec, context): return set(_load_list(rec, context)) @saver(slice) def _save_slice(slc, context): return dict(start=slc.start, stop=slc.stop, step=slc.step) @loader(slice) def _load_slice(rec, context): return slice(rec['start'], rec['stop'], rec['step']) @saver(WCS) def _save_wcs(wcs, context): return dict(header=wcs.to_header_string()) @loader(WCS) def _load_wcs(rec, context): from astropy.io import fits return WCS(fits.Header.fromstring(rec['header'])) @saver(CompositeSubsetState) def _save_composite_subset_state(state, context): return dict(state1=context.id(state.state1), state2=context.id(state.state2)) @loader(CompositeSubsetState) def _load_composite_subset_state(rec, context): cls = lookup_class_with_patches(rec['_type']) result = cls(context.object(rec['state1']), context.object(rec['state2'])) return result @saver(SubsetState) def _save_subset_state(state, context): return {} @loader(SubsetState) def _load_subset_state(rec, context): return SubsetState() @saver(RangeSubsetState) def _save_range_subset_state(state, context): return dict(lo=context.id(state.lo), hi=context.id(state.hi), att=context.id(state.att)) @loader(RangeSubsetState) def _load_range_subset_state(rec, context): return RangeSubsetState(context.object(rec['lo']), context.object(rec['hi']), context.object(rec['att'])) @saver(RoiSubsetState) def _save_roi_subset_state(state, context): return dict(xatt=context.id(state.xatt), yatt=context.id(state.yatt), roi=context.id(state.roi), pretransform=context.id(state.pretransform)) @loader(RoiSubsetState) def _load_roi_subset_state(rec, context): return RoiSubsetState(context.object(rec['xatt']), context.object(rec['yatt']), context.object(rec['roi']), context.object(rec['pretransform'] if 'pretransform' in rec else None)) @saver(InequalitySubsetState) def _save_inequality_subset_state(state, context): return dict(left=context.id(state.left), right=context.id(state.right), op=OPSYM.get(state.operator)) @loader(InequalitySubsetState) def _load_inequality_subset_state(rec, context): return InequalitySubsetState(context.object(rec['left']), context.object(rec['right']), SYMOP[rec['op']]) @saver(Roi) def _save_roi(roi, context): raise NotImplementedError @loader(Roi) def _laod_roi(roi, context): raise NotImplementedError @saver(VisualAttributes) def _save_style(style, context): return dict((a, getattr(style, a)) for a in style._atts) @loader(VisualAttributes) def _load_style(rec, context): result = VisualAttributes() if 'preferred_cmap' in result._atts: result._atts.remove('preferred_cmap') for attr in result._atts: setattr(result, attr, rec[attr]) return result @saver(Subset) def _save_subset(subset, context): return dict(style=context.do(subset.style), state=context.id(subset.subset_state), label=subset.label) @loader(Subset) def _load_subset(rec, context): result = Subset(None) result.style = context.object(rec['style']) result.subset_state = context.object(rec['state']) result.label = rec['label'] return result @saver(DataCollection) def _save_data_collection(dc, context): cids = [c for data in dc for c in data.component_ids()] components = [data.get_component(c) for data in dc for c in data.component_ids()] return dict(data=list(map(context.id, dc)), links=list(map(context.id, dc.links)), cids=list(map(context.id, cids)), components=list(map(context.id, components))) @saver(DataCollection, version=2) def _save_data_collection_2(dc, context): result = _save_data_collection(dc, context) result['groups'] = list(map(context.id, dc.subset_groups)) return result @saver(DataCollection, version=3) def _save_data_collection_3(dc, context): result = _save_data_collection_2(dc, context) result['subset_group_count'] = dc._sg_count return result @saver(DataCollection, version=4) def _save_data_collection_4(dc, context): cids = [c for data in dc for c in data.component_ids()] components = [data.get_component(c) for data in dc for c in data.component_ids()] return dict(data=list(map(context.id, dc)), links=list(map(context.id, dc.external_links)), cids=list(map(context.id, cids)), components=list(map(context.id, components)), groups=list(map(context.id, dc.subset_groups)), subset_group_count=dc._sg_count) @loader(DataCollection) def _load_data_collection(rec, context): datasets = list(map(context.object, rec['data'])) links = [context.object(link) for link in rec['links']] # Filter out CoordinateComponentLinks that may have been saved in the past # as these are now re-generated on-the-fly. links = [link for link in links if not isinstance(link, CoordinateComponentLink)] # Go through and split links into links internal to datasets and ones # between datasets as this dictates whether they should be set on the # data collection or on the data objects. external, internal = [], [] for link in links: parent_to = link.get_to_id().parent for cid in link.get_from_ids(): if cid.parent is not parent_to: external.append(link) break else: internal.append(link) # Remove components in datasets that have external links for data in datasets: remove = [] for cid in data.derived_components: comp = data.get_component(cid) # Neihter in external nor in links overall if rec.get('_protocol', 0) <= 3: if comp.link not in internal: remove.append(cid) if isinstance(comp.link, CoordinateComponentLink): remove.append(cid) if len(comp.link.get_from_ids()) == 1 and comp.link.get_from_ids()[0].parent is comp.link.get_to_id().parent and comp.link.get_from_ids()[0].label == comp.link.get_to_id().label: remove.append(cid) for cid in remove: data.remove_component(cid) dc = DataCollection(datasets) dc.set_links(external) coerce_subset_groups(dc) return dc @loader(DataCollection, version=2) def _load_data_collection_2(rec, context): result = _load_data_collection(rec, context) result._subset_groups = list(map(context.object, rec['groups'])) for grp in result.subset_groups: grp.register_to_hub(result.hub) return result @loader(DataCollection, version=3) def _load_data_collection_3(rec, context): result = _load_data_collection_2(rec, context) result._sg_count = rec['subset_group_count'] return result @loader(DataCollection, version=4) def _load_data_collection_4(rec, context): dc = DataCollection(list(map(context.object, rec['data']))) links = [context.object(link) for link in rec['links']] dc.set_links(links) coerce_subset_groups(dc) dc._subset_groups = list(map(context.object, rec['groups'])) for grp in dc.subset_groups: grp.register_to_hub(dc.hub) dc._sg_count = rec['subset_group_count'] return dc @saver(Data) def _save_data(data, context): state = dict(components=[(context.id(c), context.id(data.get_component(c))) for c in data._components], subsets=[context.id(s) for s in data.subsets], label=data.label) if data.coords is not None: state['coords'] = context.id(data.coords) return state @saver(Data, version=2) def _save_data_2(data, context): result = _save_data(data, context) result['style'] = context.do(data.style) return result @loader(Data) def _load_data(rec, context): label = rec['label'] result = Data(label=label) if 'coords' in rec: result.coords = context.object(rec['coords']) # we manually rebuild pixel/world components, so # we override this function. This is pretty ugly result._create_pixel_and_world_components = lambda ndim: None comps = [list(map(context.object, [cid, comp])) for cid, comp in rec['components']] for icomp, (cid, comp) in enumerate(comps): if isinstance(comp, CoordinateComponent): comp._data = result # For backward compatibility, we need to check for cases where # the component ID for the pixel components was not a PixelComponentID # and upgrade it to one. This can be removed once we no longer # support pre-v0.8 session files. if not comp.world and not isinstance(cid, PixelComponentID): cid = PixelComponentID(comp.axis, cid.label, parent=cid.parent) comps[icomp] = (cid, comp) result.add_component(comp, cid) assert result._world_component_ids == [] coord = [c for c in comps if isinstance(c[1], CoordinateComponent)] coord = [x[0] for x in sorted(coord, key=lambda x: x[1])] if getattr(result, 'coords') is not None: assert len(coord) == result.ndim * 2 result._world_component_ids = coord[:len(coord) // 2] result._pixel_component_ids = coord[len(coord) // 2:] else: assert len(coord) == result.ndim result._pixel_component_ids = coord # We can now re-generate the coordinate links result._set_up_coordinate_component_links(result.ndim) for s in rec['subsets']: result.add_subset(context.object(s)) return result @loader(Data, version=2) def _load_data_2(rec, context): # adds style saving result = _load_data(rec, context) result.style = context.object(rec['style']) return result @saver(Data, version=3) def _save_data_3(data, context): result = _save_data_2(data, context) result['_key_joins'] = [[context.id(k), context.id(v0), context.id(v1)] for k, (v0, v1) in data._key_joins.items()] return result @loader(Data, version=3) def _load_data_3(rec, context): result = _load_data_2(rec, context) yield result result._key_joins = dict((context.object(k), (context.object(v0), context.object(v1))) for k, v0, v1 in rec['_key_joins']) @saver(Data, version=4) def _save_data_4(data, context): result = _save_data_2(data, context) def save_cid_tuple(cids): return tuple(context.id(cid) for cid in cids) result['_key_joins'] = [[context.id(k), save_cid_tuple(v0), save_cid_tuple(v1)] for k, (v0, v1) in data._key_joins.items()] result['uuid'] = data.uuid return result @loader(Data, version=4) def _load_data_4(rec, context): result = _load_data_2(rec, context) yield result def load_cid_tuple(cids): return tuple(context.object(cid) for cid in cids) result._key_joins = dict((context.object(k), (load_cid_tuple(v0), load_cid_tuple(v1))) for k, v0, v1 in rec['_key_joins']) if 'uuid' in rec and rec['uuid'] is not None: result.uuid = rec['uuid'] else: result.uuid = str(uuid.uuid4()) @saver(Data, version=5) def _save_data_5(data, context): result = _save_data_4(data, context) result['primary_owner'] = [context.id(cid) for cid in data.components if cid.parent is data] # Filter out keys/values that can't be serialized meta_filtered = OrderedDict() for key, value in data.meta.items(): try: context.do(key) context.do(value) except GlueSerializeError: continue else: meta_filtered[key] = value result['meta'] = context.do(meta_filtered) return result @loader(Data, version=5) def _load_data_5(rec, context): result = _load_data_2(rec, context) if 'primary_owner' in rec: for cid in rec['primary_owner']: cid = context.object(cid) cid.parent = result yield result def load_cid_tuple(cids): return tuple(context.object(cid) for cid in cids) result._key_joins = dict((context.object(k), (load_cid_tuple(v0), load_cid_tuple(v1))) for k, v0, v1 in rec['_key_joins']) if 'uuid' in rec and rec['uuid'] is not None: result.uuid = rec['uuid'] else: result.uuid = str(uuid.uuid4()) if 'meta' in rec: result.meta.update(context.object(rec['meta'])) @saver(ComponentID) def _save_component_id(cid, context): return dict(label=cid.label) @loader(ComponentID) def _load_component_id(rec, context): return ComponentID(rec['label']) @saver(PixelComponentID) def _save_pixel_component_id(cid, context): return dict(axis=cid.axis, label=cid.label) @loader(PixelComponentID) def _load_pixel_component_id(rec, context): if 'axis' in rec: axis = rec['axis'] else: # backward-compatibility axis = int(rec['label'].split()[2]) return PixelComponentID(axis, rec['label']) @saver(Component) def _save_component(component, context): if not context.include_data and hasattr(component, '_load_log'): log = component._load_log return dict(log=context.id(log), log_item=log.id(component)) return dict(data=context.do(component.data), units=component.units) @loader(Component) def _load_component(rec, context): if 'log' in rec: return context.object(rec['log']).component(rec['log_item']) cls = lookup_class_with_patches(rec['_type']) return cls(data=context.object(rec['data']), units=rec['units']) @saver(CategoricalComponent) def _save_categorical_component(component, context): if not context.include_data and hasattr(component, '_load_log'): log = component._load_log return dict(log=context.id(log), log_item=log.id(component)) return dict(categorical_data=context.do(component.labels), categories=context.do(component.categories), jitter_method=context.do(component.jitter_method), units=component.units) @loader(CategoricalComponent) def _load_categorical_component(rec, context): if 'log' in rec: return context.object(rec['log']).component(rec['log_item']) return CategoricalComponent(categorical_data=context.object(rec['categorical_data']), categories=context.object(rec['categories']), jitter=context.object(rec['jitter_method']), units=rec['units']) @saver(DerivedComponent) def _save_derived_component(component, context): return dict(link=context.id(component.link)) @loader(DerivedComponent) def _load_derived_component(rec, context): return DerivedComponent(None, link=context.object(rec['link'])) @saver(ComponentLink) def _save_component_link(link, context): frm = list(map(context.id, link.get_from_ids())) to = list(map(context.id, [link.get_to_id()])) using = context.do(link.get_using()) inverse = context.do(link.get_inverse()) return dict(frm=frm, to=to, using=using, inverse=inverse) @loader(ComponentLink) def _load_component_link(rec, context): frm = list(map(context.object, rec['frm'])) to = list(map(context.object, rec['to']))[0] using = context.object(rec['using']) inverse = context.object(rec['inverse']) result = ComponentLink(frm, to, using, inverse) return result @saver(CoordinateComponentLink) def _save_coordinate_component_link(link, context): frm = list(map(context.id, link._from_all)) to = list(map(context.id, [link.get_to_id()])) coords = context.id(link.coords) index = link.index pix2world = link.pixel2world return dict(frm=frm, to=to, coords=coords, index=index, pix2world=pix2world) @loader(CoordinateComponentLink) def _load_coordinate_component_link(rec, context): to = list(map(context.object, rec['to']))[0] # XXX why is this a list? coords = context.object(rec['coords']) index = rec['index'] pix2world = rec['pix2world'] frm = list(map(context.object, rec['frm'])) return CoordinateComponentLink(frm, to, coords, index, pix2world) @saver(types.BuiltinFunctionType) def _save_builtin_function(function, context): ref = "%s.%s" % (function.__module__, function.__name__) return {'function': ref} @loader(types.BuiltinFunctionType) def _load_builtin_function(rec, context): return lookup_class_with_patches(rec['function']) @saver(types.FunctionType) def _save_function(function, context): ref = "%s.%s" % (function.__module__, function.__name__) if lookup_class_with_patches(ref) is function: l = lookup_class_with_patches(ref) return {'function': ref} return {'pickle': gp.dumps(function).encode('base64')} @loader(types.FunctionType) def _load_function(rec, context): if 'pickle' in rec: return gp.loads(rec['pickle'].decode('base64')) return lookup_class_with_patches(rec['function']) @saver(types.MethodType) def _save_method(method, context): # Note: this only works for methods for which the class can be serialized return {'instance': context.id(method.__self__), 'method': method.__name__} @loader(types.MethodType) def _load_method(rec, context): instance = context.object(rec['instance']) return getattr(instance, rec['method']) @saver(core.Session) def _save_session(session, context): # we will rely on GlueApplication to re-populate return {} @loader(np.ndarray) def _load_numpy(rec, context): s = BytesIO(b64decode(rec['data'])) return np.load(s) @saver(np.ndarray) def _save_numpy(obj, context): f = BytesIO() np.save(f, obj) data = b64encode(f.getvalue()).decode('ascii') return dict(data=data) @saver(Colormap) def _save_cmap(cmap, context): return {'cmap': cmap.name} @loader(Colormap) def _load_cmap(rec, context): return cm.get_cmap(rec['cmap']) @saver(np.datetime64) def _save_datetime64(dt, context): return {'datetime64': str(dt)} @loader(np.datetime64) def _load_datetime64(rec, context): return np.datetime64(rec['datetime64']) def apply_inplace_patches(rec): """ Apply in-place patches to a loaded session file. Ideally this should be empty, except for user patches, but we use this to fix session files that need fixing to be interpretable by the current version of glue. """ # The following is a patch for session files made with glue 0.15.* or # earlier that were read in with a developer version of glue for part of # the 0.16 development cycle, and re-saved. Essentially, if coords is set # to the default identity Coordinates class, we need to make sure we # always preserve the world coordinate components, and we do that by # setting force_coords to True. for key, value in rec.items(): if value['_type'] == 'glue.core.data.Data': if 'coords' in value and value['coords'] is not None: coords = rec[value['coords']] if coords['_type'] == 'glue.core.coordinates.Coordinates': for cid, comp in value['components']: if 'log' in rec[comp]: load_log = rec[rec[comp]['log']] if 'force_coords' not in load_log: load_log['force_coords'] = True glueviz-1.0.1+dfsg.orig/glue/core/command.py0000644000175000017500000002237713657331513020302 0ustar noahfxnoahfximport weakref import logging from abc import ABCMeta, abstractmethod from glue.utils import CallbackMixin from glue.core.data_factories import load_data from glue.core.edit_subset_mode import ReplaceMode MAX_UNDO = 50 """ The classes in this module allow user actions to be stored as commands, which can be undone/redone All UI frontends should map interactions to command objects, instead of directly performing an action. Commands have access to two sources of data: the first are the keyword arguments passed to the constructor. These are stored as attributes of self. The second is a session object passed to all Command.do and Command.undo calls. """ class Command(object): """ A class to encapsulate (and possibly undo) state changes Subclasses of this abstract base class must implement the `do` and `undo` methods. Both `do` and `undo` receive a single input argument named `session` -- this is whatever object is passed to the constructor of :class:`glue.core.command.CommandStack`. This object is used to store and retrieve resources needed by each command. The Glue application itself uses a :class:`~glue.core.session.Session` instance for this. Each class should also override the class-level kwargs list, to list the required keyword arguments that should be passed to the command constructor. The base class will check that these keywords are indeed provided. Commands should not take non-keyword arguments in the constructor method """ __metaclass__ = ABCMeta kwargs = [] def __init__(self, **kwargs): kwargs = kwargs.copy() for k in self.kwargs: if k not in kwargs: raise RuntimeError("Required keyword %s not passed to %s" % (k, type(self))) setattr(self, k, kwargs.pop(k)) self.extra = kwargs @abstractmethod def do(self, session): """ Execute the command :param session: An object used to store and fetch resources needed by a Command. """ pass @abstractmethod def undo(self, session): pass @property def label(self): return type(self).__name__ class CommandStack(CallbackMixin): """ The command stack collects commands, and saves them to enable undoing/redoing After instantiation, something can be assigned to the session property. This is passed as the sole argument of all Command (un)do methods. """ def __init__(self): super(CommandStack, self).__init__() self._session = None self._command_stack = [] self._undo_stack = [] @property def session(self): return self._session @session.setter def session(self, value): self._session = value @property def undo_label(self): """ Brief label for the command reversed by an undo """ if len(self._command_stack) == 0: return '' cmd = self._command_stack[-1] return cmd.label @property def redo_label(self): """ Brief label for the command executed on a redo""" if len(self._undo_stack) == 0: return '' cmd = self._undo_stack[-1] return cmd.label def do(self, cmd): """ Execute and log a new command :rtype: The return value of cmd.do() """ logging.getLogger(__name__).debug("Do %s", cmd) self._command_stack.append(cmd) result = cmd.do(self._session) self._command_stack = self._command_stack[-MAX_UNDO:] self._undo_stack = [] self.notify('do') return result def undo(self): """ Undo the previous command :raises: IndexError, if there are no objects to undo """ try: c = self._command_stack.pop() logging.getLogger(__name__).debug("Undo %s", c) except IndexError: raise IndexError("No commands to undo") self._undo_stack.append(c) c.undo(self._session) self.notify('undo') def redo(self): """ Redo the previously-undone command :raises: IndexError, if there are no undone actions """ try: c = self._undo_stack.pop() logging.getLogger(__name__).debug("Undo %s", c) except IndexError: raise IndexError("No commands to redo") result = c.do(self._session) self._command_stack.append(c) self.notify('redo') return result def can_undo_redo(self): """ Return whether undo and redo options are possible :rtype: (bool, bool) - Whether undo and redo are possible, respectively """ return len(self._command_stack) > 0, len(self._undo_stack) > 0 class LoadData(Command): kwargs = ['path', 'factory'] label = 'load data' def do(self, session): return load_data(self.path, self.factory) def undo(self, session): pass class AddData(Command): kwargs = ['data'] label = 'add data' def do(self, session): session.data_collection.append(self.data) def undo(self, session): session.data_collection.remove(self.data) class RemoveData(Command): kwargs = ['data'] label = 'remove data' def do(self, session): session.data_collection.remove(self.data) def undo(self, session): session.data_collection.append(self.data) class NewDataViewer(Command): """Add a new data viewer to the application :param viewer: The class of viewer to create :param data: The data object to initialize the viewer with, or None :type date: :class:`~glue.core.data.Data` or None """ kwargs = ['viewer', 'data'] label = 'new data viewer' def do(self, session): viewer = session.application.new_data_viewer(self.viewer, self.data) if viewer is not None: self.created = weakref.ref(viewer) return viewer def undo(self, session): created = self.created() if created is not None: created.close(warn=False) class AddLayer(Command): """Add a new layer to a viewer :param layer: The layer to add :type layer: :class:`~glue.core.data.Data` or :class:`~glue.core.subset.Subset` :param viewer: The viewer to add the layer to """ kwargs = ['layer', 'viewer'] label = 'add layer' def do(self, session): self.viewer.add_layer(self.layer) def undo(self, session): self.viewer.remove_layer(self.layer) class ApplyROI(Command): """ Apply an ROI to a data collection, updating subset states Parameters ---------- data_collection: :class:`~glue.core.data_collection.DataCollection` DataCollection to operate on roi: :class:`~glue.core.roi.Roi` ROI to apply apply_func: callable The function to call which takes the ROI and actually applies it. """ kwargs = ['data_collection', 'roi', 'apply_func'] label = 'apply ROI' def do(self, session): self.old_states = {} for data in self.data_collection: for subset in data.subsets: self.old_states[subset] = subset.subset_state self.apply_func(self.roi) def undo(self, session): for data in self.data_collection: for subset in data.subsets: if subset not in self.old_states: subset.delete() for k, v in self.old_states.items(): k.subset_state = v class ApplySubsetState(Command): """ Apply an ROI to a data collection, updating subset states Parameters ---------- data_collection: :class:`~glue.core.data_collection.DataCollection` DataCollection to operate on subset_state: :class:`~glue.core.subset_state.SubsetState` Subset state to apply override_mode: bool Flag indicating whether to update current subset or create a new one """ kwargs = ['data_collection', 'subset_state'] label = 'apply subset' def do(self, session): self.old_states = {} for data in self.data_collection: for subset in data.subsets: self.old_states[subset] = subset.subset_state mode = session.edit_subset_mode override_mode = self.extra.get('override_mode') # when creating a new subset the creation mode is replace mode # fix bug #1931 if override_mode is None: if len(mode._edit_subset) == 0: override_mode = ReplaceMode mode.update(self.data_collection, self.subset_state, override_mode=override_mode) def undo(self, session): for data in self.data_collection: for subset in data.subsets: if subset not in self.old_states: subset.delete() for k, v in self.old_states.items(): k.subset_state = v class LinkData(Command): pass class SetViewState(Command): pass class NewTab(Command): pass class CloseTab(Command): pass class NewSubset(Command): pass class CopySubset(Command): pass class PasteSubset(Command): pass class SpecialPasteSubset(Command): pass class DeleteSubset(Command): pass class SetStyle(Command): pass class SetLabel(Command): pass glueviz-1.0.1+dfsg.orig/glue/core/util.py0000644000175000017500000002676713752534424017652 0ustar noahfxnoahfximport logging from itertools import count from functools import partial import numpy as np from matplotlib.ticker import AutoLocator, MaxNLocator, LogLocator from matplotlib.ticker import LogFormatterMathtext, ScalarFormatter, FuncFormatter from matplotlib.dates import AutoDateLocator, AutoDateFormatter from matplotlib.projections.polar import ThetaFormatter, ThetaLocator from glue.utils import nanmin, nanmax __all__ = ["relim", "split_component_view", "join_component_view", "facet_subsets", "colorize_subsets", "disambiguate", 'small_view', 'small_view_array', 'visible_limits', 'tick_linker', 'update_ticks'] def relim(lo, hi, log=False): logging.getLogger(__name__).debug("Inputs to relim: %r %r", lo, hi) x, y = lo, hi if log: if lo < 0: x = 1e-5 if hi < 0: y = 1e5 return x * .95, y * 1.05 delta = y - x return (x - .02 * delta, y + .02 * delta) def split_component_view(arg): """ Split the input to data or subset.__getitem__ into its pieces. Parameters ---------- arg The input passed to ``data`` or ``subset.__getitem__``. Assumed to be either a scalar or tuple Returns ------- selection The Component selection (a ComponentID or string) view Tuple of slices, slice scalar, or view """ if isinstance(arg, tuple): if len(arg) == 1: raise TypeError("Expected a scalar or >length-1 tuple, " "got length-1 tuple") if len(arg) == 2: return arg[0], arg[1] return arg[0], arg[1:] else: return arg, None def join_component_view(component, view): """ Pack a ComponentID and optional view into single tuple. Returns an object compatible with ``data.__getitem__`` and related methods. Handles edge cases of when view is None, a scalar, a tuple, etc. Parameters ---------- component : `~glue.core.component_id.ComponentID` The ComponentID to pack view The view into the data, or `None` """ if view is None: return component result = [component] try: result.extend(view) except TypeError: # view is a scalar result = [component, view] return tuple(result) def facet_subsets(data_collection, cid, lo=None, hi=None, steps=5, prefix='', log=False): """ Create a series of subsets that partition the values of a particular attribute into several bins This creates `steps` new subset groups, adds them to the data collection, and returns the list of newly created subset groups. Parameters ---------- data : :class:`~glue.core.data_collection.DataCollection` The DataCollection object to use cid : :class:`~glue.core.component_id.ComponentID` The ComponentID to facet on lo : float, optional The lower bound for the faceting. Defaults to minimum value in data hi : float, optional The upper bound for the faceting. Defaults to maximum value in data steps : int, optional The number of subsets to create. Defaults to 5 prefix : str, optional If present, the new subset labels will begin with `prefix` log : bool, optional If `True`, space divisions logarithmically. Default is `False` Returns ------- subset_groups : iterable List of :class:`~glue.core.subset_group.SubsetGroup` instances added to `data` Examples -------- :: facet_subset(data, data.id['mass'], lo=0, hi=10, steps=2) creates 2 new subsets. The first represents the constraint 0 <= mass < 5. The second represents 5 <= mass <= 10:: facet_subset(data, data.id['mass'], lo=10, hi=0, steps=2) Creates 2 new subsets. The first represents the constraint 10 >= x > 5 The second represents 5 >= mass >= 0:: facet_subset(data, data.id['mass'], lo=0, hi=10, steps=2, prefix='m') Labels the subsets ``m_1`` and ``m_2``. Note that the last range is inclusive on both sides. For example, if ``lo`` is 0 and ``hi`` is 5, and ``steps`` is 5, then the intervals for the subsets are [0,1), [1,2), [2,3), [3,4), and [4,5]. """ from glue.core.exceptions import IncompatibleAttribute if lo is None or hi is None: for data in data_collection: try: vals = data[cid] break except IncompatibleAttribute: continue else: raise ValueError("Cannot infer data limits for ComponentID %s" % cid) if lo is None: lo = nanmin(vals) if hi is None: hi = nanmax(vals) reverse = lo > hi if log: rng = np.logspace(np.log10(lo), np.log10(hi), steps + 1) else: rng = np.linspace(lo, hi, steps + 1) states = [] labels = [] for i in range(steps): # The if i < steps - 1 clauses are needed because the last interval # has to be inclusive on both sides. if reverse: if i < steps - 1: states.append((cid <= rng[i]) & (cid > rng[i + 1])) labels.append(prefix + '{0}<{1}<={2}'.format(rng[i + 1], cid, rng[i])) else: states.append((cid <= rng[i]) & (cid >= rng[i + 1])) labels.append(prefix + '{0}<={1}<={2}'.format(rng[i + 1], cid, rng[i])) else: if i < steps - 1: states.append((cid >= rng[i]) & (cid < rng[i + 1])) labels.append(prefix + '{0}<={1}<{2}'.format(rng[i], cid, rng[i + 1])) else: states.append((cid >= rng[i]) & (cid <= rng[i + 1])) labels.append(prefix + '{0}<={1}<={2}'.format(rng[i], cid, rng[i + 1])) result = [] for lbl, s in zip(labels, states): sg = data_collection.new_subset_group(label=lbl, subset_state=s) result.append(sg) return result def colorize_subsets(subsets, cmap, lo=0, hi=1): """ Re-color a list of subsets according to a colormap. The colormap will be sampled at `len(subsets)` even intervals between `lo` and `hi`. The color at the `ith` interval will be applied to `subsets[i]` Parameters ---------- subsets : list List of subsets cmap : `~matplotlib.colors.Colormap` Matplotlib colormap instance lo : float, optional Start location in colormap. 0-1. Defaults to 0 hi : float, optional End location in colormap. 0-1. Defaults to 1 """ from matplotlib import cm sm = cm.ScalarMappable(cmap=cmap) sm.norm.vmin = 0 sm.norm.vmax = 1 vals = np.linspace(lo, hi, len(subsets)) rgbas = sm.to_rgba(vals) for color, subset in zip(rgbas, subsets): r, g, b, a = color r = int(255 * r) g = int(255 * g) b = int(255 * b) subset.style.color = '#%2.2x%2.2x%2.2x' % (r, g, b) def disambiguate(label, taken): """ If necessary, add a suffix to label to avoid name conflicts Returns label if it is not in the taken set. Otherwise, returns label_NN where NN is the lowest integer such that label_NN not in taken. Parameters ---------- label : str Desired label taken : iterable The set of already taken names """ if label not in taken: return label suffix = "_%2.2i" label = str(label) for i in count(1): candidate = label + (suffix % i) if candidate not in taken: return candidate def small_view(data, attribute): """ Extract a downsampled view from a dataset, for quick statistical summaries """ shp = data.shape view = tuple([slice(None, None, np.intp(max(s / 50, 1))) for s in shp]) return data[attribute, view] def small_view_array(data): """ Same as small_view, except using a numpy array as input """ shp = data.shape view = tuple([slice(None, None, np.intp(max(s / 50, 1))) for s in shp]) return np.asarray(data)[view] def visible_limits(artists, axis): """ Determines the data limits for the data in a set of artists. Ignores non-visible artists Assumes each artist as a get_data method which returns a tuple of x,y Returns a tuple of min, max for the requested axis, or None if no data present Parameters ---------- artists : iterable An iterable collection of artists axis : int Which axis to compute. 0=xaxis, 1=yaxis """ data = [] for art in artists: if not art.visible: continue xy = art.get_data() assert isinstance(xy, tuple) val = xy[axis] if val.size > 0: data.append(xy[axis]) if len(data) == 0: return data = np.hstack(data) if data.size == 0: return data = data[np.isfinite(data)] if data.size == 0: return lo, hi = nanmin(data), nanmax(data) if not np.isfinite(lo): return return lo, hi def tick_linker(all_categories, pos, *args): # We need to take care to ignore negative indices since these would actually # 'work' 'when accessing all_categories, but we need to avoid that. if pos < 0 or pos >= len(all_categories): return '' else: try: pos = np.round(pos) label = all_categories[int(pos)] if isinstance(label, bytes): return label.decode('ascii') else: return label except IndexError: return '' def update_ticks(axes, coord, kinds, is_log, categories, projection='rectilinear'): """ Changes the axes to have the proper tick formatting based on the type of component. Returns `None` or the number of categories if components is Categorical. Parameters ---------- axes : `~matplotlib.axes.Axes` A matplotlib axis object to alter coord : { 'x' | 'y' } The coordinate axis on which to update the ticks components : iterable A list of components that are plotted along this axis if_log : boolean Whether the axis has a log-scale projection: str The name of the matplotlib projection for the axes object. Defaults to 'rectilinear'. Currently only the scatter viewer supports different projections. """ # Short circuit the full-sphere projections if projection in ['aitoff', 'hammer', 'mollweide', 'lambert']: return if coord == 'x': axis = axes.xaxis elif coord == 'y': axis = axes.yaxis else: raise TypeError("coord must be one of x,y") is_cat = 'categorical' in kinds is_date = 'datetime' in kinds if is_date: loc = AutoDateLocator() fmt = AutoDateFormatter(loc) axis.set_major_locator(loc) axis.set_major_formatter(fmt) elif is_log: axis.set_major_locator(LogLocator()) axis.set_major_formatter(LogFormatterMathtext()) elif is_cat: locator = MaxNLocator(10, integer=True) locator.view_limits(0, categories.shape[0]) format_func = partial(tick_linker, categories) formatter = FuncFormatter(format_func) axis.set_major_locator(locator) axis.set_major_formatter(formatter) # Have to treat the theta axis of polar plots differently elif projection == 'polar' and coord == 'x': axis.set_major_locator(ThetaLocator(AutoLocator())) axis.set_major_formatter(ThetaFormatter()) else: axis.set_major_locator(AutoLocator()) axis.set_major_formatter(ScalarFormatter()) glueviz-1.0.1+dfsg.orig/glue/core/message.py0000644000175000017500000001713313605357235020304 0ustar noahfxnoahfx""" .. module::glue.message """ __all__ = ['Message', 'ErrorMessage', 'SubsetMessage', 'SubsetCreateMessage', 'SubsetUpdateMessage', 'SubsetDeleteMessage', 'DataMessage', 'DataAddComponentMessage', 'DataUpdateMessage', 'DataCollectionMessage', 'DataCollectionActiveChange', 'DataCollectionActiveDataChange', 'DataCollectionAddMessage', 'DataCollectionDeleteMessage', 'ApplicationClosedMessage', 'DataRemoveComponentMessage', 'LayerArtistEnabledMessage', 'LayerArtistDisabledMessage', 'DataRenameComponentMessage', 'DataReorderComponentMessage', 'LayerArtistVisibilityMessage'] class Message(object): """ Base class for messages that the hub handles. Each message represents a specific kind of event. After clients register to a hub, the subscribe to specific message classes, and will only receive those kinds of messages. The message class family is hierarchical, and a client subscribing to a message class implicitly subscribes to all of its subclasses. :attr sender: The object which sent the message :attr tag: An optional string describing the message """ def __init__(self, sender, tag=None): """Create a new message :param sender: The object sending the message :param tag: An optional string describing the message """ self.sender = sender self.tag = tag def __str__(self): return '%s: %s\n\t Sent from: %s' % (type(self).__name__, self.tag or '', self.sender) class ErrorMessage(Message): """ Used to send general purpose error messages """ pass class EditSubsetMessage(Message): """ Indicates that the subset currently being edited has changed """ def __init__(self, sender, subset, mode, tag=None): Message.__init__(self, sender, tag=tag) self.subset = subset self.mode = mode class SubsetMessage(Message): """ A general message issued by a subset. """ def __init__(self, sender, tag=None): from glue.core.subset import Subset if (not isinstance(sender, Subset)): raise TypeError("Sender must be a subset: %s" % type(sender)) Message.__init__(self, sender, tag=tag) self.subset = self.sender class SubsetCreateMessage(SubsetMessage): """ A message that a subset issues when its state changes """ pass class SubsetUpdateMessage(SubsetMessage): """ A message that a subset issues when its state changes. """ def __init__(self, sender, attribute=None, tag=None): """ :param attribute: An optional label of what attribute has changed """ SubsetMessage.__init__(self, sender, tag=tag) self.attribute = attribute def __str__(self): result = super(SubsetUpdateMessage, self).__str__() result += "\n\t Updated %s" % self.attribute return result class SubsetDeleteMessage(SubsetMessage): """ A message that a subset issues when it is deleted """ pass class DataMessage(Message): """ The base class for messages that data objects issue """ def __init__(self, sender, tag=None): from glue.core.data import BaseData if (not isinstance(sender, BaseData)): raise TypeError("Sender must be a data instance: %s" % type(sender)) Message.__init__(self, sender, tag=tag) self.data = self.sender class DataRenameComponentMessage(DataMessage): def __init__(self, sender, component_id, tag=None): super(DataRenameComponentMessage, self).__init__(sender, tag=tag) self.component_id = component_id class DataAddComponentMessage(DataMessage): def __init__(self, sender, component_id, tag=None): super(DataAddComponentMessage, self).__init__(sender, tag=tag) self.component_id = component_id class DataRemoveComponentMessage(DataMessage): def __init__(self, sender, component_id, tag=None): super(DataRemoveComponentMessage, self).__init__(sender, tag=tag) self.component_id = component_id class DataReorderComponentMessage(DataMessage): def __init__(self, sender, component_ids, tag=None): super(DataReorderComponentMessage, self).__init__(sender, tag=tag) self.component_ids = component_ids class ExternallyDerivableComponentsChangedMessage(DataMessage): pass class PixelAlignedDataChangedMessage(DataMessage): pass class ComponentsChangedMessage(DataMessage): pass class ComponentReplacedMessage(ComponentsChangedMessage): def __init__(self, sender, old_component, new_component, tag=None): super(ComponentReplacedMessage, self).__init__(sender, old_component) self.old = old_component self.new = new_component class DataUpdateMessage(DataMessage): def __init__(self, sender, attribute, tag=None): super(DataUpdateMessage, self).__init__(sender, tag=tag) self.attribute = attribute class NumericalDataChangedMessage(DataMessage): pass class DataCollectionMessage(Message): def __init__(self, sender, tag=None): from glue.core.data_collection import DataCollection if (not isinstance(sender, DataCollection)): raise TypeError("Sender must be a DataCollection instance: %s" % type(sender)) Message.__init__(self, sender, tag=tag) class DataCollectionActiveChange(DataCollectionMessage): pass class DataCollectionActiveDataChange(DataCollectionMessage): pass class DataCollectionAddMessage(DataCollectionMessage): def __init__(self, sender, data, tag=None): DataCollectionMessage.__init__(self, sender, tag=tag) self.data = data class DataCollectionDeleteMessage(DataCollectionMessage): def __init__(self, sender, data, tag=None): DataCollectionMessage.__init__(self, sender, tag=tag) self.data = data class SettingsChangeMessage(Message): """ Indicates that some of the application-wide settings have changed Parameters ---------- settings : iterable An iterable of the settings that have changed. """ def __init__(self, sender, settings, tag=None): super(SettingsChangeMessage, self).__init__(sender=sender, tag=tag) self.settings = settings class ApplicationClosedMessage(Message): """A general message issued when Glue application is closed.""" pass class LayerArtistEnabledMessage(Message): def __init__(self, sender, tag=None): super(LayerArtistEnabledMessage, self).__init__(sender, tag=tag) self.layer_artist = self.sender class LayerArtistUpdatedMessage(Message): def __init__(self, sender, tag=None): super(LayerArtistUpdatedMessage, self).__init__(sender, tag=tag) self.layer_artist = self.sender class LayerArtistVisibilityMessage(Message): def __init__(self, sender, tag=None): super(LayerArtistVisibilityMessage, self).__init__(sender, tag=tag) self.layer_artist = self.sender class LayerArtistDisabledMessage(Message): def __init__(self, sender, tag=None): super(LayerArtistDisabledMessage, self).__init__(sender, tag=tag) self.layer_artist = self.sender class ComputationMessage(Message): def __init__(self, sender, tag=None): super(ComputationMessage, self).__init__(sender, tag=tag) self.layer_artist = self.sender class ComputationStartedMessage(ComputationMessage): pass class ComputationEndedMessage(ComputationMessage): pass glueviz-1.0.1+dfsg.orig/glue/core/roi_pretransforms.py0000644000175000017500000000252513752534424022435 0ustar noahfxnoahfxfrom glue.viewers.matplotlib.mpl_axes import init_mpl from matplotlib.figure import Figure import numpy as np class ProjectionMplTransform(object): def __init__(self, projection, x_lim, y_lim, x_scale, y_scale): self._state = {'projection': projection, 'x_lim': x_lim, 'y_lim': y_lim, 'x_scale': x_scale, 'y_scale': y_scale} _, axes = init_mpl(Figure(), projection=self._state['projection']) axes.set_xscale(self._state['x_scale']) axes.set_yscale(self._state['y_scale']) if self._state['projection'] not in ['aitoff', 'hammer', 'lambert', 'mollweide']: axes.set_xlim(self._state['x_lim']) axes.set_ylim(self._state['y_lim']) self._transform = (axes.transData + axes.transAxes.inverted()).frozen() def __call__(self, x, y): points = np.hstack((x.reshape(-1, 1), y.reshape(-1, 1))) res = self._transform.transform(points) out = np.hsplit(res, 2) return out[0].reshape(x.shape), out[1].reshape(y.shape) def __gluestate__(self, context): return dict(state=context.do(self._state)) @classmethod def __setgluestate__(cls, rec, context): state = context.object(rec['state']) return cls(state['projection'], state['x_lim'], state['y_lim'], state['x_scale'], state['y_scale']) glueviz-1.0.1+dfsg.orig/glue/core/__init__.py0000644000175000017500000000127713605357235020421 0ustar noahfxnoahfxfrom .command import Command, CommandStack # noqa from .component import Component # noqa from .component_id import ComponentID # noqa from .component_link import ComponentLink # noqa from .coordinates import Coordinates # noqa # noqa from .data import BaseData, BaseCartesianData, Data # noqa from .data_collection import DataCollection # noqa from .hub import Hub, HubListener # noqa from .link_manager import LinkManager # noqa from .session import Session # noqa from .subset import Subset # noqa from .subset_group import SubsetGroup # noqa from .visual import VisualAttributes # noqa # We import this last to avoid circular imports from .application_base import Application # noqa glueviz-1.0.1+dfsg.orig/glue/core/simpleforms.py0000644000175000017500000000700613605357235021216 0ustar noahfxnoahfx""" The descriptors in this module are meant to be added to classes, to specify simple user-settable forms. These classes are used to automatically construct GUIs, without having to write GUI code in the form class itself. :class:`Option` objects are defined at the class-level. To instances of these classes, an :class:`Option` behaves like a normal instance attribute. See :ref:`custom-fitting` for example usage. """ class Option(object): """ Base class for other options. This should not be used directly Parameters ---------- default : object The default value for this option. label : str A short label for this option, to use in the GUI """ def __init__(self, default, label): self.label = label """A UI label for the setting""" self.default = default """The default value""" self._name = "__%s_%i" % (type(self), id(self)) def __get__(self, instance, owner=None): if instance is None: return self return getattr(instance, self._name, self.default) def __set__(self, instance, value): value = self._validate(value) setattr(instance, self._name, value) def _validate(self, value): return value class IntOption(Option): """ An integer-valued option. Parameters ---------- min : int, optional The minimum valid value max : int, optional The maximum valid value default : int, optional The default value label : str, optional A short label for this option """ def __init__(self, min=0, max=10, default=1, label="Integer"): super(IntOption, self).__init__(default, label) self.min = min self.max = max def _validate(self, value): try: if value != int(value): raise ValueError() value = int(value) except ValueError: raise ValueError("%s must be an integer" % self.label) if value < self.min: raise ValueError("%s must be >= %i" % (self.label, self.min)) if value > self.max: raise ValueError("%s must be <= %i" % (self.label, self.max)) return value class FloatOption(Option): """ A floating-point option. Parameters ---------- min : float, optional The minimum valid value max : float, optional The maximum valid value default : float, optional The default value label : str, optional A short label for this option """ def __init__(self, min=0, max=10, default=1, label="Float"): super(FloatOption, self).__init__(default, label) self.min = min self.max = max def _validate(self, value): value = float(value) if value < self.min or value > self.max: raise ValueError("%s must be between %e and %e" % (self.label, self.min, self.max)) return value class BoolOption(Option): """ A boolean-valued option. Parameters ---------- label : str, optional A short label for this option default : bool, optional The default `True`/`False` value """ def __init__(self, label="Bool", default=False): super(BoolOption, self).__init__(default, label) def _validate(self, value): if value not in [True, False]: raise ValueError( "%s must be True or False: %s" % (self.label, value)) return value glueviz-1.0.1+dfsg.orig/glue/core/hub_callback_container.py0000644000175000017500000001012313605357235023304 0ustar noahfxnoahfximport weakref from functools import partial __all__ = ['HubCallbackContainer'] class HubCallbackContainer(object): """ A list-like container for callback functions. We need to be careful with storing references to methods, because if a callback method is on a class which contains both the callback and a callback property, a circular reference is created which results in a memory leak. Instead, we need to use a weak reference which results in the callback being removed if the instance is destroyed. This container class takes care of this automatically. Adapted from echo.CallbackContainer. """ def __init__(self): self.callbacks = {} def _wrap(self, handler, filter): """ Given a function/method, this will automatically wrap a method using weakref to avoid circular references. """ if not callable(handler): raise TypeError("Only callable handlers can be stored in CallbackContainer") if filter is not None and not callable(filter): raise TypeError("Only callable filters can be stored in CallbackContainer") if self.is_bound_method(handler): # We are dealing with a bound method. Method references aren't # persistent, so instead we store a reference to the function # and instance. value = (weakref.ref(handler.__func__), weakref.ref(handler.__self__, self._auto_remove)) else: value = (handler, None) if self.is_bound_method(filter): # We are dealing with a bound method. Method references aren't # persistent, so instead we store a reference to the function # and instance. value += (weakref.ref(filter.__func__), weakref.ref(filter.__self__, self._auto_remove)) else: value += (filter, None) return value def _auto_remove(self, method_instance): # Called when weakref detects that the instance on which a method was # defined has been garbage collected. remove = [] for key, value in self.callbacks.items(): if value[1] is method_instance or value[3] is method_instance: remove.append(key) for key in remove: self.callbacks.pop(key) def __contains__(self, message_class): return message_class in self.callbacks def __getitem__(self, message_class): callback = self.callbacks[message_class] if callback[1] is None: result = (callback[0],) else: func = callback[0]() inst = callback[1]() result = (partial(func, inst),) if callback[3] is None: result += (callback[2],) else: func = callback[2]() inst = callback[3]() result += (partial(func, inst),) return result def __iter__(self): for message_class in self.callbacks: yield self[message_class] def __len__(self): return len(self.callbacks) def keys(self): return self.callbacks.keys() @staticmethod def is_bound_method(func): return hasattr(func, '__func__') and getattr(func, '__self__', None) is not None def __setitem__(self, message_class, value): handler, filter = value self.callbacks[message_class] = self._wrap(handler, filter) def pop(self, message_class): return self.callbacks.pop(message_class) def remove_handler(self, handler): if self.is_bound_method(handler): for message_class in sorted(self.callbacks): callback = self.callbacks[message_class] if callback[1] is not None and handler.__func__ is callback[0]() and handler.__self__ is callback[1](): self.callbacks.pop(callback) else: for message_class in sorted(self.callbacks): callback = self.callbacks[message_class] if callback[1] is None and handler is callback[0]: self.callbacks.pop(callback) glueviz-1.0.1+dfsg.orig/glue/core/parse.py0000644000175000017500000002271213605357235017771 0ustar noahfxnoahfximport re import random from glue.core.component_link import ComponentLink from glue.core.subset import Subset, SubsetState from glue.core.data import ComponentID # The following expression matches substrings surrounded by curly brackets # with a component name inside. The component name can be composed of any # character that is not curly brackets (i.e. [^\{\}]) and has to start and # end with a character that is not a curly bracket or a space. The component # name can be surrounded by spaces, e.g. '{ a }' TAG_RE = re.compile('\{\s*(?P[^\s\{\}]+([^\{\}]*[^\s\{\}]+)?)\s*\}') __all__ = ['ParsedCommand', 'ParsedSubsetState'] def _ensure_only_component_references(cmd, references): """ Search through tag references in a command, ensure that they all reference ComponentIDs Parameters ---------- cmd : string. A template command referenes : a mapping from tags to substitution objects Raises ------ TypeError, if cmd does not refer only to ComponentIDs """ for match in TAG_RE.finditer(cmd): tag = match.group('tag') if tag not in references or not \ isinstance(references[tag], ComponentID): raise TypeError( "Reference to %s, which is not a ComponentID" % tag) def _reference_list(cmd, references): """ Return a list of the values in the references mapping whose keys appear in the command Parameters ---------- cmd : string. A template command references : a mapping from tags to substitution objects Returns ------- A list of the unique values in references that appear in the command Examples -------- >>> cmd = '{g} - {r} + {g}' >>> references = {'g' : g_object, 'r' : r_object, 'i' : i_object} >>> _reference_list(cmd, references) [g_object, r_object] Raises ------ KeyError: if tags in the command aren't in the reference mapping """ try: return list(set(references[m.group('tag')] for m in TAG_RE.finditer(cmd))) except KeyError: raise KeyError("Tags from command not in reference mapping") def _dereference(cmd, references): """ Dereference references in the template command, to refer to objects in the reference mapping Parameters ---------- cmd : Command string references : mapping from template tags to objects Returns ------- A new command, where all the tags have been subsituted as follows: "{tag}" -> 'data[references["tag"], __view]', if references[tag] is a ComponentID "{tag}" -> 'references["tag"].to_mask(__view)' if references[tag] is a Subset __view is a placeholder variable referencing the view passed to data.__getitem__ and subset.to_mask Raises ------ TypeError, if a tag in the command maps to something other than a ComponentID or Subset object """ def sub_func(match): tag = match.group('tag') if isinstance(references[tag], ComponentID): return 'data[references["%s"], __view]' % tag elif isinstance(references[tag], Subset): return 'references["%s"].to_mask(__view)' % tag else: raise TypeError("Tag %s maps to unrecognized type: %s" % (tag, type(references[tag]))) return TAG_RE.sub(sub_func, cmd) def _dereference_random(cmd): """ Dereference references in the template command, to refer to random floating-point values. This is used to quickly test that the command evaluates without errors. Parameters ---------- cmd : str Command string Returns ------- A new command, where all the tags have been subsituted by floating point values """ def sub_func(match): tag = match.group('tag') return str(random.random()) return TAG_RE.sub(sub_func, cmd) class InvalidTagError(ValueError): def __init__(self, tag, references): msg = ("Tag %s not in reference mapping: %s" % (tag, sorted(references.keys()))) self.tag = tag self.references = references super(InvalidTagError, self).__init__(msg) def _validate(cmd, references): """ Make sure all references in the command are in the reference mapping Raises ------ TypeError, if a tag is missing from references """ replacements = {} references_new = {} for match in TAG_RE.finditer(cmd): tag = match.group('tag') if tag not in references: raise InvalidTagError(tag, references) full_tag = match.string[slice(*match.span())] replacements[full_tag] = '{' + references[tag].uuid + '}' references_new[references[tag].uuid] = references[tag] cmd_new = cmd for before, after in replacements.items(): cmd_new = cmd_new.replace(before, after) return cmd_new, references_new class ParsedCommand(object): """ Class to manage commands that define new components and subsets """ def __init__(self, cmd, references): """ Create a new parsed command object Parameters ---------- cmd : str. A template command. Can only reference ComponentID objects references : mapping from command templates to substitution objects """ self._cmd, self._references = _validate(cmd, references) def render(self, mapping=None): def sub_func(match): tag = match.group('tag') if mapping is None: label = self._references[tag].label else: label = mapping[self._references[tag]] return '{' + label + '}' return TAG_RE.sub(sub_func, self._cmd) def ensure_only_component_references(self): _ensure_only_component_references(self._cmd, self._references) @property def reference_list(self): return _reference_list(self._cmd, self._references) def evaluate(self, data, view=None): from glue import env # pylint: disable=W0613, W0612 references = self._references cmd = _dereference(self._cmd, self._references) scope = vars(env) scope['__view'] = view global_variables = vars(env) # We now import math modules if not already defined in local or # global variables if 'numpy' not in global_variables and 'numpy' not in locals(): import numpy # noqa if 'np' not in global_variables and 'np' not in locals(): import numpy as np # noqa if 'math' not in global_variables and 'math' not in locals(): import math # noqa result = eval(cmd, global_variables, locals()) # careful! # At this point, np may have been defined in the globals but not the # locals so we import it manually to avoid any issues. import numpy as np # noqa if data is not None and np.isscalar(result): result = np.ones(data.shape) * result return result def evaluate_test(self, view=None): from glue import env cmd = _dereference_random(self._cmd) scope = vars(env) scope['__view'] = view global_variables = vars(env) # We now import math modules if not already defined in local or # global variables if 'numpy' not in global_variables and 'numpy' not in locals(): import numpy # noqa if 'np' not in global_variables and 'np' not in locals(): import numpy as np # noqa if 'math' not in global_variables and 'math' not in locals(): import math # noqa return eval(cmd, global_variables, locals()) # careful! def __gluestate__(self, context): return dict(cmd=self._cmd, references=dict((k, context.id(v)) for k, v in self._references.items())) @classmethod def __setgluestate__(cls, rec, context): cmd = rec['cmd'] ref = dict((k, context.object(v)) for k, v in rec['references'].items()) return cls(cmd, ref) class ParsedComponentLink(ComponentLink): """ Class to create a new ComponentLink from a ParsedCommand object. """ def __init__(self, to_, parsed): """ Create a new link Parameters ---------- to_ : ComponentID instance to associate with the new component parsed : A ParsedCommand object """ parsed.ensure_only_component_references() super(ParsedComponentLink, self).__init__( parsed.reference_list, to_, lambda: None) self._parsed = parsed def compute(self, data, view=None): return self._parsed.evaluate(data, view) def __gluestate__(self, context): return dict(parsed=context.do(self._parsed), to=context.id(self.get_to_id())) @classmethod def __setgluestate__(cls, rec, context): return cls(context.object(rec['to']), context.object(rec['parsed'])) class ParsedSubsetState(SubsetState): """ A SubsetState defined by a ParsedCommand object """ def __init__(self, parsed): """ Create a new object Parameters ---------- parsed : A ParsedCommand object """ super(ParsedSubsetState, self).__init__() self._parsed = parsed def to_mask(self, data, view=None): """ Calculate the new mask by evaluating the dereferenced command """ result = self._parsed.evaluate(data) if view is not None: result = result[view] return result glueviz-1.0.1+dfsg.orig/glue/core/layer_artist.py0000644000175000017500000002570713657331513021366 0ustar noahfxnoahfx""" LayerArtist classes handle the visualization of an individual subset or dataset. Visualization clients in Glue typically compose visualizations by stacking visualizations of several datasets and subsets on top of each other. They do this by creating and managing a collection of LayerArtists, one for each Data or Subset to view. LayerArtists contain the bulk of the logic for actually rendering things """ import os from contextlib import contextmanager from abc import ABCMeta import numpy as np from echo.callback_container import CallbackContainer from glue.core.subset import Subset from glue.utils import Pointer, PropertySetMixin from glue.core.message import LayerArtistEnabledMessage, LayerArtistDisabledMessage __all__ = ['LayerArtistBase', 'LayerArtistContainer'] DISABLED_LAYER_WARNING = """ This layer depends on attributes that cannot be derived for the underlying dataset. This usually indicates that this dataset has not been linked with other datasets being shown. In this case, for this layer to work, it would need to be linked with the following datasets: {} """.replace(os.linesep, ' ') DISABLED_MASK_MESSAGE = """ The subset mask for this layer cannot be computed. This usually indicates that the selection was defined using attributes that are not defined in this dataset. """.replace(os.linesep, ' ') class ChangedTrigger(object): """Sets an instance's _changed attribute to True on update""" def __init__(self, default=None): self._default = default self._vals = {} def __get__(self, inst, type=None): return self._vals.get(inst, self._default) def __set__(self, inst, value): if isinstance(value, np.ndarray): changed = value is not self.__get__(inst) else: changed = value != self.__get__(inst) self._vals[inst] = value if changed: inst._changed = True class LayerArtistBase(PropertySetMixin, metaclass=ABCMeta): _property_set = ['zorder', 'visible', 'layer'] # the order of this layer in the visualizations. High-zorder # layers are drawn on top of low-zorder layers. # Subclasses should refresh plots when this property changes zorder = Pointer('_zorder') # whether this layer should be rendered. # Subclasses should refresh plots when this property changes visible = Pointer('_visible') # whether this layer is capable of being rendered # Subclasses should refresh plots when this property changes enabled = Pointer('_enabled') def __init__(self, layer): """Create a new LayerArtist Parameters ---------- layer : :class:`~glue.core.data.Data` or :class:`~glue.core.subset.Subset` Data or Subset to draw layer : :class:`~glue.core.data.Data` or `glue.core.subset.Subset` """ self._visible = True self._zorder = 0 self._enabled = True self._layer = layer self.view = None # cache of last view, if relevant self._state = None # cache of subset state, if relevant self._changed = True # hint at whether underlying data has changed since last render self._disabled_reason = '' # A string explaining why this layer is disabled. def get_layer_color(self): # This method can return either a plain color or a colormap. This is # used by the UI layer to determine a 'representative' color or colormap # for the layer to be used e.g. in icons. return self._layer.style.color def enable(self): if self.enabled: return self._disabled_reason = '' self._enabled = True self.redraw() if self._layer is not None and self._layer.hub is not None: message = LayerArtistEnabledMessage(self) self._layer.hub.broadcast(message) def disable(self, reason): """ Disable the layer for a particular reason. Layers should only be disabled when drawing is impossible, e.g. because a subset cannot be applied to a dataset. Parameters ---------- reason : str A short explanation for why the layer can't be drawn. Used by the UI """ self._disabled_reason = reason self._enabled = False self.clear() if self._layer is not None and self._layer.hub is not None: message = LayerArtistDisabledMessage(self) self._layer.hub.broadcast(message) def disable_invalid_attributes(self, *attributes): """ Disable a layer because visualization depends on knowing a set of ComponentIDs that cannot be derived from a dataset or subset Automatically generates a disabled message. Parameters ---------- attributes : sequence of ComponentIDs """ if len(attributes) == 0: self.disable('') return datasets = ', '.join(sorted(set([cid.parent.label for cid in attributes]))) self.disable(DISABLED_LAYER_WARNING.format(datasets)) def disable_incompatible_subset(self): """ Disable a layer because the subset mask cannot be computed. Automatically generates a disabled message. """ self.disable(DISABLED_MASK_MESSAGE) @property def disabled_message(self): """ Returns why a layer is disabled """ if self.enabled: return '' return "Cannot visualize this layer: %s" % self._disabled_reason @property def layer(self): """ The Data or Subset visualized in this layer """ return self._layer @layer.setter def layer(self, value): self._layer = value def redraw(self): """ Re-render the plot """ pass def update(self): """ Sync the visual appearance of the layer, and redraw """ pass def clear(self): """ Clear the visualization for this layer """ pass def remove(self): """ Remove the visualization for this layer. This is called when the layer artist is removed for good from the viewer. It defaults to calling clear, but can be overriden in cases where clear and remove should be different. """ self.clear() def force_update(self, *args, **kwargs): """ Sets the _changed flag to true, and calls update. Force an update of the layer, overriding any caching that might be going on for speed """ self._changed = True return self.update(*args, **kwargs) def _check_subset_state_changed(self): """Checks to see if layer is a subset and, if so, if it has changed subset state. Sets _changed flag to True if so""" if not isinstance(self.layer, Subset): return state = self.layer.subset_state if state is not self._state: self._changed = True self._state = state def __str__(self): return "%s for %s" % (self.__class__.__name__, self.layer.label) def __gluestate__(self, context): # note, this doesn't yet have a restore method. Will rely on client return dict((k, context.id(v)) for k, v in self.properties.items()) __repr__ = __str__ class LayerArtistContainer(object): """A collection of LayerArtists""" def __init__(self): self.artists = [] self.empty_callbacks = CallbackContainer() self.change_callbacks = CallbackContainer() self._ignore_empty_callbacks = False self._ignore_change_callbacks = False def on_empty(self, func): """ Register a callback function that should be invoked when this container is emptied """ self.empty_callbacks.append(func) def on_changed(self, func): """ Register a callback function that should be invoked when this container's elements change """ self.change_callbacks.append(func) def _duplicate(self, artist): for a in self.artists: if type(a) == type(artist) and a.layer is artist.layer: return True return False def append(self, artist): """Add a LayerArtist to this collection""" self.artists.append(artist) artist.zorder = max(a.zorder for a in self.artists) + 1 self._notify() def remove(self, artist): """Remove a LayerArtist from this collection :param artist: The artist to remove :type artist: :class:`LayerArtistBase` """ if artist in self.artists: self.artists.remove(artist) artist.remove() self._notify() def clear(self): """ Remove all layer artists from this collection """ for artist in self.artists: artist.remove() self.artists.clear() def clear_callbacks(self): """ Remove all callbacks """ self.empty_callbacks.clear() self.change_callbacks.clear() def _notify(self): if not self._ignore_change_callbacks: for cb in self.change_callbacks: cb() if not self._ignore_empty_callbacks and len(self) == 0: for cb in self.empty_callbacks: cb() def pop(self, layer): """Remove all artists associated with a layer""" to_remove = [a for a in self.artists if a.layer is layer] for r in to_remove: self.remove(r) return to_remove @property def layers(self): """A list of the unique layers in the container""" return list(set([a.layer for a in self.artists])) @contextmanager def ignore_empty(self): """ A context manager that temporarily disables calling callbacks if container is empty. """ try: self._ignore_empty_callbacks = True yield finally: self._ignore_empty_callbacks = False @contextmanager def ignore_change(self): """ A context manager that temporarily disables calling callbacks if container is changed. """ try: self._ignore_change_callbacks = True yield finally: self._ignore_change_callbacks = False @contextmanager def ignore_callbacks(self): try: self._ignore_change_callbacks = True self._ignore_empty_callbacks = True yield finally: self._ignore_change_callbacks = False self._ignore_empty_callbacks = False def __len__(self): return len(self.artists) def __iter__(self): return iter(sorted(self.artists, key=lambda x: x.zorder)) def __contains__(self, item): return any(item is a.layer for a in self.artists) def __getitem__(self, layer): if isinstance(layer, int): return self.artists[layer] return [a for a in self.artists if a.layer is layer] glueviz-1.0.1+dfsg.orig/glue/core/glue_pickle.py0000644000175000017500000000032613605357235021137 0ustar noahfxnoahfxfrom glue.logger import logger try: from dill import dumps, loads # noqa except ImportError: logger.info("Dill library not installed. Falling back to cPickle") from pickle import dumps, loads # noqa glueviz-1.0.1+dfsg.orig/glue/core/link_helpers.py0000644000175000017500000003723713752534424021346 0ustar noahfxnoahfx""" This module provides several classes and LinkCollection classes to assist in linking data. The :class:`LinkCollection` class and its sub-classes are factories to create multiple ComponentLinks easily. They are meant to be passed to :meth:`~glue.core.data_collection.DataCollection.add_link()` """ import types from glue.config import link_function from glue.core.data import ComponentID from glue.core.component_link import ComponentLink from inspect import getfullargspec __all__ = ['LinkCollection', 'LinkSame', 'LinkTwoWay', 'MultiLink', 'LinkAligned', 'BaseMultiLink', 'ManualLinkCollection'] @link_function("Link conceptually identical components", output_labels=['y']) def identity(x): return x @link_function("Convert between linear measurements and volume", output_labels=['volume']) def lengths_to_volume(width, height, depth): return width * height * depth class PartialResult(object): def __init__(self, func, index, name_prefix=""): self.func = func self.index = index self.__name__ = '%s%s_%i' % (name_prefix, func.__name__, index + 1) def __call__(self, *args, **kwargs): return self.func(*args, **kwargs)[self.index] def __gluestate__(self, context): return dict(func=context.do(self.func), index=self.index) @classmethod def __setgluestate__(cls, rec, context): return cls(context.object(rec['func']), rec['index']) def _toid(arg): """Coerce the input to a ComponentID, if possible""" if isinstance(arg, ComponentID): return arg elif isinstance(arg, str): return ComponentID(arg) else: raise TypeError('Cannot be cast to a ComponentID: %s' % arg) class LinkCollection(object): """ A collection of links between two datasets. Parameters ---------- data1 : `~glue.core.data.Data` The first dataset being linked data2 : `~glue.core.data.Data` The second dataset being linked cids1 : list of `~glue.core.component_id.ComponentID` The set of `~glue.core.component_id.ComponentID` in ``data1`` which can be used to parameterize the links. Note that the links can also use other IDs, but the ones defined here are the ones that can be modified through e.g. the graphical link editor. cids2 : list of `~glue.core.component_id.ComponentID` The set of `~glue.core.component_id.ComponentID` in ``data2``. This is defined as for ``cids1``. """ # The following is a short name to be used for the link, which is used # in e.g. drop-down menus in link editors. display = 'Collection of links' # The following can be a paragraph description explaining how the set # of links works description = '' # The following are lists of human-readable names for the component IDs # to be specified in the initializer. For this base class, these are # empty, but can be overridden in sub-classes. labels1 = [] labels2 = [] def __init__(self, data1=None, data2=None, cids1=None, cids2=None): self.cids1 = cids1 or [] self.cids2 = cids2 or [] if data1 is None: if len(self.cids1) == 0: self.data1 = None else: self.data1 = self.cids1[0].parent else: self.data1 = data1 if data2 is None: if len(self.cids2) == 0: self.data2 = None else: self.data2 = self.cids2[0].parent else: self.data2 = data2 self._links = [] def __iter__(self): for link in self._links: yield link def __len__(self): return len(self._links) def __getitem__(self, item): return self._links[item] def __contains__(self, cid): for link in self: if cid in link: return True return False def __gluestate__(self, context): state = {} state['data1'] = context.id(self.data1) state['data2'] = context.id(self.data2) state['cids1'] = context.id(self.cids1) state['cids2'] = context.id(self.cids2) return state @classmethod def __setgluestate__(cls, rec, context): if 'data1' in rec: self = cls(data1=context.object(rec['data1']), data2=context.object(rec['data2']), cids1=context.object(rec['cids1']), cids2=context.object(rec['cids2'])) else: # glue-core <0.15 cids = context.object(rec['cids']) cids1 = [context.object(c) for c in cids[:len(cls.labels1)]] cids2 = [context.object(c) for c in cids[len(cls.labels1):]] self = cls(cids1=cids1, cids2=cids2) return self class ManualLinkCollection(object): """ A collection of links between two datasets. This class is intended for manual link collections, i.e. collections where the caller manually adds and removes individual links. These links can be between any component IDs as long as they link component IDs between the two specified datasets. Parameters ---------- data1 : `~glue.core.data.Data` The first dataset being linked data2 : `~glue.core.data.Data` The second dataset being linked links : list The initial links to add to the collection. """ display = 'Custom list of links' description = 'This is a list of links that has been manually constructed' def __init__(self, data1=None, data2=None, links=None): super(ManualLinkCollection, self).__init__(data1=data1, data2=data2) self._links[:] = links or [] def append(self, link): self._links.append(link) def extend(self, links): self._links.extend(links) def __gluestate__(self, context): state = super(ManualLinkCollection, self).__gluestate__(context) state['values'] = context.id(self._links) return state @classmethod def __setgluestate__(cls, rec, context): self = super(ManualLinkCollection, cls).__setgluestate__(rec, context) self._values[:] = context.object(rec['values']) return self class BaseMultiLink(LinkCollection): """ A link collection that is generated on-the-fly based on forward and backward transformation functions and lists of input/output component IDs. The input parameters are as for :class:`~glue.core.link_helpers.LinkCollection`. Sub-classes should override the :meth:`~glue.core.link_helpers.BaseMultiLink.forwards` and :meth:`~glue.core.link_helpers.BaseMultiLink.backwards` methods. """ # Some sub-classes take only data and don't need CIDs, so we have a flag # for this in case other parts of glue need to know. cid_independent = False # TODO: could add a metaclass to set labels1 and labels2 automatically def __init__(self, cids1=None, cids2=None, data1=None, data2=None): super(BaseMultiLink, self).__init__(data1=data1, data2=data2, cids1=cids1, cids2=cids2) links = [] if self.forwards is not None: if self.forwards is identity: links.append(ComponentLink(cids1, cids2[0])) elif len(cids2) == 1: links.append(ComponentLink(cids1, cids2[0], self.forwards)) else: for i, r in enumerate(cids2): func = PartialResult(self.forwards, i, name_prefix=self.__class__.__name__ + ".") links.append(ComponentLink(cids1, r, using=func, input_names=self.labels1)) if self.backwards is not None: if self.backwards is identity: links.append(ComponentLink(cids2, cids1[0])) elif len(cids1) == 1: links.append(ComponentLink(cids2, cids1[0], self.backwards)) else: for i, l in enumerate(cids1): func = PartialResult(self.backwards, i, name_prefix=self.__class__.__name__ + ".") links.append(ComponentLink(cids2, l, using=func, input_names=self.labels1)) self._links[:] = links def forwards(self): raise NotImplementedError() def backwards(self): raise NotImplementedError() class MultiLink(BaseMultiLink): """ A link collection that is generated on-the-fly based on forward and backward transformation functions and lists of input/output component IDs. This is similar to :meth:`~glue.core.link_helpers.BaseMultiLink` except that the ``forwards`` and ``backwards`` functions are specified in the initializer rather than being methods of the class. Parameters ---------- forwards : function Function that maps ``cids1`` to ``cids2``. This should have the signature ``cids2 = forwards(*cids1)``, and is assumed to return a tuple. If not specified, no forward links are calculated. backwards : function The inverse function to ``forwards``. If not specified, no forward links are calculated. labels1 : list of str The human-readable names of the inputs to the ``forwards`` function. If not specified, this is set to the argument names of ``forwards``. labels2 : list of str The human-readable names of the inputs to the ``backwards`` function. If not specified, this is set to the argument names of ``backwards``. kwargs : Additional arguments are passed """ def __init__(self, cids1=None, cids2=None, forwards=None, backwards=None, labels1=None, labels2=None, **kwargs): # NOTE: we explicitly specify ``cids1`` and ``cids2`` as the two first # arguments for backwards-compatibility with callers that use positional # arguments. if forwards is None and backwards is None: raise TypeError("Must supply either forwards or backwards") self.forwards = forwards self.backwards = backwards # NOTE: the getattr(forwards, 'func', forwards) in the following code # is to make sure that things work properly if the functions are # PartialResult objects. if labels1 is None: if forwards is not None: if isinstance(forwards, types.MethodType): labels1 = getfullargspec(getattr(forwards, 'func', forwards))[0][1:] else: labels1 = getfullargspec(getattr(forwards, 'func', forwards))[0] else: raise ValueError("labels1 needs to be specified if forwards isn't") if labels2 is None: if backwards is not None: if isinstance(backwards, types.MethodType): labels2 = getfullargspec(getattr(backwards, 'func', backwards))[0][1:] else: labels2 = getfullargspec(getattr(backwards, 'func', backwards))[0] else: raise ValueError("labels2 needs to be specified if backwards isn't") self.labels1 = labels1 self.labels2 = labels2 super(MultiLink, self).__init__(cids1=cids1, cids2=cids2, **kwargs) def __gluestate__(self, context): state = super(MultiLink, self).__gluestate__(context) state['forwards'] = context.id(self._forwards) state['backwards'] = context.id(self._backwards) return state @classmethod def __setgluestate__(cls, rec, context): self = super(MultiLink, cls).__setgluestate__(rec, context) self._forwards = context.object(rec['forwards']) self._backwards = context.object(rec['backwards']) return self class LinkSame(MultiLink): """ A bi-directional identity link between two components. """ display = "identity link" def __init__(self, cid1=None, cid2=None, **kwargs): if cid1 is None: cid1 = kwargs['cids1'][0] else: cid1 = _toid(cid1) kwargs['cids1'] = [cid1] if cid2 is None: cid2 = kwargs['cids2'][0] else: cid2 = _toid(cid2) kwargs['cids2'] = [cid2] kwargs['forwards'] = identity default_kwargs = {'data1': cid1.parent, 'data2': cid2.parent, 'labels1': ['x'], 'labels2': ['y']} for keyword, value in default_kwargs.items(): if keyword not in kwargs: kwargs[keyword] = value self._cid1 = cid1 self._cid2 = cid2 super(LinkSame, self).__init__(**kwargs) def __gluestate__(self, context): state = {} state['cid1'] = context.id(self._cid1) state['cid2'] = context.id(self._cid2) return state @classmethod def __setgluestate__(cls, rec, context): self = cls(context.object(rec['cid1']), context.object(rec['cid2'])) return self class LinkTwoWay(MultiLink): """ Return two links that connect input ComponentIDs in both directions Parameters ---------- cid1 : `glue.core.component_id.ComponentID` The first ComponentID to link cid2 : `glue.core.component_id.ComponentID` The second ComponentID to link forwards : function Function which maps cid1 to cid2 (e.g. ``cid2=f(cid1)``) backwards : function Function which maps cid2 to cid1 (e.g. ``cid1=f(cid2)``) """ def __init__(self, cid1=None, cid2=None, forwards=None, backwards=None, **kwargs): if cid1 is None: cid1 = kwargs['cids1'] else: kwargs['data1'] = cid1.parent kwargs['cids1'] = [cid1] if cid2 is None: cid2 = kwargs['cids2'] else: kwargs['data2'] = cid2.parent kwargs['cids2'] = [cid2] super(LinkTwoWay, self).__init__(forwards=forwards, backwards=backwards, **kwargs) def __gluestate__(self, context): state = {} state['cid1'] = context.id(self._cid1) state['cid2'] = context.id(self._cid2) state['forwards'] = context.id(self.forwards) state['backwards'] = context.id(self.backwards) return state @classmethod def __setgluestate__(cls, rec, context): self = cls(context.object(rec['cid1']), context.object(rec['cid2']), context.object(rec['forwards']), context.object(rec['backwards'])) return self class LinkAligned(LinkCollection): """ Compute all the links to specify that the input data are pixel-aligned. """ def __init__(self, data1=None, data2=None): super(LinkAligned, self).__init__(data1=data1, data2=data2) if data1.shape != data2.shape: raise TypeError("Input data do not have the same shape") links = [] for j in range(data1.ndim): links.extend(LinkSame(data1.pixel_component_ids[j], data2.pixel_component_ids[j])) self._links[:] = links def functional_link_collection(function, labels1=None, labels2=None, display=None, description=None): class FunctionalLinkCollection(LinkCollection): def __init__(self, data1=None, data2=None, cids1=None, cids2=None): super(FunctionalLinkCollection, self).__init__(data1=data1, data2=data2, cids1=cids1, cids2=cids2) self._links[:] = function(*self.cids1, *self.cids2) FunctionalLinkCollection.labels1 = labels1 or [] FunctionalLinkCollection.labels2 = labels2 or [] FunctionalLinkCollection.display = display or '' FunctionalLinkCollection.description = description or '' return FunctionalLinkCollection glueviz-1.0.1+dfsg.orig/glue/core/joins.py0000644000175000017500000000702013502206677017773 0ustar noahfxnoahfximport numpy as np from glue.core.exceptions import IncompatibleAttribute __all__ = ['get_mask_with_key_joins'] def concatenate_arrays(*arrays): """ Given N arrays of size M, return an array of size M where each item is the concatenation of the bytes of the orginal arrays for the respective item. """ # Find the total size of an item in the final array total_size = sum(x.dtype.itemsize for x in arrays) # Set up the final array buffer = np.zeros(len(arrays[0]) * total_size, dtype=np.byte) # Now view this array as a structured array colnames = ['{0:x}'.format(x) for x in range(len(arrays))] dtype = list(zip(colnames, [x.dtype for x in arrays])) buffer_as_struct = buffer.view(dtype) # Finally, fill the array column by column for letter, array in zip(colnames, arrays): buffer_as_struct[letter] = array # And view as an array of size M return buffer_as_struct.view('S{0}'.format(total_size)) def get_mask_with_key_joins(data, key_joins, subset_state, view=None): """ Given a dataset and a subset state, check whether the subset state can be translated to the current dataset via key joins. Note that this does not try simply applying the subset state to the dataset, as it is assumed this has been tried first. """ for other, (cid1, cid2) in key_joins.items(): if getattr(other, '_recursing', False): continue try: data._recursing = True mask_right = other.get_mask(subset_state) except IncompatibleAttribute: continue finally: data._recursing = False if len(cid1) == 1 and len(cid2) == 1: key_left = data.get_data(cid1[0], view=view) key_right = other.get_data(cid2[0], view=mask_right) mask = np.in1d(key_left.ravel(), key_right.ravel()) return mask.reshape(key_left.shape) elif len(cid1) == len(cid2): key_left_all = [] key_right_all = [] for cid1_i, cid2_i in zip(cid1, cid2): key_left_all.append(data.get_data(cid1_i, view=view).ravel()) key_right_all.append(other.get_data(cid2_i, view=mask_right).ravel()) key_left_all = concatenate_arrays(*key_left_all) key_right_all = concatenate_arrays(*key_right_all) mask = np.in1d(key_left_all, key_right_all) return mask.reshape(data.get_data(cid1_i, view=view).shape) elif len(cid1) == 1: key_left = data.get_data(cid1[0], view=view).ravel() mask = np.zeros_like(key_left, dtype=bool) for cid2_i in cid2: key_right = other.get_data(cid2_i, view=mask_right).ravel() mask |= np.in1d(key_left, key_right) return mask.reshape(data.get_data(cid1[0], view=view).shape) elif len(cid2) == 1: key_right = other.get_data(cid2[0], view=mask_right).ravel() mask = np.zeros_like(data.get_data(cid1[0], view=view).ravel(), dtype=bool) for cid1_i in cid1: key_left = data.get_data(cid1_i, view=view).ravel() mask |= np.in1d(key_left, key_right) return mask.reshape(data.get_data(cid1[0], view=view).shape) else: raise Exception("Either the number of components in the key join sets " "should match, or one of the component sets should ", "contain a single component.") raise IncompatibleAttribute glueviz-1.0.1+dfsg.orig/glue/core/data_exporters/0000755000175000017500000000000013752535025021323 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_exporters/tests/0000755000175000017500000000000013752535025022465 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_exporters/tests/test_hdf5.py0000644000175000017500000000354513605357235024735 0ustar noahfxnoahfximport pytest import numpy as np from glue.core import Data from glue.tests.helpers import requires_h5py from ..hdf5 import hdf5_writer DTYPES = [np.int16, np.int32, np.int64, np.float32, np.float64] @requires_h5py @pytest.mark.parametrize('dtype', DTYPES) def test_hdf5_writer_data(tmpdir, dtype): filename = tmpdir.join('test1.hdf5').strpath data = Data(x=np.arange(6).reshape(2, 3).astype(dtype), y=(np.arange(6) * 2).reshape(2, 3).astype(dtype)) hdf5_writer(filename, data) from h5py import File f = File(filename) assert len(f) == 2 np.testing.assert_equal(f['x'][()], data['x']) np.testing.assert_equal(f['y'][()], data['y']) assert f['x'][()].dtype == dtype assert f['y'][()].dtype == dtype f.close() # Only write out some components filename = tmpdir.join('test2.hdf5').strpath hdf5_writer(filename, data, components=[data.id['x']]) f = File(filename) assert len(f) == 1 np.testing.assert_equal(f['x'][()], data['x']) f.close() @requires_h5py @pytest.mark.parametrize('dtype', DTYPES) def test_hdf5_writer_subset(tmpdir, dtype): filename = tmpdir.join('test').strpath data = Data(x=np.arange(6).reshape(2, 3).astype(dtype), y=(np.arange(6) * 2).reshape(2, 3).astype(dtype)) subset = data.new_subset() subset.subset_state = data.id['x'] > 2 hdf5_writer(filename, subset) from h5py import File f = File(filename) if np.dtype(dtype).kind == 'f': assert np.all(np.isnan(f['x'][0])) assert np.all(np.isnan(f['y'][0])) else: np.testing.assert_equal(f['x'][0], 0) np.testing.assert_equal(f['y'][0], 0) np.testing.assert_equal(f['x'][1], data['x'][1]) np.testing.assert_equal(f['y'][1], data['y'][1]) assert f['x'][()].dtype == dtype assert f['y'][()].dtype == dtype f.close() glueviz-1.0.1+dfsg.orig/glue/core/data_exporters/tests/__init__.py0000644000175000017500000000000013455362716024571 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_exporters/tests/test_astropy_table.py0000644000175000017500000000177513605357235026762 0ustar noahfxnoahfximport pytest import numpy as np from astropy.table import Table from glue.core import Data from ..astropy_table import (ipac_exporter, latex_exporter, votable_exporter, fits_exporter) EXPORTERS = {} EXPORTERS['ascii.ipac'] = ipac_exporter EXPORTERS['ascii.latex'] = latex_exporter EXPORTERS['votable'] = votable_exporter EXPORTERS['fits'] = fits_exporter @pytest.mark.parametrize('fmt', EXPORTERS) def test_astropy_table(tmpdir, fmt): filename = tmpdir.join('test1').strpath data = Data(x=[1, 2, 3], y=[b'a', b'b', b'c']) EXPORTERS[fmt](filename, data) t = Table.read(filename, format=fmt) assert t.colnames == ['x', 'y'] np.testing.assert_equal(t['x'], [1, 2, 3]) np.testing.assert_equal(t['y'].astype(bytes), [b'a', b'b', b'c']) filename = tmpdir.join('test2').strpath EXPORTERS[fmt](filename, data, components=[data.id['x']]) t = Table.read(filename, format=fmt) assert t.colnames == ['x'] np.testing.assert_equal(t['x'], [1, 2, 3]) glueviz-1.0.1+dfsg.orig/glue/core/data_exporters/tests/test_gridded_fits.py0000644000175000017500000000615113612634027026525 0ustar noahfxnoahfximport pytest import numpy as np from glue.core import Data from glue.core.coordinates import WCSCoordinates from astropy.io import fits from ..gridded_fits import fits_writer BITPIX = {} BITPIX[np.int16] = 16 BITPIX[np.int32] = 32 BITPIX[np.int64] = 64 BITPIX[np.float32] = -32 BITPIX[np.float64] = -64 @pytest.mark.parametrize('dtype', BITPIX.keys()) def test_fits_writer_data(tmpdir, dtype): dtype = np.dtype(dtype) filename = tmpdir.join('test1.fits').strpath data = Data(x=np.arange(6).reshape(2, 3).astype(dtype), y=(np.arange(6) * 2).reshape(2, 3).astype(dtype)) fits_writer(filename, data) with fits.open(filename) as hdulist: assert len(hdulist) == 2 np.testing.assert_equal(hdulist['x'].data, data['x']) np.testing.assert_equal(hdulist['y'].data, data['y']) # Note: the following tolerates endian-ness change assert hdulist['x'].data.dtype in (dtype, dtype.newbyteorder()) assert hdulist['y'].data.dtype in (dtype, dtype.newbyteorder()) # Only write out some components filename = tmpdir.join('test2.fits').strpath fits_writer(filename, data, components=[data.id['x']]) with fits.open(filename) as hdulist: assert len(hdulist) == 1 np.testing.assert_equal(hdulist['x'].data, data['x']) def test_component_unit_header(tmpdir): from astropy import units as u filename = tmpdir.join('test3.fits').strpath data = Data(x=np.arange(6).reshape(2, 3), y=(np.arange(6) * 2).reshape(2, 3), z=(np.arange(6) * 2).reshape(2, 3)) data.coords = WCSCoordinates() unit1 = data.get_component("x").units = u.m / u.s unit2 = data.get_component("y").units = u.Jy unit3 = data.get_component("z").units = "" fits_writer(filename, data) with fits.open(filename) as hdulist: assert len(hdulist) == 3 bunit = hdulist['x'].header.get('BUNIT') assert u.Unit(bunit) == unit1 bunit = hdulist['y'].header.get('BUNIT') assert u.Unit(bunit) == unit2 bunit = hdulist['z'].header.get('BUNIT') assert bunit == unit3 @pytest.mark.parametrize('dtype', BITPIX.keys()) def test_fits_writer_subset(tmpdir, dtype): filename = tmpdir.join('test').strpath data = Data(x=np.arange(6).reshape(2, 3).astype(dtype), y=(np.arange(6) * 2).reshape(2, 3).astype(dtype)) subset = data.new_subset() subset.subset_state = data.id['x'] > 2 fits_writer(filename, subset) with fits.open(filename) as hdulist: assert np.all(np.isnan(hdulist['x'].data[0])) assert np.all(np.isnan(hdulist['y'].data[0])) np.testing.assert_equal(hdulist['x'].data[1], data['x'][1]) np.testing.assert_equal(hdulist['y'].data[1], data['y'][1]) # Here we check BITPIX, not the dtype of the read in data, because if # BLANK is present, astropy.io.fits scales the data to float. We want to # just make sure here the data is stored with the correct type on disk. assert hdulist['x'].header['BITPIX'] == BITPIX[dtype] assert hdulist['y'].header['BITPIX'] == BITPIX[dtype] glueviz-1.0.1+dfsg.orig/glue/core/data_exporters/qt/0000755000175000017500000000000013752535025021747 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_exporters/qt/tests/0000755000175000017500000000000013752535025023111 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_exporters/qt/tests/__init__.py0000644000175000017500000000000013605357235025212 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_exporters/qt/tests/test_dialog.py0000644000175000017500000000144313605357235025765 0ustar noahfxnoahfxfrom unittest.mock import patch, MagicMock from collections import namedtuple from glue.core import Data from ..dialog import export_data def test_export(tmpdir): filename = tmpdir.join('data') data = Data(x=[1, 2, 3]) mock = MagicMock() test_exporter_cls = namedtuple('exporter', 'function label extension') test_exporter = test_exporter_cls(function=mock, label='Test', extension='') with patch('qtpy.compat.getsavefilename') as dialog: with patch('glue.config.data_exporter') as data_exporter: def test_iter(x): yield test_exporter data_exporter.__iter__ = test_iter dialog.return_value = filename, 'Test (*)' export_data(data) assert test_exporter.function.call_args[0] == (filename, data) glueviz-1.0.1+dfsg.orig/glue/core/data_exporters/qt/dialog.py0000644000175000017500000000150513605357235023563 0ustar noahfxnoahfxfrom qtpy import compat from glue import config def export_data(data, components=None, exporter=None): if exporter is None: exporters = {} for e in config.data_exporter: if e.extension == '': fltr = "{0} (*)".format(e.label) else: fltr = "{0} ({1})".format(e.label, ' '.join('*.' + ext for ext in e.extension)) exporters[fltr] = e.function filters = ';;'.join(sorted(exporters)) else: filters = None filename, fltr = compat.getsavefilename(caption="Choose an output filename", filters=filters) filename = str(filename) if not filename: return if filters is not None: exporter = exporters[fltr] exporter(filename, data, components=components) glueviz-1.0.1+dfsg.orig/glue/core/data_exporters/qt/__init__.py0000644000175000017500000000000013605357235024050 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_exporters/__init__.py0000644000175000017500000000017313502206677023436 0ustar noahfxnoahfxdef setup(): from . import gridded_fits # noqa from . import astropy_table # noqa from . import hdf5 # noqa glueviz-1.0.1+dfsg.orig/glue/core/data_exporters/astropy_table.py0000644000175000017500000000265413605357235024556 0ustar noahfxnoahfximport os from glue.core import Subset from glue.config import data_exporter __all__ = [] def data_to_astropy_table(data, components=None): if isinstance(data, Subset): mask = data.to_mask() data = data.data else: mask = None from astropy.table import Table table = Table() for cid in data.main_components + data.derived_components: if components is not None and cid not in components: continue values = data[cid] if mask is not None: values = values[mask] table[cid.label] = values return table def table_exporter(fmt, label, extension): @data_exporter(label=label, extension=extension) def factory(filename, data, components=None): if os.path.exists(filename): os.remove(filename) return data_to_astropy_table(data, components=components).write(filename, format=fmt) # rename function to its variable reference below # allows pickling to work factory.__name__ = '%s_factory' % fmt.replace('.', '_') return factory csv_exporter = table_exporter('ascii.csv', 'Comma-separated table', ['csv']) ipac_exporter = table_exporter('ascii.ipac', 'IPAC Catalog', ['tbl']) latex_exporter = table_exporter('ascii.latex', 'LaTeX Table', ['tex']) votable_exporter = table_exporter('votable', 'VO Table', ['xml', 'vot']) fits_exporter = table_exporter('fits', 'FITS Table', ['fits', 'fit']) glueviz-1.0.1+dfsg.orig/glue/core/data_exporters/hdf5.py0000644000175000017500000000333313605357235022527 0ustar noahfxnoahfximport warnings import numpy as np from glue.core import Subset from glue.config import data_exporter __all__ = [] @data_exporter(label='HDF5', extension=['hdf5']) def hdf5_writer(filename, data, components=None): """ Write a dataset or a subset to a FITS file. Parameters ---------- data : `~glue.core.data.Data` or `~glue.core.subset.Subset` The data or subset to export components : `list` or `None` The components to export. Set this to `None` to export all components. """ if isinstance(data, Subset): mask = data.to_mask() data = data.data else: mask = None from h5py import File f = File(filename, 'w') for cid in data.main_components + data.derived_components: if components is not None and cid not in components: continue if data.get_kind(cid) == 'categorical': values = data[cid] if values.dtype.kind == 'U': values = np.char.encode(values, encoding='ascii', errors='replace') else: values = values.copy() else: values = data[cid].copy() if mask is not None: if values.ndim == 1: values = values[mask] else: if values.dtype.kind == 'f': values[~mask] = np.nan elif values.dtype.kind == 'i': values[~mask] = 0 elif values.dtype.kind == 'S': values[~mask] = '' else: warnings.warn("Unknown data type in HDF5 export: {0}".format(values.dtype)) continue f.create_dataset(cid.label, data=values) f.close() glueviz-1.0.1+dfsg.orig/glue/core/data_exporters/gridded_fits.py0000644000175000017500000000501613612622321024315 0ustar noahfxnoahfximport numpy as np from astropy.wcs import WCS from glue.core import Subset, Data from glue.config import data_exporter __all__ = [] def make_component_header(component, header): """ Function that extracts information from components and adds it to the data header. The input header is expected to come from Data.coords.header by default. Parameters ---------- component: glue Component Glue component to extract info from header: astropy.io.fits.header.Header Input header to be modified according to the input component """ # Add units information header["BUNIT"] = component.units return header @data_exporter(label='FITS (1 component/HDU)', extension=['fits', 'fit']) def fits_writer(filename, data, components=None): """ Write a dataset or a subset to a FITS file. Parameters ---------- data : `~glue.core.data.Data` or `~glue.core.subset.Subset` The data or subset to export components : `list` or `None` The components to export. Set this to `None` to export all components. """ from astropy.io import fits if isinstance(data, Subset): mask = data.to_mask() data = data.data else: mask = None data_header = data.coords.to_header() if isinstance(data.coords, WCS) else fits.Header() hdus = fits.HDUList() for cid in data.main_components + data.derived_components: if components is not None and cid not in components: continue if data.get_kind(cid) != 'numerical': # TODO: emit warning continue values = data[cid] if mask is not None: # We need to copy the values so that we can mask them values = values.copy() if values.dtype.kind == 'f': blank = None values[~mask] = np.nan elif values.dtype.kind == 'i': blank = np.iinfo(values.dtype).min values[~mask] = blank # TODO: special behavior for PRIMARY? if isinstance(data, Data): comp = data.get_component(cid) header = make_component_header(comp, data_header) else: header = fits.Header() if mask is not None and blank is not None: header['BLANK'] = blank hdu = fits.ImageHDU(values, name=cid.label, header=header) hdus.append(hdu) try: hdus.writeto(filename, overwrite=True) except TypeError: hdus.writeto(filename, clobber=True) glueviz-1.0.1+dfsg.orig/glue/core/autolinking.py0000644000175000017500000000143613503203504021165 0ustar noahfxnoahfxfrom glue.config import autolinker __all__ = ['find_possible_links'] def expand_links(links): new_links = [] if isinstance(links, list): for link in links: new_links.extend(expand_links(link)) else: new_links.append(links) return new_links def find_possible_links(data_collection): """ Given a `~glue.core.data_collection.DataCollection` object, return a dictionary containing possible link suggestions, where the keys are the name of the auto-linking plugin, and the values are lists of links. """ suggestions = {} for label, function in autolinker: links = function(data_collection) links = expand_links(links) if len(links) > 0: suggestions[label] = links return suggestions glueviz-1.0.1+dfsg.orig/glue/core/contracts.py0000644000175000017500000000511713605357235020657 0ustar noahfxnoahfx""" An interface to PyContracts, to annotate functions with type information The @contract decorator is disabled by default, to avoid any runtime overhead when using Glue. To enable runtime checking, run glue.config.enable_contracts(True) in a glue config file. If PyContrats is imported, a no-op @contract decorator is provided for compatibility Glue code should only import contract through this module, and never directly from the contracts package. """ from pandas import Series from numpy import ndarray, s_ from glue.config import enable_contracts def _build_custom_contracts(): """ Define some custom contracts if PyContracts is found """ from contracts import new_contract @new_contract def cid_like(value): """ Value is a ComponentID or a string """ from glue.core import ComponentID return isinstance(value, (ComponentID, str)) @new_contract def component_like(value): from glue.core import Component, ComponentLink return isinstance(value, (Component, ComponentLink, ndarray, list, Series)) @new_contract def array_like(value): return isinstance(value, (ndarray, list)) @new_contract def color(value): """ A valid matplotlib color """ from matplotlib.colors import colorConverter try: colorConverter.to_rgba(value) except ValueError: return False @new_contract def inst(value, *types): return isinstance(value, types) @new_contract def data_view(value): from glue.core import ComponentID if value is None: return if isinstance(value, ComponentID): return try: if not isinstance(value[0], ComponentID): return False s_[value[1:]] except Exception: return False @new_contract def array_view(value): try: s_[value] except Exception: return False @new_contract def callable(value): return hasattr(value, '__call__') try: from contracts import contract, ContractsMeta if not enable_contracts(): from contracts import disable_all disable_all() _build_custom_contracts() except ImportError: # no-op interface if PyContracts isn't installed def contract(*args, **kwargs): if args: # called as @contract return args[0] else: # called as @contract(x='int', ...) return lambda func: func ContractsMeta = type glueviz-1.0.1+dfsg.orig/glue/core/hub.py0000644000175000017500000002106513605357235017435 0ustar noahfxnoahfximport logging from contextlib import contextmanager from weakref import WeakKeyDictionary from inspect import getmro from collections import Counter from glue.core.exceptions import InvalidSubscriber, InvalidMessage from glue.core.message import Message from glue.core.hub_callback_container import HubCallbackContainer __all__ = ['Hub', 'HubListener'] class Hub(object): """The hub manages communication between subscribers. Objects :func:`subscribe` to receive specific message types. When a message is passed to :func:`broadcast`, the hub observes the following protocol: * For each subscriber, it looks for a message class subscription that is a parent class of the input message type (if several are found, the most-subclassed one is chosen) * If one is found, it calls the subscriptions filter(message) class (if provided) * If filter(message) == True, it calls handler(message) (or notify(message) if handler wasn't provided). """ def __init__(self, *args): """ Any arguments that are passed to Hub will be registered to the new hub object. """ # Dictionary of subscriptions self._subscriptions = WeakKeyDictionary() self._paused = False self._queue = [] self._ignore = Counter() from glue.core.data import BaseData from glue.core.subset import Subset from glue.core.data_collection import DataCollection listeners = set(filter(lambda x: isinstance(x, HubListener), args)) data = set(filter(lambda x: isinstance(x, BaseData), args)) subsets = set(filter(lambda x: isinstance(x, Subset), args)) dcs = set(filter(lambda x: isinstance(x, DataCollection), args)) listeners -= (data | subsets | dcs) if set(listeners | data | subsets | dcs) != set(args): raise TypeError("Inputs must be HubListener, data, subset, or " "data collection objects") for l in listeners: l.register_to_hub(self) for d in data: d.register_to_hub(self) for dc in dcs: dc.register_to_hub(self) for s in subsets: s.register() def subscribe(self, subscriber, message_class, handler=None, filter=lambda x: True): """Subscribe an object to a type of message class. :param subscriber: The subscribing object :type subscriber: :class:`~glue.core.hub.HubListener` :param message_class: A :class:`~glue.core.message.Message` class to subscribe to :param handler: An optional function of the form handler(message) that will receive the message on behalf of the subscriber. If not provided, this defaults to the HubListener's notify method :param filter: An optional function of the form filter(message). Messages are only passed to the subscriber if filter(message) == True. The default is to always pass messages. Raises: InvalidMessage: If the input class isn't a :class:`~glue.core.message.Message` class InvalidSubscriber: If the input subscriber isn't a HubListener object. """ if not isinstance(subscriber, HubListener): raise InvalidSubscriber("Subscriber must be a HubListener: %s" % type(subscriber)) if not isinstance(message_class, type) or \ not issubclass(message_class, Message): raise InvalidMessage("message class must be a subclass of " "glue.Message: %s" % type(message_class)) logging.getLogger(__name__).info("Subscribing %s to %s", subscriber, message_class.__name__) if not handler: handler = subscriber.notify if subscriber not in self._subscriptions: self._subscriptions[subscriber] = HubCallbackContainer() self._subscriptions[subscriber][message_class] = handler, filter def is_subscribed(self, subscriber, message): """ Test whether the subscriber has subscribed to a given message class :param subscriber: The subscriber to test :param message: The message class to test Returns: True if the subscriber/message pair have been subscribed to the hub """ return (subscriber in self._subscriptions and message in self._subscriptions[subscriber]) def get_handler(self, subscriber, message): if subscriber is None: return None try: return self._subscriptions[subscriber][message][0] except KeyError: return None def unsubscribe(self, subscriber, message): """ Remove a (subscriber,message) pair from subscription list. The handler originally attached to the subscription will no longer be called when broadcasting messages of type message """ if subscriber not in self._subscriptions: return if message in self._subscriptions[subscriber]: self._subscriptions[subscriber].pop(message) def unsubscribe_all(self, subscriber): """ Unsubscribe the object from any subscriptions. """ if subscriber in self._subscriptions: self._subscriptions.pop(subscriber) def _find_handlers(self, message): """Yields all (subscriber, handler) pairs that should receive a message """ # self._subscriptions: # subscriber => { message type => (filter, handler)} # loop over subscribed objects for subscriber, subscriptions in list(self._subscriptions.items()): # subscriptions to message or its superclasses messages = [msg for msg in subscriptions.keys() if issubclass(type(message), msg)] if len(messages) == 0: continue # narrow to the most-specific message candidate = max(messages, key=_mro_count) handler, test = subscriptions[candidate] if test(message): yield subscriber, handler @contextmanager def ignore_callbacks(self, ignore_type): self._ignore[ignore_type] += 1 try: yield finally: self._ignore[ignore_type] -= 1 @contextmanager def delay_callbacks(self): self._paused = True try: yield finally: self._paused = False # TODO: could de-duplicate messages here for message in self._queue: self.broadcast(message) self._queue = [] def broadcast(self, message): """Broadcasts a message to all subscribed objects. :param message: The message to broadcast :type message: :class:`~glue.core.message.Message` """ if self._ignore.get(type(message), 0) > 0: return elif self._paused: self._queue.append(message) else: logging.getLogger(__name__).info("Broadcasting %s", message) for subscriber, handler in self._find_handlers(message): handler(message) def __getstate__(self): """ Return a picklable representation of the hub Note: Only objects in glue.core are currently supported as pickleable. Thus, any subscriptions from objects outside glue.core will note be saved or restored """ result = self.__dict__.copy() result['_subscriptions'] = self._subscriptions.copy() for s in self._subscriptions: try: module = s.__module__ except AttributeError: module = '' if not module.startswith('glue.core'): print('Pickle warning: Hub removing subscription to %s' % s) result['_subscriptions'].pop(s) return result class HubListener(object): """ The base class for any object that subscribes to hub messages. This interface defines a single method, notify, that receives messages """ def register_to_hub(self, hub): raise NotImplementedError def unregister(self, hub): """ Default unregistration action. Calls hub.unsubscribe_all on self""" hub.unsubscribe_all(self) def notify(self, message): raise NotImplementedError("Message has no handler: %s" % message) def _mro_count(obj): return len(getmro(obj)) glueviz-1.0.1+dfsg.orig/glue/core/aggregate.py0000644000175000017500000000217113605357235020602 0ustar noahfxnoahfx""" Classes to perform aggregations over cubes """ import numpy as np def mom1(data, axis=0): """ Intensity-weighted coordinate (function version). Pixel units. """ shp = list(data.shape) n = shp.pop(axis) result = np.zeros(shp) w = np.zeros(shp) # build up slice-by-slice, to avoid big temporary cubes for loc in range(n): slc = tuple(loc if j == axis else slice(None) for j in range(data.ndim)) val = data[slc] val = np.maximum(val, 0) result += val * loc w += val return result / w def mom2(data, axis=0): """ Intensity-weighted coordinate dispersion (function version). Pixel units. """ shp = list(data.shape) n = shp.pop(axis) x = np.zeros(shp) x2 = np.zeros(shp) w = np.zeros(shp) # build up slice-by-slice, to avoid big temporary cubes for loc in range(n): slc = tuple(loc if j == axis else slice(None) for j in range(data.ndim)) val = data[slc] val = np.maximum(val, 0) x += val * loc x2 += val * loc * loc w += val return np.sqrt(x2 / w - (x / w) ** 2) glueviz-1.0.1+dfsg.orig/glue/core/data.py0000644000175000017500000022342113752534502017565 0ustar noahfxnoahfxfrom collections import OrderedDict import abc import uuid import warnings from contextlib import contextmanager import numpy as np import pandas as pd from fast_histogram import histogram1d, histogram2d from glue.core.message import (DataUpdateMessage, DataRemoveComponentMessage, DataAddComponentMessage, NumericalDataChangedMessage, SubsetCreateMessage, ComponentsChangedMessage, ComponentReplacedMessage, DataReorderComponentMessage, ExternallyDerivableComponentsChangedMessage, PixelAlignedDataChangedMessage) from glue.core.decorators import clear_cache from glue.core.util import split_component_view from glue.core.hub import Hub from glue.core.subset import Subset, SubsetState, SliceSubsetState from glue.core.component_id import ComponentIDList from glue.core.component_link import ComponentLink, CoordinateComponentLink from glue.core.exceptions import IncompatibleAttribute from glue.core.visual import VisualAttributes from glue.core.contracts import contract from glue.core.joins import get_mask_with_key_joins from glue.config import settings, data_translator, subset_state_translator from glue.utils import (compute_statistic, unbroadcast, iterate_chunks, datetime64_to_mpl, broadcast_to, categorical_ndarray, format_choices, random_views_for_dask_array) from glue.core.coordinate_helpers import axis_label # Note: leave all the following imports for component and component_id since # they are here for backward-compatibility (the code used to live in this # file) from glue.core.component import Component, CoordinateComponent, DerivedComponent from glue.core.component_id import ComponentID, ComponentIDDict, PixelComponentID try: import dask.array as da DASK_INSTALLED = True except ImportError: DASK_INSTALLED = False __all__ = ['Data', 'BaseCartesianData', 'BaseData'] class BaseData(object, metaclass=abc.ABCMeta): """ Base class for any glue data object which indicates which methods should be provided at a minimum. For now, subclasses of BaseData are not guaranteed to work in glue, and you should instead subclass BaseCartesianData. """ def __init__(self): # Metadata self.meta = OrderedDict() # Subsets of the data self._subsets = [] # Hub that the data is attached to self.hub = None self.style = VisualAttributes(parent=self) @property def label(self): """ The name of the dataset """ raise NotImplementedError() @abc.abstractmethod def get_kind(self, cid): """ Get the kind of data for a given component. Parameters ---------- cid : `ComponentID` The component ID to get the data kind for Returns ------- kind : {'numerical', 'categorical', 'datetime'} The kind of data for the given component ID. """ raise NotImplementedError() @abc.abstractproperty def main_components(self): raise NotImplementedError() @property def components(self): """ A list of :class:`~glue.core.component_id.ComponentID` giving all available components in the data """ return self.pixel_component_ids + self.world_component_ids + self.main_components @property def coordinate_components(self): """ A list of :class:`~glue.core.component_id.ComponentID` giving all coordinate components in the data """ return self.pixel_component_ids + self.world_component_ids @property def pixel_component_ids(self): """ A list of :class:`~glue.core.component_id.ComponentID` giving all pixel coordinate components in the data """ if not hasattr(self, '_pixel_component_ids'): self._pixel_component_ids = [] for i in range(self.ndim): pid = PixelComponentID(i, 'Pixel Axis {0}'.format(i), parent=self) self._pixel_component_ids.append(pid) return self._pixel_component_ids @property def world_component_ids(self): """ A list of :class:`~glue.core.component_id.ComponentID` giving all world coordinate components in the data """ return [] @property def derived_components(self): return [] def find_component_id(self, label): """ Find a component ID by name. This returns the associated ComponentID if label is found and unique, and `None` otherwise. """ # This is a simple implementation that relies on .components and should # not need to be overriden if isinstance(label, ComponentID): return label matches = [cid for cid in self.components if cid.label == label] if len(matches) == 1: return matches[0] elif len(matches) > 1: return None @contract(hub=Hub) def register_to_hub(self, hub): """ Connect to a hub. This method usually doesn't have to be called directly, as DataCollections manage the registration of data objects """ if not isinstance(hub, Hub): raise TypeError("input is not a Hub object: %s" % type(hub)) self.hub = hub @property def data(self): return self @contract(subset='isinstance(Subset)|None', color='color|None', label='string|None', returns=Subset) def new_subset(self, subset=None, color=None, label=None, **kwargs): """ Create a new subset, and attach to self. .. note:: The preferred way for creating subsets is via :meth:`~glue.core.data_collection.DataCollection.new_subset_group`. Manually-instantiated subsets will **not** be represented properly by the UI :param subset: optional, reference subset or subset state. If provided, the new subset will copy the logic of this subset. :returns: The new subset object """ nsub = len(self.subsets) color = color or settings.SUBSET_COLORS[nsub % len(settings.SUBSET_COLORS)] label = label or "%s.%i" % (self.label, nsub + 1) new_subset = Subset(self, color=color, label=label, **kwargs) if subset is not None: new_subset.subset_state = subset.subset_state.copy() self.add_subset(new_subset) return new_subset @contract(subset='inst($Subset, $SubsetState)') def add_subset(self, subset, label=None): """Assign a pre-existing subset to this data object. :param subset: A :class:`~glue.core.subset.Subset` or :class:`~glue.core.subset.SubsetState` object If input is a :class:`~glue.core.subset.SubsetState`, it will be wrapped in a new Subset automatically .. note:: The preferred way for creating subsets is via :meth:`~glue.core.data_collection.DataCollection.new_subset_group`. Manually-instantiated subsets will **not** be represented properly by the UI """ if subset in self.subsets: return # prevents infinite recursion if isinstance(subset, SubsetState): # auto-wrap state in subset state = subset subset = Subset(None) subset.subset_state = state if label: subset.label = label self._subsets.append(subset) if subset.data is not self: subset.do_broadcast(False) subset.data = self subset.label = subset.label # hacky. disambiguates name if needed if self.hub is not None: msg = SubsetCreateMessage(subset) self.hub.broadcast(msg) subset.do_broadcast(True) @contract(attribute='string') def broadcast(self, attribute): """ Send a :class:`~glue.core.message.DataUpdateMessage` to the hub :param attribute: Name of an attribute that has changed (or None) :type attribute: str """ if not self.hub: return msg = DataUpdateMessage(self, attribute=attribute) self.hub.broadcast(msg) @property def subsets(self): """ Tuple of subsets attached to this dataset """ return tuple(self._subsets) def get_object(self, cls=None, **kwargs): """ Get the dataset represented as a non-glue object, using the translation infrastructure. Parameters ---------- cls : type, optional The class to use for representing the data object. If a non-glue data object was added to the glue data collection, it should automatically be returned using the same class as it was provided in, and this argument shouldn't be needed. """ if cls is None: if hasattr(self, '_preferred_translation'): cls = self._preferred_translation else: raise ValueError('Specify the object class to use with cls= - supported ' 'classes are:' + format_choices(data_translator.supported_classes)) handler, _ = data_translator.get_handler_for(cls) return handler.to_object(self, **kwargs) @property def _subset_labels(self): return [subset.label for subset in self.subsets] def get_subset_object(self, subset_id=None, cls=None, **kwargs): """ Get a subset represented as a non-glue object, using the translation infrastructure. Parameters ---------- subset_id : str or int, optional The name or index of the subset to retrieve. cls : type, optional The class to use for representing the data object. If a non-glue data object was added to the data collection, the subset should automatically be returned using the same class as it was provided in, and this argument shouldn't be needed. """ if cls is None: if hasattr(self, '_preferred_translation'): cls = self._preferred_translation else: raise ValueError('Specify the object class to use with cls= - supported ' 'classes are:' + format_choices(data_translator.supported_classes)) if len(self.subsets) == 0: raise ValueError("Dataset does not contain any subsets") elif subset_id is None: if len(self.subsets) == 1: subset = self.subsets[0] else: raise ValueError("Several subsets are present, specify which one to retrieve with subset_id= - valid options are:" + format_choices(self._subset_labels, index=True)) elif isinstance(subset_id, str): matches = [subset for subset in self.subsets if subset.label == subset_id] if len(matches) == 0: raise ValueError("No subset found with the label '{0}'".format(subset_id)) elif len(matches) > 1: raise ValueError("Several subsets were found with the label '{0}', use a numerical index instead".format(subset_id)) else: subset = matches[0] else: subset = self.subsets[subset_id] handler, _ = data_translator.get_handler_for(cls) return handler.to_object(subset, **kwargs) def get_selection_definition(self, subset_id=None, format=None, **kwargs): """ Get subset state represented as a non-glue object, using the translation infrastructure. Parameters ---------- subset_id : str or int, optional The name or index of the subset to retrieve. format : str, optional The format to translate the subset state to. """ if len(self.subsets) == 0: raise ValueError("Dataset does not contain any subsets") elif subset_id is None: if len(self.subsets) == 1: subset = self.subsets[0] else: raise ValueError("Several subsets are present, specify which one to retrieve with subset_id= - valid options are:" + format_choices(self._subset_labels, index=True)) elif isinstance(subset_id, str): matches = [subset for subset in self.subsets if subset.label == subset_id] if len(matches) == 0: raise ValueError("No subset found with the label '{0}'".format(subset_id)) elif len(matches) > 1: raise ValueError("Several subsets were found with the label '{0}', use a numerical index instead".format(subset_id)) else: subset = matches[0] else: subset = self.subsets[subset_id] handler = subset_state_translator.get_handler_for(format) return handler.to_object(subset, **kwargs) class BaseCartesianData(BaseData, metaclass=abc.ABCMeta): """ Base class for any glue data object which indicates which methods should be provided at a minimum. The underlying data can be any kind of data (structured or unstructured) but it needs to expose an interface that looks like a regular n-dimensional cartesian dataset. This means exposing e.g. ``shape`` and ``ndim``, and means that get_data can expect ndarray slices. Non-regular datasets should therefore have the concept of 'virtual' pixel coordinates and should typically match the highest resolution a user might want to access the data at. """ def __init__(self, coords=None): super(BaseCartesianData, self).__init__() self._coords = coords @property def coords(self): """ The coordinates object for the data. """ return self._coords @abc.abstractproperty def shape(self): """ The n-dimensional shape of the dataset, as a tuple. """ raise NotImplementedError() @property def ndim(self): """ The number of dimensions of the data, as an integer. """ return len(self.shape) @property def size(self): """ The size of the data (the product of the shape dimensions), as an integer. """ return np.product(self.shape) def get_data(self, cid, view=None): """ Get the data values for a given component Parameters ---------- cid : `ComponentID` The component ID to get the data for view The 'view' on the data - anything that is considered a valid Numpy slice/index. """ if cid in self.pixel_component_ids: shape = tuple(-1 if i == cid.axis else 1 for i in range(self.ndim)) pix = np.arange(self.shape[cid.axis], dtype=float).reshape(shape) return broadcast_to(pix, self.shape)[view] elif cid in self.world_component_ids: comp = self.world_components[cid] if view is not None: result = comp[view] else: result = comp.data else: raise IncompatibleAttribute(cid) @abc.abstractmethod def get_mask(self, subset_state, view=None): """ Get a boolean mask for a given subset state. Parameters ---------- subset_state : `SubsetState` The subset state to use to compute the mask view The 'view' on the mask - anything that is considered a valid Numpy slice/index. """ raise NotImplementedError() @abc.abstractmethod def compute_statistic(self, statistic, cid, subset_state=None, axis=None, finite=True, positive=False, percentile=None, view=None, random_subset=None): """ Compute a statistic for the data. Parameters ---------- statistic : {'minimum', 'maximum', 'mean', 'median', 'sum', 'percentile'} The statistic to compute cid : `ComponentID` or str The component ID to compute the statistic on - if given as a string this will be assumed to be for the component belonging to the dataset (not external links). subset_state : `SubsetState` If specified, the statistic will only include the values that are in the subset specified by this subset state. axis : None or int or tuple of int If specified, the axis/axes to compute the statistic over. finite : bool, optional Whether to include only finite values in the statistic. This should be `True` to ignore NaN/Inf values positive : bool, optional Whether to include only (strictly) positive values in the statistic. This is used for example when computing statistics of data shown in log space. percentile : float, optional If ``statistic`` is ``'percentile'``, the ``percentile`` argument should be given and specify the percentile to calculate in the range [0:100] random_subset : int, optional If specified, this should be an integer giving the number of values to use for the statistic. This can only be used if ``axis`` is `None` """ raise NotImplementedError() @abc.abstractmethod def compute_histogram(self, cids, weights=None, range=None, bins=None, log=None, subset_state=None): """ Compute an n-dimensional histogram with regularly spaced bins. Parameters ---------- cids : list of str or `ComponentID` Component IDs to compute the histogram over weights : str or ComponentID Component IDs to use for the histogram weights range : list of tuple The ``(min, max)`` of the histogram range bins : list of int The number of bins log : list of bool Whether to compute the histogram in log space subset_state : `SubsetState`, optional If specified, the histogram will only take into account values in the subset state. """ raise NotImplementedError() def compute_fixed_resolution_buffer(self, bounds, target_data=None, target_cid=None, subset_state=None, broadcast=True): """ Get a fixed-resolution buffer. Parameters ---------- bounds : list The list of bounds for the fixed resolution buffer. This list should have as many items as there are dimensions in ``target_data``. Each item should either be a scalar value, or a tuple of ``(min, max, nsteps)``. target_data : `~glue.core.Data`, optional The data in whose frame of reference the bounds are defined. Defaults to ``data``. target_cid : `~glue.core.component_id.ComponentID`, optional If specified, gives the component ID giving the component to use for the data values. Alternatively, use ``subset_state`` to get a subset mask. subset_state : `~glue.core.subset.SubsetState`, optional If specified, gives the subset state for which to compute a mask. Alternatively, use ``target_cid`` if you want to get data values. broadcast : bool, optional If `True`, then if a dimension in ``target_data`` for which ``bounds`` is not a scalar does not affect any of the dimensions in ``data``, then the final array will be effectively broadcast along this dimension, otherwise an error will be raised. """ raise NotImplementedError() def __getitem__(self, key): """ Shortcut syntax to access the numerical data in a component. Equivalent to:: component = data.get_data(component_id) The key can be either just a component name, component ID, or a component name/ID and a view. """ # Note: this method is generic and shouldn't need to be overriden by # subclasses. key, view = split_component_view(key) if isinstance(key, str): _k = key key = self.find_component_id(key) if key is None: raise IncompatibleAttribute(_k) return self.get_data(key, view=view) def _ipython_key_completions_(self): return [cid.label for cid in self.components] @property def world_component_ids(self): """ A list of :class:`~glue.core.component_id.ComponentID` giving all world coordinate component IDs in the data """ if self.coords is None: return [] elif not hasattr(self, '_world_component_ids'): self._world_component_ids = [] self._world_components = {} for i in range(self.ndim): comp = CoordinateComponent(self, i, world=True) label = axis_label(self.coords, i) cid = ComponentID(label, parent=self) self._world_component_ids.append(cid) self._world_components[cid] = comp return self._world_component_ids class Data(BaseCartesianData): """ The basic data container in Glue. The data object stores data as a collection of :class:`~glue.core.component.Component` objects. Each component stored in a dataset must have the same shape. Catalog data sets are stored such that each column is a distinct 1-dimensional :class:`~glue.core.component.Component`. There are several ways to extract the actual numerical data stored in a :class:`~glue.core.data.Data` object:: data = Data(x=[1, 2, 3], label='data') xid = data.id['x'] data[xid] data.get_component(xid).data data['x'] # if 'x' is a unique component name Likewise, datasets support :ref:`fancy indexing `:: data[xid, 0:2] data[xid, [True, False, True]] See also: :ref:`data_tutorial` Parameters ---------- label : str The name of the dataset coords : :class:`~glue.core.coordinates.Coordinates` The coordinates object to use to define world coordinates """ def __init__(self, label="", coords=None, **kwargs): super(Data, self).__init__() self.label = label self._shape = () # Components self._components = OrderedDict() self._externally_derivable_components = OrderedDict() self._pixel_aligned_data = OrderedDict() self._pixel_component_ids = ComponentIDList() self._world_component_ids = ComponentIDList() # Coordinate conversion object self.coords = coords self.id = ComponentIDDict(self) self._coordinate_links = [] self.edit_subset = None for lbl, data in sorted(kwargs.items()): self.add_component(data, lbl) self._key_joins = {} # To avoid circular references when saving objects with references to # the data, we make sure that all Data objects have a UUID that can # uniquely identify them. self.uuid = str(uuid.uuid4()) @property def coords(self): """ The coordinates object for the data. """ return self._coords @coords.setter def coords(self, value): if (hasattr(self, '_coords') and self._coords != value) or not hasattr(self, '_coords'): self._coords = value if len(self.components) > 0: self._update_world_components(self.ndim) @property def ndim(self): return len(self.shape) @property def shape(self): return self._shape @property def label(self): return self._label @label.setter def label(self, value): if getattr(self, '_label', None) != value: self._label = value self.broadcast(attribute='label') elif value is None: self._label = value @property def size(self): return np.product(self.shape) @contract(component=Component) def _check_can_add(self, component): if isinstance(component, DerivedComponent): return component._data is self else: if len(self._components) == 0: return True else: if all(comp.shape == () for comp in self._components.values()): return True else: return component.shape == self.shape @contract(cid=ComponentID, returns=np.dtype) def dtype(self, cid): """Lookup the dtype for the data associated with a ComponentID""" # grab a small piece of data ind = tuple([slice(0, 1)] * self.ndim) arr = self.get_data(cid, view=ind) return arr.dtype @contract(component_id=ComponentID) def remove_component(self, component_id): """ Remove a component from a data set :param component_id: the component to remove :type component_id: :class:`~glue.core.component_id.ComponentID` """ # TODO: avoid too many messages when removing a component triggers # the removal of derived components. if component_id in self._components: self._components.pop(component_id) self._removed_derived_that_depend_on(component_id) if self.hub: msg = DataRemoveComponentMessage(self, component_id) self.hub.broadcast(msg) msg = ComponentsChangedMessage(self) self.hub.broadcast(msg) def _removed_derived_that_depend_on(self, component_id): """ Remove internal derived components that can no longer be derived. """ remove = [] for cid in self.derived_components: comp = self.get_component(cid) if component_id in comp.link.get_from_ids(): remove.append(cid) for cid in remove: self.remove_component(cid) @contract(other='isinstance(Data)', cid='cid_like', cid_other='cid_like') def join_on_key(self, other, cid, cid_other): """ Create an *element* mapping to another dataset, by joining on values of ComponentIDs in both datasets. This join allows any subsets defined on `other` to be propagated to self. The different ways to call this method are described in the **Examples** section below. Parameters ---------- other : :class:`~glue.core.data.Data` Data object to join with cid : str or :class:`~glue.core.component_id.ComponentID` or iterable Component(s) in this dataset to use as a key cid_other : str or :class:`~glue.core.component_id.ComponentID` or iterable Component(s) in the other dataset to use as a key Examples -------- There are several ways to use this function, depending on how many components are passed to ``cid`` and ``cid_other``. **Joining on single components** First, one can specify a single component ID for both ``cid`` and ``cid_other``: this is the standard mode, and joins one component from one dataset to the other: >>> d1 = Data(x=[1, 2, 3, 4, 5], k1=[0, 0, 1, 1, 2], label='d1') >>> d2 = Data(y=[2, 4, 5, 8, 4], k2=[1, 3, 1, 2, 3], label='d2') >>> d2.join_on_key(d1, 'k2', 'k1') Selecting all values in ``d1`` where x is greater than 2 returns the last three items as expected: >>> s = d1.new_subset() >>> s.subset_state = d1.id['x'] > 2 >>> s.to_mask() array([False, False, True, True, True], dtype=bool) The linking was done between k1 and k2, and the values of k1 for the last three items are 1 and 2 - this means that the first, third, and fourth item in ``d2`` will then get selected, since k2 has a value of either 1 or 2 for these items. >>> s = d2.new_subset() >>> s.subset_state = d1.id['x'] > 2 >>> s.to_mask() array([ True, False, True, True, False], dtype=bool) **Joining on multiple components** .. note:: This mode is currently slow, and will be optimized significantly in future. Next, one can specify several components for each dataset: in this case, the number of components given should match for both datasets. This causes items in both datasets to be linked when (and only when) the set of keys match between the two datasets: >>> d1 = Data(x=[1, 2, 3, 5, 5], ... y=[0, 0, 1, 1, 2], label='d1') >>> d2 = Data(a=[2, 5, 5, 8, 4], ... b=[1, 3, 2, 2, 3], label='d2') >>> d2.join_on_key(d1, ('a', 'b'), ('x', 'y')) Selecting all items where x is 5 in ``d1`` in which x is a component works as expected and selects the two last items:: >>> s = d1.new_subset() >>> s.subset_state = d1.id['x'] == 5 >>> s.to_mask() array([False, False, False, True, True], dtype=bool) If we apply this selection to ``d2``, only items where a is 5 and b is 2 will be selected: >>> s = d2.new_subset() >>> s.subset_state = d1.id['x'] == 5 >>> s.to_mask() array([False, False, True, False, False], dtype=bool) and in particular, the second item (where a is 5 and b is 3) is not selected. **One-to-many and many-to-one joining** Finally, you can specify one component in one dataset and multiple ones in the other. In the case where one component is specified for this dataset and multiple ones for the other dataset, then when an item is selected in the other dataset, it will cause any item in the present dataset which matches any of the keys in the other data to be selected: >>> d1 = Data(x=[1, 2, 3], label='d1') >>> d2 = Data(a=[1, 1, 2], ... b=[2, 3, 3], label='d2') >>> d1.join_on_key(d2, 'x', ('a', 'b')) In this case, if we select all items in ``d2`` where a is 2, this will select the third item: >>> s = d2.new_subset() >>> s.subset_state = d2.id['a'] == 2 >>> s.to_mask() array([False, False, True], dtype=bool) Since we have joined the datasets using both a and b, we select all items in ``d1`` where x is either the value or a or b (2 or 3) which means we select the second and third item: >>> s = d1.new_subset() >>> s.subset_state = d2.id['a'] == 2 >>> s.to_mask() array([False, True, True], dtype=bool) We can also join the datasets the other way around: >>> d1 = Data(x=[1, 2, 3], label='d1') >>> d2 = Data(a=[1, 1, 2], ... b=[2, 3, 3], label='d2') >>> d2.join_on_key(d1, ('a', 'b'), 'x') In this case, selecting items in ``d1`` where x is 1 selects the first item, as expected: >>> s = d1.new_subset() >>> s.subset_state = d1.id['x'] == 1 >>> s.to_mask() array([ True, False, False], dtype=bool) This then causes any item in ``d2`` where either a or b are 1 to be selected, i.e. the first two items: >>> s = d2.new_subset() >>> s.subset_state = d1.id['x'] == 1 >>> s.to_mask() array([ True, True, False], dtype=bool) """ # To make things easier, we transform all component inputs to a tuple if isinstance(cid, str) or isinstance(cid, ComponentID): cid = (cid,) if isinstance(cid_other, str) or isinstance(cid_other, ComponentID): cid_other = (cid_other,) if len(cid) > 1 and len(cid_other) > 1 and len(cid) != len(cid_other): raise Exception("Either the number of components in the key join " "sets should match, or one of the component sets " "should contain a single component.") def get_component_id(data, name): if isinstance(name, ComponentID): return name else: cid = data.find_component_id(name) if cid is None: raise ValueError("ComponentID not found in %s: %s" % (data.label, name)) return cid cid = tuple(get_component_id(self, name) for name in cid) cid_other = tuple(get_component_id(other, name) for name in cid_other) self._key_joins[other] = (cid, cid_other) other._key_joins[self] = (cid_other, cid) @contract(component='component_like', label='cid_like') def add_component(self, component, label): """ Add a new component to this data set. :param component: object to add. Can be a Component, array-like object, or ComponentLink :param label: The label. If this is a string, a new :class:`glue.core.component_id.ComponentID` with this label will be created and associated with the Component :type component: :class:`~glue.core.component.Component` or array-like :type label: :class:`str` or :class:`~glue.core.component_id.ComponentID` :raises: TypeError, if label is invalid ValueError if the component has an incompatible shape :returns: The ComponentID associated with the newly-added component """ if isinstance(component, ComponentLink): return self.add_component_link(component, label=label) if not isinstance(component, Component): component = Component.autotyped(component) if isinstance(component, DerivedComponent): if len(self._components) == 0: raise TypeError("Cannot add a derived component as a first component") component.set_parent(self) if not(self._check_can_add(component)): raise ValueError("The dimensions of component %s are " "incompatible with the dimensions of this data: " "%r vs %r" % (label, component.shape, self.shape)) if isinstance(label, ComponentID): component_id = label if component_id.parent is None: component_id.parent = self else: component_id = ComponentID(label, parent=self) if len(self._components) == 0: # TODO: make sure the following doesn't raise a componentsraised message self._create_pixel_and_world_components(ndim=component.ndim) # In some cases, such as when loading a session, we actually disable the # auto-creation of pixel and world coordinates, so the first component # may be a coordinate component with no shape. Therefore we only set the # shape once a component has a valid shape rather than strictly on the # first component. if self._shape == () and component.shape != (): self._shape = component.shape is_present = component_id in self._components self._components[component_id] = component if self.hub and not is_present: msg = DataAddComponentMessage(self, component_id) self.hub.broadcast(msg) msg = ComponentsChangedMessage(self) self.hub.broadcast(msg) return component_id def _set_externally_derivable_components(self, derivable_components): """ Externally deriable components are components identified by component IDs from other datasets. This method is meant for internal use only and is called by the link manager. The ``derivable_components`` argument should be set to a dictionary where the keys are the derivable component IDs, and the values are DerivedComponent instances which can be used to get the data. """ if len(self._externally_derivable_components) == 0 and len(derivable_components) == 0: return elif len(self._externally_derivable_components) == len(derivable_components): for key in derivable_components: if key in self._externally_derivable_components: if self._externally_derivable_components[key].link is not derivable_components[key].link: break else: break else: return # Unchanged! self._externally_derivable_components = derivable_components if self.hub: msg = ExternallyDerivableComponentsChangedMessage(self) self.hub.broadcast(msg) def _set_pixel_aligned_data(self, pixel_aligned_data): """ Pixel-aligned data are datasets that contain pixel component IDs that are equivalent (identically, not transformed) with all pixel component IDs in the present dataset. Note that the other datasets may have more but not fewer dimensions, so this information may not be symmetric between datasets with differing numbers of dimensions. """ # First check if anything has changed, as if not then we should just # leave things as-is and avoid emitting a message. if len(self._pixel_aligned_data) == len(pixel_aligned_data): for data in self._pixel_aligned_data: if data not in pixel_aligned_data or pixel_aligned_data[data] != self._pixel_aligned_data[data]: break else: return self._pixel_aligned_data = pixel_aligned_data if self.hub: msg = PixelAlignedDataChangedMessage(self) self.hub.broadcast(msg) @property def pixel_aligned_data(self): """ Information about other datasets in the same data collection that have matching or a subset of pixel component IDs. This is returned as a dictionary where each key is a dataset with matching pixel component IDs, and the value is the order in which the pixel component IDs of the other dataset can be found in the current one. """ return self._pixel_aligned_data @contract(link=ComponentLink, label='cid_like|None', returns=DerivedComponent) def add_component_link(self, link, label=None): """ Shortcut method for generating a new :class:`~glue.core.component.DerivedComponent` from a ComponentLink object, and adding it to a data set. Parameters ---------- link : :class:`~glue.core.component_link.ComponentLink` The link to use to generate a new component label : :class:`~glue.core.component_id.ComponentID` or str The ComponentID or label to attach to. Returns ------- component : :class:`~glue.core.component.DerivedComponent` The component that was added """ if label is not None: if not isinstance(label, ComponentID): label = ComponentID(label, parent=self) link.set_to_id(label) if link.get_to_id() is None: raise TypeError("Cannot add component_link: " "has no 'to' ComponentID") for cid in link.get_from_ids(): if cid not in self.components: raise ValueError("Can only add internal links with add_component_link " "- use DataCollection.add_link to add inter-data links") dc = DerivedComponent(self, link) to_ = link.get_to_id() self.add_component(dc, label=to_) return dc def _create_pixel_and_world_components(self, ndim): self._update_pixel_components(ndim) self._update_world_components(ndim) def _update_pixel_components(self, ndim): for i in range(ndim): comp = CoordinateComponent(self, i) label = pixel_label(i, ndim) cid = PixelComponentID(i, "Pixel Axis %s" % label, parent=self) self.add_component(comp, cid) self._pixel_component_ids.append(cid) def _update_world_components(self, ndim): if self.hub: delay_callbacks = self.hub.delay_callbacks else: @contextmanager def delay_callbacks(): yield with delay_callbacks(): for cid in self._world_component_ids[:]: self.remove_component(cid) self._world_component_ids.remove(cid) if self.coords: for i in range(ndim): comp = CoordinateComponent(self, i, world=True) label = axis_label(self.coords, i) cid = self.add_component(comp, label) self._world_component_ids.append(cid) self._set_up_coordinate_component_links(ndim) def _set_up_coordinate_component_links(self, ndim): if self.coords is None: return result = [] for i in range(ndim): link = CoordinateComponentLink(self._pixel_component_ids, self._world_component_ids[i], self.coords, i) result.append(link) link = CoordinateComponentLink(self._world_component_ids, self._pixel_component_ids[i], self.coords, i, pixel2world=False) result.append(link) self._coordinate_links = result return result @property def components(self): """All :class:`ComponentIDs ` in the Data :rtype: list """ return list(self._components.keys()) @property def externally_derivable_components(self): return list(self._externally_derivable_components.keys()) @property def coordinate_components(self): """The ComponentIDs associated with a :class:`~glue.core.component.CoordinateComponent` :rtype: list """ return [c for c in self.component_ids() if isinstance(self._components[c], CoordinateComponent)] @property def main_components(self): return [c for c in self.component_ids() if not isinstance(self._components[c], (DerivedComponent, CoordinateComponent))] @property def derived_components(self): """The ComponentIDs for each :class:`~glue.core.component.DerivedComponent` :rtype: list """ return [c for c in self.component_ids() if isinstance(self._components[c], DerivedComponent)] @property def pixel_component_ids(self): """ The :class:`ComponentIDs ` for each pixel coordinate. """ return self._pixel_component_ids @property def world_component_ids(self): """ The :class:`ComponentIDs ` for each world coordinate. """ return self._world_component_ids @contract(label='cid_like', returns='inst($ComponentID)|None') def find_component_id(self, label): """ Retrieve component_ids associated by label name. :param label: ComponentID or string to search for :returns: The associated ComponentID if label is found and unique, else None. First, this checks whether the component ID is present and unique in the primary (non-derived) components of the data, and if not then the derived components are checked. If there is one instance of the label in the primary and one in the derived components, the primary one takes precedence. """ for cid_set in (self.main_components, self.derived_components, self.coordinate_components, list(self._externally_derivable_components)): result = [] for cid in cid_set: if isinstance(label, ComponentID): if cid is label: result.append(cid) else: if cid.label == label: result.append(cid) if len(result) == 1: return result[0] elif len(result) > 1: return None return None @property def links(self): """ A list of all the links internal to the dataset. """ return self.coordinate_links + self.derived_links @property def coordinate_links(self): """ A list of the ComponentLinks that connect pixel and world. If no coordinate transformation object is present, return an empty list. """ return self._coordinate_links @property def derived_links(self): """ A list of the links present inside all of the DerivedComponent objects in this dataset. """ return [self.get_component(cid).link for cid in self.derived_components] @contract(returns='list(inst($ComponentID))') def component_ids(self): """ Equivalent to :attr:`Data.components` """ return ComponentIDList(self._components.keys()) @contract(old=ComponentID, new=ComponentID) def update_id(self, old, new): """ Reassign a component to a different :class:`glue.core.component_id.ComponentID` Parameters ---------- old : :class:`glue.core.component_id.ComponentID` The old component ID new : :class:`glue.core.component_id.ComponentID` The new component ID """ if new is old: return if new.parent is None: new.parent = self changed = False if old in self._components: # We want to keep the original order, so we can't just do: # self._components[new] = self._components[old] # which will put the new component ID at the end, but instead # we need to do: self._components = OrderedDict((new, value) if key is old else (key, value) for key, value in self._components.items()) changed = True try: index = self._pixel_component_ids.index(old) self._pixel_component_ids[index] = new changed = True except ValueError: pass try: index = self._world_component_ids.index(old) self._world_component_ids[index] = new changed = True except ValueError: pass if changed and self.hub is not None: # remove old component and broadcast the change # see #508 for discussion of this msg = ComponentReplacedMessage(self, old, new) self.hub.broadcast(msg) def __str__(self): s = "Data Set: %s\n" % self.label s += "Number of dimensions: %i\n" % self.ndim s += "Shape: %s\n" % ' x '.join([str(x) for x in self.shape]) categories = [('Main', self.main_components), ('Derived', self.derived_components), ('Coordinate', self.coordinate_components)] for category, components in categories: if len(components) > 0: s += category + " components:\n" for cid in components: comp = self.get_component(cid) if comp.units is None or comp.units == '': s += " - {0}\n".format(cid) else: s += " - {0} [{1}]\n".format(cid, comp.units) return s[:-1] def __repr__(self): return 'Data (label: %s)' % self.label def __setattr__(self, name, value): if name == "hub" and hasattr(self, 'hub') \ and self.hub is not value and self.hub is not None: raise AttributeError("Data has already been assigned " "to a different hub") object.__setattr__(self, name, value) def get_data(self, cid, view=None): if isinstance(cid, ComponentLink): return cid.compute(self, view) if cid in self._components: comp = self._components[cid] elif cid in self._externally_derivable_components: comp = self._externally_derivable_components[cid] else: raise IncompatibleAttribute(cid) if view is not None: result = comp[view] else: result = comp.data return result def get_kind(self, cid): comp = self.get_component(cid) if comp.datetime: return 'datetime' elif comp.numeric: return 'numerical' elif comp.categorical: return 'categorical' else: raise TypeError("Unknown data kind") def get_mask(self, subset_state, view=None): try: return subset_state.to_mask(self, view=view) except IncompatibleAttribute: return get_mask_with_key_joins(self, self._key_joins, subset_state, view=view) def __setitem__(self, key, value): """ Wrapper for data.add_component() """ self.add_component(value, key) @contract(component_id='cid_like|None', returns=Component) def get_component(self, component_id): """Fetch the component corresponding to component_id. :param component_id: the component_id to retrieve """ if component_id is None: raise IncompatibleAttribute() if isinstance(component_id, str): component_id = self.id[component_id] if component_id in self._components: return self._components[component_id] elif component_id in self._externally_derivable_components: return self._externally_derivable_components[component_id] else: raise IncompatibleAttribute(component_id) def to_dataframe(self, index=None): """ Convert the Data object into a pandas.DataFrame object :param index: Any 'index-like' object that can be passed to the pandas.Series constructor :return: pandas.DataFrame """ h = lambda comp: self.get_component(comp).to_series(index=index) df = pd.DataFrame(dict((comp.label, h(comp)) for comp in self.components)) order = [comp.label for comp in self.components] return df[order] def reorder_components(self, component_ids): """ Reorder the components using a list of component IDs. The new set of component IDs has to match the existing set (though order may differ). """ # We need to be careful because component IDs overload == so we can't # use the normal ways to test whether the component IDs are the same # as self.components - instead we need to explicitly use id if len(component_ids) != len(self.components): raise ValueError("Number of component in component_ids does not " "match existing number of components") if set(id(c) for c in self.components) != set(id(c) for c in component_ids): raise ValueError("specified component_ids should match existing components") existing = self.components for idx in range(len(component_ids)): if component_ids[idx] is not existing[idx]: break else: # If we get here then the suggested order is the same as the existing one return # TODO: We could instead sort in-place using the move_to_end method on OrderedDict self._components = OrderedDict((key, self._components[key]) for key in component_ids) if self.hub: msg = DataReorderComponentMessage(self, list(self._components)) self.hub.broadcast(msg) @contract(mapping="dict(inst($Component, $ComponentID):array_like)") def update_components(self, mapping): """ Change the numerical data associated with some of the Components in this Data object. All changes to component numerical data should use this method, which broadcasts the state change to the appropriate places. :param mapping: A dict mapping Components or ComponenIDs to arrays. This method has the following restrictions: - New components must have the same shape as old components - Component subclasses cannot be updated. """ for comp, data in mapping.items(): if isinstance(comp, ComponentID): comp = self.get_component(comp) data = np.asarray(data) if data.shape != self.shape: raise ValueError("Cannot change shape of data") comp._data = data # alert hub of the change if self.hub is not None: msg = NumericalDataChangedMessage(self) self.hub.broadcast(msg) for subset in self.subsets: clear_cache(subset.subset_state.to_mask) def update_values_from_data(self, data): """ Replace numerical values in data to match values from another dataset. Notes ----- This method drops components that aren't present in the new data, and adds components that are in the new data that were not in the original data. The matching is done by component label, and components are resized if needed. This means that for components with matching labels in the original and new data, the :class:`~glue.core.component_id.ComponentID` are preserved, and existing plots and selections will be updated to reflect the new values. Note that the coordinates are also copied, but the style is **not** copied. """ old_labels = [cid.label for cid in self.components] new_labels = [cid.label for cid in data.components] if len(old_labels) == len(set(old_labels)): old_labels = set(old_labels) else: raise ValueError("Non-unique component labels in original data") if len(new_labels) == len(set(new_labels)): new_labels = set(new_labels) else: raise ValueError("Non-unique component labels in new data") # Remove components that don't have a match in new data for cname in old_labels - new_labels: cid = self.find_component_id(cname) self.remove_component(cid) # Update shape self._shape = data._shape # Update components that exist in both. Note that we can't just loop # over old_labels & new_labels since we need to make sure we preserve # the order of the components, and sets don't preserve order. for cid in self.components: cname = cid.label if cname in old_labels & new_labels: comp_old = self.get_component(cname) comp_new = data.get_component(cname) comp_old._data = comp_new._data # Add components that didn't exist in original one. As above, we try # and preserve the order of components as much as possible. for cid in data.components: cname = cid.label if cname in new_labels - old_labels: cid = data.find_component_id(cname) comp_new = data.get_component(cname) self.add_component(comp_new, cid.label) # Update data label self.label = data.label # Update data coordinates self.coords = data.coords # alert hub of the change if self.hub is not None: msg = NumericalDataChangedMessage(self) self.hub.broadcast(msg) for subset in self.subsets: clear_cache(subset.subset_state.to_mask) # The following are methods for accessing the data in various ways that # can be overriden by subclasses that want to improve performance. def compute_statistic(self, statistic, cid, subset_state=None, axis=None, finite=True, positive=False, percentile=None, view=None, random_subset=None, n_chunk_max=40000000): """ Compute a statistic for the data. Parameters ---------- statistic : {'minimum', 'maximum', 'mean', 'median', 'sum', 'percentile'} The statistic to compute cid : `ComponentID` or str The component ID to compute the statistic on - if given as a string this will be assumed to be for the component belonging to the dataset (not external links). subset_state : `SubsetState` If specified, the statistic will only include the values that are in the subset specified by this subset state. axis : None or int or tuple of int If specified, the axis/axes to compute the statistic over. finite : bool, optional Whether to include only finite values in the statistic. This should be `True` to ignore NaN/Inf values positive : bool, optional Whether to include only (strictly) positive values in the statistic. This is used for example when computing statistics of data shown in log space. percentile : float, optional If ``statistic`` is ``'percentile'``, the ``percentile`` argument should be given and specify the percentile to calculate in the range [0:100] random_subset : int, optional If specified, this should be an integer giving the number of values to use for the statistic. This can only be used if ``axis`` is `None` n_chunk_max : int, optional If there are more elements in the array than this value, operate in chunks with at most this size. """ # TODO: generalize chunking to more types of axis # In recent version of Numpy, using lists is not the same as using # tuples, so we make sure we always use tuples to avoid confusion. if isinstance(view, list): view = tuple(view) if (view is None and isinstance(axis, tuple) and len(axis) > 0 and len(axis) == self.ndim - 1 and self.size > n_chunk_max and not isinstance(subset_state, SliceSubsetState)): # We operate in chunks here to avoid memory issues. axis_index = [a for a in range(self.ndim) if a not in axis][0] # In the specific case where the subset state depends only on pixel # component IDs but not the one for the chunk iteration axis used # here, we should not need to chunk. However this doesn't quite # work because compute_statistic still makes a copy of the data # so we need to make sure we never have too large arrays there. # efficient_subset_state = False # if subset_state is not None: # from glue.core.link_manager import pixel_cid_to_pixel_cid_matrix # for att in subset_state.attributes: # # TODO: in principle we cold still deal with non-pixel # # componnet IDs, so this should be fixed. # if not isinstance(att, PixelComponentID): # break # matrix = pixel_cid_to_pixel_cid_matrix(att.parent, self) # if matrix[att.axis, axis_index]: # break # else: # efficient_subset_state = True # For now, just assume we always have to chunk efficient_subset_state = False if not efficient_subset_state: result = np.zeros(self.shape[axis_index]) chunk_shape = list(self.shape) # Deliberately leave n_chunks as float to not round twice n_chunks = self.size / n_chunk_max chunk_shape[axis_index] = max(1, int(chunk_shape[axis_index] / n_chunks)) for chunk_view in iterate_chunks(self.shape, chunk_shape=chunk_shape): values = self.compute_statistic(statistic, cid, subset_state=subset_state, axis=axis, finite=finite, positive=positive, percentile=percentile, view=chunk_view) result[chunk_view[axis_index]] = values return result # We initialize subarray_slices here because if it is set at any point # later we will need to pad out the result of compute_statistic. subarray_slices = None if subset_state: if isinstance(subset_state, SliceSubsetState) and view is None: mask = None data = subset_state.to_array(self, cid) else: mask = subset_state.to_mask(self, view) unbroadcast_mask = unbroadcast(mask) if np.any(unbroadcast_mask): # Find minimal subarray containing the masked area. At this # point we've already accessed all the values in unbroadcast_mask # so the calls to .any() below should not have a significant # performance impact. subarray_slices = [] for idim in range(mask.ndim): # Check whether any values should be included for each # element along the axis being considered. For efficiency # we use the unbroadcast mask here. collapse_axes = tuple(index for index in range(mask.ndim) if index != idim) valid = unbroadcast_mask.any(axis=collapse_axes) # Since we just used the unbroadcast mask, we need to # broadcast it back to the original mask shape. valid = np.broadcast_to(valid, mask.shape[idim:idim + 1]) # We now find the first and last value for which the mask # is set, to determine the slice of the minimal subarray indices = np.where(valid)[0] subarray_slices.append(slice(np.min(indices), np.max(indices) + 1)) subarray_slices = tuple(subarray_slices) # We now need to determine the view for which to extract the # data, which essentially needs to combine the original view # which was used to compute the mask, and the new view which # extracts a subset of this mask. # For some views we don't support this, so we keep track of # whether we can actually use subarray_slices above use_subarray_slices = True if view is None or view is Ellipsis: # In the case where view is None, things are pretty # simple since we just use subarray_slices to view the data view = subarray_slices elif isinstance(view, (list, tuple)): # At this point view is a list or a tuple, which could # contain either scalar values or slice objects. In # addition, it may contain fewer elements than are needed # to slice the data, so we need to take this into account. mask_idim = 0 new_view = [] for idim in range(self.ndim): if idim >= len(view): new_view.append(subarray_slices[mask_idim]) mask_idim += 1 elif isinstance(view[idim], slice): if view[idim].step is not None and view[idim].step != 1: # This makes things more complicated, so bail out at this point use_subarray_slices = False new_view = view break view_start, _, _ = view[idim].indices(self.shape[idim]) sub_start, sub_stop, _ = subarray_slices[mask_idim].indices(mask.shape[mask_idim]) new_view.append(slice(view_start + sub_start, view_start + sub_stop)) mask_idim += 1 else: new_view.append(view[idim]) view = tuple(new_view) else: # pragma: nocover # This should probably never happen, but just in case! use_subarray_slices = False if use_subarray_slices: # Extract the mask in the subarray region. The view will # then also take into account the subarray slices in this # case. mask = mask[subarray_slices] data = self.get_data(cid, view) else: if axis is None: return np.nan else: if isinstance(axis, int): axis = [axis] final_shape = [mask.shape[i] for i in range(mask.ndim) if i not in axis] return broadcast_to(np.nan, final_shape) else: data = self.get_data(cid, view=view) mask = None if isinstance(data, categorical_ndarray): data = data.codes if axis is None and mask is None: # Since we are just finding overall statistics, not along axes, we # can remove any broadcasted dimension since these should not affect # the statistics. data = unbroadcast(data) if random_subset and data.size > random_subset: if DASK_INSTALLED and isinstance(data, da.Array): if not hasattr(self, '_random_subset_indices') or self._random_subset_indices[0] != data.size: self._random_subset_indices = (data.size, random_views_for_dask_array(data, random_subset, n_chunks=10)) data = np.hstack([data[slices].ravel() for slices in self._random_subset_indices[1]]) if mask is not None: mask = np.hstack([mask[slices].ravel() for slices in self._random_subset_indices[1]]) else: if not hasattr(self, '_random_subset_indices') or self._random_subset_indices[0] != data.size: self._random_subset_indices = (data.size, np.random.randint(0, data.size, random_subset)) data = data.ravel(order="K")[self._random_subset_indices[1]] if mask is not None: mask = mask.ravel(order="K")[self._random_subset_indices[1]] result = compute_statistic(statistic, data, mask=mask, axis=axis, finite=finite, positive=positive, percentile=percentile) if subarray_slices is None or axis is None: return result else: # Since subarray_slices was set above, we need to determine the # shape of the full results had subarray_slices not been set, # then insert the result into it. If axis is None, then we don't # need to do anything, and this is covered by the first clause # of the if statement above. if not isinstance(axis, tuple): axis = (axis,) full_shape = [self.shape[idim] for idim in range(self.ndim) if idim not in axis] full_result = np.zeros(full_shape) * np.nan result_slices = [subarray_slices[idim] for idim in range(self.ndim) if idim not in axis] full_result[result_slices] = result return full_result def compute_histogram(self, cids, weights=None, range=None, bins=None, log=None, subset_state=None): """ Compute an n-dimensional histogram with regularly spaced bins. Currently this only implements 1-D histograms. Parameters ---------- cids : list of str or `ComponentID` Component IDs to compute the histogram over weights : str or ComponentID Component IDs to use for the histogram weights range : list of tuple The ``(min, max)`` of the histogram range bins : list of int The number of bins log : list of bool Whether to compute the histogram in log space subset_state : `SubsetState`, optional If specified, the histogram will only take into account values in the subset state. """ if len(cids) > 2: raise NotImplementedError() ndim = len(cids) x = self.get_data(cids[0]) if isinstance(x, categorical_ndarray): x = x.codes if ndim > 1: y = self.get_data(cids[1]) if isinstance(y, categorical_ndarray): y = y.codes if weights is not None: w = self.get_data(weights) if isinstance(w, categorical_ndarray): w = w.codes else: w = None if subset_state is not None: mask = subset_state.to_mask(self) if DASK_INSTALLED and isinstance(x, da.Array) and not isinstance(mask, da.Array): x = x[da.asarray(mask)] else: x = x[mask] if ndim > 1: if DASK_INSTALLED and isinstance(y, da.Array) and not isinstance(mask, da.Array): y = y[da.asarray(mask)] else: y = y[mask] if w is not None: if DASK_INSTALLED and isinstance(w, da.Array) and not isinstance(mask, da.Array): w = w[da.asarray(mask)] else: w = w[mask] if ndim == 1: xmin, xmax = range[0] xmin, xmax = sorted((xmin, xmax)) keep = (x >= xmin) & (x <= xmax) else: (xmin, xmax), (ymin, ymax) = range xmin, xmax = sorted((xmin, xmax)) ymin, ymax = sorted((ymin, ymax)) keep = (x >= xmin) & (x <= xmax) & (y >= ymin) & (y <= ymax) if x.dtype.kind == 'M': x = datetime64_to_mpl(x) xmin = datetime64_to_mpl(xmin) xmax = datetime64_to_mpl(xmax) else: keep &= ~np.isnan(x) if ndim > 1: if y.dtype.kind == 'M': y = datetime64_to_mpl(y) ymin = datetime64_to_mpl(ymin) ymax = datetime64_to_mpl(ymax) else: keep &= ~np.isnan(y) x = x[keep] if ndim > 1: y = y[keep] if w is not None: w = w[keep] # For now, compute dask arrays at this point. In future we could delegate # the histogram calculation to dask. if DASK_INSTALLED: if isinstance(x, da.Array): x = x.compute() if ndim > 1 and isinstance(y, da.Array): x = x.compute() if isinstance(w, da.Array): w = w.compute() if len(x) == 0: return np.zeros(bins) if ndim > 1 and len(y) == 0: return np.zeros(bins) if log is not None and log[0]: if xmin < 0 or xmax < 0: return np.zeros(bins) xmin = np.log10(xmin) xmax = np.log10(xmax) x = np.log10(x) if ndim > 1 and log is not None and log[1]: if ymin < 0 or ymax < 0: return np.zeros(bins) ymin = np.log10(ymin) ymax = np.log10(ymax) y = np.log10(y) # By default fast-histogram drops values that are exactly xmax, so we # increase xmax very slightly to make sure that this doesn't happen, to # be consistent with np.histogram. if ndim >= 1: xmax += 10 * np.spacing(xmax) if ndim >= 2: ymax += 10 * np.spacing(ymax) if ndim == 1: range = (xmin, xmax) return histogram1d(x, range=range, bins=bins[0], weights=w) elif ndim > 1: range = [(xmin, xmax), (ymin, ymax)] return histogram2d(x, y, range=range, bins=bins, weights=w) def compute_fixed_resolution_buffer(self, *args, **kwargs): from .fixed_resolution_buffer import compute_fixed_resolution_buffer return compute_fixed_resolution_buffer(self, *args, **kwargs) # DEPRECATED @property def primary_components(self): """ The ComponentIDs not associated with a :class:`~glue.core.component.DerivedComponent` This property is deprecated. """ warnings.warn('Data.primary_components is deprecated', UserWarning) return [c for c in self.component_ids() if not isinstance(self._components[c], DerivedComponent)] @property def visible_components(self): """All :class:`ComponentIDs ` in the Data that aren't coordinates. This property is deprecated. """ warnings.warn('Data.visible_components is deprecated', UserWarning) return [cid for cid, comp in self._components.items() if not isinstance(comp, CoordinateComponent) and cid.parent is self] @contract(i=int, ndim=int) def pixel_label(i, ndim): label = "{0}".format(i) if 1 <= ndim <= 3: label += " [{0}]".format('xyz'[ndim - 1 - i]) return label glueviz-1.0.1+dfsg.orig/glue/core/data_factories/0000755000175000017500000000000013752535025021247 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/0000755000175000017500000000000013752535025022411 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/test_image.py0000644000175000017500000000301113605357235025101 0ustar noahfxnoahfximport os from numpy.testing import assert_array_equal from glue.core.coordinates import WCSCoordinates from glue.core import data_factories as df from glue.tests.helpers import requires_pyavm, requires_pil_or_skimage, make_file DATA = os.path.join(os.path.dirname(__file__), 'data') @requires_pil_or_skimage def test_grey_png_loader(): # Greyscale PNG data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x02\x00\x00\x00\x02\x08\x00\x00\x00\x00W\xddR\xf8\x00\x00\x00\x0eIDATx\x9ccdddab\x04\x00\x00&\x00\x0b\x8e`\xe7A\x00\x00\x00\x00IEND\xaeB`\x82' with make_file(data, '.png') as fname: d = df.load_data(fname) assert df.find_factory(fname) is df.img_data assert_array_equal(d['PRIMARY'], [[3, 4], [1, 2]]) @requires_pil_or_skimage def test_color_png_loader(): # Colorscale PNG data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x02\x00\x00\x00\x02\x08\x02\x00\x00\x00\xfd\xd4\x9as\x00\x00\x00\x15IDAT\x08\xd7\x05\xc1\x01\x01\x00\x00\x00\x80\x10\xffO\x17B\x14\x1a!\xec\x04\xfc\xf2!Q\\\x00\x00\x00\x00IEND\xaeB`\x82' with make_file(data, '.png') as fname: d = df.load_data(fname) assert df.find_factory(fname) is df.img_data assert_array_equal(d['red'], [[255, 0], [255, 0]]) assert_array_equal(d['green'], [[255, 0], [0, 255]]) assert_array_equal(d['blue'], [[0, 255], [0, 0]]) @requires_pyavm @requires_pil_or_skimage def test_avm(): data = df.load_data(os.path.join(DATA, 'ssc2006-16a1_Ti.jpg')) assert isinstance(data.coords, WCSCoordinates) glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/test_numpy.py0000644000175000017500000000477513605357235025211 0ustar noahfxnoahfx# Third-party import numpy as np from numpy.testing import assert_array_equal # Package from glue.core import data_factories as df def test_npy_load(tmpdir): data = np.array([("a", 152.2352, -21.513), ("b", 21.412, 35.1341)], dtype=[('name', '|S1'), ('ra', 'f8'), ('dec', 'f8')]) with open(tmpdir.join('test.npy').strpath, 'wb') as f: np.save(f, data) f.seek(0) data2 = df.load_data(f.name) assert_array_equal(data['name'], data2.get_component('name').labels) assert_array_equal(data['ra'], data2['ra']) assert_array_equal(data['dec'], data2['dec']) assert data2.label == 'test' def test_unstruc_npy_load(tmpdir): data = np.array([[152.2352, -21.513], [21.412, 35.1341]], dtype='f8') with open(tmpdir.join('test.npy').strpath, 'wb') as f: np.save(f, data) f.seek(0) data2 = df.load_data(f.name) assert_array_equal(data, data2['array']) assert data2.label == 'test' def test_unstruc_npz_load(tmpdir): data1 = np.array([[152.2352, -21.513], [21.412, 35.1341]], dtype='f8') data2 = np.array([[15.2352, -2.513], [2.412, 3.1341]], dtype='f8') with open(tmpdir.join('test.npz').strpath, 'wb') as f: np.savez(f, data1=data1, data2=data2) f.seek(0) data_loaded = df.load_data(f.name) arr = data_loaded[0] assert arr.label == 'data1' assert_array_equal(data1, arr['array']) arr = data_loaded[1] assert arr.label == 'data2' assert_array_equal(data2, arr['array']) def test_npz_load(tmpdir): data1 = np.array([("a", 152.2352, -21.513), ("b", 21.412, 35.1341)], dtype=[('name', '|S1'), ('ra', 'f8'), ('dec', 'f8')]) data2 = np.array([("c", 15.2352, -2.513), ("d", 2.412, 3.1341)], dtype=[('name', '|S1'), ('l', 'f8'), ('b', 'f8')]) with open(tmpdir.join('test.npz').strpath, 'wb') as f: np.savez(f, data1=data1, data2=data2) f.seek(0) data_loaded = df.load_data(f.name) arr = data_loaded[0] assert arr.label == 'data1' assert_array_equal(data1['name'], arr.get_component('name').labels) assert_array_equal(data1['ra'], arr['ra']) assert_array_equal(data1['dec'], arr['dec']) arr = data_loaded[1] assert arr.label == 'data2' assert_array_equal(data2['name'], arr.get_component('name').labels) assert_array_equal(data2['l'], arr['l']) assert_array_equal(data2['b'], arr['b']) glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/test_data_factories.py0000644000175000017500000002303513657331477027006 0ustar noahfxnoahfximport sys import pytest import numpy as np from unittest.mock import MagicMock from numpy.testing import assert_allclose, assert_array_equal from glue.core.component import CategoricalComponent from glue.core.data import BaseCartesianData, Data from glue.core import data_factories as df from glue.config import data_factory from glue.tests.helpers import requires_astropy, make_file, requires_qt def test_load_data_auto_assigns_label(): factory = MagicMock() result = Data(x=[1, 2, 3], label='') factory.return_value = result d = df.load_data('test.fits', factory) factory.assert_called_once_with('test.fits') assert d.label == 'test' def test_extension(): assert df._extension('test.fits') == 'fits' assert df._extension('test.fits.gz') == 'fits.gz' assert df._extension('test.fits.gzip') == 'fits.gzip' assert df._extension('test.fits.bz') == 'fits.bz' assert df._extension('test.fits.bz2') == 'fits.bz2' assert df._extension('test.other.names.fits') == 'fits' def test_data_label(): assert df.data_label('test.fits') == 'test' assert df.data_label('/Leading/Path/test.fits') == 'test' assert df.data_label('') == '' assert df.data_label('/Leading/Path/no_extension') == 'no_extension' assert df.data_label('no_extension') == 'no_extension' @pytest.mark.parametrize(('delim', 'suffix'), ((',', '.csv'), ('\t', '.tsv'), ('|', '.txt'), (' ', '.dat'), ('\t', '.tbl'))) def test_ascii_catalog_factory(delim, suffix): data = ("#a%sb\n1%s2" % (delim, delim)).encode('ascii') with make_file(data, suffix) as fname: d = df.load_data(fname) assert df.find_factory(fname) is df.tabular_data assert_array_equal(d['a'], [1]) assert_array_equal(d['b'], [2]) @pytest.mark.parametrize(('delim', 'suffix'), ((',', '.csv'), ('\t', '.tsv'), ('|', '.txt'), (' ', '.dat'), ('\t', '.tbl'))) def test_pandas_parse_delimiters(delim, suffix): data = ("a%sb\n1%s2" % (delim, delim)).encode('ascii') with make_file(data, suffix) as fname: d = df.load_data(fname, factory=df.pandas_read_table) assert_array_equal(d['a'], [1]) assert_array_equal(d['b'], [2]) @requires_astropy def test_csv_gz_factory(): data = b'\x1f\x8b\x08\x08z\x1e}R\x00\x03test.csv\x00\xab\xe02\xe42\xe22\xe6\x02\x00y\xffzx\x08\x00\x00\x00' with make_file(data, '.csv.gz') as fname: d = df.load_data(fname) assert df.find_factory(fname) is df.tabular_data assert_array_equal(d['x'], [1, 2, 3]) @requires_astropy def test_sextractor_factory(): data = b"""# 1 NUMBER Running object number # 2 X_IMAGE Object position along x [pixel] # 3 Y_IMAGE Object position along y [pixel] 1 2988.249 2.297 2 2373.747 3.776 3 3747.026 4.388""" with make_file(data, '.cat') as fname: d = df.load_data(fname, factory=df.sextractor_factory) assert_allclose(d['NUMBER'], [1, 2, 3]) assert_allclose(d['X_IMAGE'], [2988.249, 2373.747, 3747.026]) assert_allclose(d['Y_IMAGE'], [2.297, 3.776, 4.388]) def test_csv_pandas_factory(): data = b"""a,b,c,d 1,2.1,some,True 2,2.4,categorical,False 3,1.4,data,True 4,4.0,here,True 5,6.3,,False 6,8.7,,False 8,9.2,,True""" with make_file(data, '.csv') as fname: d = df.load_data(fname, factory=df.pandas_read_table) assert d['a'].dtype == np.int64 assert d['b'].dtype == np.float assert d['c'].dtype.kind == 'U' cat_comp = d.find_component_id('c') assert isinstance(d.get_component(cat_comp), CategoricalComponent) correct_cats = np.unique(np.asarray(['some', 'categorical', 'data', 'here', '', '', ''])) np.testing.assert_equal(d[cat_comp].categories, correct_cats) cat_comp = d.find_component_id('d') assert isinstance(d.get_component(cat_comp), CategoricalComponent) def test_dtype_int(): data = b'# a, b\n1, 1 \n2, 2 \n3, 3' with make_file(data, '.csv') as fname: d = df.load_data(fname) assert d['a'].dtype == np.int def test_dtype_float(): data = b'# a, b\n1., 1 \n2, 2 \n3, 3' with make_file(data, '.csv') as fname: d = df.load_data(fname) assert d['a'].dtype == np.float def test_dtype_str_on_categorical(): data = b'# a, b\nf, 1 \nr, 2 \nk, 3' with make_file(data, '.csv') as fname: d = df.load_data(fname) assert d['a'].dtype.kind in 'SU' def test_dtype_badtext(): data = b'# a, b\nlabel1, 1 \n2, 2 \n3, 3\n4, 4\n5, 5\n6, 6' with make_file(data, '.csv') as fname: d = df.load_data(fname) assert d['a'].dtype == np.float assert_array_equal(d['a'], [np.nan, 2, 3, 4, 5, 6]) def test_dtype_missing_data_col2(): data = b'# a, b\n1 , 1 \n2, \n3, 3.0' with make_file(data, '.csv') as fname: d = df.load_data(fname) assert d['b'].dtype == np.float assert_array_equal(d['b'], [1, np.nan, 3]) def test_dtype_missing_data_col1(): data = b'# a, b\n1.0, 1 \n , 2 \n3, 3' with make_file(data, '.csv') as fname: d = df.load_data(fname) assert d['a'].dtype == np.float assert_array_equal(d['a'], [1, np.nan, 3]) def test_column_spaces(): data = b'#a, b\nhere I go, 1\n2, 3\n3, 4\n5, 6\n7, 8' with make_file(data, '.csv') as fname: d = df.load_data(fname) assert d['a'].dtype == np.float assert_array_equal(d['a'], [np.nan, 2, 3, 5, 7]) def test_data_reload(): data = b'#a, b\n0, 1\n2, 3\n3, 4\n5, 6\n7, 8' with make_file(data, '.csv') as fname: d = df.load_data(fname) coords_old = d.coords with open(fname, 'w') as f2: f2.write('#a, b\n0, 0\n0, 0\n0, 0\n0, 0\n0, 0') d._load_log.reload() assert_array_equal(d['a'], [0, 0, 0, 0, 0]) assert_array_equal(d['b'], [0, 0, 0, 0, 0]) @pytest.mark.skipif(sys.platform.startswith('win'), reason='file deletion doesn\'t work on Windows') def test_data_reload_no_file(): data = b'#a, b\n0, 1\n2, 3\n3, 4\n5, 6\n7, 8' with make_file(data, '.csv') as fname: d = df.load_data(fname) # file no longer exists with pytest.warns(UserWarning, match='Could not reload'): d._load_log.reload() assert_array_equal(d['a'], [0, 2, 3, 5, 7]) def test_data_reload_shape_change(): data = b'#a, b\n0, 1\n2, 3\n3, 4\n5, 6\n7, 8' with make_file(data, '.csv') as fname: d = df.load_data(fname) coords_old = d.coords with open(fname, 'w') as f2: f2.write('#a, b\n0, 0\n0, 0\n0, 0\n0, 0') with pytest.warns(UserWarning, match='Cannot refresh data -- data shape changed'): d._load_log.reload() assert_array_equal(d['a'], [0, 2, 3, 5, 7]) assert d.coords is coords_old # TODO: this doesn't belong in the core since it relies on Qt @requires_qt def test_file_watch(): cb = MagicMock() with make_file(b'test', 'csv') as fname: fw = df.FileWatcher(fname, cb) fw.check_for_changes() assert cb.call_count == 0 # fudge stat_cache to simulate filechange # we could just change the file, but # the underlying OS check has low time resolution # and would require a sleep fw.stat_cache -= 1 fw.check_for_changes() assert cb.call_count == 1 @requires_qt @pytest.mark.skipif(sys.platform.startswith('win'), reason='file deletion doesn\'t work on Windows') def test_file_watch_os_error(): cb = MagicMock() with make_file(b'test', 'csv') as fname: fw = df.FileWatcher(fname, cb) with pytest.warns(UserWarning, match='Cannot access'): fw.check_for_changes() assert cb.call_count == 0 def test_ambiguous_format(tmpdir): @data_factory('b', identifier=df.has_extension('spam'), priority=34) def reader1(filename): return Data() @data_factory('a', identifier=df.has_extension('spam'), priority=34) def reader2(filename): return Data() @data_factory('c', identifier=df.has_extension('spam'), priority=22) def reader3(filename): return Data() filename = tmpdir.join('test.spam').strpath with open(filename, 'w') as f: f.write('Camelot!') # Should raise a warning and pick the highest priority one in alphabetical # order with pytest.warns(UserWarning, match="Multiple data factories matched the input: 'a', 'b'. Choosing 'a'."): factory = df.find_factory(filename) assert factory is reader2 def test_basedata(tmpdir): # Regression test for a bug that caused load_data to fail if a data # factory returned a BaseData (but not Data) subclass, due to the # LoadLog expecting a Data object class BigData(BaseCartesianData): def get_kind(self): pass def compute_histogram(self): pass def compute_statistic(self): pass def get_mask(self): pass @property def shape(self): pass @property def main_components(self): return [] @data_factory('bigdata', identifier=df.has_extension('bigdata'), priority=34) def reader(filename): return BigData() filename = tmpdir.join('test.bigdata').strpath with open(filename, 'w') as f: f.write('Camelot!') d = df.load_data(filename) assert isinstance(d, BigData) glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/test_pandas.py0000644000175000017500000000533013605360406025265 0ustar noahfxnoahfximport pytest from numpy.testing import assert_equal from pandas import DataFrame from glue.core import Data, DataCollection def test_translator_data_roundtrip(): # Case where we start from a Dataframe, convert to a glue data object, # and convert back to a DataFrame. df = DataFrame() df['a'] = [3, 5, 6, 7] df['b'] = [1.5, 2.2, 1.3, 3.3] df['c'] = ['r', 'd', 'w', 'q'] dc = DataCollection() dc['dataframe'] = df data = dc['dataframe'] assert isinstance(data, Data) assert data.main_components == ['a', 'b', 'c'] assert_equal(data['a'], [3, 5, 6, 7]) assert_equal(data['b'], [1.5, 2.2, 1.3, 3.3]) assert_equal(data['c'], ['r', 'd', 'w', 'q']) df2 = data.get_object() assert_equal(list(df2.columns), ['a', 'b', 'c']) assert_equal(df2['a'].values, [3, 5, 6, 7]) assert_equal(df2['b'].values, [1.5, 2.2, 1.3, 3.3]) assert_equal(df2['c'].values, ['r', 'd', 'w', 'q']) def test_translator_from_data(): # Case where the initial data object wasn't originally created from a # DataFrame. data = Data() data['a'] = [3, 5, 6, 7] data['b'] = [1.5, 2.2, 1.3, 3.3] data['c'] = ['r', 'd', 'w', 'q'] with pytest.raises(ValueError) as exc: df = data.get_object() assert exc.value.args[0] == ('Specify the object class to use with cls= - supported ' 'classes are:\n\n* pandas.core.frame.DataFrame') df = data.get_object(cls=DataFrame) assert_equal(list(df.columns), ['a', 'b', 'c']) assert_equal(df['a'].values, [3, 5, 6, 7]) assert_equal(df['b'].values, [1.5, 2.2, 1.3, 3.3]) assert_equal(df['c'].values, ['r', 'd', 'w', 'q']) def test_translator_from_subset(): # Case where we convert a subset to a DataFrame data = Data() data['a'] = [3, 5, 6, 7] data['b'] = [1.5, 2.2, 1.3, 3.3] data['c'] = ['r', 'd', 'w', 'q'] dc = DataCollection([data]) dc.new_subset_group(label='test subset', subset_state=data.id['b'] > 2) df = data.get_subset_object(cls=DataFrame) assert_equal(list(df.columns), ['a', 'b', 'c']) assert_equal(df['a'].values, [5, 7]) assert_equal(df['b'].values, [2.2, 3.3]) assert_equal(df['c'].values, ['d', 'q']) def test_translator_from_data_with_derived(): # Case where we convert a subset to a DataFrame data = Data() data['a'] = [3, 5, 6, 7] data['b'] = data.id['a'] + 1 dc = DataCollection([data]) dc.new_subset_group(label='test subset', subset_state=data.id['b'] > 2) df = data.get_object(cls=DataFrame) assert_equal(list(df.columns), ['a', 'b']) assert_equal(df['a'].values, [3, 5, 6, 7]) assert_equal(df['b'].values, [4, 6, 7, 8]) # TODO: Case where we convert from a BaseData object glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/test_misc.py0000644000175000017500000000064513605357235024764 0ustar noahfxnoahfximport os from glue.core import data_factories as df from glue.tests.helpers import requires_astropy DATA = os.path.join(os.path.dirname(__file__), 'data') @requires_astropy def test_load_vot(): # This checks that we can load a VO table which incidentally is a subset of # the one included in the tutorial. d_set = df.load_data(os.path.join(DATA, 'w5_subset.vot')) assert len(d_set.components) == 15 glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/test_hdf5.py0000644000175000017500000000730313605357235024655 0ustar noahfxnoahfximport os import pytest import numpy as np from numpy.testing import assert_array_equal from glue.core import data_factories as df from glue.tests.helpers import requires_h5py, requires_astropy, make_file from ..helpers import auto_data DATA = os.path.join(os.path.dirname(__file__), 'data') @requires_h5py def test_skip_non_numerical(tmpdir): # This is a regression for a bug that caused the HDF5 loader to crash if it # encountered HDF5 datasets that were not numerical. For instance, a dataset # with one string element does not have a shape so caused a crash. filename = tmpdir.join('test.hdf5').strpath import h5py f = h5py.File(filename, 'w') f.create_dataset('a', data='hello') f.create_dataset('b', data=np.array([1, 2, 3])) f.close() auto_data(filename) @requires_astropy @requires_h5py @pytest.mark.parametrize('suffix', ['.h5', '.hdf5', '.hd5', '.h5custom']) def test_hdf5_loader(suffix): data = b'x\xda\xeb\xf4pq\xe3\xe5\x92\xe2b\x00\x01\x0e\x0e\x06\x16\x06\x01\x06d\xf0\x1f\n*8P\xf90\xf9\x04(\xcd\x08\xa5;\xa0\xf4\n&\x988#XN\x02*.\x085\x1f]]H\x90\xab+H\xf5\x7f4\x00\xb3\xc7\x80\x05Bs0\x8c\x82\x91\x08<\\\x1d\x03@t\x04\x94\x0fK\xa5\'\x98P\xd5U\xa0\xa5G\x0f\n\xeded`\x83\x98\xc5\x08\xe3CR2##D\x80\x19\xaa\x0eA\x0b\x80\x95\np\xc0\xd2\xaa\x03\x98d\x05\xf2@\xe2LLL\x8c\x90t,\x01\xe633&@\x93\xb4\x04\x8a\xbdBP\xdd 5\xc9\xd5]A\x0c\x0c\r\x83"\x1e\x82\xfd\xfc]@9\x1a\x96\x0f\x15\x98G\xd3\xe6(\x18\x05\xa3\x00W\xf9\t\x01Lh\xe5$\x00\xc2A.\xaf' with make_file(data, suffix, decompress=True) as fname: d = df.load_data(fname) assert df.find_factory(fname) is df.hdf5_reader assert_array_equal(d['x'], [1, 2, 3]) @requires_astropy @requires_h5py def test_hdf5_loader_fromfile(): datasets = df.load_data(os.path.join(DATA, 'data.hdf5')) if datasets[1].label < datasets[0].label: datasets = datasets[::-1] assert datasets[0].label == 'data[/a/tab]' assert_array_equal(datasets[0]['e'], [3, 2, 1]) assert_array_equal(datasets[0]['f'], [1.5, 2.5, 1.0]) assert_array_equal(datasets[0]['g'].categories, [b'a', b'b', b'c']) assert datasets[1].label == 'data[/x]' assert_array_equal(datasets[1]['x'], [1, 2, 3]) @requires_astropy @requires_h5py def test_hdf5_auto_merge(tmpdir): filename1 = tmpdir.mkdir('test1').join('test.hdf5').strpath filename2 = tmpdir.mkdir('test2').join('test.hdf5').strpath import h5py # When heterogeneous arrays are present, auto_merge is ignored f = h5py.File(filename1, 'w') f.create_dataset('a', data=np.array([[1, 2], [3, 4]])) f.create_dataset('b', data=np.array([1, 2, 3])) f.close() for auto_merge in [False, True]: datasets = df.hdf5_reader(filename1, auto_merge=auto_merge) assert len(datasets) == 2 assert datasets[0].label == 'test[/a]' assert_array_equal(datasets[0]['a'], [[1, 2], [3, 4]]) assert datasets[1].label == 'test[/b]' assert_array_equal(datasets[1]['b'], [1, 2, 3]) # Check that the default is to merge if all arrays are homogeneous f = h5py.File(filename2, 'w') f.create_dataset('a', data=np.array([3, 4, 5])) f.create_dataset('b', data=np.array([1, 2, 3])) f.close() datasets = df.hdf5_reader(filename2) assert len(datasets) == 1 assert datasets[0].label == 'test' assert_array_equal(datasets[0]['a'], [3, 4, 5]) assert_array_equal(datasets[0]['b'], [1, 2, 3]) # And check opt-out datasets = df.hdf5_reader(filename2, auto_merge=False) assert len(datasets) == 2 assert datasets[0].label == 'test[/a]' assert_array_equal(datasets[0]['a'], [3, 4, 5]) assert datasets[1].label == 'test[/b]' assert_array_equal(datasets[1]['b'], [1, 2, 3]) glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/__init__.py0000644000175000017500000000000013455362716024515 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/data/0000755000175000017500000000000014001427230023304 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/data/__init__.py0000644000175000017500000000000013455362716025426 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/data/bunit.fits0000644000175000017500000001320013502206677025327 0ustar noahfxnoahfxSIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 1 / number of array dimensions NAXIS1 = 20 EXTNAME = 'ONED ' / extension name BUNIT = 'Jy ' END ?@@@@@@@ @"@$@&@(@*@,@.@0@1@2@3glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/data/ssc2006-16a1_Ti.jpg0000644000175000017500000026132713502206677026242 0ustar noahfxnoahfxJFIFHHExifII* p (12;-AEiThis image unveils the infrared view of the famous Orion nebula and its surrounding cloud, an industrious star-making region located near the hunter constellation's sword. The infrared picture is from NASA's Spitzer Space Telescope.

In addition to Orion, two other nebulas can be seen. The Orion nebula, or M42, is the largest and takes up the lower half of the images; the small nebula to the upper left of Orion is called M 43; and the medium-sized nebula at the top is NGC 1977. Each nebula is marked by a ring of dust that stands out in the infrared view. These rings make up the walls of cavities that are being excavated by radiation and winds from massive stars. 
Above the Orion nebula, where the massive stars have not yet ejected much of the obscuring dust, the infrared view penetrates the dark lanes of dust, revealing bright swirling clouds and numerous developing stars that have shot out jets of gas (green). This is because infrared light can travel through dust.

The infrared image shows light captured by Spitzer's infrared array camera. Light with wavelengths of 8 and 5.8 microns (red and orange) comes mainly from dust that has been heated by starlight. Light of 4.5 microns (green) shows hot gas and dust; and light of 3.6 microns (blue) is from starlight. ' 'Adobe Photoshop CS3 Macintosh2009:06:05 14:50:10Spitzer Space Telescopehttp://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtmlMJ (HH"Photoshop 3.08BIM Z%Gx This image unveils the infrared view of the famous Orion nebula and its surrounding cloud, an industrious star-making region located near the hunter constellation's sword. The infrared picture is from NASA's Spitzer Space Telescope.

In addition to Orion, two other nebulas can be seen. The Orion nebula, or M42, is the largest and takes up the lower half of the images; the small nebula to the upper left of Orion is called M 43; and the medium-sized nebula at the top is NGC 1977. Each nebula is marked by a ring of dust that stands out in the infrared view. These rings make up the walls of cavities that are being excavated by radiation and winds from massive stars. 
Above the Orion nebula, where the massive stars have not yet ejected much of the obscuring dust, the infrared view penetrates the dark lanes of dust, revealing bright swirling clouds and numerous developing stars that have shot out jets of gas (green). This is because infrared light can travel through dust.

The infrared image shows light captured by Spitzer's infrared array camera. Light with wavelengths of 8 and 5.8 microns (red and orange) comes mainly from dust that has been heated by starlight. Light of 4.5 microns (green) shows hot gas and dust; and light of 3.6 microns (blue) is from starlight.iThis image unveils the infrared view of the famous Orion nebula and its surrounding cloud, an industrious star-making region located near the hunter constellation's sword. The infrared picture is from NASA's Spitzer Space Telescope.PSpitzer Space Telescopen NASA/JPL-Caltech/T. Megeath (UnisSpitzer Space TelescopeOrion in the Infrared720060814 Orion Nebula Messier 42M42NGC 1976t@http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml8BIM%WIzlXpU8BIM1 com.apple.print.PageFormat.PMHorizontalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMHorizontalRes 72 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMOrientation com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMOrientation 1 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMScaling 1 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalRes 72 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalScaling 1 com.apple.print.ticket.stateFlag 0 com.apple.print.subTicket.paper_info_ticket com.apple.print.PageFormat.PMAdjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPageRect 0.0 0.0 734 576 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPaperRect -18 -18 774 594 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMPaperName na-letter com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPageRect 0.0 0.0 734 576 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPaperRect -18 -18 774 594 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.ppd.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.ppd.PMPaperName US Letter com.apple.print.ticket.stateFlag 0 com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PaperInfoTicket com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PageFormatTicket 8BIMHH8BIM&?8BIM x8BIM8BIM 8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM@@8BIM8BIM[JMssc2006-16a1.tif|TiMJnullboundsObjcRct1Top longLeftlongBtomlongJRghtlongMslicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlongJRghtlongMurlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?8BIM68BIM!UAdobe PhotoshopAdobe Photoshop CS38BIM$http://ns.adobe.com/xap/1.0/ Pasadena USA 1200 E. California Blvd. 91125 CA http://www.spitzer.caltech.edu Distance from fast facts 1450 Spitzer Spitzer Spitzer Spitzer ssc2006-16a1 Good IRAC IRAC IRAC IRAC 2012-03-20 1.1 Spitzer Science Center spitzer http://www.spitzer.caltech.edu/images/1645-ssc2006-16a1-Orion-in-the-Infrared ssc2006-16a1_Ti.jpg http://www.spitzer.caltech.edu/uploaded_files/images/0008/6633/ssc2006-16a1_Ti.jpg ICRS TAN J2000 Full 3220 6000 1314.11547852 459.24243164 83.817861908794939 -6.0142202087620964 -0.0327671586216 -0.00023968074797943 0.00023968074797943 Infrared Infrared Infrared Infrared Near-IR Near-IR Mid-IR Mid-IR 3600 4500 5800 8000 Blue Green Orange Red B.4.1.2. Observation Spitzer Space Telescope This infrared image from NASA's Spitzer Space Telescope shows the Orion nebula, our closest massive star-making factory, 1,450 light-years from Earth. The nebula is close enough to appear to the naked eye as a fuzzy star in the sword of the popular hunter constellation. The nebula itself is located on the lower half of the image, surrounded by a ring of dust. It formed in a cold cloud of gas and dust and contains about 1,000 young stars. These stars illuminate the cloud, creating the beautiful nebulosity, or swirls of material, seen here in infrared. This image shows infrared light captured by Spitzer's infrared array camera. Light with wavelengths of 8 and 5.8 microns (red and orange) comes mainly from dust that has been heated by starlight. Light of 4.5 microns (green) shows hot gas and dust; and light of 3.6 microns (blue) is from starlight. Above the Orion nebula, where the massive stars have not yet ejected much of the obscuring dust, the infrared view penetrates the dark lanes of dust, revealing bright swirling clouds and numerous developing stars that have shot out jets of gas (green). This is because infrared light can travel through dust.The infrared image shows light captured by Spitzer's infrared array camera. Light with wavelengths of 8 and 5.8 microns (red and orange) comes mainly from dust that has been heated by starlight. Light of 4.5 microns (green) shows hot gas and dust; and light of 3.6 microns (blue) is from starlight. Orion Nebula Messier 42 M42 NGC 1976 Orion in the Infrared NASA/JPL-Caltech/T. Megeath (University of Toledo, Ohio) 2006-08-14 This image unveils the infrared view of the famous Orion nebula and its surrounding cloud, an industrious star-making region located near the hunter constellation's sword. The infrared picture is from NASA's Spitzer Space Telescope. Spitzer Space Telescope http://www.spitzer.caltech.edu/info/18-Image-Use-Policy @ICC_PROFILE0ADBEmntrRGB XYZ acspAPPLnone-ADBE cprt2desc0kwtptbkptrTRCgTRCbTRCrXYZgXYZbXYZtextCopyright 1999 Adobe Systems IncorporateddescAdobe RGB (1998)XYZ QXYZ curv3curv3curv3XYZ OXYZ 4,XYZ &1/Adobed         ""   JMJ  s!1AQa"q2B#R3b$r%C4Scs5D'6Tdt& EFVU(eufv7GWgw8HXhx)9IYiy*:JZjzm!1AQa"q2#BRbr3$4CS%cs5DT &6E'dtU7()󄔤euFVfvGWgw8HXhx9IYiy*:JZjz ? 6 Ř\Sj-p3 qeK,+K`n~R+h^ #_ XҠ'a*88hzbV6߮6ws׾(6ZC㊗zSk9[Dk?QDrkH x+Ezbt@ i*_x<@91*GۯӒ«]t%| ȡI$bĴ' Ҫ?v (Zh.L 1dRⴵ\$#VTث\,1!j|6 0;P{bZh>xV0#*1 )ƣXر*ms+sSR4lXjd⮠8@p8pW%2qbP41VWbRĨ;-kOݬUQKn=ޘ5Z 튷MQҴ[G)vb1HTJ끘 *F,F<| jEkŒ&0(BT(|TgDe@%55ň-4acmֽmac\%O_TB#b*W,s5A%h J!'lY!}{dcƆ)^_q 2=nwŁjI1dv*t=\m7㿅1HdJl \)x@7={aJ `Ul,Jb1@*ŕrVbAvRTr0.>R -Hp%vVE4UMbqbV2b0qCUiM>]n*mqCxXVl.VCxX[xUت ܇n+@&YC)v 1V)T\ aPRT =ISXգQJ1d}8Z{VԞF ;xbĕr L-vo|M nN W`Z]E5f<ۇ oVϖKjarqۮEףj{`H+6c}HWC5t` 65<| JQ-k~5{TaRK*N.Q74s#-%AXr- 52-+@*QqZBX L)_Θmz(҇l*C[ҟ*@)o UDX݅Du Y(o_+7Z8a k*qbUVXXPCXUثt-KXUPPe.ż w Aw!bє&[pcmSjPbjŒSjNǮ, ZNJߦ6j1VXlGB1U7*O ^HbB$~r ō$$xЎ =1f֘Q &b)ܸ @E/& ޭOOd~/贫`l o A86_V:K0(A- }5v1;&Jw[BB8P^b$W}8I'@Dj-Zc)B {煍6+_ AXZ ,\? UaPǮ80qBާ8TaC0@=G(!n*qCxVVWb+U®aBpCTv򰂂-n]~?4+-#lPAS+ 8P,PD iسLƤm Cb(Sl(XqbJ"R(h*-bJN¢ɬ LPRU^UV9gXfD= S V?N(W ')s=8OpG~%gmب_MdG텪EG酭waLVԚL![CL: ޣ J2T-"/E$~Q7Ք02nN%n iV l `*"偰EޘZJOݰR47V>f)C0%b4p.|* i[jS| -Iq8h qd ڻPcj ) {xS&nbN([ZaE8O|U,Z(.. biNi x.Z 0LR*ʜWj+B-p3q-6olXDⶤϽ0QjLYb;^|Y#RP$䚋bhbBu+,e:WqyS/lkX&o*B6#d7\t V-.>u ~8uX^hۃCZ0:yyRرDTTֈIXf)Cb|t5&"׀p2WW(GLY嬄( r4KvL,cCuq=7w|FZGEVc$8plhwO-lzV*$bSLXI$J…Ūj~(v*H5F,Hp\T WZ(v*⮮*7xءثTZ8P*<&֒I‹\e`0[8q[Zi@j kqO,/wWmc7nذ%ab*00&שZ6JҾ!"b"X9U uߍ+W$8Zwjqcn# ӂiQA` (AP()`% UXADZ+Y01!* ڟs_ŋDP-tVª끘\jxOZaH.)q@,^EAJ +m-` 9FbՃ @zE%jv"5do~S;< ͊ h6ZŒ`UT oPcICDbP*J 4;v**U1CWb%Kc&WbbbP;ro!kdqV*qp\V$텁k1E/ᅉ-W4mXi >HcJJe ҿcI0bP)+㷆cûWTPSFR>h%a2MHs֪|REZT遐G UZW^/+YzbCֶ@|RNUrm…IfV̌Yh(?\mD@E )げl”mocl @|Y[pHEȲj~8jtƇ5+(CIS{bš\*aNM)C|Z+b,Nra|CS w5u1Zk Uow**Sᅉk7W TWz`J7$nUإءUb+ow\Upu+Zڜ*1WP |( (hV+(ɍ!8FRekd[ZI8>8̲F$R wa!^ ?^E^>6,1O&ԅ!M`KGLUR `He5un)D ڬjI$ԓָ7֧i;A-[pJbi:h8LRQ R!V(7St`xb"95"r- ;bJ-J"Ӻ8iARP(++z rcAZة(I[$"公;aV*(1d=R RS\Q*NHH%eAXՙTAW#ʼn[[1KX[wxabZ]LSMbueȇHGG]n.S-]Z,]\+m uq[h(.*[{V늶0%@Vҧ R] QVbO\Ump]ʣm㊴OS_sl16\ TݍI'H  &(zSrp2ꖁ@Mzv6jiE : U-Oۓ:ab⌅u*3M m>%oRq1n d*V`@aU$lGOP#xtZjMdvYG> Р`I?ڝ"F{qeJ-_Tfj|*PōAzR#[UQj9/sR`drN,IC^pZ0䆠E2JgpܰOL6YF$ P.  SUm|aW`WS .="pa Mۮ!iqWbz{Ajs^-ȑWRg%'KbW41[ݪ2!oP4-0&qip:vR[aEƔ:LAZpkTGBAlƋ%6ōWz "0z tUp3w *FywJQb<ƴ] 0ܝk @*Ć'Ḽb X+R{F(qMS~jف%m`ZF*ZZCUt 'A-D}Zt%d"7P0SŸ)1&EE"}[Řcp3 .I>u)uW qku+Zh+MQNoRqVL)Qi^ر%LZ녊OAk*Nhi VbS p$Q=|0+j1VWWCaI khacmbb b‡Aݼ1fN}'$FGp*ZAg5?,XRw^˜E…>80 >SJy0$ń85ӂӸր6&]E,2<7&SlUP ۯӊL ށQE€[[)*\J7qbp2 )P(kv*Šhƣ C`) SB: uʼn[*冤kiWDֵʎئWh-(? k C¦$\[1ciXҘA`E4|JFS@m"B \]aB ojӊ0eyP5~C' QqU5]@7rHK+rՍ~.Fr݃9*+~YYoCҺVwUPP~a;4kC`2Du0Ajإh@M)y,8b+(%@5aC_D$ #F[qJt> ߦ\xc@Œ5vJ R+T+7₰%zV=)ik^*Jĩ RʼnR&W;D87#EI Pc@HS Qfl, S ,\N6.M&o WḆXTHX{b(vN*q[vp-Wm*qQR{cH$mb(q8ڮ[Uk  Z  neNc5[)pl kœG;;|Uq4*.\-d)q8XS)v*XU1Sq (p`SdĆ:bE1ZV@H,UiS遒6GNM҇vӭ1ZD(t`P,v+KIʖhf# U>03kƧKxbȆ7WX}xATZ ABI t<v]Zp3(Gm; X;`JN`fR8r֜kJW5[Q/sQ E2,ʃtT4PsxbG,ŏSւ (R+_ (k5 ]\Vj \+mW[nSPi' OUmءإء]\PUUa9m:b[)oa[ukp\TҘ(&]Bҧ pSҺզȶ;d۱>X!<k1XXiz SJ;`eª1f"o mФ(x0oo*ѭv*zm:kdEV82X8XZEGnW\V'0C]IP`RvBn1BUX!Zcġ$bJߗ˾T|}4qiTb,cbK_kl([ASSMS.LV֗ZءUi%P)CVC*qcmaEk61P Z]SJh=dZS 9J`AQCmGŊn;`l/aE-hk,ip@{bTX-<*ƪ=p[ i ȯPwxiqC%l TtcKhG| +qd4WR4Fxb#*AFDc$^`XV'clxv^Bta^D^8"(E |qd7y 8 bL 3#Q*쨊L*\n x RnPB₢^)*@8X W|Ppw4'Ƙ\zC> )1hFOlQK#,SZmiEb Jlر%alPO|*,8uq[uN+nZ%5b"lZ[ L8EIDimqcn*k jR v;v(v*UsuN(>X/8 ,!ߠ0-5LUت])\.`lg,i+#Ux6wq(,`&Bk V8Cd4 {5)_vw*1KD5qB{ ) bc5ۮ)X-[,T5㷸G% PB =[q6KWn!U"߉}*U0>])՚O+qeKƜh0[*FkZ1 JAUT2ajO(愸g=pUMFPzbZc<(&Kⶇgރ6#N :%M+O%4#+NSS1Klp%JI;<`Js]akaAS>-lUk1ĕ,mC p߽>xZ[xiiĆ;tثWb*,1Hh↰k;+rfp2T\Y8 wQ ŕ/f"ҸT.%rv+M}2 Z*,McN 8R W| V=}6GB~xRߦE:U`JWdIzbPإ;v0 VUeԚEPEi*Q]' }I(@!]/Ar: (.;`byaT9$PackkSSlY(>ؠ\,mmm&j.ذ*.v15ֽhF,Roi6|&+N%X1MMqA-W -թƑmqkA)Šk-bR v.S;$5;WC(xx$5 R`f, zqV袻V=$ b@8 )YVzWaR&R:FNP`E-bPVzv=1cnRա| W0%ww#ª`F,XEOܞB"(+bQv*إQiOUV>cڸA֝1[k%y?߷Rp#V=2A-iӹjjJo-Jʣ| vC&=:+ъ9b*Ȅw4YT•2Apv}8Pv &6qU6MkkW 1豞XrrMr++ZI8ZjQmIuqRZf$z< bFPTW+os&݁Z# q$һP| S2v*UثqM[Xء76Ed*.,)[U/axp%TY**܀+O5_kA;jXl `\ تiQJ)bZ+J۱Ҵt"1d~}UApRGZ A) AJ@p*WMT68 R,Sͷ65o@*BG#aSOiRS5k+%WK eIv뺁A H㍪4Q$+MUjl+z$5G,b?j*k2I{lTTji^4~!TqJmNN~J-"B=wiaCa+ħ#WĨ \HژTq` Nq5i>PE8+pv(v*** * _lUثgbEAqp3jq<,hiZm %lUUثqQbb_, ,F*N(\k-WaA[iQ)^8dURTqJJP s_QHvQJ&34CZ@ i\1aL 0wE)TRdKS@*O|V߾DԤ@44J %~_ᑦvYܒx~l(I&Y'~{ P`"ZՁwSiyiC*EH o/H@4TKĒōtŬ#c%OJFcImho\bbA}AߣK~?N4d7W#hiG"Jk!@tcm=4Km ӦN AiucZl+Pc02Q3ԎC$Y$3bĕang5 SCr i8 XX2) R\V,YE d GQ=Nw"v+P-8d m V"0O Z45q=z\K=Ƞy@ IE5#t|Q*_LP}_o6F[*,1cf Ő ~^+!c:NM;wx)K7E!E qHUrI %j@;d4:adS$CENö*Pl1,-LPJTXۄxocmL1`J˾,mɺ}GŐUV=3 5r+qUt5Œ&*ⅬH RnEzb{SCM1 kk"qҘщ aM]7*8Y%'lSk`m2@C 5?˷ōnEmM(~XC]y =5 ˟kΔMǘ7mB)"9= ;GN-ko ԛP+YDyJ,rGDWaP *H,:lD-彂ܬ*v'HFKq09J Jzӗjdz%*V݊OH\z5_J oabM-S7)nH')"qJĊc 02v-ŋ 1CW{TU-6MwmkVtL6AhXS|M+MCLP1Zjثaj6=M wuO;RXإUثXWaWe.I(j {HdT&jk M~R)^!tmܼp!p') xE6޻W UqJ > e$%GJnp! AO%;yaF;Exyo@PĠ{b"1ʴNFfyTӮ bgJ>Pu8)#tY3"/?uL1ps#I<.r@Uk+!Shˮj KC|XxĚ民kl(k6 *١$aR*(\AX@H>8SJv)JubKU\RꞽqAp!48U-ᦥqD&!b,LjK*va%I[b5;W`KWbW\ >x]bj6 SʜVbţLP]bTU YS`TH ț []M-({aE-"[J)rt遐p d51Mbىw#,VZ=Osp8]ļÌ$01QՑ/$AA5Y4B^ bRvtlEz W QNIF+]'xh(pXjV2hE%Z#*4NWuCn§f0ʖiI=OсSpmM䜭vqWWlኸm-8mxz02zݱd \ pn1JUF)S"Jƪ*#!zB} g:`gj^PwT-J"TBIؠ l!B|qd;PZeS&NU]jP 'j`rƮhVI:|<ڸOdou煘qj;N`E5LVR:(Zk)l~.煏W_lUmqVuv* ӦKqK\V8:vbCXOp[]Uf?T- XP; ][p4;~8T%S0-5uSBGo&e:v )v*b4SLUӊm RI1M. 02EazY6)WYP Y-t( (,Y6,VTqRHI]m mzLV]RǶیVSGa!{HҌשŜEdF=wH &y ֤/#ZTƦ*z )PO▎o|Uإҧ)Xџ6T@#6)aE6i P-iPaE:4qAjXCWqb TW6o)XȃQ1[h↱Wbb.[}KWS8 Kx Xs4ZDniޘ 0^D)'bGlT:`M?NT\*XQ+7)l] RbEAq.\RDF֒1UG\XР)1 6$7(OjP#c͎`hB)UOňe"02=O%(K ҿ"Er=9Vp69"Rȱ_K8ZRצJJ+  ? Ҍ;aA eM+U TǍp2ܽM*qKQKH N%6ⶸa4$bG&9%. ]ul Z%09LVڣH/SZ® PPJCŠ%ثxX{Pa bvv* v*VQR0$m<.qpHtq+KwX5_܆$⮦)\]n_׈*@; zznq]U¸o}cqZrï^qT遺iYqf*p&W 5)YLXb⥠|qB↵o۵qE-WIX$z [,vO02w15rG?NRB$m^$@cwfܱ(  "vŁ7:Г咷Ըف,+j.a`VŠkiu|qVmFvR$fXojXءثWbm -hbUءWb bЎǨ´`Zu+Mӧib큕7PTlT`[1hjC)\"ŐqU8⭀)+Ep!m;acO܆V)\]LRxث{VLJ|)\JV޾=HmZ ^bɲKrĈ#IU HPa-q$+(끝.ڟŒ֍NRTbW%Њw4V䣣 ~?Ȕ  ч8䨊ŃOP;|ՔT7npcƉR"mI ^tSs#iZ(c&&a_׾ s) jȱ*o,ƨ~׿\-U[Z§ŠS4=OZw)Q5ƝF(R´ abƋJ֟zW 7[\V\ mpLP*NG mO~iZ߅b8 YVFy,KCQ-wlXJdSv*U1dtʛ);b 1V)v)`VW{⮡&s0-ShUP䂹[zO~T[c"lRx41Ku D0_8 ч5+QR|,Y~X^R6/-}5.TBV8)$aR1M5S Uԏ$Ev遍6Ȋy՘TuS_+-|F*هMi`)f# 8cZ*Q_\6rtR UOıw )`*}8Q@2FA3+U \IzK/QTO&4 Yg 72@ԂBD*wvX Y(;AbJ8-T^ Z'H U+iquڛh1S v녍,48P˅ CqZTҘCsQHP~ vq Jl9Zj*iԮէ-2hTw j}Xp~ziw+Sa4qR*,O2,R"ÛR Xw[.m2}9Xq,,S%7R)5;T\mD]O : 8M+Gq[v*E78mF*vœ`L P n̠J`6Z92-W}sY*v‡em͂+puJQp^\{WqZnmXb)l Mp2 1fNEĐbfBQU[!%(sF ¦S(!kC*)Tzb6n&-v -*h(;6@/ WF}=4xʼn  1*ZN*;N?͈c2od)CE`6E$r FyJpśf큳hNqmd}L4§|I G^JH".AS\T>֢SC(P^AN+Ğz|]rAB9aZk, Tb*桥639@rLmx\eKlRr_PJW iZp݀4;⮨4⮠"wQaap.(- wi|qZv(v*8V՘(+N([LPCmo1U}-M()68 Y1V\YJh‡U TgM0[Ɵ,Uֵ=(|r QI3-An+dGk/$ (Ł({X#l [X&QHZ{〵"AjZ\ѧ`8kAl\v9i8ro^Z$'taPFHyO$`zqۏmC}&=7b ۆ&>Zqqe'r; )OzLOM 9dKd Z0:dxZpԭ h{*ܭē,is_J1D_d b h%xFA5=G-PWz @Rm TɯLo{W"=^K~Ć&;&G Sच YRR1AqF|QK(pbE2[L,iثwA+kie7='!mGz #ql!ҔH?$RV]~#r}LnH7& Zr=7rAbCJb)%2텅cVab{#|P‡sP +Mw _d`e45ץqPW JW%I=i^[ ZE78Z!ݰqmƓĶ vk {`d9;KG jyjxuT>dWsJӰ+1FASӧRӊ|P U[( LYBp2w&qN)G*b<m6068cP1QU`UOlYN>Ap8E#Ř*aO(m/&1KiyDRq,խ$̫p5ӧ:q!ɭ2*,+)7\ CWq4`Up'W=2UI=zf ŘD+%ᖸZX*qVثbU܍)'zM6k4 TA5z8pO~h;lR U1Jӿ_W `d(Lx^㇄գ__m +nq'U"ӏz6(1(X|GRy56ƷD/?6;oݔh ׉=w.H%-#*RIH,&y :>8 O{ ^Ė,19+h%&^2FJuu$HHm^$,GJ e2,UXTH>= dW)؇?ӷlSVM P:M#u $)fKhF)UI5ڀ(<SDPO6U1U\XmL(n;p$b.uR7# AZGP튕,W*Z `d P|Xo^ R ibU4X T ; , 8PZ bmo)T fl7ȶ I4N)(b0p mb)v)vxQ4W>8d ं$n[qM2IAlH+TG=@T`f !WVv.b*<ǟq5r ־6[;UDGA^+a +hTfd˱l)v*FWzZI2Ƭ(wht^8Ph-`<04c Y kׅviV(܌[[%OMJCV%md5ALP=0hFk]K|@VE<1[k'. LUK:b\~AqE])næ)%h %m0nӊ*h@A^-ZTrpH,sh->Y\+qM8KaIŐ ,br`GFهąofVCx#Ap қ|M/z oS-i~xdSӮ,pB0&;v) Z+#,EE*FNV ҄'9&KZ0ȶ" Hѕ[޸EI &ƤMRՌƔ; XdP:bF VvGQ6[Q5+˃o~D..$us3/RTӗ*UB<-:̚sV/nv/Ӱn1SQIk(&IRէ*хoo|**Bцu vcLV4sEQSV~ȦEiisi5A !]9&j~XTnwzrrW=ƑTkV6QZ_L\IcHە0ǀT/lde5j!xE 5 8-|d&bćzn{b+&Ӿ6-=덯 b)<(+PYj-i_th4|LRAjQkN68'[W }8*8Jb+n@Qk&~Rr A텉 w7 R1[\L+@Wm`_J *7d 6f 8X=K1M*( Wpŕ4,Tu+YwL FU84)]xwҿF*;0%o2*6۷s*FۭA=B2)&B>&n$)"ÜT0_͌WLbbܣ~ln,Y,6GwHءD}~ nj2;ح 6Ӆ | ߯A2-+SNY5=|(28{xcj>HM)8Z?N(൦-XU!zrRv6wM*oV-|R@;`]Ý; *׬B+ S~R,V \VLXۦ,q[ZGLPJTuI[k@sHSu?b0++ U) %끐^02 ׶)h\Ub)!u7,+ojabB҃SCZULUfx6v*~$Aj;`fKDu %UC%._␨݁)JUc^v)(Ð5*!Ѿ @TUb#< ӑvJ+=%@ČmI,i>m H1&_ Im2 >XhEŘ\Cぐ-JGñF)B\xb!o LETxRDXӠ;5;_G2?qƛ,2+)]¸Fq^Ò^&0P5PV) PGC g؜SKbYHycKJK(JUE-7SALMG9"K}HկaFD2MY}4^4?,i | Ex०WH+gJA]00 a F%$ZJ x@vLY;?KY߱qdF.≘ m𱩭7{|ߚs52v8XNR,V| kzbB^cj eiqN,]D#bje٧răR1+ #t´mim<(bl+""SVH6そ:S,R*X>XXF6Ӧ+Kŕ[Ei(0!rV+`[[SaUA4!=GLm+B!Ħ]J*+0*b*mSbװ[\U/ Iŕœ\irxK \IWvl@iBlYZ8Z [Gir1eKjp&z oҼKNAc`h8AX-Њ-r@WO*Iw'iSҭ (fuZըz2 W@r]' Ԩ|hkUB,)hvW~u* '.j &+$u@9N ,UMGP(w7!] E{b̴B6<`&߇LYZӌ$WwQ${ X7d`PcPx)TPGD^lV6Se8ʖ2W(iӍ*e(ZT< ز\aح,0SaL,HZTb,h8mь7Ob]Ÿ > Jm O݆JobJE5viD2) N|0W*\U($sKTJPbwR4me{+nn+aVp2\ 5oB+x`flwhiڽqH ]V[ڬd*l{b!xHlQzSj`l1Z^ E*U@bdkqHD[_olUWkFjlzm2&o|Ui) XoF!+%iN hI%8+ʼn{`dA"a>J 1Vp0%^+$ {u2e)ʻ|5asZlND%^,-2.(ե~GrM\-D;,J*+ASAZ6j8:N(zxPT'm;ŒTc|zbnX(,yaژ+ d`d ǰKf]QnZ޸P2 bK]Ei\(Rf۩ʼn*2քbąi3Q1ƛ}fw S2|Gl*b5J]R AK1”+LNĕm]bKWN4̠VM+Y: (X?%a&E0+i 0 ~Xz`K^;tƖ[ abq+}{DBR Dw@+|!Qg$aj*)(*7ۧl *f Y 4덯wR,n ؊A9.R`J>PNVw`Q\ T SZw+bBE.O:b+uƖƆ;X H>8PBiL#)q%7,߶.# j74& BL yl>C dM#m$(@?,frh%H@+uSȒ@#6oK rrXGZRS%߼U}bT׻V$X" l#bBO[H۩8Dc!? eitڛC "jqVY{UHߏ&­BvLOJ+R[ءY(-_k-@E0 (,Ȼ=) t R +}Zb䜎(93l$eA':mP%Z V[Nà늒W6qMڝjOL iC3|/h>Q^vL Tm.Ao^QH58X,G˚&@[ y?$h,N(.DP#mNث#۾TI8"z˄ز+6)#RQJ֘Zw >[ P"VSU Y\1_F${ Km(!B1b֣PR,!j<~ [}X#lY8 +43qh׉_~׳PsCJY(GQgjn n6젟IJc7,„] ѵʔ F^Aizu4<[jő.WB{`dI5!' 0Pv8ȵU؀k-WΕ_vRԱ ֝Ye<|:bcP@4>hYqXP0=OTEZn0-*FOMb <0p(g(ķآP+ IlpR!xj׏  oŋj u!QTLS[k'-3qQn#i)04Zf¤ Y7L,id(YOAQ;l 6bS=ih+Khk܈ʜr87~#!.UM>Xl1[\1HU,t*qA z }8* p&J L ] O|2D[7& %Tnee~,:ACC3u޽qUѠ*{H Ir! +?@8 PHwlX7#4SaB0z',E*bT p0 ii]]7 Z gLVcڪfޤ a~iPqp7 ŕlSpA5>ȯ#AaO.zT iȧrqAtj~EKn%:; M*'Sb:Y N, zF6ʧ*r sY \Juԧ_m|eæ)T1=EF\MLQkQi\ WUL ZBGFhwƐ"áb+*} Rc 'jxXSbrیPUT# "cP*G[K3t&\PJ6uxcn Pfƻڸ[y8q@?64!$o´ L JxCOإ;%hJ큜B!bTOӋ64UNcANcnLX{~ɠqchw*Fţ,Bij002CI"Ԗ8'u҂\<ڪ=h) |_Hև ,]r)CKk֕,i``:ⴳ*0E)ت~`^%IX,bq-kVUIʼnpWv0btbd`I De6& p۵Q%Zi,q.h)*q|SKV/SV4vqI4_ )slR=M8&qZ\V3B"6/5k$ Ҿaɏ":Wd*PhJ-O_ +JHHM p_tڸy E:{⫨I,dt^hQ0U hIn"45{{BUWBBv$[eqZC {$!:&d  @aC^xZc@IT2k`M!o)C\,iU8Dƻp6֧+xߋ[a*w;8>X%NBv+ -aJ8p,IP>*ao櫷l @qf]ޝqe]T \V*T1Z_E"1eMG͉ u!%}8xzw;+x} ҄G8k$SOc٭Uh뀱(Ȩ? Qi$iF'0ϷlUiۦ$P_!AOW{TH˶=bScB >)m~Ocm`%4܏"EMwu]b )tgE{bPS2(7nJ ~*1-Z xibP8jf1l Qj_I.H?r_ZXE"Ƙ-6̍kVaO#><~.8 4%8JZSɷ"6튨Gn zv2)MRu2\aK*`fB4%ڈuU 8XMOLTaE Y$ YxHIŕ)>AH6Z Z}PNإlR- UAI*oEXKv ʼOa#\^ʜXTZ LRM{REhU)H0)n[cFoV q 8#!ao@{ơ#1Z[ʒoW<ǡ‚!, #><}Tw~ :I۾)lMpW%IֺRW۾J2ÈG|LQ>va"S\i pֈ90;*Q۔I?'B#P\X]LQI'#*FC|u$w==H rwZERּQj[VNY=7Vޠp#r#O|*֘$*zcjCՎkA{bQOio(֌7"*U14$qm]$ǎVVO62Tlx`BPT 4P(ALUiO*TJbJD:bV6mMkr1d*h,ʼnZTbߴMI AR>. UZ7 "R.~<u{b0)i8UUXȝXsk4(vQOlY3$5Jçӊ)_Y.*d*)\ePumhcԌP;lp_cۦ EH!P*eŒn t.+= yQ,o MKg\iĒE7Dv/( +SQĨ~Nd M&֖Jm$M,mDZ T8TQ;4ȍSU$㆑jLۏmR\ p!12L *oʼn(*|;5UW{S+ \rZGq ժPKdw$jNǗŐ;RJRlY<|HI Uޢb1BF >XfE)p1gj Cd{T} n!wwo($0߿|i+ t`GŘ(PָioO(Z߭79 .o裠-r;Vb;wQvЀzv,@W뷸4Wc@:h*M*S*R"PvabJp%a8X'p╵/Ai)spvز+9W/!x@6z "H|rpnAGWTW0O ZT.)TXy>N^40V` C' xǭ*6}.B=)Vx9N8Oф2D9C퀱R2S *)UR1T]\FPU?잸) JSWdLh]a٩%Pm)>N$k%T'O݀*ҵcl E0*Fjy,@hQRVd?SX-v;rIw)QQMΙIu9MoAq(@K-)'|S $Db05ŝ)nG0-_aEҽW" {X,5cQ~Q&)H gS*O_8.7֢:xa[l- cAOzRF` NhZ*ћo2V5 ,jZ.إ@(P} /aHo@'uf7dZq 56"튡.%瀡JYh1RT}N3|GY9V|i(bƴ8ZZMv‹Y7MV8 UKŅERO,܆[kҘX O\+j02$Ӯ(\:. genOpF5<Ѱ3Vx8s-IEz}ݡ1V4'ƒ@e$ ϏRe޴TBJjMk㊭u,~vT2Kj0>ڴ|PQH.ŊroJbD$Z(qbJWJ{b= ~Ib J(lQ)JL!G -2!U~$5PB]\Z|C'mce1v»2!-BÂנ~4LƵ\ -co867%1h9&?͋06rAҽ~XY4# t[{ @8%p5)%bKRAZՏ+7輺J \, >$Jc\,mqM [ݢ|qMn([#էjʼn*,]r G*:ZQsbI[Ol0hH$WbjS(XibpRp܏AE #8 gG%_#MƂԔvADFkQ(a qfcCn?F!:1bWGC@1B"]/)8ol,T[>LVbIrx`M"yuMz[ipaⴉZBx ,Go? 0%xeK~w" 97‚KTn&SO' U?|ŝ!d< !_L5W k)S ,;T`JzA*u{RܪѻFRE4e=AĊQ E&'Tφ*RwŊd#1BK J CU>s+_TXwbE7=E<1kSp;aAw0*LNMĬ7튿^K\juez7Z~KX6V7ͯOzZcjМV,`:b(@Ev>l Pw{4 u=70! At5w#)T(9 VUnإTJ V E+aCk'Z֝*KPU+Ȑӱ Gõwz Xk I  ,T" CRO<1b(\hC.q1ngLl~LmD9R9,Jm쎘FG,?9 +QMN)( (8eqRwjR\Z L1%4I$C3ኡ~[P^\UjUj-*wOU2TN:`fdR7~bZOPcbR'be#O&$Pw^ `}ZQA}nE(q2-nCFPmiS60܀D Vt;1¶@7.‰J/qmqC\n+MI0_Sld̔%bF\UN)"AKdrF,PMkb-Ct®p$YYYF% h֞8()#|-D( !Qd\,Xc!L-)NjBŊ| ^"aoD[4[78 p+ RǮ,TZ]$q)qM"B0c+N r~AV8)W!\U+E ;JZ L!+oRc6* '+4z``ZY1bփB.[eoRF´l2VfmqJ+e`+%tLw:cJ^H\=ziĐB!n}a)K1,@@J&_25M)uԔ$ZJnsCi =xc:QMx߷Nk{K^IR7c1MjNW9+ZeeSB7=H8PFn8&x+[(2%)nQw8P+ٗ\?'6&}9tiւz ALw.Ku)V౏l [^G6{⭂<6m=BI5& ԩUVF;b@c%EO"ŏJelHjHL toSኪ+Qnw 7'-+r 0zMO94'o,PIoس$=2A(pJLCA8-2\i7Hyy8⠖ dvR8\`im'Gȝɥ) t(C(J{DT|( y ɱ!=1EI큐+ۈ`A+:6@' H!MűM 煐FN6wZCvl 'qdKd</c{؇ & Xۨ$qHQzo+Kd%=nT΀W(d% 1۶SSŪ{a@s!; qaBIqbJ-kL(, *MC[A4#qC\EqM?ׅr3ݪA%ō,Abž,qVlqE.X4"T',IB#ů m iLB-u8WYG/`~YueM2 @;U\F`:₵۞%d҄PŮEHO]p5Sؚ@u텒*;ҸR[Z 0!N4={o HJ~بD$d`h\ҽs ޤqN+@tdI= a^)a(/$OӑdUǓO50J*7 )2S1%zңb lN^$$<1JKB2A,1J< ZQno ۦ $EOʶ POoM64+֣(.Q@j:XRSEBD}6P:=k V$NY$%NctR106|NݰڑKm6#ƾ8 Iu[!QֽqKvꪯ%v8C+Ӯ+q>Re,H&ZRszxd3+ IQ,͸B撢+k<1bTPץ05+CJ?䪚1LP '] Є+qgvF1;Uʬzn1E+,&Ҫ:^c8nƠ Y3m~W@UI#!8C.Euk  HVH\ |}Rj _-;(8-׮(^4nBŅ.dG{*L4\Xe+ָ"cJ2P ,J‡)EGZуW1]BBA#ZJ{)Dd7H]U߮,V6BCS>źW LbMUq +~)Zr!"nT!R L,HWDGT C,Z%@A,xZ)6_):h;ǒ?L$wSj`nVTsqA(vv‹R2bոxӺlV,T W p'ҧ:XǮ(!Jk*Mzl ~V(Vӊz;⅄ө =qBK-1AXFsWESl{u03 T[uHIxրt Tg'4ZیPRy$AlU_*RX iKMԠ,Ik5o+B[ޝF,&WpeTnjrؔqVćm| dPo`VmMy6rLҟQckgl8ꧨH(H] @Ol)sMRK[%ӸQmP VjY7/aZ~r^#ԲqeitѱxvŬ  A$1 X,*"/aۯ~m+=7U ŐiT SF>TyЮ8U]D ULUĚb4bL 02i H)`owJMIOCS*zR,$ئR;X& x#@/ap>aMǷAgޝG6|{Ef[iPh CN 12é6tqgv@6wWremVݍ& EjVF5+dI ,-Vf>]QDO,j)w(V5 |i!cĦҸCTL7\,xf\SnGL Uwŕ ^qEz ڑ8Q% 'nزV *//l(j3څa遒1ejA-qEic;ↀʼnUUi!1Uh;⪃@*#*2,"9 tAlhC0!WS W20|iu|^$遁K8v 4n,V1OR AGŷxaH28(A kq Bts3ƞ'XB G ;@(Qߐ6U}dNtwf^¾jE̕Z^_V2coSၰISCOCœkWY 'ª9)b:,ouBEU (jZfoýecYj02!@Gymw= xliDu=0-tV{PqCOHo`7~o,MGJXA0`|FI!0Z.~,TT0`VF[7G&Rz}-n kMO[x`Uyl+AV PӒkTU$ױ%3(v8 +w8i` 1^Ӛ~VZ*[텂m]| O"+]+z }B@,H:5zb>=H텋JAʿ)րWR0є7*E4+bWr r A\MOETSO|Pp:b+ ␹$SmZ&`fqao\Y|xMIPUqAc})OAYqvJplǐjbĴ\4Uhګ!e^$ [CW?$+Ѵ=&1.½z⨘N, 5Zƴ$bU APOr[)ޣcb.[{aHjwE@UUU!EU:fC+Id?ip2)V)ǐ;mO~~UR-qZRd4nŠYO!Ӿ6Qz`ek$$Cjoą;VlXΠwihPqbhdÓk*bėۀڟsqjX{M@ ZxvmM Z+C\Pbhp`}]I'z~Ӟ W`Q ԲR\un.lE~xmI*apO+֘PCQR܍)…1!c YԞ4v`xxad)b?Z85qC nw; T)l =XZ&1( 6ET{Y&uaTs8I'3^!@QEWl,9*F7`)USL DFNT,m@JcSRp0q*>6ZDՌqQҦ~j(vхILT3兏%' OA-{$QX~?y~H *<&R)w!|Tա*MޞpA‡sژ^ ӦAFcS08X/1S|r*iozR ⫓c, -sTŊn'aQ׾\+ߨ+1PPb.yqʟ=s۹ Up `UG(\RG*ۓAN[B718qPwb3OD̻צ4##'ŕE^!qRs9 4Ů\iO*\?þIw5q, ~,mUHӉJĶ#aj 6=R~[WoҘ8V6, DPe5X[ H(h0R7+VݷI۾)֧T4;b+ r,m,*w،T"(偱瀎1ݥ`WrmzT^Wxb!R02pZ@8l[~"5xi"j(4xQ"d`U )V[b|qJ2$|Í=))Zcw_l \J&Q̂ Zz d43Bx텤2y ) S@0*J9qm{S#1r%lzHo& *1ZȷAN,j8C;aT;:%%X.ޥe`8;xl3+隟t; nlՕւN$ 䲐 I鍱1B),nݰҀpQRH?exSTllSE86$),9|yb nvœR\Pn1S$#KS- ރ$bTXu\ w!mrO4M;M+S ZJ0ǿ݊V=bt<,Z#hw N⸡,I*86#,,:\Fc }6!$t8o8EۢdYK'(b搐iƔ ٔX#_QL -G0T9/0vőԤGR8zqֿ &o? AV l+*)!cd, aJ!qKdAk1YMQ|Xp@"ҰfRPu`ߙ>ޘb?k~07j*hjAH6cP02-R+Xp%irE%iQǮQl,[]g^ )w^],Vpcv>p>i"@; +W(8t5v*g (w[Wԃ\ ]NCnTL eۋ5ENˁ5k-6֧,PuJ+`d3OlV )pM֘EܶdnTa׋,J2#HQrA迎E*J },URAfU61hQ9TקQ(Pb(ߢ:! Mz_ [kuKXU@+\>P{{bH^IJ׏iIOlYgFl,qTa JI-8R3Dצ)p-MZC-xj؃LR‡lX.*銴jCӊUj6)4Wiኪ>/cU t)x 5A'ԁgl\WK5Q8 ;6 , #*qZV]2]X*ƞز^:eJ :aZpGcJbwŒ1>xQv})@_ *ww]a +’Ag>,J#V8$^$,ֻbE`CF!S6cO=nVd#_:eA jQ:5GZN,x@Qsɰ1j» R_nPVC X8PPw녩-]l |ej8b S:B &zXZjT,BOʦJƟcr,lߒĒ! 24;mbGJU\,KU…~ hE iLp6CWF)nvﱨ´{b| ?,PmV(\ثj߷qj1Zh\{``*#L Rp2 j~R0z}8ՄipQi XzS \ ֠u2lnAbϷe%UgP}/k7)ݨ)#4\WB9T)VfET`W) (o ~(y%#^MfJ #6[ʇ!,WI67 +0KuŁXvؠ @J /jSm5h&8 'u+B71lQ4MStK.n@N*;~8AUf Tؐp!d:aJU4|[_0N܎QJ21 ҽo+ķcק|PLQJmQl,XZTv1cm+ZTPVP SO~ RV$qZ 1JA8VcEIDA!!~,&t @aAm=JH‡)/ Tk 6l [jI5'rqMS4*ئ)s 7WF5?@Rݑl 8u&5ŐDB}8ngœք1]DVPMqV;Uv8Nn1F^U6}RڐsZdH|RCPbJ*ڴ,Vp߷#j[k#q{.k^LMԡT7n@T$mk x+jE*z,*-06QԨ5WY#"HOOjbׁ$UcPT\&ْRVJōbX(n&#e' Gzd[)E@튭屧\P! vʼnZf^j9'n \ndZ 嘙 .˰`)ȋ)U 8F}Fv>*(z(h;umtmS(StLl(p*$԰n0$Mzd["eqA`mn 8S.@HĤ飘[@}'aU},i!YR |d/^u^>X>,QM )Rx?VÆ͸_E1=Wާ* }"%zⴥZ`OoV?u8ZZ+A~MNrE4/bPRM7S6V–jqTMW!F#`9f&LE1I䈱X$ax-0'jZ, {mS$,V.UŌ#c?Yg;QU=hhc`d ~ <s-fى8mـ*A$,$jN`*>f@@;o:gf޸EM xXq=3 =3Pqŝ׾c;v1V-C[xSkimmȯ ;8!>hp!AR9IjcEXo_YūSSYi [}k=*Hx)ńBʤ{ 0$F{6k&ePi\!,bneZWlےd:AURA#;bUw TgOqXSbCĖy0*Ҡ} 8jSȅ1Imhh+a!XԌb+説O,B`FM VAPoY-b U$vㅁZ)I$R )PMq[+XѨ GczUQS| NE!Akt8\¢{[;U[̒)rpNJh?gX;mО&ť*<)2*HaЍ6ċ^n"aXޡ3bC-/Ӂ-^PJtɆm]bE*zb6Tl1RwS%>8PfiɈaxJX#8nOӊ$9o7SU3rŁ->؅SR%mTcliӦ$" V* m,T6,i[V^F@*$>VYpb(!I.o5z텍ʟF+kщزcOLPJ`PŐD!bbRJv cl@6٘U4fJhǜ|vQyz=$ijkA-bn،V٨E+-piSJ8XRbkF)FlQn UIi,PB^,(^S| ؐwXQMTDrcb(Ÿ5 0I Iۮ" iXi®Zm*O߀Xy\" qph̎ ÅQM&XBPb[bA O-H Q6*lTp*Hō,esiC)+Cj1H큰d~j!kU*UǦNڅ8GL dP3W- b–C˕*VzA^c+Hr6ŘPTK WzTSB)((L!Hj m̃oO'\ $ȫ[^FԁF&Vz$+ \/wV?UXl,+^"YB`䂤_|XKN&oCFxke0MJ2Ǜp;Н-b"4^kh'IѰe -Ać.("5tiZŘ(_\W1U,ip4|F*V \qdI(b(Rą [|ULUq8k  a%OM5^S*$S6H.^(е34=Dz6;׮(xӗ|Y)Zq[ZBFPMI-/@)җJE,\O5JcsLXѱ4DqG ܲ1k \ \@m{[X ;PC|\@>>xr}ߦأ^%F*+hlz{b.p{R1Un1CR|PBjzv80 xΛ+M}JFŒhd xʑ(1JĤkĚsQL-V8TWLnBR@XkkjHdRN". HSEHbt!V*$RNgmT0:xclMtp v6Ek";j:aSPGqW=M \UC\U˱Ur%y#Ss"PSz zب6:+~UT*Ozb 酁+Qؘ)=&߅+ wo(`´ֵ_u96RUOzk椝!-jj%^Lv$mz`O'obͤ1HoVqMXVq[lJ)ņ)/)\-:+M(!O )NքWyNFb_=vYGey>m`l(@}2Mu!n RdY,F˺iۮ& )Xq)O |, iuN*iM (rT JB!j|5uBƇ3*+`|ܓ6[?qCMt…hk QK^]vd$|#[$RwOWS TN9!&+jk}-oڢ EUT 1j(${Y*-8V֗Ƙ Q_Y? P:u8Ly!|AO5+xbc频QE%P"#_,HRnEw4XKKlZ&&T{DWnVIS-*QNlb9? L7!Jmpڟ4 86WN@O݁X)ZbGz{dV,ITu+£:һ(_7H!,aZ*|<(QP2Mo*+ҠCi9UIxk{–0w!Qb " UE*cYUzL$Zo,ZU __lBRzbw/E&:XKd G:!x}OjqĂp)~,qj5wĨWRk@?͋.MFVm;`d]dŁlzSm(v1S #W)C/ إo!l(#҂>kK c (Sז^ئ {ב,9ZpDHP?|YR`0b , R7ō ޯ@Yt#> +$%Okt0iY݀r%&6lApġq4=~2ڛJʲ(5(աi 6TnoLh^ V"8I'!S#7 J]JLt’P!qah%z8 =𭨵A-Zxbmjh ilRb‚ZY-!2;vXm^5Zp&S[qF[ CP07lRTm jA=W Th:\ *)Sdy,$n, <0`d#dQAZ OʼnLEkxnSN\kmp`e]qV9~m.rI'8AKׯLT4µ l+/VI]^i N(B(x,6b~<1ŘO#e*T``+˜H?PaEcㅉ BX*8/j6"Oq\Rѩ…SS`,N2WUE}|f8D:R*H%hPV tTeqa$3W YS J@ Q@Mv߶EMw>XlR)VCt8 bm] 2-]"`V)P0;hSOƐE5 7PJJSAm``lVִآdH'%^`A\b\$ ړ@]b2A¶иj2ũRw;xؠ6RTZzun>}bXZ3$(T;!YeS␇aB"{.4.JMm`ISg q& Xk]C|=ۏc *ehi6+_l ▪p(]b׭iZm!q-1Rk#f1V :Tt;ijRT0{ O7Tp!{ J֒6\hO*6') I1AYSb LRVӶZzb8Ti7ۦ!|V]ءW.$<~X AE*nP9YSixb )'aK tߦ)i#A Yo \ N+J`HDAZ4ޟ cIhUY6=ز ( <)^J?+Jfه`cJ$w8w;{PL.A߉qcAt+kĄjӾ);S\T3xX-i-o#mP[ P!~ZP *qB0z,mV]z⭖TLY\QRڠbT344 kJ;{[kq GPk|GW kJw^ !kh;W*Zjb=; ,W*Wɥ?E#VmƭNڀA8*xP AE)R|2-uMdmP1Uk -Y@d7L8F VFf$X!J{·э~(=pbYQ?59 VqU.@إpr0&сޔ9aYXbB=zbx~Uw~գ*ѩjR5`Ǩ8mYZ#r@iXՏ-jl!hB#z\{up-䟣*{C?C+]͒ rPG\Qkڠ,QH6~T֚t8/ZacK@?R7\R*BabQaJPԃSqn=Y2cC#*$-I %Ѹ~ 2S*OJb˰߮Ar^:H*ij(i⑺*_ { T X[|Up+KbtƓkM0%N;BO=2aHd"Hu)!jOϦ[Q@|.DE#ġ_ [c'&^(Hiqr*S[>UTmb6p(nCJvZZHDۧ|Y8 L~(Sy6ȷ[`d}2 )r*"~Xn| ث`U@`ejE@7?,i$jOPWVmV#j ,WH4a`M).pA8R~!Ræa)[jɶ[TZz`J|,Tk FP^'`jDx^DvJmvƧlQk,қөҘ-q"4LQM9QADY=K#4BVI#<-b@ʨË*SobV NP+x۩y\E5SŘRMfުzpv` 1zA^q܍ }$…X%d_' JJWFJt7#.&?< c -Qhd~~Yq*P+NF])T#Z}?W&]S^I,4yę `Ҥ" .Ddd 2ҟ*T+ŐWP뷎,›Ҥ8A*<*b "ܸ&׾% 6:`f`H^ɳ:ҕ86+~x bU`dTR;u”\|>xOXݫROSFkVx}<;MMNݼqbBf6,Jxb$}Zw j*Y*OJ X9oBE\-d8 ^Eʔ^p{eMmL(;l+j[nOŐ*cs$Jx|4q 7Ec d[vl.202Nqa[%@"=v7Kkmq@'\u!iZ%a^?e,Wsfff<݉5$1I5^T 2#U5$pŷz.5ޚI+w N$ڧA[C]*Uh$,m5֟|URަuYEȷ.SALR KUǮ%W02P{@+֯$T$-XbZwg lҘi`fG$Učd՗r9cAm۩$TxBeXuj8m(Z|U텋+J^ل^m'i%":0$/mm\02 KMv1UæqV*tjLiATM`gkœh|p3ڞ'aBkS_ '[ BQiV1d,Eh*X c2+bĨ3B7ŁQʑQ%7 d )¶ZъTTxbࢠi䤃؍>P| ^,e[6,K^!rŁP"iqd qV',Q<q֌ 7_8Zol(r^Bi02lJ*A\T !6)FkJ74mNN!,ؑއ ir%*&E`d t 1KExV 0T߁DPb }8SA2*Qv 1%Ïg!,w5L l-хjhzd[EqIib&7?ow6cPW%x~Br*OQ l'$p/ Tl r␨JBМ*VƔ4ȷ$| hwuy14Rl ҔqPzR41VL(v(^҇ t—I8xZ;'tP-끶ֳ Zdc^T|%a_ma`VW-P…h^`}@A߮(-ָ'jo㊫J`Ƶ%zoj[5A1CJ87)ʵ'jup-UCp<<m2[]bV:փ Z6尨j:aBrAԖ`}R%~=8+[B<(H _3dz!^f5\,ElT-mƴVNh:b;`KG5R>xw*}q(ȭ@ wL3i/_m|$U+N>m 4w?6,%&KT>jr,U#j`f=t'%o Pmi ·^J $ŐQaB|0+$.MJ |MتXîE.śb`}r)lR+ZqE:b Cmܱ[lWV0@#qK+41IRA1ܟ% }ةPhrMmqVā{}8kbc#Sb dCrDImG 6>g 4|1Ih"JoDZaM,H8֭@8֔kO R %X RA]i"Wo Y!呫C+A,vbqG& AViiPvtBG ͠+Q/0%>P1[rG#o T;78R|P[7^SlރcW8N)]\R^bT%+^6+AUF큘*؆ 6ĕ:kq =wŋ}Œ;bhk)kؠG7Oj I$ .zl+M hljѩJ `;.iR. ^&Ņ=[C Vڢ+D`LA-?8$8@ђ8Ѓ@H P=J 0JנGLUJbn02[*Oi݋ 4=<1Z^e;RuV5l,\wm6W| ^إljdž(!MR@iJPbՏPhqmW &SieA=qBbA0%`^α1'\`JсBҔp3V^D,]61U&+Ҹ*}ŰTPU@8#@S\ 6u#l)1J_*QJbdbp2o|wTӧj!vqKd7Zl:₱\Smю)Z:N4Ցȶ10%<,Ii,FţE ,QzM~YJ۱BU;tWߋ%!` һ S'|UY}Ř(@灲 8Ri@M#[NzR9#-@)\)FFjJ=cbŭ*0$*O|R3 U zƗ!j:*-Ϲd 6W(qZ\1HVK]VӶ/F@v4Mh>V֖n[88IFWIS ejh7ڌ!F(% 1b*ŠuP UlviᅌMA4aibMv@E)"특NUKl,]4<1VU UV1SE܍¥ASj|co[dO|UiW`J]5SR z]l ylvą& pŋ`ڡ'(UkMj OBj1AXG G;ckN(paR:)NǨm&,ZR|Uq Q TiϨLSM,@.RŘ\0&Z j¶ObŃ |UQ :v,UZM5y(qM-hzJb'𠅁H4BZhݰ^2)Ҙź׮)RfޝbK\ >CC XxW)Y#Rj WǦ) m-v5#ҟk(W\R1E$sEj1B(%Nɮ*n㧎$PzxňVE{υTFիnǩJc \SjRRʼnkⶱ†CùnU@FAYdŰM *J۸J/c(+ָ@d…boD`p2 n$H, ΘX7LUP'C\ ^A*ڰ߶(bB*:bm. [8zzpe; i`**B4,8*l[[Wa K 7lYڥ Ϧoir n}2hyקT=<1dnJ2DmA J9b(1ZkzR=F)ZW oUkGZIp%kҸ3Al zbRYFvo,hcۉ'lUOVŠTEp 1cZd[;}حŊ%cK8hح,}*VPw5!V_s<1AS&G+PeoiIڞ%v={ءE:oZN(SL!SN(ty9 6 X@RH /`F)S5,mxl He4#,HjDP A[ ГPi@+Ɠ> RzAڝ3 +eFEF}r1n_S7;_OhwP㒚dS_W) Aq;vʁU4hb v9LNFxȽ@Eqxhn#Ȳ N0Adv8 lǯª}8eAʘX݊VNJz▉ qU7T݅Rbqb;s[wbKA[q֓**$׮*I%p]R؎إJbVPUnHn 7ъӀ!~N;b Ux#[#lQK Wp-lUR}MV%3M µ^nj#{XekWmEv=GcXTvHh,\J ő^ZE}JӉW/ŀ&S4=0([2 YH?g5酊r8h2h@&xwU\7)`BKzZdzR%8z(%ilV5_ urRqA.( \ W =U%p"‹!gz*灕4@()|N6Ă~,mII֘P< kՆmſNڮ/Wp$UX9b=:Rԁ6N#$h/i_6lOgO^żER@N`b!j"ʖ9޽qAqHS9&w끐%\mډ,mib~X%aecNE@W %bN Z$; QmԌSkצM*v=׶*E1E4+K%|p*-)^M[ZRzbcBGpia)o*5bbU{⡧5NP8Z[}CEnX4Wl4'Kƿx\ylA[\,m8p+n)حiNqUۧlUR:DB:o)uء "4 p*l2L Q LV6KX^J eW o}>~*9r/ޔN' %%aN'o pua4J,7Lx[ϾVv;ZƻqV/|SkVFxN4$/ XMqbKDx↫j+ߝ;ߓCQVqCA-ż1[[̌QkJn1H*AJ `Mm[|mVLPⅅ -\U;/ڔŒDRH(~Y VC4[ [[&9!ةZF$-ž$6[umⶨ`JH5ȔRn2-y1R*Z) $47r?5j$ⅤaC8u]UYHPOS O'aS B?%щ "eXPiMpY8{m.iPb&T4b qE1YE4LJ3#lHaFb%6GFQo4&BE+7؊|V; (h Uav*Uܼ1Vj1[l8=1emqE ‹w*VU8Ux.)8P64XbP ENO(l6V!f‚VN(haCd7B|(% ;S'O '+I‹k 2-ۀ'ӸOqGn~❱BUثXBp3QNaV6퀳ؕu EA‡ㅬH7ӓj*D`\(u1Wb)TP֋s2{uE/%\iAh Ap{ [Xӈi` xkmumc6u8 mتa@NKhqE:bb\-SMS +k\ -JË?WjaE% &rp^ xڀҕLozZI+ ō8PŬP@2P بŀ$q`J$ɪuq[up&\V݊bUNЫޙaЭ_TY9&Ǹĩ%X<,h᥶ێEE+#) :7N&+OQX&\, {ܨ֤,FK]G`m|FjM_RJ(hָK[t)\kձd mr)ZxГabL8Xbp'W.}6`d+{VqB4_a|J@qV-N}W &:kۭqUUI5~[ZMp++bH(jث}rXq=(%azAㅬ]G-+Ù;`@@­+3nrd~y ["P2!AcC텂X3ZTa`Ta`B-v(p+lMi†(g , օJ aEĢ! 5=tѡl AZaKaNծ,J"pmPČLL '!! 6ٗb:yb݂RM8 [fpf5xG *|*᥊<4<܂;6* AMmxm-\N*8opqUt*إzl 2Ƿ]vq4^ 1Iw2QX|[ҠoLB 9+⤭? u t A]Ͼ gki(h@@إ ALXvʼnRbNI I kL,Z'-W:Uث# 䆉$pTАvc^*kXplPRUإp0)iz␯Dҹ1?cla *2@QAH h+[Ӡ*խjXZ=T+l(+ML(Xp-bثxR)Zٿň;7;\E 1[q?ًv5RmM5$-;6h|V+N)F,Z-ֻuŕ†{b۹bmb+mb[`dƖ &+kA6ֵ}0N*bح\U )w\ˉdb,T…$$\:~ob Z8CY+rL]ԥ74!pUz5DDvȖU| VWF,SH1 Pdi R&}X-$<+\lzb\Hڂo‡`KxrqJ6 Xa`Id{JRxIĄuw4ҟM{bBw0!'`ƀ,ID ; 4=x)?p R8c D q)JT 'p]V 7C4qWbbW aMTU\ULU. -]b oөmi#4PR!1C *̨(%W߳ 13JDӅ[^رv*UUP)l%YITr:M7LIaڞ*Uت!pj@* k`A_U)N?kʽ)Z6g@u=W"7VFё-dY;Ta$,O,A DX-.n-bqx<”_W{mT03x* ;劆L + 50+W T1w+SObƚ.ե}f G.v+mbOlUb uͺ6+kIʼn-aCj 0ZSJA4N`[j (H|Ja5w,VN+nq[nn)qP[$.&`Z6+jq2D$IOQ!Zgn8 4"hӶ(-aCWb]PKH:aCW`Wb-㊻nwiz1TnDH;TGdʛP!Qe5)u.a[ H+N֢IaiĢ;Ӯ)/ h@`iZV-hb Awk uqWb*UAJj P.[*M)\@ʕ߯\V֓-(jءثWaWb]][/'bU ** u1Zv***`f dzS8*/v˅P}mj7 Hć (q$Vt#*[rw. el)`!)Os&2ZqbZ AVwPB-E gh-V&銵P7{S%k1bI5'4uZONhhxe[)d$,)ɰp2o*XFزvb(OѾ$vœXWb];v**UUثKVWb۪q[uq[v(vkowv*UثWb_Mib,KTث6I(.N? -UEj-퍯s]1Up6DTそ-WFڢMX7JnbT2CǕ+ǗNNL- zx6Zb g!TS 9F]-wTA@( Z \Zabv["23#.aE3 p*+K*zma녉u|q[k:bi~x%*+Ӿq]&Fm[ PLb$se!{]yָPnMOSlU Dbm0(k op]+dzSu`gMmmw#JWn-rmƖfyHF:~4Xkov*Uثxpl#$T`[lm@aLaq. YsZAʼn iaC;֞ؠ\Upj`BKCxK"n%B}Ii l6,تbƝC ׇHaN \VU$Za[\<'/L`VUOuqKWb qj#,.ƕ5Mn;v*UثWbbb[,'e>x.]v*UUثWb]okov*RPUثWxWb*8r4$ѡvjXXX[]\R6`0W%[XSnT(lmx`Zv+KVF !B)bB^pe1E8~AD]}G҃C'R}Jy#]fyenVr ,#́ }KH V v.UUUإ|Ks$)TqWxN**ښ04=(-rv`I : Uœ`⠷\'Tb5XU#hbekS!'\T W)P7Z1UDhO@ 8ZNF`1;N(%a`]ثWbqVVVU/fhPļ'‚/oR$>EXVWPS|UUثWb]n튵v*Uت`CC^ˊ RUثWb]v*UثV U5|UxqVWb]opހnOlUp X D҇minE ;yW,n)`Ӱ$f\VۮVF pP)l ]׮K T܍?[;i`K(hA녋KXCVVVVUUp v~X]v*UثWb]v*UثWb]v*UثWb]wlUثWbW#"K"ni"G=Z._VxXWb[W4r*+^ Aq? t]犵v)u1CWb-RPUp^ܟ  )0+ &]Њ-UثWbb[]*kw\8UثWb]v*Jqvv*UkNm(KWb]v*UثWb]Mw8XWb]XWb[]/#"bQ+IW D]Un$.]LUUتBOTPnmnإ- k,Մl(9ZW!RHg`Zi8XPP T =1RFʊ| HSNKzbbZ]v*4 x& UUثWb]n}t bbW2$r GC0mnv*UثV#}**٦5][$aSXت}J?k{=?Q:xr\g?k qKWb[؎q$I;XxAP+8 @|qWb[lzTWV٢)k;v*UR8F8Z*A |<= 4Nb Uz`f ")@Ԫŏ}"[*~7XĀTj)(uqKWmW\QMbcP)?,T*&ZGB kZW1RĨ⽅kO)u +MLUUثWb]v*4{bAF*CXWb]v*UثWb@ۊxa]oVWb]Czbkv*UJo._Xo.]v*UإتAʼn qCVW R(1M8bC@q`nT:ƒGb\9`:fZ8V+mb]* ShJ\a6~<[5bɼUĒ'axb*UV9V.(̥y1ƿDrJX;l&8Xd:7V*t4ة&+bV!.]Sv*UثU{˧HB) 8~JFCREdjw>8XWb[5]v*UثWXo%݆Ҿ/zkv*UثcAZVpZ,i YSX8UU$b%̵}8 ibWgXbM)ɀ؁1KWb]u6~VWb]lRLUUثWb]yuޝq7ѬRUثWb]v*UثVkv*Uت$\jL(=p%ثCK|=^)ēJP|ZpsLR*$V(KWXv,KxWb]RZ[[xn Mi~6ŐZ b**!up30+qb*Ub-bWP]v*35+P|(Jo_ኵ]v*UثWb]Ÿ +/zkv*Uثb55.]i]mv*UثW{b]v*UتvZ4*~G6[[cRMa)v*Xv(-b]v*UثV\U-+b-8X5lb-UPURU5nzw[VFJMX% cUHoC0O)fUثWb]v*UثCKCKcoKWb]v*UثWb]v*UثWb]glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/data/w5_subset.vot0000644000175000017500000002160613502206677026002 0ustar noahfxnoahfx W5 source list Source identification (\dicS{[KAG2008] NNNNN} in Simbad) Right Ascension in decimal degrees (J2000) Declination in decimal degrees (J2000) ? 2MASS J band magnitude ? 2MASS H band magnitude ? 2MASS Ks band magnitude Spitzer/IRAC 3.6 micron band magnitude Spitzer/IRAC 4.5 micron band magnitude ? Spitzer/IRAC 5.8 micron band magnitude ? Spitzer/IRAC 8.0 micron band magnitude ? Spitzer/MIPS 24 micron band magnitude Source type as defined in text (1) mag ($8 - $9) ($9 - $10)
1 41.081526 60.510607 15.34 13.69 13.04 12.60 12.50 12.36 12.47 III 0.14000034 -0.11000061
1001 41.789827 60.225042 14.47 13.87 13.73 13.64 13.65 13.64 13.63 III 0.0099992752 0.010000229
2001 42.091916 60.612003 12.51 11.49 11.06 10.81 10.79 10.68 10.62 III 0.10999966 0.06000042
3001 42.410770 60.258330 13.43 12.43 12.12 11.93 11.93 11.83 11.80 III 0.10000038 0.029999733
4001 42.640412 60.808732 13.04 12.63 12.58 12.46 12.48 12.35 12.43 III 0.12999916 -0.079999924
5001 42.798023 60.611954 15.18 14.31 14.01 13.70 13.65 13.56 12.87 III 0.089999199 0.69000053
6001 42.941913 60.339030 13.88 13.21 12.94 12.78 12.77 12.78 12.82 III -0.0099992752 -0.039999962
7001 43.124852 61.143317 15.02 14.03 13.75 13.74 13.50 13.43 13.55 III 0.069999695 -0.11999989
8001 43.303753 60.366137 15.38 14.41 14.20 13.82 13.77 13.65 13.53 III 0.12000084 0.11999989
9001 43.489095 59.661203 14.45 13.80 13.74 13.64 13.66 13.63 13.72 III 0.029999733 -0.090000153
10001 43.659601 60.443871 10.61 9.91 9.70 9.63 9.68 9.61 9.61 9.70 III 0.070000648 0
11001 43.845790 60.226244 15.24 14.48 14.25 13.37 12.89 8.07 II
12001 44.083733 59.697005 11.92 11.40 11.27 11.24 11.26 11.21 11.18 III 0.050000191 0.029999733
13001 44.362207 60.417993 12.81 12.71 12.55 12.49 12.52 12.42 12.54 III 0.10000038 -0.11999989
14001 44.619979 59.561369 13.01 12.57 12.47 12.36 12.33 12.35 12.38 III -0.020000458 -0.029999733
15001 44.848209 60.721555 12.44 11.63 11.43 11.29 11.35 11.25 11.20 III 0.10000038 0.050000191
16001 45.077124 60.186607 10.26 9.33 9.03 8.89 8.94 8.86 8.79 III 0.079999924 0.069999695
17001 45.366181 60.062767 13.67 13.42 13.22 13.22 13.06 13.06 12.92 III 0 0.14000034
glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/data/datetime.xlsx0000644000175000017500000001152213502206677026040 0ustar noahfxnoahfxPK{+N _rels/.relsJ1}{wDdЛH}a70u}{ZI~7CfGFoZ+{kW#VJ$cʪl n0\QX^:`dd{m]_dhVFw^F9W-(F/3ODSUNl/w{N([qTu3e-AE@꽕pE ~?-QCUq@Evlŕ k𸜡"]%dB{o 1E˞Xnv=#qT /@/N2SV̺?J?x H5MHAWVH#lOi>vUЧ,PK#'bPK{+Nxl/sharedStrings.xmln0D|w@%"*d!uݠ"!nhh'MFEbfC!ރEpL~IVNDMxUZGJ(Ms%VYVB8Hi۶لyafC'1D'3uvAPY C/@|:?)[o?PKɗPK{+Nxl/_rels/workbook.xml.relsMk0 FIc8AQ6ֵ~.[ eГH4C vV͢h%؀I/Z8Y.b;]JNɺQ/IӜS[n+Psc _{0 r3Rb+/2m=?"#ʯR6w _fz o ۭP~$E|PKOz%PK{+Nxl/worksheets/sheet1.xmlVMs6Wpxȩ%%dbƑ&3屜f7EA@)):=XX,v]b7 I8[So:<#r?mXTerW ų,0V`rJU׾/Hz vr.J`)F9TR?L~sk_l<')i]b)R,H%$63;GTbث^}@nI78#Aܛ:FoOrApSe&Y7¿ Un n 3~)8ܶV^= 稦JhGr曂I^ikL: r^RDr7GBza luKhi;A^!]nM @z6|G 4j6cSPP 9ogZ$<5UR0_,}KK "x%ϋIC&*)&6nBI,TZKKC﬍qj0!ٿGLv+W8c2l5\#4|Ձ \` , Z7F ϵW, t@g^-F@ggЛXkM#o@; /Q>g3o^ e;.ƕWܛ+ԋ@]T Զ2))P7CmK) )Dа\0u(oMC@15mn-pg/?%  hS9~w#B]A ؑW6?k>>t%{yFoh6gQscC_PKO\ PK{+N xl/styles.xmlX[o0}߯BneCjMM\0`vڤ~6鲵LR>|>؎UN=pс f Kx?@*bD9\c OwTk3 L0S82p/0O.rtS,F4r]srD }繒 K8i `OM!tbe|y\\8oG׍w\D:O8(B |j2 V[rB ʐ7ۭLbz{ӽ,MƦ1@H),\7@U_ m4ҔqNZƳVyo43` 3Do&J ? }I3S*^8D)JǤME0fM|M6ojUt^U-S@EAsnHX 8+C:)%)V G\%QSL%d dW WȲhM 6&3 |Nڦ(p\H"U唻iקJQmT= ^ fkk3 b1}L'SNG{fWj?qw{og}򶞿Gy/۞?;~oZͷyen-ۜj{h]4[[(0gli˹%-~3rmӜz{ecV!6 ߷y5C݀HM͑LmcZQL : ]"5(@ HVl,!7g^˜W_|hRʎYɥjҧ/wȑ:R2<)@hL*C$d[!i#^B.R Zٶzg }~rgQ`E fwbWaf c( wK)52%d4·ޚ'^`+#)R1D[d{H?|޼r#73rlŸM/\x95\xc5\'\x8cn\x1eKC>\xf7\x11\xfb\xbcgK?\xda\xf1Rf\xf1\xe5\x1b"\xe0F\xb1\xbfx>?\xf1\x17\x0c\x08\x1aX4\x1a\xf3\xe8\x8a\xb4`\x1e\xb4\xa0C^\xc1+\xe6\'\xca\xc8\xd2\xca\xce\xe3\xbb\xef\x00\xdf\xc2-\xfc\xdc\xd1\xf6\xcd\xb5\x0b\x90}y\xbd=\xcevZ\xd8e\xdb\xce99\'\xb4\'\xea\xf0o\x1a\x11`\x06\xfe\x19\xde\x11%\xca\xe0\xcff\xb0k\x8f\x9d\xf3\xbc\xa9\xfe\x15*\xcb2E\x8c\xa0Z_\xa46\x9d\x92U\x9bw\xb9\xb5n\x97\xb7r\xf6\xb7\x90\x1cC\x1b\xeb6\xfbY4\xf3\xca\xdd\xafo\x0e\x8f\x8e\xb8\xa7\xd92\xc3\x95=\xa8\xff;d\x04\xf0qEz\x90s\x9aG",e(\xbd\xec\xb1\x87\xe5U~\xeav\xd6;Yf)A\xa9\xef\x19iW\xca\xa1Y\xe6\\\xe3\xd1\x1e\x9ag/\xe3\xad\xbb\xb3F\xdfEz\xe9\xcb\x91\xc7]\xa5O\xeen\xd2\x17\x86*\x97\xb2\x0b\xef\xdc9t\xd3\xfbh\x8c\xfc\xe55\x8b>\xd1\'n\xdf\x92x\xe2\x86\xbdP\xb5\xe2\x03\xcb\x8e_\xeaF\xc2\xb0T\xec\xbez\xc9\xb5a\x15)\xfe\xb56\x1b\x82;\x83\xee\x84\xe2\x8b\r\xfcg\xd8\xc66[\xd5\xa6\x18\xcb\xc0K\xf2\x8d:|\x83\xf9k\xcc\x0c\xd2?\xf9f\x1d\xbe\xc9\xfc5f\x86.\xc9\x1f42x\xc8U!W\xf3\xa6NACCCCCCCCC\xa3\x1e\x84:-\x98\xea_|T\x9d\x0e\xe2\xea\xbb\xce*\xb5\xaa\xfeL\xf2\xdf\xe2(&\xe9\x9a\xa2s\xe6A\x14\xa8/b\xba\xa9\xfc\xd9\x8a\xa8\x08\x9f%\x1a\xf4\t\xbf\x17J\x1c\xa7\xd9\x8b\x18\xc7(\xc71\xdet\xfe\xd2\xe9N\xd4\xfe\x9e\x86\x1d\xad\xdf\xb7\x85\x9a\x9d\xbf\xdaL\x9c\x7fx\xfe\x1f\xf5\x81\xcaV' with make_file(data, '.xlsx', decompress=True) as fname: d = df.load_data(fname) assert_array_equal(d['x'], [1, 2, 3]) assert_array_equal(d['y'], [2, 3, 4]) assert d.label.endswith(':Sheet1') @requires_xlrd def test_excel_multiple(): datasets = df.load_data(os.path.join(DATA, 'simple_data.xlsx')) assert_array_equal(datasets[0]['a'], [1, 2, 3, 4, 5]) assert_array_equal(datasets[0]['b'], ['a', 'c', 'd', 'e', 'f']) assert datasets[0].label == 'simple_data:Data1' assert_array_equal(datasets[1]['1'], [2, 3, 4, 5]) assert_array_equal(datasets[1]['a'], ['b', 'c', 'd', 'e']) assert datasets[1].label == 'simple_data:Data2' @requires_xlrd def test_excel_single(): from ..excel import panda_read_excel d = panda_read_excel(os.path.join(DATA, 'simple_data.xlsx'), sheet='Data2')[0] assert_array_equal(d['1'], [2, 3, 4, 5]) assert d['1'].dtype.kind == 'i' assert_array_equal(d['a'], ['b', 'c', 'd', 'e']) assert d['a'].dtype.kind == 'U' assert d.label == 'simple_data:Data2' @requires_xlrd def test_excel_datetime(): from ..excel import panda_read_excel d = panda_read_excel(os.path.join(DATA, 'datetime.xlsx'))[0] assert d.get_kind('date') == 'datetime' assert d.get_kind('a') == 'numerical' assert d.get_kind('b') == 'numerical' expected = np.array(['2019-01-01', '2019-02-01', '2019-03-01', '2019-04-01'], dtype='datetime64[ns]') assert_array_equal(d['date'], expected) assert d['date'].dtype.kind == 'M' assert_allclose(d['a'], [61.35, 44.06, 83.02, 66.15]) assert d['a'].dtype.kind == 'f' assert_allclose(d['b'], [79.34, 15.66, 84.30, 61.53]) assert d['b'].dtype.kind == 'f' glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tests/test_fits.py0000644000175000017500000001737013657331513024777 0ustar noahfxnoahfximport os from collections import namedtuple from copy import deepcopy import pytest import numpy as np from numpy.testing import assert_array_equal from glue.core import data_factories as df from glue.tests.helpers import requires_astropy, make_file from ..fits import fits_reader DATA = os.path.join(os.path.dirname(__file__), 'data') Expected = namedtuple('Expected', 'shape, ndim') def _assert_equal_expected(actual, expected): assert len(actual) == len(expected) for d in actual: e = expected[d.label] assert e.shape == d.shape assert e.ndim == d.ndim @requires_astropy def test_component_unit(): from astropy import units as u d_set = fits_reader(os.path.join(DATA, 'bunit.fits')) data = d_set[0] unit = u.Unit(data.get_component("ONED").units) assert unit == u.Jy @requires_astropy def test_container_fits(): from astropy.io import fits expected = { 'generic[ATAB]': Expected( shape=(20,), ndim=1 ), 'generic[TWOD]': Expected( shape=(4, 5), ndim=2 ), 'generic[ONED]': Expected( shape=(20,), ndim=1 ), 'generic[THREED]': Expected( shape=(2, 2, 5), ndim=3 ) } # Make sure the factory gets used d_set = df.load_data(os.path.join(DATA, 'generic.fits'), factory=df.fits_reader) _assert_equal_expected(d_set, expected) # Check that fits_reader takes HDUList objects with fits.open(os.path.join(DATA, 'generic.fits')) as hdulist: d_set = fits_reader(hdulist) _assert_equal_expected(d_set, expected) # Sometimes the primary HDU is empty but with an empty array rather than # None hdulist[0].data = np.array([]) d_set = fits_reader(hdulist) _assert_equal_expected(d_set, expected) # Check that exclude_exts works d_set = fits_reader(hdulist, exclude_exts=['TWOD']) expected_reduced = deepcopy(expected) expected_reduced.pop('generic[TWOD]') _assert_equal_expected(d_set, expected_reduced) @requires_astropy def test_auto_merge_fits(): from astropy.io import fits expected = { 'HDUList[A]': Expected( shape=(3, 4), ndim=2 ), 'HDUList[B]': Expected( shape=(3, 4), ndim=2 ) } # Check that merging works data = np.ones((3, 4)) hdu1 = fits.ImageHDU(data) hdu1.name = 'a' hdu2 = fits.ImageHDU(data) hdu2.name = 'b' hdulist = fits.HDUList([hdu1, hdu2]) d_set = fits_reader(hdulist) _assert_equal_expected(d_set, expected) expected = { 'HDUList[A]': Expected( shape=(3, 4), ndim=2 ), } d_set = fits_reader(hdulist, auto_merge=True) _assert_equal_expected(d_set, expected) d_set[0].get_component('A') d_set[0].get_component('B') @requires_astropy def test_fits_gz_factory(): data = b'\x1f\x8b\x08\x08\xdd\x1a}R\x00\x03test.fits\x00\xed\xd1\xb1\n\xc20\x10\xc6q\x1f\xe5\xde@ZA]\x1cZ\x8d\x10\xd0ZL\x87\xe2\x16m\x0b\x1d\x9aHR\x87n>\xba\xa5".\tRq\x11\xbe_\xe6\xfb\x93\xe3\x04\xdf\xa7;F\xb4"\x87\x8c\xa6t\xd1\xaa\xd2\xa6\xb1\xd4j\xda\xf2L\x90m\xa5*\xa4)\\\x03D1\xcfR\x9e\xbb{\xc1\xbc\xefIcdG\x85l%\xb5\xdd\xb5tW\xde\x92(\xe7\x82<\xff\x0b\xfb\x9e\xba5\xe7\xd2\x90\xae^\xe5\xba)\x95\xad\xb5\xb2\xfe^\xe0\xed\x8d6\xf4\xc2\xdf\xf5X\x9e\xb1d\xe3\xbd\xc7h\xb1XG\xde\xfb\x06_\xf4N\xecx Go\x16.\xe6\xcb\xf1\xbdaY\x00\x00\x00\x80?r\x9f<\x1f\x00\x00\x00\x00\x00|\xf6\x00\x03v\xd8\xf6\x80\x16\x00\x00' with make_file(data, '.fits.gz') as fname: d = df.load_data(fname) assert df.find_factory(fname) is df.fits_reader assert_array_equal(d['PRIMARY'], [[0, 0], [0, 0]]) @requires_astropy def test_casalike(): d = df.load_data(os.path.join(DATA, 'casalike.fits'), factory=df.casalike_cube) assert d.shape == (1, 2, 2, 2) d['STOKES 0'] d['STOKES 1'] d['STOKES 2'] d['STOKES 3'] # zlib-compressed TEST_FITS_DATA = b'x\x9c\xed\xd0\xb1\n\xc20\x14\x85\xe1\xaa/r\xde@\x8a\xe2\xe6\xa0X!\xa0\xa5\xd0\x0c]\xa3m\xa1C\x13I\xe2\xd0\xb7\xb7b\xc5\xa1)\xe2\xe6p\xbe\xe5N\xf7\xe7rsq\xceN\t\xb0E\x80\xc4\x12W\xa3kc[\x07op\x142\x87\xf3J\x97\xca\x96\xa1\x05`/d&\x8apo\xb3\xee{\xcaZ\xd5\xa1T^\xc1w\xb7*\\\xf9Hw\x85\xc81q_\xdc\xf7\xf4\xbd\xbdT\x16\xa6~\x97\x9b\xb6\xd2\xae1\xdaM\xf7\xe2\x89\xde\xea\xdb5cI!\x93\xf40\xf9\xbf\xdf{\xcf\x18\x11\x11\x11\x11\xfd\xad\xe8e6\xcc\xf90\x17\x11\x11\x11\x11\x11\x11\x8d<\x00\x7fy\x7f\xbc' @requires_astropy def test_fits_image_loader(): with make_file(TEST_FITS_DATA, '.fits', decompress=True) as fname: d = df.load_data(fname) assert df.find_factory(fname) is df.fits_reader assert_array_equal(d['PRIMARY'], [1, 2, 3]) @requires_astropy def test_fits_uses_mmapping(): with make_file(TEST_FITS_DATA, '.fits', decompress=True) as fname: d = df.load_data(fname) assert df.find_factory(fname) is df.fits_reader assert not d['PRIMARY'].flags['OWNDATA'] @requires_astropy def test_fits_catalog_factory(): data = b'\x1f\x8b\x08\x08\x19\r\x9cQ\x02\x03test.fits\x00\xed\xd7AO\x830\x18\xc6\xf1\xe9\'yo\x1c\'\x1c\x8c\x97\x1d\x86c\xa6\x911"5\xc1c\x91n\x92\x8cBJ\x97\xb8o\xef\x06\xd3\x98H\xdd\x16\x97]|~\x17\x12H\xfeyI{h\x136\x8b\xc3\x80hD=8\r\xe9\xb5R\x8bJ\x97\r\x99\x8a\xa6\x8c\'\xd4\x18\xa1r\xa1s\xea\xe53\x1e\xb3\xd4\xd2\xbb\xdb\xf6\x84\xd6bC\xb90\x82\xcc\xa6\x96t@4NYB\x96\xde\xcd\xb6\xa7\xd6e&5U\x8b\xcfrQJ\xd5\x14\x95jz{A\xca\x83hb\xfd\xdf\x93\xb51\x00\x00\x00\x00\xf87v\xc7\xc9\x84\xcd\xa3\x119>\x8b\xf8\xd8\x0f\x03\xe7\xdb\xe7!e\x85\x12zCFd+I\xf2\xddt\x87Sk\xef\xa2\xe7g\xef\xf4\xf3s\xdbs\xfb{\xee\xed\xb6\xb7\x92ji\xdev\xbd\xaf\x12\xb9\x07\xe6\xf3,\xf3\xb9\x96\x9eg\xef\xc5\xf7\xf3\xe7\x88\x1fu_X\xeaj]S-\xb4(\xa5\x91\xba\xff\x7f\x1f~\xeb\xb9?{\xcd\x81\xf5\xe0S\x16\x84\x93\xe4\x98\xf5\xe8\xb6\xcc\xa2\x90\xab\xdc^\xe5\xfc%\x0e\xda\xf5p\xc4\xfe\x95\xf3\x97\xfd\xcc\xa7\xf3\xa7Y\xd7{\x00\x04\x01*\xc7\xc0!\x00\x00' with make_file(data, '.fits') as fname: d = df.load_data(fname) assert df.find_factory(fname) is df.fits_reader assert_array_equal(d['a'], [1]) assert_array_equal(d['b'], [2]) @requires_astropy def test_fits_compressed(): # Regression test for bug that caused images with compressed image HDUs # to not be read d = df.load_data(os.path.join(DATA, 'compressed_image.fits'), factory=df.fits_reader) assert d.ndim == 2 @requires_astropy def test_fits_vector(): # Regression test for bug that caused tables with vector columns to not load with pytest.warns(UserWarning, match="Dropping column 'status' since it is not 1-dimensional"): df.load_data(os.path.join(DATA, 'events.fits'), factory=df.fits_reader) @requires_astropy def test_save_meta(): # Regression test for a bug that causes Data.meta to contain non-string # items when FITS comments were present. from glue.core.tests.test_state import clone data = df.load_data(os.path.join(DATA, 'comment.fits')) clone(data) @requires_astropy def test_coordinate_component_units(): # Regression test for a bug that caused units to be missing from coordinate # components. d = df.load_data(os.path.join(DATA, 'casalike.fits'), factory=df.casalike_cube) wid = d.world_component_ids assert wid[0].label == 'Stokes' assert d.get_component(wid[0]).units == '' assert wid[1].label == 'Vopt' assert d.get_component(wid[1]).units == 'm s-1' assert wid[2].label == 'Declination' assert d.get_component(wid[2]).units == 'deg' assert wid[3].label == 'Right Ascension' assert d.get_component(wid[3]).units == 'deg' glueviz-1.0.1+dfsg.orig/glue/core/data_factories/fits.py0000644000175000017500000001675113612622074022574 0ustar noahfxnoahfximport gzip import warnings from os.path import basename from collections import OrderedDict from glue.core.coordinates import coordinates_from_header, WCSCoordinates from glue.core.data import Component, Data from glue.config import data_factory, qglue_parser __all__ = ['is_fits', 'fits_reader', 'is_casalike', 'casalike_cube'] def is_fits(filename): # First check if the first few characters of the file are SIMPLE with open(filename, 'rb') as f: start = f.read(9) # Let's check if it could be a FITS file is uncompressed if not start == b'SIMPLE =': # It isn't, so maybe it's compressed? if start[:2] == b'\x1f\x8b': with gzip.GzipFile(filename) as gz: if not gz.read(9) == b'SIMPLE =': return False else: # Not gzip compressed, so not a FITS file return False from astropy.io import fits try: with warnings.catch_warnings(): warnings.simplefilter("ignore") with fits.open(filename, ignore_missing_end=True, mode='denywrite'): return True except IOError: return False @data_factory( label='FITS file', identifier=is_fits, priority=100, ) def fits_reader(source, auto_merge=False, exclude_exts=None, label=None): """ Read in all extensions from a FITS file. Parameters ---------- source: str or HDUList The pathname to the FITS file. If an HDUList is passed in, simply use that. auto_merge: bool Merge extensions that have the same shape and only one has a defined WCS. exclude_exts: [hdu, ] or [index, ] List of HDU's to exclude from reading. This can be a list of HDU's or a list of HDU indexes. """ from astropy.io import fits from astropy.table import Table exclude_exts = exclude_exts or [] if isinstance(source, fits.hdu.hdulist.HDUList): hdulist = source close_hdulist = False else: hdulist = fits.open(source, ignore_missing_end=True, mode='denywrite') hdulist.verify('fix') close_hdulist = True groups = OrderedDict() extension_by_shape = OrderedDict() if label is not None: label_base = label else: hdulist_name = hdulist.filename() if hdulist_name is None: hdulist_name = "HDUList" label_base = basename(hdulist_name).rpartition('.')[0] if not label_base: label_base = basename(hdulist_name) # Create a new image Data. def new_data(suffix=True): if suffix: label = '{0}[{1}]'.format(label_base, hdu_name) else: label = label_base data = Data(label=label) data.coords = coords # We need to be careful here because some header values are special # objects that we should convert to strings for key, value in hdu.header.items(): if (key == 'COMMENT' or key == 'HISTORY'): if key not in data.meta: data.meta[key] = [str(value)] else: data.meta[key].append(str(value)) elif isinstance(value, str) or isinstance(value, (int, float, bool)): data.meta[key] = value else: data.meta[key] = str(value) groups[hdu_name] = data extension_by_shape[shape] = hdu_name return data for extnum, hdu in enumerate(hdulist): hdu_name = hdu.name if hdu.name else "HDU{0}".format(extnum) if (hdu.data is not None and hdu.data.size > 0 and hdu_name not in exclude_exts and extnum not in exclude_exts): if is_image_hdu(hdu): shape = hdu.data.shape coords = coordinates_from_header(hdu.header) units = hdu.header.get('BUNIT') if not auto_merge or has_wcs(coords): data = new_data(suffix=len(hdulist) > 1) else: try: data = groups[extension_by_shape[shape]] except KeyError: data = new_data(suffix=len(hdulist) > 1) component = Component.autotyped(hdu.data, units=units) data.add_component(component=component, label=hdu_name) elif is_table_hdu(hdu): # Loop through columns and make component list table = Table.read(hdu, format='fits') label = '{0}[{1}]'.format(label_base, hdu_name) data = Data(label=label) groups[hdu_name] = data for column_name in table.columns: column = table[column_name] if column.ndim != 1: warnings.warn("Dropping column '{0}' since it is not 1-dimensional".format(column_name)) continue component = Component.autotyped(column, units=column.unit) data.add_component(component=component, label=column_name) if close_hdulist: hdulist.close() return [groups[idx] for idx in groups] # Utilities def is_image_hdu(hdu): from astropy.io.fits.hdu import PrimaryHDU, ImageHDU, CompImageHDU return isinstance(hdu, (PrimaryHDU, ImageHDU, CompImageHDU)) def is_table_hdu(hdu): from astropy.io.fits.hdu import TableHDU, BinTableHDU return isinstance(hdu, (TableHDU, BinTableHDU)) def has_wcs(coords): return (isinstance(coords, WCSCoordinates) and any(axis['coordinate_type'] is not None for axis in coords.get_axis_types())) def is_casalike(filename, **kwargs): """ Check if a FITS file is a CASA like cube, with (P, P, V, Stokes) layout """ from astropy.io import fits if not is_fits(filename): return False with fits.open(filename, ignore_missing_end=True, mode='denywrite') as hdulist: if len(hdulist) != 1: return False if hdulist[0].header['NAXIS'] != 4: return False from astropy.wcs import WCS w = WCS(hdulist[0].header) ax = [a.get('coordinate_type') for a in w.get_axis_types()] return ax == ['celestial', 'celestial', 'spectral', 'stokes'] @data_factory(label='CASA PPV Cube', identifier=is_casalike, deprecated=True) def casalike_cube(filename, **kwargs): """ This provides special support for 4D CASA FITS - like cubes, which have 2 spatial axes, a spectral axis, and a stokes axis in that order. Each stokes cube is split out as a separate component """ from astropy.io import fits result = Data() if 'ignore_missing_end' not in kwargs: kwargs['ignore_missing_end'] = True with fits.open(filename, mode='denywrite', **kwargs) as hdulist: array = hdulist[0].data header = hdulist[0].header result.coords = coordinates_from_header(header) for i in range(array.shape[0]): units = header.get('BUNIT') component = Component.autotyped(array[[i]], units=units) result.add_component(component, label='STOKES %i' % i) return result try: from astropy.io.fits import HDUList except ImportError: pass else: # Put HDUList parser before list parser @qglue_parser(HDUList, priority=100) def _parse_data_hdulist(data, label): from glue.core.data_factories.fits import fits_reader return fits_reader(data, label=label) glueviz-1.0.1+dfsg.orig/glue/core/data_factories/__init__.py0000644000175000017500000000046113614037533023357 0ustar noahfxnoahfxfrom .astropy_table import * # noqa from .dendrogram import * # noqa from .excel import * # noqa from .fits import * # noqa from .hdf5 import * # noqa from .helpers import * # noqa from .image import * # noqa from .numpy import * # noqa from .pandas import * # noqa from .tables import * # noqa glueviz-1.0.1+dfsg.orig/glue/core/data_factories/helpers.py0000644000175000017500000003050413660506557023273 0ustar noahfxnoahfx""" Factory methods to build Data objects from files Implementation notes: Each factory method conforms to the folowing structure, which helps the GUI Frontend easily load data: 1) The first argument is a file name to open 2) The return value is a Data object 3) The function should be decorated with data_factory and the decorator should be given a label parameter that describes (in human language) what kinds of files it understands, as well as a callable identifier parameter that returns whether it can handle a requested filename and keyword set Putting this together, the simplest data factory code looks like this:: from glue.config import data_factory @data_factory(label="Foo file", identifier=has_extension('foo FOO')) def dummy_factory(file_name): return glue.core.Data() """ import os import warnings from glue.core.contracts import contract from glue.core.coordinates import IdentityCoordinates from glue.core.component import CoordinateComponent from glue.core.data import Component, BaseData, Data from glue.config import auto_refresh, data_factory from glue.backends import get_timer from glue.utils import as_list from glue.logger import logger __all__ = ['FileWatcher', 'LoadLog', 'auto_data', 'data_label', 'find_factory', 'has_extension', 'load_data', '_extension'] def _extension(path): # extract the extension type from a path # test.fits -> fits # test.gz -> fits.gz (special case) # a.b.c.fits -> fits _, path = os.path.split(path) if '.' not in path: return '' stems = path.split('.')[1:] # special case: test.fits.gz -> fits.gz if len(stems) > 1 and any(x == stems[-1] for x in ['gz', 'gzip', 'bz', 'bz2']): return '.'.join(stems[-2:]) return stems[-1] def has_extension(exts): """ A simple default filetype identifier function It returns a function that tests whether its input filename contains a particular extension Parameters ---------- exts : str A space-delimited string listing the extensions (e.g., 'txt', or 'txt csv fits') Returns ------- A function suitable as a factory identifier function """ def tester(x, **kwargs): return _extension(x).lower() in set(exts.lower().split()) return tester class LoadLog(object): """ This class attaches some metadata to data created from load_data, so that the data can be re-constructed when loading saved state. It also watches the path for changes, and auto-reloads the data This is an internal class only meant to be used with load_data """ def __init__(self, path, factory, kwargs): self.path = os.path.abspath(path) self.factory = factory self.kwargs = kwargs self.components = [] self.data = [] if auto_refresh(): self.watcher = FileWatcher(path, self.reload) else: self.watcher = None self._absolute = True def _log_component(self, component): self.components.append(component) def _log_data(self, data): self.data.append(data) def log(self, obj): if isinstance(obj, Component): self._log_component(obj) elif isinstance(obj, Data): self._log_data(obj) obj._load_log = self def id(self, component): return self.components.index(component) def component(self, index): return self.components[index] def reload(self): """ Re-read files, and update data """ try: d = load_data(self.path, factory=self.factory, **self.kwargs) except (OSError, IOError) as exc: warnings.warn("Could not reload %s.\n%s" % (self.path, exc)) if self.watcher is not None: self.watcher.stop() return log = as_list(d)[0]._load_log for dold, dnew in zip(self.data, as_list(d)): if dold.shape != dnew.shape: warnings.warn("Cannot refresh data -- data shape changed") return mapping = dict((c, log.component(self.id(c)).data) for c in dold._components.values() if c in self.components and type(c) == Component) dold.coords = dnew.coords dold.update_components(mapping) def __gluestate__(self, context): if context.absolute_paths: path = os.path.abspath(self.path) else: path = os.path.relpath(self.path) # If there are twice as many world coordinates as number of dimensions # and Data.coords is set to Coordinates or None, we need to set # force_coords to True to make sure that we always restore world # coordinate components even if the transform is an identity transform. n_coords = len([comp for comp in self.components if isinstance(comp, CoordinateComponent)]) if n_coords == self.components[0].ndim * 2: force_coords = True else: force_coords = False return dict(path=path, factory=context.do(self.factory), kwargs=[list(self.kwargs.items())], force_coords=force_coords, _protocol=2) @classmethod def __setgluestate__(cls, rec, context): fac = context.object(rec['factory']) kwargs = dict(*rec['kwargs']) kwargs['coord_first'] = rec.get('_protocol', 0) >= 1 kwargs['force_coords'] = rec.get('_protocol', 0) < 2 or rec.get('force_coords') d = load_data(rec['path'], factory=fac, **kwargs) return as_list(d)[0]._load_log class FileWatcher(object): """ Watch a path for modifications, and perform an action on change """ def __init__(self, path, callback, poll_interval=1000): """ :param path: The path to watch, str :param callback: A function to call when the path changes :param poll_interval: Time to wait between checks, in ms """ self.path = path self.callback = callback self.poll_interval = poll_interval self.watcher = get_timer()(poll_interval, self.check_for_changes) try: self.stat_cache = os.stat(path).st_mtime self.start() except OSError: # file probably gone, no use watching self.stat_cache = None def stop(self): self.watcher.stop() def start(self): self.watcher.start() def check_for_changes(self): try: stat = os.stat(self.path).st_mtime except OSError: warnings.warn("Cannot access %s" % self.path) return if stat != self.stat_cache: self.stat_cache = stat self.callback() @contract(path='string', factory='callable|None', returns='inst($Data)|list(inst($Data))') def load_data(path, factory=None, **kwargs): """Use a factory to load a file and assign a label. This is the preferred interface for loading data into Glue, as it logs metadata about how data objects relate to files on disk. :param path: Path to a file :param factory: factory function to use. Defaults to :func:`auto_data` Extra keywords are passed through to factory functions """ from glue.qglue import parse_data coord_first = kwargs.pop('coord_first', True) force_coords = kwargs.pop('force_coords', False) def as_data_objects(ds, lbl): # pack other container types like astropy tables # into glue data objects for d in ds: if isinstance(d, BaseData): yield d continue for item in parse_data(d, lbl): yield item if factory is None: factory = find_factory(path, **kwargs) if factory is None: raise KeyError("Don't know how to open file: %s" % path) lbl = data_label(path) d = as_list(factory(path, **kwargs)) d = list(as_data_objects(d, lbl)) log = LoadLog(path, factory, kwargs) for item in d: # NOTE: The LoadLog infrastructure is specifically designed for Data # objects in mind and not more general data classes. if not isinstance(item, Data): continue if item.coords is None and force_coords: item.coords = IdentityCoordinates(n_dim=item.ndim) if not item.label: item.label = lbl log.log(item) # attaches log metadata to item if coord_first: # We just follow the order in which the components are now loaded, # which is coordinate components first, followed by all other # components for cid in item.coordinate_components + item.main_components: log.log(item.get_component(cid)) else: # In this case the first component was the first one that is not a # coordinate component, followed by the coordinate components, # followed by the remaining components. cid = item.main_components[0] log.log(item.get_component(cid)) for icid, cid in enumerate(item.coordinate_components): log.log(item.get_component(cid)) for icid, cid in enumerate(item.main_components[1:]): log.log(item.get_component(cid)) if len(d) == 1: # unpack single-length lists for user convenience return d[0] return d def data_label(path): """Convert a file path into a data label, by stripping out slashes, file extensions, etc.""" if os.path.basename(path) == '': path = os.path.dirname(path) _, fname = os.path.split(path) name, _ = os.path.splitext(fname) return name @contract(extension='string', factory='callable') def set_default_factory(extension, factory): # pragma: no cover warnings.warn("set_default_factory is deprecated and no longer has any effect") @contract(extension='string', returns='callable|None') def get_default_factory(extension): # pragma: no cover warnings.warn("get_default_factory is deprecated and will always return None") return None @contract(filename='string') def find_factory(filename, **kwargs): from glue.config import data_factory # We no longer try the 'default' factory first because we actually need to # try all identifiers and select the one to use based on the priority. This # allows us to define more specialized loaders take priority over more # general ones. For example, a FITS file that is a dendrogram should be # loaded as a dendrogram, not a plain FITS file. best_priority = None valid_formats = [] # Iterating over the data factory returns the formats sorted by decreasing # alphabetical order then by label (alphabetically) in order to be # deterministic. This is implemented in DataFactoryRegistry.__iter__. for df in data_factory: logger.info('Trying data factory {0}'.format(df.label)) # Once we've found a match, and iterated through the rest of the # importers with the same priority, we can exit the loop. if best_priority is not None and df.priority < best_priority: break if df.function is auto_data: continue try: is_format = df.identifier(filename, **kwargs) except ImportError: # dependencies missing continue except Exception: # any other issue continue if is_format: valid_formats.append(df) best_priority = df.priority logger.info('Valid formats: {0}'.format(valid_formats)) if len(valid_formats) == 0: return None elif len(valid_formats) > 1: labels = ["'{0}'".format(x.label) for x in valid_formats] warnings.warn("Multiple data factories matched the input: {0}. Choosing {1}.".format(', '.join(labels), labels[0])) func = valid_formats[0].function return func @data_factory(label='Auto', identifier=lambda x: True) @contract(filename='string') def auto_data(filename, *args, **kwargs): """Attempt to automatically construct a data object""" fac = find_factory(filename, **kwargs) if fac is None: raise KeyError("Don't know how to open file: %s" % filename) return fac(filename, *args, **kwargs) glueviz-1.0.1+dfsg.orig/glue/core/data_factories/pandas.py0000644000175000017500000000722413657331477023105 0ustar noahfxnoahfximport numpy as np import pandas as pd from glue.core.data_factories.helpers import has_extension from glue.core.component import Component, CategoricalComponent from glue.core.data import Data from glue.config import data_factory, data_translator __all__ = ['pandas_read_table'] def panda_process(indf): """ Build a data set from a table using pandas. This attempts to respect categorical data input by letting pandas.read_csv infer the type """ result = Data() for name, column in indf.iteritems(): if (column.dtype == np.object) | (column.dtype == np.bool): # try to salvage numerical data try: coerced = pd.to_numeric(column, errors='coerce') except AttributeError: # pandas < 0.19 coerced = column.convert_objects(convert_numeric=True) if (coerced.dtype != column.dtype) and coerced.isnull().mean() < 0.4: c = Component(coerced.values) else: # pandas has a 'special' nan implementation and this doesn't # play well with np.unique c = CategoricalComponent(np.array(column.fillna(''), dtype='U')) else: c = Component.autotyped(column.values) # convert header to string - in some cases if the first row contains # numbers, these are cast to numerical types, so we want to change that # here. if not isinstance(name, str): name = str(name) # strip off leading # name = name.strip() if name.startswith('#'): name = name[1:].strip() result.add_component(c, name) return result @data_factory(label="Pandas Table", identifier=has_extension('csv csv txt tsv tbl dat')) def pandas_read_table(path, **kwargs): """ A factory for reading tabular data using pandas :param path: path/to/file :param kwargs: All kwargs are passed to pandas.read_csv :returns: :class:`glue.core.data.Data` object """ try: from pandas.errors import ParserError except ImportError: try: from pandas.io.common import CParserError as ParserError except ImportError: # pragma: no cover try: from pandas.parser import CParserError as ParserError except ImportError: # pragma: no cover from pandas._parser import CParserError as ParserError # iterate over common delimiters to search for best option delimiters = kwargs.pop('delimiter', [None] + list(',|\t ')) fallback = None for d in delimiters: try: indf = pd.read_csv(path, delimiter=d, **kwargs) # ignore files parsed to empty dataframes if len(indf) == 0: continue # only use files parsed to single-column dataframes # if we don't find a better strategy if len(indf.columns) < 2: fallback = indf continue return panda_process(indf) except ParserError: continue if fallback is not None: return panda_process(fallback) raise IOError("Could not parse %s using pandas" % path) @data_translator(pd.DataFrame) class PandasTranslator: def to_data(self, obj): result = Data() for c in obj.columns: result.add_component(obj[c], str(c)) return result def to_object(self, data_or_subset, attribute=None): df = pd.DataFrame() coords = data_or_subset.coordinate_components for cid in data_or_subset.components: if cid not in coords: df[cid.label] = data_or_subset[cid] return df glueviz-1.0.1+dfsg.orig/glue/core/data_factories/astropy_table.py0000644000175000017500000001061413605357235024475 0ustar noahfxnoahfximport warnings import numpy as np from glue.core.data_factories.helpers import has_extension from glue.core.data import Component, Data from glue.config import data_factory, qglue_parser __all__ = ['astropy_tabular_data', 'sextractor_factory', 'cds_factory', 'daophot_factory', 'ipac_factory', 'aastex_factory', 'latex_factory'] # In this file, we define data factories based on the Astropy table reader. def is_readable_by_astropy(filename, **kwargs): # This identifier is not efficient, because it involves actually trying # to read in the table. However, we only use this as the identifier for # the astropy_tabular_data factory which has a priority of 0 and is # therefore only used as a last attempt if all else fails. try: astropy_table_read(filename, **kwargs) except Exception: return False else: return True def astropy_table_read(*args, **kwargs): from astropy.table import Table # In Python 3, as of Astropy 0.4, if the format is not specified, the # automatic format identification will fail (astropy/astropy#3013). # This is only a problem for ASCII formats however, because it is due # to the fact that the file object in io.ascii does not rewind to the # start between guesses (due to a bug), so here we can explicitly try # the ASCII format if the format keyword was not already present. But # also more generally, we should first try the ASCII readers. if 'format' not in kwargs: try: return Table.read(*args, format='ascii', **kwargs) except Exception: pass # If the above didn't work, attempt to read with no specified format return Table.read(*args, **kwargs) @data_factory(label="Catalog (astropy.table parser)", identifier=is_readable_by_astropy, priority=0) def astropy_tabular_data(*args, **kwargs): """ Build a data set from a table. We restrict ourselves to tables with 1D columns. All arguments are passed to astropy.table.Table.read(...). """ result = Data() with warnings.catch_warnings(): warnings.simplefilter("ignore") table = astropy_table_read(*args, **kwargs) result.meta = table.meta # Loop through columns and make component list for column_name in table.columns: c = table[column_name] u = c.unit if hasattr(c, 'unit') else c.units if hasattr(c, 'mask'): # fill array for now try: c = c.filled(fill_value=np.nan) except (ValueError, TypeError): # assigning nan to integer dtype c = c.filled(fill_value=-1) nc = Component.autotyped(c, units=u) result.add_component(nc, column_name) return result @data_factory(label="VO table", identifier=has_extension('xml vot xml.gz vot.gz'), priority=1) def astropy_tabular_data_votable(*args, **kwargs): kwargs['format'] = 'votable' return astropy_tabular_data(*args, **kwargs) @data_factory(label="FITS table", identifier=has_extension('fits fits.gz fit fit.gz'), priority=1) def astropy_tabular_data_fits(*args, **kwargs): kwargs['format'] = 'fits' return astropy_tabular_data(*args, **kwargs) # Add explicit factories for the formats which astropy.table # can parse, but does not auto-identify def formatted_table_factory(format, label): @data_factory(label=label, identifier=lambda *a, **k: False) def factory(file, **kwargs): kwargs['format'] = 'ascii.%s' % format return astropy_tabular_data(file, **kwargs) # rename function to its variable reference below # allows pickling to work factory.__name__ = '%s_factory' % format return factory sextractor_factory = formatted_table_factory('sextractor', 'SExtractor Catalog') cds_factory = formatted_table_factory('cds', 'CDS Catalog') daophot_factory = formatted_table_factory('daophot', 'DAOphot Catalog') ipac_factory = formatted_table_factory('ipac', 'IPAC Catalog') aastex_factory = formatted_table_factory('aastex', 'AASTeX Table') latex_factory = formatted_table_factory('latex', 'LaTeX Table') try: from astropy.table import Table except ImportError: pass else: @qglue_parser(Table) def _parse_data_astropy_table(data, label): kwargs = dict((c, data[c]) for c in data.columns) return [Data(label=label, **kwargs)] glueviz-1.0.1+dfsg.orig/glue/core/data_factories/tables.py0000644000175000017500000000136713605357235023104 0ustar noahfxnoahfxfrom glue.core.data_factories.helpers import has_extension from glue.config import data_factory __all__ = ['tabular_data'] @data_factory(label="ASCII Table", identifier=has_extension('csv txt tsv tbl dat ' 'csv.gz txt.gz tbl.bz ' 'dat.gz'), priority=1) def tabular_data(path, **kwargs): from glue.core.data_factories.astropy_table import astropy_tabular_data from glue.core.data_factories.pandas import pandas_read_table for fac in [astropy_tabular_data, pandas_read_table]: try: return fac(path, **kwargs) except Exception: pass else: raise IOError("Could not parse file: %s" % path) glueviz-1.0.1+dfsg.orig/glue/core/data_factories/dendrogram.py0000644000175000017500000000031313605357235023742 0ustar noahfxnoahfxfrom glue.core.data_factories.helpers import has_extension # noqa __all__ = [] try: from glue.core.data_factories.dendro_loader import load_dendro, is_dendro # noqa except ImportError: pass glueviz-1.0.1+dfsg.orig/glue/core/data_factories/image.py0000644000175000017500000000352513605357235022712 0ustar noahfxnoahfximport numpy as np from glue.core.coordinates import coordinates_from_wcs from glue.core.data_factories.helpers import has_extension from glue.core.data import Data from glue.config import data_factory IMG_FMT = ['jpg', 'jpeg', 'bmp', 'png', 'tiff', 'tif'] __all__ = ['img_data'] def img_loader(file_name): """Load an image to a numpy array, using either PIL or skimage :param file_name: Path of file to load :rtype: Numpy array """ try: from skimage import img_as_ubyte from skimage.io import imread return np.asarray(img_as_ubyte(imread(file_name))) except ImportError: pass try: from PIL import Image return np.asarray(Image.open(file_name)) except ImportError: raise ImportError("Reading %s requires PIL or scikit-image" % file_name) @data_factory(label='Image', identifier=has_extension(' '.join(IMG_FMT))) def img_data(file_name): """Load common image files into a Glue data object""" result = Data() data = img_loader(file_name) data = np.flipud(data) shp = data.shape comps = [] labels = [] # split 3 color images into each color plane if len(shp) == 3 and shp[2] in [3, 4]: comps.extend([data[:, :, 0], data[:, :, 1], data[:, :, 2]]) labels.extend(['red', 'green', 'blue']) if shp[2] == 4: comps.append(data[:, :, 3]) labels.append('alpha') else: comps = [data] labels = ['PRIMARY'] # look for AVM coordinate metadata try: from pyavm import AVM avm = AVM.from_image(str(file_name)) # avoid unicode wcs = avm.to_wcs() except Exception: pass else: result.coords = coordinates_from_wcs(wcs) for c, l in zip(comps, labels): result.add_component(c, l) return result glueviz-1.0.1+dfsg.orig/glue/core/data_factories/hdf5.py0000644000175000017500000001032513605357235022452 0ustar noahfxnoahfximport os import mmap import warnings from collections import OrderedDict import numpy as np from glue.core.data import Component, Data from glue.config import data_factory __all__ = ['is_hdf5', 'hdf5_reader'] def mmap_info_to_array(info, mapping): shape = info['shape'] dtype = info['dtype'] offset = info['offset'] length = np.prod(shape) return np.frombuffer(mapping, dtype=dtype, count=length, offset=offset).reshape(shape) def extract_hdf5_datasets(filename, memmap=True): """ Recursive function that returns a dictionary with all the datasets found in an HDF5 file or group. `handle` should be an instance of h5py.highlevel.File or h5py.highlevel.Group. """ import h5py file_handle = h5py.File(filename, 'r') arrays = {} from astropy.table import Table def visitor(name, item): if isinstance(item, h5py.Dataset): full_path = item.name if item.dtype.kind in ('f', 'i', 'S'): offset = item.id.get_offset() # If an offset is available, the data is contiguous and we can # use memory mapping for efficiency. if not memmap or offset is None: arrays[full_path] = item[()] else: arrays[full_path] = dict(offset=offset, shape=item.shape, dtype=item.dtype) elif item.dtype.kind in ('V',): arrays[full_path] = Table.read(item, format='hdf5') file_handle.visititems(visitor) file_handle.close() # Now create memory-mapped arrays with open(filename, 'rb') as data_file: fileno = data_file.fileno() mapping = mmap.mmap(fileno, 0, access=mmap.ACCESS_READ) for name, value in arrays.items(): if isinstance(value, dict): arrays[name] = mmap_info_to_array(value, mapping) return arrays def is_hdf5(filename): # All hdf5 files begin with the same sequence with open(filename, 'rb') as infile: return infile.read(8) == b'\x89HDF\r\n\x1a\n' @data_factory(label="HDF5 file", identifier=is_hdf5, priority=100) def hdf5_reader(filename, auto_merge=True, memmap=True, **kwargs): """ Read in all datasets from an HDF5 file Parameters ---------- filename : str The filename of the HDF5 file auto_merge : bool If all datasets have the same shape, and are at the base of the file, assume they are a column-based table and merge them into a single dataset. memmap : bool, optional Whether to use memory mapping """ from astropy.table import Table # Read in all datasets datasets = extract_hdf5_datasets(filename, memmap=memmap) label_base = os.path.basename(filename).rpartition('.')[0] if not label_base: label_base = os.path.basename(filename) if len(datasets) == 0: return if not auto_merge or len(datasets) == 1 or any([isinstance(data, Table) for data in datasets.values()]): merge_data = False else: reference_shape = list(datasets.values())[0].shape merge_data = all([data.shape == reference_shape and key.count('/') == 1 for key, data in datasets.items()]) groups = OrderedDict() data = None for key in datasets: label = '{0}[{1}]'.format(label_base, key) array = datasets[key] if isinstance(array, Table): data = Data(label=label) groups[label] = data for column_name in array.columns: column = array[column_name] if column.ndim == 1: component = Component.autotyped(column, units=column.unit) data.add_component(component=component, label=column_name) else: warnings.warn("HDF5: Ignoring vector column {0}".format(column_name)) else: if data is None and merge_data: data = Data(label=label_base) groups[label_base] = data elif not merge_data: data = Data(label=label) groups[label] = data data.add_component(component=datasets[key], label=key[1:]) return [groups[idx] for idx in sorted(groups)] glueviz-1.0.1+dfsg.orig/glue/core/data_factories/excel.py0000644000175000017500000000242513605357235022726 0ustar noahfxnoahfximport os from glue.core.data_factories.helpers import has_extension from glue.core.data_factories.pandas import panda_process from glue.config import data_factory __all__ = [] @data_factory(label="Excel", identifier=has_extension('xls xlsx')) def panda_read_excel(path, sheet=None, **kwargs): """ A factory for reading excel data using pandas. :param path: path/to/file :param sheet: The sheet to read. If `None`, all sheets are read. :param kwargs: All other kwargs are passed to pandas.read_excel :return: core.data.Data object. """ try: import pandas as pd except ImportError: raise ImportError('Pandas is required for Excel input.') try: import xlrd except ImportError: raise ImportError('xlrd is required for Excel input.') name = os.path.basename(path) if '.xls' in name: name = name.rsplit('.xls', 1)[0] xl_workbook = xlrd.open_workbook(path) if sheet is None: sheet_names = xl_workbook.sheet_names() else: sheet_names = [sheet] all_data = [] for sheet in sheet_names: indf = pd.read_excel(path, sheet, **kwargs) data = panda_process(indf) data.label = "{0}:{1}".format(name, sheet) all_data.append(data) return all_data glueviz-1.0.1+dfsg.orig/glue/core/data_factories/numpy.py0000644000175000017500000000305713605357235023000 0ustar noahfxnoahfxfrom glue.core.data import Data, Component from glue.config import data_factory from glue.core.data_factories.helpers import has_extension __all__ = ['is_npy_npz', 'npy_npz_reader'] def is_npy_npz(filename): """ The first bytes are x93NUMPY (for npy) or PKx03x04 (for npz) """ # See: https://github.com/numpy/numpy/blob/master/doc/neps/npy-format.rst from numpy.lib.format import MAGIC_PREFIX MAGIC_PREFIX_NPZ = b'PK\x03\x04' # first 4 bytes for a zipfile tester = has_extension('npz .npz') with open(filename, 'rb') as infile: prefix = infile.read(6) return prefix == MAGIC_PREFIX or (tester(filename) and prefix[:4] == MAGIC_PREFIX_NPZ) @data_factory(label="Numpy save file", identifier=is_npy_npz, priority=100) def npy_npz_reader(filename, format='auto', auto_merge=False, **kwargs): """ Read in a Numpy structured array saved to a .npy or .npz file. Parameters ---------- source: str The pathname to the Numpy save file. """ import numpy as np data = np.load(filename) if isinstance(data, np.ndarray): data = {None: data} groups = [] for groupname in sorted(data): d = Data(label=groupname) arr = data[groupname] if arr.dtype.names is None: comp = Component.autotyped(arr) d.add_component(comp, label='array') else: for name in arr.dtype.names: comp = Component.autotyped(arr[name]) d.add_component(comp, label=name) groups.append(d) return groups glueviz-1.0.1+dfsg.orig/glue/core/data_factories/deprecated.py0000644000175000017500000001032113605357235023720 0ustar noahfxnoahfxfrom glue.core.data_factories.hdf5 import is_hdf5, extract_hdf5_datasets from glue.core.data_factories.fits import is_fits, is_image_hdu from glue.core.coordinates import coordinates_from_header from glue.core.data import Component, Data from glue.config import data_factory from glue.utils import file_format __all__ = ['is_gridded_data', 'gridded_data'] def extract_data_hdf5(filename, use_datasets='all'): """ Extract non-tabular datasets from an HDF5 file. If `use_datasets` is 'all', then all non-tabular datasets are extracted, otherwise only the ones specified by `use_datasets` are extracted (`use_datasets` should then contain a list of paths). If the requested datasets do not have the same dimensions, an Exception is raised. """ # Read in all datasets datasets = extract_hdf5_datasets(filename) # Only keep non-tabular datasets remove = [] for key in datasets: if datasets[key].dtype.fields is not None: remove.append(key) for key in remove: datasets.pop(key) # Check that dimensions of all datasets are the same reference_shape = datasets[list(datasets.keys())[0]].shape for key in datasets: if datasets[key].shape != reference_shape: raise Exception("Datasets are not all the same dimensions") # Extract data arrays = {} for key in datasets: arrays[key] = datasets[key] return arrays def filter_hdulist_by_shape(hdulist, use_hdu='all'): """ Remove empty HDUs, and ensure that all HDUs can be packed into a single Data object (ie have the same shape) Parameters ---------- use_hdu : 'all' or list of integers (optional) Which HDUs to use Returns ------- a new HDUList """ from astropy.io import fits # If only a subset are requested, extract those if use_hdu != 'all': hdulist = [hdulist[hdu] for hdu in use_hdu] # Now only keep HDUs that are not tables or empty. valid_hdus = [] for hdu in hdulist: if (isinstance(hdu, fits.PrimaryHDU) or isinstance(hdu, fits.ImageHDU)) and \ hdu.data is not None: valid_hdus.append(hdu) # Check that dimensions of all HDU are the same # Allow for HDU's that have no data. reference_shape = valid_hdus[0].data.shape for hdu in valid_hdus: if hdu.data.shape != reference_shape: raise Exception("HDUs are not all the same dimensions") return valid_hdus def extract_data_fits(filename, use_hdu='all'): """ Extract non-tabular HDUs from a FITS file. If `use_hdu` is 'all', then all non-tabular HDUs are extracted, otherwise only the ones specified by `use_hdu` are extracted (`use_hdu` should then contain a list of integers). If the requested HDUs do not have the same dimensions, an Exception is raised. """ from astropy.io import fits # Read in all HDUs hdulist = fits.open(filename, ignore_blank=True) hdulist = filter_hdulist_by_shape(hdulist) # Extract data arrays = {} for hdu in hdulist: arrays[hdu.name] = hdu.data return arrays def is_gridded_data(filename, **kwargs): if is_hdf5(filename): return True if is_fits(filename): from astropy.io import fits with fits.open(filename) as hdulist: return is_image_hdu(hdulist[0]) return False @data_factory(label="FITS/HDF5 Image", identifier=is_gridded_data, deprecated=True) def gridded_data(filename, format='auto', **kwargs): result = Data() # Try and automatically find the format if not specified if format == 'auto': format = file_format(filename) # Read in the data if is_fits(filename): from astropy.io import fits arrays = extract_data_fits(filename, **kwargs) header = fits.getheader(filename) result.coords = coordinates_from_header(header) elif is_hdf5(filename): arrays = extract_data_hdf5(filename, **kwargs) else: raise Exception("Unkonwn format: %s" % format) for component_name in arrays: comp = Component.autotyped(arrays[component_name]) result.add_component(comp, component_name) return result glueviz-1.0.1+dfsg.orig/glue/core/data_combo_helper.py0000644000175000017500000004644613657331513022316 0ustar noahfxnoahfx# Combo helpers independent of GUI framework - these operate on # SelectionCallbackProperty objects. import weakref from glue.core import BaseData, Subset from glue.core.hub import HubListener from glue.core.message import (DataReorderComponentMessage, ComponentsChangedMessage, DataCollectionAddMessage, DataCollectionDeleteMessage, DataUpdateMessage, DataRenameComponentMessage) from echo import delay_callback, ChoiceSeparator __all__ = ['ComponentIDComboHelper', 'ManualDataComboHelper', 'DataCollectionComboHelper', 'ComboHelper', 'BaseDataComboHelper'] def unique_data_iter(datasets): """ Return a list with only Data objects, with duplicates removed, but preserving the original order. """ datasets_new = [] for dataset in datasets: if isinstance(dataset, BaseData): if dataset not in datasets_new: datasets_new.append(dataset) else: if dataset.data not in datasets_new: datasets_new.append(dataset.data) return datasets_new class ComboHelper(HubListener): """ Base class for any combo helper represented by a SelectionCallbackProperty. This stores the state and selection property and exposes the ``state``, ``selection`` and ``choices`` properties. Parameters ---------- state : :class:`~glue.core.state_objects.State` The state to which the selection property belongs selection_property : :class:`~echo.SelectionCallbackProperty` The selection property representing the combo. """ def __init__(self, state, selection_property): self._state = weakref.ref(state) self.selection_property = selection_property @property def state(self): """ The state to which the selection property belongs. """ return self._state() @property def selection(self): """ The current selected value. """ return getattr(self.state, self.selection_property) @selection.setter def selection(self, selection): return setattr(self.state, self.selection_property, selection) @property def choices(self): """ The current valid choices for the combo. """ prop = getattr(type(self.state), self.selection_property) return prop.get_choices(self.state) @choices.setter def choices(self, choices): with delay_callback(self.state, self.selection_property): prop = getattr(type(self.state), self.selection_property) prop.set_choices(self.state, choices) @property def display(self): """ The current display function for the combo (the function that relates the Python objects to the display label) """ prop = getattr(type(self.state), self.selection_property) return prop.get_display_func(self.state) @display.setter def display(self, display): prop = getattr(type(self.state), self.selection_property) return prop.set_display_func(self.state, display) def _on_rename(self, msg): # If a component ID is renamed, we don't need to refresh because the # list of actual component IDs is the same as before. However, we do # need to trigger a refresh of any GUI combos that use this, so we # make the property notify a change. However, if we are inside a # delay_callback block, the property will not be enabled, and notify() # won't have any effect, in which case we set the 'force_next_sync' # option which means that when exiting from the delay_callback block, # this property will show up as having changed prop = getattr(type(self.state), self.selection_property) if prop.enabled(self.state): prop.notify(self.state, self.selection, self.selection) else: prop.force_next_sync(self.state) class ComponentIDComboHelper(ComboHelper): """ The purpose of this class is to set up a combo (represented by a SelectionCallbackProperty) showing componentIDs for one or more datasets, and to update these componentIDs if needed, for example if new components are added to a dataset, or if componentIDs are renamed. This is a GUI framework-independent implementation. Parameters ---------- state : :class:`~glue.core.state_objects.State` The state to which the selection property belongs selection_property : :class:`~echo.SelectionCallbackProperty` The selection property representing the combo. data_collection : :class:`~glue.core.data_collection.DataCollection`, optional The data collection to which the datasets belong - if specified, this is used to remove datasets from the combo when they are removed from the data collection. data : :class:`~glue.core.data.Data`, optional If specified, set up the combo for this dataset only and don't allow datasets to be added/removed numeric : bool, optional Show numeric components datetime : bool, optional Show datetime components categorical : bool, optional Show categorical components pixel_coord : bool, optional Show pixel coordinate components world_coord : bool, optional Show world coordinate components derived : bool, optional Show derived components none : bool or str, optional Add an entry that means `None`. If a string, this is the display string that will be shown for the `None` entry, otherwise an empty string is shown. """ def __init__(self, state, selection_property, data_collection=None, data=None, numeric=True, datetime=True, categorical=True, pixel_coord=False, world_coord=False, derived=True, none=False): super(ComponentIDComboHelper, self).__init__(state, selection_property) if isinstance(none, str): self._none = True self._none_label = none else: self._none = none self._none_label = '' def display_func_label(cid): if cid is None: return self._none_label else: return cid.label self.display = display_func_label self._numeric = numeric self._datetime = datetime self._categorical = categorical self._pixel_coord = pixel_coord self._world_coord = world_coord self._derived = derived if data is None: self._manual_data = False self._data = [] else: self._manual_data = True self._data = [data] self._data_collection = data_collection if data_collection is None: self.hub = None else: if data_collection.hub is None: raise ValueError("Hub on data collection is not set") else: self.hub = data_collection.hub if data is not None: self.refresh() def clear(self): self._data.clear() self.refresh() @property def numeric(self): return self._numeric @numeric.setter def numeric(self, value): self._numeric = value self.refresh() @property def datetime(self): return self._datetime @datetime.setter def datetime(self, value): self._datetime = value self.refresh() @property def categorical(self): return self._categorical @categorical.setter def categorical(self, value): self._categorical = value self.refresh() @property def pixel_coord(self): return self._pixel_coord @pixel_coord.setter def pixel_coord(self, value): self._pixel_coord = value self.refresh() @property def world_coord(self): return self._world_coord @world_coord.setter def world_coord(self, value): self._world_coord = value self.refresh() @property def derived(self): return self._derived @derived.setter def derived(self, value): self._derived = value self.refresh() @property def none(self): return self._none @none.setter def none(self, value): if isinstance(value, str): self._none = True self._none_label = value else: self._none = value self._none_label = '' self.refresh() def append_data(self, data, refresh=True): if self._manual_data: raise Exception("Cannot change data in ComponentIDComboHelper " "initialized from a single dataset") if isinstance(data, Subset): data = data.data if self.hub is None: if data.hub is not None: self.hub = data.hub elif data.hub is not self.hub: raise ValueError("Data Hub is different from current hub") if data not in self._data: self._data.append(data) if refresh: self.refresh() def remove_data(self, data): if self._manual_data: raise Exception("Cannot change data in ComponentIDComboHelper " "initialized from a single dataset") if data in self._data: self._data.remove(data) self.refresh() def set_multiple_data(self, datasets): """ Add multiple datasets to the combo in one go (and clear any previous datasets). Parameters ---------- datasets : list The list of :class:`~glue.core.data.Data` objects to add """ if self._manual_data: raise Exception("Cannot change data in ComponentIDComboHelper " "initialized from a single dataset") self._data.clear() for data in unique_data_iter(datasets): self.append_data(data, refresh=False) self.refresh() @property def hub(self): return self._hub @hub.setter def hub(self, value): self._hub = value if value is not None: self.register_to_hub(value) def refresh(self, *args): choices = [] if self._none: choices.append(None) for data in self._data: derived_components = [cid for cid in data.derived_components if cid.parent is data] if len(self._data) > 1: if data.label is None or data.label == '': choices.append(ChoiceSeparator('Untitled Data')) else: choices.append(ChoiceSeparator(data.label)) cids = [ChoiceSeparator('Main components')] for cid in data.main_components: if ((data.get_kind(cid) == 'numerical' and self.numeric) or (data.get_kind(cid) == 'datetime' and self.datetime) or (data.get_kind(cid) == 'categorical' and self.categorical)): cids.append(cid) if len(cids) > 1: if self.pixel_coord or self.world_coord or (self.derived and len(derived_components) > 0): choices += cids else: choices += cids[1:] if self.numeric and self.derived: cids = [ChoiceSeparator('Derived components')] for cid in derived_components: cids.append(cid) if len(cids) > 1: choices += cids if self.pixel_coord or self.world_coord: cids = [ChoiceSeparator('Coordinate components')] if self.pixel_coord: cids += data.pixel_component_ids if self.world_coord: cids += data.world_component_ids if len(cids) > 1: choices += cids self.choices = choices def _filter_msg(self, msg): return msg.sender in self._data def register_to_hub(self, hub): hub.subscribe(self, DataRenameComponentMessage, handler=self._on_rename, filter=self._filter_msg) hub.subscribe(self, DataReorderComponentMessage, handler=self.refresh, filter=self._filter_msg) hub.subscribe(self, ComponentsChangedMessage, handler=self.refresh, filter=self._filter_msg) if self._data_collection is not None: hub.subscribe(self, DataCollectionDeleteMessage, handler=self._remove_data) def _remove_data(self, msg): self.remove_data(msg.data) def unregister(self, hub): hub.unsubscribe_all(self) class BaseDataComboHelper(ComboHelper): """ This is a base class for helpers for combo boxes that need to show a list of data objects. Parameters ---------- state : :class:`~glue.core.state_objects.State` The state to which the selection property belongs selection_property : :class:`~echo.SelectionCallbackProperty` The selection property representing the combo. data_collection : :class:`~glue.core.data_collection.DataCollection` The data collection to which the datasets belong - this is needed because if a dataset is removed from the data collection, we want to remove it here. """ def __init__(self, state, selection_property, data_collection=None): super(BaseDataComboHelper, self).__init__(state, selection_property) def display_func_label(cid): return cid.label self.display = display_func_label self._component_id_helpers = [] self.state.add_callback(self.selection_property, self.refresh_component_ids) self._data_collection = data_collection if data_collection is not None: if data_collection.hub is None: raise ValueError("Hub on data collection is not set") else: self.hub = data_collection.hub else: self.hub = None def refresh(self, *args): self.choices = [data for data in self._datasets] self.refresh_component_ids() def refresh_component_ids(self, *args): data = getattr(self.state, self.selection_property) for helper in self._component_id_helpers: helper.clear() if data is not None: helper.append_data(data) helper.refresh() def add_component_id_combo(self, combo): helper = ComponentIDComboHelper(combo) self._component_id_helpers.append_data(helper) if self._data is not None: helper.append_data(self._data) @property def hub(self): return self._hub @hub.setter def hub(self, value): self._hub = value if value is not None: self.register_to_hub(value) def register_to_hub(self, hub): pass def _on_data_update(self, msg): if msg.attribute == 'label': self._on_rename(msg) else: self.refresh() class ManualDataComboHelper(BaseDataComboHelper): """ This is a helper for combo boxes that need to show a list of data objects that is manually curated. Datasets are added and removed using the :meth:`~ManualDataComboHelper.append_data` and :meth:`~ManualDataComboHelper.remove_data` methods. Parameters ---------- state : :class:`~glue.core.state_objects.State` The state to which the selection property belongs selection_property : :class:`~echo.SelectionCallbackProperty` The selection property representing the combo. data_collection : :class:`~glue.core.data_collection.DataCollection` The data collection to which the datasets belong - this is needed because if a dataset is removed from the data collection, we want to remove it here. """ def __init__(self, state, selection_property, data_collection=None): super(ManualDataComboHelper, self).__init__(state, selection_property, data_collection=data_collection) self._datasets = [] def set_multiple_data(self, datasets): """ Add multiple datasets to the combo in one go (and clear any previous datasets). Parameters ---------- datasets : list The list of :class:`~glue.core.data.Data` objects to add """ self._datasets.clear() for data in unique_data_iter(datasets): self.append_data(data, refresh=False) self.refresh() def append_data(self, data, refresh=True): if data in self._datasets: return if self.hub is None and data.hub is not None: self.hub = data.hub self._datasets.append(data) if refresh: self.refresh() def remove_data(self, data): if data not in self._datasets: return self._datasets.remove(data) self.refresh() def _remove_data_msg(self, msg): self.remove_data(msg.data) def _filter_msg(self, msg): return msg.sender in self._datasets def _filter_msg_dc(self, msg): return msg.sender is self._data_collection def register_to_hub(self, hub): super(ManualDataComboHelper, self).register_to_hub(hub) hub.subscribe(self, DataUpdateMessage, handler=self._on_data_update, filter=self._filter_msg) hub.subscribe(self, DataCollectionDeleteMessage, handler=self._remove_data_msg, filter=self._filter_msg_dc) class DataCollectionComboHelper(BaseDataComboHelper): """ This is a helper for combo boxes that need to show a list of data objects that is always in sync with a :class:`~glue.core.data_collection.DataCollection`. Parameters ---------- state : :class:`~glue.core.state_objects.State` The state to which the selection property belongs selection_property : :class:`~echo.SelectionCallbackProperty` The selection property representing the combo. data_collection : :class:`~glue.core.data_collection.DataCollection` The data collection with which to stay in sync """ def __init__(self, state, selection_property, data_collection): super(DataCollectionComboHelper, self).__init__(state, selection_property, data_collection=data_collection) self._datasets = data_collection self.refresh() def _filter_msg_in(self, msg): return msg.sender in self._datasets def _filter_msg_is(self, msg): return msg.sender is self._datasets def register_to_hub(self, hub): super(DataCollectionComboHelper, self).register_to_hub(hub) hub.subscribe(self, DataUpdateMessage, handler=self._on_data_update, filter=self._filter_msg_in) hub.subscribe(self, DataCollectionAddMessage, handler=self.refresh, filter=self._filter_msg_is) hub.subscribe(self, DataCollectionDeleteMessage, handler=self.refresh, filter=self._filter_msg_is) glueviz-1.0.1+dfsg.orig/glue/core/component.py0000644000175000017500000003631113752534502020656 0ustar noahfxnoahfximport logging import numpy as np import pandas as pd from glue.core.coordinate_helpers import dependent_axes, pixel2world_single_axis from glue.utils import (shape_to_string, coerce_numeric, broadcast_to, categorical_ndarray) try: import dask.array as da DASK_INSTALLED = True except ImportError: DASK_INSTALLED = False __all__ = ['Component', 'DerivedComponent', 'CategoricalComponent', 'CoordinateComponent', 'DateTimeComponent'] class Component(object): """ Stores the actual, numerical information for a particular quantity Data objects hold one or more components, accessed via ComponentIDs. All Components in a data set must have the same shape and number of dimensions Notes ----- Instead of instantiating Components directly, consider using :meth:`Component.autotyped`, which chooses a subclass most appropriate for the data type. """ def __init__(self, data, units=None): """ :param data: The data to store :type data: :class:`numpy.ndarray` :param units: Optional unit label :type units: str """ # The physical units of the data self.units = units # The actual data # subclasses may pass non-arrays here as placeholders. if isinstance(data, np.ndarray): if data.dtype.kind == 'M': raise TypeError('DateTimeComponent should be used instead of Component for np.datetime64 arrays') data = coerce_numeric(data) data.setflags(write=False) # data is read-only self._data = data @property def units(self): return self._units @units.setter def units(self, value): if value is None: self._units = '' else: self._units = str(value) @property def data(self): """ The underlying :class:`numpy.ndarray` """ return self._data @property def shape(self): """ Tuple of array dimensions """ return self._data.shape @property def ndim(self): """ The number of dimensions """ return len(self._data.shape) def __getitem__(self, key): logging.debug("Using %s to index data of shape %s", key, self.shape) return self._data[key] @property def numeric(self): """ Whether or not the datatype is numeric """ # We need to be careful here to not just access self.data since that # would force the computation of the whole component in the case of # derived components, so instead we specifically only get the first # element. return np.can_cast(self[(0,) * self.ndim].dtype, np.complex) @property def categorical(self): """ Whether or not the datatype is categorical """ return False @property def datetime(self): """ Whether or not or not the datatype is a date/time """ return False def __str__(self): return "%s with shape %s" % (self.__class__.__name__, shape_to_string(self.shape)) def jitter(self, method=None): raise NotImplementedError def to_series(self, **kwargs): """ Convert into a pandas.Series object. :param kwargs: All kwargs are passed to the Series constructor. :return: pandas.Series """ return pd.Series(self.data.ravel(), **kwargs) @classmethod def autotyped(cls, data, units=None): """ Automatically choose between Component and CategoricalComponent, based on the input data type. :param data: The data to pack into a Component (array-like) :param units: Optional units :type units: str :returns: A Component (or subclass) """ if DASK_INSTALLED and isinstance(data, da.Array): return DaskComponent(data, units=units) data = np.asarray(data) if np.issubdtype(data.dtype, np.object_): return CategoricalComponent(data, units=units) if data.dtype.kind == 'M': return DateTimeComponent(data) n = coerce_numeric(data) thresh = 0.5 try: use_categorical = np.issubdtype(data.dtype, np.character) and \ np.isfinite(n).mean() <= thresh except TypeError: # isfinite not supported. non-numeric dtype use_categorical = True if use_categorical: return CategoricalComponent(data, units=units) else: return Component(n, units=units) class DerivedComponent(Component): """ A component which derives its data from a function """ def __init__(self, data, link, units=None): """ :param data: The data object to use for calculation :type data: :class:`~glue.core.data.Data` :param link: The link that carries out the function :type link: :class:`~glue.core.component_link.ComponentLink` :param units: Optional unit description """ super(DerivedComponent, self).__init__(data, units=units) self._link = link def set_parent(self, data): """ Reassign the Data object that this DerivedComponent operates on """ self._data = data @property def data(self): """ Return the numerical data as a numpy array """ return self._link.compute(self._data) @property def link(self): """ Return the component link """ return self._link def __getitem__(self, key): return self._link.compute(self._data, key) class CoordinateComponent(Component): """ Components associated with pixel or world coordinates The numerical values are computed on the fly. """ def __init__(self, data, axis, world=False): self.world = world self._data = data self.axis = axis @property def data(self): return self._calculate() @property def units(self): if self.world: return self._data.coords.world_axis_units[self._data.ndim - 1 - self.axis] else: return '' def _calculate(self, view=None): if self.world: # Calculating the world coordinates can be a bottleneck if we aren't # careful, so we need to make sure that if not all dimensions depend # on each other, we use smart broadcasting. # The unoptimized way to do this for an N-dimensional dataset would # be to construct N-dimensional arrays of pixel values for each # coordinate. However, if we are computing the coordinates for axis # i, and axis i is not dependent on any other axis, then the result # will be an N-dimensional array where the same 1D array of # coordinates will be repeated over and over. # To optimize this, we therefore essentially consider only the # dependent dimensions and then broacast the result to the full # array size at the very end. # view=None actually adds a dimension which is never what we really # mean, at least in glue. if view is None: view = Ellipsis # If the view is a tuple or list of arrays, we should actually just # convert these straight to world coordinates since the indices # of the pixel coordinates are the pixel coordinates themselves. if isinstance(view, (tuple, list)) and isinstance(view[0], np.ndarray): axis = self._data.ndim - 1 - self.axis return pixel2world_single_axis(self._data.coords, *view[::-1], world_axis=axis) # For 1D arrays, slice can be given as a single slice but we need # to wrap it in a list to make the following code work correctly, # as it is then consistent with higher-dimensional cases. if isinstance(view, slice) or np.isscalar(view): view = [view] # Some views, e.g. with lists of integer arrays, can give arbitrarily # complex (copied) subsets of arrays, so in this case we don't do any # optimization if view is Ellipsis: optimize_view = False else: for v in view: if not np.isscalar(v) and not isinstance(v, slice): optimize_view = False break else: optimize_view = True pix_coords = [] dep_coords = dependent_axes(self._data.coords, self.axis) final_slice = [] final_shape = [] for i in range(self._data.ndim): if optimize_view and i < len(view) and np.isscalar(view[i]): final_slice.append(0) else: final_slice.append(slice(None)) # We set up a 1D pixel axis along that dimension. pix_coord = np.arange(self._data.shape[i]) # If a view was specified, we need to take it into account for # that axis. if optimize_view and i < len(view): pix_coord = pix_coord[view[i]] if not np.isscalar(view[i]): final_shape.append(len(pix_coord)) else: final_shape.append(self._data.shape[i]) if i not in dep_coords: # The axis is not dependent on this instance's axis, so we # just compute the values once and broadcast along this # dimension later. pix_coord = 0 pix_coords.append(pix_coord) # We build the list of N arrays, one for each pixel coordinate pix_coords = np.meshgrid(*pix_coords, indexing='ij', copy=False) # Finally we convert these to world coordinates axis = self._data.ndim - 1 - self.axis world_coords = pixel2world_single_axis(self._data.coords, *pix_coords[::-1], world_axis=axis) # We get rid of any dimension for which using the view should get # rid of that dimension. if optimize_view: world_coords = world_coords[tuple(final_slice)] # We then broadcast the final array back to what it should be world_coords = broadcast_to(world_coords, tuple(final_shape)) # We apply the view if we weren't able to optimize before if optimize_view: return world_coords else: return world_coords[view] else: slices = [slice(0, s, 1) for s in self.shape] grids = np.broadcast_arrays(*np.ogrid[slices]) if view is not None: grids = [g[view] for g in grids] return grids[self.axis] @property def shape(self): """ Tuple of array dimensions. """ return self._data.shape @property def ndim(self): """ Number of dimensions """ return len(self._data.shape) def __getitem__(self, key): return self._calculate(key) def __lt__(self, other): if self.world == other.world: return self.axis < other.axis return self.world def __gluestate__(self, context): return dict(axis=self.axis, world=self.world) @classmethod def __setgluestate__(cls, rec, context): return cls(None, rec['axis'], rec['world']) @property def numeric(self): return True @property def categorical(self): return False class CategoricalComponent(Component): """ Container for categorical data. """ def __init__(self, categorical_data, categories=None, jitter=None, units=None): """ :param categorical_data: The underlying :class:`numpy.ndarray` :param categories: List of unique values in the data :jitter: Strategy for jittering the data """ # TOOD: deal with custom categories super(CategoricalComponent, self).__init__(None, units) self._data = categorical_ndarray(categorical_data, copy=False, categories=categories) if self._data.ndim != 1: raise ValueError("Categorical Data must be 1-dimensional") self.jitter(method=jitter) @property def codes(self): """ The index of the category for each value in the array. """ return self._data.codes @property def labels(self): """ The original categorical data. """ return self._data.view(np.ndarray) @property def categories(self): """ The categories. """ return self._data.categories @property def data(self): return self._data @property def numeric(self): return False @property def categorical(self): return True def jitter(self, method=None): """ Jitter the codes so the density of points can be easily seen in a scatter plot for example. Parameters ---------- method : {None, 'uniform'} If `None`, not jittering is done (or any jittering is undone). If ``'uniform'``, the codes are randomized by a uniformly distributed random variable. """ self._data.jitter(method=method) self.jitter_method = method def to_series(self, **kwargs): """ Convert into a pandas.Series object. This will be converted as a dtype=np.object! :param kwargs: All kwargs are passed to the Series constructor. :return: pandas.Series """ return pd.Series(self.labels, dtype=np.object, **kwargs) class DateTimeComponent(Component): """ A component representing a date/time. Parameters ---------- data : `~numpy.ndarray` The data to store, with `~numpy.datetime64` dtype """ def __init__(self, data, units=None): self.units = units if not isinstance(data, np.ndarray) or data.dtype.kind != 'M': raise TypeError("DateTimeComponent should be initialized with a datetim64 Numpy array") self._data = data @property def numeric(self): return True @property def datetime(self): return True class DaskComponent(Component): """ A data component powered by a dask array. """ def __init__(self, data, units=None): self._data = data self.units = units @property def units(self): return self._units @units.setter def units(self, value): if value is None: self._units = '' else: self._units = str(value) @property def data(self): return self._data @property def shape(self): return self._data.shape @property def ndim(self): return len(self._data.shape) def __getitem__(self, key): return self._data[key].compute() @property def numeric(self): return True @property def categorical(self): return False @property def datetime(self): return False glueviz-1.0.1+dfsg.orig/glue/core/subset.py0000644000175000017500000016630613752534424020174 0ustar noahfxnoahfximport uuid import numbers import operator import numpy as np from glue.core.roi import (PolygonalROI, CategoricalROI, RangeROI, XRangeROI, YRangeROI, RectangularROI, CircularROI, EllipticalROI, Projected3dROI) from glue.core.contracts import contract from glue.core.util import split_component_view from glue.core.registry import Registry from glue.core.exceptions import IncompatibleAttribute from glue.core.message import SubsetDeleteMessage, SubsetUpdateMessage from glue.core.decorators import memoize from glue.core.visual import VisualAttributes from glue.config import settings from glue.utils import (view_shape, broadcast_to, floodfill, combine_slices, polygon_line_intersections, categorical_ndarray, iterate_chunks) __all__ = ['Subset', 'SubsetState', 'RoiSubsetStateNd', 'RoiSubsetState', 'CategoricalROISubsetState', 'RangeSubsetState', 'MultiRangeSubsetState', 'CompositeSubsetState', 'OrState', 'AndState', 'XorState', 'InvertState', 'MaskSubsetState', 'CategorySubsetState', 'ElementSubsetState', 'InequalitySubsetState', 'combine_multiple', 'CategoricalMultiRangeSubsetState', 'CategoricalROISubsetState2D', 'SliceSubsetState', 'roi_to_subset_state', 'MultiOrState'] OPSYM = {operator.ge: '>=', operator.gt: '>', operator.le: '<=', operator.lt: '<', operator.and_: '&', operator.or_: '|', operator.xor: '^', operator.eq: '==', operator.ne: '!='} SYMOP = dict((v, k) for k, v in OPSYM.items()) class Subset(object): """Base class to handle subsets of data. These objects both describe subsets of a dataset, and relay any state changes to the hub that their parent data are assigned to. This base class only directly implements the logic that relays state changes back to the hub. Subclasses implement the actual description and manipulation of data subsets :param data: The dataset that this subset describes :type data: :class:`~glue.core.data.Data` """ @contract(data='isinstance(Data)|None', color='color', alpha=float, label='string|None') def __init__(self, data, color=settings.SUBSET_COLORS[0], alpha=0.5, label=None): """ Create a new subset object. Note: the preferred way for creating subsets is via DataCollection.new_subset_group. Manually-instantiated subsets will probably *not* be represented properly by the UI """ self._broadcasting = False # must be first def self.data = data self.label = label # trigger disambiguation self.subset_state = SubsetState() # calls proper setter method self.style = VisualAttributes(parent=self) self.style.markersize *= 2.5 self.style.linewidth *= 2.5 self.style.color = color self.style.alpha = alpha # We assign a UUID which can then be used for example in equations # for derived components - the idea is that this doesn't change over # the life cycle of glue, so it is a more reliable way to refer to # components in strings than using labels self._uuid = str(uuid.uuid4()) @property def uuid(self): return self._uuid @property def subset_state(self): return self._subset_state @subset_state.setter def subset_state(self, state): if isinstance(state, np.ndarray): if self.data.shape != state.shape: raise ValueError("Shape of mask doesn't match shape of data") cids = self.data.pixel_component_ids state = MaskSubsetState(state, cids) if not isinstance(state, SubsetState): raise TypeError("State must be a SubsetState instance or array") self._subset_state = state @property def style(self): return self._style @style.setter @contract(value=VisualAttributes) def style(self, value): value.parent = self self._style = value @property def label(self): """ Convenience access to subset's label """ return self._label @label.setter def label(self, value): """Set the subset's label Subset labels within a data object must be unique. The input will be auto-disambiguated if necessary """ value = Registry().register(self, value, group=self.data) self._label = value @property def attributes(self): """ Returns a tuple of the ComponentIDs that this subset depends upon """ return self.subset_state.attributes def register(self): """ Register a subset to its data, and start broadcasting state changes """ self.data.add_subset(self) self.do_broadcast(True) @contract(returns='array[N]') def to_index_list(self): """ Convert the current subset to a list of indices. These index the elements in the (flattened) data object that belong to the subset. If x is the numpy array corresponding to some component.data, the two following statements are equivalent:: x.flat[subset.to_index_list()] x[subset.to_mask()] Returns: A numpy array, giving the indices of elements in the data that belong to this subset. Raises: IncompatibleDataException: if an index list cannot be created for the requested data set. """ return self.subset_state.to_index_list(self.data) @contract(view='array_view', returns='array') def to_mask(self, view=None): """ Convert the current subset to a mask. :param view: An optional view into the dataset (e.g. a slice) If present, the mask will pertain to the view and not the entire dataset. A boolean numpy array, the same shape as the data, that defines whether each element belongs to the subset. """ return self.data.get_mask(self.subset_state, view=view) @contract(value=bool) def do_broadcast(self, value): """ Set whether state changes to the subset are relayed to a hub. It can be useful to turn off broadcasting, when modifying the subset in ways that don't impact any of the clients. Attributes: value: Whether the subset should broadcast state changes (True/False) """ object.__setattr__(self, '_broadcasting', value) @contract(attribute='string') def broadcast(self, attribute): """ Explicitly broadcast a SubsetUpdateMessage to the hub :param attribute: The name of the attribute (if any) that should be broadcast as updated. :type attribute: ``str`` """ if not hasattr(self, 'data') or not hasattr(self.data, 'hub'): return if self._broadcasting and self.data.hub: msg = SubsetUpdateMessage(self, attribute=attribute) self.data.hub.broadcast(msg) def delete(self): """Broadcast a SubsetDeleteMessage to the hub, and stop broadcasting Also removes subset reference from parent data's subsets list """ dobroad = self._broadcasting and self.data is not None and \ self.data.hub is not None self.do_broadcast(False) if self.data is not None and self in self.data.subsets: self.data._subsets.remove(self) if dobroad: msg = SubsetDeleteMessage(self) self.data.hub.broadcast(msg) Registry().unregister(self, group=self.data) @contract(file_name='string') def write_mask(self, file_name, format="fits"): """ Write a subset mask out to file :param file_name: name of file to write to :param format: Name of format to write to. Currently, only "fits" is supported """ mask = np.short(self.to_mask()) if format == 'fits': from astropy.io import fits try: fits.writeto(file_name, mask, overwrite=True) except TypeError: fits.writeto(file_name, mask, clobber=True) else: raise AttributeError("format not supported: %s" % format) @contract(file_name='string') def read_mask(self, file_name): try: from astropy.io import fits with fits.open(file_name) as hdulist: mask = hdulist[0].data except IOError: raise IOError("Could not read %s (not a fits file?)" % file_name) ind = np.where(mask.flat)[0] state = ElementSubsetState(indices=ind) self.subset_state = state def __del__(self): try: self.delete() except Exception: pass def __setattr__(self, attribute, value): had_attribute = hasattr(self, attribute) before = getattr(self, attribute, None) object.__setattr__(self, attribute, value) if not attribute.startswith('_') and (not had_attribute or np.any(before != value)): self.broadcast(attribute) def __getitem__(self, view): """ Retrieve the elements from a data view within the subset :param view: View of the data. See data.__getitem__ for detils """ c, v = split_component_view(view) ma = self.to_mask(v) return self.data[view][ma] @contract(other_subset='isinstance(Subset)') def paste(self, other_subset): """paste subset state from other_subset onto self """ state = other_subset.subset_state.copy() self.subset_state = state def __str__(self): dlabel = "(no data)" if self.data is not None: dlabel = "(data: %s)" % self.data.label slabel = "Subset: (no label)" if self.label: slabel = "Subset: %s" % self.label return "%s %s" % (slabel, dlabel) def __repr__(self): return self.__str__() @contract(other='isinstance(Subset)', returns='isinstance(Subset)') def __or__(self, other): return _combine([self, other], operator.or_) @contract(other='isinstance(Subset)', returns='isinstance(Subset)') def __and__(self, other): return _combine([self, other], operator.and_) @contract(returns='isinstance(Subset)') def __invert__(self): return _combine([self], operator.invert) @contract(other='isinstance(Subset)', returns='isinstance(Subset)') def __xor__(self, other): return _combine([self, other], operator.xor) def __eq__(self, other): if not isinstance(other, Subset): return False # XXX need to add equality specification for subset states if self is other: return True return (self.subset_state == other.subset_state and self.style == other.style) def state_as_mask(self): """ Convert the current SubsetState to a MaskSubsetState """ try: m = self.to_mask() except IncompatibleAttribute: m = np.zeros(self.data.shape, dtype=np.bool) cids = self.data.pixel_component_ids return MaskSubsetState(m, cids) # If __eq__ is defined, then __hash__ has to be re-defined __hash__ = object.__hash__ # Provide convenient access to Data methods/properties that make sense # here too. def component_ids(self): return self.data.component_ids() @property def components(self): return self.data.components @property def coordinate_components(self): return self.data.coordinate_components @property def derived_components(self): return self.data.derived_components @property def main_components(self): return self.data.main_components @property def pixel_component_ids(self): return self.data.pixel_component_ids @property def world_component_ids(self): return self.data.world_component_ids @property def ndim(self): return self.data.ndim @property def shape(self): return self.data.shape @property def size(self): return self.data.size @property def hub(self): return self.data.hub # DEPRECATED (warnings raised in Data) @property def primary_components(self): return self.data.primary_components @property def visible_components(self): return self.data.visible_components class SubsetState(object): """ The base class for all subset states. This defaults to an empty subset. """ def __init__(self): pass @property def attributes(self): """ The attributes that the subset state depends on. """ return tuple() @property def subset_state(self): # convenience method, mimic interface of Subset return self @contract(data='isinstance(Data)') def to_index_list(self, data): return np.where(data.get_mask(self.subset_state).flat)[0] @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): """ Compute the mask for this subset state. Parameters ---------- data : :class:`~glue.core.data.Data` The dataset to compute the mask for. view Any object that returns a valid view for a Numpy array. """ shp = view_shape(data.shape, view) return broadcast_to(False, shp) @contract(returns='isinstance(SubsetState)') def copy(self): """ Return a copy of the subset state. """ return SubsetState() @contract(other_state='isinstance(SubsetState)', returns='isinstance(SubsetState)') def __or__(self, other_state): return OrState(self, other_state) @contract(other_state='isinstance(SubsetState)', returns='isinstance(SubsetState)') def __and__(self, other_state): return AndState(self, other_state) @contract(returns='isinstance(SubsetState)') def __invert__(self): return InvertState(self) @contract(other_state='isinstance(SubsetState)', returns='isinstance(SubsetState)') def __xor__(self, other_state): return XorState(self, other_state) class RoiSubsetStateNd(SubsetState): """ A subset defined as the set of points in N dimensions that lie inside a region of interest (ROI). The dimensions are defined as numerical data attributes. Parameters ---------- atts : list of :class:`~glue.core.component_id.ComponentID` The data attributes that define the dimensions of the region. roi : :class:`~glue.core.roi.Roi` The region of interest. pretransform: callable, optional A function that can be optionally applied to the data before checking points against the region. """ def __init__(self, atts=[], roi=None, pretransform=None): self._atts = atts self._roi = roi self._pretransform = pretransform @property def roi(self): """ The region of interest. """ return self._roi @roi.setter def roi(self, value): self._roi = value @property def pretransform(self): """ An optional transformation function to apply before checking if points are in the ROI. """ return self._pretransform @pretransform.setter def pretransform(self, value): if not callable(value) and value is not None: raise TypeError("The pretransform must be callable or None.") self._pretransform = value @property def attributes(self): return tuple(self._atts) @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): # TODO: make sure that pixel components don't actually take up much # memory and are just views raw_comps = [] for att in self._atts: raw_comps.append(data[att, view]) res_shape = raw_comps[0].shape if not self.roi.defined(): return np.zeros(raw_comps[0].shape, dtype=bool) if raw_comps[0].ndim == data.ndim and all([att in data.pixel_component_ids for att in self._atts]): # This is a special case - the ROI is defined in pixel space, so we # can apply it to a single slice and then broadcast it to all other # dimensions. We start off by extracting a slice which takes only # the first elements of all dimensions except the attributes in # question, for which we take all the elements. We need to preserve # the dimensionality of the array, hence the use of slice(0, 1). # Note that we can only do this if the view (if present) preserved # the dimensionality, which is why we checked that raw_comps[0].ndim == data.ndim. axis_ids = [att.axis for att in self._atts] subset = [] for i in range(data.ndim): if i in axis_ids: subset.append(slice(None)) else: subset.append(slice(0, 1)) for i in range(len(raw_comps)): raw_comps[i] = raw_comps[i][tuple(subset)] if self.pretransform: transformed_points = [] for slices in iterate_chunks(raw_comps[0].shape, n_max=1000000): comp_subsets = [] for raw_comp in raw_comps: comp_subsets.append(raw_comp[slices]) res = self.pretransform(*comp_subsets) # Do this here in case the pretransform changes the dimensionality # e.g. 3D input to a 2D projection like Projected3dROI does internally while len(transformed_points) < len(res): transformed_points.append(np.zeros(raw_comps[0].shape)) for i in range(len(res)): transformed_points[i][slices] = res[i] else: transformed_points = raw_comps if isinstance(self.roi, Projected3dROI): result = self.roi.contains3d(*transformed_points) else: result = self.roi.contains(*transformed_points) if result.shape != res_shape: result = np.broadcast_to(result, res_shape) return result class RoiSubsetState(RoiSubsetStateNd): """ A subset defined as the set of points in two dimensions that lie inside a region of interest (ROI). The two dimensions are defined as two numerical data attributes. Parameters ---------- xatt : :class:`~glue.core.component_id.ComponentID` The data attribute on the x axis. yatt : :class:`~glue.core.component_id.ComponentID` The data attribute on the y axis. roi : :class:`~glue.core.roi.Roi` The region of interest. pretransform: callable, optional A function that can be optionally applied to the data before checking points against the region. """ @contract(xatt='isinstance(ComponentID)', yatt='isinstance(ComponentID)') def __init__(self, xatt=None, yatt=None, roi=None, pretransform=None): super(RoiSubsetState, self).__init__(atts=[xatt, yatt], roi=roi, pretransform=pretransform) @property def xatt(self): """ The data attribute on the x axis. """ return self._atts[0] @xatt.setter def xatt(self, value): self._atts[0] = value @property def yatt(self): """ The data attribute on the y axis. """ return self._atts[1] @yatt.setter def yatt(self, value): self._atts[1] = value def copy(self): result = RoiSubsetState() result.xatt = self.xatt result.yatt = self.yatt result.roi = self.roi result.pretransform = self.pretransform return result class CategoricalROISubsetState(SubsetState): """ A subset defined as the set of values for a categorical data attribute that fall inside a categorical region of interest (ROI). Parameters ---------- att : :class:`~glue.core.component_id.ComponentID` The categorical data attribute used for the subset. roi : :class:`~glue.core.roi.CategoricalROI` The categorical region of interest. """ def __init__(self, att=None, roi=None): super(CategoricalROISubsetState, self).__init__() self._att = att self._roi = roi @property def att(self): """ The categorical data attribute used for the subset. """ return self._att @att.setter def att(self, value): self._att = value @property def roi(self): """ The categorical region of interest. """ return self._roi @roi.setter def roi(self, value): self._roi = value @property def attributes(self): return self.att, @memoize @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): x = data[self.att, view] result = self.roi.contains(x, None) assert x.shape == result.shape return result.ravel() def copy(self): result = CategoricalROISubsetState() result.att = self.att result.roi = self.roi return result @staticmethod def from_range(categories, att, lo, hi): roi = CategoricalROI.from_range(categories, lo, hi) subset = CategoricalROISubsetState(roi=roi, att=att) return subset def __gluestate__(self, context): return dict(att=context.id(self.att), roi=context.id(self.roi)) @classmethod def __setgluestate__(cls, rec, context): return cls(att=context.object(rec['att']), roi=context.object(rec['roi'])) class RangeSubsetState(SubsetState): """ A subset defined as the set of values inside a range. The range is defined as being inclusive (that is, values equal to the lower or upper bounds are considered to be inside the subset). Parameters ---------- lo : `float` The lower limit of the range. hi : `float` The upper limit of the range. att : :class:`~glue.core.component_id.ComponentID` The attribute being used for the subset. """ def __init__(self, lo, hi, att=None): super(RangeSubsetState, self).__init__() self._lo = lo self._hi = hi self._att = att @property def lo(self): """ The lower limit of the range. """ return self._lo @lo.setter def lo(self, value): self._lo = value @property def hi(self): """ The upper limit of the range. """ return self._hi @hi.setter def hi(self, value): self._hi = value @property def att(self): """ The attribute being used for the subset. """ return self._att @att.setter def att(self, value): self._att = value @property def attributes(self): return (self.att,) @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): x = data[self.att, view] result = (x >= self.lo) & (x <= self.hi) return result def copy(self): return RangeSubsetState(self.lo, self.hi, self.att) class MultiRangeSubsetState(SubsetState): """ A subset state defined by multiple discontinuous ranges The ranges are defined as being inclusive (that is, values equal to the lower or upper bounds are considered to be inside the subset). Parameters ---------- pairs : list A list of (lo, hi) tuples. att : :class:`~glue.core.component_id.ComponentID` The attribute being used for the subset. """ def __init__(self, pairs, att=None): super(MultiRangeSubsetState, self).__init__() self._pairs = pairs self._att = att @property def pairs(self): """ A list of (lo, hi) tuples. """ return self._pairs @pairs.setter def pairs(self, value): self._pairs = value @property def att(self): """ The attribute being used for the subset. """ return self._att @att.setter def att(self, value): self._att = value @property def attributes(self): return (self.att,) @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): x = data[self.att, view] result = np.zeros_like(x, dtype=bool) for lo, hi in self.pairs: result |= (x >= lo) & (x <= hi) return result def copy(self): return MultiRangeSubsetState(self.pairs, self.att) class CategoricalROISubsetState2D(SubsetState): """ A subset defined as the set of values for two categorical data attributes that fall inside a categorical region of interest (ROI). Parameters ---------- categories : dict A dictionary containing for each label of one categorical component an iterable of labels for the other categorical component (using sets will provide the best performance) att1 : :class:`~glue.core.component_id.ComponentID` The component ID matching the keys of the ``categories`` dictionary att2 : :class:`~glue.core.component_id.ComponentID` The component ID matching the values of the ``categories`` dictionary """ def __init__(self, categories, att1, att2): self._categories = categories self._att1 = att1 self._att2 = att2 @property def categories(self): """ A dictionary containing for each label of one categorical component an iterable of labels for the other categorical component. """ return self._categories @categories.setter def categories(self, value): self._categories = value @property def att1(self): """ The component ID matching the keys of the ``categories`` dictionary """ return self._att1 @att1.setter def att1(self, value): self._att1 = value @property def att2(self): """ The component ID matching the values of the ``categories`` dictionary """ return self._att2 @att2.setter def att2(self, value): self._att2 = value @property def attributes(self): return (self.att1, self.att2) @memoize @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): # Extract categories and numerical values labels1 = data[self.att1, view] labels2 = data[self.att2, view] # Initialize empty mask mask = np.zeros(labels1.shape, dtype=bool) # A loop over all values here is actually reasonably efficient compared # to alternatives. Any improved implementation, even vectorized, should # ensure that it is more efficient for large numbers of categories and # values. for i in range(len(labels1)): if labels1[i] in self.categories: if labels2[i] in self.categories[labels1[i]]: mask[i] = True return mask def copy(self): result = CategoricalROISubsetState2D(self.categories, self.att1, self.att2) return result def __gluestate__(self, context): return dict(categories=self.categories, att1=context.id(self.att1), att2=context.id(self.att2)) @classmethod def __setgluestate__(cls, rec, context): return cls(categories=rec['categories'], att1=context.object(rec['att1']), att2=context.object(rec['att2'])) class CategoricalMultiRangeSubsetState(SubsetState): """ A subset state defined by two attributes where one attribute is categorical and the other is numerical, and where for each category, there are multiple possible subset ranges. Parameters ---------- ranges : dict A dictionary containing for each category (key), a list of tuples giving the ranges of values for the numerical attribute. cat_att : :class:`~glue.core.component_id.ComponentID` The component ID for the categorical attribute. num_att : :class:`~glue.core.component_id.ComponentID` The component ID for the numerical attribute. """ def __init__(self, ranges, cat_att, num_att): self.ranges = ranges self.cat_att = cat_att self.num_att = num_att @property def ranges(self): """ A dictionary containing for each category (key), a list of tuples giving the ranges of values for the numerical attribute. """ return self._ranges @ranges.setter def ranges(self, value): self._ranges = value @property def cat_att(self): """ The component ID for the categorical attribute. """ return self._cat_att @cat_att.setter def cat_att(self, value): self._cat_att = value @property def num_att(self): """ The component ID for the numerical attribute. """ return self._num_att @num_att.setter def num_att(self, value): self._num_att = value @property def attributes(self): return (self.cat_att, self._num_att) @memoize @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): # Extract categories and numerical values labels = data[self.cat_att] values = data[self.num_att] if view is not None: labels = labels[view] values = values[view] # Initialize empty mask mask = np.zeros(values.shape, dtype=bool) # A loop over all values here is actually reasonably efficient compared # to alternatives. Any improved implementation, even vectorized, should # ensure that it is more efficient for large numbers of categories and # values. For example, using 10000 categories and 1000000 data points # takes 1.2 seconds on a laptop. for i in range(len(values)): if labels[i] in self.ranges: for lo, hi in self.ranges[labels[i]]: if values[i] >= lo and values[i] <= hi: mask[i] = True break return mask def copy(self): result = CategoricalMultiRangeSubsetState(self.ranges, self.cat_att, self.num_att) return result def __gluestate__(self, context): return dict(ranges=self.ranges, cat_att=context.id(self.cat_att), num_att=context.id(self.num_att)) @classmethod def __setgluestate__(cls, rec, context): return cls(ranges=rec['ranges'], cat_att=context.object(rec['cat_att']), num_att=context.object(rec['num_att'])) class CompositeSubsetState(SubsetState): """ The base class for combinations of subset states. """ op = None def __init__(self, state1, state2=None): super(CompositeSubsetState, self).__init__() self.state1 = state1.copy() if state2: state2 = state2.copy() self.state2 = state2 def copy(self): return type(self)(self.state1, self.state2) @property def attributes(self): att = self.state1.attributes if self.state2 is not None: att += self.state2.attributes return tuple(sorted(set(att))) @memoize @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): return self.op(self.state1.to_mask(data, view), self.state2.to_mask(data, view)) def __str__(self): sym = OPSYM.get(self.op, self.op) return "(%s %s %s)" % (self.state1, sym, self.state2) class OrState(CompositeSubsetState): """ An 'or' logical combination of subset states. The two states can be accessed using the attributes ``state1`` and ``state2``. """ op = operator.or_ class AndState(CompositeSubsetState): """ An 'and' logical combination of subset states. The two states can be accessed using the attributes ``state1`` and ``state2``. """ op = operator.and_ class XorState(CompositeSubsetState): """ An 'exclusive or' logical combination of subset states. The two states can be accessed using the attributes ``state1`` and ``state2``. """ op = operator.xor class InvertState(CompositeSubsetState): """ A inverted subset state. Values inside the original subset are now considered outside, and vice-versa. The original subset state can be accessed using the attribute ``state1``. """ @memoize @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): return ~self.state1.to_mask(data, view) def __str__(self): return "(~%s)" % self.state1 class MultiOrState(SubsetState): """ A state for many states to be combined together with an 'or' operation. This is meant to be used for cases where many subset states are meant to be combined together and provides significant performance enhancements compared to chaining individual OrStates """ def __init__(self, states): super(MultiOrState, self).__init__() if len(states) < 1: raise ValueError("states should contain at least one subset state") self.states = states def copy(self): return type(self)(self.states) @property def attributes(self): att = self.states[0].attributes for state in self.states[1:]: att += state.attributes return tuple(sorted(set(att))) @memoize @contract(data='isinstance(Data)', view='array_view') def to_mask(self, data, view=None): # Copy the first mask so that we can then modify it in-place result = self.states[0].to_mask(data, view=view).copy() for state in self.states[1:]: result |= state.to_mask(data, view=view) return result def __str__(self): return "('or' combination of {0} individual states)".format(len(self.states)) class MaskSubsetState(SubsetState): """ A subset defined by a boolean mask. Parameters ---------- mask : `~numpy.ndarray` The boolean mask to apply to the data. cids : iterable of :class:`~glue.core.component_id.ComponentID` The component IDs along which the mask applies. """ def __init__(self, mask, cids): self._cids = cids self._mask = np.asarray(mask, dtype=bool) @property def mask(self): """ The boolean mask to apply to the data. """ return self._mask @mask.setter def mask(self, value): self._mask = value @property def cids(self): """ The component IDs along which the mask applies. """ return self._cids @cids.setter def cids(self, value): self._cids = value @property def attributes(self): return self._cids def copy(self): return MaskSubsetState(self.mask, self.cids) def to_mask(self, data, view=None): if view is None: view = slice(None) # shortcut for data on the same pixel grid if data.pixel_component_ids == self.cids: return self.mask[view].copy() # locate each element of data in the coordinate system of the mask vals = [data[c, view].astype(np.int) for c in self.cids] result = self.mask[tuple(vals)] for v, n in zip(vals, data.shape): result &= ((v >= 0) & (v < n)) return result def __gluestate__(self, context): return dict(cids=[context.id(c) for c in self.cids], mask=context.do(self.mask)) @classmethod def __setgluestate__(cls, rec, context): return cls(context.object(rec['mask']), [context.object(c) for c in rec['cids']]) class SliceSubsetState(SubsetState): """ A subset defined by a set of array slices. Parameters ---------- reference_data : :class:`~glue.core.data.Data` The data in whose space the slices are defined. slices : iterable of :class:`slice` An iterable containing :class:`slice` objects to apply to the data. """ def __init__(self, reference_data, slices): self._reference_data = reference_data self._slices = slices self._pad_slices() @property def reference_data(self): """ The data in whose space the slices are defined. """ return self._reference_data @reference_data.setter def reference_data(self, value): self._reference_data = value @property def slices(self): """ An iterable containing :class:`slice` objects to apply to the data. """ return self._slices @slices.setter def slices(self, value): self._slices = value def _pad_slices(self): from glue.core.data import BaseCartesianData if isinstance(self.reference_data, BaseCartesianData) and len(self.slices) < self.reference_data.ndim: self.slices = self.slices + [slice(None)] * (self.reference_data.ndim - len(self.slices)) @property def attributes(self): return self._reference_data.pixel_component_ids def copy(self): return SliceSubsetState(self.reference_data, self.slices) def to_mask(self, data, view=None): if view is None: view = Ellipsis elif isinstance(view, slice) or np.isscalar(view): view = (view,) # Figure out the shape of the final mask given the requested view shape = view_shape(data.shape, view) if data is self.reference_data: slices = self.slices else: # Check if we can transform list of slices to match this dataset order = data.pixel_aligned_data.get(self.reference_data, None) if order is None: # We use broadcast_to for minimal memory usage return broadcast_to(False, shape) else: # Reorder slices slices = [self.slices[idx] for idx in order] if (isinstance(view, np.ndarray) or (isinstance(view, (tuple, list)) and isinstance(view[0], np.ndarray))): mask = np.zeros(data.shape, dtype=bool) mask[tuple(slices)] = True return mask[view] # The original slices assume the full array, not the array with the view # applied, so we need to now adjust the slices accordingly. if view is Ellipsis: subslices = slices else: subslices = [] for i in range(data.ndim): if i >= len(view): subslices.append(slices[i]) elif np.isscalar(view[i]): beg, end, stp = slices[i].indices(data.shape[i]) if view[i] < beg or view[i] >= end or (view[i] - beg) % stp != 0: return broadcast_to(False, shape) elif isinstance(view[i], slice): if view[i].step is not None and view[i].step < 0: beg, end, step = view[i].indices(data.shape[i]) v = slice(end + 1, beg + 1, -step) else: v = view[i] subslices.append(combine_slices(v, slices[i], data.shape[i])) else: raise TypeError("Unexpected view item: {0}".format(view[i])) # Create mask with final shape mask = np.zeros(shape, dtype=bool) mask[tuple(subslices)] = True return mask def to_array(self, data, att): if data is self.reference_data: slices = self.slices else: order = data.pixel_aligned_data.get(self.reference_data, None) if order is None: raise IncompatibleAttribute() slices = [self.slices[idx] for idx in order] return data[att, tuple(slices)] def __gluestate__(self, context): return dict(slices=context.do(self.slices), reference_data=context.id(self.reference_data)) @classmethod def __setgluestate__(cls, rec, context): return cls(rec['reference_data'], context.object(rec['slices'])) def __setgluestate_callback__(self, context): self.reference_data = context.object(self.reference_data) self._pad_slices() class CategorySubsetState(SubsetState): """ A subset defined by the set of categorical values that are equal to a set of categories. Parameters ---------- att : :class:`~glue.core.component_id.ComponentID` The categorical data attribute used for the subset categories : iterable The categories that the attribute should be equal to. These should be given as the integer codes, not the categorical labels. """ def __init__(self, att, categories): super(CategorySubsetState, self).__init__() self._att = att self._categories = np.asarray(categories).ravel() @property def att(self): """ The categorical data attribute used for the subset """ return self._att @att.setter def att(self, value): self._att = value @property def categories(self): """ The categories that the attribute should be equal to. These should be given as the integer codes, not the categorical labels. """ return self._categories @categories.setter def categories(self, value): self._categories = value @property def attributes(self): return self._att, @memoize def to_mask(self, data, view=None): vals = data[self._att, view] if isinstance(vals, categorical_ndarray): vals = vals.codes result = np.in1d(vals.ravel(), self._categories) return result.reshape(vals.shape) def copy(self): return CategorySubsetState(self._att, self._categories.copy()) def __gluestate__(self, context): return dict(att=context.id(self._att), vals=context.do(self._categories)) @classmethod def __setgluestate__(cls, rec, context): return cls(context.object(rec['att']), context.object(rec['vals'])) class ElementSubsetState(SubsetState): """ A subset defined by a set of indices to apply to the data. Parameters ---------- indices Any valid object that can be used to index a Numpy array. data : :class:`~glue.core.data.Data` The data in whose space the indices are defined. """ def __init__(self, indices=None, data=None): super(ElementSubsetState, self).__init__() self._indices = indices if data is None: self._data_uuid = None else: self._data_uuid = data.uuid @property def indices(self): """ The indices which when applied to the data give the subset. """ return self._indices @indices.setter def indices(self, value): self._indices = value @property def data(self): """ The UUID of the data in whose space the indices are defined. """ return self._data_uuid @data.setter def data(self, value): self._data = value @memoize def to_mask(self, data, view=None): if data.uuid == self._data_uuid or self._data_uuid is None: # XXX this is inefficient for views result = np.zeros(data.shape, dtype=bool) if self._indices is not None: try: result.flat[self._indices] = True except IndexError: if self._data_uuid is None: raise IncompatibleAttribute() else: raise if view is not None: result = result[view] return result else: raise IncompatibleAttribute() @property def attributes(self): return self._data.pixel_component_ids def copy(self): state = ElementSubsetState(indices=self._indices) state._data_uuid = self._data_uuid return state def __gluestate__(self, context): return dict(indices=context.do(self._indices), data_uuid=self._data_uuid) @classmethod def __setgluestate__(cls, rec, context): state = cls(indices=context.object(rec['indices'])) try: state._data_uuid = rec['data_uuid'] except KeyError: # BACKCOMPAT pass return state VALID_INEQUALTIY_OPS = [operator.gt, operator.ge, operator.lt, operator.le, operator.eq, operator.ne] class InequalitySubsetState(SubsetState): """ A subset defined by a mathematical comparison of a attribute values to a reference value or attribute values. Parameters ---------- left : float or `~glue.core.component_id.ComponentID` or str The value or component on the left hand side of the comparison. right : float or `~glue.core.component_id.ComponentID` or str The value or component on the right hand side of the comparison. operator : operator The comparison operator (from the :mod:`operator` module) """ def __init__(self, left, right, operator): from glue.core.component_link import ComponentLink super(InequalitySubsetState, self).__init__() from glue.core.data import ComponentID if operator not in VALID_INEQUALTIY_OPS: raise TypeError("Invalid boolean operator: %s" % operator) if not isinstance(left, (ComponentID, numbers.Number, ComponentLink, str)): raise TypeError("Input must be ComponentID or NumberType or string: %s" % type(left)) if not isinstance(right, (ComponentID, numbers.Number, ComponentLink, str)): raise TypeError("Input must be ComponentID or NumberType or string: %s" % type(right)) self._left = left self._right = right self._operator = operator @property def left(self): """ The value or component on the left hand side of the comparison. """ return self._left @left.setter def left(self, value): self._left = value @property def right(self): """ The value or component on the right hand side of the comparison. """ return self._right @right.setter def right(self, value): self._right = value @property def operator(self): """ The comparison operator (from the :mod:`operator` module) """ return self._operator @operator.setter def operator(self, value): self._operator = value @memoize def to_mask(self, data, view=None): # FIXME: the default view in glue should be ... not None, because # if x is a Numpy array, x[None] has one more dimension than x. For # now we just fix this for the scope of this method. if view is None: view = Ellipsis if isinstance(self._left, (numbers.Number, str)): left = self._left else: left = data[self._left, view] if isinstance(self._right, (numbers.Number, str)): right = self._right else: right = data[self._right, view] return self._operator(left, right) def copy(self): return InequalitySubsetState(self._left, self._right, self._operator) def __str__(self): sym = OPSYM.get(self._operator, self._operator) return "(%s %s %s)" % (self._left, sym, self._right) def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self) class FloodFillSubsetState(MaskSubsetState): """ A subset representing a flood-fill operation, which is computed on-the-fly. Parameters ---------- data : :class:`~glue.core.data.Data` The data on which the flood fill is computed. att : :class:`glue.core.component_id.ComponentID` The attribute defining the values to use for the flood fill. start_coords : tuple The pixel coordinates of the starting point. threshold : float A value greater or equal to 1 describing the extend of the flood filling. The range of values selected by the flood fill is ``start_value * (2 -threshold)`` to ``start_value * threshold`` where ``start_value`` is the value of the data at ``start_coords``. """ # TODO: we need to recompute the mask if the numerical values of the # data changes. def __init__(self, data, att, start_coords, threshold): if len(start_coords) != data.ndim: raise ValueError("start_coords should have as many values as data " "has dimensions.") self._att = att self._data = data self._start_coords = tuple(start_coords) self._threshold = float(threshold) self._cids = self.data.pixel_component_ids self._compute_mask() @property def data(self): """ The data on which the flood fill is computed. """ return self._data @data.setter def data(self, value): self._data = value @property def att(self): """ The attribute defining the values to use for the flood fill. """ return self._att @att.setter def att(self, value): self._att = value @property def start_coords(self): """ The pixel coordinates of the starting point. """ return self._start_coords @start_coords.setter def start_coords(self, value): self._start_coords = value @property def threshold(self): """ A value greater or equal to 1 describing the extend of the flood filling. The range of values selected by the flood fill is ``start_value * (2 -threshold)`` to ``start_value * threshold`` where ``start_value`` is the value of the data at ``start_coords``. """ return self._threshold @threshold.setter def threshold(self, value): self._threshold = value def _compute_mask(self): mask = floodfill(self.data[self.att], self.start_coords, self.threshold) self._mask_cache = (self._hash, mask) @property def _hash(self): return self.data, self.att, self.start_coords, self.threshold, self.cids @property def mask(self): if self._mask_cache[0] != self._hash: self._compute_mask() return self._mask_cache[1] @property def attributes(self): return list(self._data.pixel_component_ids) + [self.att] def copy(self): return FloodFillSubsetState(self.data, self.att, self.start_coords, self.threshold) def __gluestate__(self, context): # We don't store the data since this would cause a circular reference. # However we can recover the data from the attribute ComponentID. return dict(attribute=context.id(self.att), start_coords=self.start_coords, threshold=self.threshold) @classmethod def __setgluestate__(cls, rec, context): att = context.object(rec['attribute']) return cls(att.parent, att, context.object(rec['start_coords']), context.object(rec['threshold'])) class RoiSubsetState3d(RoiSubsetStateNd): """ A subset defined as the set of points in three dimensions that lie inside a 3-d region of interest (ROI). The three dimensions are defined as three numerical data attributes. Parameters ---------- xatt : :class:`~glue.core.component_id.ComponentID` The data attribute on the x axis. yatt : :class:`~glue.core.component_id.ComponentID` The data attribute on the y axis. zatt : :class:`~glue.core.component_id.ComponentID` The data attribute on the z axis. roi : :class:`~glue.core.roi.Roi` The region of interest (which should implement ``contains3d``) pretransform: callable, optional A function that can be optionally applied to the data before checking points against the region. """ @contract(xatt='isinstance(ComponentID)', yatt='isinstance(ComponentID)', zatt='isinstance(ComponentID)') def __init__(self, xatt=None, yatt=None, zatt=None, roi=None, pretransform=None): super(RoiSubsetState3d, self).__init__(atts=[xatt, yatt, zatt], roi=roi, pretransform=pretransform) @property def xatt(self): """ The data attribute on the x axis. """ return self._atts[0] @xatt.setter def xatt(self, value): self._atts[0] = value @property def yatt(self): """ The data attribute on the y axis. """ return self._atts[1] @yatt.setter def yatt(self, value): self._atts[1] = value @property def zatt(self): """ The data attribute on the z axis. """ return self._atts[2] @zatt.setter def zatt(self, value): self._atts[2] = value def copy(self): result = RoiSubsetState3d() result.xatt = self.xatt result.yatt = self.yatt result.zatt = self.zatt result.roi = self.roi result.pretransform = self.pretransform return result def __gluestate__(self, context): return dict(xatt=context.id(self.xatt), yatt=context.id(self.yatt), zatt=context.id(self.zatt), roi=context.id(self.roi), pretransform=context.id(self.pretransform)) @classmethod def __setgluestate__(cls, rec, context): pretrans = rec['pretransform'] if 'pretransform' in rec else None return RoiSubsetState3d(context.object(rec['xatt']), context.object(rec['yatt']), context.object(rec['zatt']), context.object(rec['roi']), context.object(pretrans)) @contract(subsets='list(isinstance(Subset))', returns=Subset) def _combine(subsets, operator): state = operator(*[s.subset_state for s in subsets]) result = Subset(None) result.subset_state = state return result def combine_multiple(subsets, operator): if len(subsets) == 0: return SubsetState() else: combined = subsets[0] for subset in subsets[1:]: combined = operator(combined, subset) return combined def roi_to_subset_state(roi, x_att=None, y_att=None, x_categories=None, y_categories=None, use_pretransform=False): """ Given a 2D ROI and attributes on the x and y axis, determine the corresponding subset state. """ if isinstance(roi, RangeROI) and not use_pretransform: if roi.ori == 'x': att = x_att categories = x_categories else: att = y_att categories = y_categories if categories is not None: return CategoricalROISubsetState.from_range(categories, att, roi.min, roi.max) else: return RangeSubsetState(roi.min, roi.max, att) elif x_categories is not None or y_categories is not None: if isinstance(roi, RectangularROI): # In this specific case, we can decompose the rectangular ROI into # two RangeROIs that are combined with an 'and' logical operation. range1 = XRangeROI(roi.xmin, roi.xmax) range2 = YRangeROI(roi.ymin, roi.ymax) subset1 = roi_to_subset_state(range1, x_att=x_att, x_categories=x_categories) subset2 = roi_to_subset_state(range2, y_att=y_att, y_categories=y_categories) return AndState(subset1, subset2) elif isinstance(roi, CategoricalROI): # The selection is categorical itself. We assume this is along the x axis return CategoricalROISubsetState(roi=roi, att=x_att) else: # The selection is polygon-like, which requires special care. if x_categories is not None and y_categories is not None: # For each category, we check which categories along the other # axis fall inside the polygon: selection = {} for code, label in enumerate(x_categories): # Determine the coordinates of the points to check n_other = len(y_categories) y = np.arange(n_other) x = np.repeat(code, n_other) # Determine which points are in the polygon, and which # categories these correspond to in_poly = roi.contains(x, y) categories = y_categories[in_poly] if len(categories) > 0: selection[label] = set(categories) return CategoricalROISubsetState2D(selection, x_att, y_att) else: # If one of the components is not categorical, we treat this as # if each categorical component was mapped to a numerical value, # and at each value, we keep track of the polygon intersection # with the component. This will result in zero, one, or multiple # separate numerical ranges for each categorical value. # TODO: if we ever allow the category order to be changed, we # need to figure out how to update this! # We loop over each category and for each one we find the # numerical ranges selection = {} if x_categories is not None: categories = x_categories cat_att = x_att num_att = y_att x, y = roi.to_polygon() else: categories = y_categories cat_att = y_att num_att = x_att y, x = roi.to_polygon() for code, label in enumerate(categories): # We determine all the numerical segments that represent the # ensemble of points in y that fall in the polygon # TODO: profile the following function segments = polygon_line_intersections(x, y, xval=code) if len(segments) > 0: selection[label] = segments return CategoricalMultiRangeSubsetState(selection, cat_att=cat_att, num_att=num_att) else: # The selection is polygon-like or requires a pretransform and components are numerical if not isinstance(roi, (PolygonalROI, RectangularROI, CircularROI, EllipticalROI, RangeROI)): roi = PolygonalROI(*roi.to_polygon()) subset_state = RoiSubsetState() subset_state.xatt = x_att subset_state.yatt = y_att subset_state.roi = roi return subset_state glueviz-1.0.1+dfsg.orig/glue/core/registry.py0000644000175000017500000000475313752534424020534 0ustar noahfxnoahfxfrom functools import wraps from collections import defaultdict from glue.core.util import disambiguate from glue.core.decorators import singleton @singleton class Registry(object): """ Stores labels for classes of objects. Ensures uniqueness The registry ensures that labels for objects of the same "group" are unique, and disambiguates as necessary. By default, objects types are used to group, but anything can be used as a group Registry is a singleton, and thus all instances of Registry share the same information Usage: >>> r = Registry() >>> x, y, z = 3, 4, 5 >>> w = list() >>> r.register(x, 'Label') 'Label' >>> r.register(y, 'Label') # duplicate label disambiguated 'Label_01' >>> r.register(w, 'Label') # uniqueness only enforced within groups 'Label' >>> r.register(z, 'Label', group=int) # put z in integer registry 'Label_02' """ def __init__(self): self._registry = defaultdict(dict) self._disable = False def register(self, obj, label, group=None): """ Register label with object (possibly disambiguating) :param obj: The object to label :param label: The desired label :param group: (optional) use the registry for group (default=type(obj)) :rtype: str *Returns* The disambiguated label """ group = group or type(obj) reg = self._registry[group] has_obj = obj in reg has_label = label in reg.values() label_is_obj = has_label and has_obj and reg[obj] == label if has_label and (not label_is_obj): values = set(reg.values()) if has_obj: values.remove(reg[obj]) if not self._disable: label = disambiguate(label, values) reg[obj] = label return label def unregister(self, obj, group=None): group = group or type(obj) reg = self._registry[group] if obj in reg: reg.pop(obj) def clear(self): """ Reset registry, clearing all stored values """ self._registry = defaultdict(dict) def disable(func): """ Decorator to temporarily disable disambiguation """ @wraps(func) def wrapper(*args, **kwargs): r = Registry() old = r._disable r._disable = True try: return func(*args, **kwargs) finally: r._disable = old return wrapper glueviz-1.0.1+dfsg.orig/glue/core/visual.py0000644000175000017500000001407113752534424020161 0ustar noahfxnoahfxfrom matplotlib.colors import ColorConverter from matplotlib.cm import get_cmap from glue.config import settings from glue.config import colormaps from echo import callback_property, HasCallbackProperties # Define acceptable line styles VALID_LINESTYLES = ['solid', 'dashed', 'dash-dot', 'dotted', 'none'] __all__ = ['VisualAttributes'] class VisualAttributes(HasCallbackProperties): """ This class is used to define visual attributes for any kind of objects The essential attributes of a VisualAttributes instance are: :param color: A matplotlib color string :param alpha: Opacity (0-1) :param linewidth: The linewidth (float or int) :param linestyle: The linestyle (``'solid' | 'dashed' | 'dash-dot' | 'dotted' | 'none'``) :param marker: The matplotlib marker shape (``'o' | 's' | '^' | etc``) :param markersize: The size of the marker (int) """ def __init__(self, parent=None, washout=False, color=None, alpha=None, preferred_cmap=None): super(VisualAttributes, self).__init__() # We have to set the defaults here, otherwise the settings are fixed # once the class is defined. color = color or settings.DATA_COLOR alpha = alpha or settings.DATA_ALPHA self.parent = parent self._atts = ['color', 'alpha', 'linewidth', 'linestyle', 'marker', 'markersize', 'preferred_cmap'] self.color = color self.alpha = alpha self.preferred_cmap = preferred_cmap self.linewidth = 1 self.linestyle = 'solid' self.marker = 'o' self.markersize = 3 def __eq__(self, other): if not isinstance(other, VisualAttributes): return False elif self is other: return True else: return all(getattr(self, a) == getattr(other, a) for a in self._atts) # If __eq__ is defined, then __hash__ has to be re-defined __hash__ = object.__hash__ def set(self, other): """ Update this instance's properties based on another VisualAttributes instance. """ for att in self._atts: setattr(self, att, getattr(other, att)) def copy(self, new_parent=None): """ Create a new instance with the same visual properties """ result = VisualAttributes() result.set(self) if new_parent is not None: result.parent = new_parent return result @callback_property def color(self): """ Color specified using Matplotlib notation Specifically, it can be: * A string with a common color (e.g. 'black', 'red', 'orange') * A string containing a float in the rng [0:1] for a shade of gray ('0.0' = black,'1.0' = white) * A tuple of three floats in the rng [0:1] for (R, G, B) * An HTML hexadecimal string (e.g. '#eeefff') """ return self._color @color.setter def color(self, value): if isinstance(value, str): self._color = value.lower() else: self._color = value @callback_property def preferred_cmap(self): """ A preferred colormap specified using Matplotlib notation """ return self._preferred_cmap @preferred_cmap.setter def preferred_cmap(self, value): if isinstance(value, str): try: for i, element in enumerate(colormaps.members): if element[0] == value: self._preferred_cmap = element[1] except TypeError: self._preferred_cmap = get_cmap(value) else: self._preferred_cmap = value @callback_property def alpha(self): """ Transparency, given as a floating point value between 0 and 1. """ return self._alpha @alpha.setter def alpha(self, value): self._alpha = value @property def rgba(self): r, g, b = ColorConverter().to_rgb(self.color) return (r, g, b, self.alpha) @callback_property def linestyle(self): """ The line style, which can be one of 'solid', 'dashed', 'dash-dot', 'dotted', or 'none'. """ return self._linestyle @linestyle.setter def linestyle(self, value): if value not in VALID_LINESTYLES: raise Exception("Line style should be one of %s" % '/'.join(VALID_LINESTYLES)) self._linestyle = value @callback_property def linewidth(self): """ The line width, in points. """ return self._linewidth @linewidth.setter def linewidth(self, value): if type(value) not in [float, int]: raise Exception("Line width should be a float or an int") if value < 0: raise Exception("Line width should be positive") self._linewidth = value @callback_property def marker(self): """ The marker symbol. """ return self._marker @marker.setter def marker(self, value): self._marker = value @callback_property def markersize(self): return self._markersize @markersize.setter def markersize(self, value): self._markersize = int(value) def __setattr__(self, attribute, value): # Check that the attribute exists (don't allow new attributes) allowed = set(['color', 'linewidth', 'linestyle', 'alpha', 'parent', 'marker', 'markersize', 'preferred_cmap']) if attribute not in allowed and not attribute.startswith('_'): raise Exception("Attribute %s does not exist" % attribute) changed = getattr(self, attribute, None) != value super(VisualAttributes, self).__setattr__(attribute, value) # if parent has a broadcast method, broadcast the change if (changed and hasattr(self, 'parent') and hasattr(self.parent, 'broadcast') and attribute != 'parent' and not attribute.startswith('_')): self.parent.broadcast('style') glueviz-1.0.1+dfsg.orig/glue/core/layout.py0000644000175000017500000000456513605357235020202 0ustar noahfxnoahfx""" This module provides some routines for performing layout calculations to organize rectangular windows in a larger canvas """ from collections import Counter class Rectangle(object): def __init__(self, x, y, w, h): """ A rectangle (obviously). :param x: Left edge :param y: Bottom edge :param w: Width :param h: Height """ self.x = x self.y = y self.w = w self.h = h def __eq__(self, other): return (self.x == other.x and self.y == other.y and self.w == other.w and self.h == other.h) # If __eq__ is defined, then __hash__ has to be re-defined __hash__ = object.__hash__ def __str__(self): return repr(self) def __repr__(self): return "Rectangle(%f, %f, %f, %f)" % (self.x, self.y, self.w, self.h) def snap(self, xstep, ystep=None, padding=0.0): """ Snap the rectangle onto a grid, with optional padding. :param xstep: The number of intervals to split the x=[0, 1] range into. :param ystep: The number of intervals to split the y=[0, 1] range into. :param padding: Uniform padding to add around the result. This shrinks the result so that the edges + padding line up with the grid. :returns: A new Rectangle, obtained by snapping self onto the grid, and applying padding """ if ystep is None: ystep = xstep return Rectangle(round(self.x * xstep) / xstep + padding, round(self.y * ystep) / ystep + padding, round(self.w * xstep) / xstep - 2 * padding, round(self.h * ystep) / ystep - 2 * padding) def _snap_size(rectangles): x = Counter([round(1 / r.w) for r in rectangles]) y = Counter([round(1 / r.h) for r in rectangles]) return x.most_common()[0][0], y.most_common()[0][0] def snap_to_grid(rectangles, padding=0.0): """ Snap a collection of rectangles onto a grid, in a sensible fashion :param rectangles: List of Rectangle instances :returns: A dictionary mapping each input rectangle to a snapped position """ result = {} xs, ys = _snap_size(rectangles) for r in rectangles: result[r] = r.snap(xs, ys, padding=padding) return result glueviz-1.0.1+dfsg.orig/glue/core/coordinates.py0000644000175000017500000001770513641414126021170 0ustar noahfxnoahfximport abc import uuid import logging import numpy as np from astropy.units import Quantity from astropy.wcs import WCS from astropy.wcs.wcsapi import BaseLowLevelWCS __all__ = ['Coordinates', 'IdentityCoordinates', 'AffineCoordinates', 'coordinates_from_header', 'coordinates_from_wcs', 'header_from_string'] def default_world_coords(wcs): if isinstance(wcs, WCS): return wcs.wcs.crval else: return np.zeros(wcs.world_n_dim, dtype=float) # The Coordinates class (and sub-classes) are used to map from pixel to world # coordinates (and vice-versa). Rather than re-invent a new API, we adopt the # one from the Astropy project which is generic and not specific to astronomy, # which then allows us to make use of visualization tools (e.g. WCSAxes) built # around this API. The API is defined/described in # https://github.com/astropy/astropy-APEs/blob/master/APE14.rst class Coordinates(BaseLowLevelWCS, metaclass=abc.ABCMeta): """ Base class for coordinate transformation """ def __init__(self, n_dim=None, pixel_n_dim=None, world_n_dim=None): self._pixel_n_dim = n_dim or pixel_n_dim self._world_n_dim = n_dim or world_n_dim self._world_uuids = [str(uuid.uuid4()) for i in range(self._world_n_dim)] @property def pixel_n_dim(self): return self._pixel_n_dim @property def world_n_dim(self): return self._world_n_dim @property def world_axis_physical_types(self): return [None] * self._world_n_dim @property def world_axis_units(self): return [''] * self._world_n_dim def array_index_to_world_values(self, *index_arrays): return self.pixel_to_world_values(*index_arrays[::-1]) def world_to_array_index_values(self, *world_arrays): # Compatibility code, remove once we require Astropy 4.1 or later pixel_arrays = self.world_to_pixel_values(*world_arrays) if self.pixel_n_dim == 1: pixel_arrays = (pixel_arrays,) else: pixel_arrays = pixel_arrays[::-1] array_indices = tuple(np.asarray(np.floor(pixel + 0.5), dtype=np.int_) for pixel in pixel_arrays) return array_indices[0] if self.pixel_n_dim == 1 else array_indices @property def world_axis_object_components(self): return [(uid, 0, 'value') for uid in self._world_uuids] @property def world_axis_object_classes(self): return {self._world_uuids[idx]: (Quantity, (), {'unit': self.world_axis_units[idx]}) for idx in range(self._world_n_dim)} def __gluestate__(self, context): return {} # no state @classmethod def __setgluestate__(cls, rec, context): return cls() class LegacyCoordinates(Coordinates): def __init__(self): super().__init__(pixel_n_dim=10, world_n_dim=10) def pixel_to_world_values(self, *pixel): return pixel def world_to_pixel_values(self, *world): return world class IdentityCoordinates(Coordinates): def __init__(self, n_dim=None): super().__init__(pixel_n_dim=n_dim, world_n_dim=n_dim) def pixel_to_world_values(self, *pixel): return pixel def world_to_pixel_values(self, *world): return world @property def axis_correlation_matrix(self): return np.identity(self.pixel_n_dim).astype(bool) def __gluestate__(self, context): return {'ndim': self.pixel_n_dim} @classmethod def __setgluestate__(cls, rec, context): return cls(n_dim=rec['ndim']) class AffineCoordinates(Coordinates): """ Coordinates determined via an affine transformation represented by an augmented matrix of shape N+1 x N+1 matrix, where N is the number of pixel and world coordinates. The last column of the matrix should be used for the translation term, and the last row should be set to 0 except for the last column which should be 1. Note that the order of the dimensions in the matrix (x, y) should be the opposite of the order of the order of dimensions of Numpy arrays (y, x). """ def __init__(self, matrix, units=None, labels=None): if matrix.ndim != 2: raise ValueError("Affine matrix should be two-dimensional") if matrix.shape[0] != matrix.shape[1]: raise ValueError("Affine matrix should be square") if np.any(matrix[-1, :-1] != 0) or matrix[-1, -1] != 1: raise ValueError("Last row of matrix should be zeros and a one") if units is not None and len(units) != matrix.shape[0] - 1: raise ValueError("Expected {0} units, got {1}".format(matrix.shape[0] - 1, len(units))) if labels is not None and len(labels) != matrix.shape[0] - 1: raise ValueError("Expected {0} labels, got {1}".format(matrix.shape[0] - 1, len(labels))) self._matrix = matrix self._matrix_inv = np.linalg.inv(matrix) self._units = units self._labels = labels super().__init__(pixel_n_dim=self._matrix.shape[0] - 1, world_n_dim=self._matrix.shape[0] - 1) def pixel_to_world_values(self, *pixel): scalar = np.all([np.isscalar(p) for p in pixel]) pixel = np.array(np.broadcast_arrays(*(list(pixel) + [np.ones(np.shape(pixel[0]))]))) pixel = np.moveaxis(pixel, 0, -1) world = np.matmul(pixel, self._matrix.T) return tuple(np.moveaxis(world, -1, 0))[:-1] def world_to_pixel_values(self, *world): scalar = np.all([np.isscalar(w) for w in world]) world = np.array(np.broadcast_arrays(*(list(world) + [np.ones(np.shape(world[0]))]))) world = np.moveaxis(world, 0, -1) pixel = np.matmul(world, self._matrix_inv.T) return tuple(np.moveaxis(pixel, -1, 0))[:-1] @property def world_axis_names(self): return self._labels or [''] * self.world_n_dim @property def world_axis_units(self): return self._units or [''] * self.world_n_dim def __gluestate__(self, context): return dict(matrix=context.do(self._matrix), labels=self._labels, units=self._units) @classmethod def __setgluestate__(cls, rec, context): return cls(context.object(rec['matrix']), units=rec['units'], labels=rec['labels']) # Kept for backward-compatibility WCSCoordinates = WCS def coordinates_from_header(header): """ Convert a FITS header into a glue Coordinates object. Parameters ---------- header : :class:`astropy.io.fits.Header` Header to convert Returns ------- coordinates : :class:`~glue.core.coordinates.Coordinates` """ # We check whether the header contains at least CRVAL1 - if not, we would # end up with a default WCS that isn't quite 1 to 1 (because of a 1-pixel # offset) so better use Coordinates in that case. from astropy.io.fits import Header if isinstance(header, Header) and 'CRVAL1' in header: try: return WCSCoordinates(header) except Exception as e: logging.getLogger(__name__).warn( "\n\n*******************************\n" "Encounted an error during WCS parsing. " "Discarding world coordinates! " "\n{}\n" "*******************************\n\n".format(str(e))) try: return IdentityCoordinates(header.get('NAXIS', 2)) except Exception: return IdentityCoordinates(2) def coordinates_from_wcs(wcs): """ Convert an Astropy WCS object into a glue Coordinates object. Parameters ---------- wcs : :class:`astropy.wcs.WCS` The WCS object to use Returns ------- coordinates : :class:`~glue.core.coordinates.Coordinates` """ return WCSCoordinates(wcs.to_header()) def header_from_string(string): """ Convert a string to a FITS header. """ from astropy.io import fits return fits.Header.fromstring(string, sep='\n') glueviz-1.0.1+dfsg.orig/glue/core/application_base.py0000644000175000017500000002606613605357235022162 0ustar noahfxnoahfximport os import warnings import traceback from functools import wraps from glue.core.data import Subset from glue.core.session import Session from glue.core.hub import HubListener from glue.core import BaseData from glue.core.data_factories import load_data from glue.core.data_collection import DataCollection from glue.config import settings __all__ = ['Application'] def catch_error(msg): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception as e: m = "%s\n%s" % (msg, str(e)) detail = str(traceback.format_exc()) self = args[0] self.report_error(m, detail) return wrapper return decorator def as_flat_data_list(data): datasets = [] if isinstance(data, BaseData): datasets.append(data) else: for d in data: datasets.extend(as_flat_data_list(d)) return datasets class Application(HubListener): def __init__(self, data_collection=None, session=None): if session is not None: self._session = session session.application = self self._data = session.data_collection else: self._data = data_collection or DataCollection() self._session = Session(data_collection=self._data, application=self) self._hub = self._session.hub self._cmds = self._session.command_stack self._cmds.add_callback(self._update_undo_redo_enabled) self._settings = {} for key, value, validator in settings: self._settings[key] = [value, validator] @property def session(self): return self._session @property def data_collection(self): return self.session.data_collection def new_data_viewer(self, viewer_class, data=None, state=None): """ Create a new data viewer, add it to the UI, and populate with data """ if viewer_class is None: return if state is not None: c = viewer_class(self._session, state=state) else: c = viewer_class(self._session) c.register_to_hub(self._session.hub) if data is not None: if isinstance(data, BaseData): result = c.add_data(data) elif isinstance(data, Subset): result = c.add_subset(data) if not result: c.close(warn=False) return self.add_widget(c) return c @catch_error("Failed to save session") def save_session(self, path, include_data=False, absolute_paths=True): """ Save the data collection and hub to file. Can be restored via restore_session Note: Saving of client is not currently supported. Thus, restoring this session will lose all current viz windows """ from glue.core.state import GlueSerializer gs = GlueSerializer(self, include_data=include_data, absolute_paths=absolute_paths) # In case relative paths are needed in the session file, we do the # serialization while setting the current directory to the directory # in which the session file will be saved so that the relative paths # are relative to the session file, not the current working directory. start_dir = os.path.abspath('.') session_dir = os.path.dirname(path) or '.' try: os.chdir(session_dir) state = gs.dumps(indent=2) finally: os.chdir(start_dir) with open(path, 'w') as out: out.write(state) @staticmethod def restore_session(path): """ Reload a previously-saved session Parameters ---------- path : str Path to the file to load Returns ------- app : :class:`Application` The loaded application """ from glue.core.state import GlueUnSerializer # In case relative paths are needed in the session file, we do the # loading while setting the current directory to the directory # in which the session file is so that relative paths are interpreted # as relative to the session file. start_dir = os.path.abspath('.') session_dir = os.path.dirname(path) or '.' session_file = os.path.basename(path) try: os.chdir(session_dir) with open(session_file) as infile: state = GlueUnSerializer.load(infile) return state.object('__main__') finally: os.chdir(start_dir) def get_setting(self, key): """ Fetch the value of an application setting """ return self._settings[key][0] def set_setting(self, key, value): """ Set the value of an application setting Raises a KeyError if the setting does not exist Raises a ValueError if the value is invalid """ validator = self._settings[key][1] self._settings[key][0] = validator(value) @property def settings(self): """Iterate over settings""" for key, (value, _) in self._settings.items(): yield key, value @catch_error("Could not load data") def load_data(self, paths, auto_merge=False, **kwargs): """ Given a path to a file, load the file as a Data object and add it to the current session. This returns the added `Data` object. """ if isinstance(paths, str): paths = [paths] datasets = [] for path in paths: result = load_data(path) if isinstance(result, BaseData): datasets.append(result) else: datasets.extend(result) self.add_datasets(datasets, auto_merge=auto_merge, **kwargs) if len(datasets) == 1: return datasets[0] else: return datasets @catch_error("Could not add data") def add_data(self, *args, **kwargs): """ Add data to the session. Positional arguments are interpreted using the data factories, while keyword arguments are interpreted using the same infrastructure as the `qglue` command. This returns a list of added `Data` objects. """ datasets = [] for path in args: if isinstance(path, BaseData): datasets.append(path) else: datasets.append(load_data(path)) links = kwargs.pop('links', None) from glue.qglue import parse_data, parse_links for label, data in kwargs.items(): datasets.extend(parse_data(data, label)) self.add_datasets(datasets) if links is not None: self.data_collection.add_link(parse_links(self.data_collection, links)) return datasets def report_error(self, message, detail): """ Report an error message to the user. Must be implemented in a subclass Parameters ---------- message : str The message to display detail : str Longer context about the error """ raise Exception(message + "(" + detail + ")") def do(self, command): return self._cmds.do(command) def undo(self): try: self._cmds.undo() except RuntimeError: pass def redo(self): try: self._cmds.redo() except RuntimeError: pass def _update_undo_redo_enabled(self): raise NotImplementedError() def add_datasets(self, *args, **kwargs): """ Utility method to interactively add datasets to the data_collection. Parameters ---------- datasets : :class:`~glue.core.data.Data` or list of Data One or more :class:`~glue.core.data.Data` instances. Adds datasets to the collection in the application. """ if isinstance(args[0], DataCollection): warnings.warn("Calling add_datasets with an explicit data " "collection is now deprecated. You should now call " "app.add_datasets(datasets).", UserWarning) data_collection, datasets = args else: data_collection = self.data_collection datasets = args[0] if "skip_merge" in kwargs: warnings.warn("The skip_merge= argument now no longer has any " "effect and is deprecated, since no merging is done " "by default", UserWarning) auto_merge = kwargs.pop('auto_merge', False) datasets = as_flat_data_list(datasets) data_collection.extend(datasets) # We now automatically merge the datasets if requested. However, we only # do this if all the datasets have the same shape to avoid confusion. if auto_merge and len(datasets) > 1: reference_shape = datasets[0].shape if all([data.shape == reference_shape for data in datasets[1:]]): suggested_label = data_collection.suggest_merge_label(*datasets) return data_collection.merge(*datasets, label=suggested_label) else: raise ValueError("Can only auto-merge datasets if they all have " " the same shape") else: return datasets @staticmethod def _choose_merge(data, other, suggested_label): """ Present an interface to the user for approving or rejecting a proposed data merger. Returns a list of datasets from other that the user has approved to merge with data """ raise NotImplementedError @property def viewers(self): """ Return a tuple of tuples of viewers currently open. """ return [] def set_data_color(self, color, alpha): """ Reset all the data colors to that specified. """ for data in self.data_collection: data.style.color = color data.style.alpha = alpha def __gluestate__(self, context): viewers = [list(map(context.id, tab)) for tab in self.viewers] data = self.session.data_collection from glue.main import _loaded_plugins return dict(session=context.id(self.session), viewers=viewers, data=context.id(data), plugins=_loaded_plugins) @classmethod def __setgluestate__(cls, rec, context): self = cls(data_collection=context.object(rec['data'])) # manually register the newly-created session, which # the viewers need context.register_object(rec['session'], self.session) for i, tab in enumerate(rec['viewers']): if self.tab(i) is None: self.new_tab() for v in tab: viewer = context.object(v) self.add_widget(viewer, tab=i, hold_position=True) return self glueviz-1.0.1+dfsg.orig/glue/core/session.py0000644000175000017500000000202313605357235020333 0ustar noahfxnoahfximport weakref from glue.core.command import CommandStack from glue.core.data_collection import DataCollection from glue.core.edit_subset_mode import EditSubsetMode __all__ = ['Session'] class Session(object): def __init__(self, application=None, data_collection=None, command_stack=None, hub=None): self.application = application self.data_collection = data_collection or DataCollection() self.hub = self.data_collection.hub self.command_stack = command_stack or CommandStack() self.command_stack.session = self self.edit_subset_mode = EditSubsetMode() self.edit_subset_mode.data_collection = self.data_collection @property def application(self): if self._application is None: return None else: return self._application() @application.setter def application(self, value): if value is None: self._application = None else: self._application = weakref.ref(value) glueviz-1.0.1+dfsg.orig/glue/core/exceptions.py0000644000175000017500000000035613605357235021040 0ustar noahfxnoahfx class IncompatibleAttribute(Exception): pass class IncompatibleDataException(Exception): pass class UndefinedROI(Exception): pass class InvalidSubscriber(Exception): pass class InvalidMessage(Exception): pass glueviz-1.0.1+dfsg.orig/glue/core/roi.py0000644000175000017500000013505113752534424017451 0ustar noahfxnoahfximport copy import numpy as np from matplotlib.patches import Ellipse, Polygon, Rectangle, Path as MplPath, PathPatch from matplotlib.transforms import IdentityTransform, blended_transform_factory from glue.core.component import CategoricalComponent from glue.core.exceptions import UndefinedROI from glue.utils import points_inside_poly, iterate_chunks np.seterr(all='ignore') __all__ = ['Roi', 'RectangularROI', 'CircularROI', 'PolygonalROI', 'AbstractMplRoi', 'MplRectangularROI', 'MplCircularROI', 'MplPolygonalROI', 'MplXRangeROI', 'MplYRangeROI', 'XRangeROI', 'RangeROI', 'YRangeROI', 'VertexROIBase', 'CategoricalROI', 'EllipticalROI'] PATCH_COLOR = '#FFFF00' SCRUBBING_KEY = 'control' def aspect_ratio(axes): """ Returns the pixel height / width of a box that spans 1 data unit in x and y """ width = axes.get_position().width * axes.figure.get_figwidth() height = axes.get_position().height * axes.figure.get_figheight() xmin, xmax = axes.get_xlim() ymin, ymax = axes.get_ylim() return height / width / (ymax - ymin) * (xmax - xmin) def data_to_norm(axes, x, y): xy = np.column_stack((np.asarray(x).ravel(), np.asarray(y).ravel())) pixel = axes.transData.transform(xy) norm = axes.transAxes.inverted().transform(pixel) return norm def data_to_pixel(axes, x, y): xy = np.column_stack((np.asarray(x).ravel(), np.asarray(y).ravel())) return axes.transData.transform(xy) def pixel_to_data(axes, x, y): xy = np.column_stack((np.asarray(x).ravel(), np.asarray(y).ravel())) return axes.transData.inverted().transform(xy) def pixel_to_axes(axes, x, y): xy = np.column_stack((np.asarray(x).ravel(), np.asarray(y).ravel())) return axes.transAxes.inverted().transform(xy) class Roi(object): # pragma: no cover """ A geometrical 2D region of interest. Glue uses Roi's to represent user-drawn regions on plots. There are many specific sub-classes of Roi, but they all have a ``contains`` method to test whether a collection of 2D points lies inside the region. """ def contains(self, x, y): """Return true/false for each x/y pair. :param x: Array of X locations :param y: Array of Y locations :returns: A Boolean array, where each element is True if the corresponding (x,y) tuple is inside the Roi. :raises: UndefinedROI exception if not defined """ raise NotImplementedError() def contains3d(self, x, y, z): """Return true/false for each x/y/z pair. Parameters ---------- x : :class:`numpy.ndarray` Array of x locations y : :class:`numpy.ndarray` Array of y locations z : :class:`numpy.ndarray` Array of z locations Returns ------- :class:`numpy.ndarray` A boolean array, where each element is `True` if the corresponding (x,y,z) tuple is inside the Roi. Raises ------ UndefinedROI if not defined """ raise NotImplementedError() def center(self): """Return the (x,y) coordinates of the ROI center""" raise NotImplementedError() def move_to(self, x, y): """Translate the ROI to a center of (x, y)""" raise NotImplementedError() def defined(self): """ Returns whether or not the subset is properly defined """ raise NotImplementedError() def to_polygon(self): """ Returns a tuple of x and y points, approximating the ROI as a polygon.""" raise NotImplementedError() def copy(self): """ Return a clone of the ROI """ return copy.copy(self) def transformed(self, xfunc=None, yfunc=None): """ A transformed version of the ROI """ raise NotImplementedError() class PointROI(Roi): def __init__(self, x=None, y=None): self.x = x self.y = y def contains(self, x, y): return False def move_to(self, x, y): self.x = x self.y = y def defined(self): try: return np.isfinite([self.x, self.y]).all() except TypeError: return False def center(self): return self.x, self.y def reset(self): self.x = self.y = None class RectangularROI(Roi): """ A 2D rectangular region of interest. """ def __init__(self, xmin=None, xmax=None, ymin=None, ymax=None): super(RectangularROI, self).__init__() self.xmin = xmin self.xmax = xmax self.ymin = ymin self.ymax = ymax def __str__(self): if self.defined(): return "x=[%0.3f, %0.3f], y=[%0.3f, %0.3f]" % (self.xmin, self.xmax, self.ymin, self.ymax) else: return "Undefined Rectangular ROI" def center(self): return self.xmin + self.width() / 2, self.ymin + self.height() / 2 def move_to(self, x, y): cx, cy = self.center() dx = x - cx dy = y - cy self.xmin += dx self.xmax += dx self.ymin += dy self.ymax += dy def transpose(self, copy=True): if copy: new = self.copy() new.xmin, new.xmax = self.ymin, self.ymax new.ymin, new.ymax = self.xmin, self.xmax return new self.xmin, self.ymin = self.ymin, self.xmin self.xmax, self.ymax = self.ymax, self.xmax def corner(self): return (self.xmin, self.ymin) def width(self): return self.xmax - self.xmin def height(self): return self.ymax - self.ymin def contains(self, x, y): """ Test whether a set of (x,y) points falls within the region of interest :param x: A scalar or numpy array of x points :param y: A scalar or numpy array of y points *Returns* A list of True/False values, for whether each (x,y) point falls within the ROI """ if not self.defined(): raise UndefinedROI return (x > self.xmin) & (x < self.xmax) & \ (y > self.ymin) & (y < self.ymax) def update_limits(self, xmin, ymin, xmax, ymax): """ Update the limits of the rectangle """ self.xmin = min(xmin, xmax) self.xmax = max(xmin, xmax) self.ymin = min(ymin, ymax) self.ymax = max(ymin, ymax) def reset(self): """ Reset the rectangular region. """ self.xmin = None self.xmax = None self.ymin = None self.ymax = None def defined(self): return self.xmin is not None def to_polygon(self): if self.defined(): return (np.array([self.xmin, self.xmax, self.xmax, self.xmin, self.xmin]), np.array([self.ymin, self.ymin, self.ymax, self.ymax, self.ymin])) else: return [], [] def transformed(self, xfunc=None, yfunc=None): xmin = self.xmin if xfunc is None else xfunc(self.xmin) xmax = self.xmax if xfunc is None else xfunc(self.xmax) ymin = self.ymin if yfunc is None else yfunc(self.ymin) ymax = self.ymax if yfunc is None else yfunc(self.ymax) return RectangularROI(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax) def __gluestate__(self, context): return dict(xmin=context.do(self.xmin), xmax=context.do(self.xmax), ymin=context.do(self.ymin), ymax=context.do(self.ymax)) @classmethod def __setgluestate__(cls, rec, context): return cls(xmin=context.object(rec['xmin']), xmax=context.object(rec['xmax']), ymin=context.object(rec['ymin']), ymax=context.object(rec['ymax'])) class RangeROI(Roi): def __init__(self, orientation, min=None, max=None): """:param orientation: 'x' or 'y'. Sets which axis to range""" super(RangeROI, self).__init__() self.min = min self.max = max self.ori = orientation @property def ori(self): return self._ori @ori.setter def ori(self, value): if value in set('xy'): self._ori = value else: raise ValueError("Orientation must be one of 'x', 'y'") def __str__(self): if self.defined(): return "%0.3f < %s < %0.3f" % (self.min, self.ori, self.max) else: return "Undefined %s" % type(self).__name__ def range(self): return self.min, self.max def center(self): return (self.min + self.max) / 2 def set_range(self, lo, hi): self.min, self.max = lo, hi def move_to(self, center): delta = center - self.center() self.min += delta self.max += delta def contains(self, x, y): if not self.defined(): raise UndefinedROI() coord = x if self.ori == 'x' else y return (coord > self.min) & (coord < self.max) def transformed(self, xfunc=None, yfunc=None): if self.ori == 'x': vmin = self.min if xfunc is None else xfunc(self.min) vmax = self.max if xfunc is None else xfunc(self.max) else: vmin = self.min if yfunc is None else yfunc(self.min) vmax = self.max if yfunc is None else yfunc(self.max) return RangeROI(orientation=self.ori, min=vmin, max=vmax) def reset(self): self.min = None self.max = None def defined(self): return self.min is not None and self.max is not None def to_polygon(self): if self.defined(): on = [self.min, self.max, self.max, self.min, self.min] off = [-1e100, -1e100, 1e100, 1e100, -1e100] x, y = (on, off) if (self.ori == 'x') else (off, on) return x, y else: return [], [] def __gluestate__(self, context): return dict(ori=self.ori, min=context.do(self.min), max=context.do(self.max)) @classmethod def __setgluestate__(cls, rec, context): if cls is XRangeROI or cls is YRangeROI: return cls(min=rec['min'], max=rec['max']) else: return cls(rec['ori'], min=rec['min'], max=rec['max']) class XRangeROI(RangeROI): def __init__(self, min=None, max=None): super(XRangeROI, self).__init__('x', min=min, max=max) class YRangeROI(RangeROI): def __init__(self, min=None, max=None): super(YRangeROI, self).__init__('y', min=min, max=max) class CircularROI(Roi): """ A 2D circular region of interest. """ def __init__(self, xc=None, yc=None, radius=None): super(CircularROI, self).__init__() self.xc = xc self.yc = yc self.radius = radius def contains(self, x, y): """ Test whether a set of (x,y) points falls within the region of interest :param x: A list of x points :param y: A list of y points *Returns* A list of True/False values, for whether each (x,y) point falls within the ROI """ if not self.defined(): raise UndefinedROI if not isinstance(x, np.ndarray): x = np.asarray(x) if not isinstance(y, np.ndarray): y = np.asarray(y) return (x - self.xc) ** 2 + (y - self.yc) ** 2 < self.radius ** 2 def set_center(self, x, y): """ Set the center of the circular region """ self.xc = x self.yc = y def set_radius(self, radius): """ Set the radius of the circular region """ self.radius = radius def get_center(self): return self.xc, self.yc def get_radius(self): return self.radius def reset(self): """ Reset the rectangular region. """ self.xc = None self.yc = None self.radius = 0. def defined(self): """ Returns True if the ROI is defined """ return self.xc is not None and \ self.yc is not None and self.radius is not None def to_polygon(self): """ Returns x, y, where each is a list of points """ if not self.defined(): return [], [] theta = np.linspace(0, 2 * np.pi, num=20) x = self.xc + self.radius * np.cos(theta) y = self.yc + self.radius * np.sin(theta) return x, y def transformed(self, xfunc=None, yfunc=None): return PolygonalROI(*self.to_polygon()).transformed(xfunc=xfunc, yfunc=yfunc) def move_to(self, xdelta, ydelta): self.xc += xdelta self.yc += ydelta def __gluestate__(self, context): return dict(xc=context.do(self.xc), yc=context.do(self.yc), radius=context.do(self.radius)) @classmethod def __setgluestate__(cls, rec, context): return cls(xc=rec['xc'], yc=rec['yc'], radius=rec['radius']) class EllipticalROI(Roi): """ A 2D elliptical region of interest. """ def __init__(self, xc=None, yc=None, radius_x=None, radius_y=None, theta=None): super(EllipticalROI, self).__init__() if theta is not None: raise NotImplementedError("Rotated ellipses are not yet supported") self.xc = xc self.yc = yc self.radius_x = radius_x self.radius_y = radius_y def contains(self, x, y): """ Test whether a set of (x,y) points falls within the region of interest :param x: A list of x points :param y: A list of y points *Returns* A list of True/False values, for whether each (x,y) point falls within the ROI """ if not self.defined(): raise UndefinedROI if not isinstance(x, np.ndarray): x = np.asarray(x) if not isinstance(y, np.ndarray): y = np.asarray(y) return (((x - self.xc) ** 2 / self.radius_x ** 2 + (y - self.yc) ** 2 / self.radius_y ** 2) < 1.) def reset(self): """ Reset the rectangular region. """ self.xc = None self.yc = None self.radius_x = 0. self.radius_y = 0. def defined(self): """ Returns True if the ROI is defined """ return (self.xc is not None and self.yc is not None and self.radius_x is not None and self.radius_y is not None) def to_polygon(self): """ Returns x, y, where each is a list of points """ if not self.defined(): return [], [] theta = np.linspace(0, 2 * np.pi, num=20) x = self.xc + self.radius_x * np.cos(theta) y = self.yc + self.radius_y * np.sin(theta) return x, y def transformed(self, xfunc=None, yfunc=None): return PolygonalROI(*self.to_polygon()).transformed(xfunc=xfunc, yfunc=yfunc) def move_to(self, xdelta, ydelta): self.xc += xdelta self.yc += ydelta def __gluestate__(self, context): return dict(xc=context.do(self.xc), yc=context.do(self.yc), radius_x=context.do(self.radius_x), radius_y=context.do(self.radius_y)) @classmethod def __setgluestate__(cls, rec, context): return cls(xc=rec['xc'], yc=rec['yc'], radius_x=rec['radius_x'], radius_y=rec['radius_y']) class VertexROIBase(Roi): def __init__(self, vx=None, vy=None): """ :param vx: initial x vertices :type vx: list :param vy: initial y vertices :type vy: list """ super(VertexROIBase, self).__init__() self.vx = vx self.vy = vy if self.vx is None: self.vx = [] if self.vy is None: self.vy = [] def transformed(self, xfunc=None, yfunc=None): vx = self.vx if xfunc is None else xfunc(np.asarray(self.vx)) vy = self.vy if yfunc is None else yfunc(np.asarray(self.vy)) return self.__class__(vx=vx, vy=vy) def add_point(self, x, y): """ Add another vertex to the ROI :param x: The x coordinate :param y: The y coordinate """ self.vx.append(x) self.vy.append(y) def reset(self): """ Reset the vertex list. """ self.vx = [] self.vy = [] def replace_last_point(self, x, y): if len(self.vx) > 0: self.vx[-1] = x self.vy[-1] = y def remove_point(self, x, y, thresh=None): """Remove the vertex closest to a reference (xy) point :param x: The x coordinate of the reference point :param y: The y coordinate of the reference point :param thresh: An optional threshold. If present, the vertex closest to (x,y) will only be removed if the distance is less than thresh """ if len(self.vx) == 0: return # find distance between vertices and input dist = [(x - a) ** 2 + (y - b) ** 2 for a, b in zip(self.vx, self.vy)] inds = range(len(dist)) near = min(inds, key=lambda x: dist[x]) if thresh is not None and dist[near] > (thresh ** 2): return self.vx = [self.vx[i] for i in inds if i != near] self.vy = [self.vy[i] for i in inds if i != near] def defined(self): return len(self.vx) > 0 def to_polygon(self): return self.vx, self.vy def __gluestate__(self, context): return dict(vx=context.do(np.asarray(self.vx)), vy=context.do(np.asarray(self.vy))) @classmethod def __setgluestate__(cls, rec, context): return cls(vx=context.object(rec['vx']), vy=context.object(rec['vy'])) class PolygonalROI(VertexROIBase): """ A class to define 2D polygonal regions-of-interest """ def __str__(self): result = 'Polygonal ROI (' result += ','.join(['(%s, %s)' % (x, y) for x, y in zip(self.vx, self.vy)]) result += ')' return result def contains(self, x, y): """ Test whether a set of (x,y) points falls within the region of interest :param x: A list of x points :param y: A list of y points *Returns* A list of True/False values, for whether each (x,y) point falls within the ROI """ if not self.defined(): raise UndefinedROI if not isinstance(x, np.ndarray): x = np.asarray(x) if not isinstance(y, np.ndarray): y = np.asarray(y) result = points_inside_poly(x, y, self.vx, self.vy) return result def move_to(self, xdelta, ydelta): self.vx = list(map(lambda x: x + xdelta, self.vx)) self.vy = list(map(lambda y: y + ydelta, self.vy)) class Projected3dROI(Roi): """"A region of interest defined in screen coordinates. The screen coordinates are defined by the projection matrix. The projection matrix converts homogeneous coordinates (x, y, z, w), where w is implicitly 1, to homogeneous screen coordinates (usually the product of the world and projection matrix). """ def __init__(self, roi_2d=None, projection_matrix=None): super(Projected3dROI, self).__init__() self.roi_2d = roi_2d self.projection_matrix = np.asarray(projection_matrix) def contains3d(self, x, y, z): """ Test whether the projected coordinates are contained in the 2d ROI. """ if not self.defined(): raise UndefinedROI x = np.asarray(x) y = np.asarray(y) z = np.asarray(z) # Since the projection can significantly increase the memory usage, we # do the following operation in chunks. In future we could likely use # e.g. vaex, dask, or other multi-threaded/fast libraries to speed this # and other ROI code up. mask = np.zeros(x.shape, dtype=bool) for slices in iterate_chunks(x.shape, n_max=1000000): # Work in homogeneous coordinates so we can support perspective # projections as well x_sub, y_sub, z_sub = x[slices], y[slices], z[slices] vertices = np.array([x_sub, y_sub, z_sub, np.ones(x_sub.shape)]) # The following returns homogeneous screen coordinates screen_h = np.tensordot(self.projection_matrix, vertices, axes=(1, 0)) # Convert to screen coordinates, as we don't care about z screen_x, screen_y = screen_h[:2] / screen_h[3] mask[slices] = self.roi_2d.contains(screen_x, screen_y) return mask def __gluestate__(self, context): return dict(roi_2d=context.id(self.roi_2d), projection_matrix=self.projection_matrix.tolist()) @classmethod def __setgluestate__(cls, rec, context): return cls(roi_2d=context.object(rec['roi_2d']), projection_matrix=np.asarray(rec['projection_matrix'])) # TODO: these methods forward directly to roi_2d, not sure if this makes sense for all def contains(self, x, y): return self.roi_2d.contains(x, y) def center(self): return self.roi_2d.center() def move_to(self, x, y): return self.roi_2d.move_to(x, y) def defined(self): return self.roi_2d.defined() def to_polygon(self): return self.roi_2d.to_polygon() def transformed(self, xfunc=None, yfunc=None): return self.roi_2d.transformed(xfunc, yfunc) class Path(VertexROIBase): def __str__(self): result = 'Path (' result += ','.join(['(%s, %s)' % (x, y) for x, y in zip(self.vx, self.vy)]) result += ')' return result class AbstractMplRoi(object): """ Base class for objects which use Matplotlib user events to edit/display ROIs. Parameters ---------- axes : `~matplotlib.axes.Axes` The Matplotlib axes to draw to. roi : `~glue.core.roi.Roi`, optional If specified, this ROI will be used and updated, otherwise a new one will be created. """ _roi_cls = None def __init__(self, axes, roi=None, data_space=True): self._axes = axes self._roi = roi or self._roi_cls() self._previous_roi = None self._mid_selection = False self._scrubbing = False self._background_cache = None self._data_space = data_space def _draw(self): # When drawing the ROI, we first keep a cache of the contents of the # plot then just re-plot the ROI artist on top every time for # performance. However, if the background cache hasn't been set, we need # to do a full draw. if self._background_cache is None or not self._axes.figure.canvas.supports_blit: self._axes.figure.canvas.draw_idle() else: self._axes.figure.canvas.restore_region(self._background_cache) self._axes.draw_artist(self._patch) self._axes.figure.canvas.blit() def roi(self): return self._roi.copy() def reset(self, include_roi=True): self._mid_selection = False self._scrubbing = False if include_roi: self._roi.reset() self._sync_patch() def active(self): return self._mid_selection def start_selection(self, event): raise NotImplementedError() def update_selection(self, event): raise NotImplementedError() def finalize_selection(self, event): raise NotImplementedError() def abort_selection(self, event): if self._mid_selection: self._restore_previous_roi() self.reset(include_roi=False) def _sync_patch(self): raise NotImplementedError() def _store_previous_roi(self): self._previous_roi = self._roi.copy() def _store_background(self): self._background_cache = self._axes.figure.canvas.copy_from_bbox(self._axes.bbox) def _reset_background(self): # The purpose of this method is to provide a way to reset the background # when the figure is changed (e.g. while panning/zooming) while the ROI # is in the middle of being plotted (this is relevant for 'persistent' # ROIs such as path selections or lasso selections). if self._patch is None or not self._patch.get_visible(): return self._background_cache = None self._patch.set_visible(False) self._axes.figure.canvas.draw() self._store_background() self._patch.set_visible(True) self._axes.figure.canvas.draw_idle() def _restore_previous_roi(self): self._roi = self._previous_roi class MplPickROI(AbstractMplRoi): """ Matplotlib ROI for point selections Parameters ---------- axes : `~matplotlib.axes.Axes` The Matplotlib axes to draw to. """ _roi_cls = PointROI def _draw(self): pass def start_selection(self, event): self._roi.x = event.xdata self._roi.y = event.ydata def update_selection(self, event): self._roi.x = event.xdata self._roi.y = event.ydata def finalize_selection(self, event): self._roi.x = event.xdata self._roi.y = event.ydata def _sync_patch(self): pass class MplRectangularROI(AbstractMplRoi): """ Matplotlib ROI for rectangular selections Parameters ---------- axes : `~matplotlib.axes.Axes` The Matplotlib axes to draw to. """ _roi_cls = RectangularROI def __init__(self, axes, data_space=True): super(MplRectangularROI, self).__init__(axes, data_space=data_space) self._xi = None self._yi = None self.plot_opts = {'edgecolor': PATCH_COLOR, 'facecolor': PATCH_COLOR, 'alpha': 0.3} self._patch = Rectangle((0., 0.), 1., 1., zorder=100) self._patch.set_visible(False) if not self._data_space: self._patch.set_transform(self._axes.transAxes) self._axes.add_patch(self._patch) def start_selection(self, event): if event.inaxes != self._axes: return False if self._data_space: xval = event.xdata yval = event.ydata else: axes_trans = self._axes.transAxes.inverted() xval, yval = axes_trans.transform([event.x, event.y]) if event.key == SCRUBBING_KEY: if not self._roi.defined(): return False elif not self._roi.contains(xval, yval): return False self._store_previous_roi() self._store_background() self._xi = xval self._yi = yval if event.key == SCRUBBING_KEY: self._scrubbing = True self._cx, self._cy = self._roi.center() else: self.reset() self._roi.update_limits(self._xi, self._xi, self._yi, self._yi) self._mid_selection = True self._sync_patch() self._draw() def update_selection(self, event): if not self._mid_selection or event.inaxes != self._axes: return False if event.key == SCRUBBING_KEY: if not self._roi.defined(): return False if self._data_space: xval = event.xdata yval = event.ydata else: axes_trans = self._axes.transAxes.inverted() xval, yval = axes_trans.transform([event.x, event.y]) if self._scrubbing: self._roi.move_to(self._cx + xval - self._xi, self._cy + yval - self._yi) else: self._roi.update_limits(min(xval, self._xi), min(yval, self._yi), max(xval, self._xi), max(yval, self._yi)) self._sync_patch() self._draw() def finalize_selection(self, event): self._scrubbing = False self._mid_selection = False self._patch.set_visible(False) self._draw() def _sync_patch(self): if self._roi.defined(): corner = self._roi.corner() width = self._roi.width() height = self._roi.height() self._patch.set_xy(corner) self._patch.set_width(width) self._patch.set_height(height) self._patch.set(**self.plot_opts) self._patch.set_visible(True) else: self._patch.set_visible(False) def __str__(self): return "MPL Rectangle: %s" % self._patch class MplXRangeROI(AbstractMplRoi): """ Matplotlib ROI for x range selections Parameters ---------- axes : `~matplotlib.axes.Axes` The Matplotlib axes to draw to. """ _roi_cls = XRangeROI def __init__(self, axes, data_space=True): super(MplXRangeROI, self).__init__(axes, data_space=data_space) self._xi = None self.plot_opts = {'edgecolor': PATCH_COLOR, 'facecolor': PATCH_COLOR, 'alpha': 0.3} if self._data_space: trans = blended_transform_factory(self._axes.transData, self._axes.transAxes) else: trans = self._axes.transAxes self._patch = Rectangle((0., 0.), 1., 1., transform=trans, zorder=100) self._patch.set_visible(False) self._axes.add_patch(self._patch) def start_selection(self, event): if event.inaxes != self._axes: return False if self._data_space: x_val = event.xdata y_val = event.ydata else: transform = self._axes.transAxes.inverted() x_val, y_val = transform.transform([event.x, event.y]) if event.key == SCRUBBING_KEY: if not self._roi.defined(): return False elif not self._roi.contains(x_val, y_val): return False self._store_previous_roi() self._store_background() if event.key == SCRUBBING_KEY: self._scrubbing = True self._dx = x_val - self._roi.center() else: self.reset() self._roi.set_range(x_val, x_val) self._xi = x_val self._mid_selection = True self._sync_patch() self._draw() def update_selection(self, event): if not self._mid_selection or event.inaxes != self._axes: return False if event.key == SCRUBBING_KEY: if not self._roi.defined(): return False if self._data_space: xval = event.xdata else: axes_trans = self._axes.transAxes.inverted() xval, _ = axes_trans.transform([event.x, event.y]) if self._scrubbing: self._roi.move_to(xval + self._dx) else: self._roi.set_range(min(xval, self._xi), max(xval, self._xi)) self._sync_patch() self._draw() def finalize_selection(self, event): self._scrubbing = False self._mid_selection = False self._patch.set_visible(False) self._draw() def _sync_patch(self): if self._roi.defined(): rng = self._roi.range() self._patch.set_xy((rng[0], 0)) self._patch.set_width(rng[1] - rng[0]) self._patch.set_height(1) self._patch.set(**self.plot_opts) self._patch.set_visible(True) else: self._patch.set_visible(False) class MplYRangeROI(AbstractMplRoi): """ Matplotlib ROI for y range selections Parameters ---------- axes : `~matplotlib.axes.Axes` The Matplotlib axes to draw to. """ _roi_cls = YRangeROI def __init__(self, axes, data_space=True): super(MplYRangeROI, self).__init__(axes, data_space=data_space) self._yi = None self.plot_opts = {'edgecolor': PATCH_COLOR, 'facecolor': PATCH_COLOR, 'alpha': 0.3} if self._data_space: trans = blended_transform_factory(self._axes.transAxes, self._axes.transData) else: trans = self._axes.transAxes self._patch = Rectangle((0., 0.), 1., 1., transform=trans, zorder=100) self._patch.set_visible(False) self._axes.add_patch(self._patch) def start_selection(self, event): if event.inaxes != self._axes: return False if self._data_space: xval = event.xdata yval = event.ydata else: axes_trans = self._axes.transAxes.inverted() xval, yval = axes_trans.transform([event.x, event.y]) if event.key == SCRUBBING_KEY: if not self._roi.defined(): return False elif not self._roi.contains(xval, yval): return False self._store_previous_roi() self._store_background() if event.key == SCRUBBING_KEY: self._scrubbing = True self._dy = yval - self._roi.center() else: self.reset() self._roi.set_range(yval, yval) self._yi = yval self._mid_selection = True self._sync_patch() self._draw() def update_selection(self, event): if not self._mid_selection or event.inaxes != self._axes: return False if event.key == SCRUBBING_KEY: if not self._roi.defined(): return False if self._data_space: yval = event.ydata else: axes_trans = self._axes.transAxes.inverted() _, yval = axes_trans.transform([event.x, event.y]) if self._scrubbing: self._roi.move_to(yval + self._dy) else: self._roi.set_range(min(yval, self._yi), max(yval, self._yi)) self._sync_patch() self._draw() def finalize_selection(self, event): self._scrubbing = False self._mid_selection = False self._patch.set_visible(False) self._draw() def _sync_patch(self): if self._roi.defined(): rng = self._roi.range() self._patch.set_xy((0, rng[0])) self._patch.set_height(rng[1] - rng[0]) self._patch.set_width(1) self._patch.set(**self.plot_opts) self._patch.set_visible(True) else: self._patch.set_visible(False) class MplCircularROI(AbstractMplRoi): """ Matplotlib ROI for circular selections Since circles on the screen may not be circles in the data (due, e.g., to logarithmic scalings on the axes), the ultimate ROI that is created is a polygonal ROI Parameters ---------- axes : `~matplotlib.axes.Axes` The Matplotlib axes to draw to. """ _roi_cls = CircularROI def __init__(self, axes, data_space=True): super(MplCircularROI, self).__init__(axes, data_space=data_space) self.plot_opts = {'edgecolor': PATCH_COLOR, 'facecolor': PATCH_COLOR, 'alpha': 0.3} self._xi = None self._yi = None self._patch = Ellipse((0., 0.), transform=IdentityTransform(), width=0., height=0., zorder=100) self._patch.set_visible(False) self._axes.add_patch(self._patch) def _sync_patch(self): if self._roi.defined(): xy = self._roi.get_center() r = self._roi.get_radius() self._patch.center = xy self._patch.width = 2. * r self._patch.height = 2. * r self._patch.set(**self.plot_opts) self._patch.set_visible(True) else: self._patch.set_visible(False) def start_selection(self, event): if event.inaxes != self._axes: return False xy = data_to_pixel(self._axes, [event.xdata], [event.ydata]) xi = xy[0, 0] yi = xy[0, 1] if event.key == SCRUBBING_KEY: if not self._roi.defined(): return False elif not self._roi.contains(xi, yi): return False self._store_previous_roi() self._store_background() if event.key == SCRUBBING_KEY: self._scrubbing = True (xc, yc) = self._roi.get_center() self._dx = xc - xi self._dy = yc - yi else: self.reset() self._roi.set_center(xi, yi) self._roi.set_radius(0.) self._xi = xi self._yi = yi self._mid_selection = True self._sync_patch() self._draw() def update_selection(self, event): if not self._mid_selection or event.inaxes != self._axes: return False xy = data_to_pixel(self._axes, [event.xdata], [event.ydata]) xi = xy[0, 0] yi = xy[0, 1] if event.key == SCRUBBING_KEY: if not self._roi.defined(): return False if self._scrubbing: self._roi.set_center(xi + self._dx, yi + self._dy) else: dx = xy[0, 0] - self._xi dy = xy[0, 1] - self._yi self._roi.set_radius(np.hypot(dx, dy)) self._sync_patch() self._draw() def roi(self): if not self._roi.defined(): return PolygonalROI() # Get the circular ROI parameters in pixel units xy_center = self._roi.get_center() rad = self._roi.get_radius() # At this point, if one of the axes is not linear, we convert to a polygon if (self._axes.get_xscale() != 'linear' or self._axes.get_yscale() != 'linear') and self._data_space: theta = np.linspace(0, 2 * np.pi, num=200) x = xy_center[0] + rad * np.cos(theta) y = xy_center[1] + rad * np.sin(theta) xy_data = pixel_to_data(self._axes, x, y) vx = xy_data[:, 0].ravel().tolist() vy = xy_data[:, 1].ravel().tolist() result = PolygonalROI(vx, vy) else: # We should now check if the radius in data coordinates is the same # along x and y, as if so then we can return a circle, otherwise we # should return an ellipse. x = xy_center[0] + np.array([0, 0, rad]) y = xy_center[1] + np.array([0, rad, rad]) xy_data = pixel_to_data(self._axes, x, y) if self._data_space else pixel_to_axes(self._axes, x, y) rx = xy_data[2, 0] - xy_data[0, 0] ry = xy_data[1, 1] - xy_data[0, 1] xc, yc = xy_data[0, :] if np.allclose(rx, ry): return CircularROI(xc=xc, yc=yc, radius=rx) else: return EllipticalROI(xc=xc, yc=yc, radius_x=rx, radius_y=ry) return result def finalize_selection(self, event): self._scrubbing = False self._mid_selection = False self._patch.set_visible(False) self._draw() class MplPolygonalROI(AbstractMplRoi): """ Matplotlib ROI for polygon selections Parameters ---------- axes : `~matplotlib.axes.Axes` The Matplotlib axes to draw to. roi : `~glue.core.roi.Roi`, optional If specified, this ROI will be used and updated, otherwise a new one will be created. """ _roi_cls = PolygonalROI def __init__(self, axes, roi=None, data_space=True): super(MplPolygonalROI, self).__init__(axes, roi=roi, data_space=data_space) self.plot_opts = {'edgecolor': PATCH_COLOR, 'facecolor': PATCH_COLOR, 'alpha': 0.3} self._patch = Polygon(np.array(list(zip([0, 1], [0, 1]))), zorder=100) self._patch.set_visible(False) if not self._data_space: self._patch.set_transform(self._axes.transAxes) self._axes.add_patch(self._patch) def _sync_patch(self): if self._roi.defined(): x, y = self._roi.to_polygon() self._patch.set_xy(list(zip(x + [x[0]], y + [y[0]]))) self._patch.set_visible(True) self._patch.set(**self.plot_opts) else: self._patch.set_visible(False) def start_selection(self, event, scrubbing=False): if event.inaxes != self._axes: return False if self._data_space: xval = event.xdata yval = event.ydata else: axes_trans = self._axes.transAxes.inverted() xval, yval = axes_trans.transform([event.x, event.y]) if scrubbing or event.key == SCRUBBING_KEY: if not self._roi.defined(): return False elif not self._roi.contains(xval, yval): return False self._store_previous_roi() self._store_background() if scrubbing or event.key == SCRUBBING_KEY: self._scrubbing = True self._cx = xval self._cy = yval else: self.reset() self._roi.add_point(xval, yval) self._mid_selection = True self._sync_patch() self._draw() def update_selection(self, event): if not self._mid_selection or event.inaxes != self._axes: return False if event.key == SCRUBBING_KEY: if not self._roi.defined(): return False if self._data_space: xval = event.xdata yval = event.ydata else: axes_trans = self._axes.transAxes.inverted() xval, yval = axes_trans.transform([event.x, event.y]) if self._scrubbing: self._roi.move_to(xval - self._cx, yval - self._cy) self._cx = xval self._cy = yval else: self._roi.add_point(xval, yval) self._sync_patch() self._draw() def finalize_selection(self, event): self._scrubbing = False self._mid_selection = False self._patch.set_visible(False) self._draw() class MplPathROI(MplPolygonalROI): """ Matplotlib ROI for path selections Parameters ---------- axes : `~matplotlib.axes.Axes` The Matplotlib axes to draw to. """ _roi_cls = Path def __init__(self, axes, roi=None): super(MplPolygonalROI, self).__init__(axes) self.plot_opts = {'edgecolor': PATCH_COLOR, 'facecolor': PATCH_COLOR, 'alpha': 0.3} self._patch = None def start_selection(self, event): if self._patch is not None: self._patch.remove() self._patch = None self._background_cache = None self._axes.figure.canvas.draw() super(MplPathROI, self).start_selection(event) def _sync_patch(self): if self._patch is not None: self._patch.remove() self._patch = None if self._roi.defined(): x, y = self._roi.to_polygon() p = MplPath(np.column_stack((x, y))) self._patch = PathPatch(p, transform=self._axes.transData) self._patch.set_visible(True) self._patch.set(**self.plot_opts) self._axes.add_artist(self._patch) def finalize_selection(self, event): self._mid_selection = False if self._patch is not None: self._patch.remove() self._patch = None self._draw() class CategoricalROI(Roi): """ A ROI abstraction to represent selections of categorical data. """ def __init__(self, categories=None): if categories is None: self.categories = None else: self.update_categories(categories) def to_polygon(self): """ Just not possible. """ raise NotImplementedError def _categorical_helper(self, indata): """ A helper function to do the rigamaroll of getting categorical data. :param indata: Any type of input data :return: The best guess at the categorical data associated with indata """ try: if isinstance(indata, CategoricalComponent): return indata.data else: return indata[:] except AttributeError: return np.asarray(indata) def contains(self, x, y): """ Test whether a set categorical elements fall within the region of interest :param x: Any array-like object of categories (includes CategoricalComponenets) :param y: Unused but required for compatibility *Returns* A list of True/False values, for whether each x value falls within the ROI """ if self.categories is None or len(self.categories) == 0: return np.zeros(x.shape, dtype=bool) else: check = self._categorical_helper(x) index = np.minimum(np.searchsorted(self.categories, check), len(self.categories) - 1) return self.categories[index] == check def update_categories(self, categories): self.categories = np.unique(self._categorical_helper(categories)) def defined(self): """ Returns True if the ROI is defined """ return self.categories is not None def reset(self): self.categories = None @staticmethod def from_range(categories, lo, hi): """ Utility function to help construct the Roi from a range. :param cat_comp: Anything understood by ._categorical_helper ... array, list or component :param lo: lower bound of the range :param hi: upper bound of the range :return: CategoricalROI object """ # Convert lo and hi to integers. Note that if lo or hi are negative, # which can happen if the user zoomed out, we need to reset the to zero # otherwise they will have strange effects when slicing the categories. # Note that we used ceil for lo, because if lo is 0.9 then we should # only select 1 and above. lo = np.intp(np.ceil(lo) if lo > 0 else 0) hi = np.intp(np.ceil(hi) if hi > 0 else 0) roi = CategoricalROI() roi.update_categories(categories[lo:hi]) return roi def __gluestate__(self, context): return dict(categories=self.categories.tolist()) @classmethod def __setgluestate__(cls, rec, context): return cls(categories=rec['categories']) glueviz-1.0.1+dfsg.orig/glue/core/subset_group.py0000644000175000017500000001570413605357235021403 0ustar noahfxnoahfx""" A :class:`~glue.core.subset_group.SubsetGroup` unites a group of :class:`~glue.core.subset.Subset` instances together with a consistent state, label, and style. While subsets are internally associated with particular datasets, it's confusing for the user to juggle multiple similar or identical subsets, applied to different datasets. Because of this, the GUI manages SubsetGroups, and presents each group to the user as a single entity. The individual subsets are held in-sync by the SubsetGroup. Client code should *only* create Subset Groups via DataCollection.new_subset_group. It should *not* call Data.add_subset or Data.new_subset directly """ from warnings import warn from glue.core.contracts import contract from glue.core.message import (DataCollectionAddMessage, DataCollectionDeleteMessage) from glue.core.visual import VisualAttributes from glue.core.hub import HubListener from glue.utils import Pointer from glue.core.subset import SubsetState from glue.core import Subset from glue.config import settings __all__ = ['GroupedSubset', 'SubsetGroup'] class GroupedSubset(Subset): """ A member of a SubsetGroup, whose internal representation is shared with other group members """ subset_state = Pointer('group.subset_state') label = Pointer('group.label') def __init__(self, data, group): """ :param data: :class:`~glue.core.data.Data` instance to bind to :param group: :class:`~glue.core.subset_group.SubsetGroup` """ # We deliberately don't call Subset.__init__ here because we don't want # to set e.g. the subset state, color, transparency, etc. Instead we # just want to defer to the SubsetGroup for these. self._broadcasting = False # must be first def self.group = group self.data = data self.label = group.label # trigger disambiguation @property def style(self): return self.group.style @property def subset_group(self): return self.group.subset_group @property def verbose_label(self): return "%s (%s)" % (self.label, self.data.label) def __eq__(self, other): return other is self # If __eq__ is defined, then __hash__ has to be re-defined __hash__ = object.__hash__ def __gluestate__(self, context): return dict(group=context.id(self.group), style=context.do(self.style)) @classmethod def __setgluestate__(cls, rec, context): dummy_grp = SubsetGroup() # __init__ needs group.label self = cls(None, dummy_grp) yield self self.group = context.object(rec['group']) class SubsetGroup(HubListener): def __init__(self, color=settings.SUBSET_COLORS[0], alpha=0.5, label=None, subset_state=None): """ Create a new empty SubsetGroup Note: By convention, SubsetGroups should be created via DataCollection.new_subset. """ self.subsets = [] if subset_state is None: self.subset_state = SubsetState() else: self.subset_state = subset_state self.label = label self.style = VisualAttributes(parent=self) self.style.markersize *= 2.5 self.style.linewidth *= 2.5 self.style.color = color self.style.alpha = alpha @contract(data='isinstance(DataCollection)') def register(self, data): """ Register to a :class:`~glue.core.data_collection.DataCollection` This is called automatically by :meth:`glue.core.data_collection.DataCollection.new_subset_group` """ self.register_to_hub(data.hub) # add to self, then register, so fully populated by first # broadcast for d in data: s = GroupedSubset(d, self) self.subsets.append(s) for d, s in zip(data, self.subsets): d.add_subset(s) def paste(self, other_subset): """paste subset state from other_subset onto self """ state = other_subset.subset_state.copy() self.subset_state = state def _add_data(self, data): # add a new data object to group s = GroupedSubset(data, self) data.add_subset(s) self.subsets.append(s) def _remove_data(self, data): # remove a data object from group for s in list(self.subsets): if s.data is data: self.subsets.remove(s) def register_to_hub(self, hub): hub.subscribe(self, DataCollectionAddMessage, lambda x: self._add_data(x.data)) hub.subscribe(self, DataCollectionDeleteMessage, lambda x: self._remove_data(x.data)) @property def style(self): return self._style @style.setter def style(self, value): self._style = value @contract(item='string') def broadcast(self, item): for s in self.subsets: s.broadcast(item) def __setattr__(self, attr, value): # We terminate early here if the value is not None but hasn't changed # to avoid broadcasting a message after. if value is not None and value == getattr(self, attr, None): return object.__setattr__(self, attr, value) if attr in ['subset_state', 'label', 'style']: self.broadcast(attr) def __gluestate__(self, context): return dict(label=self.label, state=context.id(self.subset_state), style=context.do(self.style), subsets=list(map(context.id, self.subsets))) @classmethod def __setgluestate__(cls, rec, context): result = cls() yield result result.subset_state = context.object(rec['state']) result.label = rec['label'] result.style = context.object(rec['style']) result.style.parent = result result.subsets = list(map(context.object, rec['subsets'])) def __and__(self, other): return self.subset_state & other.subset_state def __or__(self, other): return self.subset_state | other.subset_state def __xor__(self, other): return self.subset_state ^ other.subset_state def __invert__(self): return ~self.subset_state def coerce_subset_groups(collect): """ If necessary, reassign non-grouped subsets in a DataCollection into SubsetGroups. This is used to support DataCollections saved with version 1 of glue.core.state.save_data_collection """ for data in collect: for subset in data.subsets: if not isinstance(subset, GroupedSubset): warn("DataCollection has subsets outside of " "subset groups, which are no longer supported. " "Moving to subset groups") subset.delete() grp = collect.new_subset_group() grp.subset_state = subset.subset_state grp.style = subset.style grp.label = subset.label glueviz-1.0.1+dfsg.orig/glue/core/link_manager.py0000644000175000017500000003322413660511701021275 0ustar noahfxnoahfx""" The LinkManager class is responsible for maintaining the conistency of the "web of links" in a DataCollection. It discovers how to combine ComponentLinks together to discover all of the ComponentIDs that a Data object can derive, As a trivial example, imagine a chain of 2 ComponentLinks linking ComponentIDs across 3 datasets: Data: D1 D2 D3 ComponentID: x y z Link: <---x2y---><--y2z--> The LinkManager autocreates a link from D1.id['x'] to D3.id['z'] by chaining x2y and y2z. """ import logging import numpy as np from glue.core.hub import HubListener from glue.core.message import DataCollectionDeleteMessage, DataRemoveComponentMessage from glue.core.contracts import contract from glue.core.link_helpers import LinkCollection from glue.core.component_link import ComponentLink from glue.core.data import Data from glue.core.component import DerivedComponent from glue.core.exceptions import IncompatibleAttribute from glue.core.subset import Subset from glue.utils import unbroadcast from glue.core.coordinate_helpers import dependent_axes __all__ = ['accessible_links', 'discover_links', 'find_dependents', 'LinkManager', 'is_equivalent_cid', 'pixel_cid_to_pixel_cid_matrix'] def accessible_links(cids, links): """ Calculate all ComponentLink objects in a list that can be calculated from a collection of componentIds :param cids: Collection of ComponentID objects :param links: Iterable of ComponentLink objects :rtype: list A list of all links that can be evaluated given the input ComponentIDs """ cids = set(cids) return [l for l in links if set(l.get_from_ids()) <= cids] def discover_links(data, links): """ Discover all links to components that can be derived based on the current components known to a dataset, and a set of ComponentLinks. :param Data: Data object to discover new components for :param links: Set of ComponentLinks to use :rtype: dict A dict of componentID -> componentLink The ComponentLink that data can use to generate the componentID. """ # TODO: try to add shortest paths first -- should # prevent lots of repeated checking cids = set(data.main_components + data.coordinate_components) cid_links = {} depth = {} for cid in cids: depth[cid] = 0 while True: for link in accessible_links(cids, links): from_ = set(link.get_from_ids()) to_ = link.get_to_id() if len(from_) > 0: cost = max([depth[f] for f in from_]) + 1 else: cost = 1 if to_ in cids and cost >= depth[to_]: continue depth[to_] = cost cids.add(to_) cid_links[to_] = link break else: # no more links to add break return cid_links def find_dependents(data, link): """ Determine which `DerivedComponents` in a data set depend (either directly or implicitly) on a given `ComponentLink`. :param data: The data object to consider :param link: The `ComponentLink` object to consider :rtype: set A `set` of `glue.core.component.DerivedComponent` IDs that cannot be calculated without the input `Link` """ dependents = set() visited = set() while True: for derived in data.derived_components: derived = data.get_component(derived) if derived in visited: continue to_, from_ = derived.link.get_to_id(), derived.link.get_from_ids() if derived.link is link: dependents.add(to_) visited.add(derived) break if any(f in dependents for f in from_): dependents.add(to_) visited.add(derived) break else: break # nothing more to remove return dependents class LinkManager(HubListener): """A helper class to generate and store ComponentLinks, and compute which components are accesible from which data sets """ def __init__(self, data_collection=None): self._external_links = [] self.hub = None self.trigger = False self.data_collection = data_collection def register_to_hub(self, hub): self.hub = hub self.hub.subscribe(self, DataRemoveComponentMessage, handler=self._component_removed) self.hub.subscribe(self, DataCollectionDeleteMessage, handler=self._data_removed) def _component_removed(self, msg): remove = [] remove_cid = msg.component_id for link in self._external_links: if remove_cid in link: remove.append(link) for link in remove: self.remove_link(link) def _data_removed(self, msg): remove = [] for link in self._external_links: for cid in msg.data.components: if cid in link and cid.parent is msg.data: remove.append(link) break for link in remove: self.remove_link(link) def clear_links(self): self._external_links[:] = [] def add_link(self, link, update_external=True): """ Ingest one or more ComponentLinks to the manager Parameters ---------- link : ComponentLink, LinkCollection, or list thereof The link(s) to ingest """ if isinstance(link, list): for l in link: self.add_link(l, update_external=False) if update_external: self.update_externally_derivable_components() else: if link not in self._external_links and isinstance(link, LinkCollection) or link.inverse not in self._external_links: self._external_links.append(link) if update_external: self.update_externally_derivable_components() @contract(link=ComponentLink) def remove_link(self, link, update_external=True): if isinstance(link, list): for l in link: self.remove_link(l, update_external=False) if update_external: self.update_externally_derivable_components() else: logging.getLogger(__name__).debug('removing link %s', link) self._external_links.remove(link) if update_external: self.update_externally_derivable_components() @contract(data=Data) def update_externally_derivable_components(self, data=None): """ Update all the externally derived components in all data objects, based on all the Components deriveable based on the links in self. This overrides any ComponentLinks stored in the DerivedComponents of the data itself -- any components which depend on a link not tracked by the LinkManager will be deleted. Parameters ----------- data : Data object Behavior -------- DerivedComponents will be replaced / added into the data object """ if self.data_collection is None: if data is None: return else: data_collection = [data] else: data_collection = self.data_collection # Only keep actual Data instances since only they support links for now data_collection = [d for d in data_collection if isinstance(d, Data)] for data in data_collection: links = discover_links(data, self._links | self._inverse_links) comps = {} for cid, link in links.items(): d = DerivedComponent(data, link) comps[cid] = d data._set_externally_derivable_components(comps) # Now update information about pixel-aligned data for data1 in data_collection: equivalent = {} for data2 in data_collection: if data1 is not data2: order = equivalent_pixel_cids(data2, data1) if order is not None: equivalent[data2] = order data1._set_pixel_aligned_data(equivalent) @property def _links(self): if self.data_collection is None: data_links = set() else: data_links = set(link for data in self.data_collection for link in getattr(data, 'links', [])) external_links = set() for link in self._external_links: if isinstance(link, LinkCollection): for sublink in link: external_links.add(sublink) else: external_links.add(link) return data_links | external_links @property def _inverse_links(self): return set(link.inverse for link in self._links if link.inverse is not None) @property def links(self): return list(self._links) @property def external_links(self): return self._external_links def clear(self): self._external_links[:] = [] def __contains__(self, item): return item in self._links def _find_identical_reference_cid(data, cid): """ Given a dataset and a component ID, return the equivalent component ID that truly belongs to the dataset (not via a link). Returns None if there is no strictly identical component in the dataset. """ try: target_comp = data.get_component(cid) except IncompatibleAttribute: return None if isinstance(target_comp, DerivedComponent): if target_comp.link.identity: updated_cid = target_comp.link.get_from_ids()[0] return _find_identical_reference_cid(data, updated_cid) else: return None else: return cid def is_equivalent_cid(data, cid1, cid2): """ Convenience function to determine if two component IDs in a dataset are equivalent. Parameters ---------- data : `~glue.core.data.Data` The data object in which to check for the component IDs cid1, cid2 : `~glue.core.ComponentID` The two component IDs to compare """ # Dereference the component IDs to find base component ID cid1 = _find_identical_reference_cid(data, cid1) cid2 = _find_identical_reference_cid(data, cid2) return cid1 is cid2 def is_convertible_to_single_pixel_cid(data, cid): """ Given a dataset and a component ID, determine whether a pixel component exists in data such that the component ID can be derived solely from the pixel component. Returns `None` if no such pixel component ID can be found and returns the pixel component ID if one exists. Parameters ---------- data : `~glue.core.data.Data` The data in which to check for pixel components IDs cid : `~glue.core.ComponentID` The component ID to search for """ if isinstance(data, Subset): data = data.data if cid in data.pixel_component_ids: return cid else: try: target_comp = data.get_component(cid) except IncompatibleAttribute: return None if cid in data.world_component_ids: if len(dependent_axes(data.coords, target_comp.axis)) == 1: return data.pixel_component_ids[target_comp.axis] else: return None else: if isinstance(target_comp, DerivedComponent): from_ids = [is_convertible_to_single_pixel_cid(data, c) for c in target_comp.link.get_from_ids()] if None in from_ids: return None else: # Use set to get rid of duplicates from_ids = list(set(from_ids)) if len(from_ids) == 1: return is_convertible_to_single_pixel_cid(data, from_ids[0]) def equivalent_pixel_cids(reference, target): """ Given two datasets with potentially linked pixel coordinates, determine whether the two datasets have an equivalent set of pixel component IDs. Note that the target can have fewer pixel components than the reference. This returns either the order of the reference pixel component IDs in the target dataset, or `None` if there is no match """ # Shortcut - if target has more dimensions than the reference, we know # it's impossible for all pixel components in target to be contained in the # reference if target.ndim > reference.ndim: return # Shortcut, if target is reference, we can just return the axis order if target is reference: return list(range(reference.ndim)) order = [] for tar_cid in target.pixel_component_ids: for iref, ref_cid in enumerate(reference.pixel_component_ids): if is_equivalent_cid(reference, ref_cid, tar_cid): order.append(iref) break else: return None return order def pixel_cid_to_pixel_cid_matrix(data1, data2): """ Given two datasets, return a boolean matrix indicating which of the pixel components are linked. The returned matrix has the shape (data1.ndim, data2.ndim). """ # For simplicity, and to avoid code used elsewhere, we rely on the fact # that independent coordinates are broadcasted during conversions. matrix = np.zeros((data1.ndim, data2.ndim), dtype=bool) for idim, pix_cid in enumerate(data1.pixel_component_ids): try: pix_coords = unbroadcast(data2[pix_cid, (slice(2),) * data2.ndim]) matrix[idim] = np.array(pix_coords.shape) == 2 except IncompatibleAttribute: pass return matrix glueviz-1.0.1+dfsg.orig/glue/_mpl_backend.py0000644000175000017500000000266513605357235020332 0ustar noahfxnoahfximport os class MatplotlibBackendSetter(object): """ Import hook to make sure the proper Qt backend is set when importing Matplotlib. """ enabled = True def find_module(self, mod_name, pth=None): if self.enabled and 'matplotlib' in mod_name: self.enabled = False set_mpl_backend() def find_spec(self, name, import_path, target_module=None): if self.enabled and name.startswith('matplotlib'): self.enabled = False set_mpl_backend() def set_mpl_backend(): from matplotlib import rcParams, rcdefaults # Standardize mpl setup rcdefaults() # Set default backend to Agg. The Qt and Jupyter glue applications don't # use the default backend, so this is just to make sure that importing # matplotlib doesn't cause errors related to the MacOSX or Qt backend. rcParams['backend'] = 'Agg' # Disable key bindings in matplotlib for setting in list(rcParams.keys()): if setting.startswith('keymap'): rcParams[setting] = '' # Set the MPLBACKEND variable explicitly, because ipykernel uses the lack of # MPLBACKEND variable to indicate that it should use its own backend, and # this in turn causes some rcParams to be changed, causing test failures # etc. os.environ['MPLBACKEND'] = 'Agg' # Explicitly switch backend from matplotlib.pyplot import switch_backend switch_backend('agg') glueviz-1.0.1+dfsg.orig/glue/default_config.py0000644000175000017500000000054613605357235020701 0ustar noahfxnoahfx """Declare any extra link functions like this""" # @link_function(info='translates A to B', output_labels=['b']) # def a_to_b(a): # return a * 3 """Data factories take a filename as input and return a Data object""" # @data_factory('JPEG Image') # def jpeg_reader(file_name): # ... # return data """Extra qt clients""" # qt_client(ClientClass) glueviz-1.0.1+dfsg.orig/glue/main.py0000755000175000017500000002626013657331513016656 0ustar noahfxnoahfx#!/usr/bin/env python import sys import optparse from importlib import import_module from glue import __version__ from glue.logger import logger def parse(argv): """ Parse argument list, check validity :param argv: Arguments passed to program *Returns* A tuple of options, position arguments """ usage = """usage: %prog [options] [FILE FILE...] # start a new session %prog # start a new session and load a file %prog image.fits #start a new session with multiple files %prog image.fits catalog.csv #restore a saved session %prog saved_session.glu or %prog -g saved_session.glu #run a script %prog -x script.py #run the test suite %prog -t """ parser = optparse.OptionParser(usage=usage, version=str(__version__)) parser.add_option('-x', '--execute', action='store_true', dest='script', help="Execute FILE as a python script", default=False) parser.add_option('-g', action='store_true', dest='restore', help="Restore glue session from FILE", default=False) parser.add_option('-t', '--test', action='store_true', dest='test', help="Run test suite", default=False) parser.add_option('-c', '--config', type='string', dest='config', metavar='CONFIG', help='use CONFIG as configuration file') parser.add_option('-v', '--verbose', action='store_true', help="Increase the vebosity level", default=False) parser.add_option('--no-maximized', action='store_true', dest='nomax', help="Do not start Glue maximized", default=False) parser.add_option('--startup', dest='startup', type='string', help="Startup actions to carry out", default='') parser.add_option('--auto-merge', dest='auto_merge', action='store_true', help="Automatically merge any data passed on the command-line", default='') parser.add_option('--faulthandler', dest='faulthandler', action='store_true', help="Run glue with the built-in faulthandler to debug segmentation faults", default=False) err_msg = verify(parser, argv) if err_msg: sys.stderr.write('\n%s\n' % err_msg) parser.print_help() sys.exit(1) return parser.parse_args(argv) def verify(parser, argv): """ Check for input errors :param parser: OptionParser instance :param argv: Argument list :type argv: List of strings *Returns* An error message, or None """ opts, args = parser.parse_args(argv) err_msg = None if opts.script and opts.restore: err_msg = "Cannot specify -g with -x" elif opts.script and opts.config: err_msg = "Cannot specify -c with -x" elif opts.script and len(args) != 1: err_msg = "Must provide a script\n" elif opts.restore and len(args) != 1: err_msg = "Must provide a .glu file\n" return err_msg def load_data_files(datafiles): """Load data files and return a list of datasets""" from glue.core.data_factories import load_data datasets = [] for df in datafiles: datasets.append(load_data(df)) return datasets def run_tests(): from glue import test test() def start_glue(gluefile=None, config=None, datafiles=None, maximized=True, startup_actions=None, auto_merge=False): """Run a glue session and exit Parameters ---------- gluefile : str An optional ``.glu`` file to restore. config : str An optional configuration file to use. datafiles : str An optional list of data files to load. maximized : bool Maximize screen on startup. Otherwise, use default size. auto_merge : bool, optional Whether to automatically merge data passed in `datafiles` (default is `False`) """ import glue # Some Qt modules are picky in terms of being imported before the # application is set up, so we import them here. We do it here rather # than in get_qapp since in the past, including this code in get_qapp # caused severe issues (e.g. segmentation faults) in plugin packages # during testing. try: from qtpy import QtWebEngineWidgets # noqa except ImportError: # Not all PyQt installations have this module pass from glue.utils.qt.decorators import die_on_error from glue.utils.qt import get_qapp app = get_qapp() splash = get_splash() splash.show() # Start off by loading plugins. We need to do this before restoring # the session or loading the configuration since these may use existing # plugins. load_plugins(splash=splash, require_qt_plugins=True) from glue.app.qt import GlueApplication datafiles = datafiles or [] hub = None from qtpy.QtCore import QTimer timer = QTimer() timer.setInterval(1000) timer.setSingleShot(True) timer.timeout.connect(splash.close) timer.start() if gluefile is not None: with die_on_error("Error restoring Glue session"): app = GlueApplication.restore_session(gluefile, show=False) return app.start(maximized=maximized) if config is not None: glue.env = glue.config.load_configuration(search_path=[config]) data_collection = glue.core.DataCollection() hub = data_collection.hub splash.set_progress(100) session = glue.core.Session(data_collection=data_collection, hub=hub) ga = GlueApplication(session=session) if datafiles: with die_on_error("Error reading data file"): datasets = load_data_files(datafiles) ga.add_datasets(datasets, auto_merge=auto_merge) if startup_actions is not None: for name in startup_actions: ga.run_startup_action(name) return ga.start(maximized=maximized) def execute_script(script): """ Run a python script and exit. Provides a way for people with pre-installed binaries to use the glue library """ from glue.utils.qt.decorators import die_on_error with die_on_error("Error running script"): with open(script) as fin: exec(fin.read()) sys.exit(0) def get_splash(): """Instantiate a splash screen""" from glue.app.qt.splash_screen import QtSplashScreen splash = QtSplashScreen() return splash def main(argv=sys.argv): opt, args = parse(argv[1:]) if opt.verbose: logger.setLevel("INFO") if opt.faulthandler: import faulthandler faulthandler.enable() logger.info("Input arguments: %s", sys.argv) # Global keywords for Glue startup. kwargs = {'config': opt.config, 'maximized': not opt.nomax, 'auto_merge': opt.auto_merge} if opt.startup: kwargs['startup_actions'] = opt.startup.split(',') if opt.test: return run_tests() elif opt.restore: start_glue(gluefile=args[0], **kwargs) elif opt.script: execute_script(args[0]) else: has_file = len(args) == 1 has_files = len(args) > 1 has_py = has_file and args[0].endswith('.py') has_glu = has_file and args[0].endswith('.glu') if has_py: execute_script(args[0]) elif has_glu: start_glue(gluefile=args[0], **kwargs) elif has_file or has_files: start_glue(datafiles=args, **kwargs) else: start_glue(**kwargs) _loaded_plugins = set() _installed_plugins = set() REQUIRED_PLUGINS = ['glue.plugins.coordinate_helpers', 'glue.core.data_exporters', 'glue.io.formats.fits'] REQUIRED_PLUGINS_QT = ['glue.plugins.tools.pv_slicer.qt', 'glue.viewers.image.qt', 'glue.viewers.scatter.qt', 'glue.viewers.histogram.qt', 'glue.viewers.table.qt'] def load_plugins(splash=None, require_qt_plugins=False): # Search for plugins installed via entry_points. Basically, any package can # define plugins for glue, and needs to define an entry point using the # following format: # # entry_points = """ # [glue.plugins] # webcam_importer=glue_exp.importers.webcam:setup # vizier_importer=glue_exp.importers.vizier:setup # dataverse_importer=glue_exp.importers.dataverse:setup # """ # # where ``setup`` is a function that does whatever is needed to set up the # plugin, such as add items to various registries. import setuptools logger.info("Loading external plugins using " "setuptools=={0}".format(setuptools.__version__)) from glue._plugin_helpers import iter_plugin_entry_points, PluginConfig config = PluginConfig.load() n_plugins = len(list(iter_plugin_entry_points())) for iplugin, item in enumerate(iter_plugin_entry_points()): if item.module_name not in _installed_plugins: _installed_plugins.add(item.name) if item.module_name in _loaded_plugins: logger.info("Plugin {0} already loaded".format(item.name)) continue if not config.plugins[item.name]: continue # We don't use item.load() because that then checks requirements of all # the imported packages, which can lead to errors like this one that # don't really matter: # # Exception: (pytest 2.6.0 (/Users/tom/miniconda3/envs/py27/lib/python2.7/site-packages), # Requirement.parse('pytest>=2.8'), set(['astropy'])) # # Just to be clear, this kind of error does indicate that there is an # old version of a package in the environment, but this can confuse # users as importing astropy directly would work (as setuptools then # doesn't do a stringent test of dependency versions). Often this kind # of error can occur if there is a conda version of a package and and # older pip version. try: module = import_module(item.module_name) function = getattr(module, item.attrs[0]) function() except Exception as exc: # Here we check that some of the 'core' plugins load well and # raise an actual exception if not. if item.module_name in REQUIRED_PLUGINS: raise elif item.module_name in REQUIRED_PLUGINS_QT and require_qt_plugins: raise else: logger.info("Loading plugin {0} failed " "(Exception: {1})".format(item.name, exc)) else: logger.info("Loading plugin {0} succeeded".format(item.name)) _loaded_plugins.add(item.module_name) if splash is not None: splash.set_progress(100. * iplugin / float(n_plugins)) try: config.save() except Exception as e: logger.warn("Failed to load plugin configuration") # Reload the settings now that we have loaded plugins, since some plugins # may have added some settings. Note that this will not re-read settings # that were previously read. from glue._settings_helpers import load_settings load_settings() if __name__ == "__main__": sys.exit(main(sys.argv)) # pragma: no cover glueviz-1.0.1+dfsg.orig/glue/plugins/0000755000175000017500000000000013752535025017030 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tests/0000755000175000017500000000000013752535025020172 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tests/__init__.py0000644000175000017500000000000013455362716022276 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tests/test_d3po.py0000644000175000017500000000270613613544511022451 0ustar noahfxnoahfximport os from shutil import rmtree from tempfile import mkdtemp import numpy as np from glue.core import Data, DataCollection from glue.tests.helpers import requires_astropy from glue.tests.helpers import requires_qt from ..export_d3po import make_data_file, save_d3po @requires_astropy def test_make_data_file(): from astropy.table import Table # astropy.Table interface has changed across versions. Check # that we build a valid table d = Data(x=[1, 2, 3], y=[2, 3, 4], label='data') s = d.new_subset(label='test') s.subset_state = d.id['x'] > 1 dir = mkdtemp() try: make_data_file(d, (s,), dir) t = Table.read(os.path.join(dir, 'data.csv'), format='ascii') np.testing.assert_array_equal(t['x'], [1, 2, 3]) np.testing.assert_array_equal(t['y'], [2, 3, 4]) np.testing.assert_array_equal(t['selection_0'], [0, 1, 1]) finally: rmtree(dir, ignore_errors=True) @requires_qt def test_save_d3po(tmpdir): from glue.app.qt.application import GlueApplication from glue.viewers.scatter.qt import ScatterViewer from glue.viewers.histogram.qt import HistogramViewer output = tmpdir.join('output.html').strpath d = Data(x=[1, 2, 3], y=[2, 3, 4], label='data') dc = DataCollection([d]) app = GlueApplication(dc) app.new_data_viewer(ScatterViewer, data=d) app.new_data_viewer(HistogramViewer, data=d) save_d3po(app, output, launch=False) app.close() glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/0000755000175000017500000000000013752535025021664 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/tests/0000755000175000017500000000000013752535025023026 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/tests/test_data_factory.py0000644000175000017500000000672613605357235027114 0ustar noahfxnoahfximport os import pytest import numpy as np from numpy.testing import assert_array_equal from glue.tests.helpers import make_file from glue.core.data_factories.helpers import find_factory from glue.core import data_factories as df from glue.tests.helpers import requires_astrodendro DATA = os.path.join(os.path.dirname(__file__), 'data') @requires_astrodendro @pytest.mark.parametrize('filename', ['dendro.fits', 'dendro_old.fits', 'dendro.hdf5']) def test_is_dendro(filename): from ..data_factory import is_dendro assert is_dendro(os.path.join(DATA, filename)) @requires_astrodendro @pytest.mark.parametrize('filename', ['dendro.fits', 'dendro_old.fits', 'dendro.hdf5']) def test_find_factory(filename): from ..data_factory import load_dendro assert find_factory(os.path.join(DATA, filename)) is load_dendro @requires_astrodendro def test_identifier_heuristics(tmpdir): filename = tmpdir.join('test.fits').strpath from ..data_factory import is_dendro from astropy.io import fits hdulist = fits.HDUList() hdulist.append(fits.PrimaryHDU()) hdulist.append(fits.ImageHDU()) hdulist.append(fits.ImageHDU()) hdulist.writeto(filename) assert not is_dendro(filename) hdulist.append(fits.ImageHDU()) hdulist.writeto(filename, overwrite=True) assert not is_dendro(filename) hdulist[1].name = 'random' hdulist.writeto(filename, overwrite=True) assert not is_dendro(filename) hdulist[1].name = '' hdulist[0].data = np.array([1, 2, 3]) hdulist.writeto(filename, overwrite=True) assert not is_dendro(filename) hdulist[0].data = None hdulist[1].data = np.ones((3, 4)) hdulist[2].data = np.ones((2, 4)) hdulist[3].data = np.ones((3, 5)) hdulist.writeto(filename, overwrite=True) assert not is_dendro(filename) hdulist[2].data = np.ones((3, 4)) hdulist.writeto(filename, overwrite=True) assert not is_dendro(filename) hdulist[3].data = np.ones(3) hdulist.writeto(filename, overwrite=True) assert is_dendro(filename) @requires_astrodendro def test_dendrogram_load(): from ..data_factory import load_dendro data = b"""x\xda\xed\xda]K\xc2`\x18\xc6\xf1^\xbe\xc8}fA\xe4[X\x14\x1eX\x99<\x90S\xd8\x02O\x9f\xf2Q<\xd8&\xcf&\xe4\xb7\xcft\x82\xc9\xe6\x1be\x91\xff\xdf\xc9\xc5\xd8v\xc1vt\xeff\xaej\xb6\x9f\xeb"UI\xe1I^\xde\xc2\xa0\x17Z?\x928\x94\'\xe5\xb9\x12\xc5:\xe8j\xdb\x95T\xf7\xcak\xabNF\xdf\xcd\xa4O[\xab\xc7\xd2\xd5\xb1\x96x<4\xb2\x86S\xeb(W2\xfa\n\x93\xbe`\xe4\xbf\x1a+ao\xde<\xf0M\x10\r\xc2 J\xed\xabw\xbc\xba\xf3\x98\xf9\xbc[\x9b\x96\x01\x00\x00\xe0`|\x8e\x93\xaej9U\xc9\xa9f\xad1\x99\xa4%\xb7p:/\xca\xd7}#\xe6=\x9eM\xa5\xeb\xfaV\xcd\xcf\x95\xabo\x9e\x9f\x8b\xdb\xcf\xcf\xd3\xbebF_e\xfb\xf7\xd7~h\xbd8\xdeF\xf3\xfdP[\xed\x9b\xd8\xd8hE_cU\xdf\xd7\xe7\xed\xdbp4\x8c\x98\xef\x01\x00\x00\xf6\xeah\xe68\xc9\x93$O3\x8e\xe7\xd7\x01\x00\x00\x00\x07i\x9f\xfb\xe7r\x89\xfd3\xfbg\x00\x00\x80\x7f\xb1\x7fN\xdbA\x03\x00\x00\x00\xf8\xc5\xfd\xf3_\xff\xff\xb9t\xcd\xfe\x19\x00\x00\x00\x1b\xed\x9f\xcf\x96\xb2\x98\xe4m\x92\xe5$/\x93,d\xe4E\x92\xa5\x1d\xef?_:\xde\xf5\xfe;\xbe\x8c\x00\x00\x00\xf0\x13>\x00\x8e\xbe x""" with make_file(data, 'fits', decompress=True) as fname: dg, im = df.load_data(fname, factory=load_dendro) assert_array_equal(im['intensity'], [1, 2, 3, 2, 3, 1]) assert_array_equal(im['structure'], [0, 0, 1, 0, 2, 0]) assert_array_equal(dg['parent'], [-1, 0, 0]) assert_array_equal(dg['height'], [3, 3, 3]) assert_array_equal(dg['peak'], [3, 3, 3]) glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/tests/__init__.py0000644000175000017500000000000013455362716025132 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/tests/data/0000755000175000017500000000000014001427230023721 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/tests/data/__init__.py0000644000175000017500000000000013455362716026043 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/0000755000175000017500000000000013752535025022310 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/tests/0000755000175000017500000000000013752535025023452 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/tests/__init__.py0000644000175000017500000000000013455362716025556 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/tests/data/0000755000175000017500000000000013752535025024363 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/tests/data/__init__.py0000644000175000017500000000000013502206677026463 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/tests/data/dendro_v1.glu0000644000175000017500000001661413644362032026761 0ustar noahfxnoahfx{ "CallbackList": { "_type": "glue.external.echo.list.CallbackList", "values": [ "DendrogramLayerState", "DendrogramLayerState_0" ] }, "CategorySubsetState": { "_type": "glue.core.subset.CategorySubsetState", "att": "Pixel Axis 0 [x]", "vals": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEsKSwgfSAgICAgICAgICAgIAoCAAAAAAAAAA==" } }, "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDQsKSwgfSAgICAgICAgICAgIArNzMzMzMz0P5qZmZmZmQFAmpmZmZmZCUCamZmZmZkRQA==" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDQsKSwgfSAgICAgICAgICAgIAr//////////wAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAA==" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_1": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponentLink_2": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "height", "Pixel Axis 0 [x]", "World 0", "parent" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0" ], "data": [ "data" ], "groups": [ "Subset 1" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0", "CoordinateComponentLink_1", "CoordinateComponentLink_2" ], "subset_group_count": 1 }, "DendrogramLayerState": { "_type": "glue.plugins.dendro_viewer.state.DendrogramLayerState", "values": { "alpha": 0.78, "color": "st__#404040", "layer": "data", "linewidth": 1, "visible": true, "zorder": 2 } }, "DendrogramLayerState_0": { "_type": "glue.plugins.dendro_viewer.state.DendrogramLayerState", "values": { "alpha": 0.5, "color": "st__#e31a1c", "layer": "Subset 1_0", "linewidth": 1, "visible": true, "zorder": 3 } }, "DendrogramViewer": { "_protocol": 1, "_type": "glue.plugins.dendro_viewer.qt.data_viewer.DendrogramViewer", "layers": [ { "_type": "glue.plugins.dendro_viewer.layer_artist.DendrogramLayerArtist", "state": "DendrogramLayerState" }, { "_type": "glue.plugins.dendro_viewer.layer_artist.DendrogramLayerArtist", "state": "DendrogramLayerState_0" } ], "pos": [ 0, 0 ], "session": "Session", "size": [ 640, 480 ], "state": { "values": { "aspect": "st__auto", "height_att": "height", "layers": "CallbackList", "order_att": "height", "parent_att": "parent", "reference_data": "data", "select_substruct": true, "x_log": false, "x_max": 1.05, "x_min": -0.05, "y_log": false, "y_max": 4.555000000000001, "y_min": 1.145 } } }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "CategorySubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1_0" ] }, "Subset 1_0": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue.viewers.scatter", "glue.io.formats.fits", "glue_exp.importers.webcam", "glue.viewers.table", "glue.plugins.dendro_viewer", "glue_wwt", "glue.plugins.export_d3po", "glue.plugins.tools.pv_slicer", "glue.core.data_exporters", "glue_aladin", "glue_vispy_viewers.volume", "glue_exp.tools.floodfill_selection", "glue_exp.tools.contour_selection", "glue.plugins.tools.spectrum_tool", "glue_samp", "glue_exp.importers.vizier", "glue.plugins.coordinate_helpers", "glue.plugins.exporters.plotly", "glue.viewers.histogram", "glue.viewers.image", "glue_exp.tools.zoom_buttons", "glue_vispy_viewers.scatter" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "DendrogramViewer" ] ] }, "data": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "height", "Component" ], [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "parent", "Component_0" ] ], "coords": "Coordinates", "label": "data", "primary_owner": [ "height", "Pixel Axis 0 [x]", "World 0", "parent" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.78, "color": "#404040", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1_0" ], "uuid": "50d692d5-e30f-4218-aa60-393ced074298" }, "height": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "height" }, "parent": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "parent" } }glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/tests/data/dendro_v0.glu0000644000175000017500000001360413502206677026761 0ustar noahfxnoahfx{ "CategorySubsetState": { "_type": "glue.core.subset.CategorySubsetState", "att": "Pixel Axis 0 [x]", "vals": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEsKSwgfSAgICAgICAgICAgIAoCAAAAAAAAAA==" } }, "Component": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDQsKSwgfSAgICAgICAgICAgIArNzMzMzMz0P5qZmZmZmQFAmpmZmZmZCUCamZmZmZkRQA==" }, "units": "" }, "Component_0": { "_type": "glue.core.component.Component", "data": { "_type": "numpy.ndarray", "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDQsKSwgfSAgICAgICAgICAgIAr//////////wAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAA==" }, "units": "" }, "CoordinateComponent": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": false }, "CoordinateComponentLink": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "World 0" ], "index": 0, "pix2world": false, "to": [ "Pixel Axis 0 [x]" ] }, "CoordinateComponentLink_0": { "_type": "glue.core.component_link.CoordinateComponentLink", "coords": "Coordinates", "frm": [ "Pixel Axis 0 [x]" ], "index": 0, "pix2world": true, "to": [ "World 0" ] }, "CoordinateComponent_0": { "_type": "glue.core.component.CoordinateComponent", "axis": 0, "world": true }, "Coordinates": { "_type": "glue.core.coordinates.Coordinates" }, "DataCollection": { "_protocol": 3, "_type": "glue.core.data_collection.DataCollection", "cids": [ "height", "Pixel Axis 0 [x]", "World 0", "parent" ], "components": [ "Component", "CoordinateComponent", "CoordinateComponent_0", "Component_0" ], "data": [ "data" ], "groups": [ "Subset 1_0" ], "links": [ "CoordinateComponentLink", "CoordinateComponentLink_0" ], "subset_group_count": 1 }, "DendroWidget": { "_type": "glue.plugins.dendro_viewer.qt.viewer_widget.DendroWidget", "layers": [ { "_type": "glue.plugins.dendro_viewer.layer_artist.DendroLayerArtist", "layer": "data", "visible": true, "zorder": 1 }, { "_type": "glue.plugins.dendro_viewer.layer_artist.DendroLayerArtist", "layer": "Subset 1", "visible": true, "zorder": 2 } ], "pos": [ 0, 0 ], "properties": { "height": "height", "order": "height", "parent": "parent", "ylog": false }, "session": "Session", "size": [ 640, 480 ] }, "Pixel Axis 0 [x]": { "_type": "glue.core.component_id.PixelComponentID", "axis": 0, "hidden": true, "label": "Pixel Axis 0 [x]" }, "Session": { "_type": "glue.core.session.Session" }, "Subset 1": { "_type": "glue.core.subset_group.GroupedSubset", "group": "Subset 1_0", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 } }, "Subset 1_0": { "_type": "glue.core.subset_group.SubsetGroup", "label": "Subset 1", "state": "CategorySubsetState", "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.5, "color": "#e31a1c", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 7 }, "subsets": [ "Subset 1" ] }, "World 0": { "_type": "glue.core.component_id.ComponentID", "hidden": true, "label": "World 0" }, "__main__": { "_type": "glue.app.qt.application.GlueApplication", "data": "DataCollection", "plugins": [ "glue_exp.tools.floodfill_selection", "glue_vispy_viewers.volume", "glue_exp.tools.contour_selection", "glue.plugins.coordinate_helpers", "glue_samp", "glue.core.data_exporters", "glue.plugins.export_d3po", "glue_vispy_viewers.scatter", "glue_aladin", "glue.viewers.table", "glue_exp.importers.webcam", "glue.plugins.tools.spectrum_tool", "glue.plugins.tools.pv_slicer", "glue.plugins.exporters.plotly", "glue.viewers.image", "glue.io.formats.fits", "glue.viewers.scatter", "glue_exp.tools.zoom_buttons", "glue_exp.importers.vizier", "glue_wwt", "glue.viewers.histogram", "glue.plugins.dendro_viewer" ], "session": "Session", "tab_names": [ "Tab 1" ], "viewers": [ [ "DendroWidget" ] ] }, "data": { "_key_joins": [], "_protocol": 5, "_type": "glue.core.data.Data", "components": [ [ "height", "Component" ], [ "Pixel Axis 0 [x]", "CoordinateComponent" ], [ "World 0", "CoordinateComponent_0" ], [ "parent", "Component_0" ] ], "coords": "Coordinates", "label": "data", "primary_owner": [ "height", "Pixel Axis 0 [x]", "World 0", "parent" ], "style": { "_type": "glue.core.visual.VisualAttributes", "alpha": 0.78, "color": "#404040", "linestyle": "solid", "linewidth": 1, "marker": "o", "markersize": 3 }, "subsets": [ "Subset 1" ], "uuid": "50d692d5-e30f-4218-aa60-393ced074298" }, "height": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "height" }, "parent": { "_type": "glue.core.component_id.ComponentID", "hidden": false, "label": "parent" } }glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/tests/test_data_viewer.py0000644000175000017500000001161213605357235027360 0ustar noahfxnoahfx# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 import os import pytest import numpy as np from numpy.testing import assert_equal from glue.core import Data from glue.core.roi import PointROI from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer from glue.app.qt import GlueApplication from glue.core.state import GlueUnSerializer from ..data_viewer import DendrogramViewer DATA = os.path.join(os.path.dirname(__file__), 'data') class TestDendrogramCommon(BaseTestMatplotlibDataViewer): viewer_cls = DendrogramViewer def init_data(self): return Data(label='d1', parent=[-1, 0, 1, 1], height=[1.3, 2.2, 3.2, 4.4]) # TODO: Find a way to simplify having to overload this just because # we need to use a different ROI def test_apply_roi_undo(self): pass self.data_collection.append(self.data) self.viewer.add_data(self.data) roi = PointROI(0.2, 2.8) self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 mask1 = self.data.subsets[0].subset_state.to_mask(self.data) roi = PointROI(0.7, 2.8) self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 mask2 = self.data.subsets[0].subset_state.to_mask(self.data) assert np.any(mask1 != mask2) self.application.undo() assert len(self.data.subsets) == 1 mask3 = self.data.subsets[0].subset_state.to_mask(self.data) assert np.all(mask3 == mask1) self.application.redo() assert len(self.data.subsets) == 1 mask4 = self.data.subsets[0].subset_state.to_mask(self.data) assert np.all(mask4 == mask2) @pytest.mark.skip def test_add_invalid_data(): pass class TestDendrogramViewer(): def setup_method(self, method): self.data = Data(label='d1', parent=[-1, 0, 1, 1], height=[1.3, 2.2, 3.2, 4.4]) self.app = GlueApplication() self.session = self.app.session self.hub = self.session.hub self.data_collection = self.session.data_collection self.data_collection.append(self.data) self.viewer = self.app.new_data_viewer(DendrogramViewer) self.data_collection.register_to_hub(self.hub) self.viewer.register_to_hub(self.hub) def teardown_method(self, method): self.viewer.close() self.viewer = None self.app.close() self.app = None def test_point_select(self): self.viewer.add_data(self.data) # By default selecting a structure selects all substructures roi = PointROI(0.5, 1.5) self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 mask1 = self.data.subsets[0].subset_state.to_mask(self.data) assert_equal(mask1, [0, 1, 1, 1]) # But this option can be turned off self.viewer.state.select_substruct = False self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 mask1 = self.data.subsets[0].subset_state.to_mask(self.data) assert_equal(mask1, [0, 1, 0, 0]) self.viewer.state.select_substruct = True # Try selecting a leaf roi = PointROI(0.2, 2.8) self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 mask1 = self.data.subsets[0].subset_state.to_mask(self.data) assert_equal(mask1, [0, 0, 1, 0]) # Try selecting another leaf roi = PointROI(0.7, 2.8) self.viewer.apply_roi(roi) assert len(self.data.subsets) == 1 mask1 = self.data.subsets[0].subset_state.to_mask(self.data) assert_equal(mask1, [0, 0, 0, 1]) def test_attribute_change_triggers_relayout(self): self.data.add_component([4, 5, 6, 7], 'flux') self.viewer.add_data(self.data) l = self.viewer.state._layout self.viewer.state.height_att = self.data.id['flux'] assert self.viewer.state._layout is not l class TestSessions(object): @pytest.mark.parametrize('protocol', [0, 1]) def test_session_back_compat(self, protocol): filename = os.path.join(DATA, 'dendro_v{0}.glu'.format(protocol)) with open(filename, 'r') as f: session = f.read() state = GlueUnSerializer.loads(session) ga = state.object('__main__') dc = ga.session.data_collection assert len(dc) == 1 assert dc[0].label == 'data' viewer1 = ga.viewers[0][0] assert len(viewer1.state.layers) == 2 assert viewer1.state.parent_att is dc[0].id['parent'] assert viewer1.state.height_att is dc[0].id['height'] assert viewer1.state.order_att is dc[0].id['height'] layer_state = viewer1.state.layers[0] assert layer_state.visible assert layer_state.layer is dc[0] layer_state = viewer1.state.layers[1] assert layer_state.visible assert layer_state.layer is dc[0].subsets[0] ga.close() glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/data_viewer.py0000644000175000017500000001130313605357235025154 0ustar noahfxnoahfximport numpy as np from glue.core.roi import PointROI from glue.core.subset import CategorySubsetState from glue.core.exceptions import IncompatibleDataException from glue.utils import defer_draw from glue.utils.qt import messagebox_on_error from glue.plugins.dendro_viewer.dendro_helpers import _substructures from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer from glue.plugins.dendro_viewer.layer_artist import DendrogramLayerArtist from glue.plugins.dendro_viewer.qt.options_widget import DendrogramOptionsWidget from glue.plugins.dendro_viewer.state import DendrogramViewerState from glue.plugins.dendro_viewer.qt.layer_style_editor import DendrogramLayerStyleEditor from glue.plugins.dendro_viewer.compat import update_dendrogram_viewer_state __all__ = ['DendrogramViewer'] class DendrogramViewer(MatplotlibDataViewer): LABEL = 'Dendrogram' _layer_style_widget_cls = DendrogramLayerStyleEditor _state_cls = DendrogramViewerState _options_cls = DendrogramOptionsWidget _data_artist_cls = DendrogramLayerArtist _subset_artist_cls = DendrogramLayerArtist tools = ['select:pick'] def __init__(self, *args, **kwargs): super(DendrogramViewer, self).__init__(*args, **kwargs) self.axes.set_xticks([]) self.axes.spines['top'].set_visible(False) self.axes.spines['bottom'].set_visible(False) self.state.add_callback('_layout', self._update_limits) self._update_limits() def _update_limits(self, layout=None): if self.state._layout is None: return x, y = self.state._layout.xy x, y = x[::3], y[::3] xlim = np.array([x.min(), x.max()]) xpad = .05 * xlim.ptp() xlim[0] -= xpad xlim[1] += xpad ylim = np.array([y.min(), y.max()]) if self.state.y_log: ylim = np.maximum(ylim, 1e-5) pad = 1.05 * ylim[1] / ylim[0] ylim[0] /= pad ylim[1] *= pad else: pad = .05 * ylim.ptp() ylim[0] -= pad ylim[1] += pad self.axes.set_xlim(*xlim) self.axes.set_ylim(*ylim) def initialize_toolbar(self): super(DendrogramViewer, self).initialize_toolbar() def on_move(mode): if mode._drag: self.apply_roi(mode.roi()) self.toolbar.tools['select:pick']._move_callback = on_move def close(self, *args, **kwargs): self.toolbar.tools['select:pick']._move_callback = None super(DendrogramViewer, self).close(*args, **kwargs) @messagebox_on_error('Failed to add data') def add_data(self, data): if data.ndim != 1: raise IncompatibleDataException("Only 1-D data can be added to " "the dendrogram viewer (tried to add a {}-D " "dataset)".format(data.ndim)) return super(DendrogramViewer, self).add_data(data) # TODO: move some of the ROI stuff to state class? @defer_draw def apply_roi(self, roi, override_mode=None): # Force redraw to get rid of ROI. We do this because applying the # subset state below might end up not having an effect on the viewer, # for example there may not be any layers, or the active subset may not # be one of the layers. So we just explicitly redraw here to make sure # a redraw will happen after this method is called. self.redraw() # TODO Does subset get applied to all data or just visible data? if self.state._layout is None: return if not roi.defined(): return if len(self.layers) == 0: return if isinstance(roi, PointROI): x, y = roi.x, roi.y xs, ys = self.state._layout.xy parent_ys = ys[1::3] xs, ys = xs[::3], ys[::3] delt = np.abs(x - xs) delt[y > ys] = np.nan delt[y < parent_ys] = np.nan if np.isfinite(delt).any(): select = np.nanargmin(delt) if self.state.select_substruct: parent = self.state.reference_data[self.state.parent_att] select = _substructures(parent, select) select = np.asarray(select, dtype=np.int) else: select = np.array([], dtype=np.int) subset_state = CategorySubsetState(self.state.reference_data.pixel_component_ids[0], select) self.apply_subset_state(subset_state) else: raise TypeError("Only PointROI selections are supported") @staticmethod def update_viewer_state(rec, context): return update_dendrogram_viewer_state(rec, context) glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/options_widget.ui0000644000175000017500000000663413502206677025717 0ustar noahfxnoahfx DendroWidgetPanel 0 0 251 140 Form QComboBox::AdjustToMinimumContentsLength 75 true parent Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 y log true Qt::Horizontal 40 1 75 true sort by Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true height Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QComboBox::AdjustToMinimumContentsLength QComboBox::AdjustToMinimumContentsLength auto select substructures glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/__init__.py0000644000175000017500000000021313502206677024416 0ustar noahfxnoahfxfrom .data_viewer import DendrogramViewer # noqa def setup(): from glue.config import qt_client qt_client.add(DendrogramViewer) glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/options_widget.py0000644000175000017500000000121413657331513025716 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui __all__ = ['DendrogramOptionsWidget'] class DendrogramOptionsWidget(QtWidgets.QWidget): def __init__(self, viewer_state, session, parent=None): super(DendrogramOptionsWidget, self).__init__(parent=parent) self.ui = load_ui('options_widget.ui', self, directory=os.path.dirname(__file__)) self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) self.viewer_state = viewer_state def reset_limits(self): self.viewer_state.reset_limits() glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/layer_style_editor.ui0000644000175000017500000000707613502206677026564 0ustar noahfxnoahfx Form 0 0 127 107 Form 5 5 5 5 10 5 100 Qt::Horizontal 75 true opacity Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 75 true color Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 75 true width Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 Qt::Horizontal 40 1 QColorBox QLabel
glue.utils.qt.colors
glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/qt/layer_style_editor.py0000644000175000017500000000107413657331513026566 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui class DendrogramLayerStyleEditor(QtWidgets.QWidget): def __init__(self, layer, parent=None): super(DendrogramLayerStyleEditor, self).__init__(parent=parent) self.ui = load_ui('layer_style_editor.ui', self, directory=os.path.dirname(__file__)) connect_kwargs = {'alpha': dict(value_range=(0, 1))} self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/state.py0000644000175000017500000000715013657331513023361 0ustar noahfxnoahfx# -*- coding: utf-8 -*- from glue.core import BaseData from glue.viewers.matplotlib.state import (MatplotlibDataViewerState, MatplotlibLayerState, DeferredDrawCallbackProperty as DDCProperty, DeferredDrawSelectionCallbackProperty as DDSCProperty) from glue.core.data_combo_helper import ComponentIDComboHelper from echo import keep_in_sync from .dendro_helpers import dendrogram_layout __all__ = ['DendrogramViewerState', 'DendrogramLayerState'] class Layout(object): def __init__(self, x, y): self.x = x self.y = y @property def xy(self): return self.x, self.y class DendrogramViewerState(MatplotlibDataViewerState): """ A state class that includes all the attributes for a dendrogram viewer. """ height_att = DDSCProperty() parent_att = DDSCProperty() order_att = DDSCProperty() y_log = DDCProperty(False) select_substruct = DDCProperty(True) reference_data = DDCProperty() _layout = DDCProperty() def __init__(self, **kwargs): super(DendrogramViewerState, self).__init__() self.add_callback('layers', self._layers_changed) self.height_att_helper = ComponentIDComboHelper(self, 'height_att') self.parent_att_helper = ComponentIDComboHelper(self, 'parent_att') self.order_att_helper = ComponentIDComboHelper(self, 'order_att') self.add_callback('height_att', self._update_layout) self.add_callback('parent_att', self._update_layout) self.add_callback('order_att', self._update_layout) self.add_callback('reference_data', self._on_reference_data_change) self.update_from_dict(kwargs) def _on_reference_data_change(self, data): if self.reference_data is None: return self.height_att = self.reference_data.find_component_id('height') self.parent_att = self.reference_data.find_component_id('parent') self.order_att = self.height_att def _update_layout(self, att): if self.height_att is None or self.parent_att is None or self.order_att is None or self.reference_data is None: self._layout = None else: height = self.reference_data[self.height_att].ravel() parent = self.reference_data[self.parent_att].astype(int).ravel() order = self.reference_data[self.order_att].ravel() x, y = dendrogram_layout(parent, height, order) self._layout = Layout(x, y) def _layers_changed(self, *args): layers_data = self.layers_data layers_data_cache = getattr(self, '_layers_data_cache', []) if layers_data == layers_data_cache: return self.height_att_helper.set_multiple_data(layers_data) self.parent_att_helper.set_multiple_data(layers_data) self.order_att_helper.set_multiple_data(layers_data) for layer in layers_data: if isinstance(layer, BaseData): self.reference_data = layer break self._layers_data_cache = layers_data class DendrogramLayerState(MatplotlibLayerState): """ A state class that includes all the attributes for layers in a dendrogram plot. """ linewidth = DDCProperty(1, docstring="The line width") def __init__(self, viewer_state=None, **kwargs): super(DendrogramLayerState, self).__init__(viewer_state=viewer_state, **kwargs) self.linewidth = self.layer.style.linewidth self._sync_linewidth = keep_in_sync(self, 'linewidth', self.layer.style, 'linewidth') glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/data_factory.py0000644000175000017500000000626513605357235024711 0ustar noahfxnoahfx""" Load files created by the astrodendro package. astrodendro must be installed in order to use this loader """ import numpy as np from astrodendro import Dendrogram from glue.core.data_factories.hdf5 import is_hdf5 from glue.core.data_factories.fits import is_fits from glue.core.data_factories.helpers import data_label from glue.core.data import Data from glue.config import data_factory __all__ = ['load_dendro', 'is_dendro'] def is_dendro(file, **kwargs): if is_hdf5(file): import h5py f = h5py.File(file, 'r') return 'data' in f and 'index_map' in f and 'newick' in f elif is_fits(file): from astropy.io import fits with fits.open(file, ignore_missing_end=True) as hdulist: # For recent versions of astrodendro the HDUs have a recongnizable # set of names. if 'DATA' in hdulist and 'INDEX_MAP' in hdulist and 'NEWICK' in hdulist: return True # For older versions of astrodendro, the HDUs did not have names # Here we use heuristics to figure out if this is likely to be a # dendrogram. Specifically, there should be three HDU extensions. # The primary HDU should be empty, HDU 1 and HDU 2 should have # matching shapes, and HDU 3 should have a 1D array. Also, if the # HDUs do have names then this is not a dendrogram since the old # files did not have names # This branch can be removed once we think most dendrogram files # will have HDU names. if len(hdulist) != 4: return False if hdulist[1].name != '' or hdulist[2].name != '' or hdulist[3].name != '': return False if hdulist[0].data is not None: return False if hdulist[1].data is None or hdulist[2].data is None or hdulist[3].data is None: return False if hdulist[1].data.shape != hdulist[2].data.shape: return False if hdulist[3].data.ndim != 1: return False # We're probably ok, so return True return True else: return False @data_factory(label='Dendrogram', identifier=is_dendro, priority=1000) def load_dendro(filename): """ Load a dendrogram saved by the astrodendro package :param file: Path to a dendrogram file :returns: A list of 2 glue Data objects: the original dataset, and dendrogram. """ label = data_label(filename) dg = Dendrogram.load_from(filename) structs = np.arange(len(dg)) parent = np.array([dg[i].parent.idx if dg[i].parent is not None else -1 for i in structs]) height = np.array([dg[i].height for i in structs]) pk = np.array([dg[i].get_peak(True)[1] for i in structs]) dendro = Data(parent=parent, height=height, peak=pk, label="{} [dendrogram]".format(label)) im = Data(intensity=dg.data, structure=dg.index_map, label="{} [data]".format(label)) im.join_on_key(dendro, 'structure', dendro.pixel_component_ids[0]) return [dendro, im] glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/__init__.py0000644000175000017500000000007713502206677024002 0ustar noahfxnoahfxdef setup(): from .data_factory import load_dendro # noqa glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/compat.py0000644000175000017500000000332013605357235023521 0ustar noahfxnoahfximport uuid from .state import DendrogramLayerState STATE_CLASS = {} STATE_CLASS['DendroLayerArtist'] = DendrogramLayerState def update_dendrogram_viewer_state(rec, context): """ Given viewer session information, make sure the session information is compatible with the current version of the viewers, and if not, update the session information in-place. """ if '_protocol' not in rec: # Note that files saved with protocol < 1 have bin settings saved per # layer but they were always restricted to be the same, so we can just # use the settings from the first layer rec['state'] = {} rec['state']['values'] = {} # TODO: could generalize this into a mapping properties = rec.pop('properties') viewer_state = rec['state']['values'] viewer_state['parent_att'] = properties['parent'] viewer_state['height_att'] = properties['height'] viewer_state['order_att'] = properties['order'] viewer_state['y_log'] = properties['ylog'] layer_states = [] for layer in rec['layers']: state_id = str(uuid.uuid4()) state_cls = STATE_CLASS[layer['_type'].split('.')[-1]] state = state_cls(layer=context.object(layer.pop('layer'))) for prop in ('visible', 'zorder'): value = layer.pop(prop) value = context.object(value) setattr(state, prop, value) context.register_object(state_id, state) layer['state'] = state_id layer_states.append(state) list_id = str(uuid.uuid4()) context.register_object(list_id, layer_states) rec['state']['values']['layers'] = list_id glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/layer_artist.py0000644000175000017500000000665513605357235024756 0ustar noahfxnoahfximport numpy as np from glue.utils import defer_draw from glue.core.exceptions import IncompatibleAttribute from glue.core.subset import Subset from glue.viewers.matplotlib.layer_artist import MatplotlibLayerArtist from glue.plugins.dendro_viewer.state import DendrogramLayerState class DendrogramLayerArtist(MatplotlibLayerArtist): # X vertices of structure i are in layout[0][3*i: 3*i+3] # layout = ChangedTrigger() _layer_state_cls = DendrogramLayerState def __init__(self, axes, viewer_state, layer_state=None, layer=None): super(DendrogramLayerArtist, self).__init__(axes, viewer_state, layer_state=layer_state, layer=layer) # Watch for changes in the viewer state which would require the # layers to be redrawn self._viewer_state.add_global_callback(self._update) self.state.add_global_callback(self._update) # TODO: following is temporary self.state.data_collection = self._viewer_state.data_collection self.data_collection = self._viewer_state.data_collection @defer_draw def _update_dendrogram(self): self.remove() if self.state.viewer_state._layout is None: return # layout[0] is [x0, x0, x[parent0], nan, ...] # layout[1] is [y0, y[parent0], y[parent0], nan, ...] ids = 3 * np.arange(self.layer.data.size) try: if isinstance(self.layer, Subset): ids = ids[self.layer.to_mask()] except IncompatibleAttribute as exc: self.disable_invalid_attributes(*exc.args) return False x, y = self.state.viewer_state._layout.xy blank = np.zeros(ids.size) * np.nan x = np.column_stack([x[ids], x[ids + 1], x[ids + 2], blank]).ravel() y = np.column_stack([y[ids], y[ids + 1], y[ids + 2], blank]).ravel() self.mpl_artists = self.axes.plot(x, y, '-') @defer_draw def _update_visual_attributes(self): if not self.enabled: return for mpl_artist in self.mpl_artists: mpl_artist.set_visible(self.state.visible) mpl_artist.set_zorder(self.state.zorder) mpl_artist.set_color(self.state.color) mpl_artist.set_alpha(self.state.alpha) mpl_artist.set_linewidth(self.state.linewidth) self.redraw() @defer_draw def _update(self, force=False, **kwargs): if (self._viewer_state.height_att is None or self._viewer_state.parent_att is None or self._viewer_state.order_att is None or self.state.layer is None): return changed = set() if force else self.pop_changed_properties() if force or any(prop in changed for prop in ('layer', 'height_att', 'parent_att', 'order_att')): self._update_dendrogram() force = True # make sure scaling and visual attributes are updated if force or any(prop in changed for prop in ('linewidth', 'alpha', 'color', 'zorder', 'visible')): self._update_visual_attributes() @defer_draw def update(self): # Recompute the histogram self._update(force=True) # Reset the axes stack so that pressing the home button doesn't go back # to a previous irrelevant view. self.axes.figure.canvas.toolbar.update() self.redraw() glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/dendro_helpers.py0000644000175000017500000000476313605357235025247 0ustar noahfxnoahfximport numpy as np def dendrogram_layout(parent, height, key): children = _dendro_children(parent) pos = np.zeros(key.size) - 1 cur_pos = 0 for struct in _iter_sorted(children, parent, key): if children[struct].size == 0: # leaf pos[struct] = cur_pos cur_pos += 1 else: # branch assert pos[children[struct]].mean() >= 0 pos[struct] = pos[children[struct]].mean() layout = np.zeros((2, 3 * height.size)) layout[0, ::3] = pos layout[0, 1::3] = pos layout[0, 2::3] = np.where(parent >= 0, pos[parent], np.nan) layout[1, ::3] = height layout[1, 1::3] = np.where(parent >= 0, height[parent], height.min()) layout[1, 2::3] = layout[1, 1::3] return layout def _substructures(parent, idx): """ Return an array of all substructure indices of a given index. The input is included in the output. Parameters ---------- idx : int The structure to extract. Returns ------- array """ children = _dendro_children(parent) result = [] if np.isscalar(idx): todo = [idx] else: todo = idx.tolist() while todo: result.append(todo.pop()) todo.extend(children[result[-1]]) return np.array(result, dtype=np.int) def _dendro_children(parent): children = [[] for _ in range(parent.size)] for i, p in enumerate(parent): if p < 0: continue children[p].append(i) return list(map(np.asarray, children)) def _iter_sorted(children, parent, key): # must yield both children before parent yielded = set() trunks = np.array([i for i, p in enumerate(parent) if p < 0], dtype=np.int) for idx in np.argsort(key[trunks]): idx = trunks[idx] for item in _postfix_iter(idx, children, parent, yielded, key): yield item def _postfix_iter(node, children, parent, yielded, key): """ Iterate over a node and its children, in the following fashion: parents are yielded after children children are yielded in order of ascending key value """ todo = [node] expanded = set() while todo: node = todo[-1] if node in yielded: todo.pop() continue if children[node].size == 0 or node in expanded: yield todo.pop() yielded.add(node) continue c = children[node] ind = np.argsort(key[c])[::-1] todo.extend(c[ind]) expanded.add(node) glueviz-1.0.1+dfsg.orig/glue/plugins/dendro_viewer/layer_style_editor.py0000644000175000017500000000107413657331513026142 0ustar noahfxnoahfximport os from qtpy import QtWidgets from echo.qt import autoconnect_callbacks_to_qt from glue.utils.qt import load_ui class DendorgramLayerStyleEditor(QtWidgets.QWidget): def __init__(self, layer, parent=None): super(DendorgramLayerStyleEditor, self).__init__(parent=parent) self.ui = load_ui('layer_style_editor.ui', self, directory=os.path.dirname(__file__)) connect_kwargs = {'alpha': dict(value_range=(0, 1))} self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) glueviz-1.0.1+dfsg.orig/glue/plugins/exporters/0000755000175000017500000000000013752535025021063 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/exporters/__init__.py0000644000175000017500000000000013455362716023167 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/__init__.py0000644000175000017500000000046413605357235021147 0ustar noahfxnoahfx def load_plugin(plugin): """ Load plugin referred to by name 'plugin' """ import importlib module = importlib.import_module(plugin) if hasattr(module, 'setup'): module.setup() else: raise AttributeError("Plugin {0} should define 'setup' function".format(plugin)) glueviz-1.0.1+dfsg.orig/glue/plugins/tools/0000755000175000017500000000000013752535025020170 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tools/tests/0000755000175000017500000000000013752535025021332 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tools/tests/__init__.py0000644000175000017500000000000013455362716023436 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tools/pv_slicer/0000755000175000017500000000000013752535025022156 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tools/pv_slicer/qt/0000755000175000017500000000000013752535025022602 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tools/pv_slicer/qt/tests/0000755000175000017500000000000013752535025023744 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tools/pv_slicer/qt/tests/test_pv_slicer.py0000644000175000017500000001067013630201003027324 0ustar noahfxnoahfximport numpy as np from unittest.mock import MagicMock from numpy.testing import assert_allclose from glue.core import Data from glue.core.coordinates import IdentityCoordinates from glue.viewers.image.qt import StandaloneImageViewer, ImageViewer from glue.tests.helpers import requires_astropy, requires_scipy from glue.app.qt import GlueApplication from glue.utils.qt import process_events from ..pv_slicer import _slice_from_path, _slice_label, _slice_index, PVSliceWidget @requires_astropy @requires_scipy class TestSliceExtraction(object): def setup_method(self, method): self.x = np.random.random((2, 3, 4)) self.d = Data(x=self.x) def test_constant_y(self): slc = (0, 'y', 'x') x = [-0.5, 3.5] y = [0, 0] s = _slice_from_path(x, y, self.d, 'x', slc)[0] assert_allclose(s, self.x[:, 0, :]) def test_constant_x(self): slc = (0, 'y', 'x') y = [-0.5, 2.5] x = [0, 0] s = _slice_from_path(x, y, self.d, 'x', slc)[0] assert_allclose(s, self.x[:, :, 0]) def test_transpose(self): slc = (0, 'x', 'y') y = [-0.5, 3.5] x = [0, 0] s = _slice_from_path(x, y, self.d, 'x', slc)[0] assert_allclose(s, self.x[:, 0, :]) def test_slice_label(): d = Data(x=np.zeros((2, 3, 4)), coords=IdentityCoordinates(n_dim=3)) assert _slice_label(d, (0, 'y', 'x')) == 'World 0' assert _slice_label(d, ('y', 0, 'x')) == 'World 1' assert _slice_label(d, ('y', 'x', 0)) == 'World 2' def test_slice_label_nocoords(): d = Data(x=np.zeros((2, 3, 4))) assert _slice_label(d, (0, 'y', 'x')) == 'Pixel Axis 0 [z]' assert _slice_label(d, ('y', 0, 'x')) == 'Pixel Axis 1 [y]' assert _slice_label(d, ('y', 'x', 0)) == 'Pixel Axis 2 [x]' def test_slice_index(): d = Data(x=np.zeros((2, 3, 4))) assert _slice_index(d, (0, 'y', 'x')) == 0 assert _slice_index(d, ('y', 0, 'x')) == 1 class TestStandaloneImageViewer(object): def setup_method(self, method): im = np.random.random((3, 3)) self.w = StandaloneImageViewer(im) def teardown_method(self, method): self.w.close() def test_set_cmap(self): cm_mode = self.w.toolbar.tools['image:colormap'] act = cm_mode.menu_actions()[1] act.trigger() assert self.w._composite.layers['image']['color'] is act.cmap def test_double_set_image(self): assert len(self.w._axes.images) == 1 self.w.set_image(np.zeros((3, 3))) assert len(self.w._axes.images) == 1 class MockImageViewer(object): def __init__(self, slice, data): self.slice = slice self.data = data self.wcs = None self.state = MagicMock() class TestPVSliceWidget(object): def setup_method(self, method): self.d = Data(x=np.zeros((2, 3, 4))) self.slc = (0, 'y', 'x') self.image = MockImageViewer(self.slc, self.d) self.w = PVSliceWidget(image=np.zeros((3, 4)), wcs=None, image_viewer=self.image) def teardown_method(self, method): self.w.close() def test_basic(self): pass class TestPVSliceTool(object): def setup_method(self, method): self.cube = Data(label='cube', x=np.arange(1000).reshape((5, 10, 20))) self.application = GlueApplication() self.application.data_collection.append(self.cube) self.viewer = self.application.new_data_viewer(ImageViewer) self.viewer.add_data(self.cube) def teardown_method(self, method): self.viewer.close() self.viewer = None self.application.close() self.application = None @requires_astropy @requires_scipy def test_basic(self): self.viewer.toolbar.active_tool = 'slice' self.viewer.axes.figure.canvas.draw() process_events() x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0] self.viewer.axes.figure.canvas.button_press_event(x, y, 1) x, y = self.viewer.axes.transData.transform([[7.2, 6.6]])[0] self.viewer.axes.figure.canvas.button_press_event(x, y, 1) process_events() assert len(self.application.tab().subWindowList()) == 1 self.viewer.axes.figure.canvas.key_press_event('enter') process_events() assert len(self.application.tab().subWindowList()) == 2 pv_widget = self.application.tab().subWindowList()[1].widget() assert pv_widget._x.shape == (6,) assert pv_widget._y.shape == (6,) glueviz-1.0.1+dfsg.orig/glue/plugins/tools/pv_slicer/qt/tests/__init__.py0000644000175000017500000000000013455362716026050 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tools/pv_slicer/qt/__init__.py0000644000175000017500000000031513657331513024712 0ustar noahfxnoahfxfrom .pv_slicer import * # noqa def setup(): from glue.viewers.image.qt import ImageViewer from glue.plugins.tools.pv_slicer.qt import PVSlicerMode # noqa ImageViewer.tools.append('slice') glueviz-1.0.1+dfsg.orig/glue/plugins/tools/pv_slicer/qt/pv_slicer.py0000644000175000017500000002173013630201003025122 0ustar noahfxnoahfximport numpy as np from glue.viewers.matplotlib.toolbar_mode import PathMode from glue.viewers.image.qt import StandaloneImageViewer from glue.config import viewer_tool from glue.utils import defer_draw from glue.core.coordinate_helpers import axis_label @viewer_tool class PVSlicerMode(PathMode): icon = 'glue_slice' tool_id = 'slice' action_text = 'Slice Extraction' tool_tip = ('Extract a slice from an arbitrary path\n' ' ENTER accepts the path\n' ' ESCAPE clears the path') status_tip = 'Draw a path then press ENTER to extract slice, or press ESC to cancel' shortcut = 'P' def __init__(self, viewer, **kwargs): super(PVSlicerMode, self).__init__(viewer, **kwargs) self._roi_callback = self._extract_callback self._slice_widget = None self.viewer.state.add_callback('reference_data', self._on_reference_data_change) def _on_reference_data_change(self, reference_data): if reference_data is not None: self.enabled = reference_data.ndim == 3 def _clear_path(self): self.viewer.hide_crosshairs() self.clear() def _extract_callback(self, mode): """ Extract a PV-like slice, given a path traced on the widget """ vx, vy = mode.roi().to_polygon() self._build_from_vertices(vx, vy) def _build_from_vertices(self, vx, vy): pv_slice, x, y, wcs = _slice_from_path(vx, vy, self.viewer.state.reference_data, self.viewer.state.layers[0].attribute, self.viewer.state.wcsaxes_slice[::-1]) if self._slice_widget is None: self._slice_widget = PVSliceWidget(image=pv_slice, wcs=wcs, image_viewer=self.viewer, x=x, y=y, interpolation='nearest') self.viewer._session.application.add_widget(self._slice_widget, label='Custom Slice') self._slice_widget.window_closed.connect(self._clear_path) else: self._slice_widget.set_image(image=pv_slice, wcs=wcs, x=x, y=y, interpolation='nearest') result = self._slice_widget result.axes.set_xlabel("Position along path") if wcs is None: result.axes.set_ylabel("Cube slice index") else: result.axes.set_ylabel(_slice_label(self.viewer.state.reference_data, self.viewer.state.wcsaxes_slice[::-1])) result.show() def close(self): if self._slice_widget: self._slice_widget.close() return super(PVSlicerMode, self).close() class PVSliceWidget(StandaloneImageViewer): """ A standalone image widget with extra interactivity for PV slices """ def __init__(self, image=None, wcs=None, image_viewer=None, x=None, y=None, **kwargs): """ :param image: 2D Numpy array representing the PV Slice :param wcs: WCS for the PV slice :param image_viewer: Parent ImageViewer this was extracted from :param kwargs: Extra keywords are passed to imshow """ self._crosshairs = None self._parent = image_viewer super(PVSliceWidget, self).__init__(image=image, wcs=wcs, **kwargs) conn = self.axes.figure.canvas.mpl_connect self._down_id = conn('button_press_event', self._on_click) self._move_id = conn('motion_notify_event', self._on_move) self.axes.format_coord = self._format_coord self._x = x self._y = y self._parent.state.add_callback('x_att', self.reset) self._parent.state.add_callback('y_att', self.reset) def _format_coord(self, x, y): """ Return a formatted location label for the taskbar :param x: x pixel location in slice array :param y: y pixel location in slice array """ # xy -> xyz in image view pix = self._pos_in_parent(xdata=x, ydata=y) # xyz -> data pixel coords # accounts for fact that image might be shown transposed/rotated s = list(self._slc) idx = _slice_index(self._parent.state.reference_data, self._slc) s[s.index('x')] = pix[0] s[s.index('y')] = pix[1] s[idx] = pix[2] # labels = self._parent.coordinate_labels(s) # return ' '.join(labels) return '' def set_image(self, image=None, wcs=None, x=None, y=None, **kwargs): super(PVSliceWidget, self).set_image(image=image, wcs=wcs, **kwargs) self._axes.set_aspect('auto') self._axes.set_xlim(-0.5, image.shape[1] - 0.5) self._axes.set_ylim(-0.5, image.shape[0] - 0.5) self._slc = self._parent.state.wcsaxes_slice[::-1] self._x = x self._y = y @defer_draw def _sync_slice(self, event): s = list(self._slc) # XXX breaks if display_data changes _, _, z = self._pos_in_parent(event) s[_slice_index(self._parent.state.reference_data, s)] = int(z) self._parent.state.slices = tuple(s) @defer_draw def _draw_crosshairs(self, event): x, y, _ = self._pos_in_parent(event) self._parent.show_crosshairs(x, y) @defer_draw def _on_move(self, event): if not event.button: return if not event.inaxes or event.canvas.toolbar.mode != '': return self._sync_slice(event) self._draw_crosshairs(event) def _pos_in_parent(self, event=None, xdata=None, ydata=None): if event is not None: xdata = event.xdata ydata = event.ydata # Find position slice where cursor is ind = int(round(np.clip(xdata, 0, self._im_array.shape[1] - 1))) # Find pixel coordinate in input image for this slice x = self._x[ind] y = self._y[ind] # The 3-rd coordinate in the input WCS is simply the second # coordinate in the PV slice. z = ydata return x, y, z def _on_click(self, event): if not event.inaxes or event.canvas.toolbar.mode != '': return self._sync_slice(event) self._draw_crosshairs(event) def reset(self, *args): self.close() def _slice_from_path(x, y, data, attribute, slc): """ Extract a PV-like slice from a cube :param x: An array of x values to extract (pixel units) :param y: An array of y values to extract (pixel units) :param data: :class:`~glue.core.data.Data` :param attribute: :claass:`~glue.core.data.Component` :param slc: orientation of the image widget that `pts` are defined on :returns: (slice, x, y) slice is a 2D Numpy array, corresponding to a "PV ribbon" cutout from the cube x and y are the resampled points along which the ribbon is extracted :note: For >3D cubes, the "V-axis" of the PV slice is the longest cube axis ignoring the x/y axes of `slc` """ from glue.external.pvextractor import Path, extract_pv_slice p = Path(list(zip(x, y))) cube = data[attribute] dims = list(range(data.ndim)) s = list(slc) ind = _slice_index(data, slc) cube_wcs = getattr(data.coords, 'wcs', None) # transpose cube to (z, y, x, ) def _swap(x, s, i, j): x[i], x[j] = x[j], x[i] s[i], s[j] = s[j], s[i] _swap(dims, s, ind, 0) _swap(dims, s, s.index('y'), 1) _swap(dims, s, s.index('x'), 2) cube = cube.transpose(dims) if cube_wcs is not None: cube_wcs = cube_wcs.sub([data.ndim - nx for nx in dims[::-1]]) # slice down from >3D to 3D if needed s = tuple([slice(None)] * 3 + [slc[d] for d in dims[3:]]) cube = cube[s] # sample cube spacing = 1 # pixel x, y = [np.round(_x).astype(int) for _x in p.sample_points(spacing)] from astropy.wcs import WCS try: result = extract_pv_slice(cube, path=p, wcs=cube_wcs, order=0) wcs = WCS(result.header) except Exception: # sometimes pvextractor complains due to wcs. Try to recover result = extract_pv_slice(cube, path=p, wcs=None, order=0) wcs = None data = result.data return data, x, y, wcs def _slice_index(data, slc): """ The axis over which to extract PV slices """ for i in range(len(slc)): if np.isreal(slc[i]): return i raise ValueError("Could not find slice index with slc={0}".format(slc)) def _slice_label(data, slc): """ Returns a formatted axis label corresponding to the slice dimension in a PV slice :param data: Data that slice is extracted from :param slc: orientation in the image widget from which the PV slice was defined """ idx = _slice_index(data, slc) if getattr(data, 'coords') is None: return data.pixel_component_ids[idx].label else: return axis_label(data.coords, idx) glueviz-1.0.1+dfsg.orig/glue/plugins/tools/pv_slicer/__init__.py0000644000175000017500000000000013657331513024255 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/tools/python_export.py0000644000175000017500000000112413605357235023464 0ustar noahfxnoahfxfrom qtpy import compat from glue.config import viewer_tool from glue.viewers.common.tool import Tool @viewer_tool class PythonExportTool(Tool): icon = 'glue_pythonsave' tool_id = 'save:python' action_text = 'Save Python script to reproduce plot' tool_tip = 'Save Python script to reproduce plot' def activate(self): filename, _ = compat.getsavefilename(parent=self.viewer, basedir="make_plot.py") if not filename: return if not filename.endswith('.py'): filename += '.py' self.viewer.export_as_script(filename) glueviz-1.0.1+dfsg.orig/glue/plugins/tools/__init__.py0000644000175000017500000000151313657331513022301 0ustar noahfxnoahfxfrom copy import deepcopy def setup(): from glue.plugins.tools import python_export # noqa from glue.viewers.histogram.qt.data_viewer import HistogramViewer HistogramViewer.subtools = deepcopy(HistogramViewer.subtools) HistogramViewer.subtools['save'].append('save:python') from glue.viewers.image.qt.data_viewer import ImageViewer ImageViewer.subtools = deepcopy(ImageViewer.subtools) ImageViewer.subtools['save'].append('save:python') from glue.viewers.scatter.qt.data_viewer import ScatterViewer ScatterViewer.subtools = deepcopy(ScatterViewer.subtools) ScatterViewer.subtools['save'].append('save:python') from glue.viewers.profile.qt.data_viewer import ProfileViewer ProfileViewer.subtools = deepcopy(ProfileViewer.subtools) ProfileViewer.subtools['save'].append('save:python') glueviz-1.0.1+dfsg.orig/glue/plugins/wcs_autolinking/0000755000175000017500000000000013752535025022230 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/wcs_autolinking/tests/0000755000175000017500000000000013752535025023372 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/wcs_autolinking/tests/test_wcs_autolinking.py0000644000175000017500000003255013752534424030212 0ustar noahfxnoahfximport pytest import numpy as np from astropy.wcs import WCS from glue.core import Data, DataCollection from glue.plugins.wcs_autolinking.wcs_autolinking import wcs_autolink, WCSLink from glue.core.link_helpers import MultiLink from glue.core.tests.test_state import clone from glue.dialogs.link_editor.state import EditableLinkFunctionState def test_wcs_autolink_nowcs(): # No links should be found because there are no WCS coordinates present data1 = Data(x=[1, 2, 3]) data2 = Data(x=[4, 5, 6]) dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 0 def test_wcs_autolink_emptywcs(): # No links should be found because the WCS don't actually have well defined # physical types. data1 = Data() data1.coords = WCS(naxis=1) data1['x'] = [1, 2, 3] data2 = Data() data2.coords = WCS(naxis=1) data2['x'] = [4, 5, 6] dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 0 def test_wcs_autolink_spectral_cube(): # This should link all coordinates wcs1 = WCS(naxis=3) wcs1.wcs.ctype = 'DEC--TAN', 'FREQ', 'RA---TAN' wcs1.wcs.set() data1 = Data() data1.coords = wcs1 data1['x'] = np.ones((2, 3, 4)) pz1, py1, px1 = data1.pixel_component_ids wcs2 = WCS(naxis=3) wcs2.wcs.ctype = 'GLON-CAR', 'GLAT-CAR', 'FREQ' wcs2.wcs.set() data2 = Data() data2.coords = wcs2 data2['x'] = np.ones((2, 3, 4)) pz2, py2, px2 = data2.pixel_component_ids dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 link = links[0] assert isinstance(link, MultiLink) assert len(link) == 6 assert link[0].get_to_id() == px2 assert link[0].get_from_ids() == [px1, py1, pz1] assert link[1].get_to_id() == py2 assert link[1].get_from_ids() == [px1, py1, pz1] assert link[2].get_to_id() == pz2 assert link[2].get_from_ids() == [px1, py1, pz1] assert link[3].get_to_id() == px1 assert link[3].get_from_ids() == [px2, py2, pz2] assert link[4].get_to_id() == py1 assert link[4].get_from_ids() == [px2, py2, pz2] assert link[5].get_to_id() == pz1 assert link[5].get_from_ids() == [px2, py2, pz2] def test_wcs_autolink_image_and_spectral_cube(): # This should link the celestial coordinates wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs1.wcs.set() data1 = Data() data1.coords = wcs1 data1['x'] = np.ones((2, 3)) py1, px1 = data1.pixel_component_ids wcs2 = WCS(naxis=3) wcs2.wcs.ctype = 'GLON-CAR', 'FREQ', 'GLAT-CAR' wcs2.wcs.set() data2 = Data() data2.coords = wcs2 data2['x'] = np.ones((2, 3, 4)) pz2, py2, px2 = data2.pixel_component_ids dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 link = links[0] assert isinstance(link, MultiLink) assert len(link) == 4 assert link[0].get_to_id() == px2 assert link[0].get_from_ids() == [px1, py1] assert link[1].get_to_id() == pz2 assert link[1].get_from_ids() == [px1, py1] assert link[2].get_to_id() == px1 assert link[2].get_from_ids() == [px2, pz2] assert link[3].get_to_id() == py1 assert link[3].get_from_ids() == [px2, pz2] def test_clone_wcs_link(): # Make sure that WCSLink can be serialized/deserialized wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3)) wcs2 = WCS(naxis=3) wcs2.wcs.ctype = 'GLON-CAR', 'FREQ', 'GLAT-CAR' wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((2, 3, 4)) link1 = WCSLink(data1, data2) link2 = clone(link1) assert isinstance(link2, WCSLink) assert link2.data1.label == 'Data 1' assert link2.data2.label == 'Data 2' def test_link_editor(): # Make sure that the WCSLink works property in the link editor and is # returned unmodified. The main way to check that is just to make sure that # the link round-trips when going through EditableLinkFunctionState. wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'DEC--TAN', 'RA---TAN' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3)) wcs2 = WCS(naxis=3) wcs2.wcs.ctype = 'GLON-CAR', 'FREQ', 'GLAT-CAR' wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((2, 3, 4)) link1 = WCSLink(data1, data2) link2 = EditableLinkFunctionState(link1).link assert isinstance(link2, WCSLink) assert link2.data1.label == 'Data 1' assert link2.data2.label == 'Data 2' def test_celestial_with_unknown_axes(): # Regression test for a bug that caused n-d datasets with celestial axes # and axes with unknown physical types to not even be linked by celestial # axes. wcs1 = WCS(naxis=3) wcs1.wcs.ctype = 'DEC--TAN', 'RA---TAN', 'SPAM' wcs1.wcs.set() data1 = Data() data1.coords = wcs1 data1['x'] = np.ones((2, 3, 4)) pz1, py1, px1 = data1.pixel_component_ids wcs2 = WCS(naxis=3) wcs2.wcs.ctype = 'GLON-CAR', 'FREQ', 'GLAT-CAR' wcs2.wcs.set() data2 = Data() data2.coords = wcs2 data2['x'] = np.ones((2, 3, 4)) pz2, py2, px2 = data2.pixel_component_ids dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 link = links[0] assert isinstance(link, MultiLink) assert len(link) == 4 assert link[0].get_to_id() == px2 assert link[0].get_from_ids() == [px1, py1] assert link[1].get_to_id() == pz2 assert link[1].get_from_ids() == [px1, py1] assert link[2].get_to_id() == px1 assert link[2].get_from_ids() == [px2, pz2] assert link[3].get_to_id() == py1 assert link[3].get_from_ids() == [px2, pz2] def test_wcs_autolinking_of_2d_cube_with_temporal_and_spectral_axes_case_1(): """ A test to confirm that two 2D data cubes with matching number of dimensions where the first is temporal and the next one spectral (vacuum wavelength in this case) is indeed autolinked. """ wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'TIME', 'WAVE' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3)) py1, px1 = data1.pixel_component_ids wcs2 = WCS(naxis=2) wcs2.wcs.ctype = 'TAI', 'WAVE' wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((2, 3)) py2, px2 = data2.pixel_component_ids dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 link = links[0] assert isinstance(link, MultiLink) assert len(link) == 4 assert link[0].get_to_id() == px2 assert link[0].get_from_ids() == [px1, py1] assert link[1].get_to_id() == py2 assert link[1].get_from_ids() == [px1, py1] assert link[2].get_to_id() == px1 assert link[2].get_from_ids() == [px2, py2] assert link[3].get_to_id() == py1 assert link[3].get_from_ids() == [px2, py2] def test_wcs_autolinking_of_2d_cube_with_temporal_and_spectral_axes_case_2(): """ A test to confirm that two 2D data cubes with matching number of dimensions where the one is spectral (air wavelength in this case) and the other one temporal is indeed autolinked, to test that the order does not matter. """ wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'AWAV', 'TIME' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3)) py1, px1 = data1.pixel_component_ids wcs2 = WCS(naxis=2) wcs2.wcs.ctype = 'TIME', 'AWAV' wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((2, 3)) py2, px2 = data2.pixel_component_ids dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 link = links[0] assert isinstance(link, MultiLink) assert len(link) == 4 assert link[0].get_to_id() == px2 assert link[0].get_from_ids() == [px1, py1] assert link[1].get_to_id() == py2 assert link[1].get_from_ids() == [px1, py1] assert link[2].get_to_id() == px1 assert link[2].get_from_ids() == [px2, py2] assert link[3].get_to_id() == py1 assert link[3].get_from_ids() == [px2, py2] def test_has_celestial_with_time_and_spectral_axes(): """ To test the case in which we have two data cubes with unequal number of dimensions, but both have celestial axes. """ wcs1 = WCS(naxis=4) wcs1.wcs.ctype = 'WAVE', 'HPLT-TAN', 'HPLN-TAN', 'TIME' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3, 4, 5)) pw1, pz1, py1, px1 = data1.pixel_component_ids wcs2 = WCS(naxis=3) wcs2.wcs.ctype = 'HPLN-TAN', 'HPLT-TAN', 'TIME' wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((2, 3, 4)) pz2, py2, px2 = data2.pixel_component_ids dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 link = links[0] assert isinstance(link, MultiLink) assert len(link) == 6 assert link[0].get_to_id() == px2 assert link[0].get_from_ids() == [py1, pz1, pw1] assert link[1].get_to_id() == py2 assert link[1].get_from_ids() == [py1, pz1, pw1] assert link[2].get_to_id() == pz2 assert link[2].get_from_ids() == [py1, pz1, pw1] assert link[3].get_to_id() == py1 assert link[3].get_from_ids() == [px2, py2, pz2] assert link[4].get_to_id() == pz1 assert link[4].get_from_ids() == [px2, py2, pz2] assert link[5].get_to_id() == pw1 assert link[5].get_from_ids() == [px2, py2, pz2] @pytest.mark.xfail def test_2d_and_1d_data_cubes_with_no_celestial_axes(): """ Test the case where we have one 2D dataset with WAVE and TIME as CTYPEs and a 1D dataset with WAVE as the CTYPE. """ wcs1 = WCS(naxis=2) wcs1.wcs.ctype = 'TIME', 'WAVE' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3)) py1, px1 = data1.pixel_component_ids wcs2 = WCS(naxis=1) wcs2.wcs.ctype = ['WAVE'] wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones(3) px2 = data2.pixel_component_ids dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 link = links[0] assert isinstance(link, MultiLink) assert len(link) == 2 assert ' '.join(str(link[0].get_to_id()).split()[:2]) == ' '.join(str(py1).split()[:2]) assert ' '.join(str(link[0].get_from_ids()).split()[:2]) == ' '.join(str(px2).split()[:2]) @pytest.mark.xfail def test_link_of_spectral_axes_of_different_physical_types(): """ To check that there is no auto-link of spectral axes of two different physical types, e.g. between FREQ and WAVE. """ wcs1 = WCS(naxis=1) wcs1.wcs.ctype = ['FREQ'] wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones(2) px1 = data1.pixel_component_ids wcs2 = WCS(naxis=1) wcs2.wcs.ctype = ['WAVE'] wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones(2) px2 = data2.pixel_component_ids dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 link = links[0] assert isinstance(link, MultiLink) assert len(link) == 2 assert link[0].get_to_id() == str(px1[0]) assert str(link[0].get_from_ids()) == str(px2) assert link[1].get_to_id() == str(px2) assert str(link[1].get_from_ids()) == str(px1) def test_cube_has_celestial_and_cube_without_celestial_axes_1(): """ To test that there should be a link between a 3D dataset with celestial axes and a 2D dataset with no celestial axes (variant 1). """ wcs1 = WCS(naxis=3) wcs1.wcs.ctype = 'RA---TAN', 'FREQ', 'DEC--TAN' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3, 4)) pz1, py1, px1 = data1.pixel_component_ids wcs2 = WCS(naxis=2) wcs2.wcs.ctype = 'FREQ', 'TIME' wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((4, 5)) py2, px2 = data2.pixel_component_ids dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 link = links[0] assert isinstance(link, MultiLink) assert len(link) == 2 assert link[0].get_to_id() == px2 assert link[0].get_from_ids() == [py1] assert link[1].get_to_id() == py1 assert link[1].get_from_ids() == [px2] @pytest.mark.xfail def test_cube_has_celestial_and_cube_without_celestial_axes_2(): """ To test that there should be a link between a 3D dataset with celestial axes and a 2D dataset with no celestial axes (variant 2). TODO: To modify code base so that the FREQ axis would be linked up with the WAVE axis. """ wcs1 = WCS(naxis=3) wcs1.wcs.ctype = 'RA---TAN', 'FREQ', 'DEC--TAN' wcs1.wcs.set() data1 = Data(label='Data 1') data1.coords = wcs1 data1['x'] = np.ones((2, 3, 4)) wcs2 = WCS(naxis=2) wcs2.wcs.ctype = 'WAVE', 'TIME' wcs2.wcs.set() data2 = Data(label='Data 2') data2.coords = wcs2 data2['x'] = np.ones((4, 5)) dc = DataCollection([data1, data2]) links = wcs_autolink(dc) assert len(links) == 1 glueviz-1.0.1+dfsg.orig/glue/plugins/wcs_autolinking/tests/__init__.py0000644000175000017500000000000013503203504025455 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/wcs_autolinking/__init__.py0000644000175000017500000000006713613547630024345 0ustar noahfxnoahfxdef setup(): from . import wcs_autolinking # noqa glueviz-1.0.1+dfsg.orig/glue/plugins/wcs_autolinking/wcs_autolinking.py0000644000175000017500000002054113752534424026006 0ustar noahfxnoahfximport copy from astropy.wcs import WCS from astropy.wcs.utils import pixel_to_pixel from astropy.wcs.wcsapi import SlicedLowLevelWCS, HighLevelWCSWrapper from glue.config import autolinker, link_helper from glue.core.link_helpers import MultiLink __all__ = ['IncompatibleWCS', 'WCSLink', 'wcs_autolink'] class IncompatibleWCS(Exception): pass def get_cids_and_functions(wcs1, wcs2, pixel_cids1, pixel_cids2): def forwards(*pixel_input): return pixel_to_pixel(wcs1, wcs2, *pixel_input) def backwards(*pixel_input): return pixel_to_pixel(wcs2, wcs1, *pixel_input) pixel_input = [0] * len(pixel_cids1) try: # the case with wcs linkages forwards(*pixel_input) backwards(*pixel_input) except Exception: # the case without wcs linkages return None, None, None, None return pixel_cids1, pixel_cids2, forwards, backwards @link_helper(category='Astronomy') class WCSLink(MultiLink): """ A collection of links that link the pixel components of two datasets via WCS transformations. """ display = 'WCS link' cid_independent = True def __init__(self, data1=None, data2=None, cids1=None, cids2=None): wcs1, wcs2 = data1.coords, data2.coords forwards = backwards = None if wcs1.pixel_n_dim == wcs2.pixel_n_dim and wcs1.world_n_dim == wcs2.world_n_dim: if (wcs1.world_axis_physical_types.count(None) == 0 and wcs2.world_axis_physical_types.count(None) == 0): # The easiest way to check if the WCSes are compatible is to simply try and # see if values can be transformed for a single pixel. In future we might # find that this requires optimization performance-wise, but for now let's # not do premature optimization. pixel_cids1, pixel_cids2, forwards, backwards = get_cids_and_functions(wcs1, wcs2, data1.pixel_component_ids[::-1], data2.pixel_component_ids[::-1]) self._physical_types_1 = wcs1.world_axis_physical_types self._physical_types_2 = wcs2.world_axis_physical_types if not forwards or not backwards: # A generalized APE 14-compatible way # Handle also the extra-spatial axes such as those of the time and wavelength dimensions wcs1_celestial_physical_types = wcs2_celestial_physical_types = [] slicing_axes1 = slicing_axes2 = [] cids1 = data1.pixel_component_ids cids2 = data2.pixel_component_ids if wcs1.has_celestial and wcs2.has_celestial: wcs1_celestial_physical_types = wcs1.celestial.world_axis_physical_types wcs2_celestial_physical_types = wcs2.celestial.world_axis_physical_types cids1_celestial = [cids1[wcs1.wcs.naxis - wcs1.wcs.lng - 1], cids1[wcs1.wcs.naxis - wcs1.wcs.lat - 1]] cids2_celestial = [cids2[wcs2.wcs.naxis - wcs2.wcs.lng - 1], cids2[wcs2.wcs.naxis - wcs2.wcs.lat - 1]] if wcs1.celestial.wcs.lng > wcs1.celestial.wcs.lat: cids1_celestial = cids1_celestial[::-1] if wcs2.celestial.wcs.lng > wcs2.celestial.wcs.lat: cids2_celestial = cids2_celestial[::-1] slicing_axes1 = [cids1_celestial[0].axis, cids1_celestial[1].axis] slicing_axes2 = [cids2_celestial[0].axis, cids2_celestial[1].axis] wcs1_sliced_physical_types = wcs2_sliced_physical_types = [] if wcs1_celestial_physical_types is not None: wcs1_sliced_physical_types = wcs1_celestial_physical_types if wcs2_celestial_physical_types is not None: wcs2_sliced_physical_types = wcs2_celestial_physical_types for i, physical_type1 in enumerate(wcs1.world_axis_physical_types): for j, physical_type2 in enumerate(wcs2.world_axis_physical_types): if physical_type1 == physical_type2: if physical_type1 not in wcs1_sliced_physical_types: slicing_axes1.append(wcs1.world_n_dim - i - 1) wcs1_sliced_physical_types.append(physical_type1) if physical_type2 not in wcs2_sliced_physical_types: slicing_axes2.append(wcs2.world_n_dim - j - 1) wcs2_sliced_physical_types.append(physical_type2) slicing_axes1 = sorted(slicing_axes1, key=str, reverse=True) slicing_axes2 = sorted(slicing_axes2, key=str, reverse=True) # Generate slices for the wcs slicing slices1 = [slice(None)] * wcs1.world_n_dim slices2 = [slice(None)] * wcs2.world_n_dim for i in range(wcs1.world_n_dim): if i not in slicing_axes1: slices1[i] = 0 for j in range(wcs2.world_n_dim): if j not in slicing_axes2: slices2[j] = 0 wcs1_sliced = SlicedLowLevelWCS(wcs1, tuple(slices1)) wcs2_sliced = SlicedLowLevelWCS(wcs2, tuple(slices2)) wcs1_final = HighLevelWCSWrapper(copy.copy(wcs1_sliced)) wcs2_final = HighLevelWCSWrapper(copy.copy(wcs2_sliced)) cids1_sliced = [cids1[x] for x in slicing_axes1] cids1_sliced = sorted(cids1_sliced, key=str, reverse=True) cids2_sliced = [cids2[x] for x in slicing_axes2] cids2_sliced = sorted(cids2_sliced, key=str, reverse=True) pixel_cids1, pixel_cids2, forwards, backwards = get_cids_and_functions( wcs1_final, wcs2_final, cids1_sliced, cids2_sliced) self._physical_types_1 = wcs1_sliced_physical_types self._physical_types_2 = wcs2_sliced_physical_types if pixel_cids1 is None: raise IncompatibleWCS("Can't create WCS link between {0} and {1}".format(data1.label, data2.label)) super(WCSLink, self).__init__(pixel_cids1, pixel_cids2, forwards=forwards, backwards=backwards) self.data1 = data1 self.data2 = data2 def __gluestate__(self, context): state = {} state['data1'] = context.id(self.data1) state['data2'] = context.id(self.data2) return state @classmethod def __setgluestate__(cls, rec, context): self = cls(context.object(rec['data1']), context.object(rec['data2'])) return self @property def description(self): types1 = ''.join(['
  • ' + phys_type for phys_type in self._physical_types_1]) types2 = ''.join(['
  • ' + phys_type for phys_type in self._physical_types_2]) return ('This automatically links the coordinates of the ' 'two datasets using the World Coordinate System (WCS) ' 'coordinates defined in the files.

    The physical types ' 'of the coordinates linked in the first dataset are: ' '
      {0}
    and in the second dataset:
      {1}
    ' .format(types1, types2)) @autolinker('Astronomy WCS') def wcs_autolink(data_collection): # Find subset of datasets with WCS coordinates wcs_datasets = [data for data in data_collection if hasattr(data, 'coords') and isinstance(data.coords, WCS)] # Only continue if there are at least two such datasets if len(wcs_datasets) < 2: return [] # Find existing WCS links existing = set() for link in data_collection.external_links: if isinstance(link, WCSLink): existing.add((link.data1, link.data2)) # Loop through all pairs of datasets, skipping pairs for which a link # already exists. PERF: in practice we don't actually have to link all # pairs, so we should try and optimize that. all_links = [] for i1, data1 in enumerate(wcs_datasets): for data2 in wcs_datasets[i1 + 1:]: if (data1, data2) not in existing: try: link = WCSLink(data1, data2) except IncompatibleWCS: continue all_links.append(link) return all_links glueviz-1.0.1+dfsg.orig/glue/plugins/data_factories/0000755000175000017500000000000013752535025022000 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/data_factories/__init__.py0000644000175000017500000000000013455362716024104 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/coordinate_helpers/0000755000175000017500000000000013752535025022701 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/coordinate_helpers/tests/0000755000175000017500000000000013752535025024043 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/coordinate_helpers/tests/test_link_helpers.py0000644000175000017500000001302113657331477030141 0ustar noahfxnoahfximport pytest import numpy as np pytest.importorskip('astropy') from glue.core import ComponentID, Data, DataCollection from glue.core.tests.test_link_helpers import check_link, check_using from glue.core.tests.test_state import clone from ..link_helpers import (Galactic_to_FK5, FK4_to_FK5, ICRS_to_FK5, Galactic_to_FK4, ICRS_to_FK4, ICRS_to_Galactic, GalactocentricToGalactic) # We now store for each class the expected result of the conversion of (45,50) # from the input frame to output frame and then from the output frame to the # input frame. EXPECTED = { Galactic_to_FK5: [(238.23062386, 27.96352696), (143.12136866, -7.76422226)], FK4_to_FK5: [(45.87780898, 50.19529421), (44.12740884, 49.80169907)], ICRS_to_FK5: [(45.00001315, 49.99999788), (44.99998685, 50.00000212)], Galactic_to_FK4: [(237.71557513, 28.11113265), (143.52337155, -7.32105993)], ICRS_to_FK4: [(44.12742195, 49.801697), (45.87779583, 50.19529642)], ICRS_to_Galactic: [(143.12137717, -7.76422008), (238.23062019, 27.96352359)], } lon1, lat1, lon2, lat2 = (ComponentID('lon_in'), ComponentID('lat_in'), ComponentID('lon_out'), ComponentID('lat_out')) @pytest.mark.parametrize(('conv_class', 'expected'), list(EXPECTED.items())) def test_conversion(conv_class, expected): result = conv_class([lon1, lat1], [lon2, lat2]) assert len(result) == 4 # Check links are correct check_link(result[0], [lon1, lat1], lon2) check_link(result[1], [lon1, lat1], lat2) check_link(result[2], [lon2, lat2], lon1) check_link(result[3], [lon2, lat2], lat1) # Check string representation assert str(result[0]) == "lon_out <- " + conv_class.__name__ + ".forwards_1(lon_in, lat_in)" assert str(result[1]) == "lat_out <- " + conv_class.__name__ + ".forwards_2(lon_in, lat_in)" assert str(result[2]) == "lon_in <- " + conv_class.__name__ + ".backwards_1(lon_out, lat_out)" assert str(result[3]) == "lat_in <- " + conv_class.__name__ + ".backwards_2(lon_out, lat_out)" # Check numerical accuracy x = np.array([45]) y = np.array([50]) check_using(result[0], (x, y), expected[0][0]) check_using(result[1], (x, y), expected[0][1]) check_using(result[2], (x, y), expected[1][0]) check_using(result[3], (x, y), expected[1][1]) # Check that state saving works check_using(clone(result[0]), (x, y), expected[0][0]) check_using(clone(result[1]), (x, y), expected[0][1]) check_using(clone(result[2]), (x, y), expected[1][0]) check_using(clone(result[3]), (x, y), expected[1][1]) x, y, z, l, b, d = (ComponentID('x'), ComponentID('y'), ComponentID('z'), ComponentID('l'), ComponentID('b'), ComponentID('d')) def test_galactocentric(): result = GalactocentricToGalactic([x, y, z], [l, b, d]) assert len(result) == 6 # Check links are correct check_link(result[0], [x, y, z], l) check_link(result[1], [x, y, z], b) check_link(result[2], [x, y, z], d) check_link(result[3], [l, b, d], x) check_link(result[4], [l, b, d], y) check_link(result[5], [l, b, d], z) # Check string representation assert str(result[0]) == "l <- GalactocentricToGalactic.forwards_1(x, y, z)" assert str(result[1]) == "b <- GalactocentricToGalactic.forwards_2(x, y, z)" assert str(result[2]) == "d <- GalactocentricToGalactic.forwards_3(x, y, z)" assert str(result[3]) == "x <- GalactocentricToGalactic.backwards_1(l, b, d)" assert str(result[4]) == "y <- GalactocentricToGalactic.backwards_2(l, b, d)" assert str(result[5]) == "z <- GalactocentricToGalactic.backwards_3(l, b, d)" # Check numerical accuracy ref = (1., 1., 2.) check_using(result[0], ref, 6.141592406648453) check_using(result[1], ref, 12.096336453181067) check_using(result[2], ref, 9.559388692193782) check_using(result[3], ref, -6.30046229834067) check_using(result[4], ref, 0.03489758558640843) check_using(result[5], ref, 0.055403498825152414) # Check that state saving works check_using(clone(result[0]), ref, 6.141592406648453) check_using(clone(result[1]), ref, 12.096336453181067) check_using(clone(result[2]), ref, 9.559388692193782) check_using(clone(result[3]), ref, -6.30046229834067) check_using(clone(result[4]), ref, 0.03489758558640843) check_using(clone(result[5]), ref, 0.055403498825152414) @pytest.mark.parametrize(('conv_class', 'expected'), list(EXPECTED.items())) def test_cid_parent(conv_class, expected): # Regression test for a bug that caused CIDs in the links in a restored # session to not be set. This was due to the list serializer using # 'do' instead of 'id' which caused ComponentIDs to be reserialized even # if they were already serialized elsewhere in the session file. data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='test1') data2 = Data(a=[1, 2, 3], b=[2, 3, 4], label='test2') result = conv_class([data1.id['x'], data1.id['x']], [data2.id['a'], data2.id['b']]) dc = DataCollection([data1, data2]) dc.add_link(result) dc2 = clone(dc) assert dc2[0].id['x'].parent is dc2[0] assert dc2[0].id['y'].parent is dc2[0] assert dc2[1].id['a'].parent is dc2[1] assert dc2[1].id['b'].parent is dc2[1] el = dc2._link_manager._external_links[0] for link in dc2.links: for cid in link.get_from_ids() + link.get_to_ids(): if cid.label in ('x', 'y'): assert cid.parent is dc2[0] else: assert cid.parent is dc2[1] glueviz-1.0.1+dfsg.orig/glue/plugins/coordinate_helpers/tests/__init__.py0000644000175000017500000000000013455362716026147 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/plugins/coordinate_helpers/__init__.py0000644000175000017500000000006413613546740025014 0ustar noahfxnoahfxdef setup(): from . import link_helpers # noqa glueviz-1.0.1+dfsg.orig/glue/plugins/coordinate_helpers/link_helpers.py0000644000175000017500000000737613660512166025746 0ustar noahfxnoahfx# A plugin to enable link helpers for Astronomical coordinate conversions. from astropy import units as u from astropy.coordinates import (ICRS, FK5, FK4, Galactic, Galactocentric, galactocentric_frame_defaults) from glue.core.link_helpers import BaseMultiLink from glue.config import link_helper __all__ = ["BaseCelestialMultiLink", "Galactic_to_FK5", "FK4_to_FK5", "ICRS_to_FK5", "Galactic_to_FK4", "ICRS_to_FK4", "ICRS_to_Galactic"] class BaseCelestialMultiLink(BaseMultiLink): display = None frame_in = None frame_out = None def forwards(self, in_lon, in_lat): cin = self.frame_in(in_lon * u.deg, in_lat * u.deg) cout = cin.transform_to(self.frame_out) return cout.spherical.lon.degree, cout.spherical.lat.degree def backwards(self, out_lon, out_lat): cout = self.frame_out(out_lon * u.deg, out_lat * u.deg) cin = cout.transform_to(self.frame_in) return cin.spherical.lon.degree, cin.spherical.lat.degree # Backward-compatibility with glue-core <0.15 forward = forwards backward = backwards @link_helper(category='Astronomy') class Galactic_to_FK5(BaseCelestialMultiLink): description = 'Link Galactic and FK5 (J2000) Equatorial coordinates' labels1 = 'l', 'b' labels2 = 'ra (fk5)', 'dec (fk5)' display = "Galactic <-> FK5 (J2000)" frame_in = Galactic frame_out = FK5 @link_helper(category='Astronomy') class FK4_to_FK5(BaseCelestialMultiLink): description = 'Link FK4 (B1950) and FK5 (J2000) Equatorial coordinates' labels1 = 'ra (fk4)', 'dec (fk4)' labels2 = 'ra (fk5)', 'dec (fk5)' display = "FK4 (B1950) <-> FK5 (J2000)" frame_in = FK4 frame_out = FK5 @link_helper(category='Astronomy') class ICRS_to_FK5(BaseCelestialMultiLink): description = 'Link ICRS and FK5 (J2000) Equatorial coordinates' labels1 = 'ra (fk4)', 'dec (fk4)' labels2 = 'ra (icrs)', 'dec (icrs)' display = "ICRS <-> FK5 (J2000)" frame_in = ICRS frame_out = FK5 @link_helper(category='Astronomy') class Galactic_to_FK4(BaseCelestialMultiLink): description = 'Link Galactic and FK4 (B1950) Equatorial coordinates' labels1 = 'l', 'b' labels2 = 'ra (fk4)', 'dec (fk4)' display = "Galactic <-> FK4 (B1950)" frame_in = Galactic frame_out = FK4 @link_helper(category='Astronomy') class ICRS_to_FK4(BaseCelestialMultiLink): description = 'Link ICRS and FK4 (B1950) Equatorial coordinates' labels1 = 'ra (icrs)', 'dec (icrs)' labels2 = 'ra (fk4)', 'dec (fk4)' display = "ICRS <-> FK4 (B1950)" frame_in = ICRS frame_out = FK4 @link_helper(category='Astronomy') class ICRS_to_Galactic(BaseCelestialMultiLink): description = 'Link ICRS and Galactic coordinates' labels1 = 'ra (icrs)', 'dec (icrs)' labels2 = 'l', 'b' display = "ICRS <-> Galactic" frame_in = ICRS frame_out = Galactic @link_helper(category='Astronomy') class GalactocentricToGalactic(BaseMultiLink): description = 'Link 3D Galactocentric and Galactic coordinates' labels1 = 'x (kpc)', 'y (kpc)', 'z (kpc)' labels2 = 'l (deg)', 'b (deg)', 'distance (kpc)' display = "3D Galactocentric <-> Galactic" def forwards(self, x_kpc, y_kpc, z_kpc): with galactocentric_frame_defaults.set('pre-v4.0'): gal = Galactocentric(x=x_kpc * u.kpc, y=y_kpc * u.kpc, z=z_kpc * u.kpc).transform_to(Galactic) return gal.l.degree, gal.b.degree, gal.distance.to(u.kpc).value def backwards(self, l_deg, b_deg, d_kpc): with galactocentric_frame_defaults.set('pre-v4.0'): gal = Galactic(l=l_deg * u.deg, b=b_deg * u.deg, distance=d_kpc * u.kpc).transform_to(Galactocentric) return gal.x.to(u.kpc).value, gal.y.to(u.kpc).value, gal.z.to(u.kpc).value glueviz-1.0.1+dfsg.orig/glue/plugins/coordinate_helpers/deprecated.py0000644000175000017500000000160213605357235025354 0ustar noahfxnoahfxfrom astropy import units as u from astropy.coordinates import FK5, Galactic def fk52gal(ra, dec): c = FK5(ra * u.deg, dec * u.deg) out = c.transform_to(Galactic) return out.l.degree, out.b.degree def gal2fk5(l, b): c = Galactic(l * u.deg, b * u.deg) out = c.transform_to(FK5) return out.ra.degree, out.dec.degree def radec2glon(ra, dec): """ Compute galactic longitude from right ascension and declination. """ return fk52gal(ra, dec)[0] def radec2glat(ra, dec): """ Compute galactic latitude from right ascension and declination. """ return fk52gal(ra, dec)[1] def lb2ra(lon, lat): """ Compute right ascension from galactic longitude and latitude. """ return gal2fk5(lon, lat)[0] def lb2dec(lon, lat): """ Compute declination from galactic longitude and latitude. """ return gal2fk5(lon, lat)[1] glueviz-1.0.1+dfsg.orig/glue/plugins/export_d3po.py0000644000175000017500000002124013605357235021651 0ustar noahfxnoahfximport os import json from glue.core import Subset DISPATCH = {} def save_page(page, page_number, label, subset): """ Convert a tab of a glue session into a D3PO page :param page: Tuple of data viewers to save :param label: Tab label """ result = {} # layout settings result['grid'] = {'nRows': 1, 'nColumns': len(page)} result['name'] = str(label) result['caption'] = 'Generated by Glue' # style settings d = page[0]._data[0] unselected = dict(opacity=d.style.alpha, size=d.style.markersize / 2, color=d.style.color) result['markerStyle'] = dict(unselected=unselected) if subset is not None: s = subset.style selected = dict(opacity=s.alpha, size=s.markersize / 2, color=s.color) result['markerStyle']['selected'] = selected result['selection'] = {'type': 'booleanColumn', 'columnName': 'selection_%i' % page_number} result['histogramStyle'] = result['markerStyle'] # save each plot result['plots'] = list(map(save_plot, page, range(len(page)))) return result def save_plot_base(plot, index): result = {} result['gridPosition'] = [0, index] return result def save_plot(plot, index): typ = type(plot) return DISPATCH[typ](plot, index) def save_scatter(plot, index): """ Convert a single glue scatter plot to a D3PO plot :param plot: Glue scatter plot :class:`~glue.viewers.scatter.qt.ScatterViewer` :param index: 1D index of plot on the page :type index: int :rtype: json-serializable dict """ result = save_plot_base(plot, index) result['type'] = 'scatter' result['xAxis'] = dict(columnName=plot.state.x_att.label, range=[float(plot.state.x_min), float(plot.state.x_max)]) result['yAxis'] = dict(columnName=plot.state.y_att.label, range=[float(plot.state.y_min), float(plot.state.y_max)]) # XXX log scales return result def save_histogram(plot, index): """ Convert a single histogram to a D3PO plot :param plot: Glue histogram :type plot: :class:`~glue.viewers.histogram.qt.HistogramViewer` :param index: 1D index of plot on the page :type index: int :rtype: json-serializable dict """ result = save_plot_base(plot, index) result['type'] = 'histogram' result['xAxis'] = dict(columnName=plot.state.x_att.label, bins=int(plot.state.hist_n_bin), range=[float(plot.state.hist_x_min), float(plot.state.hist_x_max)]) # XXX normed, cumultive, log return result def stage_subsets(application): """ Return a tuple of the subset to use for each stage/tab, or None if the tab has no subset If more than one subset is used per stage/tab, returns None """ result = [] for page in application.viewers: subset = None for viewer in page: for layer_artist in viewer.layers: if not layer_artist.visible: continue s = layer_artist.layer if not isinstance(s, Subset): continue if subset is not None and s is not subset: return None if subset is None: subset = s result.append(subset) return tuple(result) def can_save_d3po(application): """ Check whether an application can be exported to D3PO. Raises an exception if not """ dc = application.session.data_collection if len(dc) != 1: raise ValueError("D3PO Export only supports a single dataset") for tab in application.viewers: for viewer in tab: if not isinstance(viewer, tuple(DISPATCH.keys())): raise ValueError("D3PO Export only supports scatter " "and histogram plots") if sum(len(tab) for tab in application.viewers) == 0: raise ValueError("D3PO Export requires at least one scatterplot " "or histogram") if stage_subsets(application) is None: raise ValueError("D3PO Export restricted to 0 or 1 subsets visible " "in each tab") def make_data_file(data, subsets, path): """ Create the data.csv file, given Data and tuple of subsets """ from astropy.table import Table, Column data_path = os.path.join(path, 'data.csv') t = Table([data[c] for c in data.components], names=[c.label for c in data.components]) for i, subset in enumerate(subsets): if subset is None: continue c = Column(data=subset.to_mask().astype('i'), name='selection_%i' % i) t.add_column(c) t.write(data_path, format='ascii', delimiter=',') def save_d3po(application, path, launch=True): """Save a Glue session to a D3PO bundle. Currently, this has the following restrictions: - The Glue session must have only one dataset open, and 0 or 1 subsets - Only scatter plots or histograms are present - At least one plot is present :param application: Glue appication to save :param path: Path to directory to save in. Will be created if needed """ if os.path.exists(path) and not os.path.isdir(path): os.unlink(path) if not os.path.exists(path): os.mkdir(path) data = application.session.data_collection[0] subsets = stage_subsets(application) viewers = application.viewers # data.csv make_data_file(data, subsets, path) # states.json result = {} result['filename'] = 'data.csv' # XXX don't think this is needed? result['title'] = "Glue export of %s" % data.label result['states'] = list(map(save_page, application.viewers, range(len(viewers)), application.tab_names, subsets)) state_path = os.path.join(path, 'states.json') with open(state_path, 'w') as outfile: json.dump(result, outfile, indent=2, sort_keys=True) # index.html html_path = os.path.join(path, 'index.html') with open(html_path, 'w') as outfile: outfile.write(HTML) # show the result if launch: launch_d3po(path) def launch_d3po(path): """Start a server to view an exported D3PO bundle, and open a browser. :param path: The TLD of the bundle """ from socketserver import TCPServer from http.server import SimpleHTTPRequestHandler from random import randrange from socket import error import webbrowser from threading import Thread os.chdir(path) while True: try: PORT = randrange(8000, 9000) server = TCPServer(("", PORT), SimpleHTTPRequestHandler, False) server.allow_reuse_address = True server.server_bind() break except error: # port already taken pass print('Serving D3PO on port 0.0.0.0:%i' % PORT) server.server_activate() thread = Thread(target=server.serve_forever) thread.setDaemon(True) # do not prevent shutdown thread.start() webbrowser.open('http://0.0.0.0:%i' % PORT) def setup(): from glue.config import exporters exporters.add('D3PO', save_d3po, can_save_d3po, outmode='directory') HTML = """
    """ try: from glue.viewers.scatter.qt import ScatterViewer from glue.viewers.histogram.qt import HistogramViewer except ImportError: pass else: DISPATCH[ScatterViewer] = save_scatter DISPATCH[HistogramViewer] = save_histogram glueviz-1.0.1+dfsg.orig/glue/utils/0000755000175000017500000000000013752535025016507 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/utils/tests/0000755000175000017500000000000013752535025017651 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/utils/tests/test_matplotlib.py0000644000175000017500000001251313605357235023435 0ustar noahfxnoahfx import pytest import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Circle from matplotlib.artist import Artist from numpy.testing import assert_allclose from matplotlib.backends.backend_agg import FigureCanvasAgg from glue.tests.helpers import requires_scipy, requires_skimage from glue.utils.misc import DeferredMethod from ..matplotlib import (point_contour, fast_limits, all_artists, new_artists, remove_artists, view_cascade, get_extent, color2rgb, defer_draw, freeze_margins, datetime64_to_mpl, mpl_to_datetime64, color2hex) @requires_scipy @requires_skimage def test_point_contour(): data = np.array([[0, 0, 0, 0], [0, 2, 3, 0], [0, 4, 2, 0], [0, 0, 0, 0]]) xy = point_contour(2, 2, data) x = np.array([2., 1., 0.5, 0.5, 1., 2., 2.5, 2.5, 2.]) y = np.array([2.5, 2.5, 2., 1., 0.5, 0.5, 1., 2., 2.5]) np.testing.assert_array_almost_equal(xy[:, 0], x) np.testing.assert_array_almost_equal(xy[:, 1], y) def test_fast_limits_nans(): x = np.zeros((10, 10)) * np.nan assert_allclose(fast_limits(x, 0, 1), [0, 1]) def test_single_value(): x = np.array([1]) assert_allclose(fast_limits(x, 5., 95.), [1, 1]) def test_artist_functions(): c1 = Circle((0, 0), radius=1) c2 = Circle((1, 0), radius=1) c3 = Circle((2, 0), radius=1) fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.add_patch(c1) ax.add_patch(c2) assert all_artists(fig) == set([c1, c2]) ax.add_patch(c3) assert new_artists(fig, set([c1, c2])) == set([c3]) remove_artists([c2]) assert all_artists(fig) == set([c1, c3]) # check that it can deal with being passed the same artist twice remove_artists([c1, c1]) assert all_artists(fig) == set([c3]) def test_get_extent(): assert get_extent((slice(0, 5, 1), slice(0, 10, 2))) == (0, 10, 0, 5) assert get_extent((slice(0, 5, 1), slice(0, 10, 2)), transpose=True) == (0, 5, 0, 10) def test_view_cascade(): data = np.zeros((100, 100)) v2, view = view_cascade(data, (slice(0, 5, 1), slice(0, 5, 1))) assert v2 == ((slice(0, 100, 20), slice(0, 100, 20))) assert view == (slice(0, 5, 1), slice(0, 5, 1)) v2, view = view_cascade(data, (3, slice(0, 5, 1))) assert v2 == ((3, slice(0, 100, 20))) assert view == (3, slice(0, 5, 1)) def test_defer_draw(): @defer_draw def draw_figure(): fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.plot([1, 2, 3], [4, 5, 6]) fig.canvas.draw_idle() return 3.5 result = draw_figure() # Make sure that the return value was passed through correctly assert result == 3.5 def test_defer_draw_exception(tmpdir): # Regression test for a bug that meant that if an exception happened during # drawing, the draw method was not restored correctly # Make sure we start off with a clean draw method assert not isinstance(FigureCanvasAgg.draw, DeferredMethod) class ProblematicArtist(Artist): def draw(self, *args, **kwargs): raise ValueError('You shall not pass!') @defer_draw def draw_figure(): fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.add_artist(ProblematicArtist()) fig.savefig(tmpdir.join('test.png').strpath) with pytest.raises(ValueError) as exc: result = draw_figure() assert exc.value.args[0] == 'You shall not pass!' # Make sure that draw is no longer a deferred method assert not isinstance(FigureCanvasAgg.draw, DeferredMethod) @pytest.mark.parametrize(('color', 'rgb'), (('red', (1, 0, 0)), ('green', (0, 0.5020, 0)), ('orange', (1., 0.6470, 0.)))) def test_color2rgb(color, rgb): assert_allclose(color2rgb(color), rgb, atol=0.001) def test_color2hex(): assert color2hex('red') == '#ff0000' def test_freeze_margins(): fig = plt.figure(figsize=(4, 4)) ax = fig.add_subplot(1, 1, 1) freeze_margins(ax, margins=[1, 1, 1, 1]) # Note, we don't test the following since the defaults change depending # on the Matplotlib version # bbox = ax.get_position() # np.testing.assert_allclose(bbox.x0, 0.125) # np.testing.assert_allclose(bbox.y0, 0.1) # np.testing.assert_allclose(bbox.x1, 0.9) # np.testing.assert_allclose(bbox.y1, 0.9) fig.canvas.resize_event() bbox = ax.get_position() np.testing.assert_allclose(bbox.x0, 0.25) np.testing.assert_allclose(bbox.y0, 0.25) np.testing.assert_allclose(bbox.x1, 0.75) np.testing.assert_allclose(bbox.y1, 0.75) fig.set_size_inches(8, 8) fig.canvas.resize_event() bbox = ax.get_position() np.testing.assert_allclose(bbox.x0, 0.125) np.testing.assert_allclose(bbox.y0, 0.125) np.testing.assert_allclose(bbox.x1, 0.875) np.testing.assert_allclose(bbox.y1, 0.875) ax.resizer.margins = [0, 1, 2, 4] fig.canvas.resize_event() bbox = ax.get_position() np.testing.assert_allclose(bbox.x0, 0.) np.testing.assert_allclose(bbox.y0, 0.25) np.testing.assert_allclose(bbox.x1, 0.875) np.testing.assert_allclose(bbox.y1, 0.5) def test_mpl_datetime64(): # Make sure the mpl <-> datetime64 conversion round-trips mpl1 = 719313 mpl2 = datetime64_to_mpl(mpl_to_datetime64(mpl1)) assert mpl1 == mpl2 glueviz-1.0.1+dfsg.orig/glue/utils/tests/test_misc.py0000644000175000017500000000513313605357235022221 0ustar noahfxnoahfximport pytest from ..misc import (as_variable_name, file_format, DeferredMethod, nonpartial, lookup_class, as_list, common_prefix) INPUT_EXPECTED = [('x', 'x'), ('x2', 'x2'), ('2x', '_2x'), ('x!', 'x_'), ('x y z', 'x_y_z'), ('_XY', '_XY')] @pytest.mark.parametrize(('input', 'expected'), INPUT_EXPECTED) def test_as_variable_name(input, expected): assert as_variable_name(input) == expected class TestFileFormat(object): def test_gz(self): fmt = file_format('test.tar.gz') assert fmt == 'tar' def test_normal(self): fmt = file_format('test.data') assert fmt == 'data' def test_underscores(self): fmt = file_format('test_file.fits_file') assert fmt == 'fits_file' def test_multidot(self): fmt = file_format('test.a.b.c') assert fmt == 'c' def test_nodot(self): fmt = file_format('test') assert fmt == '' def test_deferred_method(): class Test(object): def __init__(self): self.a = 1 def change_a(self): self.a = 2 t = Test() Test.change_a = DeferredMethod(Test.change_a) t.change_a() assert t.a == 1 Test.change_a.execute_deferred_calls() assert t.a == 2 def test_nonpartial(): def test(a=1, b=2): pass test_wrapped = nonpartial(test) test_wrapped(a=1, b=2, c=3) def test_lookup_class(): assert lookup_class('glue.utils.misc.DeferredMethod') is DeferredMethod with pytest.raises(ValueError) as exc: assert lookup_class('gluh.utils.misc.DeferredMethod') is None assert exc.value.args[0] == "Module 'gluh.utils.misc' not found" with pytest.raises(ValueError) as exc: assert lookup_class('glue.utils.misc.DeferredMethods') is None assert exc.value.args[0] == "Object 'glue.utils.misc.DeferredMethods' not found" def test_lookup_class_builtins(): assert lookup_class('__builtin__.dict') is dict assert lookup_class('builtins.dict') is dict def test_as_list(): as_list(1) == [1] as_list([2, 3]) == [2, 3] def test_common_prefix(): assert common_prefix(['abc', 'ab', 'abcd']) == 'ab' assert common_prefix(['aaabbc', 'aabc', 'aaba']) == 'aa' assert common_prefix(['aaabbc', 'baabbc', 'caabbc']) == '' assert common_prefix(['abc1', 'abc2', 'abc3']) == 'abc' assert common_prefix(['abc_1', 'abc_2', 'abc_3']) == 'abc' assert common_prefix(['abc_1', 'abc_2', 'abc_3'], exclude_punctuation=False) == 'abc_' # TODO: add test for PropertySetMixin glueviz-1.0.1+dfsg.orig/glue/utils/tests/test_decorator.py0000644000175000017500000000052413605357235023247 0ustar noahfxnoahfxfrom ..decorators import avoid_circular def test_avoid_circular(): class CircularCall(object): @avoid_circular def a(self): self.b() @avoid_circular def b(self): self.a() c = CircularCall() # Without avoid_circular, the following causes a recursion error c.a() glueviz-1.0.1+dfsg.orig/glue/utils/tests/__init__.py0000644000175000017500000000000013455362716021755 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/utils/tests/test_geometry.py0000644000175000017500000000715713605357235023131 0ustar noahfxnoahfximport numpy as np from numpy.testing import assert_equal from glue.tests.helpers import requires_scipy from ..geometry import polygon_line_intersections, floodfill def test_square_nonclosed(): x = [0, 2, 2, 0] y = [-1, -1, 3, 3] assert polygon_line_intersections(x, y, xval=-0.1) == [] assert polygon_line_intersections(x, y, xval=0) == [(-1, 3)] assert polygon_line_intersections(x, y, xval=1) == [(-1, 3)] assert polygon_line_intersections(x, y, xval=2) == [(-1, 3)] assert polygon_line_intersections(x, y, xval=2.1) == [] assert polygon_line_intersections(x, y, yval=-1.1) == [] assert polygon_line_intersections(x, y, yval=-1) == [] assert polygon_line_intersections(x, y, yval=0) == [(0, 2)] assert polygon_line_intersections(x, y, yval=1) == [(0, 2)] assert polygon_line_intersections(x, y, yval=2) == [(0, 2)] assert polygon_line_intersections(x, y, yval=3) == [] assert polygon_line_intersections(x, y, yval=3.1) == [] def test_polygon(): x = [0, 0, 2, 2, 1, 1, 3, 3, 2, 0] y = [2, 4, 4, 3, 3, 2, 2, 1, 0, 2] assert polygon_line_intersections(x, y, xval=-0.1) == [] assert polygon_line_intersections(x, y, xval=+0.0) == [] assert polygon_line_intersections(x, y, xval=+0.5) == [(1.5, 4)] assert polygon_line_intersections(x, y, xval=+1.0) == [(1, 2), (3, 4)] assert polygon_line_intersections(x, y, xval=+1.5) == [(0.5, 2), (3, 4)] assert polygon_line_intersections(x, y, xval=+2.0) == [(0, 2)] assert polygon_line_intersections(x, y, xval=+2.5) == [(0.5, 2)] assert polygon_line_intersections(x, y, xval=+3.0) == [] assert polygon_line_intersections(x, y, xval=+3.1) == [] assert polygon_line_intersections(x, y, yval=-0.1) == [] assert polygon_line_intersections(x, y, yval=+0.0) == [] assert polygon_line_intersections(x, y, yval=+0.5) == [(1.5, 2.5)] assert polygon_line_intersections(x, y, yval=+1.0) == [(1, 3)] assert polygon_line_intersections(x, y, yval=+1.5) == [(0.5, 3)] assert polygon_line_intersections(x, y, yval=+2.0) == [(0, 1), (1, 3)] assert polygon_line_intersections(x, y, yval=+2.5) == [(0, 1)] assert polygon_line_intersections(x, y, yval=+3.0) == [(0, 1), (1, 2)] assert polygon_line_intersections(x, y, yval=+3.1) == [(0, 2)] assert polygon_line_intersections(x, y, yval=+3.5) == [(0, 2)] assert polygon_line_intersections(x, y, yval=+4.0) == [(0, 2)] assert polygon_line_intersections(x, y, yval=+4.1) == [] DATA = np.array([[9, 6, 2, 3], [4, 5, 2, 5], [2, 4, 1, 0], [5, 6, 0, -1]]) @requires_scipy def test_floodfill(): result = floodfill(DATA, (1, 0), 1.01) assert_equal(result, [[0, 0, 0, 0], [1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) result = floodfill(DATA, (1, 0), 1.3) assert_equal(result, [[0, 0, 0, 0], [1, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0]]) result = floodfill(DATA, (1, 0), 1.6) assert_equal(result, [[0, 1, 1, 1], [1, 1, 1, 1], [1, 1, 0, 0], [1, 1, 0, 0]]) result = floodfill(DATA, (1, 0), 2.1) assert_equal(result, [[0, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 0]]) result = floodfill(DATA, (1, 0), 3) assert_equal(result, [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]) glueviz-1.0.1+dfsg.orig/glue/utils/tests/test_array.py0000644000175000017500000003064513657331513022410 0ustar noahfxnoahfxfrom itertools import product import pytest import numpy as np from numpy.testing import assert_equal, assert_allclose from glue.tests.helpers import HYPOTHESIS_INSTALLED from ..array import (view_shape, coerce_numeric, stack_view, unique, broadcast_to, shape_to_string, check_sorted, pretty_number, unbroadcast, iterate_chunks, combine_slices, nanmean, nanmedian, nansum, nanmin, nanmax, format_minimal, compute_statistic, categorical_ndarray, index_lookup, broadcast_arrays_minimal) @pytest.mark.parametrize(('before', 'ref_after', 'ref_indices'), (([2.2, 5, 4, 4, 2, 8.3, 2.2], [2, 2.2, 4, 5, 8.3], [1, 3, 2, 2, 0, 4, 1]), ([2.2, 5, np.nan, 2, 8.3, 2.2], [2, 2.2, 5, 8.3], [1, 2, -1, 0, 3, 1]))) def test_unique(before, ref_after, ref_indices): after, indices = unique(before) np.testing.assert_array_equal(after, ref_after) np.testing.assert_array_equal(indices, ref_indices) def test_unique_dtype(): # Regression test to make sure that when working with strings, we don't # get an object array back from the unique function array = np.array(['a', 'b', 'c']) U, I = unique(array) assert U.dtype.kind in 'SU' li = ['a', 'b', 'c'] U, I = unique(li) assert U.dtype.kind in 'SU' def test_shape_to_string(): assert shape_to_string((1, 4, 3)) == "(1, 4, 3)" def test_view_shape(): assert view_shape((10, 10), np.s_[:]) == (10, 10) assert view_shape((10, 10, 10), np.s_[:]) == (10, 10, 10) assert view_shape((10, 10), np.s_[:, 1]) == (10,) assert view_shape((10, 10), np.s_[2:3, 2:3]) == (1, 1) assert view_shape((10, 10), None) == (10, 10) assert view_shape((10, 10), ([1, 2, 3], [2, 3, 4])) == (3,) def test_coerce_numeric(): x = np.array(['1', '2', '3.14', '4'], dtype=str) np.testing.assert_array_equal(coerce_numeric(x), [1, 2, 3.14, 4]) x = np.array([1, 2, 3]) assert coerce_numeric(x) is x x = np.array([0, 1, 1, 0], dtype=bool) np.testing.assert_array_equal(coerce_numeric(x), np.array([0, 1, 1, 0], dtype=np.int)) @pytest.mark.parametrize(('shape', 'views'), [ [(5, 5), (np.s_[0:3],)], [(5, 4), (np.s_[0:3],)], [(5, 4), ((3, 2),)], [(5, 4), (np.s_[0:4], np.s_[:, 0:2])], [(5, 4), (np.s_[0:3, 0:2], 'transpose', (0, 0))], [(10, 20), (np.random.random((10, 20)) > 0.1, 3)], [(5, 7), ('transpose', (3, 2))], ]) def test_stack_view(shape, views): x = np.random.random(shape) exp = x for v in views: if isinstance(v, str) and v == 'transpose': exp = exp.T else: exp = exp[v] actual = x[stack_view(shape, *views)] np.testing.assert_array_equal(exp, actual) @pytest.mark.parametrize(('array', 'is_sorted'), (([1, 3, 4, 3], False), ([1, 2, np.nan, 3], True), ([1, 3, 4, 4.1], True))) def test_check_sorted(array, is_sorted): assert check_sorted(array) is is_sorted class TestPrettyNumber(object): def test_single(self): assert pretty_number([1]) == ['1'] assert pretty_number([0]) == ['0'] assert pretty_number([-1]) == ['-1'] assert pretty_number([1.0001]) == ['1'] assert pretty_number([1.01]) == ['1.01'] assert pretty_number([1e-5]) == ['1.000e-05'] assert pretty_number([1e5]) == ['1.000e+05'] assert pretty_number([3.3]) == ['3.3'] assert pretty_number([1.]) == ['1'] assert pretty_number([1.200]) == ['1.2'] def test_large(self): # Regression test or a bug that caused trailing zeros in exponent to # be removed. assert pretty_number([1e9]) == ['1.000e+09'] assert pretty_number([2e10]) == ['2.000e+10'] assert pretty_number([3e11]) == ['3.000e+11'] def test_list(self): assert pretty_number([1, 2, 3.3, 1e5]) == ['1', '2', '3.3', '1.000e+05'] def test_unbroadcast(): x = np.array([1, 2, 3]) y = broadcast_to(x, (2, 4, 3)) z = unbroadcast(y) assert z.shape == (1, 1, 3) np.testing.assert_allclose(z[0, 0], x) def test_broadcast_arrays_minimal(): a = np.array([1, 2, 3]) b = broadcast_to(a, (2, 4, 3)) c = np.ones((2, 1, 1)) d = broadcast_to(c, (2, 4, 3)) e, f = broadcast_arrays_minimal(b, d) assert e.shape == (2, 1, 3) assert f.shape == (2, 1, 3) @pytest.mark.parametrize(('chunk_shape', 'n_max'), [(None, 121), (None, 100000), (None, 5), ((3, 1, 2, 4, 1, 2), None)]) def test_iterate_chunks(chunk_shape, n_max): array = np.zeros((6, 3, 4, 5, 1, 8)) for slices in iterate_chunks(array.shape, chunk_shape=chunk_shape, n_max=n_max): print(slices) # Make sure empty slices aren't returned assert array[slices].size > 0 # Increment all values in slice by 1 array[slices] += 1 assert_equal(array, 1) def test_iterate_chunks_invalid(): array = np.zeros((6, 3, 4, 5, 1, 8)) with pytest.raises(ValueError) as exc: next(iterate_chunks(array.shape, chunk_shape=(6, 2, 1))) assert exc.value.args[0] == 'chunk_shape should have the same length as shape' with pytest.raises(ValueError) as exc: next(iterate_chunks(array.shape, chunk_shape=(6, 2, 1, 5, 2, 8))) assert exc.value.args[0] == 'chunk_shape should fit within shape' FUNCTIONS = [nanmean, nanmedian, nanmin, nanmax, nansum] AXIS = [None, 0, 2, 3, (0, 1), (2, 3), (0, 1, 2), (0, 1, 2, 3)] ARRAY = np.random.random((4, 5, 2, 7)) ARRAY[ARRAY < 0.1] = np.nan @pytest.mark.parametrize(('function', 'axis'), product(FUNCTIONS, AXIS)) def test_nanfunctions(function, axis): name = function.__name__ np_func = getattr(np, name) assert_allclose(function(ARRAY, axis=axis), np_func(ARRAY, axis=axis)) SLICE_CASES = [ (slice(None), slice(5), 10), (slice(None), slice(1, 5), 10), (slice(None), slice(1, 5, 2), 10), (slice(1, 8), slice(1, 5), 10), (slice(1, 8), slice(1, 5, 2), 10), (slice(1, 9, 2), slice(2, 5), 10), (slice(1, 9, 2), slice(2, 6), 10), (slice(1, 20, 2), slice(3, 18, 2), 20), (slice(1, 20, 2), slice(4, 18, 2), 20), (slice(1, 20, 2), slice(4, 18, 3), 20), (slice(1, None), slice(None, None, 2), 2), (slice(2), slice(None), 3)] @pytest.mark.parametrize(('slice1', 'slice2', 'length'), SLICE_CASES) def test_combine_slices(slice1, slice2, length): # Rather than hard-code the expected result, we can directly check that # the resulting slice gives the same indices if applied after the first # compared to a manual check indices1 = list(range(*slice1.indices(length))) indices2 = list(range(*slice2.indices(length))) expected = [indices1.index(idx) for idx in indices2 if idx in indices1] actual = list(range(*combine_slices(slice1, slice2, length).indices(length))) assert actual == expected if HYPOTHESIS_INSTALLED: from hypothesis import given, settings from hypothesis.strategies import none, integers @given(beg1=none() | integers(-100, 100), end1=none() | integers(-100, 100), stp1=none() | integers(1, 100), beg2=none() | integers(-100, 100), end2=none() | integers(-100, 100), stp2=none() | integers(1, 100), length=integers(0, 100)) @settings(max_examples=10000, derandomize=True) def test_combine_slices_hypot(beg1, end1, stp1, beg2, end2, stp2, length): slice1 = slice(beg1, end1, stp1) slice2 = slice(beg2, end2, stp2) # Rather than hard-code the expected result, we can directly check that # the resulting slice gives the same indices if applied after the first # compared to a manual check indices1 = list(range(*slice1.indices(length))) indices2 = list(range(*slice2.indices(length))) expected = [indices1.index(idx) for idx in indices2 if idx in indices1] actual = list(range(*combine_slices(slice1, slice2, length).indices(length))) assert actual == expected def test_format_minimal(): # TODO: in future could consider detecting integer cases fmt, strings = format_minimal([133, 1444, 3300]) assert fmt == "{:.1f}" assert strings == ['133.0', '1444.0', '3300.0'] # TODO: in future could consider detecting if all intervals are integers fmt, strings = format_minimal([133., 1444., 3300.]) assert fmt == "{:.1f}" assert strings == ['133.0', '1444.0', '3300.0'] fmt, strings = format_minimal([3, 4.3, 4.4, 5]) assert fmt == "{:.1f}" assert strings == ['3.0', '4.3', '4.4', '5.0'] fmt, strings = format_minimal([3, 4.325, 4.326, 5]) assert fmt == "{:.3f}" assert strings == ['3.000', '4.325', '4.326', '5.000'] fmt, strings = format_minimal([-3, 0., 0.993e-4, 5]) assert fmt == "{:.4f}" assert strings == ['-3.0000', '0.0000', '0.0001', '5.0000'] fmt, strings = format_minimal([-3, 0., 0.993e-8, 5]) assert fmt == "{:.1e}" assert strings == ['-3.0e+00', '0.0e+00', '9.9e-09', '5.0e+00'] CASES = [('mean', [-3, 1, 6], dict(), 4 / 3), ('mean', [-3, 1, 6], dict(mask=[1, 0, 1]), 1.5), ('mean', [-3, 1, 6], dict(positive=True), 3.5), ('mean', [-3, 1, np.nan], dict(finite=True), -1), ('mean', [-3, 1, np.nan], dict(finite=False), np.nan), ('median', [-3, 1, 6], dict(), 1), ('median', [-3, 1, 6, 7], dict(mask=[1, 1, 0, 1]), 1), ('median', [-3, 1, 6, 7], dict(positive=True), 6), ('median', [-3, 1, np.nan, 6], dict(finite=True), 1), ('median', [-3, 1, np.nan, 6], dict(finite=False), np.nan), ('maximum', [-3, 1, 6], dict(), 6), ('minimum', [-3, 1, 6], dict(), -3), ('sum', [-3, 1, 6], dict(), 4), ('percentile', [-3, 1, 6], dict(percentile=0), -3), ('percentile', [-3, 1, 6], dict(percentile=50), 1), ('percentile', [-3, 1, 6], dict(percentile=100), 6), ('mean', [-3, 1, 6], dict(axis=()), [-3, 1, 6]), ('maximum', [-3, 1, 6], dict(axis=()), [-3, 1, 6]), ('mean', [-3, 1, 6], dict(axis=0), 4 / 3), ('mean', [-3, 1, 6], dict(axis=(0,)), 4 / 3), ('mean', [[-1, 2], [3, 4]], dict(), 2), ('mean', [[-1, 2], [3, 4]], dict(axis=(0, 1)), 2), ('mean', [[-1, 2], [3, 4]], dict(axis=0), [1, 3]), ('mean', [[-1, 2], [3, 4]], dict(axis=1), [0.5, 3.5]), ('mean', [[-1, 2], [3, 4]], dict(axis=1, positive=True), [2, 3.5]), ('mean', [[np.nan, 2], [3, 4]], dict(axis=1, finite=True), [2, 3.5]), ('mean', [[np.nan, 2], [3, 4]], dict(axis=1, finite=False), [np.nan, 3.5]), ('mean', [], dict(), np.nan), ('mean', [-3, 1, 6], dict(mask=[0, 0, 0]), np.nan), ] @pytest.mark.parametrize(('statistic', 'data', 'kwargs', 'result'), CASES) def test_compute_statistic(statistic, data, kwargs, result): assert_allclose(compute_statistic(statistic, data, **kwargs), result) def test_index_lookup(): assert_equal(index_lookup(['a', 'b', 't', 'f', 'a', 'b'], ['a', 'b', 'f']), [0, 1, np.nan, 2, 0, 1]) def test_categorical_ndarray(): array = categorical_ndarray(['a', 'b', 'c', 'b', 'b', 'a']) assert_equal(array, ['a', 'b', 'c', 'b', 'b', 'a']) assert_equal(array.codes, [0, 1, 2, 1, 1, 0]) assert_equal(array.categories, ['a', 'b', 'c']) array = categorical_ndarray(['a', 'b', 'c', 'b', 'b', 'a'], categories=['b', 'a']) assert_equal(array, ['a', 'b', 'c', 'b', 'b', 'a']) assert_equal(array.codes, [1, 0, np.nan, 0, 0, 1]) assert_equal(array.categories, ['b', 'a']) np.random.seed(12345) array.jitter(method='uniform') assert_allclose(array.codes, [1.42961609, -0.18362445, np.nan, -0.29543972, 0.06772503, 1.0955447]) array.jitter(method=None) assert_equal(array.codes, [1, 0, np.nan, 0, 0, 1]) def test_categorical_ndarray_slicing(): array = categorical_ndarray(['a', 'b', 'c', 'b', 'b', 'a']) assert_equal(array, ['a', 'b', 'c', 'b', 'b', 'a']) assert_equal(array.codes, [0, 1, 2, 1, 1, 0]) assert_equal(array.categories, ['a', 'b', 'c']) subset = array[1:5] assert_equal(subset, ['b', 'c', 'b', 'b']) assert_equal(subset.codes, [1, 2, 1, 1]) assert_equal(subset.categories, ['a', 'b', 'c']) glueviz-1.0.1+dfsg.orig/glue/utils/qt/0000755000175000017500000000000013752535025017133 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/utils/qt/tests/0000755000175000017500000000000013752535025020275 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/utils/qt/tests/test_helpers.py0000644000175000017500000000224013605357235023350 0ustar noahfxnoahfxfrom qtpy import QtWidgets from ..helpers import update_combobox def test_update_combobox(): combo = QtWidgets.QComboBox() update_combobox(combo, [('a', 1), ('b', 2)]) update_combobox(combo, [('c', 3)]) def test_update_combobox_indexchanged(): # Regression test for bug that caused currentIndexChanged to not be # emitted if the new index happened to be the same as the old one but the # label data was different. class MyComboBox(QtWidgets.QComboBox): def __init__(self, *args, **kwargs): self.change_count = 0 super(MyComboBox, self).__init__(*args, **kwargs) self.currentIndexChanged.connect(self.changed) def changed(self): self.change_count += 1 combo = MyComboBox() update_combobox(combo, [('a', 1), ('b', 2)]) update_combobox(combo, [('c', 3)]) assert combo.change_count == 2 assert combo.currentIndex() == 0 combo = MyComboBox() update_combobox(combo, [('a', 1), ('b', 2)]) update_combobox(combo, [('a', 1), ('b', 3)]) update_combobox(combo, [('a', 3), ('b', 1)]) assert combo.change_count == 3 assert combo.currentIndex() == 1 glueviz-1.0.1+dfsg.orig/glue/utils/qt/tests/test_colors.py0000644000175000017500000000176213657331513023215 0ustar noahfxnoahfxfrom unittest.mock import MagicMock from echo import CallbackProperty from qtpy import QtGui from ..colors import qt_to_mpl_color, QColorBox, connect_color, QColormapCombo def test_colors(): assert qt_to_mpl_color(QtGui.QColor(255, 0, 0)) == '#ff0000' assert qt_to_mpl_color(QtGui.QColor(255, 255, 255)) == '#ffffff' # TODO: add a test for the other way around # TODO: add a test for cmap2pixmap # TODO: add a test for tint_pixmap def test_color_box(): func = MagicMock() label = QColorBox() label.resize(100, 100) label.colorChanged.connect(func) label.setColor('#472822') assert func.call_count == 1 def test_connect_color(): class FakeClass(object): color = CallbackProperty() c = FakeClass() label = QColorBox() connect_color(c, 'color', label) label.setColor('#472822') assert c.color == '#472822' c.color = '#012345' assert label.color() == '#012345' def test_colormap_combo(): combo = QColormapCombo() glueviz-1.0.1+dfsg.orig/glue/utils/qt/tests/test_dialogs.py0000644000175000017500000000237713605357235023343 0ustar noahfxnoahfxfrom unittest import mock from ..dialogs import pick_item, pick_class, get_text def test_pick_item(): items = ['a', 'b', 'c'] labels = ['1', '2', '3'] with mock.patch('qtpy.QtWidgets.QInputDialog') as d: d.getItem.return_value = '1', True assert pick_item(items, labels) == 'a' d.getItem.return_value = '2', True assert pick_item(items, labels) == 'b' d.getItem.return_value = '3', True assert pick_item(items, labels) == 'c' d.getItem.return_value = '3', False assert pick_item(items, labels) is None def test_pick_class(): class Foo: pass class Bar: pass Bar.LABEL = 'Baz' with mock.patch('glue.utils.qt.dialogs.pick_item') as d: pick_class([Foo, Bar], default=Foo) d.assert_called_once_with([Foo, Bar], ['Foo', 'Baz'], default=Foo) with mock.patch('glue.utils.qt.dialogs.pick_item') as d: pick_class([Foo, Bar], sort=True) d.assert_called_once_with([Bar, Foo], ['Baz', 'Foo']) def test_get_text(): with mock.patch('qtpy.QtWidgets.QInputDialog') as d: d.getText.return_value = 'abc', True assert get_text() == 'abc' d.getText.return_value = 'abc', False assert get_text() is None glueviz-1.0.1+dfsg.orig/glue/utils/qt/tests/test_python_list_model.py0000644000175000017500000000407513605357235025452 0ustar noahfxnoahfximport pytest from qtpy.QtCore import Qt from ..python_list_model import PythonListModel class TestListModel(object): def test_row_count(self): assert PythonListModel([]).rowCount() == 0 assert PythonListModel([1]).rowCount() == 1 assert PythonListModel([1, 2]).rowCount() == 2 def test_data_display(self): m = PythonListModel([1, 'a']) i = m.index(0) assert m.data(i, role=Qt.DisplayRole) == '1' i = m.index(1) assert m.data(i, role=Qt.DisplayRole) == 'a' def test_data_edit(self): m = PythonListModel([1, 'a']) i = m.index(0) assert m.data(i, role=Qt.EditRole) == '1' i = m.index(1) assert m.data(i, role=Qt.EditRole) == 'a' def test_data_user(self): m = PythonListModel([1, 'a']) i = m.index(0) assert m.data(i, role=Qt.UserRole) == 1 i = m.index(1) assert m.data(i, role=Qt.UserRole) == 'a' def test_itemget(self): m = PythonListModel([1, 'a']) assert m[0] == 1 assert m[1] == 'a' def test_itemset(self): m = PythonListModel([1, 'a']) m[0] = 'b' assert m[0] == 'b' @pytest.mark.parametrize('items', ([], [1, 2, 3], [1])) def test_len(self, items): assert len(PythonListModel(items)) == len(items) def test_pop(self): m = PythonListModel([1, 2, 3]) assert m.pop() == 3 assert len(m) == 2 assert m.pop(0) == 1 assert len(m) == 1 assert m[0] == 2 def test_append(self): m = PythonListModel([]) m.append(2) assert m[0] == 2 m.append(3) assert m[1] == 3 m.pop() m.append(4) assert m[1] == 4 def test_extend(self): m = PythonListModel([]) m.extend([2, 3]) assert m[0] == 2 assert m[1] == 3 def test_insert(self): m = PythonListModel([1, 2, 3]) m.insert(1, 5) assert m[1] == 5 def test_iter(self): m = PythonListModel([1, 2, 3]) assert list(m) == [1, 2, 3] glueviz-1.0.1+dfsg.orig/glue/utils/qt/tests/__init__.py0000644000175000017500000000000013455362716022401 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/utils/qt/tests/test_widget_properties.py0000644000175000017500000000775213657331513025460 0ustar noahfxnoahfximport pytest from qtpy import QtWidgets from echo.qt.connect import UserDataWrapper from ..widget_properties import (CurrentComboDataProperty, CurrentComboTextProperty, CurrentTabProperty, TextProperty, ButtonProperty, FloatLineProperty, ValueProperty) def test_combo_data(): class TestClass(object): co = CurrentComboDataProperty('_combo') def __init__(self): self._combo = QtWidgets.QComboBox() self._combo.addItem('a', UserDataWrapper('a')) self._combo.addItem('b', UserDataWrapper('b')) tc = TestClass() tc.co = 'a' assert tc.co == 'a' assert tc._combo.currentIndex() == 0 tc.co = 'b' assert tc.co == 'b' assert tc._combo.currentIndex() == 1 with pytest.raises(ValueError) as exc: tc.co = 'c' assert exc.value.args[0] == "Cannot find data 'c' in combo box" def test_combo_text(): class TestClass(object): co = CurrentComboTextProperty('_combo') def __init__(self): self._combo = QtWidgets.QComboBox() self._combo.addItem('a') self._combo.addItem('b') tc = TestClass() tc.co = 'a' assert tc.co == 'a' assert tc._combo.currentIndex() == 0 tc.co = 'b' assert tc.co == 'b' assert tc._combo.currentIndex() == 1 with pytest.raises(ValueError) as exc: tc.co = 'c' assert exc.value.args[0] == "Cannot find text 'c' in combo box" tc.co = None assert tc.co is None assert tc._combo.currentIndex() == -1 def test_text(): class TestClass(object): lab = TextProperty('_label') def __init__(self): self._label = QtWidgets.QLabel() tc = TestClass() tc.lab = 'hello' assert tc.lab == 'hello' assert tc._label.text() == 'hello' def test_button(): class TestClass(object): but = ButtonProperty('_button') def __init__(self): self._button = QtWidgets.QCheckBox() tc = TestClass() assert tc.but == tc._button.checkState() tc.but = True assert tc._button.isChecked() tc.but = False assert not tc._button.isChecked() tc._button.setChecked(True) assert tc.but tc._button.setChecked(False) assert not tc.but def test_float(): class TestClass(object): flt = FloatLineProperty('_float') def __init__(self): self._float = QtWidgets.QLineEdit() tc = TestClass() tc.flt = 1.0 assert float(tc._float.text()) == 1.0 tc._float.setText('10') assert tc.flt == 10.0 tc._float.setText('') assert tc.flt == 0.0 def test_value(): class TestClass(object): val1 = ValueProperty('_slider') val2 = ValueProperty('_slider', value_range=(0, 10)) val3 = ValueProperty('_slider', value_range=(0.01, 100), log=True) def __init__(self): self._slider = QtWidgets.QSlider() self._slider.setMinimum(0) self._slider.setMaximum(100) tc = TestClass() tc.val1 = 2.0 assert tc.val1 == 2.0 assert tc._slider.value() == 2.0 tc.val2 = 3.2 assert tc.val2 == 3.2 assert tc._slider.value() == 32 tc.val3 = 10 assert tc.val3 == 10 assert tc._slider.value() == 75 def test_tab(): class TestClass(object): tab = CurrentTabProperty('_tab') def __init__(self): self._tab = QtWidgets.QTabWidget() self._tab.addTab(QtWidgets.QWidget(), 'tab1') self._tab.addTab(QtWidgets.QWidget(), 'tab2') tc = TestClass() tc.tab = 'tab1' assert tc.tab == 'tab1' assert tc._tab.currentIndex() == 0 tc.tab = 'tab2' assert tc.tab == 'tab2' assert tc._tab.currentIndex() == 1 with pytest.raises(ValueError) as exc: tc.tab = 'tab3' assert exc.value.args[0] == "Cannot find value 'tab3' in tabs" glueviz-1.0.1+dfsg.orig/glue/utils/qt/tests/test_mime.py0000644000175000017500000000613613605357235022645 0ustar noahfxnoahfx# import pytest # # from qtpy.QtTest import QTest # from qtpy.QtCore import Qt # from qtpy import QtWidgets from .. import mime INSTANCE_MIME_TYPE = mime.PyMimeData.MIME_TYPE TEST_MIME_TYPE_1 = 'test1/test1' TEST_MIME_TYPE_2 = 'test2/test2' class TestMime(): def test_formats(self): d = mime.PyMimeData() assert set(d.formats()) == set([INSTANCE_MIME_TYPE]) d = mime.PyMimeData(**{'text/plain': 'hello'}) assert set(d.formats()) == set([INSTANCE_MIME_TYPE, 'text/plain']) def test_empty_has_format(self): d = mime.PyMimeData() assert d.hasFormat(INSTANCE_MIME_TYPE) assert not d.hasFormat(TEST_MIME_TYPE_1) assert not d.hasFormat(TEST_MIME_TYPE_2) def test_instance_format(self): d = mime.PyMimeData(5) assert d.hasFormat(INSTANCE_MIME_TYPE) assert not d.hasFormat(TEST_MIME_TYPE_1) assert not d.hasFormat(TEST_MIME_TYPE_2) def test_layer_format(self): d = mime.PyMimeData(5, **{TEST_MIME_TYPE_1: 10}) assert d.hasFormat(INSTANCE_MIME_TYPE) assert d.hasFormat(TEST_MIME_TYPE_1) assert not d.hasFormat(TEST_MIME_TYPE_2) def test_layers_format(self): d = mime.PyMimeData(5, **{TEST_MIME_TYPE_2: 10}) assert d.hasFormat(INSTANCE_MIME_TYPE) assert d.hasFormat(TEST_MIME_TYPE_2) assert not d.hasFormat(TEST_MIME_TYPE_1) def test_retrieve_instance(self): d = mime.PyMimeData(10) assert d.data(INSTANCE_MIME_TYPE) == 10 def test_retrieve_layer(self): d = mime.PyMimeData(**{TEST_MIME_TYPE_2: 12}) assert d.data(TEST_MIME_TYPE_2) == 12 d = mime.PyMimeData(**{TEST_MIME_TYPE_1: 12}) assert d.data(TEST_MIME_TYPE_1) == 12 def test_retrieve_not_present_returns_null(self): d = mime.PyMimeData() assert d.data('not-a-format').size() == 0 # class TestWidget(QtWidgets.QWidget): # def __init__(self, out_mime, parent=None): # super(TestWidget, self).__init__(parent) # self.setAcceptDrops(True) # # self.last_mime = None # self.out_mime = out_mime # # def dragEnterEvent(self, event): # print('drag enter') # event.accept() # # def dropEvent(self, event): # print('drop') # self.last_mime = event.mimeData() # # def mousePressEvent(self, event): # print('mouse event') # drag = QtWidgets.QDrag(self) # drag.setMimeData(self.out_mime) # drop_action = drag.exec_() # print(drop_action) # event.accept() # # # class TestMimeDragAndDrop(object): # # def setup_method(self, method): # # m1 = mime.PyMimeData(1, **{'text/plain': 'hi', 'test': 4}) # m2 = mime.PyMimeData(1, **{'test': 5}) # # w1 = TestWidget(m1) # w2 = TestWidget(m2) # # self.w1 = w1 # self.w2 = w2 # self.m1 = m1 # self.m2 = m2 # # def test_drag_drop(self): # QTest.mousePress(self.w1, Qt.LeftButton) # QTest.mouseMove(self.w2) # QTest.mouseRelease(self.w2, Qt.LeftButton) # # assert self.w2.last_mime == self.m1 glueviz-1.0.1+dfsg.orig/glue/utils/qt/tests/test_decorators.py0000644000175000017500000000532513605357235024062 0ustar noahfxnoahfximport os from unittest.mock import patch from ..decorators import messagebox_on_error, die_on_error def test_messagebox_on_error(): os.environ['GLUE_TESTING'] = 'False' def failing_function(): raise ValueError("Dialog failure") def working_function(): pass @messagebox_on_error('An error occurred') def decorated_failing_function(): failing_function() @messagebox_on_error('An error occurred') def decorated_working_function(): working_function() # Test decorator with patch('qtpy.QtWidgets.QMessageBox') as mb: decorated_failing_function() assert mb.call_args[0][2] == 'An error occurred\nDialog failure' with patch('qtpy.QtWidgets.QMessageBox') as mb: decorated_working_function() assert mb.call_count == 0 # Test context manager with patch('qtpy.QtWidgets.QMessageBox') as mb: with messagebox_on_error('An error occurred'): failing_function() assert mb.call_args[0][2] == 'An error occurred\nDialog failure' with patch('qtpy.QtWidgets.QMessageBox') as mb: with messagebox_on_error('An error occurred'): working_function() assert mb.call_count == 0 os.environ['GLUE_TESTING'] = 'True' def test_die_on_error(): os.environ['GLUE_TESTING'] = 'False' def failing_function(): raise ValueError("Dialog failure") def working_function(): pass @die_on_error('An error occurred') def decorated_failing_function(): failing_function() @die_on_error('An error occurred') def decorated_working_function(): working_function() # Test decorator with patch('sys.exit') as exit: with patch('qtpy.QtWidgets.QMessageBox') as mb: decorated_failing_function() assert mb.call_args[0][2] == 'An error occurred\nDialog failure' assert exit.called_once_with(1) with patch('sys.exit') as exit: with patch('qtpy.QtWidgets.QMessageBox') as mb: decorated_working_function() assert mb.call_count == 0 assert exit.call_count == 0 # Test context manager with patch('sys.exit') as exit: with patch('qtpy.QtWidgets.QMessageBox') as mb: with die_on_error('An error occurred'): failing_function() assert mb.call_args[0][2] == 'An error occurred\nDialog failure' assert exit.called_once_with(1) with patch('sys.exit') as exit: with patch('qtpy.QtWidgets.QMessageBox') as mb: with die_on_error('An error occurred'): working_function() assert mb.call_count == 0 assert exit.call_count == 0 os.environ['GLUE_TESTING'] = 'True' glueviz-1.0.1+dfsg.orig/glue/utils/qt/widget_properties.py0000644000175000017500000002036513657331513023252 0ustar noahfxnoahfx""" The classes in this module provide a property-like interface to widget instance variables in a class. These properties translate essential pieces of widget state into more convenient python objects (for example, the check state of a button to a bool). Example Use:: class Foo(object): bar = ButtonProperty('_button') def __init__(self): self._button = QtWidgets.QCheckBox() f = Foo() f.bar = True # equivalent to f._button.setChecked(True) assert f.bar == True """ import math from functools import reduce from glue.logger import logger from glue.utils.array import pretty_number from echo.qt.connect import _find_combo_data, UserDataWrapper # Backward-compatibility from echo.qt import (connect_checkable_button as connect_bool_button, # noqa connect_combo_data as connect_current_combo, # noqa connect_combo_text as connect_current_combo_text, # noqa connect_float_text as connect_float_edit, # noqa connect_value, connect_text) # noqa connect_int_spin = connect_value __all__ = ['WidgetProperty', 'CurrentComboDataProperty', 'CurrentComboTextProperty', 'CurrentTabProperty', 'TextProperty', 'ButtonProperty', 'FloatLineProperty', 'ValueProperty'] class WidgetProperty(object): """ Base class for widget properties Subclasses implement, at a minimum, the "get" and "set" methods, which translate between widget states and python variables Parameters ---------- att : str The location, within a class instance, of the widget to wrap around. If the widget is nested inside another variable, normal '.' syntax can be used (e.g. 'sub_window.button') docstring : str, optional Optional short summary for the property. Used by sphinx. Should be 1 sentence or less. """ def __init__(self, att, docstring=''): self.__doc__ = docstring self._att = att.split('.') def __get__(self, instance, type=None): # Under certain circumstances, PyQt will try and access these properties # while loading the ui file, so we have to be robust to failures. # However, we print out a warning if things fail. try: widget = reduce(getattr, [instance] + self._att) return self.getter(widget) except Exception: logger.info("An error occured when accessing attribute {0} of {1}. Returning None.".format('.'.join(self._att), instance)) return None def __set__(self, instance, value): widget = reduce(getattr, [instance] + self._att) self.setter(widget, value) def getter(self, widget): """ Return the state of a widget. Depends on type of widget, and must be overridden""" raise NotImplementedError() # pragma: no cover def setter(self, widget, value): """ Set the state of a widget to a certain value""" raise NotImplementedError() # pragma: no cover class CurrentComboDataProperty(WidgetProperty): """ Wrapper around the data in QComboBox. """ def getter(self, widget): """ Return the itemData stored in the currently-selected item """ if widget.currentIndex() == -1: return None else: data = widget.itemData(widget.currentIndex()) if isinstance(data, UserDataWrapper): return data.data else: return data def setter(self, widget, value): """ Update the currently selected item to the one which stores value in its itemData """ # Note, we don't use findData here because it doesn't work # well with non-str data try: idx = _find_combo_data(widget, value) except ValueError: if value is None: idx = -1 else: raise ValueError("Cannot find data '{0}' in combo box".format(value)) widget.setCurrentIndex(idx) CurrentComboProperty = CurrentComboDataProperty class CurrentComboTextProperty(WidgetProperty): """ Wrapper around the text in QComboBox. """ def getter(self, widget): """ Return the itemData stored in the currently-selected item """ if widget.currentIndex() == -1: return None else: return widget.itemText(widget.currentIndex()) def setter(self, widget, value): """ Update the currently selected item to the one which stores value in its itemData """ idx = widget.findText(value) if idx == -1: if value is not None: raise ValueError("Cannot find text '{0}' in combo box".format(value)) widget.setCurrentIndex(idx) class CurrentTabProperty(WidgetProperty): """ Wrapper around QTabWidget. """ def getter(self, widget): """ Return the itemData stored in the currently-selected item """ return widget.tabText(widget.currentIndex()) def setter(self, widget, value): """ Update the currently selected item to the one which stores value in its itemData """ for idx in range(widget.count()): if widget.tabText(idx) == value: break else: raise ValueError("Cannot find value '{0}' in tabs".format(value)) widget.setCurrentIndex(idx) class TextProperty(WidgetProperty): """ Wrapper around the text() and setText() methods for QLabel etc """ def getter(self, widget): return widget.text() def setter(self, widget, value): widget.setText(value) if hasattr(widget, 'editingFinished'): widget.editingFinished.emit() class ButtonProperty(WidgetProperty): """ Wrapper around the check state for QAbstractButton widgets """ def getter(self, widget): return widget.isChecked() def setter(self, widget, value): widget.setChecked(value) class FloatLineProperty(WidgetProperty): """ Wrapper around the text state for QLineEdit widgets. Assumes that the text is a floating-point number """ def getter(self, widget): try: return float(widget.text()) except ValueError: return 0 def setter(self, widget, value): widget.setText(pretty_number(value)) widget.editingFinished.emit() class ValueProperty(WidgetProperty): """ Wrapper around widgets with value() and setValue() Parameters ---------- att : str The location, within a class instance, of the widget to wrap around. If the widget is nested inside another variable, normal '.' syntax can be used (e.g. 'sub_window.button') docstring : str, optional Optional short summary for the property. Used by sphinx. Should be 1 sentence or less. value_range : tuple, optional If set, the slider values are mapped to this range. log : bool, optional If `True`, the mapping is assumed to be logarithmic instead of linear. """ def __init__(self, att, docstring='', value_range=None, log=False): super(ValueProperty, self).__init__(att, docstring=docstring) if log: if value_range is None: raise ValueError("log option can only be set if value_range is given") else: value_range = math.log10(value_range[0]), math.log10(value_range[1]) self.log = log self.value_range = value_range def getter(self, widget): val = widget.value() if self.value_range is not None: imin, imax = widget.minimum(), widget.maximum() vmin, vmax = self.value_range val = (val - imin) / (imax - imin) * (vmax - vmin) + vmin if self.log: val = 10 ** val return val def setter(self, widget, val): if self.log: val = math.log10(val) if self.value_range is not None: imin, imax = widget.minimum(), widget.maximum() vmin, vmax = self.value_range val = (val - vmin) / (vmax - vmin) * (imax - imin) + imin widget.setValue(val) glueviz-1.0.1+dfsg.orig/glue/utils/qt/decorators.py0000644000175000017500000000533513605357235021662 0ustar noahfxnoahfximport os import sys import traceback from contextlib import contextmanager from functools import wraps __all__ = ['set_cursor', 'set_cursor_cm', 'messagebox_on_error', 'die_on_error'] def set_cursor(shape): """Set the Qt cursor for the duration of a function call, and unset :param shape: Cursor shape to set. """ def wrapper(func): @wraps(func) def result(*args, **kwargs): from glue.utils.qt import get_qapp # Here to avoid circ import app = get_qapp() app.setOverrideCursor(shape) try: return func(*args, **kwargs) finally: app.restoreOverrideCursor() return result return wrapper # TODO: Does this really belong in this module? @contextmanager def set_cursor_cm(shape): """Context manager equivalent for :func:`set_cursor`.""" from glue.utils.qt import get_qapp app = get_qapp() app.setOverrideCursor(shape) try: yield finally: app.restoreOverrideCursor() # TODO: We should be able to avoid defining these as classes and defining # __call__ below, by using contextmanager. class messagebox_on_error(object): def __init__(self, msg, sep='\n', exit=False): self.msg = msg self.sep = sep self.exit = exit def __call__(self, f): @wraps(f) def decorated(*args, **kwargs): # If we are in glue testing mode, just execute function if os.environ.get("GLUE_TESTING") == 'True': return f(*args, **kwargs) with self: return f(*args, **kwargs) return decorated def __enter__(self): pass def __exit__(self, exc_type, exc_val, tb): if exc_type is None: return # Make sure application has been started from glue.utils.qt import get_qapp # Here to avoid circular import get_qapp() m = "%s\n%s" % (self.msg, exc_val) detail = ''.join(traceback.format_exception(exc_type, exc_val, tb)) if len(m) > 500: detail = "Full message:\n\n%s\n\n%s" % (m, detail) m = m[:500] + '...' from qtpy import QtWidgets from qtpy.QtCore import Qt qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Error", m) qmb.setDetailedText(detail) qmb.resize(400, qmb.size().height()) qmb.setTextInteractionFlags(Qt.TextSelectableByMouse) qmb.exec_() if self.exit: sys.exit(1) # Just for cases where we are testing and patching sys.exit return True class die_on_error(messagebox_on_error): def __init__(self, msg): super(die_on_error, self).__init__(msg, exit=True) glueviz-1.0.1+dfsg.orig/glue/utils/qt/dialogs.py0000644000175000017500000000536613605357235021143 0ustar noahfxnoahfxfrom qtpy import QtWidgets __all__ = ['pick_item', 'pick_class', 'get_text', 'CenteredDialog'] def pick_item(items, labels, title="Pick an item", label="Pick an item", default=None): """ Prompt the user to choose an item Returns the selected item, or `None` Parameters ---------- items : iterable Items to choose (can be any Python object) labels : iterables Labels for the items to choose title : str, optional The title of the dialog label : str, optional The prompt message """ if default in items: current = items.index(default) else: current = 0 choice, isok = QtWidgets.QInputDialog.getItem(None, title, label, labels, current=current, editable=False) if isok: index = labels.index(str(choice)) return items[index] def pick_class(classes, sort=False, **kwargs): """ Prompt the user to pick from a list of classes using Qt This is the same as `pick_item`, but the labels are automatically determined from the classes using the LABEL attribute, and if not set, then the __name__. Returns the class that was selected, or `None` Parameters ---------- classes : iterable The classes to choose from title : str, optional The title of the dialog label : str, optional The prompt message """ def _label(c): try: return c.LABEL except AttributeError: return c.__name__ if sort: classes = sorted(classes, key=lambda x: _label(x)) choices = [_label(c) for c in classes] return pick_item(classes, choices, **kwargs) def get_text(title='Enter a label', default=None): """ Prompt the user to enter text using Qt Returns the text the user typed, or `None` Parameters ---------- title : str The prompt message and widget title. default : str The default text to show in the prompt. """ result, isok = QtWidgets.QInputDialog.getText(None, title, title, text=default) if isok: return str(result) class CenteredDialog(QtWidgets.QDialog): """ A dialog that is centered on the screen. """ def center(self): # Adapted from StackOverflow # https://stackoverflow.com/questions/20243637/pyqt4-center-window-on-active-screen frameGm = self.frameGeometry() screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos()) centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center() frameGm.moveCenter(centerPoint) self.move(frameGm.topLeft()) glueviz-1.0.1+dfsg.orig/glue/utils/qt/__init__.py0000644000175000017500000000055113605357235021247 0ustar noahfxnoahfxfrom .autocomplete_widget import * # noqa from .dialogs import * # noqa from .colors import * # noqa from .decorators import * # noqa from .helpers import * # noqa from .mixins import * # noqa from .mime import * # noqa from .python_list_model import * # noqa from .threading import * # noqa from .app import * # noqa from .delegates import * # noqa glueviz-1.0.1+dfsg.orig/glue/utils/qt/helpers.py0000644000175000017500000001366113657331513021156 0ustar noahfxnoahfximport os import sys from contextlib import contextmanager from qtpy import QtCore, QtWidgets from qtpy.QtCore import Qt from qtpy.uic import loadUi from glue.utils.qt import get_text __all__ = ['update_combobox', 'GlueTabBar', 'load_ui', 'process_dialog', 'combo_as_string', 'qurl_to_path'] def update_combobox(combo, labeldata, default_index=0, block_signals=True): """ Redefine the items in a QComboBox Parameters ---------- widget : QComboBox The widget to update labeldata : sequence of N (label, data) tuples The combobox will contain N items with the appropriate labels, and data set as the userData Returns ------- combo : QComboBox The updated input Notes ----- If the current userData in the combo box matches any of labeldata, that selection will be retained. Otherwise, the first item will be selected. Signals are disabled while the combo box is updated The QComboBox is modified inplace """ if block_signals: combo.blockSignals(True) idx = combo.currentIndex() if idx >= 0: current = combo.itemData(idx) else: current = None combo.clear() index = None for i, (label, data) in enumerate(labeldata): combo.addItem(label, userData=data) if data is current or data == current: index = i if default_index < 0: default_index = combo.count() + default_index if index is None: index = min(default_index, combo.count() - 1) combo.setCurrentIndex(index) if block_signals: combo.blockSignals(False) # We need to force emit this, otherwise if the index happens to be the # same as before, even if the data is different, callbacks won't be # called. So we block the signals until just before now then always call # callback manually. combo.currentIndexChanged.emit(index) class GlueTabBar(QtWidgets.QTabBar): def __init__(self, *args, **kwargs): super(GlueTabBar, self).__init__(*args, **kwargs) def choose_rename_tab(self, index=None): """ Prompt user to rename a tab Parameters ---------- index : int Index of tab to edit. Defaults to current index """ index = index or self.currentIndex() label = get_text("New Tab Label") if not label: return self.rename_tab(index, label) def rename_tab(self, index, label): """ Updates the name used for given tab Parameters ---------- index : int Index of tab to edit. Defaults to current index label : str New label to use for this tab """ self.setTabText(index, label) def mouseDoubleClickEvent(self, event): if event.button() != Qt.LeftButton: return index = self.tabAt(event.pos()) if index >= 0: self.choose_rename_tab(index) def load_ui(path, parent=None, directory=None): """ Load a .ui file Parameters ---------- path : str Name of ui file to load parent : QObject Object to use as the parent of this widget Returns ------- w The new widget """ if directory is not None: full_path = os.path.join(directory, path) else: full_path = os.path.abspath(path) if not os.path.exists(full_path) and 'site-packages.zip' in full_path: # Workaround for Mac app full_path = os.path.join(full_path.replace('site-packages.zip', 'glue')) return loadUi(full_path, parent) @contextmanager def process_dialog(delay=0, accept=False, reject=False, function=None): """ Context manager to automatically capture the active dialog and carry out certain actions. Note that only one of ``accept``, ``reject``, or ``function`` should be specified. Parameters ---------- delay : int, optional The delay in ms before acting on the dialog (since it may not yet exist when the context manager is called). accept : bool, optional If `True`, accept the dialog after the specified delay. reject : bool, optional If `False`, reject the dialog after the specified delay function : func, optional For more complex user actions, specify a function that takes the dialog as the first and only argument. """ def _accept(dialog): dialog.accept() def _reject(dialog): dialog.reject() n_args = sum((accept, reject, function is not None)) if n_args > 1: raise ValueError("Only one of ``accept``, ``reject``, or " "``function`` should be specified") elif n_args == 0: raise ValueError("One of ``accept``, ``reject``, or " "``function`` should be specified") if accept: function = _accept elif reject: function = _reject def wrapper(): from glue.utils.qt import get_qapp app = get_qapp() # Make sure that any window/dialog that needs to be shown is shown app.processEvents() dialog = app.activeWindow() function(dialog) timer = QtCore.QTimer() timer.setInterval(delay) timer.setSingleShot(True) timer.timeout.connect(wrapper) timer.start() yield def combo_as_string(combo): """ Return the text labels of a combo box as a string to make it easier to check the content of a combo box in tests. """ items = [combo.itemText(i) for i in range(combo.count())] return ":".join(items) def qurl_to_path(url): """ Convert a local QUrl to a normal path """ # Get path to file path = url.path() # Workaround for a Qt bug that causes paths to start with a / # on Windows: https://bugreports.qt.io/browse/QTBUG-46417 if sys.platform.startswith('win'): if path.startswith('/') and path[2] == ':': path = path[1:] return path glueviz-1.0.1+dfsg.orig/glue/utils/qt/threading.py0000644000175000017500000000230413605357235021453 0ustar noahfxnoahfximport sys from qtpy import QtCore __all__ = ['Worker'] class Worker(QtCore.QThread): result = QtCore.Signal(object) error = QtCore.Signal(object) def __init__(self, func, *args, **kwargs): """ Execute a function call on a different thread. Parameters ---------- func : callable The function object to call args Positional arguments to pass to the function kwargs Keyword arguments to pass to the function """ super(Worker, self).__init__() self.func = func self.args = args or () self.kwargs = kwargs or {} def run(self): """ Invoke the function. Upon successful completion, the result signal will be fired with the output of the function If an exception occurs, the error signal will be fired with the result form ``sys.exc_info()``. """ try: self.running = True result = self.func(*self.args, **self.kwargs) self.running = False self.result.emit(result) except Exception: self.running = False self.error.emit(sys.exc_info()) glueviz-1.0.1+dfsg.orig/glue/utils/qt/python_list_model.py0000644000175000017500000000735213605357235023252 0ustar noahfxnoahfxfrom qtpy import QtCore from qtpy.QtCore import Qt __all__ = ['PythonListModel'] class PythonListModel(QtCore.QAbstractListModel): """ A Qt Model that wraps a python list, and exposes a list-like interface This can be connected directly to multiple QListViews, which will stay in sync with the state of the container. """ def __init__(self, items, parent=None): """ Create a new model Parameters ---------- items : list The initial list to wrap parent : QObject The model parent """ super(PythonListModel, self).__init__(parent) self.items = items def rowCount(self, parent=None): """Number of rows""" return len(self.items) def headerData(self, section, orientation, role): """Column labels""" if role != Qt.DisplayRole: return None return "%i" % section def row_label(self, row): """ The textual label for the row""" return str(self.items[row]) def data(self, index, role): """Retrieve data at each index""" if not index.isValid(): return None if role == Qt.DisplayRole or role == Qt.EditRole: return self.row_label(index.row()) if role == Qt.UserRole: return self.items[index.row()] def setData(self, index, value, role): """ Update the data in-place Parameters ---------- index : QModelIndex The location of the change value : object The new value role : QEditRole Which aspect of the model to update """ if not index.isValid(): return False if role == Qt.UserRole: row = index.row() self.items[row] = value self.dataChanged.emit(index, index) return True return super(PythonListModel, self).setDdata(index, value, role) def removeRow(self, row, parent=None): """ Remove a row from the table Parameters ---------- row : int Row to remove Returns ------- successful : bool """ if row < 0 or row >= len(self.items): return False self.beginRemoveRows(QtCore.QModelIndex(), row, row) self._remove_row(row) self.endRemoveRows() return True def pop(self, row=None): """ Remove and return an item (default last item) Parameters ---------- row : int (optional) Which row to remove. Default=last Returns -------- popped : object """ if row is None: row = len(self) - 1 result = self[row] self.removeRow(row) return result def _remove_row(self, row): # actually remove data. Subclasses can override this as needed self.items.pop(row) def __getitem__(self, row): return self.items[row] def __setitem__(self, row, value): index = self.index(row) self.setData(index, value, role=Qt.UserRole) def __len__(self): return len(self.items) def insert(self, row, value): self.beginInsertRows(QtCore.QModelIndex(), row, row) self.items.insert(row, value) self.endInsertRows() self.rowsInserted.emit(self.index(row), row, row) def append(self, value): row = len(self) self.insert(row, value) def extend(self, values): for v in values: self.append(v) def set_list(self, values): """ Set the model to a new list """ self.beginResetModel() self.items = values self.endResetModel() glueviz-1.0.1+dfsg.orig/glue/utils/qt/autocomplete_widget.py0000644000175000017500000000640313605357235023556 0ustar noahfxnoahfx# Code adapted from: # # http://rowinggolfer.blogspot.de/2010/08/qtextedit-with-autocompletion-using.html # # and based on: # # http://qt-project.org/doc/qt-4.8/tools-customcompleter.html from qtpy import QtGui, QtWidgets from qtpy.QtCore import Qt __all__ = ["CompletionTextEdit"] class CompletionTextEdit(QtWidgets.QTextEdit): def __init__(self, parent=None): super(CompletionTextEdit, self).__init__(parent) self.setMinimumWidth(400) self.completer = None self.word_list = None self.moveCursor(QtGui.QTextCursor.End) def set_word_list(self, word_list): self.word_list = word_list self.set_completer(QtWidgets.QCompleter(word_list)) def set_completer(self, completer): if self.completer: self.disconnect(self.completer, 0, self, 0) if not completer: return self.completer = completer self.completer.setWidget(self) self.completer.setCompletionMode(QtWidgets.QCompleter.PopupCompletion) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.activated.connect(self.insert_completion) def insert_completion(self, completion): tc = self.textCursor() existing = self.text_under_cursor() completion = completion[len(existing):] + " " self.setTextCursor(tc) self.insertPlainText(completion) self.completer.setCompletionPrefix('') def text_under_cursor(self): tc = self.textCursor() text = self.toPlainText() pos1 = tc.position() if pos1 == 0 or text[pos1 - 1] == ' ': return '' else: sub = text[:pos1] if ' ' in sub: pos2 = sub.rindex(' ') return sub[pos2 + 1:] else: return sub # The following methods override methods in QTextEdit and should not be # renamed. def focusInEvent(self, event): if self.completer: self.completer.setWidget(self) QtWidgets.QTextEdit.focusInEvent(self, event) def keyPressEvent(self, event): if self.completer and self.completer.popup().isVisible(): if event.key() in ( Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab, Qt.Key_Backtab): event.ignore() return # Check if TAB has been pressed is_shortcut = event.key() == Qt.Key_Tab if not self.completer or not is_shortcut: QtWidgets.QTextEdit.keyPressEvent(self, event) return eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=" completion_prefix = self.text_under_cursor() if not is_shortcut and (len(event.text()) == 0 or event.text()[-1:] in eow): self.completer.popup().hide() return self.completer.setCompletionPrefix(completion_prefix) popup = self.completer.popup() popup.setCurrentIndex(self.completer.completionModel().index(0, 0)) cr = self.cursorRect() cr.setWidth(self.completer.popup().sizeHintForColumn(0) + self.completer.popup().verticalScrollBar().sizeHint().width()) self.completer.complete(cr) glueviz-1.0.1+dfsg.orig/glue/utils/qt/mime.py0000644000175000017500000000311613605357235020437 0ustar noahfxnoahfxfrom qtpy import QtCore __all__ = ['PyMimeData'] class PyMimeData(QtCore.QMimeData): """ A custom MimeData object that stores live python objects Associate specific objects with a mime type by passing mime type / object key/value pairs to the __init__ method If a single object is passed to the init method, that object is associated with the PyMimeData.MIME_TYPE mime type """ MIME_TYPE = 'application/py_instance' def __init__(self, instance=None, **kwargs): """ :param instance: The python object to store kwargs: Optional mime type / objects pairs to store as objects """ super(PyMimeData, self).__init__() self._instances = {} self.setData(self.MIME_TYPE, instance) for k, v in kwargs.items(): self.setData(k, v) def formats(self): return list(set(super(PyMimeData, self).formats() + list(self._instances.keys()))) def hasFormat(self, fmt): return fmt in self._instances or super(PyMimeData, self).hasFormat(fmt) def setData(self, mime, data): super(PyMimeData, self).setData(mime, QtCore.QByteArray(1, '1')) self._instances[mime] = data def data(self, mime_type): """ Retrieve the data stored at the specified mime_type If mime_type is application/py_instance, a python object is returned. Otherwise, a QtCore.QByteArray is returned """ if str(mime_type) in self._instances: return self._instances[mime_type] return super(PyMimeData, self).data(mime_type) glueviz-1.0.1+dfsg.orig/glue/utils/qt/colors.py0000644000175000017500000001304513657331513021011 0ustar noahfxnoahfximport numpy as np from matplotlib.colors import ColorConverter from glue import config from qtpy import QtCore, QtWidgets, QtGui from echo import add_callback from glue.utils import nonpartial from glue.utils.qt.widget_properties import WidgetProperty from matplotlib import cm __all__ = ['mpl_to_qt_color', 'qt_to_mpl_color', 'cmap2pixmap', 'tint_pixmap', 'QColorBox', 'ColorProperty', 'connect_color', 'QColormapCombo'] def mpl_to_qt_color(color, alpha=None): """ Convert a matplotlib color string into a Qt QColor object Parameters ---------- color : str A color specification that matplotlib understands alpha : float Optional opacity. Float in range [0,1] Returns ------- qcolor : ``QColor`` A QColor object representing the converted color """ if color in [None, 'none', 'None']: return QtGui.QColor(0, 0, 0, 0) cc = ColorConverter() r, g, b, a = cc.to_rgba(color) if alpha is not None: a = alpha return QtGui.QColor(int(r * 255), int(g * 255), int(b * 255), int(a * 255)) def qt_to_mpl_color(qcolor): """ Convert a QColor object into a string that matplotlib understands Note: This ignores opacity Parameters ---------- qcolor : ``QColor`` The Qt color Returns ------- color : str A hex string describing that color """ hexid = qcolor.name() return str(hexid) def cmap2pixmap(cmap, steps=50, size=(100, 100)): """ Convert a matplotlib colormap into a QPixmap Parameters ---------- cmap : `~matplotlib.colors.Colormap` The colormap to use steps : int The number of color steps in the output. Default=50 Returns ------- pixmap : ``QPixmap`` The QPixmap instance """ sm = cm.ScalarMappable(cmap=cmap) sm.norm.vmin = 0.0 sm.norm.vmax = 1.0 inds = np.linspace(0, 1, steps) rgbas = sm.to_rgba(inds) rgbas = [QtGui.QColor(int(r * 255), int(g * 255), int(b * 255), int(a * 255)).rgba() for r, g, b, a in rgbas] im = QtGui.QImage(steps, 1, QtGui.QImage.Format_Indexed8) im.setColorTable(rgbas) for i in range(steps): im.setPixel(i, 0, i) im = im.scaled(*size) pm = QtGui.QPixmap.fromImage(im) return pm def tint_pixmap(bm, color): """ Re-color a monochrome pixmap object using `color` Parameters ---------- bm : ``QBitmap`` The Pixmap object color : ``QColor`` The Qt color Returns ------- pixmap : ``QPixmap`` The new pixmap """ if bm.depth() != 1: raise TypeError("Input pixmap must have a depth of 1: %i" % bm.depth()) image = bm.toImage() image.setColor(1, color.rgba()) image.setColor(0, QtGui.QColor(0, 0, 0, 0).rgba()) result = QtGui.QPixmap.fromImage(image) return result class ColorProperty(WidgetProperty): def getter(self, widget): return widget.color() def setter(self, widget, value): widget.setColor(value) def connect_color(client, prop, widget): def update_widget(text): widget.setColor(text) def update_prop(): setattr(client, prop, widget.color()) add_callback(client, prop, update_widget) widget.colorChanged.connect(nonpartial(update_prop)) update_widget(getattr(client, prop)) from echo.qt.autoconnect import HANDLERS HANDLERS['color'] = connect_color class QColorBox(QtWidgets.QLabel): mousePressed = QtCore.Signal() colorChanged = QtCore.Signal() def __init__(self, *args, **kwargs): super(QColorBox, self).__init__(*args, **kwargs) self.mousePressed.connect(nonpartial(self.query_color)) self.colorChanged.connect(nonpartial(self.on_color_change)) self.setColor("#000000") def mousePressEvent(self, event): self.mousePressed.emit() event.accept() def query_color(self): color = QtWidgets.QColorDialog.getColor(self._qcolor, parent=self) if color.isValid(): self.setColor(qt_to_mpl_color(color)) def setColor(self, color): self._color = color self.colorChanged.emit() def color(self): return self._color def on_color_change(self): self._qcolor = mpl_to_qt_color(self.color()) image = QtGui.QImage(70, 22, QtGui.QImage.Format_RGB32) try: image.fill(self._qcolor) except TypeError: # PySide and old versions of PyQt require a RGBA integer image.fill(self._qcolor.rgba()) pixmap = QtGui.QPixmap.fromImage(image) self.setPixmap(pixmap) from echo.qt.connect import UserDataWrapper class QColormapCombo(QtWidgets.QComboBox): def __init__(self, *args, **kwargs): super(QColormapCombo, self).__init__(*args, **kwargs) for label, cmap in config.colormaps: self.addItem(label, userData=UserDataWrapper(cmap)) self._update_icons() def _update_icons(self): self.setIconSize(QtCore.QSize(self.width(), 15)) for index in range(self.count()): cmap = self.itemData(index).data icon = QtGui.QIcon(cmap2pixmap(cmap, size=(self.width(), 15), steps=200)) self.setItemIcon(index, icon) def resizeEvent(self, *args, **kwargs): super(QColormapCombo, self).resizeEvent(*args, **kwargs) self._update_icons() if __name__ == "__main__": from glue.utils.qt import get_qapp app = get_qapp() label = QColorBox() label.resize(100, 100) label.show() label.raise_() app.exec_() glueviz-1.0.1+dfsg.orig/glue/utils/qt/mixins.py0000644000175000017500000000403313605357235021016 0ustar noahfxnoahfxfrom glue.utils.qt.mime import PyMimeData __all__ = ['GlueItemWidget'] class GlueItemWidget(object): """ A mixin for QtWidgets.QListWidget/GlueTreeWidget subclasses, that provides drag+drop functionality. """ # Implementation detail: QXXWidgetItems are unhashable in PySide, # and cannot be used as dictionary keys. we hash on IDs instead SUPPORTED_MIME_TYPE = None def __init__(self, parent=None): super(GlueItemWidget, self).__init__(parent) self._mime_data = {} self.setDragEnabled(True) def mimeTypes(self): """ Return the list of MIME Types supported for this object. """ types = [self.SUPPORTED_MIME_TYPE] return types def mimeData(self, selected_items): """ Return a list of MIME data associated with the each selected item. Parameters ---------- selected_items : list A list of ``QtWidgets.QListWidgetItems`` or ``QtWidgets.QTreeWidgetItems`` instances Returns ------- result : list A list of MIME objects """ try: data = [self.get_data(i) for i in selected_items] except KeyError: data = None result = PyMimeData(data, **{self.SUPPORTED_MIME_TYPE: data}) # apparent bug in pyside garbage collects custom mime # data, and crashes. Save result here to avoid self._mime = result return result def get_data(self, item): """ Convenience method to fetch the data associated with a ``QxxWidgetItem``. """ # return item.data(Qt.UserRole) return self._mime_data.get(id(item), None) def set_data(self, item, data): """ Convenience method to set data associated with a ``QxxWidgetItem``. """ # item.setData(Qt.UserRole, data) self._mime_data[id(item)] = data def drop_data(self, item): self._mime_data.pop(id(item)) @property def data(self): return self._mime_data glueviz-1.0.1+dfsg.orig/glue/utils/qt/app.py0000644000175000017500000000542213605357235020272 0ustar noahfxnoahfximport time import platform from qtpy import QtCore, QtGui, QtWidgets from glue.config import settings from glue._settings_helpers import save_settings __all__ = ['process_events', 'get_qapp', 'fix_tab_widget_fontsize', 'update_global_font_size'] qapp = None def __get_font_size_offset(): if platform.system() == 'Darwin': # On Mac, the fonts are generally too large compared to other # applications, so we reduce the default here. In future we should # make this a setting in the system preferences. size_offset = 2 else: # On other platforms, we reduce the font size by 1 point to save # space too. Again, this should probably be a global setting. size_offset = 1 return size_offset def process_events(wait=None): app = get_qapp() if wait is None: app.processEvents() else: start = time.time() while time.time() - start < wait: app.processEvents() def get_qapp(icon_path=None): global qapp qapp = QtWidgets.QApplication.instance() if qapp is None: # NOTE: plugins that need WebEngine may complain that QtWebEngineWidgets # needs to be imported before QApplication is constructed, but this can # cause segmentation faults to crop up under certain conditions, so we # don't do it here and instead ask that the plugins do it in their # main __init__.py (which should get executed before glue is launched). qapp = QtWidgets.QApplication(['']) qapp.setQuitOnLastWindowClosed(True) if icon_path is not None: qapp.setWindowIcon(QtGui.QIcon(icon_path)) size_offset = __get_font_size_offset() font = qapp.font() if settings.FONT_SIZE is None or settings.FONT_SIZE == -1: default_font = QtGui.QFont() settings.FONT_SIZE = default_font.pointSize() save_settings() point_size = settings.FONT_SIZE font.setPointSize(int(point_size - size_offset)) qapp.setFont(font) # Make sure we use high resolution icons for HDPI displays. qapp.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) return qapp def fix_tab_widget_fontsize(tab_widget): """ Because of a bug in Qt, tab titles on MacOS X don't have the right font size """ if platform.system() == 'Darwin': app = get_qapp() app_font = app.font() tab_widget.setStyleSheet('font-size: {0}px'.format(app_font.pointSize())) def update_global_font_size(): """Updates the global font size through the current UI backend """ if qapp is None: get_qapp() font = qapp.font() point_size = settings.FONT_SIZE size_offset = __get_font_size_offset() font.setPointSize(point_size - size_offset) qapp.setFont(font) glueviz-1.0.1+dfsg.orig/glue/utils/qt/delegates.py0000644000175000017500000000402413605357235021444 0ustar noahfxnoahfxfrom qtpy import QtWidgets, QtGui, QtCore class HtmlItemDelegate(QtWidgets.QStyledItemDelegate): """ An item delegate that can be used for e.g. QTreeView, QTreeWidget, QListView or QListWidget. This will automatically interpret any HTML that is inside the items in these views/widgets. This is more efficient than using e.g. QLabel instances embedded in the views/widgets, and is required for horizontal scroll bars to appear correctly. """ # Implementation adapted based on solutions presented on StackOverflow: # https://stackoverflow.com/questions/1956542/how-to-make-item-view-render-rich-html-text-in-qt def paint(self, painter, option, index): options = QtWidgets.QStyleOptionViewItem(option) self.initStyleOption(options, index) painter.save() doc = QtGui.QTextDocument() doc.setTextWidth(options.rect.width()) text_option = QtGui.QTextOption() text_option.setWrapMode(QtGui.QTextOption.NoWrap) text_option.setAlignment(options.displayAlignment) doc.setDefaultTextOption(text_option) doc.setHtml(options.text) options.text = "" options.widget.style().drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter) iconSize = options.icon.actualSize(options.rect.size()) painter.translate(options.rect.left() + iconSize.width(), options.rect.top()) clip = QtCore.QRectF(0, 0, options.rect.width() + iconSize.width(), options.rect.height()) doc.drawContents(painter, clip) painter.restore() def sizeHint(self, option, index): options = QtWidgets.QStyleOptionViewItem(option) self.initStyleOption(options, index) doc = QtGui.QTextDocument() text_option = QtGui.QTextOption() text_option.setWrapMode(QtGui.QTextOption.NoWrap) doc.setDefaultTextOption(text_option) doc.setHtml(options.text) doc.setTextWidth(options.rect.width()) return QtCore.QSize(doc.idealWidth(), doc.size().height()) glueviz-1.0.1+dfsg.orig/glue/utils/geometry.py0000644000175000017500000000755613605357235020733 0ustar noahfxnoahfximport numpy as np from glue.utils import unbroadcast, broadcast_to __all__ = ['points_inside_poly', 'polygon_line_intersections', 'floodfill'] def points_inside_poly(x, y, vx, vy): if x.dtype.kind == 'M' and vx.dtype.kind == 'M': vx = vx.astype(x.dtype).astype(float) x = x.astype(float) if y.dtype.kind == 'M' and vy.dtype.kind == 'M': vy = vy.astype(y.dtype).astype(float) y = y.astype(float) original_shape = x.shape x = unbroadcast(x) y = unbroadcast(y) x = x.astype(float) y = y.astype(float) x, y = np.broadcast_arrays(x, y) reduced_shape = x.shape x = x.flat y = y.flat from matplotlib.path import Path p = Path(np.column_stack((vx, vy))) keep = ((x >= np.min(vx)) & (x <= np.max(vx)) & (y >= np.min(vy)) & (y <= np.max(vy))) inside = np.zeros(len(x), bool) x = x[keep] y = y[keep] coords = np.column_stack((x, y)) inside[keep] = p.contains_points(coords).astype(bool) good = np.isfinite(x) & np.isfinite(y) inside[keep][~good] = False inside = inside.reshape(reduced_shape) inside = broadcast_to(inside, original_shape) return inside def polygon_line_intersections(px, py, xval=None, yval=None): """ Find all the segments of intersection between a polygon and an infinite horizontal/vertical line. The polygon is assumed to be closed. Due to numerical precision, the behavior at the edges of polygons is not always predictable, i.e. a point on the edge of a polygon may be considered inside or outside the polygon. Parameters ---------- px, py : `~numpy.ndarray` The vertices of the polygon xval : float, optional The x coordinate of the line (for vertical lines). This should only be specified if yval is not specified. yval : float, optional The y coordinate of the line (for horizontal lines). This should only be specified if xval is not specified. Returns ------- segments : list A list of segments given as tuples of coordinates along the line. """ if xval is not None and yval is not None: raise ValueError("Only one of xval or yval should be specified") elif xval is None and yval is None: raise ValueError("xval or yval should be specified") if yval is not None: return polygon_line_intersections(py, px, xval=yval) px = np.asarray(px, dtype=float) py = np.asarray(py, dtype=float) # Make sure that the polygon is closed if px[0] != px[-1] or py[0] != py[-1]: px = np.hstack([px, px[0]]) py = np.hstack([py, py[0]]) # For convenience x1, x2 = px[:-1], px[1:] y1, y2 = py[:-1], py[1:] # Vertices that intersect keep1 = (px == xval) points1 = py[keep1] # Segments (excluding vertices) that intersect keep2 = ((x1 < xval) & (x2 > xval)) | ((x2 < xval) & (x1 > xval)) points2 = (y1 + (y2 - y1) * (xval - x1) / (x2 - x1))[keep2] # Make unique and sort points = np.array(np.sort(np.unique(np.hstack([points1, points2])))) # Because of various corner cases, we don't actually know which pairs of # points are inside the polygon, so we check this using the mid-points ymid = 0.5 * (points[:-1] + points[1:]) xmid = np.repeat(xval, len(ymid)) keep = points_inside_poly(xmid, ymid, px, py) segments = list(zip(points[:-1][keep], points[1:][keep])) return segments def floodfill(data, start_coords, threshold): from scipy.ndimage.measurements import label # Determine value at the starting coordinates value = data[start_coords] # Determine all pixels that match mask = (data > value * (2 - threshold)) & (data < value * threshold) # Determine all individual chunks labels, num_features = label(mask) mask = labels == labels[start_coords] return mask glueviz-1.0.1+dfsg.orig/glue/utils/decorators.py0000644000175000017500000000233713605357235021235 0ustar noahfxnoahfximport inspect import traceback __all__ = ['die_on_error', 'avoid_circular', 'decorate_all_methods'] def die_on_error(msg): """ Non-GUI version of the decorator in glue.utils.qt.decorators. In this case we just let the Python exception terminate the execution. """ def decorator(func): def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception as e: print('=' * 72) print(msg + ' (traceback below)') print('-' * 72) traceback.print_exc() print('=' * 72) return wrapper return decorator def avoid_circular(meth): def wrapper(self, *args, **kwargs): if not hasattr(self, '_in_avoid_circular') or not self._in_avoid_circular: self._in_avoid_circular = True try: return meth(self, *args, **kwargs) finally: self._in_avoid_circular = False return wrapper def decorate_all_methods(decorator): def decorate(cls): for name, value in inspect.getmembers(cls, inspect.isfunction): setattr(cls, name, decorator(value)) return cls return decorate glueviz-1.0.1+dfsg.orig/glue/utils/matplotlib.py0000644000175000017500000003616613661230662021242 0ustar noahfxnoahfximport logging import warnings from functools import wraps import numpy as np import matplotlib.units as units import matplotlib.dates as dates from matplotlib.legend_handler import HandlerBase from matplotlib.patches import Rectangle # We avoid importing matplotlib up here otherwise Matplotlib and therefore Qt # get imported as soon as glue.utils is imported. from glue.external.axescache import AxesCache from glue.utils.misc import DeferredMethod __all__ = ['renderless_figure', 'all_artists', 'new_artists', 'remove_artists', 'get_extent', 'view_cascade', 'fast_limits', 'defer_draw', 'color2rgb', 'point_contour', 'cache_axes', 'datetime64_to_mpl', 'mpl_to_datetime64', 'color2hex'] def renderless_figure(): # Matplotlib figure that skips the render step, for test speed from unittest.mock import MagicMock import matplotlib.pyplot as plt fig = plt.figure() fig.canvas.draw = MagicMock() plt.close('all') return fig def all_artists(fig): """ Build a set of all Matplotlib artists in a Figure """ return set(item for axes in fig.axes for container in [axes.collections, axes.patches, axes.lines, axes.texts, axes.artists, axes.images] for item in container) def new_artists(fig, old_artists): """ Find the newly-added artists in a figure :param fig: Matplotlib figure :param old_artists: Return value from :func:all_artists :returns: All artists added since all_artists was called """ return all_artists(fig) - old_artists def remove_artists(artists): """ Remove a collection of matplotlib artists from a scene :param artists: Container of artists """ for a in artists: try: a.remove() except ValueError: # already removed pass def get_extent(view, transpose=False): sy, sx = [s for s in view if isinstance(s, slice)] if transpose: return (sy.start, sy.stop, sx.start, sx.stop) return (sx.start, sx.stop, sy.start, sy.stop) def view_cascade(data, view): """ Return a set of views progressively zoomed out of input at roughly constant pixel count Parameters ---------- data : array-like The array to view view : The original view into the data """ shp = data.shape v2 = list(view) logging.debug("image shape: %s, view: %s", shp, view) # choose stride length that roughly samples entire image # at roughly the same pixel count step = max(shp[i - 1] * v.step // max(v.stop - v.start, 1) for i, v in enumerate(view) if isinstance(v, slice)) step = max(step, 1) for i, v in enumerate(v2): if not(isinstance(v, slice)): continue v2[i] = slice(0, shp[i - 1], step) return tuple(v2), view def _scoreatpercentile(values, percentile, limit=None): # Avoid using the scipy version since it is available in Numpy if limit is not None: values = values[(values >= limit[0]) & (values <= limit[1])] return np.percentile(values, percentile) def fast_limits(data, plo, phi): """ Quickly estimate percentiles in an array, using a downsampled version Parameters ---------- data : `numpy.ndarray` The array to estimate the percentiles for plo, phi : float The percentile values Returns ------- lo, hi : float The percentile values """ shp = data.shape view = tuple([slice(None, None, np.intp(max(s / 50, 1))) for s in shp]) values = np.asarray(data)[view] if ~np.isfinite(values).any(): return (0.0, 1.0) limits = (-np.inf, np.inf) lo = _scoreatpercentile(values.flat, plo, limit=limits) hi = _scoreatpercentile(values.flat, phi, limit=limits) return lo, hi # We don't know in advance what backends are going to be used for Matplotlib # so we can set up a list here and use it in defer_draw below, and each front- # end is responsible for adding their backend here. DEFER_DRAW_BACKENDS = [] def defer_draw(func): """ Decorator that globally defers all Agg canvas draws until function exit. If a Canvas instance's draw method is invoked multiple times, it will only be called once after the wrapped function returns. """ @wraps(func) def wrapper(*args, **kwargs): if len(DEFER_DRAW_BACKENDS) == 0: return func(*args, **kwargs) # Don't recursively defer draws. We just check the first draw_idle # method since all should be modified in sync. if isinstance(DEFER_DRAW_BACKENDS[0].draw_idle, DeferredMethod): return func(*args, **kwargs) try: for backend in DEFER_DRAW_BACKENDS: backend.draw_idle = DeferredMethod(backend.draw_idle) result = func(*args, **kwargs) finally: for backend in DEFER_DRAW_BACKENDS: # We need to use another try...finally block here in case the # executed deferred draw calls fail for any reason try: try: backend.draw_idle.execute_deferred_calls() except RuntimeError: # For C/C++ errors with Qt pass finally: backend.draw_idle = backend.draw_idle.original_method return result wrapper._is_deferred = True return wrapper def color2rgb(color): from matplotlib.colors import ColorConverter result = ColorConverter().to_rgb(color) return result def color2hex(color): try: from matplotlib.colors import to_hex result = to_hex(color) except ImportError: # MPL 1.5 from matplotlib.colors import ColorConverter, rgb2hex result = rgb2hex(ColorConverter().to_rgb(color)) return result def point_contour(x, y, data): """Calculate the contour that passes through (x,y) in data :param x: x location :param y: y location :param data: 2D image :type data: :class:`numpy.ndarray` Returns: * A (nrow, 2column) numpy array. The two columns give the x and y locations of the contour vertices """ try: from scipy.ndimage import label, binary_fill_holes from skimage.measure import find_contours except ImportError: raise ImportError("Image processing in Glue requires SciPy and scikit-image") # Find the intensity of the selected pixel inten = data[y, x] # Find all 'islands' above this intensity labeled, nr_objects = label(data >= inten) # Pick the object we clicked on z = (labeled == labeled[y, x]) # Fill holes inside it so we don't get 'inner' contours z = binary_fill_holes(z).astype(float) # Pad the resulting array so that for contours that go to the edge we get # one continuous contour z = np.pad(z, 1, mode='constant') # Finally find the contours around the island xy = find_contours(z, 0.5, fully_connected='high') if not xy: return None if len(xy) > 1: warnings.warn("Too many contours found, picking the first one") # We need to flip the array to get (x, y), and subtract one to account for # the padding return xy[0][:, ::-1] - 1 class AxesResizer(object): def __init__(self, ax, margins): self.ax = ax self.margins = margins @property def margins(self): return self._margins @margins.setter def margins(self, margins): self._margins = margins def on_resize(self, event): fig_width = self.ax.figure.get_figwidth() fig_height = self.ax.figure.get_figheight() x0 = self.margins[0] / fig_width x1 = 1 - self.margins[1] / fig_width y0 = self.margins[2] / fig_height y1 = 1 - self.margins[3] / fig_height dx = max(0.01, x1 - x0) dy = max(0.01, y1 - y0) self.ax.set_position([x0, y0, dx, dy]) self.ax.figure.canvas.draw_idle() def freeze_margins(axes, margins=[1, 1, 1, 1]): """ Make sure margins of axes stay fixed. Parameters ---------- ax_class : matplotlib.axes.Axes The axes class for which to fix the margins margins : iterable The margins, in inches. The order of the margins is ``[left, right, bottom, top]`` Notes ----- The object that controls the resizing is stored as the resizer attribute of the Axes. This can be used to then change the margins: >> ax.resizer.margins = [0.5, 0.5, 0.5, 0.5] """ axes.resizer = AxesResizer(axes, margins) axes.figure.canvas.mpl_connect('resize_event', axes.resizer.on_resize) def cache_axes(axes, toolbar): """ Set up caching for an axes object. After this, cached renders will be used to quickly re-render an axes during window resizing or interactive pan/zooming. This function returns an AxesCache instance. Parameters ---------- axes : `~matplotlib.axes.Axes` The axes to cache toolbar : `~glue.viewers.common.qt.toolbar.GlueToolbar` The toolbar managing the axes' canvas """ canvas = axes.figure.canvas cache = AxesCache(axes) canvas.resize_begin.connect(cache.enable) canvas.resize_end.connect(cache.disable) toolbar.pan_begin.connect(cache.enable) toolbar.pan_end.connect(cache.disable) return cache class ColormapPatchHandler(HandlerBase): def __init__(self, cmap, nb_subpatch=10, xpad=0.0, ypad=0.0): """ A custom legend handler to represent 2D dataset coded in colormaps Parameters ---------- cmap : `~matplotlib.colors.colormap` The matplotlib colormap to use nb_subpatch : int, optional The number of stripes to use to represent the colormap. The default is 10. xpad : float, optional Padding in the x direction. The default is 0.0. ypad : float, optional Padding in the y direction. The default is 0.0. """ super().__init__(xpad, ypad) self.nb_subpatch = nb_subpatch self.cmap = cmap def create_artists(self, legend, orig_handle, xdescent, ydescent, width, height, fontsize, trans): collection = [] for i in range(self.nb_subpatch): width_sub = width / self.nb_subpatch x = xdescent + i * width_sub collection.append( Rectangle((x, ydescent), width_sub, height, transform=trans, facecolor=self.cmap(i / (self.nb_subpatch - 1)), edgecolor="none")) return collection # In Matplotlib < 2.2, there is no datetime64 support, so we register a converter # here to deal with it with older versions. class Datetime64Converter(units.ConversionInterface): @staticmethod def convert(value, unit, axis): value = np.asarray(value) if value.dtype.kind == 'M': return datetime64_to_mpl(value) else: return value @staticmethod def axisinfo(unit, axis): majloc = dates.AutoDateLocator() majfmt = dates.AutoDateFormatter(majloc) return units.AxisInfo(majloc=majloc, majfmt=majfmt) @staticmethod def default_units(x, axis): return None units.registry[np.datetime64] = Datetime64Converter() # The following code is copied from the developer version of Matplotlib # for compatibility with older versions. The following is the license # agreement for Matplotlib: # # 1. This LICENSE AGREEMENT is between the Matplotlib Development Team # ("MDT"), and the Individual or Organization ("Licensee") accessing and # otherwise using matplotlib software in source or binary form and its # associated documentation. # # 2. Subject to the terms and conditions of this License Agreement, MDT # hereby grants Licensee a nonexclusive, royalty-free, world-wide license # to reproduce, analyze, test, perform and/or display publicly, prepare # derivative works, distribute, and otherwise use matplotlib # alone or in any derivative version, provided, however, that MDT's # License Agreement and MDT's notice of copyright, i.e., "Copyright (c) # 2012- Matplotlib Development Team; All Rights Reserved" are retained in # matplotlib alone or in any derivative version prepared by # Licensee. # # 3. In the event Licensee prepares a derivative work that is based on or # incorporates matplotlib or any part thereof, and wants to # make the derivative work available to others as provided herein, then # Licensee hereby agrees to include in any such work a brief summary of # the changes made to matplotlib . # # 4. MDT is making matplotlib available to Licensee on an "AS # IS" basis. MDT MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR # IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDT MAKES NO AND # DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS # FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB # WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. # # 5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB # FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR # LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING # MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF # THE POSSIBILITY THEREOF. # # 6. This License Agreement will automatically terminate upon a material # breach of its terms and conditions. # # 7. Nothing in this License Agreement shall be deemed to create any # relationship of agency, partnership, or joint venture between MDT and # Licensee. This License Agreement does not grant permission to use MDT # trademarks or trade name in a trademark sense to endorse or promote # products or services of Licensee, or any third party. # # 8. By copying, installing or otherwise using matplotlib , # Licensee agrees to be bound by the terms and conditions of this License # Agreement. HOURS_PER_DAY = 24. MIN_PER_HOUR = 60. SEC_PER_MIN = 60. SEC_PER_HOUR = SEC_PER_MIN * MIN_PER_HOUR SEC_PER_DAY = SEC_PER_HOUR * HOURS_PER_DAY T0 = np.datetime64('0001-01-01T00:00:00').astype('datetime64[s]') def datetime64_to_mpl(d): """ Convert `numpy.datetime64` or an ndarray of those types to Gregorian date as UTC float. The precision is limited to float64 precision. Practically: microseconds for dates between 290301 BC, 294241 AD, milliseconds for larger dates (see `numpy.datetime64`). Nanoseconds aren't possible because we do times compared to ``0001-01-01T00:00:00`` (plus one day). """ # the "extra" ensures that we at least allow the dynamic range out to # seconds. That should get out to +/-2e11 years. extra = d - d.astype('datetime64[s]') extra = extra.astype('timedelta64[ns]') dt = (d.astype('datetime64[s]') - T0).astype(np.float64) dt += extra.astype(np.float64) / 1.0e9 dt = dt / SEC_PER_DAY + 1.0 return dt def mpl_to_datetime64(dt): dt = np.asarray(dt, np.float64) dt = (dt - 1.0) * SEC_PER_DAY dt_s = dt.astype(np.int64) + T0.astype(np.int64) dt_ns = ((dt % 1) * 1e9).astype(np.int64) dt_s = np.array(dt_s, dtype='datetime64[s]') dt_ns = np.array(dt_ns, dtype='timedelta64[ns]') return dt_s + dt_ns glueviz-1.0.1+dfsg.orig/glue/utils/array.py0000644000175000017500000004557413660506557020224 0ustar noahfxnoahfximport warnings import numpy as np from numpy.lib.stride_tricks import as_strided import pandas as pd import bottleneck as bt __all__ = ['unique', 'shape_to_string', 'view_shape', 'stack_view', 'coerce_numeric', 'check_sorted', 'broadcast_to', 'unbroadcast', 'iterate_chunks', 'combine_slices', 'nanmean', 'nanmedian', 'nansum', 'nanmin', 'nanmax', 'format_minimal', 'compute_statistic', 'categorical_ndarray', 'index_lookup', 'ensure_numerical', 'broadcast_arrays_minimal', 'random_views_for_dask_array'] def unbroadcast(array): """ Given an array, return a new array that is the smallest subset of the original array that can be re-broadcasted back to the original array. See https://stackoverflow.com/questions/40845769/un-broadcasting-numpy-arrays for more details. """ if array.ndim == 0 or not hasattr(array, 'strides'): return array new_shape = np.where(np.array(array.strides) == 0, 1, array.shape) return as_strided(array, shape=new_shape) def broadcast_arrays_minimal(*arrays): """ Unbroadcast arrays then broadcast to smallest common shape. """ return np.broadcast_arrays(*[unbroadcast(array) for array in arrays]) def unique(array): """ Return the unique elements of the array U, as well as the index array I such that U[I] == array Parameters ---------- array : `numpy.ndarray` The array to use Returns ------- U : `numpy.ndarray` The unique elements of the array I : `numpy.ndarray` The indices such that ``U[I] == array`` """ # numpy.unique doesn't handle mixed-types on python3, # so we use pandas array = np.asarray(array) I, U = pd.factorize(array, sort=True) return U.astype(array.dtype), I def shape_to_string(shape): """ On Windows, shape tuples use long ints which results in formatted shapes such as (2L, 3L). This function ensures that the shape is always formatted without the Ls. """ return "({0})".format(", ".join(str(int(item)) for item in shape)) def view_shape(shape, view): """ Return the shape of a view of an array. Returns equivalent of ``np.zeros(shape)[view].shape`` but with minimal memory usage. Parameters ---------- shape : tuple The shape of the array view : slice A valid index into a Numpy array, or None """ if view is None: return shape else: return np.broadcast_to(1, shape)[view].shape def stack_view(shape, *views): shp = tuple(slice(0, s, 1) for s in shape) result = np.broadcast_arrays(*np.ogrid[shp]) for v in views: if isinstance(v, str) and v == 'transpose': result = [r.T for r in result] continue result = [r[v] for r in result] return tuple(result) def coerce_numeric(arr): """ Coerce an array into a numeric array, replacing non-numeric elements with nans. If the array is already a numeric type, it is returned unchanged Parameters ---------- arr : `numpy.ndarray` The array to coerce """ # Already numeric type if np.issubdtype(arr.dtype, np.number): return arr # Numpy datetime64 format if np.issubdtype(arr.dtype, np.datetime64): return arr # Convert booleans to integers if np.issubdtype(arr.dtype, np.bool_): return arr.astype(np.int) # a string dtype, or anything else try: return pd.to_numeric(arr, errors='coerce') except AttributeError: # pandas < 0.19 return pd.Series(arr).convert_objects(convert_numeric=True).values def check_sorted(array): """ Return `True` if the array is sorted, `False` otherwise. """ # this ignores NANs, and does the right thing if nans # are concentrated at beginning or end of array # otherwise, it will miss things at nan/finite boundaries array = np.asarray(array) return not (array[:-1] > array[1:]).any() def pretty_number(numbers): """ Convert a list/array of numbers into a nice list of strings Parameters ---------- numbers : list The numbers to convert """ try: return [pretty_number(n) for n in numbers] except TypeError: pass n = numbers if n == 0: result = '0' elif (abs(n) < 1e-3) or (abs(n) > 1e3): result = "%0.3e" % n elif abs(int(n) - n) < 1e-3 and int(n) != 0: result = "%i" % n else: result = "%0.3f" % n if result.find('.') != -1: result = result.rstrip('0') return result def broadcast_to(array, shape): """ Compatibility function - can be removed once we support only Numpy 1.10 and above """ try: return np.broadcast_to(array, shape) except AttributeError: array = np.asarray(array) return np.broadcast_arrays(array, np.ones(shape, array.dtype))[0] def find_chunk_shape(shape, n_max=None): """ Given the shape of an n-dimensional array, and the maximum number of elements in a chunk, return the largest chunk shape to use for iteration. This currently assumes the optimal chunk shape to return is for C-contiguous arrays. """ if n_max is None: return tuple(shape) block_shape = [] max_repeat_remaining = n_max for size in shape[::-1]: if max_repeat_remaining > size: block_shape.append(size) max_repeat_remaining = max_repeat_remaining // size else: block_shape.append(max_repeat_remaining) max_repeat_remaining = 1 return tuple(block_shape[::-1]) def iterate_chunks(shape, chunk_shape=None, n_max=None): """ Given a data shape and a chunk shape (or maximum chunk size), iteratively return slice objects that can be used to slice the array. """ # Shortcut - if there are any 0 elements in the shape, there are no # chunks to iterate over. if np.prod(shape) == 0: return if chunk_shape is None and n_max is None: raise ValueError('Either chunk_shape or n_max should be specified') elif chunk_shape is not None and n_max is not None: raise ValueError('Either chunk_shape or n_max should be specified (not both)') elif chunk_shape is None: chunk_shape = find_chunk_shape(shape, n_max) else: if len(chunk_shape) != len(shape): raise ValueError('chunk_shape should have the same length as shape') elif any(x > y for (x, y) in zip(chunk_shape, shape)): raise ValueError('chunk_shape should fit within shape') ndim = len(chunk_shape) start_index = [0] * ndim shape = list(shape) while start_index <= shape: end_index = [min(start_index[i] + chunk_shape[i], shape[i]) for i in range(ndim)] slices = tuple([slice(start_index[i], end_index[i]) for i in range(ndim)]) yield slices # Update chunk index. What we do is to increment the # counter for the first dimension, and then if it # exceeds the number of elements in that direction, # cycle back to zero and advance in the next dimension, # and so on. start_index[0] += chunk_shape[0] for i in range(ndim - 1): if start_index[i] >= shape[i]: start_index[i] = 0 start_index[i + 1] += chunk_shape[i + 1] # We can now check whether the iteration is finished if start_index[-1] >= shape[-1]: break def combine_slices(slice1, slice2, length): """ Given two slices that can be applied to a 1D array and the length of that array, this returns a new slice which is the one that should be applied to the array instead of slice2 if slice1 has already been applied. """ beg1, end1, step1 = slice1.indices(length) beg2, end2, step2 = slice2.indices(length) if step1 < 0 or step2 < 0: raise ValueError("combine_slices does not support slices with negative step") if beg2 >= end1 or end2 <= beg1: return slice(0, 0, 1) beg = max(beg1, beg2) end = min(end1, end2) if (beg - beg2) % step2 != 0: beg += step2 - ((beg - beg2) % step2) # Now we want to find the two first overlap indices inside the overlap # range. Loop over indices of second slice (but with min/max constraints # of first added) and check if they are valid indices given slice1 indices = [] for idx in range(beg, end, step2): if (idx - beg1) % step1 == 0: indices.append((idx - beg1) // step1) if len(indices) == 2: break if len(indices) == 0: return slice(0, 0, 1) elif len(indices) == 1: return slice(indices[0], indices[0] + 1, 1) else: end_new = (end - beg1) // step1 if (end - beg1) % step1 != 0: end_new += 1 return slice(indices[0], end_new, indices[1] - indices[0]) def _move_tuple_axes_first(array, axis): """ Bottleneck can only take integer axis, not tuple, so this function takes all the axes to be operated on and combines them into the first dimension of the array so that we can then use axis=0 """ # Figure out how many axes we are operating over naxis = len(axis) # Add remaining axes to the axis tuple axis += tuple(i for i in range(array.ndim) if i not in axis) # The new position of each axis is just in order destination = tuple(range(array.ndim)) # Reorder the array so that the axes being operated on are at the beginning array_new = np.moveaxis(array, axis, destination) # Figure out the size of the product of the dimensions being operated on first = np.prod(array_new.shape[:naxis]) # Collapse the dimensions being operated on into a single dimension so that # we can then use axis=0 with the bottleneck functions array_new = array_new.reshape((first,) + array_new.shape[naxis:]) return array_new def nanmean(array, axis=None): if isinstance(axis, tuple): array = _move_tuple_axes_first(array, axis=axis) axis = 0 return bt.nanmean(array, axis=axis) def nanmedian(array, axis=None): if isinstance(axis, tuple): array = _move_tuple_axes_first(array, axis=axis) axis = 0 return bt.nanmedian(array, axis=axis) def nansum(array, axis=None): if isinstance(axis, tuple): array = _move_tuple_axes_first(array, axis=axis) axis = 0 return bt.nansum(array, axis=axis) def nanmin(array, axis=None): if isinstance(axis, tuple): array = _move_tuple_axes_first(array, axis=axis) axis = 0 return bt.nanmin(array, axis=axis) def nanmax(array, axis=None): if isinstance(axis, tuple): array = _move_tuple_axes_first(array, axis=axis) axis = 0 return bt.nanmax(array, axis=axis) def format_minimal(values): """ Find the shortest format that can be used to represent all values in an array such that all the string representations are different. The current implementation is not incredibly efficient, but it takes only ~30ms for a 1000 element array and 200ms for a 10000 element array. One could probably make a more efficient implementation but this is good enough for now for what we use it for. Returns the optimal format as well as an array of formatted values. """ values = np.asarray(values) if np.max(np.abs(values)) > 1e5 or np.min(np.diff(values)) < 1e-5: fmt_type = 'e' else: fmt_type = 'f' for ndec in range(1, 15): fmt = '{{:.{0}{1}}}'.format(ndec, fmt_type) strings = [fmt.format(x) for x in values] if len(strings) == len(set(strings)): break return fmt, strings PLAIN_FUNCTIONS = {'minimum': np.min, 'maximum': np.max, 'mean': np.mean, 'median': np.median, 'sum': np.sum, 'percentile': np.percentile} NAN_FUNCTIONS = {'minimum': nanmin, 'maximum': nanmax, 'mean': nanmean, 'median': nanmedian, 'sum': nansum, 'percentile': np.nanpercentile} def compute_statistic(statistic, data, mask=None, axis=None, finite=True, positive=False, percentile=None): """ Compute a statistic for the data. Parameters ---------- statistic : {'minimum', 'maximum', 'mean', 'median', 'sum', 'percentile'} The statistic to compute data : `numpy.ndarray` The data to compute the statistic for. mask : `numpy.ndarray` The mask to apply when computing the statistic. axis : None or int or tuple of int If specified, the axis/axes to compute the statistic over. finite : bool, optional Whether to include only finite values in the statistic. This should be `True` to ignore NaN/Inf values positive : bool, optional Whether to include only (strictly) positive values in the statistic. This is used for example when computing statistics of data shown in log space. percentile : float, optional If ``statistic`` is ``'percentile'``, the ``percentile`` argument should be given and specify the percentile to calculate in the range [0:100] """ data = np.asanyarray(data) if mask is not None: mask = np.asanyarray(mask, dtype=bool) # NOTE: this function should not ever have to use glue-specific objects. # The aim is to eventually use a fast C implementation of this function. if statistic not in PLAIN_FUNCTIONS: raise ValueError("Unrecognized statistic: {0}".format(statistic)) if (finite or positive or mask is not None) and data.dtype.kind != 'M': keep = np.ones(data.shape, dtype=bool) if finite: keep &= np.isfinite(data) if positive: keep &= data > 0 if mask is not None: keep &= mask if axis is None: data = data[keep] else: # We need to force a copy since we are editing the values and we # might as well convert to float just in case data = np.array(data, dtype=float) data[~keep] = np.nan function = NAN_FUNCTIONS[statistic] else: function = PLAIN_FUNCTIONS[statistic] if data.size == 0: return np.nan if isinstance(axis, tuple) and len(axis) == 0: return data with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) if statistic == 'percentile': return function(data, percentile, axis=axis) else: return function(data, axis=axis) class categorical_ndarray(np.ndarray): """ A Numpy array subclass that includes properties to find the categories and unique integer codes for array values. """ _jitter = None def __new__(cls, value, dtype=None, copy=True, order=None, subok=False, ndmin=0, categories=None): result = np.array(value, dtype=dtype, copy=copy, order=order, subok=True, ndmin=ndmin).view(categorical_ndarray) if categories is not None: result.categories = categories return result def __array_finalize__(self, obj): if isinstance(obj, categorical_ndarray): self.categories = obj.categories def _update_categories_and_codes(self): if hasattr(self, '_categories'): self._codes = index_lookup(self, self._categories) else: self._categories, self._codes = unique(self) self._categories.setflags(write=False) self._codes = self._codes.astype(float) self._codes.setflags(write=False) @property def categories(self): if not hasattr(self, '_categories'): self._update_categories_and_codes() return self._categories @categories.setter def categories(self, value): self._categories = value @property def codes(self): if not hasattr(self, '_codes'): self._update_categories_and_codes() if self._jitter is None: return self._codes else: return self._codes + self._jitter def jitter(self, method=None): """ Jitter the codes. Parameters ---------- method : {None, 'uniform'} If `None`, not jittering is done (or any jittering is undone). If ``'uniform'``, the codes are randomized by a uniformly distributed random variable. """ if method is None: self._jitter = None elif method == 'uniform': self._jitter = np.random.random(self.shape) self._jitter -= 0.5 else: raise ValueError("method should be None or 'uniform'") def ensure_numerical(values): if isinstance(values, categorical_ndarray): return values.codes else: return values def index_lookup(data, items): """ Lookup which index in items each data value is equal to Parameters ---------- data An array-like object items Array-like of unique values Returns ------- array If result[i] is finite, then data[i] = categories[result[i]] Otherwise, data[i] is not in the categories list """ # np.searchsorted doesn't work on mixed types in Python3 ndata, ncat = len(data), len(items) data = pd.DataFrame({'data': data, 'row': np.arange(ndata)}) cats = pd.DataFrame({'items': items, 'cat_row': np.arange(ncat)}) m = pd.merge(data, cats, left_on='data', right_on='items') result = np.zeros(ndata, dtype=float) * np.nan result[np.array(m.row)] = m.cat_row return result def random_views_for_dask_array(array, n_random_samples, n_chunks): """ Return a list of views to extract random values from a dask array in an efficient way taking into account the chunk layout. This will return n_chunks views such that all views together add up to approximately n_random_samples samples. """ # Find the indices of the chunks to extract indices = [np.random.randint(dimsize, size=n_chunks) for dimsize in array.numblocks] # Determine the boundaries of chunks along each dimension chunk_indices = [np.hstack([0, np.cumsum([size for size in sizes])]) for sizes in array.chunks] n_per_chunk = n_random_samples // n_chunks all_slices = [] for ichunk in range(n_chunks): slices = [] remaining_size = n_per_chunk for idim in range(array.ndim): start = chunk_indices[idim][indices[idim][ichunk]] stop = chunk_indices[idim][indices[idim][ichunk] + 1] if stop - start > remaining_size: stop = start + remaining_size slices.append(slice(start, stop)) remaining_size //= (stop - start) remaining_size = max(1, remaining_size) all_slices.append(tuple(slices)) return all_slices glueviz-1.0.1+dfsg.orig/glue/utils/__init__.py0000644000175000017500000000074413613546740020627 0ustar noahfxnoahfx""" General utilities not specifically related to data linking (e.g. WCS or matplotlib helper functions). Utilities here cannot import from anywhere else in glue, with the exception of glue.external, and can only import standard library or external dependencies. """ from .array import * # noqa from .matplotlib import * # noqa from .misc import * # noqa from .geometry import * # noqa from .colors import * # noqa from .decorators import * # noqa from .data import * # noqa glueviz-1.0.1+dfsg.orig/glue/utils/error.py0000644000175000017500000000030013605357235020205 0ustar noahfxnoahfx class GlueDeprecationWarning(UserWarning): """ Deprecation warnings for glue - this inherits from UserWarning not DeprecationWarning, to make sure it is shown by default. """ glueviz-1.0.1+dfsg.orig/glue/utils/noconflict.py0000644000175000017500000000423213605357235021222 0ustar noahfxnoahfx# Code adapted from: # # http://code.activestate.com/recipes/204197-solving-the-metaclass-conflict/ # # The code at the above URL was released under the PSF license. import inspect import builtins # noqa CLASS_TYPE = type __all__ = ['classmaker'] def skip_redundant(iterable, skipset=None): """ Redundant items are repeated items or items in the original skipset. """ if skipset is None: skipset = set() for item in iterable: if item not in skipset: skipset.add(item) yield item def remove_redundant(metaclasses): skipset = set([CLASS_TYPE]) for meta in metaclasses: # determines the metaclasses to be skipped skipset.update(inspect.getmro(meta)[1:]) return tuple(skip_redundant(metaclasses, skipset)) memoized_metaclasses_map = {} def get_noconflict_metaclass(bases, left_metas, right_metas): """ Not intended to be used outside of this module, unless you know what you are doing. """ # make tuple of needed metaclasses in specified priority order metas = left_metas + tuple(map(type, bases)) + right_metas needed_metas = remove_redundant(metas) # return existing confict-solving meta, if any if needed_metas in memoized_metaclasses_map: return memoized_metaclasses_map[needed_metas] # nope: compute, memoize and return needed conflict-solving meta elif not needed_metas: # wee, a trivial case, happy us meta = type elif len(needed_metas) == 1: # another trivial case meta = needed_metas[0] # check for recursion, can happen i.e. for Zope ExtensionClasses elif needed_metas == bases: raise TypeError("Incompatible root metatypes", needed_metas) else: # gotta work ... metaname = '_' + ''.join([m.__name__ for m in needed_metas]) meta = classmaker()(metaname, needed_metas, {}) memoized_metaclasses_map[needed_metas] = meta return meta def classmaker(left_metas=(), right_metas=()): def make_class(name, bases, adict): metaclass = get_noconflict_metaclass( bases, left_metas, right_metas) return metaclass(name, bases, adict) return make_class glueviz-1.0.1+dfsg.orig/glue/utils/data.py0000644000175000017500000000126213613546740017775 0ustar noahfxnoahfxfrom urllib.request import urlopen __all__ = ['require_data'] DATA_REPO = "https://raw.githubusercontent.com/glue-viz/glue-example-data/master/" def require_data(file_path): """ Download the specified file to the current folder, preserving the directory structure. Note that this should include forward slashes for paths even on Windows. """ # We use urlopen instead of urlretrieve to have control over the timeout local_path = file_path.split('/')[-1] request = urlopen(DATA_REPO + file_path, timeout=60) with open(local_path, 'wb') as f: f.write(request.read()) print("Successfully downloaded data file to {0}".format(local_path)) glueviz-1.0.1+dfsg.orig/glue/utils/colors.py0000644000175000017500000000151613605357235020367 0ustar noahfxnoahfxfrom matplotlib.colors import ColorConverter __all__ = ['alpha_blend_colors'] COLOR_CONVERTER = ColorConverter() def alpha_blend_colors(colors, additional_alpha=1.0): """ Given a sequence of colors, return the alpha blended color. This assumes the last color is the one in front. """ srcr, srcg, srcb, srca = COLOR_CONVERTER.to_rgba(colors[0]) srca *= additional_alpha for color in colors[1:]: dstr, dstg, dstb, dsta = COLOR_CONVERTER.to_rgba(color) dsta *= additional_alpha outa = srca + dsta * (1 - srca) outr = (srcr * srca + dstr * dsta * (1 - srca)) / outa outg = (srcg * srca + dstg * dsta * (1 - srca)) / outa outb = (srcb * srca + dstb * dsta * (1 - srca)) / outa srca, srcr, srcg, srcb = outa, outr, outg, outb return srcr, srcg, srcb, srca glueviz-1.0.1+dfsg.orig/glue/utils/misc.py0000644000175000017500000001501213657331513020013 0ustar noahfxnoahfximport queue import string from functools import partial, reduce from echo.callback_container import CallbackContainer __all__ = ['DeferredMethod', 'nonpartial', 'lookup_class', 'as_variable_name', 'as_list', 'file_format', 'CallbackMixin', 'PropertySetMixin', 'Pointer', 'common_prefix', 'queue_to_list', 'format_choices'] class DeferredMethod(object): """ This class stubs out a method, and provides a callable interface that logs its calls. These can later be actually executed on the original (non-stubbed) method by calling executed_deferred_calls """ def __init__(self, method): self.method = method self.calls = [] # avoid hashability issues with dict/set @property def original_method(self): return self.method def __call__(self, instance, *a, **k): if instance not in (c[0] for c in self.calls): self.calls.append((instance, a, k)) def __get__(self, instance, owner): if instance is None: return self return partial(self.__call__, instance) def execute_deferred_calls(self): for instance, args, kwargs in self.calls: self.method(instance, *args, **kwargs) def nonpartial(func, *args, **kwargs): """ Like functools.partial, this returns a function which, when called, calls ``func(*args, **kwargs)``. Unlike functools.partial, extra arguments passed to the returned function are *not* passed to the input function. This is used when connecting slots to ``QAction.triggered`` signals, which appear to have different signatures, which seem to add and extra argument in PyQt but not PySide """ def result(*a, **k): return func(*args, **kwargs) return result def lookup_class(ref): """ Look up an object via its module string (e.g., 'glue.core.data.Data') Parameters ---------- ref : str The module string """ if ref.startswith('__builtin__'): ref = '.'.join(['builtins'] + ref.split('.')[1:]) mod = ref.rsplit('.', 1)[0] try: result = __import__(mod) except ImportError: raise ValueError("Module '{0}' not found".format(mod)) try: for attr in ref.split('.')[1:]: result = getattr(result, attr) return result except AttributeError: raise ValueError("Object '{0}' not found".format(ref)) def as_variable_name(x): """ Convert a string to a legal python variable name Parameters ---------- x : str A string to (possibly) rename Returns ------- variable_name : str A legal Python variable name """ allowed = string.ascii_letters + string.digits + '_' result = [letter if letter in allowed else '_' for letter in x or 'x'] if result[0] in string.digits: result.insert(0, '_') return ''.join(result) def as_list(x): if isinstance(x, list): return x return [x] def file_format(filename): if filename.find('.') == -1: return '' if filename.lower().endswith('.gz'): result = filename.lower().rsplit('.', 2)[1] else: result = filename.lower().rsplit('.', 1)[1] return result class CallbackMixin(object): """ A mixin that provides a utility for attaching callback functions to methods """ def __init__(self): self._callbacks = CallbackContainer() def add_callback(self, function): self._callbacks.append(function) def remove_callback(self, function): self._callbacks.remove(function) def notify(self, *args, **kwargs): for func in self._callbacks: func(*args, **kwargs) class PropertySetMixin(object): """An object that provides a set of properties that are meant to encapsulate state information This class exposes a properties attribute, which is a dict of all properties. Similarly, assigning to the properties dict will update the individual properties """ _property_set = [] @property def properties(self): """ A dict mapping property names to values """ return dict((p, getattr(self, p)) for p in self._property_set) @properties.setter def properties(self, value): """ Update the properties with a new dict. Keys in the new dict must be valid property names defined in the _property_set class level attribute""" invalid = set(value.keys()) - set(self._property_set) if invalid: raise ValueError("Invalid property values: %s" % invalid) for k in self._property_set: if k not in value: continue setattr(self, k, value[k]) def common_prefix(strings, exclude_punctuation=True): """ Given a list of strings, find the longest prefix common to all of them """ if len(strings) > 0: for i in range(len(strings[0]), 0, -1): if exclude_punctuation and strings[0][i - 1] in string.punctuation: continue for st in strings[1:]: if st[:i] != strings[0][:i]: break else: return strings[0][:i] return '' class Pointer(object): def __init__(self, key): self.key = key def __get__(self, instance, type=None): val = instance for k in self.key.split('.'): val = getattr(val, k, None) return val def __set__(self, instance, value): v = self.key.split('.') attr = reduce(getattr, [instance] + v[:-1]) setattr(attr, v[-1], value) def queue_to_list(q): """ Get all the values in a :class:`queue.Queue` object and return a list. """ l = [] while True: try: l.append(q.get_nowait()) except queue.Empty: return l def format_choices(options, index=False): """ Return a string with an error message formatted as: * option 1 * option 2 This can be preprended to existing error messages. """ updated_options = [] for option in options: if isinstance(option, str): updated_options.append("'{0}'".format(option)) elif isinstance(option, type): updated_options.append(str(option.__module__) + '.' + option.__name__) else: updated_options.append(option) if index: return "\n\n" + '\n'.join(['* {0} or {1}'.format(idx, option) for idx, option in enumerate(updated_options)]) else: return "\n\n" + '\n'.join(['* {0}'.format(option) for option in updated_options]) glueviz-1.0.1+dfsg.orig/glue/qglue.py0000644000175000017500000001273613605360406017043 0ustar noahfxnoahfx""" Utility function to load a variety of python objects into glue """ # Note: this is imported with Glue. We want # to minimize imports so that utilities like glue-deps # can run on systems with missing dependencies import sys from contextlib import contextmanager import numpy as np from glue.config import qglue_parser try: from glue.core import BaseData, Data except ImportError: # let qglue import, even though this won't work # qglue will throw an ImportError BaseData = Data = None __all__ = ['qglue'] @contextmanager def restore_io(): stdin = sys.stdin stdout = sys.stdout stderr = sys.stderr _in = sys.__stdin__ _out = sys.__stdout__ _err = sys.__stderr__ try: yield finally: sys.stdin = stdin sys.stdout = stdout sys.stderr = stderr sys.__stdin__ = _in sys.__stdout__ = _out sys.__stderr__ = _err @qglue_parser(dict) def _parse_data_dict(data, label): result = Data(label=label) for label, component in data.items(): result.add_component(component, label) return [result] @qglue_parser(np.recarray) def _parse_data_recarray(data, label): kwargs = dict((n, data[n]) for n in data.dtype.names) return [Data(label=label, **kwargs)] @qglue_parser(BaseData) def _parse_data_glue_data(data, label): if isinstance(data, Data): data.label = label return [data] @qglue_parser(np.ndarray) def _parse_data_numpy(data, label): return [Data(**{label: data, 'label': label})] @qglue_parser(list) def _parse_data_list(data, label): return [Data(**{label: data, 'label': label})] @qglue_parser(str) def _parse_data_path(path, label): from glue.core.data_factories import load_data, as_list data = load_data(path) for d in as_list(data): d.label = label return as_list(data) def parse_data(data, label): # First try new data translation layer from glue.config import data_translator try: handler, preferred = data_translator.get_handler_for(data) except TypeError: pass else: data = handler.to_data(data) data.label = label data._preferred_translation = preferred return [data] # Then try legacy 'qglue_parser' infrastructure for item in qglue_parser: data_class = item.data_class parser = item.parser if isinstance(data, data_class): try: return parser(data, label) except Exception as e: raise ValueError("Invalid format for data '%s'\n\n%s" % (label, e)) raise TypeError("Invalid data description: %s" % data) def parse_links(dc, links): from glue.core.link_helpers import MultiLink from glue.core import ComponentLink data = dict((d.label, d) for d in dc) result = [] def find_cid(s): dlabel, clabel = s.split('.') d = data[dlabel] c = d.find_component_id(clabel) if c is None: raise ValueError("Invalid link (no component named %s)" % s) return c for link in links: f, t = link[0:2] # from and to component names u = u2 = None if len(link) >= 3: # forward translation function u = link[2] if len(link) == 4: # reverse translation function u2 = link[3] # component names -> component IDs if isinstance(f, str): f = [find_cid(f)] else: f = [find_cid(item) for item in f] if isinstance(t, str): t = find_cid(t) result.append(ComponentLink(f, t, u)) else: t = [find_cid(item) for item in t] result += MultiLink(f, t, u, u2) return result def qglue(**kwargs): """ Quickly send python variables to Glue for visualization. The generic calling sequence is:: qglue(label1=data1, label2=data2, ..., [links=links]) The kewyords label1, label2, ... can be named anything besides ``links`` data1, data2, ... can be in many formats: * A pandas data frame * A path to a file * A numpy array, or python list * A numpy rec array * A dictionary of numpy arrays with the same shape * An astropy Table ``Links`` is an optional list of link descriptions, each of which has the format: ([left_ids], [right_ids], forward, backward) Each ``left_id``/``right_id`` is a string naming a component in a dataset (i.e., ``data1.x``). ``forward`` and ``backward`` are functions which map quantities on the left to quantities on the right, and vice versa. `backward` is optional Examples:: balls = {'kg': [1, 2, 3], 'radius_cm': [10, 15, 30]} cones = {'lbs': [5, 3, 3, 1]} def lb2kg(lb): return lb / 2.2 def kg2lb(kg): return kg * 2.2 links = [(['balls.kg'], ['cones.lbs'], lb2kg, kg2lb)] qglue(balls=balls, cones=cones, links=links) :returns: A :class:`~glue.app.qt.application.GlueApplication` object """ from glue.core import DataCollection from glue.app.qt import GlueApplication from glue.dialogs.autolinker.qt import run_autolinker links = kwargs.pop('links', None) dc = DataCollection() for label, data in kwargs.items(): dc.extend(parse_data(data, label)) if links is not None: dc.add_link(parse_links(dc, links)) with restore_io(): ga = GlueApplication(dc) run_autolinker(dc) ga.start() return ga glueviz-1.0.1+dfsg.orig/glue/_deps.py0000755000175000017500000001620513752534424017024 0ustar noahfxnoahfx#!/usr/bin/env python """ Guide users through installing Glue's dependencies """ import os from collections import OrderedDict import sys import importlib from glue._plugin_helpers import iter_plugin_entry_points class Dependency(object): def __init__(self, module, info, package=None, min_version=None): self.module = module self.info = info self.package = package or module self.min_version = min_version self.failed = False @property def installed(self): try: importlib.import_module(self.module) return True except ImportError: return False @property def version(self): try: module = __import__(self.module) return module.__version__ except ImportError: return 'unknown version' except AttributeError: try: return module.__VERSION__ except AttributeError: return 'unknown version' def help(self): result = """ {module}: ****************** {info} PIP package name: {package} """.format(module=self.module, info=self.info, package=self.package) return result def __str__(self): if self.installed: status = 'INSTALLED (%s)' % self.version elif self.failed: status = 'FAILED (%s)' % self.info else: status = 'MISSING (%s)' % self.info return "%20s:\t%s" % (self.package, status) class Python(Dependency): def __init__(self): self.package = 'Python' @property def installed(self): return True @property def version(self): return sys.version.split()[0] class QtDependency(Dependency): def __str__(self): if self.installed: status = 'INSTALLED (%s)' % self.version else: status = 'NOT INSTALLED' return "%20s:\t%s" % (self.module, status) class PyQt5(QtDependency): @property def version(self): try: from PyQt5 import Qt return "PyQt: {0} - Qt: {1}".format(Qt.PYQT_VERSION_STR, Qt.QT_VERSION_STR) except (ImportError, AttributeError): return 'unknown version' class PySide2(QtDependency): @property def version(self): try: import PySide2 from PySide2 import QtCore return "PySide2: {0} - Qt: {1}".format(PySide2.__version__, QtCore.__version__) except (ImportError, AttributeError): return 'unknown version' class QtPy(Dependency): @property def installed(self): try: importlib.import_module(self.module) return True except Exception: # QtPy raises a PythonQtError in some cases, so we can't use # ImportError. return False # Add any dependencies here # Make sure to add new categories to the categories tuple python = ( Python(), ) gui_framework = ( PyQt5('PyQt5', ''), PySide2('PySide2', '') ) required = ( QtPy('qtpy', 'Required', min_version='1.9'), Dependency('setuptools', 'Required', min_version='30.3'), Dependency('echo', 'Required', min_version='0.5'), Dependency('numpy', 'Required', min_version='1.16'), Dependency('bottleneck', 'Required', min_version='1.2'), Dependency('matplotlib', 'Required for plotting', min_version='3.2'), Dependency('pandas', 'Adds support for Excel files and DataFrames', min_version='1.0'), Dependency('astropy', 'Used for FITS I/O, table reading, and WCS Parsing', min_version='4.0'), Dependency('dill', 'Used when saving Glue sessions', min_version='0.2'), Dependency('h5py', 'Used to support HDF5 files', min_version='2.10'), Dependency('xlrd', 'Used to support Excel files', min_version='1.2'), Dependency('mpl_scatter_density', 'Used to make fast scatter density plots', 'mpl-scatter-density', min_version='0.7'), ) general = ( Dependency('scipy', 'Used for some image processing calculation', min_version='1.0'), Dependency('skimage', 'Used to read popular image formats (jpeg, png, etc.)', 'scikit-image')) ipython = ( Dependency('IPython', 'Needed for interactive IPython terminal', min_version='4'), Dependency('qtconsole', 'Needed for interactive IPython terminal'), Dependency('ipykernel', 'Needed for interactive IPython terminal'), Dependency('traitlets', 'Needed for interactive IPython terminal'), Dependency('pygments', 'Needed for interactive IPython terminal'), Dependency('zmq', 'Needed for interactive IPython terminal', 'pyzmq')) astronomy = ( Dependency('pyavm', 'Used to parse AVM metadata in image files', 'PyAVM'), Dependency('spectral_cube', 'Used to read in spectral cubes', 'spectral-cube'), Dependency('astrodendro', 'Used to read in and represent dendrograms', 'astrodendro')) testing = ( Dependency('mock', 'Used in test code'), Dependency('pytest', 'Used in test code')) export = ( Dependency('plotly', 'Used to explort plots to Plot.ly'), ) def plugins(): modules = [] dependencies = [] for entry_point in iter_plugin_entry_points(): module_name = entry_point.module_name.split('.')[0] package = entry_point.dist.project_name modules.append((module_name, package)) for module, package in sorted(set(modules)): dependencies.append(Dependency(module, '', package=package)) return dependencies categories = (('python', python), ('gui framework', gui_framework), ('required', required), ('plugins', plugins()), ('ipython terminal', ipython), ('general', general), ('astronomy', astronomy), ('testing', testing), ('export', export)) dependencies = dict((d.package, d) for c in categories for d in c[1]) def get_status(): s = "" for category, deps in categories: s += "%21s" % category.upper() + os.linesep for dep in deps: s += str(dep) + os.linesep s += os.linesep return s def get_status_as_odict(): status = OrderedDict() for category, deps in categories: for dep in deps: if dep.installed: status[dep.package] = dep.version else: status[dep.package] = "Not installed" return status def show_status(): print(get_status()) USAGE = """usage: #show all dependencies glue-deps list #display information about a dependency glue-deps info astropy """ def main(argv=None): argv = argv or sys.argv if len(argv) < 2 or argv[1] not in ['list', 'info']: sys.stderr.write(USAGE) sys.exit(1) if argv[1] == 'info': if len(argv) != 3: sys.stderr.write(USAGE) sys.stderr.write("Please specify a dependency\n") sys.exit(1) dep = dependencies.get(argv[2], None) if dep is None: sys.stderr.write("Unrecognized dependency: %s\n" % argv[2]) sys.exit(1) print(dep.help()) sys.exit(0) if argv[1] == 'list': show_status() sys.exit(0) if __name__ == "__main__": main() glueviz-1.0.1+dfsg.orig/glue/backends.py0000644000175000017500000000212413605357235017474 0ustar noahfxnoahfx""" A common interface for accessing backend UI functionality. At the moment, the only backend is Qt """ from abc import abstractmethod _backend = None class TimerBase(object): @abstractmethod def __init__(self, interval, callback): pass @abstractmethod def stop(self): pass @abstractmethod def start(self): pass class QtTimer(TimerBase): def __init__(self, interval, callback): from qtpy import QtCore self._timer = QtCore.QTimer() self._timer.setInterval(interval) self._timer.timeout.connect(callback) def start(self): self._timer.start() def stop(self): self._timer.stop() def get_timer(backend='qt'): if backend == 'qt': return QtTimer else: raise ValueError("Only QT Backend supported") def get_backend(backend='qt'): global _backend if _backend is not None: return _backend if backend != 'qt': raise ValueError("Only QT Backend supported") from glue.qt import qt_backend _backend = qt_backend return _backend glueviz-1.0.1+dfsg.orig/glue/_plugin_helpers.py0000644000175000017500000000513413605357235021105 0ustar noahfxnoahfx# The following function is a thin wrapper around iter_entry_points. The reason it # is in this separate file is that when making the Mac app, py2app doesn't # support entry points, so we replace this function with a version that has the # entry points we want hardcoded. If this function was in glue/main.py, the # reference to the iter_plugin_entry_points function in load_plugin would be # evaluated at compile time rather than at runtime, so the patched version # wouldn't be used. import os from collections import defaultdict def iter_plugin_entry_points(): from pkg_resources import iter_entry_points return iter_entry_points(group='glue.plugins', name=None) class PluginConfig(object): def __init__(self, plugins={}): self.plugins = defaultdict(lambda: True) self.plugins.update(plugins) def __str__(self): string = "" for plugin in sorted(self.plugins): string += "{0}: {1}\n".format(plugin, self.plugins[plugin]) return string @classmethod def load(cls): # Import at runtime because some tests change this value. We also don't # just import the variable directly otherwise it is cached. from glue import config cfg_dir = config.CFG_DIR plugin_cfg = os.path.join(cfg_dir, 'plugins.cfg') import configparser config = configparser.ConfigParser() read = config.read(plugin_cfg) if len(read) == 0 or not config.has_section('plugins'): return cls() plugins = {} for name, enabled in config.items('plugins'): plugins[name] = bool(int(enabled)) self = cls(plugins=plugins) return self def save(self): # Import at runtime because some tests change this value. We also don't # just import the variable directly otherwise it is cached. from glue import config cfg_dir = config.CFG_DIR plugin_cfg = os.path.join(cfg_dir, 'plugins.cfg') import configparser config = configparser.ConfigParser() config.add_section('plugins') for key in sorted(self.plugins): config.set('plugins', key, value=str(int(self.plugins[key]))) if not os.path.exists(cfg_dir): os.mkdir(cfg_dir) with open(plugin_cfg, 'w') as fout: config.write(fout) def filter(self, keep): """ Keep only certain plugins. This is used to filter out plugins that are not installed. """ for key in list(self.plugins.keys())[:]: if key not in keep: self.plugins.pop(key) glueviz-1.0.1+dfsg.orig/glue/dialogs/0000755000175000017500000000000013752535025016771 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/0000755000175000017500000000000013752535025021274 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/tests/0000755000175000017500000000000013752535025022436 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/tests/__init__.py0000644000175000017500000000000013455362716024542 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/qt/0000755000175000017500000000000013752535025021720 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/qt/tests/0000755000175000017500000000000013752535025023062 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/qt/tests/__init__.py0000644000175000017500000000000013455362716025166 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/qt/tests/test_link_editor.py0000644000175000017500000006360713752534424027014 0ustar noahfxnoahfxfrom unittest.mock import patch from qtpy import QtWidgets from glue.utils.qt import process_events from glue.core import Data, DataCollection from glue.dialogs.link_editor.qt import LinkEditor from glue.core.component_link import ComponentLink from glue.plugins.coordinate_helpers.link_helpers import Galactic_to_FK5, ICRS_to_Galactic from glue.core.link_helpers import identity, functional_link_collection, LinkSame def non_empty_rows_count(layout): """ Determine how many rows of the QGridLayout are not empty """ count = 0 for row in range(layout.rowCount()): for col in range(layout.columnCount()): item = layout.itemAtPosition(row, col) if item is not None and item.widget() is not None and item.widget().isVisible(): count += 1 break return count def get_action(link_widget, text): for submenu in link_widget._menu.children(): if isinstance(submenu, QtWidgets.QMenu): for action in submenu.actions(): if action.text() == text: return action raise ValueError("Action '{0}' not found".format(text)) class TestLinkEditor: def setup_method(self, method): self.data1 = Data(x=[1, 2, 3], y=[2, 3, 4], z=[6, 5, 4], label='data1') self.data2 = Data(a=[2, 3, 4], b=[4, 5, 4], c=[3, 4, 1], label='data2') self.data3 = Data(i=[5, 4, 3], j=[2, 2, 1], label='data3') self.data_collection = DataCollection([self.data1, self.data2, self.data3]) def test_defaults(self): # Make sure the dialog opens and closes and check default settings. dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget assert link_widget.state.data1 is None assert link_widget.state.data2 is None assert not link_widget.button_add_link.isEnabled() assert not link_widget.button_remove_link.isEnabled() link_widget.state.data1 = self.data2 assert not link_widget.button_add_link.isEnabled() assert not link_widget.button_remove_link.isEnabled() link_widget.state.data2 = self.data1 assert link_widget.button_add_link.isEnabled() assert not link_widget.button_remove_link.isEnabled() dialog.accept() assert len(self.data_collection.external_links) == 0 def test_defaults_two(self): # Make sure the dialog opens and closes and check default settings. With # two datasets, the datasets should be selected by default. self.data_collection.remove(self.data3) dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget assert link_widget.state.data1 is self.data1 assert link_widget.state.data2 is self.data2 assert link_widget.button_add_link.isEnabled() assert not link_widget.button_remove_link.isEnabled() dialog.accept() assert len(self.data_collection.external_links) == 0 def test_ui_behavior(self): # This is a bit more detailed test that checks that things update # correctly as we change various settings dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget link_widget.state.data1 = self.data1 link_widget.state.data2 = self.data2 add_identity_link = get_action(link_widget, 'identity') add_lengths_volume_link = get_action(link_widget, 'lengths_to_volume') # At this point, there should be no links in the main list widget # and nothing on the right. assert link_widget.listsel_current_link.count() == 0 assert link_widget.link_details.text() == '' assert non_empty_rows_count(link_widget.combos1) == 0 assert non_empty_rows_count(link_widget.combos2) == 0 # Let's add an identity link add_identity_link.trigger() # Ensure that all events get processed process_events() # Now there should be one link in the main list and content in the # right hand panel. assert link_widget.listsel_current_link.count() == 1 assert link_widget.link_details.text() == 'Link conceptually identical components' assert non_empty_rows_count(link_widget.combos1) == 1 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' # Let's change the current components for the link link_widget.state.current_link.x = self.data1.id['y'] link_widget.state.current_link.y = self.data2.id['b'] # and make sure the UI gets updated assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'y' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'b' # We now add another link of a different type add_lengths_volume_link.trigger() # Ensure that all events get processed process_events() # and make sure the UI has updated assert link_widget.listsel_current_link.count() == 2 assert link_widget.link_details.text() == 'Convert between linear measurements and volume' assert non_empty_rows_count(link_widget.combos1) == 3 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'y' assert link_widget.combos1.itemAtPosition(2, 1).widget().currentText() == 'z' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' # Try swapping the order of the data, the current link should stay the same link_widget.state.flip_data() assert link_widget.link_details.text() == 'Convert between linear measurements and volume' # And flip it back link_widget.state.flip_data() assert link_widget.link_details.text() == 'Convert between linear measurements and volume' # Now switch back to the first link link_widget.state.current_link = type(link_widget.state).current_link.get_choices(link_widget.state)[0] # and make sure the UI updates and has preserved the correct settings assert link_widget.listsel_current_link.count() == 2 assert link_widget.link_details.text() == 'Link conceptually identical components' assert non_empty_rows_count(link_widget.combos1) == 1 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'y' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'b' # Next up, we try changing the data link_widget.state.data1 = self.data3 # At this point there should be no links in the list assert link_widget.listsel_current_link.count() == 0 assert link_widget.link_details.text() == '' assert non_empty_rows_count(link_widget.combos1) == 0 assert non_empty_rows_count(link_widget.combos2) == 0 # Add another identity link add_identity_link.trigger() # Ensure that all events get processed process_events() # Now there should be one link in the main list assert link_widget.listsel_current_link.count() == 1 assert link_widget.link_details.text() == 'Link conceptually identical components' assert non_empty_rows_count(link_widget.combos1) == 1 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'i' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' # Switch back to the original data link_widget.state.data1 = self.data1 # And check the output is as before assert link_widget.listsel_current_link.count() == 2 assert link_widget.link_details.text() == 'Link conceptually identical components' assert non_empty_rows_count(link_widget.combos1) == 1 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'y' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'b' # Let's now remove this link link_widget.button_remove_link.click() # Ensure that all events get processed process_events() # We should now see the lengths/volume link assert link_widget.listsel_current_link.count() == 1 assert link_widget.link_details.text() == 'Convert between linear measurements and volume' assert non_empty_rows_count(link_widget.combos1) == 3 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'y' assert link_widget.combos1.itemAtPosition(2, 1).widget().currentText() == 'z' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' dialog.accept() links = self.data_collection.external_links assert len(links) == 2 assert isinstance(links[0], ComponentLink) assert links[0].get_from_ids()[0] is self.data1.id['x'] assert links[0].get_from_ids()[1] is self.data1.id['y'] assert links[0].get_from_ids()[2] is self.data1.id['z'] assert links[0].get_to_id() is self.data2.id['a'] assert isinstance(links[1], ComponentLink) assert links[1].get_from_ids()[0] is self.data3.id['i'] assert links[1].get_to_id() is self.data2.id['a'] def test_graph(self): dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget add_identity_link = get_action(link_widget, 'identity') graph = link_widget.graph_widget def click(node_or_edge): # We now simulate a selection - since we can't deterministically # figure out the exact pixel coordinates to use, we patch # 'find_object' to return the object we want to select. with patch.object(graph, 'find_object', return_value=node_or_edge): graph.mousePressEvent(None) def hover(node_or_edge): # Same as for select, we patch find_object with patch.object(graph, 'find_object', return_value=node_or_edge): graph.mouseMoveEvent(None) # To start with, no data should be selected assert link_widget.state.data1 is None assert link_widget.state.data2 is None # and the graph should have three nodes and no edges assert len(graph.nodes) == 3 assert len(graph.edges) == 0 click(graph.nodes[0]) # Check that this has caused one dataset to be selected assert link_widget.state.data1 is self.data1 assert link_widget.state.data2 is None # Click on the same node again and this should deselect the data # (but only once we move off from the node) click(graph.nodes[0]) assert link_widget.state.data1 is self.data1 assert link_widget.state.data2 is None hover(None) assert link_widget.state.data1 is None assert link_widget.state.data2 is None # Select it again click(graph.nodes[0]) # and now select another node too click(graph.nodes[1]) assert link_widget.state.data1 is self.data1 assert link_widget.state.data2 is self.data2 assert len(graph.nodes) == 3 assert len(graph.edges) == 0 add_identity_link.trigger() assert len(graph.nodes) == 3 assert len(graph.edges) == 1 # Unselect and select another node click(graph.nodes[1]) click(graph.nodes[2]) # and check the data selections have been updated assert link_widget.state.data1 is self.data1 assert link_widget.state.data2 is self.data3 # Deselect it and move off click(graph.nodes[2]) hover(None) # and the second dataset should now once again be None assert link_widget.state.data1 is self.data1 assert link_widget.state.data2 is None # Now change the data manually link_widget.state.data1 = self.data2 link_widget.state.data2 = self.data3 # and check that if we select the edge the datasets change back click(graph.edges[0]) assert link_widget.state.data1 is self.data1 assert link_widget.state.data2 is self.data2 # Unselect and hover over nothing click(graph.edges[0]) hover(None) assert link_widget.state.data1 is None assert link_widget.state.data2 is None # Hover over the edge and the datasets should change back hover(graph.edges[0]) assert link_widget.state.data1 is self.data1 assert link_widget.state.data2 is self.data2 # And check that clicking outside of nodes/edges deselects everything click(None) assert link_widget.state.data1 is None assert link_widget.state.data2 is None # Select a node, select another, then make sure that selecting a third # one will deselect the two original ones click(graph.nodes[0]) click(graph.nodes[1]) click(graph.nodes[2]) assert link_widget.state.data1 is self.data3 assert link_widget.state.data2 is None dialog.accept() def test_preexisting_links(self): # Check that things work properly if there are pre-existing links link1 = ComponentLink([self.data1.id['x']], self.data2.id['c']) def add(x, y): return x + y def double(x): return x * 2 def halve(x): return x / 2 link2 = ComponentLink([self.data2.id['a'], self.data2.id['b']], self.data3.id['j'], using=add) link3 = ComponentLink([self.data3.id['i']], self.data2.id['c'], using=double, inverse=halve) # Test using a LinkHelper link since that caused a bug earlier link4 = LinkSame(self.data1.id['z'], self.data2.id['c']) self.data_collection.add_link(link1) self.data_collection.add_link(link2) self.data_collection.add_link(link3) self.data_collection.add_link(link4) dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget link_widget.state.data1 = self.data1 link_widget.state.data2 = self.data2 assert link_widget.listsel_current_link.count() == 2 assert link_widget.link_details.text() == '' assert non_empty_rows_count(link_widget.combos1) == 1 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' link_widget.state.current_link = type(link_widget.state).current_link.get_choices(link_widget.state)[1] assert link_widget.link_details.text() == '' assert non_empty_rows_count(link_widget.combos1) == 1 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'z' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' link_widget.state.data1 = self.data3 assert link_widget.listsel_current_link.count() == 2 assert link_widget.link_details.text() == '' assert non_empty_rows_count(link_widget.combos1) == 1 assert non_empty_rows_count(link_widget.combos2) == 2 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'j' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' assert link_widget.combos2.itemAtPosition(1, 1).widget().currentText() == 'b' link_widget.state.current_link = type(link_widget.state).current_link.get_choices(link_widget.state)[1] assert link_widget.listsel_current_link.count() == 2 assert link_widget.link_details.text() == '' assert non_empty_rows_count(link_widget.combos1) == 1 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'i' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' dialog.accept() links = self.data_collection.external_links assert len(links) == 4 assert isinstance(links[0], ComponentLink) assert links[0].get_from_ids()[0] is self.data1.id['x'] assert links[0].get_to_id() is self.data2.id['c'] assert links[0].get_using() is identity assert isinstance(links[1], ComponentLink) assert links[1].get_from_ids()[0] is self.data2.id['a'] assert links[1].get_from_ids()[1] is self.data2.id['b'] assert links[1].get_to_id() is self.data3.id['j'] assert links[1].get_using() is add assert isinstance(links[2], ComponentLink) assert links[2].get_from_ids()[0] is self.data3.id['i'] assert links[2].get_to_id() is self.data2.id['c'] assert links[2].get_using() is double assert links[2].get_inverse() is halve assert isinstance(links[3], LinkSame) assert len(links[3].cids1) == 1 assert links[3].cids1[0] is self.data1.id['z'] assert len(links[3].cids2) == 1 assert links[3].cids2[0] is self.data2.id['c'] assert links[3].forwards is identity def test_add_helper(self): dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget link_widget.state.data1 = self.data1 link_widget.state.data2 = self.data2 add_coordinate_link = get_action(link_widget, 'ICRS <-> Galactic') # Add a coordinate link add_coordinate_link.trigger() # Ensure that all events get processed process_events() assert link_widget.listsel_current_link.count() == 1 assert link_widget.link_details.text() == 'Link ICRS and Galactic coordinates' assert non_empty_rows_count(link_widget.combos1) == 2 assert non_empty_rows_count(link_widget.combos2) == 2 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'y' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' assert link_widget.combos2.itemAtPosition(1, 1).widget().currentText() == 'b' dialog.accept() links = self.data_collection.external_links assert len(links) == 1 assert isinstance(links[0], ICRS_to_Galactic) assert links[0].cids1[0] is self.data1.id['x'] assert links[0].cids1[1] is self.data1.id['y'] assert links[0].cids2[0] is self.data2.id['a'] assert links[0].cids2[1] is self.data2.id['b'] def test_preexisting_helper(self): link1 = Galactic_to_FK5(cids1=[self.data1.id['x'], self.data1.id['y']], cids2=[self.data2.id['c'], self.data2.id['b']]) self.data_collection.add_link(link1) dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget assert link_widget.listsel_current_link.count() == 0 link_widget.state.data1 = self.data1 link_widget.state.data2 = self.data2 assert link_widget.listsel_current_link.count() == 1 assert link_widget.link_details.text() == 'Link Galactic and FK5 (J2000) Equatorial coordinates' assert non_empty_rows_count(link_widget.combos1) == 2 assert non_empty_rows_count(link_widget.combos2) == 2 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'y' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' assert link_widget.combos2.itemAtPosition(1, 1).widget().currentText() == 'b' dialog.accept() links = self.data_collection.external_links assert len(links) == 1 assert isinstance(links[0], Galactic_to_FK5) assert links[0].cids1[0] is self.data1.id['x'] assert links[0].cids1[1] is self.data1.id['y'] assert links[0].cids2[0] is self.data2.id['c'] assert links[0].cids2[1] is self.data2.id['b'] def test_cancel(self): # Make sure that changes aren't saved if dialog is cancelled # This is a bit more detailed test that checks that things update # correctly as we change various settings link1 = ComponentLink([self.data1.id['x']], self.data2.id['c']) self.data_collection.add_link(link1) dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget link_widget.state.data1 = self.data1 link_widget.state.data2 = self.data2 link_widget.state.current_link.x = self.data1.id['y'] assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'y' add_identity_link = get_action(link_widget, 'identity') add_identity_link.trigger() assert link_widget.listsel_current_link.count() == 2 dialog.reject() links = self.data_collection.external_links assert len(links) == 1 assert isinstance(links[0], ComponentLink) assert links[0].get_from_ids()[0] is self.data1.id['x'] assert links[0].get_to_id() is self.data2.id['c'] def test_functional_link_collection(self): # Test that if we use a @link_helper in 'legacy' mode, i.e. with only # input labels, both datasets are available from the combos in the # link editor dialog. Also test the new-style @link_helper. def deg_arcsec(degree, arcsecond): return [ComponentLink([degree], arcsecond, using=lambda d: d * 3600), ComponentLink([arcsecond], degree, using=lambda a: a / 3600)] # Old-style link helper helper1 = functional_link_collection(deg_arcsec, description='Legacy link', labels1=['deg', 'arcsec'], labels2=[]) link1 = helper1(cids1=[self.data1.id['x'], self.data2.id['c']]) self.data_collection.add_link(link1) # New-style link helper helper2 = functional_link_collection(deg_arcsec, description='New-style link', labels1=['deg'], labels2=['arcsec']) link2 = helper2(cids1=[self.data1.id['x']], cids2=[self.data2.id['c']]) self.data_collection.add_link(link2) dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget assert link_widget.listsel_current_link.count() == 0 link_widget.state.data1 = self.data1 link_widget.state.data2 = self.data2 assert link_widget.listsel_current_link.count() == 2 assert not link_widget.combos1_header.isVisible() assert not link_widget.combos2_header.isVisible() assert link_widget.link_details.text() == 'Legacy link' assert non_empty_rows_count(link_widget.combos1) == 2 assert non_empty_rows_count(link_widget.combos2) == 0 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'c' link_widget.state.current_link = type(link_widget.state).current_link.get_choices(link_widget.state)[1] assert link_widget.combos1_header.isVisible() assert link_widget.combos2_header.isVisible() assert link_widget.link_details.text() == 'New-style link' assert non_empty_rows_count(link_widget.combos1) == 1 assert non_empty_rows_count(link_widget.combos2) == 1 assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' dialog.accept() links = self.data_collection.external_links assert len(links) == 2 assert isinstance(links[0], helper1) assert links[0].cids1[0] is self.data1.id['x'] assert links[0].cids1[1] is self.data2.id['c'] assert isinstance(links[1], helper2) assert links[1].cids1[0] is self.data1.id['x'] assert links[1].cids2[0] is self.data2.id['c'] def test_same_data(self): # Test that we can't set the same data twice dialog = LinkEditor(self.data_collection) dialog.show() link_widget = dialog.link_widget link_widget.state.data1 = self.data1 link_widget.state.data2 = self.data2 assert link_widget.state.data1 == self.data1 assert link_widget.state.data2 == self.data2 link_widget.state.data1 = self.data2 assert link_widget.state.data1 == self.data2 assert link_widget.state.data2 == self.data1 link_widget.state.data2 = self.data2 assert link_widget.state.data1 == self.data1 assert link_widget.state.data2 == self.data2 dialog.accept() def test_preexisting_links_twodata(self): # Regression test for an issue that occurred specifically if there were # exactly two datasets and pre-existing links (since this means that # the window opens with a current_link selected by default) data1 = Data(x=[1, 2, 3], y=[2, 3, 4], z=[6, 5, 4], label='data1') data2 = Data(a=[2, 3, 4], b=[4, 5, 4], c=[3, 4, 1], label='data2') data_collection = DataCollection([data1, data2]) link1 = ComponentLink([data1.id['x']], data2.id['c']) data_collection.add_link(link1) dialog = LinkEditor(data_collection) dialog.show() dialog.accept() glueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/qt/__init__.py0000644000175000017500000000004313503203504024012 0ustar noahfxnoahfxfrom .link_editor import * # noqa glueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/qt/link_editor_widget.ui0000644000175000017500000002212513522025220026111 0ustar noahfxnoahfx LinkEditorWidget 0 0 627 648 0 0 Link Editor 0 0 0 300 0 300 Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff QAbstractScrollArea::AdjustToContents Qt::Vertical QSizePolicy::Fixed 20 20 10 Qt::Horizontal 40 20 75 true Link details Qt::AlignCenter 75 true Links between Dataset 1 and Dataset 2 Qt::AlignCenter Glue attributes 75 true Dataset 2 Qt::AlignCenter 0 0 QComboBox::AdjustToMinimumContentsLength QComboBox::AdjustToMinimumContentsLength 75 true Dataset 1 Qt::AlignCenter Create advanced link QToolButton::InstantPopup Qt::Horizontal 40 20 Remove link padding: 0px QLayout::SetDefaultConstraint Details about the link Qt::AlignJustify true 75 true Dataset 1 attributes 10 5 75 true Dataset 2 attributes 10 5 Qt::Vertical 20 40 DataGraphWidget QGraphicsView
    glue.dialogs.link_editor.qt.data_graph
    1
    glueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/qt/link_editor_dialog.ui0000644000175000017500000000502513522025220026065 0ustar noahfxnoahfx LinkEditor 0 0 900 700 Link Editor true QLayout::SetDefaultConstraint Click on two datasets to set up links or click on an existing connection to edit links. Selected datasets are shown in green. When one dataset is selected, the colors show directly and indirectly linked (blue) and inaccessible (red) datasets. true Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok false buttonBox accepted() LinkEditor accept() 248 254 157 274 buttonBox rejected() LinkEditor reject() 316 260 286 274 glueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/qt/data_graph.py0000644000175000017500000003751713605357235024403 0ustar noahfxnoahfxfrom collections import OrderedDict import numpy as np from qtpy.QtCore import Qt, Signal from qtpy.QtGui import QPainter, QTransform, QPen from qtpy.QtWidgets import (QGraphicsView, QGraphicsScene, QApplication, QGraphicsTextItem, QGraphicsEllipseItem, QGraphicsLineItem) from glue.utils.qt import mpl_to_qt_color, qt_to_mpl_color COLOR_SELECTED = (0.2, 0.9, 0.2) COLOR_CONNECTED = (0.6, 0.9, 0.9) COLOR_DISCONNECTED = (0.9, 0.6, 0.6) def get_pen(color, linewidth=1): color = mpl_to_qt_color(color) return QPen(color, linewidth, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) class Edge(QGraphicsLineItem): def __init__(self, node_source, node_dest, linewidth=3, zindex=5): self.linewidth = linewidth self.node_source = node_source self.node_dest = node_dest super(Edge, self).__init__(0, 0, 1, 1) self.setZValue(zindex) self.color = '0.5' def update_position(self): x0, y0 = self.node_source.node_position x1, y1 = self.node_dest.node_position self.setLine(x0, y0, x1, y1) @property def color(self): return qt_to_mpl_color(self.pen().color()) @color.setter def color(self, value): self.setPen(get_pen(value, self.linewidth)) def add_to_scene(self, scene): scene.addItem(self) def remove_from_scene(self, scene): scene.removeItem(self) def contains(self, point): return super(Edge, self).contains(self.mapFromScene(point)) class DataNode: def __init__(self, data, radius=15): self.data = data # Add circular node self.node = QGraphicsEllipseItem(0, 0, 1, 1) # Set radius self.radius = radius # Add text label self.label = QGraphicsTextItem(data.label) font = self.label.font() font.setPointSize(10) self.label.setFont(font) # Add line between label and node self.line1 = QGraphicsLineItem(0, 0, 1, 1) self.line2 = QGraphicsLineItem(0, 0, 1, 1) self.node.setZValue(20) self.label.setZValue(10) self.line1.setZValue(10) self.line2.setZValue(10) self.line1.setPen(get_pen('0.5')) self.line2.setPen(get_pen('0.5')) self.color = '0.8' @property def radius(self): return self._radius @radius.setter def radius(self, value): self._radius = value self.node.setRect(-value, -value, 2 * value, 2 * value) def contains(self, point): # Check label if self.label.contains(self.label.mapFromScene(point)): return True # Check node if self.node.contains(self.node.mapFromScene(point)): return True return False def update(self): self.node.update() def add_to_scene(self, scene): scene.addItem(self.node) scene.addItem(self.label) scene.addItem(self.line1) scene.addItem(self.line2) def remove_from_scene(self, scene): scene.removeItem(self.node) scene.removeItem(self.label) scene.removeItem(self.line1) scene.removeItem(self.line2) @property def node_position(self): pos = self.node.pos() return pos.x(), pos.y() @node_position.setter def node_position(self, value): self.node.setPos(value[0], value[1]) self.update_lines() @property def label_position(self): pos = self.label.pos() return pos.x(), pos.y() @label_position.setter def label_position(self, value): self.label.setPos(value[0], value[1]) self.update_lines() def update_lines(self): x0, y0 = self.label_position x2, y2 = self.node_position x1 = 0.5 * (x0 + x2) y1 = y0 self.line1.setLine(x0, y0, x1, y1) self.line2.setLine(x1, y1, x2, y2) @property def color(self): return qt_to_mpl_color(self.node.brush().color()) @color.setter def color(self, value): self.node.setBrush(mpl_to_qt_color(value)) def get_connections(dc_links): links = [] for link in dc_links: data1 = link.data1 data2 = link.data2 if (data1, data2) not in links and (data2, data1) not in links: links.append((data1, data2)) return links def layout_simple_circle(nodes, edges, center=None, radius=None, reorder=True): # Place nodes around a circle if reorder: nodes[:] = order_nodes_by_connections(nodes, edges) for i, node in enumerate(nodes): angle = 2 * np.pi * i / len(nodes) nx = radius * np.cos(angle) + center[0] ny = radius * np.sin(angle) + center[1] node.node_position = nx, ny def order_nodes_by_connections(nodes, edges): search_nodes = list(nodes) sorted_nodes = [] while len(search_nodes) > 0: lengths = [] connections = [] for node in search_nodes: direct, indirect = find_connections(node, search_nodes, edges) connections.append((indirect, direct)) lengths.append((len(indirect), len(direct))) m = max(lengths) for i in range(len(lengths)): if lengths[i] == m: for node in connections[i][0] + connections[i][1]: if node not in sorted_nodes: sorted_nodes.append(node) search_nodes = [node for node in nodes if node not in sorted_nodes] return sorted_nodes class DataGraphWidget(QGraphicsView): selection_changed = Signal() def __init__(self, parent=None): super(DataGraphWidget, self).__init__(parent=parent) # Set up scene self.scene = QGraphicsScene(self) self.scene.setItemIndexMethod(QGraphicsScene.NoIndex) self.scene.setSceneRect(0, 0, 800, 300) self.setScene(self.scene) self.setWindowTitle("Glue data graph") self.setRenderHint(QPainter.Antialiasing) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorViewCenter) self.selection_level = 0 def resizeEvent(self, event): self.scene.setSceneRect(0, 0, self.width(), self.height()) self.relayout(reorder=False) def relayout(self, reorder=True): # Update radius for node in self.nodes: node.radius = self.height() / 30. layout_simple_circle(self.nodes, self.edges, center=(self.width() / 2, self.height() / 2), radius=self.height() / 3, reorder=reorder) # Update edge positions for edge in self.background_edges + self.edges: edge.update_position() # Set up labels self.left_nodes = [node for node in self.nodes if node.node_position[0] < self.width() / 2] self.left_nodes = sorted(self.left_nodes, key=lambda x: x.node_position[1], reverse=True) self.right_nodes = [node for node in self.nodes if node not in self.left_nodes] self.right_nodes = sorted(self.right_nodes, key=lambda x: x.node_position[1], reverse=True) for i, node in enumerate(self.left_nodes): y = self.height() - (i + 1) / (len(self.left_nodes) + 1) * self.height() node.label_position = self.width() / 2 - self.height() / 2, y for i, node in enumerate(self.right_nodes): y = self.height() - (i + 1) / (len(self.right_nodes) + 1) * self.height() node.label_position = self.width() / 2 + self.height() / 2, y def set_data_collection(self, data_collection, old_links=None, new_links=None): # Get data and initialize nodes self.data_to_nodes = OrderedDict((data, DataNode(data)) for data in data_collection) self.nodes = list(self.data_to_nodes.values()) # Get links and set up edges if old_links: self.background_edges = [Edge(self.data_to_nodes[data1], self.data_to_nodes[data2], linewidth=1, zindex=1) for data1, data2 in get_connections(data_collection.external_links)] else: self.background_edges = [] if new_links: self.edges = [Edge(self.data_to_nodes[data1], self.data_to_nodes[data2]) for data1, data2 in get_connections(new_links)] else: self.edges = [] # Figure out positions self.relayout() # Add nodes and edges to graph for node in self.nodes: node.add_to_scene(self.scene) for edge in self.background_edges + self.edges: edge.add_to_scene(self.scene) self.text_adjusted = False self.selected_edge = None self.selected_node1 = None self.selected_node2 = None def set_links(self, links): for edge in self.edges: edge.remove_from_scene(self.scene) self.edges = [Edge(self.data_to_nodes[data1], self.data_to_nodes[data2]) for data1, data2 in get_connections(links)] for edge in self.edges: edge.update_position() for edge in self.edges: edge.add_to_scene(self.scene) self._update_selected_edge() self._update_selected_colors() def paintEvent(self, event): super(DataGraphWidget, self).paintEvent(event) if not self.text_adjusted: for node in self.nodes: width = node.label.boundingRect().width() height = node.label.boundingRect().height() transform = QTransform() if node in self.left_nodes: transform.translate(-width, -height / 2) else: transform.translate(0, -height / 2) node.label.setTransform(transform) self.text_adjusted = True def manual_select(self, data1=None, data2=None): if data1 is None and data2 is not None: data1, data2 = data2, data1 if data2 is not None: self.selection_level = 2 elif data1 is not None: self.selection_level = 1 else: self.selection_level = 0 self.selected_node1 = self.data_to_nodes.get(data1, None) self.selected_node2 = self.data_to_nodes.get(data2, None) self._update_selected_edge() self._update_selected_colors() def find_object(self, event): for obj in list(self.nodes) + self.edges: if obj.contains(event.localPos()): return obj def mouseMoveEvent(self, event): # TODO: Don't update until the end # TODO: Only select object on top selected = self.find_object(event) if selected is None: if self.selection_level == 0: self.selected_node1 = None self.selected_node2 = None self._update_selected_edge() elif self.selection_level == 1: self.selected_node2 = None self._update_selected_edge() elif isinstance(selected, DataNode): if self.selection_level == 0: self.selected_node1 = selected self.selected_node2 = None elif self.selection_level == 1: if selected is not self.selected_node1: self.selected_node2 = selected self._update_selected_edge() elif isinstance(selected, Edge): if self.selection_level == 0: self.selected_edge = selected self.selected_node1 = selected.node_source self.selected_node2 = selected.node_dest self._update_selected_colors() self.selection_changed.emit() def mousePressEvent(self, event): # TODO: Don't update until the end # TODO: Only select object on top selected = self.find_object(event) if selected is None: self.selection_level = 0 self.selected_node1 = None self.selected_node2 = None self._update_selected_edge() elif isinstance(selected, DataNode): if self.selection_level == 0: self.selected_node1 = selected self.selection_level += 1 elif self.selection_level == 1: if selected is self.selected_node1: self.selected_node1 = None self.selection_level = 0 else: self.selected_node2 = selected self.selection_level = 2 elif self.selection_level == 2: if selected is self.selected_node2: self.selected_node2 = None self.selection_level = 1 else: self.selected_node1 = selected self.selected_node2 = None self.selection_level = 1 self._update_selected_edge() elif isinstance(selected, Edge): if self.selected_edge is selected and self.selection_level == 2: self.selected_edge = None self.selected_node1 = None self.selected_node2 = None self.selection_level = 0 else: self.selected_edge = selected self.selected_node1 = selected.node_source self.selected_node2 = selected.node_dest self.selection_level = 2 self.mouseMoveEvent(event) def _update_selected_edge(self): for edge in self.edges: if (edge.node_source is self.selected_node1 and edge.node_dest is self.selected_node2 or edge.node_source is self.selected_node2 and edge.node_dest is self.selected_node1): self.selected_edge = edge break else: self.selected_edge = None def _update_selected_colors(self): colors = {} if self.selected_node1 is not None and self.selection_level < 2: direct, indirect = find_connections(self.selected_node1, self.nodes, self.edges) for node in self.nodes: if node in direct or node in indirect: colors[node] = COLOR_CONNECTED else: colors[node] = COLOR_DISCONNECTED for edge in self.edges: if (edge.node_source is self.selected_node1 or edge.node_dest is self.selected_node1): colors[edge] = COLOR_CONNECTED if self.selected_edge is not None: colors[self.selected_edge] = COLOR_SELECTED if self.selected_node1 is not None: colors[self.selected_node1] = COLOR_SELECTED if self.selected_node2 is not None: colors[self.selected_node2] = COLOR_SELECTED self.set_colors(colors) def set_colors(self, colors): for obj in list(self.nodes) + self.edges: default_color = '0.8' if isinstance(obj, DataNode) else '0.5' obj.color = colors.get(obj, default_color) obj.update() def find_connections(node, nodes, edges): direct = [node] indirect = [] current = direct connected = [node] changed = True while changed: changed = False for edge in edges: source = edge.node_source dest = edge.node_dest if source in connected and dest not in connected: current.append(dest) changed = True if dest in connected and source not in connected: current.append(source) changed = True current = indirect connected.extend(current) return direct, indirect if __name__ == '__main__': import sys app = QApplication(sys.argv) app.setAttribute(Qt.AA_UseHighDpiPixmaps) from glue.core.state import load dc = load('links.glu') widget = DataGraphWidget(dc) widget.show() sys.exit(app.exec_()) glueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/qt/link_editor.py0000644000175000017500000002236013657331513024600 0ustar noahfxnoahfximport os from qtpy import QtWidgets from glue.config import link_function, link_helper from glue.utils.decorators import avoid_circular from glue.utils.qt import load_ui from echo.qt import autoconnect_callbacks_to_qt from echo.qt.connect import UserDataWrapper, connect_combo_selection from glue.dialogs.link_editor.state import LinkEditorState __all__ = ['LinkEditor', 'main'] N_COMBO_MAX = 10 def get_function_name(info): item = info[0] if hasattr(item, 'display') and item.display is not None: return item.display else: return item.__name__ class LinkMenu(QtWidgets.QMenu): def __init__(self, parent=None): super(LinkMenu, self).__init__(parent=parent) categories = [] for function in link_function.members: if len(function.output_labels) == 1: categories.append(function.category) for helper in link_helper.members: categories.append(helper.category) categories = ['General'] + sorted(set(categories) - set(['General'])) for category in categories: submenu = self.addMenu(category) for function in link_function.members: if function.category == category and len(function.output_labels) == 1: action = submenu.addAction(get_function_name(function)) action.setData(UserDataWrapper(function)) for helper in link_helper.members: if helper.category == category: action = submenu.addAction(get_function_name(helper)) action.setData(UserDataWrapper(helper)) class LinkEditorWidget(QtWidgets.QWidget): def __init__(self, data_collection, suggested_links=None, parent=None): super(LinkEditorWidget, self).__init__(parent=parent) self._data_collection = data_collection self.state = LinkEditorState(data_collection, suggested_links=suggested_links) self._connections = [] self._ui = load_ui('link_editor_widget.ui', self, directory=os.path.dirname(__file__)) self._handlers = autoconnect_callbacks_to_qt(self.state, self._ui) self._set_up_combos() self._ui.graph_widget.set_data_collection(data_collection, new_links=self.state.links) self._ui.graph_widget.selection_changed.connect(self._on_data_change_graph) self._menu = LinkMenu(parent=self._ui.button_add_link) self._menu.triggered.connect(self._add_link) self._ui.button_add_link.setMenu(self._menu) self._watched_links = [] self.state.add_callback('data1', self._on_data_change) self.state.add_callback('data2', self._on_data_change) self._on_data_change() self.state.add_callback('data1', self._on_data_change_always) self.state.add_callback('data2', self._on_data_change_always) self._on_data_change_always() self.state.add_callback('current_link', self._on_current_link_change) self._on_current_link_change() def _add_link(self, action): self.state.new_link(action.data().data) def _set_up_combos(self): # Set up combo boxes - for now we hard-code the maximum number, but # we could do this more smartly by checking existing links and all # possible links in registry to figure out max number needed. self.att_names1 = [] self.att_combos1 = [] for combo_idx in range(N_COMBO_MAX): label_widget = QtWidgets.QLabel() combo_widget = QtWidgets.QComboBox(parent=self._ui) self.att_names1.append(label_widget) self.att_combos1.append(combo_widget) self._ui.combos1.addWidget(label_widget, combo_idx, 0) self._ui.combos1.addWidget(combo_widget, combo_idx, 1) self.att_names2 = [] self.att_combos2 = [] for combo_idx in range(N_COMBO_MAX): label_widget = QtWidgets.QLabel() combo_widget = QtWidgets.QComboBox(parent=self._ui) self.att_names2.append(label_widget) self.att_combos2.append(combo_widget) self._ui.combos2.addWidget(label_widget, combo_idx, 0) self._ui.combos2.addWidget(combo_widget, combo_idx, 1) @avoid_circular def _on_attribute_combo_change(self, *args, **kwargs): # Force a re-sync of the choices self._handlers['listsel_current_link'].update_widget(self.state.current_link, force=True) @avoid_circular def _on_data_change_graph(self): self.state.data1 = getattr(self._ui.graph_widget.selected_node1, 'data', None) self.state.data2 = getattr(self._ui.graph_widget.selected_node2, 'data', None) @avoid_circular def _on_data_change(self, *args): self._ui.graph_widget.manual_select(self.state.data1, self.state.data2) def _on_data_change_always(self, *args): # This should always run even when the change comes from the graph enabled = self.state.data1 is not None and self.state.data2 is not None self._ui.button_add_link.setEnabled(enabled) self._ui.button_simple_link.setEnabled(enabled) def _on_current_link_change(self, *args): # We update the link details panel on the right for connnection in self._connections: connnection.disconnect() self._connections = [] link = self.state.current_link if link is None: self._ui.button_remove_link.setEnabled(False) self._ui.link_details.setText('') self._ui.combos1_header.hide() self._ui.combos2_header.hide() for widget in self.att_combos1 + self.att_names1 + self.att_combos2 + self.att_names2: widget.hide() return self._ui.button_remove_link.setEnabled(True) self._ui.link_details.setText(link.description) if link.data1 is self.state.data1: data1_names = link.names1 else: data1_names = link.names2 for idx, (label, combo) in enumerate(zip(self.att_names1, self.att_combos1)): if idx < len(data1_names): combo.show() label.show() label.setText(data1_names[idx]) disconnector = connect_combo_selection(link, data1_names[idx], combo) self._connections.append(disconnector) else: label.hide() combo.hide() if link.data1 is self.state.data2: data2_names = link.names1 else: data2_names = link.names2 for idx, (label, combo) in enumerate(zip(self.att_names2, self.att_combos2)): if idx < len(data2_names): combo.show() label.show() label.setText(data2_names[idx]) disconnector = connect_combo_selection(link, data2_names[idx], combo) self._connections.append(disconnector) else: label.hide() combo.hide() # Headers aren't needed if data2_names is 0 (legacy mode for old link # helpers where all attributes are 'inputs') if len(data2_names) == 0: self._ui.combos1_header.hide() self._ui.combos2_header.hide() else: self._ui.combos1_header.show() self._ui.combos2_header.show() self._ui.graph_widget.set_links(self.state.links) # When the user changes one of the attributes, we need to update the # main list of links to show the attributes being linked. This is # actually tricker than it sounds, and to solve this we listen for # changes in any of the link properties. We don't need to unsubscribe # since there's no harm in keeping them connected, and we only need to # subscribe to links that have been shown at least once as the other # ones can't be changed by users. if link not in self._watched_links: link.add_global_callback(self._on_attribute_combo_change) self._watched_links.append(link) class LinkEditor(QtWidgets.QDialog): def __init__(self, data_collection, suggested_links=None, parent=None): super(LinkEditor, self).__init__(parent=parent) self._ui = load_ui('link_editor_dialog.ui', self, directory=os.path.dirname(__file__)) self.link_widget = LinkEditorWidget(data_collection, suggested_links=suggested_links, parent=self) self._ui.layout().insertWidget(1, self.link_widget) def accept(self, *args): self.link_widget.state.update_links_in_collection() super(LinkEditor, self).accept(*args) @classmethod def update_links(cls, collection, suggested_links=None): widget = cls(collection, suggested_links=suggested_links) widget._ui.exec_() def main(): # pragma: no cover import numpy as np from glue.main import load_plugins from glue.utils.qt import get_qapp from glue.core import Data, DataCollection load_plugins() app = get_qapp() dc = DataCollection() for i in range(10): x = np.array([1, 2, 3]) d = Data(label='data_{0:02d}'.format(i), x=x, y=x * 2) dc.append(d) LinkEditor.update_links(dc) glueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/state.py0000644000175000017500000003062413657331513022773 0ustar noahfxnoahfxfrom types import FunctionType, MethodType from inspect import getfullargspec from glue.config import link_function from glue.core.component_link import ComponentLink from glue.core.link_helpers import LinkCollection from glue.core.state_objects import State from echo import CallbackProperty, SelectionCallbackProperty, delay_callback from glue.core.data_combo_helper import DataCollectionComboHelper, ComponentIDComboHelper __all__ = ['LinkEditorState', 'EditableLinkFunctionState'] class LinkEditorState(State): data1 = SelectionCallbackProperty() data2 = SelectionCallbackProperty() att1 = SelectionCallbackProperty() att2 = SelectionCallbackProperty() current_link = SelectionCallbackProperty() link_type = SelectionCallbackProperty() restrict_to_suggested = CallbackProperty(False) def __init__(self, data_collection, suggested_links=None): super(LinkEditorState, self).__init__() # Find identity function for func in link_function: if func.function.__name__ == 'identity': self._identity = func break else: raise ValueError("Could not find identity link function") self.data1_helper = DataCollectionComboHelper(self, 'data1', data_collection) self.data2_helper = DataCollectionComboHelper(self, 'data2', data_collection) self.att1_helper = ComponentIDComboHelper(self, 'att1', pixel_coord=True, world_coord=True) self.att2_helper = ComponentIDComboHelper(self, 'att2', pixel_coord=True, world_coord=True) # FIXME: We unregister the combo helpers straight away to avoid issues with # leftover references once the dialog is closed. This shouldn't happen # ideally so in future we should investigate how to avoid it. self.data1_helper.unregister(data_collection.hub) self.data2_helper.unregister(data_collection.hub) self.data_collection = data_collection # Convert links to editable states links = [EditableLinkFunctionState(link) for link in data_collection.external_links] # If supplied, also add suggested links and make sure we toggle the # suggestion flag on the link state so that we can handle suggestions # differently in the link viewer. if suggested_links is not None: for link in suggested_links: link_state = EditableLinkFunctionState(link) link_state.suggested = True links.append(link_state) self.links = links if len(data_collection) == 2: self.data1, self.data2 = self.data_collection else: self.data1 = self.data2 = None self._on_data_change() self._on_data1_change() self._on_data2_change() self.add_callback('data1', self._on_data1_change) self.add_callback('data2', self._on_data2_change) self.add_callback('restrict_to_suggested', self._on_data_change) LinkEditorState.current_link.set_display_func(self, self._display_link) @property def visible_links(self): if self.data1 is None or self.data2 is None: return [] links = [] for link in self.links: if link.suggested or not self.restrict_to_suggested: if ((link.data1 is self.data1 and link.data2 is self.data2) or (link.data1 is self.data2 and link.data2 is self.data1)): links.append(link) return links def flip_data(self, *args): # FIXME: since the links will be the same in the list of current links, # we can make sure we reselect the same one as before - it would be # better if this didn't change in the first place though. _original_current_link = self.current_link with delay_callback(self, 'data1', 'data2'): self.data1, self.data2 = self.data2, self.data1 self.current_link = _original_current_link def _on_data1_change(self, *args): if self.data1 is self.data2 and self.data1 is not None: self.data2 = next(data for data in self.data_collection if data is not self.data1) else: self._on_data_change() self.att1_helper.set_multiple_data([] if self.data1 is None else [self.data1]) def _on_data2_change(self, *args): if self.data2 is self.data1 and self.data2 is not None: self.data1 = next(data for data in self.data_collection if data is not self.data2) else: self._on_data_change() self.att2_helper.set_multiple_data([] if self.data2 is None else [self.data2]) def _on_data_change(self, *args): links = self.visible_links with delay_callback(self, 'current_link'): LinkEditorState.current_link.set_choices(self, links) if len(links) > 0: self.current_link = links[0] def _display_link(self, link): if link.suggested: return str(link) + ' [Suggested]' else: return str(link) def simple_link(self, *args): self.new_link(self._identity) self.current_link.x = self.att1 self.current_link.y = self.att2 def new_link(self, function_or_helper): if hasattr(function_or_helper, 'function'): link = EditableLinkFunctionState(function_or_helper.function, data1=self.data1, data2=self.data2, names2=function_or_helper.output_labels, description=function_or_helper.info, display=function_or_helper.function.__name__) elif function_or_helper.helper.cid_independent: # This shortcut is needed for e.g. the WCS auto-linker, which has a dynamic # description but doesn't need to take any component IDs. link = EditableLinkFunctionState(function_or_helper.helper(data1=self.data1, data2=self.data2)) else: link = EditableLinkFunctionState(function_or_helper.helper, data1=self.data1, data2=self.data2) self.links.append(link) with delay_callback(self, 'current_link'): self._on_data_change() self.current_link = link def remove_link(self): self.links.remove(self.current_link) self._on_data_change() def update_links_in_collection(self): links = [link_state.link for link_state in self.links] self.data_collection.set_links(links) class EditableLinkFunctionState(State): function = CallbackProperty() data1 = CallbackProperty() data2 = CallbackProperty() description = CallbackProperty() display = CallbackProperty() suggested = CallbackProperty(False) def __new__(cls, function, data1=None, data2=None, cids1=None, cid_out=None, names1=None, names2=None, display=None, description=None): if isinstance(function, ComponentLink): names1 = function.input_names names2 = [function.output_name] elif isinstance(function, LinkCollection): names1 = function.labels1 names2 = function.labels2 elif type(function) is type and issubclass(function, LinkCollection): names1 = function.labels1 names2 = function.labels2 class CustomizedStateClass(EditableLinkFunctionState): pass if names1 is None: names1 = getfullargspec(function)[0] if names2 is None: names2 = [] setattr(CustomizedStateClass, 'names1', names1) setattr(CustomizedStateClass, 'names2', names2) for index, input_arg in enumerate(CustomizedStateClass.names1): setattr(CustomizedStateClass, input_arg, SelectionCallbackProperty(default_index=index)) for index, output_arg in enumerate(CustomizedStateClass.names2): setattr(CustomizedStateClass, output_arg, SelectionCallbackProperty(default_index=index)) return super(EditableLinkFunctionState, cls).__new__(CustomizedStateClass) def __init__(self, function, data1=None, data2=None, cids1=None, cids2=None, names1=None, names2=None, display=None, description=None): super(EditableLinkFunctionState, self).__init__() if isinstance(function, ComponentLink): self._function = function.get_using() self._inverse = function.get_inverse() self._helper_class = None cids1 = function.get_from_ids() cids2 = function.get_to_ids() data1 = cids1[0].parent data2 = cids2[0].parent self.display = self._function.__name__ self.description = function.description elif isinstance(function, LinkCollection): self._function = None self._helper_class = function.__class__ cids1 = function.cids1 cids2 = function.cids2 data1 = cids1[0].parent # To be backward-compatible with cases where @link_helper doesn't # include output labels, we need to assume cids2 can be empty # in which case we look for the second dataset inside cids1 if len(cids2) > 0: data2 = cids2[0].parent else: for cid in cids1[1:]: if cid.parent is not data1: data2 = cid.parent break else: raise ValueError("Could not determine second dataset in link") self.display = function.display self.description = function.description self._mode = 'helper' elif type(function) is type and issubclass(function, LinkCollection): self._function = None self._helper_class = function self.display = function.display self.description = function.description elif isinstance(function, (FunctionType, MethodType)): self._function = function self._inverse = None self._helper_class = None self.inverse = None self.display = display self.description = description else: raise TypeError("Unexpected type for 'function': {0}".format(type(function))) self.data1 = data1 self.data2 = data2 for name in self.names1: helper = ComponentIDComboHelper(self, name, pixel_coord=True, world_coord=True) helper.append_data(data1) if len(self.names2) == 0: # legacy mode for old link helpers helper.append_data(data2) setattr(self, '_' + name + '_helper', helper) for name in self.names2: helper = ComponentIDComboHelper(self, name, pixel_coord=True, world_coord=True) helper.append_data(data2) setattr(self, '_' + name + '_helper', helper) if cids1 is not None: for name, cid in zip(self.names1, cids1): setattr(self, name, cid) if cids2 is not None: for name, cid in zip(self.names2, cids2): setattr(self, name, cid) def __str__(self): if len(self.names1) > 0 or len(self.names2) > 0: # Construct display of linked cids cids1 = [str(getattr(self, cid)) for cid in self.names1] cids2 = [str(getattr(self, cid)) for cid in self.names2] cids = ','.join(cids1) + ' <-> ' + ','.join(cids2) return '{0}({1})'.format(self.display, cids) else: return self.display @property def link(self): """ Return a `glue.core.component_link.ComponentLink` object. """ if self._function is not None: cids1 = [getattr(self, name) for name in self.names1] cid_out = getattr(self, self.names2[0]) return ComponentLink(cids1, cid_out, using=self._function, inverse=self._inverse) else: cids1 = [getattr(self, name) for name in self.names1] cids2 = [getattr(self, name) for name in self.names2] return self._helper_class(cids1=cids1, cids2=cids2, data1=self.data1, data2=self.data2) glueviz-1.0.1+dfsg.orig/glue/dialogs/link_editor/__init__.py0000644000175000017500000000000013455362716023400 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/0000755000175000017500000000000013752535025023204 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/0000755000175000017500000000000013752535025023630 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/tests/0000755000175000017500000000000013752535025024772 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/tests/__init__.py0000644000175000017500000000000013502206677027072 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/tests/test_component_arithmetic.py0000644000175000017500000001422013605357235032617 0ustar noahfxnoahfxfrom unittest.mock import patch from numpy.testing import assert_equal from glue.core import Data, DataCollection, HubListener, ComponentID from glue.core import message as msg from glue.core.component_link import ComponentLink from glue.core.parse import ParsedCommand, ParsedComponentLink from ..component_arithmetic import ArithmeticEditorWidget, EquationEditorDialog def auto_accept(equation): def exec_replacement(self): self.ui.expression.clear() self.ui.expression.insertPlainText(equation) self.accept() return exec_replacement def auto_reject(): def exec_replacement(self): self.ui.expression.clear() self.reject() return exec_replacement class ChangeListener(HubListener): def __init__(self, data, *args, **kwargs): super(ChangeListener, self).__init__(*args, **kwargs) self.data = data self.removed = [] self.added = [] self.renamed = [] self.reordered = [] self.numeric = False self.register_to_hub(data.hub) def _has_data(self, message): return message.sender is self.data def register_to_hub(self, hub): hub.subscribe(self, msg.DataAddComponentMessage, handler=self._component_added, filter=self._has_data) hub.subscribe(self, msg.DataRemoveComponentMessage, handler=self._component_removed, filter=self._has_data) hub.subscribe(self, msg.DataRenameComponentMessage, handler=self._component_renamed, filter=self._has_data) hub.subscribe(self, msg.DataReorderComponentMessage, handler=self._components_reordered, filter=self._has_data) hub.subscribe(self, msg.NumericalDataChangedMessage, handler=self._numerical_changed, filter=self._has_data) def _component_added(self, message): self.added.append(message.component_id) def _component_removed(self, message): self.removed.append(message.component_id) def _component_renamed(self, message): self.renamed.append(message.component_id) def _components_reordered(self, message): self.reordered = message.component_ids def _numerical_changed(self, message): self.numeric = True def assert_exact_changes(self, added=[], removed=[], renamed=[], reordered=[], numerical=False): assert set(added) == set(self.added) assert set(removed) == set(self.removed) assert set(renamed) == set(self.renamed) assert reordered == self.reordered assert numerical is self.numeric class TestArithmeticEditorWidget: def setup_method(self): self.data1 = Data(x=[1, 2, 3], y=[3.5, 4.5, -1.0], z=['a', 'r', 'w']) self.data2 = Data(a=[3, 4, 1], b=[1.5, -2.0, 3.5], c=['y', 'e', 'r']) # Add a derived component so that we can test how we deal with existing ones components = dict((cid.label, cid) for cid in self.data2.components) pc = ParsedCommand('{a}', components) link = ParsedComponentLink(ComponentID('d'), pc) self.data2.add_component_link(link) self.data_collection = DataCollection([self.data1, self.data2]) link = ComponentLink([self.data1.id['x']], self.data2.id['a']) self.data_collection.add_link(link) self.listener1 = ChangeListener(self.data1) self.listener2 = ChangeListener(self.data2) def test_nochanges(self): editor = ArithmeticEditorWidget(self.data_collection) editor.show() editor.button_ok.click() self.listener1.assert_exact_changes() self.listener2.assert_exact_changes() editor.close() def test_add_derived_and_rename(self): editor = ArithmeticEditorWidget(self.data_collection) editor.show() with patch.object(EquationEditorDialog, 'exec_', auto_accept('{x} + {y}')): editor.button_add_derived.click() item = list(editor.list)[0] item.setText(0, 'new') editor.button_ok.click() self.listener1.assert_exact_changes(added=[self.data1.id['new']]) self.listener2.assert_exact_changes() assert_equal(self.data1['new'], [4.5, 6.5, 2.0]) editor.close() def test_add_derived_and_cancel(self): editor = ArithmeticEditorWidget(self.data_collection) editor.show() with patch.object(EquationEditorDialog, 'exec_', auto_reject()): editor.button_add_derived.click() assert len(editor.list) == 0 editor.close() def test_edit_existing_equation(self): assert_equal(self.data2['d'], [3, 4, 1]) editor = ArithmeticEditorWidget(self.data_collection) editor.show() assert len(editor.list) == 0 editor.combosel_data.setCurrentIndex(1) assert len(editor.list) == 1 editor.list.select_cid(self.data2.id['d']) with patch.object(EquationEditorDialog, 'exec_', auto_accept('{a} + {b}')): editor.button_edit_derived.click() editor.button_ok.click() self.listener1.assert_exact_changes() self.listener2.assert_exact_changes(numerical=True) assert_equal(self.data2['d'], [4.5, 2.0, 4.5]) editor.close() # TODO: add an updated version of the following test back once we add # support for using derived components in derived components. # # def test_edit_equation_after_rename(self): # editor = ArithmeticEditorWidget(self.data_collection) # editor.show() # editor.combosel_data.setCurrentIndex(1) # editor.list['main'].select_cid(self.data2.id['a']) # editor.list['main'].selected_item.setText(0, 'renamed') # editor.list.select_cid(self.data2.id['d']) # with patch.object(EquationEditorDialog, 'exec_', auto_accept('{renamed} + 1')): # editor.button_edit_derived.click() # editor.button_ok.click() # self.listener1.assert_exact_changes() # self.listener2.assert_exact_changes(renamed=[self.data2.id['renamed']], numerical=True) # assert_equal(self.data2['d'], [4, 5, 2]) glueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/tests/test_equation_editor.py0000644000175000017500000000621113605357235031600 0ustar noahfxnoahfximport pytest from qtpy import QtTest from qtpy.QtCore import Qt from glue.core import Data from ..equation_editor import EquationEditorDialog class TestEquationEditor: def setup_method(self, method): self.data = Data(x=[1, 2, 3], y=[3, 4, 5]) self.dialog = EquationEditorDialog(label='z', data=self.data, equation='') def test_empty(self): assert not self.dialog.ui.button_ok.isEnabled() assert self.dialog.ui.label_status.text() == '' @pytest.mark.parametrize('expression', ['1', '1 + {x}', '1 * np.sin({y}) + {x}']) def test_valid_cases(self, expression): self.dialog.expression.insertPlainText(expression) assert self.dialog.ui.button_ok.isEnabled() assert self.dialog.ui.label_status.text() == 'Valid expression' self.dialog.ui.button_ok.click() assert self.dialog._get_raw_command() == expression def test_invalid_syntax(self): self.dialog.expression.insertPlainText('1 + {x') assert not self.dialog.ui.button_ok.isEnabled() assert self.dialog.ui.label_status.text() == 'Incomplete or invalid syntax' def test_unknown_component(self): self.dialog.expression.insertPlainText('1 + {z}') assert not self.dialog.ui.button_ok.isEnabled() assert self.dialog.ui.label_status.text() == 'Invalid component: z' def test_undefined_name(self): self.dialog.expression.insertPlainText('1 + {x} + abc') assert not self.dialog.ui.button_ok.isEnabled() assert self.dialog.ui.label_status.text() == "name 'abc' is not defined" def test_insert_component(self): self.dialog.expression.insertPlainText('1 + ') self.dialog.button_insert.click() assert self.dialog.ui.label_status.text() == 'Valid expression' self.dialog.ui.button_ok.click() assert self.dialog._get_raw_command() == '1 + {Pixel Axis 0 [x]}' def test_nolabel(self): self.dialog.ui.text_label.setText('') self.dialog.expression.insertPlainText('1 + {x}') assert not self.dialog.ui.button_ok.isEnabled() assert self.dialog.ui.label_status.text() == 'Attribute name not set' def test_typing(self): # This ensures that the code that highlights syntax gets called, # and also ensures we can test undoing. chars = (Qt.Key_1, Qt.Key_Space, Qt.Key_Plus, Qt.Key_Space, Qt.Key_BraceLeft, Qt.Key_X, Qt.Key_BraceRight) for char in chars: QtTest.QTest.keyClick(self.dialog.expression, char) assert self.dialog.expression.toPlainText() == '1 + {x}' QtTest.QTest.keyClick(self.dialog.expression, Qt.Key_Z, Qt.ControlModifier) assert self.dialog.expression.toPlainText() == '1 + {x' for i in range(4): QtTest.QTest.keyClick(self.dialog.expression, Qt.Key_Z, Qt.ControlModifier) assert self.dialog.expression.toPlainText() == '1 ' def test_cancel(self): self.dialog.expression.insertPlainText('1 + {x}') assert self.dialog.ui.label_status.text() == 'Valid expression' self.dialog.ui.button_cancel.click() assert self.dialog.final_expression is None glueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/__init__.py0000644000175000017500000000010113502206677025732 0ustar noahfxnoahfxfrom .component_arithmetic import ArithmeticEditorWidget # noqa glueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/equation_editor.py0000644000175000017500000002121013657331513027371 0ustar noahfxnoahfximport os from collections import deque, OrderedDict from qtpy import QtWidgets, QtCore from qtpy.QtCore import Qt from echo import SelectionCallbackProperty from echo.qt import connect_combo_selection from glue.core.parse import InvalidTagError, ParsedCommand, TAG_RE from glue.utils.qt import load_ui, CompletionTextEdit __all__ = ['EquationEditorDialog'] class ColorizedCompletionTextEdit(CompletionTextEdit): updated = QtCore.Signal() def __init__(self, *args, **kwargs): super(ColorizedCompletionTextEdit, self).__init__(*args, **kwargs) self.setAlignment(Qt.AlignLeft) self.setUndoRedoEnabled(False) self._undo_stack = deque(maxlen=100) self._redo_stack = deque(maxlen=100) def insertPlainText(self, *args): super(ColorizedCompletionTextEdit, self).insertPlainText(*args) self.reformat_text() self.updated.emit() self.setAlignment(Qt.AlignLeft) def keyReleaseEvent(self, event): super(ColorizedCompletionTextEdit, self).keyReleaseEvent(event) self.reformat_text() self.updated.emit() def keyPressEvent(self, event): super(ColorizedCompletionTextEdit, self).keyPressEvent(event) # NOTE: We use == here instead of & for the modifiers because we don't # want to catch e.g. control-shift-z or other combinations. if event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_Z: if len(self._undo_stack) > 1: self._undo_stack.pop() self.setHtml(self._undo_stack[-1]) text = self.toPlainText() tc = self.textCursor() tc.setPosition(len(text)) self.setTextCursor(tc) self._cache = self._undo_stack[-1] self.updated.emit() def reformat_text(self): # If the text hasn't changed, no need to reformat if self.toPlainText() == getattr(self, '_cache', None): return # Here every time a key is released, we re-colorize the expression. # We show valid components in blue, and invalid ones in red. We # recognized components because they contain a ":" which is not valid # Python syntax (except if one considers lambda functions, but we can # probably ignore that here) text = self.toPlainText() # We need to be careful with < and > otherwise they get erased text = text.replace('<', '<').replace('>', '>') def format_components(m): component = m.group(0) if component in self.word_list: return "" + component + "" else: return "" + component + "" html = TAG_RE.sub(format_components, text) tc = self.textCursor() pos = tc.position() self._undo_stack.append(html) self.setHtml(html) # Sometimes the HTML gets rid of double spaces so we have to make # sure the position isn't greater than the text length. text = self.toPlainText() pos = min(pos, len(text)) tc.setPosition(pos) self.setTextCursor(tc) self._cache = self.toPlainText() class EquationEditorDialog(QtWidgets.QDialog): tip_text = ("Note: Attribute names in the expression should be surrounded " "by {{ }} brackets (e.g. {{{example}}}), and you can use " "Numpy functions using np.<function>, as well as any " "other function defined in your config.py file.

    " "Example expressions:

    " " - Subtract 10 from '{example}': {{{example}}} - 10
    " " - Scale '{example}' to [0:1]: ({{{example}}} - np.min({{{example}}})) / np.ptp({{{example}}})
    " " - Multiply '{example}' by pi: {{{example}}} * np.pi
    " " - Use masking: {{{example}}} * ({{{example}}} < 1)
    ") placeholder_text = ("Type any mathematical expression here - " "you can include attribute names from the " "drop-down below by selecting them and " "clicking 'Insert'. See below for examples " "of valid expressions") attribute = SelectionCallbackProperty() def __init__(self, label=None, data=None, equation=None, references=None, parent=None): super(EquationEditorDialog, self).__init__(parent=parent) self.ui = load_ui('equation_editor.ui', self, directory=os.path.dirname(__file__)) # Get mapping from label to component ID if references is not None: self.references = references elif data is not None: self.references = OrderedDict() for cid in data.coordinate_components + data.main_components: self.references[cid.label] = cid example = sorted(self.references, key=len)[0] self.ui.text_label.setPlaceholderText("New attribute name") self.ui.expression.setPlaceholderText(self.placeholder_text.format(example=example)) self.ui.label.setText(self.tip_text.format(example=example)) if label is not None: self.ui.text_label.setText(label) self.ui.text_label.textChanged.connect(self._update_status) # Populate component combo EquationEditorDialog.attribute.set_choices(self, list(self.references)) self._connection = connect_combo_selection(self, 'attribute', self.ui.combosel_component) # Set up labels for auto-completion labels = ['{' + l + '}' for l in self.references] self.ui.expression.set_word_list(labels) if equation is not None: self.ui.expression.insertPlainText(equation) self.ui.button_ok.clicked.connect(self.accept) self.ui.button_cancel.clicked.connect(self.reject) self.ui.button_insert.clicked.connect(self._insert_component) self.ui.expression.updated.connect(self._update_status) self._update_status() def _insert_component(self): label = self.attribute self.expression.insertPlainText('{' + label + '}') def _update_status(self): # If the text hasn't changed, no need to check again if hasattr(self, '_cache') and self._cache == (self.ui.text_label.text(), self._get_raw_command()): return if self.ui.text_label.text() == "": self.ui.label_status.setStyleSheet('color: red') self.ui.label_status.setText("Attribute name not set") self.ui.button_ok.setEnabled(False) elif self._get_raw_command() == "": self.ui.label_status.setText("") self.ui.button_ok.setEnabled(False) else: try: pc = self._get_parsed_command() pc.evaluate_test() except SyntaxError: self.ui.label_status.setStyleSheet('color: red') self.ui.label_status.setText("Incomplete or invalid syntax") self.ui.button_ok.setEnabled(False) except InvalidTagError as exc: self.ui.label_status.setStyleSheet('color: red') self.ui.label_status.setText("Invalid component: {0}".format(exc.tag)) self.ui.button_ok.setEnabled(False) except Exception as exc: self.ui.label_status.setStyleSheet('color: red') self.ui.label_status.setText(str(exc)) self.ui.button_ok.setEnabled(False) else: self.ui.label_status.setStyleSheet('color: green') self.ui.label_status.setText("Valid expression") self.ui.button_ok.setEnabled(True) self._cache = self.ui.text_label.text(), self._get_raw_command() def _get_raw_command(self): return str(self.ui.expression.toPlainText()) def _get_parsed_command(self): expression = self._get_raw_command() return ParsedCommand(expression, self.references) def get_final_label_and_parsed_command(self): return self.ui.text_label.text(), self._get_parsed_command() def accept(self): self.final_expression = self._get_parsed_command()._cmd super(EquationEditorDialog, self).accept() def reject(self): self.final_expression = None super(EquationEditorDialog, self).reject() if __name__ == "__main__": # pragma: nocover from glue.utils.qt import get_qapp app = get_qapp() from glue.core.data import Data d = Data(label='test1', x=[1, 2, 3], y=[2, 3, 4], z=[3, 4, 5]) widget = EquationEditorDialog(data=d, equation='') widget.exec_() glueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/component_arithmetic.ui0000644000175000017500000001505213522025220030367 0ustar noahfxnoahfx Form 0 0 478 371 Arithmetic editor 5 10 5 10 5 Qt::Horizontal 40 20 50 false Dataset: 200 0 QComboBox::AdjustToMinimumContentsLength Qt::Horizontal 40 20 Qt::Vertical QSizePolicy::Fixed 10 5 50 false In this dialog you can define data attributes based on other attributes using arithmetic operations. First, select the dataset above, then you will be able to create/edit arithmetic attributes for that dataset below. Qt::AlignJustify|Qt::AlignVCenter true Qt::Vertical QSizePolicy::Fixed 10 5 New arithmetic attribute Qt::Horizontal 40 20 Edit selected attribute Qt::Horizontal 40 20 Remove selected QAbstractItemView::InternalMove Name Expression Qt::AlignCenter Qt::Horizontal 40 20 Cancel OK true ComponentTreeWidget QTreeWidget
    glue.dialogs.common.qt.component_tree_widget
    glueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/component_arithmetic.py0000644000175000017500000002600013657331513030413 0ustar noahfxnoahfximport os from collections import defaultdict, Counter from qtpy import QtWidgets, QtGui from qtpy.QtCore import Qt from echo import SelectionCallbackProperty from echo.qt import connect_combo_selection from glue.core import ComponentID from glue.core.parse import ParsedComponentLink, ParsedCommand from glue.utils.qt import load_ui from glue.core.message import NumericalDataChangedMessage from glue.dialogs.component_arithmetic.qt.equation_editor import EquationEditorDialog __all__ = ['ArithmeticEditorWidget'] class ArithmeticEditorWidget(QtWidgets.QDialog): data = SelectionCallbackProperty() def __init__(self, data_collection=None, initial_data=None, parent=None): super(ArithmeticEditorWidget, self).__init__(parent=parent) self.ui = load_ui('component_arithmetic.ui', self, directory=os.path.dirname(__file__)) self.list = self.ui.list_derived_components self.data_collection = data_collection self._components_derived = defaultdict(list) self._components_other = defaultdict(list) self._state = defaultdict(dict) for data in data_collection: # First find all derived components (only ones based on arithmetic # expressions) self._components_derived[data] = [] for cid in data.derived_components: comp = data.get_component(cid) if isinstance(comp.link, ParsedComponentLink): comp_state = {} comp_state['cid'] = cid comp_state['label'] = cid.label comp_state['equation'] = comp.link._parsed self._state[data][cid] = comp_state self._components_derived[data].append(cid) # Keep track of all other components self._components_other[data] = [] for cid in data.components: if cid not in self._components_derived[data]: self._components_other[data].append(cid) # Populate data combo ArithmeticEditorWidget.data.set_choices(self, list(self.data_collection)) ArithmeticEditorWidget.data.set_display_func(self, lambda x: x.label) self._connection = connect_combo_selection(self, 'data', self.ui.combosel_data) if initial_data is None: self.ui.combosel_data.setCurrentIndex(0) else: self.data = initial_data self.ui.combosel_data.currentIndexChanged.connect(self._update_component_lists) self._update_component_lists() self.ui.button_add_derived.clicked.connect(self._add_derived_component) self.ui.button_edit_derived.clicked.connect(self._edit_derived_component) self.ui.button_remove_derived.clicked.connect(self._remove_derived_component) self.ui.list_derived_components.itemSelectionChanged.connect(self._update_selection_derived) self._update_selection_derived() self.ui.list_derived_components.itemChanged.connect(self._update_state) self.ui.list_derived_components.order_changed.connect(self._update_state) self.ui.list_derived_components.itemDoubleClicked.connect(self._edit_derived_component) self.ui.button_ok.clicked.connect(self.accept) self.ui.button_cancel.clicked.connect(self.reject) def _update_selection_derived(self): enabled = self.list.selected_cid is not None self.button_edit_derived.setEnabled(enabled) self.button_remove_derived.setEnabled(enabled) def _update_component_lists(self, *args): # This gets called when the data is changed and we need to update the # components shown in the lists. self.list.blockSignals(True) mapping = {} for cid in self.data.components: mapping[cid] = cid.label self.list.clear() for cid in self._components_derived[self.data]: label = self._state[self.data][cid]['label'] if self._state[self.data][cid]['equation'] is None: expression = '' else: expression = self._state[self.data][cid]['equation'].render(mapping) self.list.add_cid_and_label(cid, [label, expression], editable=False) self.list.blockSignals(False) self._validate() def _validate(self): # Construct a list of all labels for the current dataset so that # we can check which ones are duplicates labels = [c.label for c in self._components_other[self.data]] labels.extend([c['label'] for c in self._state[self.data].values()]) if len(labels) == 0: return label_count = Counter(labels) # It's possible that the duplicates are entirely for components not # shown in this editor, so we keep track here of whether an invalid # component has been found. invalid = False if label_count.most_common(1)[0][1] > 1: # If we are here, there are duplicates somewhere in the list # of components. brush_red = QtGui.QBrush(Qt.red) brush_black = QtGui.QBrush(Qt.black) self.list.blockSignals(True) for item in self.list: label = item.text(0) if label_count[label] > 1: item.setForeground(0, brush_red) invalid = True else: item.setForeground(0, brush_black) self.list.blockSignals(False) if invalid: self.ui.label_status.setStyleSheet('color: red') self.ui.label_status.setText('Error: some components have duplicate names') self.ui.button_ok.setEnabled(False) self.ui.combosel_data.setEnabled(False) else: self.ui.label_status.setStyleSheet('') self.ui.label_status.setText('') self.ui.button_ok.setEnabled(True) self.ui.combosel_data.setEnabled(True) def _update_state(self, *args): self._components_derived[self.data] = [] for item in self.list: cid = item.data(0, Qt.UserRole) self._state[self.data][cid]['label'] = item.text(0) self._components_derived[self.data].append(cid) self._update_component_lists() def _remove_derived_component(self, *args): cid = self.list.selected_cid if cid is not None: self._components_derived[self.data].remove(cid) self._state[self.data].pop(cid) self._update_component_lists() def _add_derived_component(self, *args): comp_state = {} comp_state['cid'] = ComponentID('') comp_state['label'] = '' comp_state['equation'] = None self._components_derived[self.data].append(comp_state['cid']) self._state[self.data][comp_state['cid']] = comp_state self._update_component_lists() self.list.select_cid(comp_state['cid']) result = self._edit_derived_component() if not result: # user cancelled self._components_derived[self.data].remove(comp_state['cid']) self._state[self.data].pop(comp_state['cid']) self._update_component_lists() def _edit_derived_component(self, event=None): derived_item = self.list.selected_item if derived_item is None: return False derived_cid = self.list.selected_cid # Note, we put the pixel/world components last as it's most likely the # user wants to use one of the main components. mapping = {} references = {} for cid in (self.data.main_components + self.data.pixel_component_ids + self.data.world_component_ids): if cid is not derived_cid: mapping[cid] = cid.label references[cid.label] = cid label = self._state[self.data][derived_cid]['label'] if self._state[self.data][derived_cid]['equation'] is None: equation = None else: equation = self._state[self.data][derived_cid]['equation'].render(mapping) dialog = EquationEditorDialog(label=label, equation=equation, references=references, parent=self) dialog.setWindowFlags(self.windowFlags() | Qt.Window) dialog.setFocus() dialog.raise_() dialog.exec_() if dialog.final_expression is None: return False name, equation = dialog.get_final_label_and_parsed_command() self._state[self.data][derived_cid]['label'] = name self._state[self.data][derived_cid]['equation'] = equation derived_item.setText(0, name) # Make sure we update the component list here since the equation may # have changed and we need to update the preview self._update_component_lists() return True def accept(self): for data in self._components_derived: cids_derived = self._components_derived[data] cids_other = self._components_other[data] cids_all = cids_other + cids_derived cids_existing = data.components components = dict((cid.uuid, cid) for cid in data.components) # First deal with renaming of components for cid_new in cids_derived: label = self._state[data][cid_new]['label'] if label != cid_new.label: cid_new.label = label # Second deal with the removal of components for cid_old in cids_existing: if not any(cid_old is cid_new for cid_new in cids_all): data.remove_component(cid_old) # Third, update/add arithmetic expressions as needed for cid_new in cids_derived: if any(cid_new is cid_old for cid_old in cids_existing): comp = data.get_component(cid_new) if comp.link._parsed._cmd != self._state[data][cid_new]['equation']._cmd: comp.link._parsed._cmd = self._state[data][cid_new]['equation']._cmd comp.link._parsed._references = components if data.hub: msg = NumericalDataChangedMessage(data) data.hub.broadcast(msg) else: pc = ParsedCommand(self._state[data][cid_new]['equation']._cmd, components) link = ParsedComponentLink(cid_new, pc) data.add_component_link(link) # Findally, reorder components as needed data.reorder_components(cids_all) super(ArithmeticEditorWidget, self).accept() if __name__ == "__main__": # pragma: nocover from glue.utils.qt import get_qapp app = get_qapp() import numpy as np from glue.core.data import Data from glue.core.data_collection import DataCollection x = np.random.random((5, 5)) y = x * 3 dc = DataCollection() dc.append(Data(label='test1', x=x, y=y)) dc.append(Data(label='test2', a=x, b=y)) widget = ArithmeticEditorWidget(dc) widget.exec_() glueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/qt/equation_editor.ui0000644000175000017500000001234213522025220027346 0ustar noahfxnoahfx Dialog 0 0 477 400 Equation Editor 5 10 10 10 5 5 = Qt::Horizontal 40 20 0 0 16777215 16777215 0 0 75 true Available attributes: 0 0 QComboBox::AdjustToMinimumContentsLength 100 16777215 Insert 0 0 false Qt::AlignJustify|Qt::AlignVCenter true Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse status Qt::Horizontal 40 20 Cancel OK true ColorizedCompletionTextEdit QTextEdit
    glue.dialogs.component_arithmetic.qt.equation_editor
    glueviz-1.0.1+dfsg.orig/glue/dialogs/component_arithmetic/__init__.py0000644000175000017500000000000013502206677025304 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/__init__.py0000644000175000017500000000000013455362716021075 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/README.md0000644000175000017500000000056313455362716020261 0ustar noahfxnoahfxAbout ===== These are GUI dialogs that could be re-used in other glue applications. These will often be dialogs that allow the user to change the underlying glue state, for example creating links, editing the data, creating new components. Each dialog should be included in a directory, and should contain a sub-directory for each specific GUI framework (e.g. ``qt/``).glueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/0000755000175000017500000000000013752535025021440 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/tests/0000755000175000017500000000000013752535025022602 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/tests/__init__.py0000644000175000017500000000000013455362716024706 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/qt/0000755000175000017500000000000013752535025022064 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/qt/tests/0000755000175000017500000000000013752535025023226 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/qt/tests/__init__.py0000644000175000017500000000000013455362716025332 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/qt/tests/test_subset_facet.py0000644000175000017500000000222213605357235027306 0ustar noahfxnoahfxfrom unittest.mock import patch from matplotlib import cm from glue.core import Data, DataCollection from ..subset_facet import SubsetFacetDialog patched_facet = patch('glue.dialogs.subset_facet.qt.subset_facet.facet_subsets') class TestSubsetFacet(object): def setup_method(self, method): d = Data(x=[1, 2, 3]) dc = DataCollection([d]) self.collect = dc self.s = dc.new_subset_group() def test_limits(self): s = SubsetFacetDialog(self.collect) s.state.data = self.collect[0] s.state.att = self.collect[0].id['x'] assert s.state.v_min == 1 assert s.state.v_max == 3 def test_get_set_cmap(self): s = SubsetFacetDialog(self.collect) assert s.state.cmap is cm.RdYlBu def test_apply(self): with patched_facet as p: s = SubsetFacetDialog(self.collect) s.state.data = self.collect[0] s.state.att = self.collect[0].id['x'] s._apply() p.assert_called_once_with(self.collect, s.state.att, lo=1, hi=3, steps=5, log=False) glueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/qt/__init__.py0000644000175000017500000000004413502206677024174 0ustar noahfxnoahfxfrom .subset_facet import * # noqa glueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/qt/subset_facet.ui0000644000175000017500000001077013522025220025061 0ustar noahfxnoahfx SubsetFacet 0 0 359 411 Subset Facet 4 4 4 4 4 Datasets Qt::AlignCenter QComboBox::AdjustToMinimumContentsLength Qt::LeftToRight Attributes Qt::AlignCenter The attributes associated with this data set Number of Subsets 100 5 Min Max Log spacing Color Scale QComboBox::AdjustToMinimumContentsLength Qt::Horizontal 40 20 Cancel OK true QColormapCombo QComboBox
    glue.utils.qt.colors
    glueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/qt/subset_facet.py0000644000175000017500000000625013657331513025110 0ustar noahfxnoahfximport os import numpy as np from matplotlib import cm from qtpy import QtWidgets from glue.core.util import colorize_subsets, facet_subsets from glue.core.state_objects import State from echo import CallbackProperty, SelectionCallbackProperty from glue.utils.qt import load_ui from glue.core.data_combo_helper import DataCollectionComboHelper, ComponentIDComboHelper from echo.qt import autoconnect_callbacks_to_qt from glue.core.state_objects import StateAttributeLimitsHelper __all__ = ['SubsetFacetDialog'] class SubsetFacetState(State): log = CallbackProperty(False) v_min = CallbackProperty(0.) v_max = CallbackProperty(1.) steps = CallbackProperty(5) data = SelectionCallbackProperty() att = SelectionCallbackProperty() cmap = CallbackProperty() def __init__(self, data_collection): super(SubsetFacetState, self).__init__() self.data_helper = DataCollectionComboHelper(self, 'data', data_collection) self.att_helper = ComponentIDComboHelper(self, 'att') self.lim_helper = StateAttributeLimitsHelper(self, attribute='att', lower='v_min', upper='v_max', log='log') self.add_callback('data', self._on_data_change) self._on_data_change() def _on_data_change(self, *args, **kwargs): self.att_helper.set_multiple_data([] if self.data is None else [self.data]) class SubsetFacetDialog(QtWidgets.QDialog): """ Create a new dialog for subset faceting Parameters ---------- collect : :class:`~glue.core.data_collection.DataCollection` The data collection to use default : :class:`~glue.core.data.Data`, optional The default dataset in the collection (optional) """ def __init__(self, collect, default=None, parent=None): super(SubsetFacetDialog, self).__init__(parent=parent) self.state = SubsetFacetState(collect) self.ui = load_ui('subset_facet.ui', self, directory=os.path.dirname(__file__)) self._connections = autoconnect_callbacks_to_qt(self.state, self.ui) self._collect = collect if default is not None: self.state.data = default self.state.cmap = cm.RdYlBu self.ui.button_ok.clicked.connect(self.accept) self.ui.button_cancel.clicked.connect(self.reject) def _apply(self): try: lo, hi = self.state.v_min, self.state.v_max except ValueError: return # limits not set. Abort if not np.isfinite(lo) or not np.isfinite(hi): return subsets = facet_subsets(self._collect, self.state.att, lo=lo, hi=hi, steps=self.state.steps, log=self.state.log) colorize_subsets(subsets, self.state.cmap) @classmethod def facet(cls, collect, default=None, parent=None): """ Class method to create facted subsets. The arguments are the same as __init__. """ self = cls(collect, parent=parent, default=default) value = self.exec_() if value == QtWidgets.QDialog.Accepted: self._apply() glueviz-1.0.1+dfsg.orig/glue/dialogs/subset_facet/__init__.py0000644000175000017500000000000013455362716023544 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/0000755000175000017500000000000013752535025021262 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/tests/0000755000175000017500000000000013752535025022424 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/tests/__init__.py0000644000175000017500000000000013455362716024530 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/qt/0000755000175000017500000000000013752535025021706 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/qt/tests/0000755000175000017500000000000013752535025023050 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/qt/tests/__init__.py0000644000175000017500000000000013455362716025154 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/qt/tests/test_data_wizard.py0000644000175000017500000000636713614064044026761 0ustar noahfxnoahfxfrom unittest.mock import MagicMock, patch from glue.core import Data from glue.config import data_factory from ..data_wizard_dialog import GlueDataDialog, data_wizard @data_factory('testing_factory', identifier=lambda *args: True, priority=-999) def dummy_factory(filename): result = Data() result.made_with_dummy_factory = True return result dummy_factory_member = [f for f in data_factory.members if f[0] is dummy_factory][0] class TestGlueDataDialog(object): def test_factory(self): """Factory method should always match with filter""" fd = GlueDataDialog() fd._fd.show() assert len(fd.filters) > 0 for k, v in fd.filters: fd._fd.selectNameFilter(v) assert fd.factory() is k def test_load_data_cancel(self): """Return None if user cancels operation""" fd = GlueDataDialog() mock_file_exec(fd, cancel=True) assert fd.load_data() == [] def test_load_data_normal(self): """normal load_data dispatches path to factory""" fd = GlueDataDialog() mock_file_exec(fd, cancel=False, path='ld_data_nrml', factory=dummy_factory_member) d = fd.load_data() assert len(d) == 1 d = d[0] assert d.label == 'ld_data_nrml' assert d.made_with_dummy_factory is True def test_filters(self): """Should build filter list from data_factories env var""" fd = GlueDataDialog() assert len(fd.filters) == len([x for x in data_factory.members if not x.deprecated]) def test_load_multiple(self): fd = GlueDataDialog() mock_file_exec(fd, cancel=False, path=['a.fits', 'b.fits'], factory=dummy_factory_member) ds = fd.load_data() assert len(ds) == 2 for d, label in zip(ds, 'ab'): assert d.label == label assert d.made_with_dummy_factory is True def test_directories(self): fd = GlueDataDialog(mode='directories') fd._fd.show() def mock_file_exec(fd, cancel=False, path='junk', factory=dummy_factory_member): if not isinstance(path, list): path = [path] fd._fd.exec_ = MagicMock() fd._fd.exec_.return_value = 1 - cancel fd.factory = MagicMock() fd.factory.return_value = factory fd.paths = MagicMock() fd.paths.return_value = path def test_data_wizard_cancel(): """Returns empty list if user cancel's dialog""" with patch('glue.dialogs.data_wizard.qt.data_wizard_dialog.GlueDataDialog') as mock: mock().load_data.return_value = [] assert data_wizard() == [] def test_data_wizard_normal(): """Returns data list if successful""" with patch('glue.dialogs.data_wizard.qt.data_wizard_dialog.GlueDataDialog') as mock: mock().load_data.return_value = [1] assert data_wizard() == [1] def test_data_wizard_error_cancel(): """Returns empty list of error generated and then canceled""" with patch('glue.dialogs.data_wizard.qt.data_wizard_dialog.GlueDataDialog') as mock: mock().load_data.side_effect = Exception with patch('qtpy.QtWidgets.QMessageBox') as qmb: qmb().exec_.return_value = 0 assert data_wizard() == [] glueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/qt/__init__.py0000644000175000017500000000005213502206677024015 0ustar noahfxnoahfxfrom .data_wizard_dialog import * # noqa glueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/qt/data_wizard_dialog.py0000644000175000017500000001106313614064355026071 0ustar noahfxnoahfxfrom qtpy.QtCore import Qt from qtpy import QtWidgets from glue.utils.qt import set_cursor_cm __all__ = ['data_wizard', 'GlueDataDialog'] def data_wizard(mode='files'): """ Qt Dialog to load a file into a new data object Parameters ---------- mode : {'files', 'directories'} Whether to allow the user to select files or directories. Returns ------- A list of new data objects. Returns an empty list if selection is canceled. """ def report_error(error, factory, curfile): import traceback retry = QtWidgets.QMessageBox.Retry cancel = QtWidgets.QMessageBox.Cancel buttons = retry | cancel detail = traceback.format_exc() msg = "\n".join(["Could not load %s (wrong load method?)" % curfile, "File load method: %s" % factory.label]) detail = "\n\n".join(["Error message: %s" % error, detail]) mb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Data Load Error", msg) mb.setDetailedText(detail) mb.setDefaultButton(cancel) mb.setStandardButtons(buttons) ok = mb.exec_() return ok == retry while True: gdd = GlueDataDialog(mode=mode) try: result = gdd.load_data() break except Exception as e: decision = report_error(e, gdd.factory(), gdd._curfile) if not decision: return [] return result class GlueDataDialog(object): def __init__(self, mode='files', parent=None): self._fd = QtWidgets.QFileDialog(parent) from glue.config import data_factory self.filters = [(f, self._filter(f)) for f in data_factory.members if not f.deprecated] self.setNameFilter() if mode == 'files': self._fd.setFileMode(QtWidgets.QFileDialog.ExistingFiles) else: self._fd.setFileMode(QtWidgets.QFileDialog.Directory) self._fd.setOption(QtWidgets.QFileDialog.ShowDirsOnly, True) self._curfile = '' try: self._fd.setOption( QtWidgets.QFileDialog.Option.HideNameFilterDetails, True) except AttributeError: # HideNameFilterDetails not present pass def factory(self): fltr = self._fd.selectedNameFilter() for k, v in self.filters: if v.startswith(fltr): return k def setNameFilter(self): fltr = ";;".join([flt for fac, flt in self.filters]) self._fd.setNameFilter(fltr) def _filter(self, factory): return "%s (*)" % factory.label def paths(self): """ Return all selected paths, as a list of unicode strings """ return self._fd.selectedFiles() def _get_paths_and_factory(self): """Show dialog to get a file path and data factory :rtype: tuple of (list-of-strings, func) giving the path and data factory. returns ([], None) if user cancels dialog """ result = self._fd.exec_() if result == QtWidgets.QDialog.Rejected: return [], None # path = list(map(str, self.paths())) # cast out of unicode path = list(self.paths()) factory = self.factory() return path, factory def load_data(self): """Highest level method to interactively load a data set. :rtype: A list of constructed data objects """ from glue.core.data_factories import data_label, load_data paths, fac = self._get_paths_and_factory() result = [] # Check that the user didn't select a .glu file by mistake for path in paths: if path.endswith('.glu'): mb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Error loading data", "It looks like you have selected " "a .glu session file. You should open " "this using 'Open Session' under the " "'File' menu instead") mb.exec_() return [] with set_cursor_cm(Qt.WaitCursor): for path in paths: self._curfile = path d = load_data(path, factory=fac.function) if not isinstance(d, list): if not d.label: d.label = data_label(path) d = [d] result.extend(d) return result glueviz-1.0.1+dfsg.orig/glue/dialogs/data_wizard/__init__.py0000644000175000017500000000000013455362716023366 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/common/0000755000175000017500000000000013752535025020261 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/common/tests/0000755000175000017500000000000013752535025021423 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/common/tests/__init__.py0000644000175000017500000000000013455362716023527 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/common/qt/0000755000175000017500000000000013752535025020705 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/common/qt/tests/0000755000175000017500000000000013752535025022047 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/common/qt/tests/__init__.py0000644000175000017500000000000013455362716024153 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/common/qt/__init__.py0000644000175000017500000000000013455362716023011 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/common/qt/component_tree_widget.py0000644000175000017500000000361413605357235025651 0ustar noahfxnoahfxfrom qtpy import QtWidgets, QtCore from qtpy.QtCore import Qt __all__ = ['ComponentTreeWidget'] class ComponentTreeWidget(QtWidgets.QTreeWidget): order_changed = QtCore.Signal() def select_cid(self, cid): for item in self: if item.data(0, Qt.UserRole) is cid: self.select_item(item) return raise ValueError("Could not find find cid {0} in list".format(cid)) def select_item(self, item): self.selection = self.selectionModel() self.selection.select(QtCore.QItemSelection(self.indexFromItem(item, 0), self.indexFromItem(item, self.columnCount() - 1)), QtCore.QItemSelectionModel.ClearAndSelect) @property def selected_item(self): items = self.selectedItems() return items[0] if len(items) == 1 else None @property def selected_cid(self): selected = self.selected_item return None if selected is None else selected.data(0, Qt.UserRole) def add_cid_and_label(self, cid, columns, editable=True): item = QtWidgets.QTreeWidgetItem(self.invisibleRootItem(), columns) item.setData(0, Qt.UserRole, cid) if editable: item.setFlags(item.flags() | Qt.ItemIsEditable) item.setFlags(item.flags() ^ Qt.ItemIsDropEnabled) def __iter__(self): root = self.invisibleRootItem() for idx in range(root.childCount()): yield root.child(idx) def __len__(self): return self.invisibleRootItem().childCount() def dropEvent(self, event): selected = self.selected_item super(ComponentTreeWidget, self).dropEvent(event) self.select_item(selected) self.order_changed.emit() def mousePressEvent(self, event): self.clearSelection() super(ComponentTreeWidget, self).mousePressEvent(event) glueviz-1.0.1+dfsg.orig/glue/dialogs/common/__init__.py0000644000175000017500000000000013455362716022365 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_manager/0000755000175000017500000000000013752535025022465 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_manager/qt/0000755000175000017500000000000013752535025023111 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_manager/qt/tests/0000755000175000017500000000000013752535025024253 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_manager/qt/tests/__init__.py0000644000175000017500000000000013502206677026353 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/component_manager/qt/tests/test_component_manager.py0000644000175000017500000000621113605357235031362 0ustar noahfxnoahfxfrom numpy.testing import assert_equal from glue.core import Data, DataCollection, ComponentID from glue.core.component_link import ComponentLink from glue.core.parse import ParsedCommand, ParsedComponentLink from ..component_manager import ComponentManagerWidget from glue.dialogs.component_arithmetic.qt.tests.test_component_arithmetic import ChangeListener class TestComponentManagerWidget: def setup_method(self): self.data1 = Data(x=[1, 2, 3], y=[3.5, 4.5, -1.0], z=['a', 'r', 'w']) self.data2 = Data(a=[3, 4, 1], b=[1.5, -2.0, 3.5], c=['y', 'e', 'r']) # Add a derived component so that we can test how we deal with existing ones components = dict((cid.label, cid) for cid in self.data2.components) pc = ParsedCommand('{a}', components) link = ParsedComponentLink(ComponentID('d'), pc) self.data2.add_component_link(link) self.data_collection = DataCollection([self.data1, self.data2]) link = ComponentLink([self.data1.id['x']], self.data2.id['a']) self.data_collection.add_link(link) self.listener1 = ChangeListener(self.data1) self.listener2 = ChangeListener(self.data2) def test_nochanges(self): self.manager = ComponentManagerWidget(self.data_collection) self.manager.show() self.manager.button_ok.click() self.listener1.assert_exact_changes() self.listener2.assert_exact_changes() def test_remove(self): x_cid = self.data1.id['x'] self.manager = ComponentManagerWidget(self.data_collection) self.manager.show() item = list(self.manager.list)[0] self.manager.list.select_item(item) self.manager.button_remove_main.click() self.manager.button_ok.click() self.listener1.assert_exact_changes(removed=[x_cid]) self.listener2.assert_exact_changes() def test_rename_valid(self): x_cid = self.data1.id['x'] self.manager = ComponentManagerWidget(self.data_collection) self.manager.show() item = list(self.manager.list)[0] item.setText(0, 'newname') self.manager.button_ok.click() assert self.manager.result() == 1 self.listener1.assert_exact_changes(renamed=[x_cid]) self.listener2.assert_exact_changes() assert x_cid.label == 'newname' assert_equal(self.data1['newname'], [1, 2, 3]) def test_rename_invalid(self): x_cid = self.data1.id['x'] self.manager = ComponentManagerWidget(self.data_collection) self.manager.show() item = list(self.manager.list)[0] item.setText(0, 'y') assert not self.manager.button_ok.isEnabled() assert self.manager.ui.label_status.text() == 'Error: some components have duplicate names' item = list(self.manager.list)[0] item.setText(0, 'a') assert self.manager.button_ok.isEnabled() assert self.manager.ui.label_status.text() == '' self.manager.button_ok.click() self.listener1.assert_exact_changes(renamed=[x_cid]) self.listener2.assert_exact_changes() assert x_cid.label == 'a' assert_equal(self.data1['a'], [1, 2, 3]) glueviz-1.0.1+dfsg.orig/glue/dialogs/component_manager/qt/component_manager.py0000644000175000017500000001501213657331513027156 0ustar noahfxnoahfximport os from collections import defaultdict, Counter from qtpy import QtWidgets, QtGui from qtpy.QtCore import Qt from echo import SelectionCallbackProperty from echo.qt import connect_combo_selection from glue.utils.qt import load_ui __all__ = ['ComponentManagerWidget'] class ComponentManagerWidget(QtWidgets.QDialog): data = SelectionCallbackProperty() def __init__(self, data_collection=None, initial_data=None, parent=None): super(ComponentManagerWidget, self).__init__(parent=parent) self.ui = load_ui('component_manager.ui', self, directory=os.path.dirname(__file__)) self.list = {} self.list = self.ui.list_main_components self.data_collection = data_collection self._components_main = defaultdict(list) self._components_other = defaultdict(list) self._state = defaultdict(dict) for data in data_collection: for cid in data.main_components: comp_state = {} comp_state['cid'] = cid comp_state['label'] = cid.label self._state[data][cid] = comp_state self._components_main[data].append(cid) # Keep track of all other components self._components_other[data] = [] for cid in data.components: if cid not in self._components_main[data]: self._components_other[data].append(cid) # Populate data combo ComponentManagerWidget.data.set_choices(self, list(self.data_collection)) ComponentManagerWidget.data.set_display_func(self, lambda x: x.label) connect_combo_selection(self, 'data', self.ui.combosel_data) if initial_data is None: self.ui.combosel_data.setCurrentIndex(0) else: self.data = initial_data self.ui.combosel_data.currentIndexChanged.connect(self._update_component_lists) self._update_component_lists() self.ui.button_remove_main.clicked.connect(self._remove_main_component) self.ui.list_main_components.itemSelectionChanged.connect(self._update_selection_main) self._update_selection_main() self.ui.list_main_components.itemChanged.connect(self._update_state) self.ui.list_main_components.order_changed.connect(self._update_state) self.ui.button_ok.clicked.connect(self.accept) self.ui.button_cancel.clicked.connect(self.reject) def _update_selection_main(self): enabled = self.list.selected_cid is not None self.button_remove_main.setEnabled(enabled) def _update_component_lists(self, *args): # This gets called when the data is changed and we need to update the # components shown in the lists. self.list.blockSignals(True) self.list.clear() for cid in self._components_main[self.data]: self.list.add_cid_and_label(cid, [self._state[self.data][cid]['label']]) self.list.blockSignals(False) self._validate() def _validate(self): # Construct a list of all labels for the current dataset so that # we can check which ones are duplicates labels = [c.label for c in self._components_other[self.data]] labels.extend([c['label'] for c in self._state[self.data].values()]) if len(labels) == 0: return label_count = Counter(labels) # It's possible that the duplicates are entirely for components not # shown in this editor, so we keep track here of whether an invalid # component has been found. invalid = False if label_count.most_common(1)[0][1] > 1: # If we are here, there are duplicates somewhere in the list # of components. brush_red = QtGui.QBrush(Qt.red) brush_black = QtGui.QBrush(Qt.black) self.list.blockSignals(True) for item in self.list: label = item.text(0) if label_count[label] > 1: item.setForeground(0, brush_red) invalid = True else: item.setForeground(0, brush_black) self.list.blockSignals(False) if invalid: self.ui.label_status.setStyleSheet('color: red') self.ui.label_status.setText('Error: some components have duplicate names') self.ui.button_ok.setEnabled(False) self.ui.combosel_data.setEnabled(False) else: self.ui.label_status.setStyleSheet('') self.ui.label_status.setText('') self.ui.button_ok.setEnabled(True) self.ui.combosel_data.setEnabled(True) def _update_state(self, *args): self._components_main[self.data] = [] for item in self.list: cid = item.data(0, Qt.UserRole) self._state[self.data][cid]['label'] = item.text(0) self._components_main[self.data].append(cid) self._update_component_lists() def _remove_main_component(self, *args): cid = self.list.selected_cid if cid is not None: self._components_main[self.data].remove(cid) self._state[self.data].pop(cid) self._update_component_lists() def accept(self): for data in self._components_main: cids_main = self._components_main[data] cids_existing = data.components cids_all = data.pixel_component_ids + data.world_component_ids + cids_main + data.derived_components # First deal with renaming of components for cid_new in cids_main: label = self._state[data][cid_new]['label'] if label != cid_new.label: cid_new.label = label # Second deal with the removal of components for cid_old in cids_existing: if not any(cid_old is cid_new for cid_new in cids_all): data.remove_component(cid_old) # Findally, reorder components as needed data.reorder_components(cids_all) super(ComponentManagerWidget, self).accept() if __name__ == "__main__": # pragma: nocover from glue.utils.qt import get_qapp app = get_qapp() import numpy as np from glue.core.data import Data from glue.core.data_collection import DataCollection x = np.random.random((5, 5)) y = x * 3 dc = DataCollection() dc.append(Data(label='test1', x=x, y=y)) dc.append(Data(label='test2', a=x, b=y)) widget = ComponentManagerWidget(dc) widget.exec_() glueviz-1.0.1+dfsg.orig/glue/dialogs/component_manager/qt/component_manager.ui0000644000175000017500000001201013522025220027120 0ustar noahfxnoahfx Form 0 0 534 306 Organize data attributes 5 10 5 10 5 Qt::Horizontal 40 20 50 false Dataset: 200 0 QComboBox::AdjustToMinimumContentsLength Qt::Horizontal 40 20 Qt::Vertical QSizePolicy::Fixed 10 5 QAbstractItemView::InternalMove Name Tip: Drag and drop to re-order, and double-click to rename Qt::AlignCenter Qt::Horizontal 40 20 Remove selected Qt::AlignCenter Qt::Horizontal 40 20 Cancel OK true ComponentTreeWidget QTreeWidget
    glue.dialogs.common.qt.component_tree_widget
    glueviz-1.0.1+dfsg.orig/glue/dialogs/component_manager/qt/__init__.py0000644000175000017500000000007613502206677025226 0ustar noahfxnoahfxfrom .component_manager import ComponentManagerWidget # noqa glueviz-1.0.1+dfsg.orig/glue/dialogs/component_manager/__init__.py0000644000175000017500000000000013502206677024565 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/autolinker/0000755000175000017500000000000013752535025021146 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/autolinker/qt/0000755000175000017500000000000013752535025021572 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/autolinker/qt/tests/0000755000175000017500000000000013752535025022734 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/autolinker/qt/tests/__init__.py0000644000175000017500000000000013503203504025017 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/glue/dialogs/autolinker/qt/tests/test_autolinker.py0000644000175000017500000000620113522025220026503 0ustar noahfxnoahfximport pytest from glue.core import Data, DataCollection from glue.core.link_helpers import identity from glue.dialogs.autolinker.qt.autolinker import AutoLinkPreview from glue.core.component_link import ComponentLink # NOTE: the autolinker re-uses the main widget from the link editor so we don't # need to test all the functionality - just things that are specific to the # autolink preview. class TestLinkEditor: def setup_method(self, method): self.data1 = Data(x=[1, 2, 3], y=[2, 3, 4], z=[6, 5, 4], label='data1') self.data2 = Data(a=[2, 3, 4], b=[4, 5, 4], c=[3, 4, 1], label='data2') self.data3 = Data(i=[5, 4, 3], j=[2, 2, 1], label='data3') self.data_collection = DataCollection([self.data1, self.data2, self.data3]) @pytest.mark.parametrize('accept', [False, True]) def test_basic(self, accept): # Set up an existing link link1 = ComponentLink([self.data1.id['x']], self.data2.id['c']) self.data_collection.add_link(link1) # Set up two suggested links def add(x, y): return x + y def double(x): return x * 2 def halve(x): return x / 2 link2 = ComponentLink([self.data2.id['a'], self.data2.id['b']], self.data3.id['j'], using=add) link3 = ComponentLink([self.data3.id['i']], self.data2.id['c'], using=double, inverse=halve) suggested_links = [link2, link3] dialog = AutoLinkPreview('test autolinker', self.data_collection, suggested_links) dialog.show() link_widget = dialog.link_widget link_widget.state.data1 = self.data1 link_widget.state.data2 = self.data2 assert link_widget.listsel_current_link.count() == 1 link_widget.state.data1 = self.data3 assert link_widget.listsel_current_link.count() == 2 if accept: dialog.accept() links = self.data_collection.external_links assert len(links) == 3 assert isinstance(links[0], ComponentLink) assert links[0].get_from_ids()[0] is self.data1.id['x'] assert links[0].get_to_id() is self.data2.id['c'] assert links[0].get_using() is identity assert isinstance(links[1], ComponentLink) assert links[1].get_from_ids()[0] is self.data2.id['a'] assert links[1].get_from_ids()[1] is self.data2.id['b'] assert links[1].get_to_id() is self.data3.id['j'] assert links[1].get_using() is add assert isinstance(links[2], ComponentLink) assert links[2].get_from_ids()[0] is self.data3.id['i'] assert links[2].get_to_id() is self.data2.id['c'] assert links[2].get_using() is double assert links[2].get_inverse() is halve else: dialog.reject() links = self.data_collection.external_links assert len(links) == 1 assert isinstance(links[0], ComponentLink) assert links[0].get_from_ids()[0] is self.data1.id['x'] assert links[0].get_to_id() is self.data2.id['c'] assert links[0].get_using() is identity glueviz-1.0.1+dfsg.orig/glue/dialogs/autolinker/qt/autolinker.ui0000644000175000017500000000533113522025220024272 0ustar noahfxnoahfx LinkEditor 0 0 900 700 Link Suggestions true QLayout::SetDefaultConstraint 5 5 Description of results true The suggested links are shown as lines in the following visualization. Hover over the lines to see more details about the links, then decide whether or not to proceed with the link suggestions. true Show Details Qt::Horizontal 40 20 Apply to future suggestions (can be changed in Preferences) Ignore Apply true false glueviz-1.0.1+dfsg.orig/glue/dialogs/autolinker/qt/__init__.py0000644000175000017500000000011113503203504023660 0ustar noahfxnoahfxfrom glue.dialogs.autolinker.qt.autolinker import run_autolinker # noqa glueviz-1.0.1+dfsg.orig/glue/dialogs/autolinker/qt/autolinker.py0000644000175000017500000000627613605357235024336 0ustar noahfxnoahfximport os from qtpy import QtWidgets from glue.config import settings from glue.utils.qt import load_ui from glue.core.autolinking import find_possible_links from glue.dialogs.link_editor.qt.link_editor import LinkEditorWidget __all__ = ['run_autolinker'] DESCRIPTION = "The auto-linking plugin '{0}' has identified {1} links between your datasets - click on 'More Details' below to find out more about the suggested links." class AutoLinkPreview(QtWidgets.QDialog): def __init__(self, autolinker_name, data_collection, suggested_links, parent=None): super(AutoLinkPreview, self).__init__(parent=parent) self._data_collection = data_collection self._ui = load_ui('autolinker.ui', self, directory=os.path.dirname(__file__)) self._autolinker_name = autolinker_name self.link_widget = LinkEditorWidget(data_collection, suggested_links=suggested_links, parent=self) self._ui.layout().insertWidget(2, self.link_widget) self._ui.label.setText(DESCRIPTION.format(autolinker_name, len(suggested_links))) self._ui.button_apply.clicked.connect(self.accept) self._ui.button_ignore.clicked.connect(self.reject) self._ui.button_details.clicked.connect(self._toggle_details) self._set_details_visibility(False) def _toggle_details(self, *args): self._set_details_visibility(not self._details_visible) def _set_details_visibility(self, visible): self._details_visible = visible self.link_widget.setVisible(visible) self.label_viz.setVisible(visible) if visible: self._ui.button_details.setText('Hide Details') self.setFixedHeight(800) else: self._ui.button_details.setText('Show Details') self.setFixedHeight(100) # Make sure the dialog is centered on the screen screen = QtWidgets.QApplication.desktop().screenGeometry(0) self.move(screen.center() - self.rect().center()) def accept(self): # Check what we need to do here to apply links if self._ui.checkbox_apply_future.isChecked(): settings.AUTOLINK[self._autolinker_name] = 'always_accept' self.link_widget.state.update_links_in_collection() super(AutoLinkPreview, self).accept() def reject(self): if self._ui.checkbox_apply_future.isChecked(): settings.AUTOLINK[self._autolinker_name] = 'always_ignore' super(AutoLinkPreview, self).reject() @classmethod def suggest_links(cls, autolinker_name, data_collection, links, parent=None): mode = settings.AUTOLINK.get(autolinker_name, 'always_show') if mode == 'always_show': widget = cls(autolinker_name, data_collection, links) widget._ui.exec_() elif mode == 'always_accept': data_collection.add_link(links) else: pass def run_autolinker(data_collection): suggestions = find_possible_links(data_collection) for autolinker_name, links in suggestions.items(): AutoLinkPreview.suggest_links(autolinker_name, data_collection, links) glueviz-1.0.1+dfsg.orig/glue/dialogs/autolinker/__init__.py0000644000175000017500000000000013503203504023231 0ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/README.rst0000644000175000017500000000453013605357235016106 0ustar noahfxnoahfx|Azure Status| |Coverage Status| |DOI| |User mailing list| |Developer mailing list| Glue ==== Glue is a python project to link visualizations of scientific datasets across many files. Click on the image for a quick demo: |Glue demo| Features -------- - Interactive, linked statistical graphics of multiple files. - Support for many `file formats `__ including common image formats (jpg, tiff, png), ascii tables, astronomical image and table formats (fits, vot, ipac), and HDF5. Custom data loaders can also be `easily added `__. - Highly `scriptable and extendable `__. Installation ------------ For installation documentation, visit `glueviz.org `__. Contributing ------------ If you are interested in contributing to ``glue``, please read our `Code of Conduct `_ and `Contribution Guidelines `_. Support ------- Please report problems to glueviz@gmail.com, or `open an issue `__. License ------- Glue is licensed under the `BSD License `__. .. |Azure Status| image:: https://dev.azure.com/glue-viz/glue/_apis/build/status/glue-viz.glue?branchName=master :target: https://dev.azure.com/glue-viz/glue/_build/latest?definitionId=4&branchName=master .. |Coverage Status| image:: https://codecov.io/gh/glue-viz/glue/branch/master/graph/badge.svg :target: https://codecov.io/gh/glue-viz/glue .. |DOI| image:: https://zenodo.org/badge/doi/10.5281/zenodo.13866.svg :target: http://dx.doi.org/10.5281/zenodo.13866 .. |User mailing list| image:: http://img.shields.io/badge/mailing%20list-users-green.svg?style=flat :target: https://groups.google.com/forum/#!forum/glue-viz .. |Developer mailing list| image:: http://img.shields.io/badge/mailing%20list-development-green.svg?style=flat :target: https://groups.google.com/forum/#!forum/glue-viz-dev .. |Glue demo| image:: https://raw.githubusercontent.com/glue-viz/glue/master/doc/readme.gif :target: http://vimeo.com/53378575 glueviz-1.0.1+dfsg.orig/setup.cfg0000644000175000017500000000712113752535025016235 0ustar noahfxnoahfx[metadata] name = glue-core url = http://glueviz.org author = Thomas Robitaille, Chris Beaumont author_email = glueviz@gmail.com classifiers = Intended Audience :: Science/Research Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Topic :: Scientific/Engineering :: Visualization License :: OSI Approved :: BSD License description = Multidimensional data visualization across files long_description = file: README.rst [options] zip_safe = False packages = find: python_requires = >=3.6 setup_requires = setuptools_scm install_requires = numpy>=1.16 matplotlib>=3.2 scipy>=1.0 pandas>=1.0 echo>=0.5 astropy>=4.0 setuptools>=30.3.0 qtpy>=1.9 ipython>=4.0 ipykernel>=4.0,!=5.0.0,!=5.1.0 qtconsole>=4.3 jupyter_client<7 dill>=0.2 xlrd>=1.2 h5py>=2.10 mpl-scatter-density>=0.7 bottleneck>=1.2 [options.entry_points] glue.plugins = export_d3po = glue.plugins.export_d3po:setup pv_slicer = glue.plugins.tools.pv_slicer.qt:setup coordinate_helpers = glue.plugins.coordinate_helpers:setup wcs_autolinking = glue.plugins.wcs_autolinking:setup dendro_factory = glue.plugins.dendro_viewer:setup dendro_viewer = glue.plugins.dendro_viewer.qt:setup image_viewer = glue.viewers.image.qt:setup scatter_viewer = glue.viewers.scatter.qt:setup histogram_viewer = glue.viewers.histogram.qt:setup profile_viewer = glue.viewers.profile.qt:setup table_viewer = glue.viewers.table.qt:setup data_exporters = glue.core.data_exporters:setup fits_format = glue.io.formats.fits:setup export_python = glue.plugins.tools:setup directory_importer = glue.io.qt.directory_importer:setup console_scripts = glue-config = glue.config_gen:main glue-deps = glue._deps:main gui_scripts = glue = glue.main:main [options.extras_require] all = scipy scikit-image PyAVM astrodendro spectral-cube pillow!=7.1.0 docs = sphinx sphinx-automodapi sphinxcontrib-spelling numpydoc sphinx-rtd-theme astronomy = PyAVM astrodendro spectral-cube recommended = scikit-image qt = PyQt5>=5.9 test = pytest pytest-cov pytest-faulthandler objgraph [options.package_data] * = *.png, *.ui, *.glu, *.hdf5, *.fits, *.xlsx, *.txt, *.csv, *.svg, *.vot glue.core.data_factories.tests = data/*.jpg glue.external.pvextractor = LICENSE glue.viewers.histogram.qt.tests = data/*.glu glue.viewers.image.qt.tests = data/*.glu, baseline/*.png glue.viewers.profile.qt.tests = data/*.glu glue.viewers.scatter.qt.tests = data/*.glu [flake8] ignore = E501,E731,F841,E127,E741,E402,W504,W605 [tool:pytest] addopts = -p no:logging flake8-ignore = E501,E731,F841,E127,E741,E402,W504,W605 filterwarnings = ignore::PendingDeprecationWarning:xlrd ignore:Session._key_changed is deprecated ignore:zmq.* is deprecated ignore:can't be aliased because it is another magic command ignore:DragAndDropTerminal._style_sheet_changed is deprecated ignore:::ipykernel ignore:Accessing zmq Socket attribute ignore:'U' mode is deprecated:DeprecationWarning:PyQt5 [coverage:run] omit = glue/*tests/* glue/qt/ui/* glue/core/odict.py, glue/core/glue_pickle.py glue/external/* */glue/*tests/* */glue/qt/ui/* */glue/core/odict.py, */glue/core/glue_pickle.py */glue/external/* [coverage:paths] source = glue/ */site-packages/glue *\site-packages\glue [coverage:report] exclude_lines = pragma: no cover except ImportError raise AssertionError raise NotImplementedError def main\(.*\): pragma: py{ignore_python_version} def _ipython_key_completions_ [egg_info] tag_build = tag_date = 0 glueviz-1.0.1+dfsg.orig/.travis.yml0000644000175000017500000000440413657331477016537 0ustar noahfxnoahfxlanguage: python sudo: false addons: apt: packages: - ruby - enchant notifications: email: false env: global: - NO_CFG_FILES=false - SETUP_XVFB=True matrix: include: # Test without any Qt installation, which will also cause all qt # sub-directories to be removed, to make sure that no non-Qt code has # any dependence on Qt code. - os: linux python: 3.6 env: QT_PKG=False before_install: # now set up a plugin configuration file with some plugins disabled. This is # to make sure that when we run the tests, glue will ignore this # configuration and will instead run tests for all plugins. - if [[ $NO_CFG_FILES == false ]]; then mkdir ~/.glue; printf "[plugins]\nspectrum_tool = 0\n" >> ~/.glue/plugins.cfg; fi install: - LC_ALL=C # Install glue and dependencies (but without Qt) - pip install .[all] # Uninstall any version of Qt if QT_PKG is False, and remove all qt # sub-directories - if [[ $QT_PKG == False ]]; then sed -i.bak '/qtpy/d' setup.py; sed -i.bak '/sys.exit(1)/d' setup.py; pip uninstall PyQt5 || true; pip uninstall PyQtWebEngine || true; pip uninstall PySide2 || true; find . -name "qt" -type d -exec rm -r {} \; || true; rm glue/external/qt.py || true; sed -i.bak 's/in REQUIRED_PLUGINS/in REQUIRED_PLUGINS and False/' glue/main.py || true; fi # Set MPLBACKEND to Agg by default - this will get overriden if Qt is present, # but it avoids having Matplotlib default to the osx backend on MacOS X - export MPLBACKEND='Agg'; # List installed packages - pip freeze # Check that all the plugins load correctly - python -c 'from glue.logger import logger; logger.setLevel("DEBUG"); from glue.main import load_plugins; load_plugins()' script: # In the following, we make sure there are no font sizes hard-coded in *.ui files. # We do this because the default application font size may change on different # platforms, but the sizes in ui files are absolute, which can lead to mismatched # font sizes. - find glue -name "*.ui" -exec grep "pointsize" {} \; >& font.log - test ! -s font.log - if [[ $QT_PKG == False ]]; then glue --version; fi - pytest --durations=20 -vs $PYTEST_ARGS glue glueviz-1.0.1+dfsg.orig/MANIFEST.in0000644000175000017500000000026613502206677016156 0ustar noahfxnoahfxrecursive-include glue *.ui *.png *.glu *.hdf5 *.fits *.xlsx include LICENSE include README.md include glueviz.desktop include CHANGES.md include conftest.py recursive-include doc * glueviz-1.0.1+dfsg.orig/PKG-INFO0000644000175000017500000000724013752535025015513 0ustar noahfxnoahfxMetadata-Version: 2.1 Name: glue-core Version: 1.0.1 Summary: Multidimensional data visualization across files Home-page: http://glueviz.org Author: Thomas Robitaille, Chris Beaumont Author-email: glueviz@gmail.com License: UNKNOWN Description: |Azure Status| |Coverage Status| |DOI| |User mailing list| |Developer mailing list| Glue ==== Glue is a python project to link visualizations of scientific datasets across many files. Click on the image for a quick demo: |Glue demo| Features -------- - Interactive, linked statistical graphics of multiple files. - Support for many `file formats `__ including common image formats (jpg, tiff, png), ascii tables, astronomical image and table formats (fits, vot, ipac), and HDF5. Custom data loaders can also be `easily added `__. - Highly `scriptable and extendable `__. Installation ------------ For installation documentation, visit `glueviz.org `__. Contributing ------------ If you are interested in contributing to ``glue``, please read our `Code of Conduct `_ and `Contribution Guidelines `_. Support ------- Please report problems to glueviz@gmail.com, or `open an issue `__. License ------- Glue is licensed under the `BSD License `__. .. |Azure Status| image:: https://dev.azure.com/glue-viz/glue/_apis/build/status/glue-viz.glue?branchName=master :target: https://dev.azure.com/glue-viz/glue/_build/latest?definitionId=4&branchName=master .. |Coverage Status| image:: https://codecov.io/gh/glue-viz/glue/branch/master/graph/badge.svg :target: https://codecov.io/gh/glue-viz/glue .. |DOI| image:: https://zenodo.org/badge/doi/10.5281/zenodo.13866.svg :target: http://dx.doi.org/10.5281/zenodo.13866 .. |User mailing list| image:: http://img.shields.io/badge/mailing%20list-users-green.svg?style=flat :target: https://groups.google.com/forum/#!forum/glue-viz .. |Developer mailing list| image:: http://img.shields.io/badge/mailing%20list-development-green.svg?style=flat :target: https://groups.google.com/forum/#!forum/glue-viz-dev .. |Glue demo| image:: https://raw.githubusercontent.com/glue-viz/glue/master/doc/readme.gif :target: http://vimeo.com/53378575 Platform: UNKNOWN Classifier: Intended Audience :: Science/Research Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Scientific/Engineering :: Visualization Classifier: License :: OSI Approved :: BSD License Requires-Python: >=3.6 Provides-Extra: all Provides-Extra: astronomy Provides-Extra: docs Provides-Extra: qt Provides-Extra: recommended Provides-Extra: test glueviz-1.0.1+dfsg.orig/.gitignore0000644000175000017500000000067213527542473016415 0ustar noahfxnoahfx# Sphinx & coverage build doc/_build doc/api glue/tests/htmlcov *.coverage *htmlcov* # Packages/installer info doc/.eggs *.egg-info dist # Compiled files *.pyc # Other generated files glue/_githash.py # Other .pylintrc *.ropeproject glue/qt/glue_qt_resources.py *.__junk* *.orig *~ .cache # Mac OSX .DS_Store # PyCharm .idea # Eclipse editor project files .project .pydevproject .settings .eggs .hypothesis .pytest_cache .tox .vscode glueviz-1.0.1+dfsg.orig/glueviz.desktop0000644000175000017500000000022613455362716017500 0ustar noahfxnoahfx[Desktop Entry] Type=Application Name=Glueviz Comment=Link visualizations of scientific datasets Exec=glue Icon=glueviz Categories=Education;Science; glueviz-1.0.1+dfsg.orig/setup.py0000755000175000017500000000055013607077207016132 0ustar noahfxnoahfx#!/usr/bin/env python import sys from distutils.version import LooseVersion try: import setuptools assert LooseVersion(setuptools.__version__) >= LooseVersion('30.3') except (ImportError, AssertionError): sys.stderr.write("ERROR: setuptools 30.3 or later is required\n") sys.exit(1) from setuptools import setup setup(use_scm_version=True) glueviz-1.0.1+dfsg.orig/CHANGES.md0000644000175000017500000017034113752534713016016 0ustar noahfxnoahfxFull changelog ============== v1.0.1 (2020-11-10) ------------------- * Updated supported versions of jupyter_client. [#2175] v1.0.0 (2020-09-17) ------------------- * Remove bundled echo package and list as a dependency. [#2125] * Add the ability to export Python scripts for the profile viewer. [#2082] * Add support for polar and other non-rectilinear projections in 2-d scatter viewer [#2170] * Add legend for matplotlib viewers (in qt and in export scripts) [#2097, #2144, #2146] * Add new registry to apply in-place patches to the session file [#2127] * Initial support for dask arrays. [#2137, #2149] * Don't sync color and transparency of image layers by default. [#2116] * Python 2.7 and 3.5 are no longer supported. [#2075] * Always use replace mode when creating a new subset. [#2090] * Fixed bugs in the profile viewer that occurred when using the profile viewer and setting reference_data to a different value than the default one. [#2078] * Updated the data loader to be able to select directories. [#2077,#2080] * Move spectral-cube data loader to glue-astronomy plugin package. [#2077] * Show session filename path in window title. [#2096] * Change ``Data.coords`` API to now rely on the API described in https://github.com/astropy/astropy-APEs/blob/master/APE14.rst [#2079] * Update minimum required version of Astropy to 4.0. [#2079] * Added a ``DataCollection.clear()`` method to remove all datasets. [#2079] * Fixed a bug that caused profiles of subsets to not be hidden if an existing subset was emptied. [#2095] * Improved ``IndexedData`` so that world coordinates can now be shown. [#2081] * Make loading Qt plugins optional when calling ``load_plugins``. [#2112] * Preserve ``RectangularROI`` rather than converting them to ``PolygonalROI``. [#2112] * Significantly improve performance of ``compute_fixed_resolution_buffer`` when using linked datasets with some axes uncorrelated. [#2115] * Improve performance of profile viewer when not all layers are visible. [#2115] * Fixed missing .units on ``CoordinateComponent``. [#2117] * Improve auto-linking of astronomical datasets with WCS information. [#2161] * Fix bug that occurred when using the GUI link editor when links had been defined programmatically. [#2166] * Add the ability to programmatically set the preferred colormap for a dataset using ``Data.visual.preferred_cmap`` [#2131, #2168] * Fix bug that caused incorrect unit to be shown on slider for images. [#2159] * Improve performance of ``Data.compute_statistic`` for subsets. [#2147] * Fix a bug where x_att and y_att could end up being out of sync in image viewer. [#2141] v0.15.7 (2020-03-12) -------------------- * Fix bug that caused an infinite loop in the table viewer and caused glue to hang if too many incompatible subsets were in a table viewer. [#2105] * Fixed a bug that caused an error when an invalid data was added to the table viewer and the table viewer was then automatically closed. [#2103] * Fixed the dropdowns for vector and error markers to not include datetime components (since they represent absolute times, not deltas). [#2102] * Fixed a bug that caused session files that were saved with LinkCollection to not work correctly when re-loaded. [#2100] * Fixed a bug that caused issues when saving and re-loading sessions that were originally created using Excel data with string columns. [#2101] * Avoid converting circular selections in Matplotlib plots to polygons if it can be avoided. [#2094] v0.15.6 (2019-08-22) -------------------- * Fixed bugs related to auto-linking of astronomical data with WCS information. In particular, links between datasets with celestial coordinates in a different order or links between some higher dimensional datasets with celestial axes did not always work correctly. [#2052] * Fixed a bug that caused the auto-linking framework to not run when opening glue from the ``qglue()`` function. [#2052] * Fixed a limitation that caused pixel selections to not propagate to other datasets. [#2052] * Fixed a deprecation warning related to ``add_datasets``. [#2052] * Fixed compatibility with Python 3.7.4. [#2060] * Fixed performance issue with arrays that were not in native C order. [#2056] v0.15.5 (2019-07-09) -------------------- * Fixed bug with density map visibility when using color-coding. [#2041] * Fixed bug with incompatible subsets and density maps. [#2041] * Make sure that lines/errors/vectors are disabled when in density map mode. [#2041] v0.15.4 (2019-07-08) -------------------- * Fixed bug that occurred when trying to add a subset to a new table viewer (without the parent data). [#2038] v0.15.3 (2019-06-27) -------------------- * Fixed bugs related to the preferences dialog - first, the dialog would not open if no auto-linkers were present, and second, the preferences dialog would sometimes get sent behind the main application when editing the color. [#2034] v0.15.2 (2019-06-24) -------------------- * Fixed a bug in ``autoconnect_callbacks_to_qt`` which caused some widgets to not stay connect to state callback properties if a callback property was linked to multiple widgets. [#2032] v0.15.1 (2019-06-24) -------------------- * Fixed __version__ variable. [#2031] * Fixed tox configuration. [#2031] * Improve error message if loading a session file that uses WCS auto-linking but the installed version of astropy is too old. [#2031] v0.15.0 (2019-06-23) -------------------- * Added a new ``glue.core.coordinates.AffineCoordinates`` class for common affine transformations, and also added documentation on defining custom coordinates. [#1994] * Rewrote ``SubsetFacet`` (now ``SubsetFacetDialog``), and updated the available colormaps. [#1998] * Removed the ``ComponentSelector`` class. [#1998] * Make it possible to view only a subset of data in the table viewer. [#1988] * Show dataset name in table viewer title. [#1973] * Expose an option (``inherit_tools``) on data viewer classes related to whether tools should be inherited or not from parent classes. [#1972] * Added a new method ``compute_fixed_resolution_buffer`` to data objects (including the base data class) and use this for the image viewer. This improves the case where images are reprojected as they are now all reprojected to the screen resolution rather than the resolution of the reference data, and this also opens up the possibility of doing n-dimensional reprojection. [#1895] * Added initial infrastructure for developing auto-linking helpers and implement an initial astronomy WCS auto-linker. [#1933] * Improve the user interface for the link editor. [#1934, #1998] * Added a new ``IndexedData`` class to represent a derived dataset produced by indexing a higher-dimensional dataset. [#1953] * Removed plot.ly export plugin - this is now part of the glue-plotly package. [#1999] * Fix path to data bundle when exporting Python scripts to another directory than the one glue was run from. [#2023] * Added a ``--faulthandler`` command-line flag to help debug segmentation faults. [#1974] * Removed ``glue.core.qt.roi`` submodule. This provided faster versions of the Matplotlib ROI classes in ``glue.core.roi`` but the latter are now efficient enough that the Qt-specific versions are no longer needed. [#1983] * Moved ``glue.viewers.common.qt.tool`` to ``glue.viewers.common.tool``; ``glue.viewers.common.qt.mouse_mode`` to ``glue.viewers.matplotlib.mouse_mode``; and ``glue.viewers.common.qt.toolbar_mode`` to ``glue.viewers.matplotlib.toolbar_mode`` and ``glue.viewers.matplotlib.qt.toolbar_mode``. [#1984] * Implement a human-readable str/repr for State objects. [#2021] * Avoiding importing Qt when using the base histogram and profile layer artists. [#2012] * Fixed a bug that caused error bars to be colored incorrectly if NaN values were present. [#2020] * Fixed compatibility with the latest versions of Matplotlib and Astropy. [#2020] * No longer suggest merging data by default whenever datasets are added to the session. Merging different datasets should now be done manually through the GUI. [#2020] * Fixed compatibility with Numpy 1.16.x. [#1989] * Improve tab-completion of attribute names in Data to not include non-relevant items. [#1971] * Fix an error that occurred if creating a new viewer was cancelled. [#1952] * Fix error in image viewer when using update_values_from_data. [#1975] * Exclude problematic versions of ipykernel from dependencies. [#1952] * Make sure that scrolling above a viewer does not result in the whole canvas also scrolling. [#1919] * Fix bug that caused date/time columns in Excel files to not be read in correctly. * Improve performance when reading in large non-FITS files. [#1920] * Fix bug that caused log parameter to be ignore for density plots. [#1963] * Fix bug that caused an error when using the profile collapse tool and dragging from right to left. [#2002] * Fix bug that caused labels on the x-axis of the histogram viewer to be incorrectly set to numbers instead of strings when showing a histogram of a string variable and saving/reloading session. [#2009] v0.14.2 (2019-02-04) -------------------- * Fix bug that caused demo VO Table to not be read in correctly with recent versions of Numpy and Astropy. [#1911] v0.14.1 (2018-11-23) -------------------- * Fix bug that caused the links based on ``join_on_key`` to not always work. [#1902] v0.14.0 (2018-11-14) -------------------- * Improved how we handle equal aspect ratio to not depend on Matplotlib. [#1894] * Avoid showing a warning when closing an empty tab. [#1890] * Fix bug that caused component arithmetic to not work if Numpy was imported in user's config.py file. [#1887] * Added the ability to define custom layer artist makers to override default layer artists in viewers. [#1850] * Fix Plot.ly exporter for categorical components and histogram viewer. [#1886] * Fix issues with reading very large FITS files on some systems. [#1884] * Added documentation about plugins. [#1837] * Better isolate code related to pixel selection tool in image viewer that depended on Qt. [#1763] * Improve handling of units in FITS files. [#1723] * Added documentation about creating viewers for glue using the new state-based infrastructure. [#1740] * Make it possible to pass the initial state of a viewer to an application's ``new_data_viewer`` method. [#1877] * Ensure that glue can be imported if QtPy is installed but PyQt and PySide aren't. [#1865, #1836] * Fix unit display for coordinates from WCS headers that don't have CTYPE but have CUNIT. [#1856] * Enable tab completion on Data objects. [#1874] * Automatically select datasets in link editor if there are only two. [#1837] * Change 'Export Session' dialog to offer to save with relative paths to data by default instead of absolute paths. [#1803] * Added a new method ``screenshot`` on ``GlueApplication`` to save a screenshot of the current view. [#1808] * Show the active subset in the toolbar. [#1797] * Refactored the viewer class base classes [#1746]: - ``glue.core.application_base.ViewerBase`` has been removed in favor of ``glue.viewers.common.viewer.BaseViewer`` and ``glue.viewers.common.viewer.Viewer``. - ``glue.viewers.common.viewer.Viewer`` is now where the base logic is defined for using state classes in viewers (instead of ``glue.viewers.common.qt.DataViewerWithState``). - ``glue.viewers.common.qt.DataViewerWithState`` is now deprecated. * Make it so that the modest image only resamples the data when the mouse is no longer pressed - this avoids too many refreshes when panning/zooming. [#1866] * Make it possible to unglue multiple links in one go. [#1809] * Make it so that adding a subset to a viewer no longer adds the associated data, since in some cases the viewer can handle the subset size, but not the full data. [#1807] * Defined a new abstract base class for all datasets, ``BaseData``, and a base class ``BaseCartesianData``, which can be used to implement interfaces to datasets that may be remote or may not be stored as regular cartesian data. [#1768] * Add a new method ``Data.compute_statistic`` which can be used to find scalar and array statistics on the data, and use for the profile viewer and the state limits helpers. [#1737] * Add a new method ``Data.compute_histogram`` which can be used to find histograms of specific components, with or without subsets applied. [#1739] * Removed ``Data.get_pixel_component_ids`` and ``Data.get_world_component_ids`` in favor of ``Data.pixel_component_ids`` and ``Data.world_component_ids``. [#1784] * Deprecated ``Data.visible_components`` and ``Data.primary_components``. [#1788] * Speed up histogram calculations by using the fast-histogram package instead of np.histogram. [#1806] * In the case of categorical attributes, ``Data[name]`` now returns a ``categorical_ndarray`` object rather than the indices of the categories. You can access the indices with ``Data[name].codes`` and the unique categories with ``Data[name].categories``. [#1784] * Compute profiles and histograms asynchronously when dataset is large to avoid holding up the UI, and compute profiles in chunks to avoid excessive memory usage. [#1736, #1764] * Improved naming of components when merging datasets. [#1249] * Fixed an issue that caused residual references to viewers after they were closed if they were accessed through the IPython console. [#1770] * Don't show layer edit options if layer is not visible. [#1805] * Make the Matplotlib viewer code that doesn't depend on Qt accessible to non-Qt frontends. [#1841] * Avoid repeated coordinate components in merged datasets. [#1792] * Fix bug that caused new subset to be created when dragging an existing subset in an image viewer. [#1793] * Better preserve data types when exporting data/subsets to FITS and HDF5 formats. [#1800] v0.13.4 (2018-10-19) -------------------- * Fix bug that caused .svg icons to not be correctly installed. [#1882] * Fix bug that occurred in certain cases when using the state attribute limit helper with a state class that did not have a log attribute. [#1842] * Fix HDF5 reader for string columns. [#1840] * Fix visual bug in link editor in advanced mode when resizing window. * Fixed a bug that caused custom data importers to no longer work. [#1813] * Fixed a bug that caused ROIs to not be erased after selection if the active subset was not in the list of layers for the viewer. [#1801] * Always returned to last used folder when opening/saving files. [#1794] * Show correct dataset when using control-click to select to add arithmetic attributes or rename/reorder components. [#1802] * Improve performance when updating links and changing attributes on subsets. [#1716] * Fix errors that happened when clicking on the 'Export Data' and 'Define arithmetic attributes' buttons when no data was present, and fixed Qt errors that happened if the data collection changed after the 'Export Data' dialog was opened. [#1795] * Fixed parsing of AVM meta-data from images. [#1732] * Fixed compatibility with Matplotlib 3.0. [#1875] v0.13.3 (2018-05-08) -------------------- * Fixed a bug that caused the expression for derived attributes to not immediately be updated in the list of derived attributes. [#1708] * Fixed a bug that caused combo boxes with Data objects to not update when a data label was changed. [#1704] * Fixed a bug related to callback functions when restoring sessions. [#1695] * Fixed a bug that meant that setting Data.coords after adding components didn't work as expected. [#1196] * Fixed bugs related to components with only NaN or Inf values. [#1712] * Fixed a bug that caused an error when the zoom or pan tools were active and the viewer was closed. [#1712] * Fixed a Qt-related segmentation fault that occurred during the testing process and may also affect users. [#1703] * Show image layer attribute in list of layers. [#1706] * Fixed a bug that caused scatter plots to not revert to fixed color mode after being in linear color mode. [#1705] v0.13.2 (2018-05-01) -------------------- * Fixed a bug that caused the EditSubsetMode toolbar to not change when EditSubsetMode.mode was changed programatically. [#1684] * Fixed unintuitive behavior of single-pixel selection tool - now moving the crosshairs requires clicking and dragging. [#1684] * Fixed bug that caused crosshairs to not be hidden when a layer was set to not be visible [#1684] * Fixed a bug that caused viewers to be closed without warning when pressing delete. [#1684] v0.13.1 (2018-04-29) -------------------- * Fixed resetting and opening of sessions which caused Glue to quit. [#1681] * Fixed serialization of Data.meta when non-serializable keys or values are present. [#1681] v0.13.0 (2018-04-27) -------------------- * Added new perceptually uniform Matplotlib colormaps. [#1679] * Fixed a bug that caused vectors to not correctly be flipped when flipping the x/y limits of the plot. [#1677] * Added a CSV and HDF5 data/subset exporter. [#1676] * Started adding helpful information dialogs that can be hidden. [#1669] * Make it possible to have a 'None' entry in the ComponentIDComboHelper. [#1661] * Added a new metadata/header viewer for datasets/subsets. [#1659] * Re-write spectrum viewer into a generic profile viewer that uses subsets to define the areas in which to compute profiles rather than custom ROIs. [#1635] * Added support for PySide2 and remove support for PyQt4 and PySide. [#1662] * Remove support for Matplotlib 1.5. [#1662] * Renamed ``qt4_to_mpl_color`` to ``qt_to_mpl_color`` and ``mpl_to_qt4_color`` to ``mpl_to_qt_color``. [#1662] * Improve performance when changing visual attributes of subsets. [#1617] * Removed ``glue.core.qt.data_combo_helper`` (we now recommend using the GUI framework-independent equivalent in ``glue.core.data_combo_helper``). [#1625] * Removed ``glue.viewers.common.qt.attribute_limits_helper`` in favor of ``glue.core.state_objects``. [#1625] * Removed unused function ``glue.utils.misc.defer``. [#1625] * Added a new ``FloodFillSubsetState`` class to represent and calculate subsets made by a flood-fill algorithm. [#1616] * Added the ability to easily create viewer tools with dropdown menus. [#1634] * Remove the ``MatplotlibViewerToolbar`` class as it is now no longer needed - instead you can just list the matplotlib tools directly in the ``tools`` attribute of the viewer. [#1634] * Improve hiding/showing of side-panels. No longer hide side-panels when glue application goes out of focus. [#1535] * Use memory-mapping with contiguous arrays in HDF5 files, resulting in improved performance for large files. [#1628] * Deselect tools when the viewer focus changes. [#1584, #1608] * Added support for whether symbols are shown filled or not. [#1559] * Improved link editor to include a graph of links. [#1534] * Improve mouse interaction with ROIs in image viewers, including click-and-drag relocation. Allow for more customization of mouse/toolbar modes. [#1515] * Add a toolbar item to save data. [#1516, #1519, #1575] * Give instructions for how to move selections in status tip. [#1504] * Improve the display of data cube slice labels to include only the precision required given the separation of world coordinate values. [#1500, #1660] * Removed the ability to edit the marker symbol in the style dialog since this isn't recognized by any viewer anymore. [#1560] * Remove back/forward tools in Matplotlib viewer toolbars to declutter. [#1505] * Added a new component manager that makes it possible to rename, reorder, and remove components, as well as better manage derived components, including editing previous equations. [#1479] * Added new messages ``DataReorderComponentMessage`` and ``DataRenameComponentMessage`` which can be subscribed to. [#1479] * Add support for the datetime64 dtype in Data objects, and adjust Matplotlib viewers to correctly show this data. [#1510] * Make it possible to reorder components in ``Data`` using the new ``Data.reorder_components`` method. [#1479] * The default order of components has changed - coordinate components will now always come first (rather than second). [#1479] * Added support for scatter density maps, which is useful when making scatter plots of many points. [#1461] * Improve how ComponentIDComboHelper deals with non-primary components. The .visible property has been removed, and a new .derived property has been added (to show/hide derived components). Components are now split up into sections in the combo boxes. [#1476] * Fixed a bug that caused ghost components to be added when creating a derived component with data[...] = ... [#1476] * Fixed a bug that caused errors when removing items from a selection property linked to a QComboBox. [#1476] * Added initial support for customizing keyboard shortcuts. [#1475, #1514, #1524] * Added support for using relative paths in session files. [#1537] * Remember last session filename and filter used. [#1537] * EditSubsetMode is now no longer a singleton class and is instead instantiated at the Application/Session level. [#1530] * Improve performance of image viewer. [#1558] * Added new ``Projected3dROI`` and ``RoiSubsetState3d`` classes to represent 3D selections made in the projection plane. [#1522] * Fixed saving of sessions with ``BinaryComponentLink``. [#1533] * Refactored/simplified handling of links between datasets and fixed performance issues when adding/removing links or loading data collections with many links. [#1531, #1533] * Significantly improve performance of link computations when the links depend only on pixel or world coordinate components. [#1585] * Added the ability to customize the appearance of tick and axis labels in Matplotlib plots. [#1511] * Added the ability to export Python scripts from the main Matplotlib-based viewers. [#1511] * Added a new selection mode that always forces the creation of a new subset. [#1525] * Added a mouse over pixel selection tool, which creates a one pixel subset under the mouse cursor. [#1619] * Fixed an issue that caused sliders to not be correctly updated when switching reference data in the image viewer. [#1665] * Fixed a bug that caused Data.meta to not be saved/restored from session files. [#1668] * Fixed an issue that caused an IndexError when quitting glue in some cases. [#1657] * Fixed a bug that caused matplotlibrc files to not be ignored. [#1649] * Fixed a non-deterministic error that happened when closing the TableViewer. [#7310] * Fixed size of markers when value for size is out of vmin/vmax range. [#1609] * Fix a bug that caused viewer limits to be calculated incorrectly if inf/-inf values were present in the data. [#1614] * Fixed a bug which caused the y-axis in the PV slice viewer to be incorrect if the WCS could not be computed. [#1615] * Fixed a bug that caused the WCS of a PV slice to be incorrect if the user has selected a custom order of the axes of a cube in the image viewer. [#1615] * Fixed ticks on log x-axis in histogram viewer. [#7310] * Fixed a bug that led to poor performance when slicing through data cubes. [#1554] v0.12.5 (2018-03-10) -------------------- * Fixed a bug which caused the current slices to be lost when adding a second dataset to the image viewer. [#1581] * Fixed a bug when two datasets with a different number of dimensions were in an image viewer and a subset was created. [#1577] * Fixed issues when attempting to close a viewer with the delete key. [#1574] * Disabled default Matplotlib key bindings. [#1574] * Fix compatibility with Matplotlib 2.2. [#1566] * Fix compatibility with some versions of pytest. [#1520] * Fix calculation of dependent_axes to account for cases where there are some non-zero non-diagonal PC values. Previously any such values resulted in all axes being returned as dependent axes even though this isn't necessary. [#1552] * Avoid prompting users multiple times to merge data when dragging and dropping multiple data files onto glue. [#1564] * Improve error message in PV slicer when _slice_index fails. [#1536] * Fixed a bug that caused an error when trying to save a session that included an image viewer with an aggregated slice. [#1561] * Fixed a bug that caused an error in the terminal if creating a data viewer failed properly (with a GUI error message). [#1501] * Fixed a bug that caused performance issues when hiding all image layers from an image viewer. [#1557, #1562] * Fixed a bug that caused layers to not always be properly removed when deleting a row from the layer list. [#1502] * Make JSON circular reference errors more explicit. [#1529] v0.12.4 (2018-01-09) -------------------- * Improve plugin loading to be less sensitive to exact versions of installed dependencies for plugins. [#1487] v0.12.3 (2017-11-14) -------------------- * Fixed issues with PV slicer and spectrum viewer when changing axes in the parent image viewer. v0.12.2 (2017-11-09) -------------------- * Fix a bug when renaming tabs through the UI. [#1470] * Fix a bug that caused the 1D and 2D viewers to not update correctly when the numerical values in data were changed. [#1471] * Fix a bug that caused exporting of subsets to not work with integer data. [#1472] v0.12.1 (2017-10-30) -------------------- * Fix a bug that caused glue to crash when adding components to a dataset after closing a viewer that had that data. [#1460, #1464] v0.12.0 (2017-10-25) -------------------- * Show a GUI error message when restoring a session via drag-and-drop if session loading fails. [#1454] * Don't disable layer completely if it is not enabled, just disable checkbox. Also show warnings instead of layer style editor. [#1451] * Generalize registry for data/subset actions to replace the former single_subset_action registry (which applied only to single subset selections). Layer actions can now be registered with the ``@layer_action`` decorator. [#1396] * Added support for plotting vectors in the scatter plot viewer. [#1410] * Added glue plugins to the Version Information dialog. [#1427] * Added the ability to create fixed layout tabs. [#1403] * Fix selection in custom viewers. [#1453] * Fix a bug that caused the home/reset limits button to not work correctly. [#1452] * Fix a bug that caused the wrong layers to be enabled when mixing image and scatter layers and setting up links. [#1451] * Remove 'sep' from menu on Linux. [#1394] * Fixed bug in spectrum tool that caused the upper range in aggregations to be incorrectly calculated. [#1402] * Fixed icon for scatter plot layer when a colormap is used, and fix issues with viewer layer icons not updating immediately. [#1425] * Fixed dragging and dropping session files onto glue (this now loads the session rather than trying to load it as a dataset). Also now show a warning when the application is about to be reset to open a new session. [#1425, #1448] * Make sure no errors happen if making a selection in an empty viewer. [#1425] * Fix creating faceted subsets on Python 3.x when no dataset is selected. [#1425] * Fix issues with overlaying a scatter layer on an image. [#1425] * Fix issues with labels for categorical axes in the scatter and histogram viewers, in particular when loading viewers with categorical axes from session files. [#1425] * Make sure a GUI error message is shown when adding non-1-dimensional data to a table viewer. [#1425] * Fix issues when trying to launch glue multiple times from a Jupyter session. [#1425] * Remove the ability to define the color of a subset differ from that of a subset group it belongs to - this was virtually never needed but could cause issues. [#1426] * Fixed a bug that caused a previously disabled image subset layer to not become visible when shown again. [#1450] * Added the ability to rename tabs programmatically. [#1405] v0.11.1 (2017-08-25) -------------------- * Fixed bug that caused ModestImage references to not be properly deleted, in turn leading to issues/crashes when removing subsets from image viewers. [#1390] * Fixed bug with reading in old session files with a table viewer. [#1389] v0.11.0 (2017-08-22) -------------------- * Added splash screen. [#694] * Make file extension check case-insensitive. [#1275] * Fixed bug that caused table viewer to not update when adding components. [#1386] * Fixed loading of plain (non-structured) arrays from Numpy files. [#1314, #1385] * Disabled layer artists can no longer be selected to avoid any confusion. [#1367] * Layer artist icons can now show colormaps when appropriate. [#1367] * Fix behavior of data wizard so that it doesn't overwrite labels set by data factories. [#1367] * Add a status tip for all ROI selection tools. [#1367] * Fixed a bug that caused the terminal to not be available after resetting or opening a session. [#1366] * If a subset's visual properties are changed, change the visual properties of the parent SubsetGroup. [#1365] * Give an error if the user selects a session file when going through the 'Open Data Set' menu. [#1364] * Improved scatter plot viewer to be able to show points with color or size based on other attributes. Also added a 'line' style to make line plots, and added the ability to show error bars. [#1358] * Changed order of arguments for data exporters from (data, filename) to (filename, data). [#1251] * Added registry decorators to define subset mask importers and exporters. [#1251] * Get rid of QTimers for updating the data collection and layer artist lists, and instead refresh whenever a message is sent from the hub (which results in immediate changes rather than waiting up to a second for things to change). [#1343] * Made it possible to delay callbacks from the Hub using the ``Hub.delay_callbacks`` context manager. Also fixed the Hub so that it uses weak references to classes and methods wherever possible. [#1339] * Added a new method DataCollection.remove_link to match existing DataCollection.add_link. [#1339] * Fix a bug that caused no messages to be emitted when components were removed from Data objects, and add a new DataRemoveComponentMesssage. [#1339] * Fix a long-standing bug which caused performance issues after linking coordinate or derived components between datasets. [#1339] * Added a function is_equivalent_cid that can be used to determine whether two component IDs in a dataset are equivalent. [#1339] * The image contrast and bias can now be set with the left click as well as right click. [#1323] * Updated ComponentIDComboHelper so that it can work with single datasets that aren't necessarily attached to a DataCollection. [#1296] * Updated bundled version of echo to include fixes to avoid circular references, which in turn caused some callback functions to not be cleaned up. [#1281] * Rewrote the histogram, scatter, and image viewers to use the new state infrastructure. This significantly simplifies the actual histogram viewer code both in terms of number of lines and in terms of the number of connections/callbacks that need to be set up manually. [#1278, #1289, #1388] * Updated EditSubsetMode so that Data objects no longer have an edit_subset attribute - instead, the current list of subsets being edited is kept in EditSubsetMode itself. We also update the subset state only once on each subset group, rather than once per dataset, which avoids doing the same update to each dataset multiple times. [#1338] * Remove the ability to create a new viewer by right-clicking on the canvas, since this causes confusion when trying to control-click to paste in the IPython terminal. [#1342] * Make process_dialog more robust. [#1333] * Fix example of setting up a custom preferences pane. [#1326] * Fix a bug that caused links to not get removed if associated datasets were removed. [#1329] * Fixed a bug that meant that the table viewer did not update when a ``NumericalDataChangedMessage`` message was emitted. [#1378] * Added new combo helpers in ``glue.core.data_combo_helper`` which are similar to those in ``glue.core.qt.data_combo_helper`` but operate on ``SelectionCallbackProperty`` and are Qt-independent. [#1346] * Rewrote installation instructions. [#1330] v0.10.4 (2017-05-23) -------------------- * Fixed a bug that caused merged datasets to crash viewers (because the visible_components attribute returned an empty list). [#1288] v0.10.3 (2017-04-20) ------------------- * Fixed bugs with saving and restoring of various types of subset states. [#1285] * Fixed a bug that caused glue to not open when IPython 4.0 was installed. [#1287] v0.10.2 (2017-03-22) -------------------- * Fixed a bug that caused components that were linked to then disappear from drop-down lists of available components in new viewers. [#1270] * Fixed a bug that caused Data.find_component_id to return incorrect results when string components were present in the data. [#1269] * Fixed a bug that caused errors to appear in the console log after a table viewer was closed. [#1267] * Fixed a bug that caused error message dialogs to not work correctly with Qt4. [#1262] * Fixed a deprecation warning for pandas >= 0.19. [#1263] * Hide common Matplotlib warnings when min/max along an axis are equal. [#1268] * Fixed a bug that caused sessions with table viewers that had no subsets to not be restored correctly. [#1271] v0.10.1 (2017-03-16) -------------------- * Fixed compatibility with session files from before v0.8. [#1243] * Renamed package to glue-core, since glueviz is now a meta-package (no need for a new major version since this change should be seamless to users). v0.10.0 (2017-02-14) -------------------- * The GlueApplication.add_data and load_data methods now return the loaded data objects. [#1239] * Change default name of subsets to include the word 'Subset'. [#1234] * Removed ginga plugin from core package and moved it to a separate repository at https://github.com/ejeschke/glue-ginga. * Switch from using bundled WCSAxes to using the version in Astropy, and fixed an issue that caused the frame of the axes to be too thick. [#1231] * Make it possible to define a default index for DataComboHelper - this makes it possible for viewers to have three DataComboHelper and ensure that they default to different attributes. [#1163] * Deal properly with adding Subset objects to DataComboHelper. [#1163] * Added the ability to export data and subset from the data collection view via contextual menus. It was previously possible to export only the mask itself, and only to FITS files, but the framework for exporting data/subsets has now been generalized. * When hiding layers in the RGB image viewer, make sure the current layer changes to be a visible one if possible. * Avoid merging IDs when creating identity links. The previous behavior of merging was good for performance but made it impossible to undo the linking by un-glueing. Derived components created by links are now hidden by default. Finally, ``ComponentID`` objects now hold a reference to the first parent data they are used in. [#1189] * Added a decorator that can be used to avoid circular calling of methods (can occur when dealing with callbacks). [#1207] * Drop support for IPython 3.x and below, and make IPython and qtconsole into required dependencies. [#1145] * Added new classes that can be used to hold the state of e.g. viewers and other objects. These classes allow callbacks to be attached to various properties, and can be used to define logical relations between different attributes in a GUI-independent way. * Fix selections when using Matplotlib 2.x, PyQt5 and a retina display. [#1236] * Updated ComponentIDComboHelper to no longer store ``(cid, data)`` as the ``userData`` but instead just the ``cid`` (the data is now accessible via ``cid.parent``). [#1212] * Avoid duplicate toolbar in dendrogram viewer. [#1213, #1237] * Fixed bug that caused the contrast to change for the incorrect layer in the RGB image viewer. * Fixed bug that caused coordinate information to be lost when merging datasets. The original behavior of keeping the coordinate information from the first dataset has been restored. [#1186] * Fix Data.update_values_from_data to make sure that original component order is preserved. [#1238] * Fix Data.components to return original component order, not alphabetical order. [#1238] * Fix significant performance bottlenecks with WCS coordinate conversions. [#1185] * Fix error when changing the contrast radio button the RGB image viewer mode, and also fix bugs with setting the range of values manually. [#1187] * Fix a bug that caused coordinate axis labels to be lost during merging. [#1195] * Fix a bug that caused tab names to not be saved and restored to/from session files. [#1241, #1242] v0.9.1 (2016-11-01) ------------------- * Fixed loading of session files made with earlier versions of glue that contained selections made in 3D viewers. [#1160] * Fixed plotting of fit on spectrum to make sure that the two are properly aligned. [#1158] * Made it possible to now create InequalitySubsetStates for categorical components using e.g. d.id['a'] == 'string'. [#1153] * Fixed a bug that caused selections to not propagate properly between linked images and cubes. [#1144] * Make last interval of faceted subsets inclusive so as to make sure all values in the faceted subset range end up in a subset. [#1154] v0.9.0 (2016-10-10) ------------------- * Fix serialization of celestial coordinate link functions. Classes inheriting from MultiLink should now call MultiLink.__init__ with individual components (not grouped into left/right) then the create_links method with the components separated into left/right and the methods for forward/backward transformation. The original behavior can be retained by using the ``multi_link`` function instead of the ``MultiLink`` class. [#1139] * Improve support for spectral cubes. [#1075] * Allow link functions/helpers to define a category using the ``category=`` argument (which defaults to ``General``), and make it possible to filter by category in the link editor. [#1141] * Only show the 'waiting' cursor when glue is doing something. [#1097] * Make sure that the scatter layer artist style editor is shown when overplotting a scatter plot on top of an image. [#1134] * Data viewers can now make layer_style_widget_cls a dictionary in cases where multiple layer artist types are supported. [#1134] * Fix compatibility of test suite with pytest 3.x. [#1116] * Updated bundled version of WCSAxes to v0.9. [#1089] * Fix compatibility with pre-releases of Matplotlib 2.0. [#1115] * Implement new widget with better control over exporting to Plotly. The preference pane for Plotly export has now been removed in favor of this new way to set the options. [#1057] * Renamed the ``ComponentIDComboHelper`` and ``ManualDataComboHelper`` ``append`` methods to ``append_data`` and the ``remove`` methods to ``remove_data``, and added a new ``ComponentIDComboHelper.set_multiple_data`` method. [#1060] * Fixed reading of units from FITS and VO tables, and display units in table viewer. [#1135, #1137] * Make use of the QtPy package to deal with differences between PyQt4, PyQt5, and PySide, instead of the custom qt-helpers package. The ``glue.external.qt`` package is now deprecated. The ``get_qapp`` and ``load_ui`` functions are now available in ``glue.utils.qt``. [#1018, #1074, #1077, #1078, #1081] * Avoid raising a (harmless) error when selecting a region in between two categorical components. * Added a new Data method, ``update_values_from_data``, that can be used to replicate components from one dataset into another. [#1112] * Refactored code related to toolbars in order to make it easier to define toolbars and toolbar modes that aren't Matplotlib-specific. [#1085, #1120] * Added a new table viewer. [#1084, #1123] * Fix saving/loading of categorical components. [#1084] * Make it possible for tools to define a status bar message. [#1084] * Added a command-line option, ``--no-maximized``, that prevents glue from opening up with the application window maximized. [#1093, #1126] * When opening multiple files in one go, if one of the files fails to read, the error will now indicate which file failed. [#1104] * Fixed a bug that caused new subset colors to incorrectly start from the start of the color cycle after loading a session. [#1055] * Fixed a bug that caused the functionality to execute scripts (glue -x) to not work in Python 3. [#1101, #1114] * The minimum supported version of Astropy is now 1.0, and the minimum supported version of IPython is now 1.0. [#1076] * Show world coordinates and units in the cube slicer. [#1059, #1068] * Fix errors that occurred when selecting categorical data. [#1069] * Added experimental support for joining on multiple keys in ``join_on_key``. [#974] * Fix compatibility with the latest version of ginga. [#1063] v0.8.2 (2016-07-06) ------------------- * Implement missing MaskSubsetState.copy. [#1033] * Ensure that failing data factory identifier functions are skipped. [#1029] * The naming of pixel axes is now more consistent between data with 3 or fewer dimensions, and data with more than 3 dimensions. The naming is now always ``Pixel Axis ?`` where ``?`` is the index of the array, and for datasets with 1 to 3 dimensions, we add a suffix e.g. ``[x]`` to indicate the traditional axes. [#1029] * Implemented a number of performance improvements, including for: the check of whether points are in polygon (``points_inside_poly``), the selection of polygonal regions in multi-dimentional cubes when the selections are along pixel axes, the selection of points in scatter plots with one or two categorical components for rectangular, circular, and polygonal regions. [#1029] * Fix a bug that caused multiple custom viewer classes to not work properly if the user did not override ``_custom_functions`` (which was private). [#810] * Make sure histograms are updated if only the attribute changes and the limits and number of bins stay the same. [#1012] * Fix a bug on Windows that caused drag and dropping files onto the glue application to not work. [#1007] * Fix compatibility with PyQt5. [#1015] * Fix a bug that caused ComponentIDComboHelper to not take into account the numeric and categorical options in __init__. [#1014] * Fix a bug that caused saving of scatter plots to SVG files to crash. [#984] v0.8.1 (2016-05-25) ------------------- * Fixed a bug in the memoize function that caused selections using ElementSubsetState to fail when using views on the data. [#1004] * Explicitly set the icon size for the slicing playback controls to avoid issues when using a mix of retina and non-retina displays. [#1005] * Fixed a bug that caused add_datasets to crash if ``datasets`` was a list of lists of data, which is possible if a data factory returns more than one data object. [#1006] v0.8 (2016-05-23) ----------------- * Add support for circular and polygonal spectrum extraction. [#994, #1003] * Fix compatibility with latest developer version of Numpy which does not allow non-integer indices for arrays. [#1002] * Add a new method ``add_data`` to application instances. This allows for example additional data to be passed to glue after being launched by ``qglue``. [#993] * Add playback controls to slice widget. [#971] * Add tooltip for data labels so that long labels can be more easily inspected. [#912] * Added a new helper class ``AttributeLimitsHelper`` to link widgets related to setting limits and handle the caching of the limits as a function of attribute. [#872] * Add Quit menu item for Linux and Windows. [#926] * Refactored the window for sending feedback to include more version information, and also to have a separate form for feedback and crash reports. [#955] * Add log= option to ValueProperty and remove mapping= option. [#965] * Added helper classes for ComponentID and Data combo boxes. [#891] * Improved new component window: expressions can now include math or numpy functions by default, and expressions are tested on-the-fly to check that there are no issues with syntax or undefined variables. [#956] * Fixed D3PO export when using Python 3. [#989] * Fixed display of certain error messages when using Python 3. [#989] * Add an extensible preferences window. [#988] * Add the ability to change the foreground and background color for viewers. [#988] * Fixed a bug that caused images to appear over-pixellated on the edges when zooming in. [#1000] * Added an option to control whether warnings are shown when passing large data objects to viewers. [#999] v0.7.3 (2016-05-04) ------------------- * Remove icons for actions that appear in contextual menus, since these appear too large due to a Qt bug. [#911] * Add missing find_spec for import hook, to avoid issues when trying to set colormap. [#930] * Ignore extra dimensions in WCS (for instance, if the data is 3D and the header is 4D, ignore the 4th dimension in the WCS). [#935] * Fix a bug that caused the merge window to appear multiple times, make sure that all components named PRIMARY get renamed after merging, and make sure that the merge mechanism is also triggered when opening datasets from the command-line. [#936] * Remove the scrollbars added in v0.7.1 since they cause issues on certain systems. [#953] * Fix saving of ElementSubsetState to session files. [#966] * Fix saving of Matplotlib colormaps to session files. [#967] * Fix the selection of the default viewer based on the data shape. [#968] * Make sure that no combo boxes get resized based on the content (unless strictly needed). [#978] v0.7.2 (2016-04-05) ------------------- * Fix a bug that caused string columns in FITS files to not be read correctly, and updated coerce_numeric to give a ValueError for string columns that can't be convered. [#919] * Make sure main window title is set. [#914] * Fix issue with FITS files that are missing an END card. [#915] * Fix a bug that caused values in exponential notation in text fields to lose a trailing zero (e.g. 1.000e+10 would become 1.000e+1). [#925] v0.7.1 (2016-03-30) ------------------- * Fix issue with small screens and layer and viewer options by adding scrollbars. [#902] * Fixed a failure due to a missing Qt import in glue.core.roi. [#901] * Fixed a bug that caused an abort trap if the filename specified on the command line did not exist. [#903] * Gracefully skip vector columnns when reading in FITS files. [#896] * Change default gray color to work on black and white backgrounds. [#906] * Fixed a bug that caused the color in the scatter and histogram style editors to not show the correct initial color. [#907] v0.7 (2016-03-10) ----------------- * Python 2.6 is no longer supported. [#804] * Added a generic QColorBox widget to pick colors, and an associated connect_color helper for callback properties. [#864] * Added a generic QColormapCombo widget to pick colormaps. * The ``artist_container`` argument to client classes has been renamed to ``layer_artist_container``. [#814] * Added documentation about how to use layer artists in custom Qt data viewers. [#814] * Fixed missing newline in Data.__str__. [#877] * A large fraction of the code has been re-organized, which may lead to some imports in ``config.py`` files no longer working. However, no functionality has been removed, so this can be fixed by updating the imports to reflect the new locations. In particular, the following utilities have been moved: ``glue.qt.widget_properties`` | ``glue.utils.qt.widget_properties`` ``glue.qt.decorators`` | ``glue.utils.qt.decorators`` ``glue.qt.qtutil.mpl_to_qt4_color`` | ``glue.utils.qt.colors.mpl_to_qt4_color`` ``glue.qt.qtutil.qt4_to_mpl_color`` | ``glue.utils.qt.colors.qt4_to_mpl_color`` ``glue.qt.qtutil.pick_item`` | ``glue.utils.qt.dialogs.pick_item`` ``glue.qt.qtutil.pick_class`` | ``glue.utils.qt.dialogs.pick_class`` ``glue.qt.qtutil.get_text`` | ``glue.utils.qt.dialogs.get_text`` ``glue.qt.qtutil.tint_pixmap`` | ``glue.utils.qt.colors.tint_pixmap`` ``glue.qt.qtutil.cmap2pixmap`` | ``glue.utils.qt.colors.cmap2pixmap`` ``glue.qt.qtutil.pretty_number`` | ``glue.utils.qt.PropertySetMixin`` ``glue.qt.qtutil.Worker`` | ``glue.utils.qt.threading.Worker`` ``glue.qt.qtutil.update_combobox`` | ``glue.utils.qt.helpers.update_combobox`` ``glue.qt.qtutil.PythonListModel`` | ``glue.utils.qt.python_list_model.PythonListModel`` ``glue.clients.tests.util.renderless_figure`` | ``glue.utils.matplotlib.renderless_figure`` ``glue.core.util.CallbackMixin`` | ``glue.utils.misc.CallbackMixin`` ``glue.core.util.Pointer`` | ``glue.utils.misc.Pointer`` ``glue.core.util.PropertySetMixin`` | ``glue.utils.misc.PropertySetMixin`` ``glue.core.util.defer`` | ``glue.utils.misc.defer`` ``glue.qt.mime.PyMimeData`` | ``glue.utils.qt.mime.PyMimeData`` ``glue.qt.qtutil.GlueItemWidget`` | ``glue.utils.qt.mixins.GlueItemWidget`` ``glue.qt.qtutil.cache_axes`` | ``glue.utils.matplotlib.cache_axes`` ``glue.qt.qtutil.GlueTabBar`` | ``glue.utils.qt.helpers.GlueTabBar`` [#827, #828, #829, #830, #831] ``glue.clients.histogram_client`` | ``glue.viewers.histogram.client`` ``glue.clients.image_client`` | ``glue.viewers.image.client`` ``glue.clients.scatter_client`` | ``glue.viewers.scatter.client`` ``glue.clients.layer_artist.LayerArtist`` | ``glue.clients.layer_artist.MatplotlibLayerArtist`` ``glue.clients.layer_artist.ChangedTrigger`` | ``glue.clients.layer_artist.ChangedTrigger`` ``glue.clients.layer_artist.LayerArtistContainer`` | ``glue.clients.layer_artist.LayerArtistContainer`` ``glue.clients.ds9norm`` | ``glue.viewers.image.ds9norm`` ``glue.clients.profile_viewer`` | ``glue.plugins.tools.spectrum_viewer.profile_viewer`` ``glue.clients.util.small_view`` | ``glue.core.util.small_view`` ``glue.clients.util.small_view_array`` | ``glue.core.util.small_view_array`` ``glue.clients.util.visible_limits`` | ``glue.core.util.visible_limits`` ``glue.clients.util.tick_linker`` | ``glue.core.util.tick_linker`` ``glue.clients.util.update_ticks`` | ``glue.core.util.update_ticks`` ``glue.qt.widgets.histogram_widget`` | ``glue.viewers.histogram.qt`` ``glue.qt.widgets.scatter_widget`` | ``glue.viewers.scatter.qt`` ``glue.qt.widgets.histogram_widget`` | ``glue.viewers.image.qt`` ``glue.qt.widgets.table_widget`` | ``glue.viewers.table.qt`` ``glue.qt.widgets.data_viewer`` | ``glue.viewers.common.qt.data_viewer`` ``glue.qt.widgets.mpl_widget`` | ``glue.viewers.common.qt.mpl_widget`` ``glue.qt.widgets.MplWidget`` | ``glue.viewers.common.qt.mpl_widget.MplWidget`` ``glue.qt.glue_toolbar`` | ``glue.viewers.common.qt.toolbar`` ``glue.qt.custom_viewer`` | ``glue.viewers.custom.qt`` [#835] ``glue.qt.glue_application.GlueApplication`` | ``glue.app.qt.application.GlueApplication`` ``glue.qt.plugin_manager.QtPluginManager`` | ``glue.app.qt.plugin_manager.QtPluginManager`` ``glue.qt.feedback.FeedbackWidget`` | ``glue.app.qt.feedback.FeedbackWidget`` ``glue.qt.widgets.glue_mdi_area`` | ``glue.app.qt.mdi_area`` ``glue.qt.widgets.terminal`` | ``glue.app.qt.terminal`` ``glue.qt.qt_roi`` | ``glue.core.qt.roi`` ``glue.core.qt.simpleforms`` | ``glue.core.qt.simpleforms`` ``glue.qt.widgets.style_dialog`` | ``glue.core.qt.style_dialog`` ``glue.qt.layer_artist_model`` | ``glue.core.qt.layer_artist_model`` ``glue.qt.widgets.custom_component_widget`` | ``glue.dialogs.custom_component.qt`` ``glue.qt.link_editor`` | ``glue.dialogs.link_editor.qt`` ``glue.qt.widgets.subset_facet`` | ``glue.dialogs.subset_facet.qt`` ``glue.qt.mouse_mode`` | ``glue.viewers.common.qt.mouse_mode`` ``glue.qt.data_slice_widget`` | ``glue.viewers.common.qt.data_slice_widget`` ``glue.qt.widgets.layer_tree_widget`` | ``glue.app.qt.layer_tree_widget`` ``glue.qt.widgets.message_widget`` | ``glue.core.qt.message_widget`` ``glue.qt.widgets.settings_editor`` | ``glue.app.qt.settings_editor`` ``glue.qt.qtutil.data_wizard`` | ``glue.dialogs.data_wizard.qt.data_wizard`` ``glue.qt.mime`` | ``glue.core.qt.mime`` ``glue.qt.qtutil.ComponentIDCombo`` | ``glue.core.qt.component_id_combo`` ``glue.qt.qtutil.RGBEdit`` | ``glue.viewers.image.qt.rgb_edit.RGBEdit`` ``glue.qt.qtutil.GlueListWidget`` | ``glue.core.qt.mime.GlueMimeListWidget`` ``glue.qt.qtutil.load_ui`` | ``glue.utils.qt.helpers.load_ui`` ``glue.qt.qtutil.icon_path`` | ``glue.icons.icon_path`` ``glue.qt.qtutil.load_icon`` | ``glue.icons.qt.load_icon`` ``glue.qt.qtutil.symbol_icon`` | ``glue.icons.qt.symbol_icon`` ``glue.qt.qtutil.layer_icon`` | ``glue.icons.qt.layer_icon`` ``glue.qt.qtutil.layer_artist_icon`` | ``glue.icons.qt.layer_artist_icon`` ``glue.qt.qtutil.GlueActionButton`` | ``glue.app.qt.actions.GlueActionButton`` ``glue.qt.qtutil.action`` | ``glue.app.qt.actions.action`` ``glue.qt.qt_backend.Timer`` | ``glue.backends.QtTimer`` [#845] * Improved under-the-hood creation of ROIs for Scatter and Histogram Clients. [#676] * Data viewers can now define a layer artist style editor class that appears under the list of layer artists. [#852] * Properties of the VisualAttributes class are now callback properties. [#852] * Add ``glue.utils.qt.widget_properties.connect_value`` function which can take an optional value_range and log option to scale the Qt values to a custom range of values (optionally in log space). [#852] * Make list of data viewers sorted alphabetically. [#866] v0.6 (2015-11-20) ----------------- * Added experimental support for PyQt5. [#663] * Fix ``glue -t`` option. [#791] * Updated ``glue-deps`` to show PyQt/PySide versions. [#796] * Fix bug that caused viewers to be restored with the wrong size. [#781, #783] * Fixed compatibility with the latest stable version of ginga. [#797] * Prevent axes from moving around when data viewers are being resized, and instead make the absolute margins between axes and figure edge fixed. [#745] * Fixed a bug that caused image plots to not be updated immediately when changing component, and fixed a bug that caused data and attribute combo boxes to not update correctly when showing multiple datasets in an ImageWidget. [#755] * Added tests to ensure that we remain backward-compatible with old session files for the FITS and HDF5 factories. [#736, #748] * When a box has been drawn to extract a spectrum from a cube, the box can then be moved by pressing the control key and dragging it. [#707] * Refactored ASCII I/O to include more Astropy table formats. [#762] * When saving a session, if no extension is specified, the .glu extension is added. [#729] * Added a GUI plugin manager in the 'Plugins' menu. [#682] * Added an option to specify whether to use an automatic aspect ratio for image data or whether to enforce square pixels. [#717] * Data factories can now be given priorities to determine which ones should take precedence in ambiguous cases. The ``set_default_factory`` and ``get_default_factory`` functions are now deprecated since it is possible to achieve this solely with priorities. [#719] * Improved cube slider to include editable slice value as well as first/previous/next/last buttons, and improved spacing of sliders for 4+ dimensional cubes. [#690, #734] * Registering data factories should now always be done with the ``@data_factory`` decorator, and not by adding functions to ``__factories__``, as was possible in early versions of Glue. [#724] * Made the Excel spreadsheet reader more robust: column headers no longer have to be strings, and the reader no longer expects the first sheet to be called 'Sheet1'. All sheets are now read by default. Datasets are now named as filename:sheetname. [#726] * Fix compatibility with IPython 4. [#733] * Improved reading of FITS files - all HDUs are now read by default. [#704, #732] * Added new widget property classes, for combo boxes (based on label instead of data) and for tab widgets. [#752] * Improved reading of HDF5 files - all datasets in an HDF5 file are now read by default. [#747] * Fix a bug that caused images to not be shown at full resolution after resizing. [#768] * Fix a bug that caused the color of an extracted spectrum to vary if extracted multiple times. [#743] * Fixed a bug that caused compressed image HDUs to not be read correctly. [#767] * Added two new settings ``settings.SUBSET_COLORS`` and ``settings.DATA_COLOR`` that can be used to customize the default subset and data colors. [#742] v0.5.3 (unreleased) ------------------- * Fix selection in scatter plots when categorical data are present. [#727] v0.5.2 (2015-08-13) ------------------- * Fix loading of plugins with setuptools < 11.3 [#699] * Fix loading of plugins when using glue programmatically rather than through the GUI [#698] * Backward-compatibility fixes after refactoring data_factories [#696, #703] v0.5.1 (2015-07-06) ------------------- * Fixed treatment of newlines when copying detailed error. [#687] * Fix a bug that prevented sessions from being saved with embedded files if component units were Astropy units. [#686] * Users should now press 'control' to drag rather than re-define subsets. [#689] v0.5 (2015-07-03) ----------------- * Improvements to the PyQt/PySide wrapper module (now maintained in a separate repository). [#671] * Fixed broken links on website. [#678] * Added the ability to discover plugins via entry points. [#677] * Added the ability to include float and string UI elements in custom viewers. [#653] * Added an option to bundle all data in .glu session files. [#661] * Added a ``menu_plugin`` registry to add custom tools to the registry. [#644] * Support for 'lazy-loading' plugins which means their import is deferred until they are needed. [#590] * Support for connecting custom importers. [#593] * ``qglue`` now correctly interprets HDUList objects. [#598] * Internal improvements to organization of domain-specific code (such as the Astronomy coordinate conversions and ginga data viewer). [#488, #585] * Astronomy coordinate conversions now include more coordinate frames. [#578] * ``load_ui`` now checks whether ``.ui`` file exists locally before retrieving it from the ``glue.qt.ui`` sub-package. [#599] * Improved interface for adding new components, with syntax highlighting and tab-completion. [#572, #575] * Improved error/warning messages. [#582] * Miscellaneous bug fixes. [#637, #636, #608] * The error console log is now available through the View menu * Improved under-the-hood handling of categorical ROIs. [#601] * Fixed compatibility with Python 2.6. [#540] * Python 3.x support is now stable. [#576] * Fixed the ability to copy detailed error messages. [#675] * Added instructions on how to make a fully-customized Qt viewer. [#619] * Fixes to the ginga plugin to support the latest version. [#584, #656] * Added the ability to drag circular, rectangular, and lasso selections. [#657] * Added the ability to reset a session. [#630] v0.4 (Released December 22, 2015) --------------------------------- Release Highlights: * Introduced custom viewers * Ginga-based image viewer * Experimental Python 3.x support Other Notes * Better testing for support of optional dependencies * Refactored spectrum and position-velocity features from the Image widget into plugin tools * Adopted contracts for contracters to add optional runtime type checking * Added ability to export collapsed cubes as 2D fits files * More flexible data parsing in qglue utility * Numerous bugfixes glueviz-1.0.1+dfsg.orig/.readthedocs.yml0000644000175000017500000000026713613574503017506 0ustar noahfxnoahfxversion: 2 build: image: latest python: version: 3.7 install: - method: pip path: . extra_requirements: - docs - qt - all formats: [] glueviz-1.0.1+dfsg.orig/doc/0000755000175000017500000000000013752535025015160 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/doc/whatsnew/0000755000175000017500000000000013752535025017020 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/doc/whatsnew/0.7_code_reorganization.rst0000644000175000017500000002056313455362716024176 0ustar noahfxnoahfx:orphan: Code reorganization in Glue v0.7 ================================ In the Glue v0.7 release, a large number of functions and classes have been reorganized to help make the code base more approachable to new developers. As a result, users importing classes/functions from Glue may need to update imports in e.g. config scripts. Most of the files affected are likely only used internally in Glue, but all the moves are nevertheless documented below for completeness. For a few common cases, backward-compatibility is provided for now. These special cases are: ==================================================== =============================================================== Old location in Glue v0.6 New location in Glue v0.7 ==================================================== =============================================================== ``glue.qt.get_qapp`` ``glue.external.qt.get_qapp`` ``glue.qt.qtutil.load_ui`` ``glue.utils.qt.helpers.load_ui`` ``glue.qt.widget_properties`` ``glue.utils.qt.widget_properties`` ``glue.qt.widgets.data_viewer`` ``glue.viewers.common.qt.data_viewer`` ==================================================== =============================================================== The old imports will continue to work for now, but will be removed in future, after a couple of major releases. On the other hand, the following imports will need to be updated now, as no backward-compatibility is provided for these: ==================================================== =============================================================== Old location in Glue v0.6 New location in Glue v0.7 ==================================================== =============================================================== ``glue.clients.ds9norm`` ``glue.viewers.image.ds9norm`` ``glue.clients.histogram_client`` ``glue.viewers.histogram.client`` ``glue.clients.image_client`` ``glue.viewers.image.client`` ``glue.clients.layer_artist.ChangedTrigger`` ``glue.clients.layer_artist.ChangedTrigger`` ``glue.clients.layer_artist.LayerArtistContainer`` ``glue.clients.layer_artist.LayerArtistContainer`` ``glue.clients.layer_artist.LayerArtist`` ``glue.clients.layer_artist.MatplotlibLayerArtist`` ``glue.clients.profile_viewer`` ``glue.plugins.tools.spectrum_viewer.profile_viewer`` ``glue.clients.scatter_client`` ``glue.viewers.scatter.client`` ``glue.clients.tests.util.renderless_figure`` ``glue.utils.matplotlib.renderless_figure`` ``glue.clients.util.small_view_array`` ``glue.core.util.small_view_array`` ``glue.clients.util.small_view`` ``glue.core.util.small_view`` ``glue.clients.util.tick_linker`` ``glue.core.util.tick_linker`` ``glue.clients.util.update_ticks`` ``glue.core.util.update_ticks`` ``glue.clients.util.visible_limits`` ``glue.core.util.visible_limits`` ``glue.core.qt.simpleforms`` ``glue.core.qt.simpleforms`` ``glue.core.util.CallbackMixin`` ``glue.utils.misc.CallbackMixin`` ``glue.core.util.defer`` ``glue.utils.misc.defer`` ``glue.core.util.Pointer`` ``glue.utils.misc.Pointer`` ``glue.core.util.PropertySetMixin`` ``glue.utils.misc.PropertySetMixin`` ``glue.qt.custom_viewer`` ``glue.viewers.custom.qt`` ``glue.qt.data_slice_widget`` ``glue.viewers.common.qt.data_slice_widget`` ``glue.qt.decorators`` ``glue.utils.qt.decorators`` ``glue.qt.feedback.FeedbackWidget`` ``glue.app.qt.feedback.FeedbackWidget`` ``glue.qt.glue_application.GlueApplication`` ``glue.app.qt.application.GlueApplication`` ``glue.qt.glue_toolbar`` ``glue.viewers.common.qt.toolbar`` ``glue.qt.layer_artist_model`` ``glue.core.qt.layer_artist_model`` ``glue.qt.link_editor`` ``glue.dialogs.link_editor.qt`` ``glue.qt.mime.PyMimeData`` ``glue.utils.qt.mime.PyMimeData`` ``glue.qt.mime`` ``glue.core.qt.mime`` ``glue.qt.mouse_mode`` ``glue.viewers.common.qt.mouse_mode`` ``glue.qt.plugin_manager.QtPluginManager`` ``glue.app.qt.plugin_manager.QtPluginManager`` ``glue.qt.qtutil.action`` ``glue.app.qt.actions.action`` ``glue.qt.qtutil.cache_axes`` ``glue.utils.matplotlib.cache_axes`` ``glue.qt.qtutil.cmap2pixmap`` ``glue.utils.qt.colors.cmap2pixmap`` ``glue.qt.qtutil.ComponentIDCombo`` ``glue.core.qt.component_id_combo`` ``glue.qt.qtutil.data_wizard`` ``glue.dialogs.data_wizard.qt.data_wizard`` ``glue.qt.qtutil.get_text`` ``glue.utils.qt.dialogs.get_text`` ``glue.qt.qtutil.GlueActionButton`` ``glue.app.qt.actions.GlueActionButton`` ``glue.qt.qtutil.GlueItemWidget`` ``glue.utils.qt.mixins.GlueItemWidget`` ``glue.qt.qtutil.GlueListWidget`` ``glue.core.qt.mime.GlueMimeListWidget`` ``glue.qt.qtutil.GlueTabBar`` ``glue.utils.qt.helpers.GlueTabBar`` ``glue.qt.qtutil.icon_path`` ``glue.icons.icon_path`` ``glue.qt.qtutil.layer_artist_icon`` ``glue.icons.qt.layer_artist_icon`` ``glue.qt.qtutil.layer_icon`` ``glue.icons.qt.layer_icon`` ``glue.qt.qtutil.load_icon`` ``glue.icons.qt.load_icon`` ``glue.qt.qtutil.mpl_to_qt4_color`` ``glue.utils.qt.colors.mpl_to_qt4_color`` ``glue.qt.qtutil.pick_class`` ``glue.utils.qt.dialogs.pick_class`` ``glue.qt.qtutil.pick_item`` ``glue.utils.qt.dialogs.pick_item`` ``glue.qt.qtutil.pretty_number`` ``glue.utils.qt.PropertySetMixin`` ``glue.qt.qtutil.PythonListModel`` ``glue.utils.qt.python_list_model.PythonListModel`` ``glue.qt.qtutil.qt4_to_mpl_color`` ``glue.utils.qt.colors.qt4_to_mpl_color`` ``glue.qt.qtutil.RGBEdit`` ``glue.viewers.image.qt.rgb_edit.RGBEdit`` ``glue.qt.qtutil.symbol_icon`` ``glue.icons.qt.symbol_icon`` ``glue.qt.qtutil.tint_pixmap`` ``glue.utils.qt.colors.tint_pixmap`` ``glue.qt.qtutil.update_combobox`` ``glue.utils.qt.helpers.update_combobox`` ``glue.qt.qtutil.Worker`` ``glue.utils.qt.threading.Worker`` ``glue.qt.qt_backend.Timer`` ``glue.backends.QtTimer`` ``glue.qt.qt_roi`` ``glue.core.qt.roi`` ``glue.qt.widgets.custom_component_widget`` ``glue.dialogs.custom_component.qt`` ``glue.qt.widgets.glue_mdi_area`` ``glue.app.qt.mdi_area`` ``glue.qt.widgets.histogram_widget`` ``glue.viewers.histogram.qt`` ``glue.qt.widgets.histogram_widget`` ``glue.viewers.image.qt`` ``glue.qt.widgets.layer_tree_widget`` ``glue.app.qt.layer_tree_widget`` ``glue.qt.widgets.message_widget`` ``glue.core.qt.message_widget`` ``glue.qt.widgets.MplWidget`` ``glue.viewers.common.qt.mpl_widget.MplWidget`` ``glue.qt.widgets.mpl_widget`` ``glue.viewers.common.qt.mpl_widget`` ``glue.qt.widgets.scatter_widget`` ``glue.viewers.scatter.qt`` ``glue.qt.widgets.settings_editor`` ``glue.app.qt.settings_editor`` ``glue.qt.widgets.style_dialog`` ``glue.core.qt.style_dialog`` ``glue.qt.widgets.subset_facet`` ``glue.dialogs.subset_facet.qt`` ``glue.qt.widgets.table_widget`` ``glue.viewers.table.qt`` ``glue.qt.widgets.terminal`` ``glue.app.qt.terminal`` ==================================================== =============================================================== If it would be helpful for us to add back backward-compatibility for any of these, please let us know!glueviz-1.0.1+dfsg.orig/doc/whatsnew/preferences.png0000644000175000017500000014743313455362716022050 0ustar noahfxnoahfxPNG  IHDR/ iCCPICC ProfileHTSiǿ-!)w^CJ(!BTTQpT`{ 2l{v}{=[ HeHg \Q ` &fs.AA~wp@t,7!p<7 Ļ9a6(āVn` >?-OR?%,9Ny 94bsxs;M%=' =%5=P"%p=$y~d URS4$"79`s $<r10C.3[|.e,n#!J62Gұo∄99li@J@ h}`th 3ұ , ҁB2l58:)p\Mp<b0^qLB(R!#AP%A|HB2&g$tChz }Q0.f./ /L.7p|n໰~O4P&(& F%URTՂBnĨ1g4ME3&h{7: AgW7kЍvmzC`0v&Ŕ`00m!,Kam(l vv#v7ۍb'p8 ıqٸNaY-W[=x>~-  ?$tv@O(''tnDYсJL!!V[OH$&ɖ#IGIId9!ٍC7(.řMɦl4QSQ>IQLXR\RRR^Ku]KHWI!=&Cѕqaˬ9)s_fB*k.(.Q9WXnyA*EurC4,MƢhGh}qy9Eyt]΢_T\6((R@Y1ATU%RVheC%ʹ{/*--_YP؂G*J }*U&TTT;Uϫ՜R*ΨSyg_2.4F5c\CE[CQѧ1VUQUգ5]ݬHI١ӫQWO7BwnK@Y>EI?SAրij!lhelXkx6667M\LrLML~kM;L_/^pޅͬ=631_keЂcQkqǒbiڲ"E ,z`EZocZhb=jmg> bnd^غڮ=e.ݟ&G-NX񠃦ۡAps(vpb;58=wr:pv1pIq9Um[;˽ԽC#̣㙧gg縗 noV,UYsomO2~Y]vɋ`jHlȡ塏Da=1M##*"đ #WF^REuFãDO,X}PULI̽ez]Y'{~1)SO!)>3uD{\ҹ؞#߹BEߋ/y^:{SW쮜ʼqZumZg~FMۛ]ruKwXw /ރ1F=|(''OeV=SyobkC? =C/(/ՇF,FNz|+ɱ?dZ?>9>FfwJ_g"hهK?)}j%dWoߺ~2>5%` 3R8ۃP(5g 3C˾nBCN$". D uaYȈ|z o©SS#"ڦ;sVO *y{[oL/bش-iTXtXML:com.adobe.xmp 559 507 @IDATx `T=$p r Xx[h XDB[,-ؗ4FUTVZbߚVEEB*A B !ylNNޒM|G3<9;Of, $ &ֿHo[Psq]c @|#=NqU$@$@"Ю01vHH6̴ʼn-m|8   "Ц2'8_ hm $}O䉳5mo;[IHbK5qn [F4V$ hWZh>bvin.[ݘ$6D$@$@l4WM;6ƺ=h1O$@$)un/w('Xo1  6E VNCӛ=F¦є6j,lH@ph MklE&6I6՞hG<mK @% g 6woj}g{;QTv Q4MU hwfRַ֦h׍I:&MHu#sol=g;̓ }#F~g[Fӭ=6HJGFV47!E7uIHbK yE+7bVɲ֜Dw$uB*30#iØHH@4$tB5qc꘺݉Kh ߔu% fz = UL6yµor5&h <\+ &DǮoO7IH@S&H &WBʂ pFz[z2pʛ[n/N4XM% M t$in!Snѵ׋:ReV&TtCյ45}4F' D'p$}ӉF&{L~jM?jYKMJ׭%d ֭;pvݦ[$@$}Wr7yKȌn}2gnD##[Y$2ɛ)Rqӵeґ}$@$@m@x0=7y(,\^I;u]q7e8R={=F~(=2,T^̕,i"i7]{[t4zL $h&`nP2-_6.:peѳ׉(ݜjms+sy{Zh^42JS$27}\ dB2*njPmHHiBM葴[SWY\rʟVgM_VUL5vַӑM7diNY)S%C9%%%7vi~999GX2T    P[[?}Ѫ?}r1[u`4{Y& %2{pi/kTLRE"K[+.t^nkMM zc1  @HJJBJJuڵQFJңF O~N=k$2SfˣN`w&BNvPi[L۷UUUx܄Ry|S:ciSXEupQoU "#FEhwR鬸ɂ îktd,jJjlasKuLyq\z})ROREv#23IeFuPqIKK3qnvFW\qy?c;zv6}:Zdsϩ? nAuXuZ֡yN<<N|н̾w"L6 uJAϜkI@`73ʜ;eY&:CpzZE %.fF`ϻ,Xu>^*WH"SsHtؠ @O]q~IF:dm+u[Ep\j6suI4T9L!~&%[Fe?uB0'sJJ2RSSe7N^L}oEl[ /= >Ϸ 6OqxϹ|yU-g.K$hy0L :[UM3WYp^tni#$Vt)ǣ.80!]RFU*V¨n^^)r_-tqg-1OZYPinpm%'XP4|rxe%6o/v~Zѭ[ -&y;ɋyov~ W{_8<? ,(ʏS3| 6bZi?hWO-.#˭>ȓݤ=o/Lj{1a\q ƨޥ߆^0:/J_oX="W0U2w;Tyxw{N&;%j/$AXnZݔ9n`2,4y55pKYحLeϼܤ=}̺oR80~'!I o'p51iy=_4XQ}{ù].gRgögo /#OVSҎU-R{Zt s`jHިdީ"?GJ_SY蝛Oou_ŧkK?y??akB$S8;_=C^G 2>Jκ]E0.S1+Q73ש=2J] ܑ{ Xevdۉ%owvWc 0@@aI32#$:gڴmlo/ں3{QWV¥My^L'!Br<\{Npxo/,o~՞Zdft@ZR<"B Ym-;.[H[n 4a)k||q^|sHwqlZ'7bT_/cy!K~t =f;z'ުK=8熹8gpjj{ʙ [-y7\zzױLe:"jWN8W73XGpʎ2x0 Ci4$k񲬙+RVS|!D'ӛ21ΊM:X= V= r$M]yL`t":nw;qvrʩz8WWXR,CtõWWBcbV\Lߚ_BTp % =x+S<^ מqHOϐm49Zoxà&^헴4vjyvȖ+;>޽Gf;snř=Eڋ2{Q[g[1Z+u!mO5Բ)9)',3k7O3O4 $2`/L:8Ch ip4F7MGevɇ]y=LToS;z8CH3VIB]pH-}8:T~;2Q]ี:uygεθ&2gRNV?ox@͈9k4aivQ-Ons1ˉrub,I&|V+ *?y.= d<1& ?, 8i'cO`b8o_}raƋ__#O9DOvbbDElER+Q]ףշ,TPerS?`H.&& Pb>%W'\ dr˜iAm*4K3ȝz&U_evu)/ŨrX^ q^nШC`׷Z(MچIƑ~qc@tU!]fR+˟Q8J.r3ҭe䔗W֤q1 &pΉ3b۝#7v([G_1pk)Bgye_j=L nݟSe  h/8Q2Gla$[/or$V NǙiս+h6f%P9ʄ:)7Y\yqcHHpΏt#׼Men!\[FZy1(*WG'ܹS :3L Iw sf4#;-ZMڮ]fc鼨[]feny&IHHϛ:/6Gbek.7i^nd&Uft"u@`;e3yގM[lw`'  N̍q0n:*3V3Lf7)K4 YyV2sÌc  h/'c7+&ZL27)o8V΋1ҞM=rMߔcHHHp[G:Oڃ̮ooGZߔ27*brw2!Fֱmhn$@$@$ 9-ֱny7>ZS_Fnb,T/XfL6L*sU}  h/Bi0uĞW 2bCf4jPQ5hPnN=i{޴&2ilcL$@$@텀sqyvzNkNga5,8ʋi,h#Qj˔iL=nygZb-0 @"0svMk0:&mbKb/7`NVfo3tضZsrW㜰y{ZkwK3uHHH-p΅μȜiAMUfktZ,nL$;86 I;ycHH3Ci0eacSȜy#7N:/ 1&UL}IS^IgL$@$@@$󣛎\&1y-2K/v^jiǀ2rخ6m1&  Bm>tʔ4c+r-GA]ȝ1uLɛr3 @{!`\hd62;c껕)y΋e˩cTn&v2{|? 391mc7&"h;A ʜuδֵM&$$c4l<*SKHJJ=={Q8? uĩ 99Bǎ ĀyȴMGÙc Uԍ8KE 42si{ʌUE( 5)HBҌZk5)IBGr64cO§~Ǐ{3ӌݱĆ؝3eJɞ6ysb"ѠI[]fױUb{{L@T^4eTd?k2& ЭY/ +&4Sw]t^6mѣGѵkfͶ3n󡑙O=oO+*=NeTVnKK:/ff\&obU攛)sƦ-yR! qZtKZw~n#nJ^{n:/ѽ9UäqBTn4δqU,)΋` vM&>r{bc F{4*1Mϳ3n)YDT-OnGPo증p2Ce c_T6yg=oO&ӴtUfRNA x<țJZ/u{'ayaI^"ܦ䬯bLe- 睖뿔 Ϟ>s1vnJl߰(/Nc.-X0@HY?ӈW?ڮ x$H̃vaVIGӵ=m8vCk$ʌTn/3iglkŪg0MIB\rL_[ꌁ)j//[mKW ܦr/Uxq"9+ɵeK#LCr2*>ŚRDo#z ]E̷Bk9J}a*V}/2TUAe~Uk M TFL|Y͏F2Pʜ2 :*3nМ΋Ğf&\v}#sF1 DM#NAUlS> 4ȢG:9ub3NM%&ɇWv5uJ#u =j/YUYˡwӡ4,:,x .Vិ>݌§nŸoO $8\hϛe60^'r7m1y gJ}y&WfbL! XFoZ˥N [KK0K'cYIB}_g̼L+^ůwKAf$l\8T 5/j.I1׼qJ2 G& lt{-~_{kdxzŲM?ڞ/{)vw-Ջ^ky*xx\lP}w?IUQ-f?>_U.iE{xқi^0`B- hL}}㛏lNg7zK*4$2Nf/3rcɛ[,/B0SVWeKδMI *OKVadWJsY΋[I,yZf?d ݅%yv듌{qa-fKYe)"hRӀ[S%_gIyGY ||;f%aXwJ[Ћ&֞j8^(-i^{iC}˚55_k$LcvZK k+7q9ů_ubyxXC3F])2iѿpGɪ3Vb|X_=F7SrL%_{]p#l׳UasNW{^&sLWy,V^bbX֎9\R]g~UUUJ8q<`( SY&J"uo+ʋ{0k堯~ҒeD&߾\۹i%gVyC"Yi+^9 c$zU_m uZzz+}Kυ%\^\qzNMOǁNcߕ~? . =\-^'?:c@*/ʇ0`\|zh;|J&{5`Rdtys>'ac,=%!>*w>n[ߒ߻-R{Zg-,yn3" ʥFW/Ix컶FEt8$]al!;kyڪGk#U&,YXri1}g;~b7gZQy/z&<_C$7\Lƕ@E금XUkF<5c1rܻĤr8"!:bp]&JE06c͇q*E͍@*NٗDC380Rn^4rX?\*726~hI@r̞v2glTnejAμQW}vȁ07,GeWǦ=ǬW_*ߤӰ.0b']CZFҰ!J0]yѫZeWyQzA_v*O3똴Ѽ=mM$MV?Xڎ[[n`mD$WoYA:b;s!v . &ctNwOY9lL"Fr8 Ǯ* kwIKpխ6QߝRw[k4NT#G1-cՓ~8.46՘}}74]y'jyIX(wpgR) '\N7Ȗa#> uMfĦHcl]5_Bz 0`ؿg#~?l\1oljzEN܁ 7?۞|c#+?ߗDl:sk?TjڊB7dڭM3m/7:N$Dž+.1~_yǦž]V] =[tN5ˤ}|=(!U͹0 k.}"F~*O"UʄGtFvrOtk%m&Q{Q_ŷ$$Y]|KL£ƾT:a]W6Oƾf lmW/iGoyh|$WqȺuV*] ! 4$Wẳorſh38AX_ߪK1^kg\vz)F$ sWb F?ta7+2*״3dz1C5ck\ڟuL2#wK3ֿLӺNSE★rk/f &0>x5B45&8E&nO7F]2I_U<eNXczLC6#2n[C4>iPI#!533ʿbF"rJ ; ~Όr$yyyߔ*{R]_.{Ȝ:,FLk\i Ffb#s2gZL㵵?b7)Dm`"p¿md*x~N2^ >7|ڬBkoY?/[<"3=:ș`<3?bEjqEN Ĺ&6C|cQeZK1rkb-L@t Z끗nvni+>\ A?m۷^c&$qK4p]i7]e $(?w"9zz0 -FNkS[[̄:J>}%?ٶ7ZTRa9Bt>uvAtakXK^JLSyM7@H{a,$IY Uڰ^nϿv߾}O@l߿ Zd$`ρf^{ڎ?^*M}N[y + PzPuXF3p o#̊ ghuO?tOp4(CGҥ zmjIhONjznvԀ& ͛`2SƘ"pi oC/!pQBKH ̓9єǟXCG0z`֍} @ϋt$V?6K)`0C2  h͏ԸeK0N`91O$@$@$9HH"9 v^H &(QHH@cPuT^'xp^P(`LcsE6uIHH-0s}~ 6><ȑV?vG$@$@m@sh/3PmhLh> $:̗Ӭy1VXVŘHH2h-/!Zc   Kj 3)  6=%7$MD'f6D$@$@qH 0Z":/N>$@$@$О $47FΘHH33/8Y%%o@$@$@$@jxj wm~+y&VF?e"/=6?;5ʲx)9Z]MhS01qP Q5{-ر&Rl̡\;ߑ]v܈>F}o~<};tE!QNR;kBE$1]yV\5[DKƌQ)(z2ql|,_'P&@@,ů]ѯ_?&xݾ@ UaqqQ.YJ &k~]=V;_ vv]I %kBxrd,Z)}UlcIJp?]+az".p%E~rp oZ=ʍSSwl۹t: j{~'w?~<w?vn{ wOo_swe>x!sq|\~x'f핀qH|99WfI[5ZŁZW˪MϹomt^펄G90*Fc1j \y?x<蟺C-cS5&m'#zϿƽrNh=;?O/?,Ӱzݕc?|:,>:` `.CvOQw0B ŖPkEH{رVJ!Xb`j~>u,N4 3zRZ^uףƜ{%?T1IY\oIEK7/~T89#fŷRpq ^Ţ$Yꏽ{:/#+/{ W^c7 5n yVuUf& ?~ XjcG< 9^>˄nsNGuo&}G'1WW\ ~ԏ"epY݀mve 5߄1/g}Hs8cfvۅ!Q 6_CNYGGO~!1 yb[vuϚ?bɇowof3ߍN)zW.ƼӇ֑ϱAj?R6ͭڰ3qR3uFRJ3R]m)*q@|A?}+O@V! "5y&UZ)ހ%`Vi]K{xt@V މ>"O\/tֈ>/Ǚ7k5=n'GXj:Yo4q>X+EOɪP KT m[νf&fMI;1\t`f4Zt8R3WIfo,Ĵgcҹ*0m÷qK=%2>^ 3ƒ3'Lx`f_>t- FP ]'1A7+z2]$׍@{wŃw2O\$F>W?5&u\p@޹5ݫ|EIrI$x0ꯈLϼ8k$]62ͥc6iʏ3mdKuLZФ56y{i@ P^^.k<e( 5#r FvʰQxR3C&)4ʈ mPIEG{CY[][he[,?8g@|txC\sbҵww_]-?$ǢrwiPV~h?8rL:o%+^՚1֤ϾI tͬt:t$t(+MH c"~9:kU #\_}y[O~anr8 yi[@H ~uEEl{k7}zuA<>*@J WP;p,'ԧQ4t2ũ~Czx^Yt^=@%m 8!pj{]*|Zfi 4@m=k-nN_A'0z C yl$VCώI 2YeIC< ğ""[B?EDv%=2"zL<:/-Ϝ=@\PfpjO'[OY $>"+{^N'q@;!pH$@ dyd &pIO/1s0$@$@$@ LK߈ЊS15#g`贅xu(|qMw0J_+}||zx;T9K cɵXZ20 o~J|6\(;WSH|l))~uiPJn+TZ_3mn ُuKӿm`L$@$'ɍ0fdkbY!&-3|q\tKixߖǖ͡N>%1uX6*qlLu 0_VW*6WL[COdyϨd iS6MF|/R@;:̟{ >ȶsNn}&HH87?`8*ߺp@$2>'dK6[ul\Kĩ2Gk[lmU|V^dpyiPMTmJeAHmj϶YX56mrgAћ6_ah   '@%o{4 kiź]eTacpz#=BLYA߰*(ۂ[&`ڴ1亶ue0o|h&A`m9ふx|Tɮ71klIx13|m>&]YiߥjԴM5KiGu4,8_n|gHis][=.g w`ʴ|׺v3m; Q8cJn #F.П׳l,Y(|72pe᫘2Le/A5!l{*הco  HIXcLcsiW&mb]qLcsI2B 2ǚֹL/٦rh<8oណy}k eK=Ltw"dg+(9 #@ņ0tҳ;6ӠѪ}8p#MG{*P&? Q_Ҡ eʌc[-\ѵxlUGz:1U_vBi 'Ʀ櫝{ҟk_.rM{2Sfּؙ^Lڞ7rK%3eB&@*÷BRW)^YY;qM%> wfSsQgB \#(rIv9[΢Pu#ճaHH x/hM((X4n=KoYq  X Fvqu%HH3# $@$@$@$0$̭$@$@$@J $@$@$@ EKB.K$@$@$| 4 3i 8"84HHH <:/QHHH yASHHH5HHH84HHH <:/QHHH yASHHH5HHH84HHH <:/QHHH yASHHH5HHH84HHH <[/vD$@$FWFKcF_5HHZݻ[vZLC͎HHϼ$ͣ$@$@$ yiwc&  &@%oM'  HK{3 $0:/ |h: Gt^]IHH yIGIHH=:L$@$@ LK~&c |+8w>pQWY@`j?m{n4_.fSBOlǴE̥ԍ\))K-uVPQLI A|gp23 8"{{guY-rU;si>IBBU|.:]֬d{_fai&=cRG}Bc cA=Eb )]تK8Ɵt\̵s.TtȮ;?Y7b>qA+N@&'"ccB@!pr{޲`j9DE-r#rV"wE*$!2y#g~q|"jrJe + Cڜ Xm8 5^5;ե۱aKAuWd H 1S W=ɉ9+b>!,+kL$-E~~.Rf".:,y(ɨ)6>DumkX/ǐH yX(YI3`1Ԗ"2:qix'DҌX1&NnBb\~ ,=UݠI! !^霴B..b,%V >EWEYxf~`^ll/n` QK37i&XYƘfOy79twu A!p1 ͋IIۼx@+Zޤ٩HE?n8>:mVq8t8WBGf"e ܲw=bIBҴ$D'q.&Eݻ_ +KDTa-9_Fch:C!<'G&.C탙=o6S1tu]LOB|4rva[6#azfgџcE~LM&[a䡨]C:C3ҍ5 t`t_}(SB@!:xР3]LV:M)Yżccu/JXe{8q6R>>\8v+h)$$4g}YD>hۥv^h5@u4bUm+g.YVI)ܗ{W֪2P*wk+6=>ս_*Q-~(c8QiPq=*OTS,-s.=?uuugty1"})]]g:YY>gש:H*Kv;r Eսjw#v`,]ބ91M^iL]wzt9zg; h^wIyQAU$-\ae k 6R4<;: wHu"U|:#k2X@=EůIӆO’B oAAAn[ꪫ-bc{$gdr bSrMwQ۷q%/݈2/]K6 X)ٺ sclv}N5;l+83,U9*ΝYɹR$!\>sbkV/4{ʱiʣ>LMBZiѠ"1zr.lUUܟK6!q|YⱔVre8gU(Ƙs1ZbKcn.ML5hg951 Ŏ0}/޸bBnovP?xko,{;1q}tUȝ5kpM)u_]eeX< ɫH{kpe7ܨ0 oٳIEur\^pF@gTښ0~5:MMD\q,Zؒ~  Gxl*uڊ (|X4 is&`,`{Tndž-vWd H 1QUO`rb&f-GΊOH!@Mg0#)Isԁ1K(bJ" 6>DumkXտs& 3'caD/`!%kVlG^NV&w}LC:w5&E!Š"i8`qJۨ,$Ig6PDpn\].lJCRnMUU;0{dO 9F#c+YH3b"Dc3Y5`Lea“2Pd%[AfAEtuؚE&^u 4'=38+*#7 QHsQfHC= H؍+q%38+qFFZ g@m#R[ti0v>#02thZe وJœS"*V`aOZU!biߎ#U5XMY xv͆x lX^f7w\\n9H]OaPdWa }KM#Gرc8w\b!< pIr-X66+駟k׮3/9"m^vqzڌ FIӢޠ8ҧ띓S8*2o>bV6Tu:,jk}* 7bu2f8I6VePg99Ll.Ը?Qq@SS5^ӶU1ȍ 8Sc2{v&N3Z4sa mC =d^arwj/jB٘0t WSl+^I?QސT-/(b R I&m؍;"*I~ZT8.nHV[@-)[G\-v?.E9mtlt_Ƅ}sʩpp>ۿ r9oꢵbNz dRpUA9%(JP\u;}E'C-^Ju 5O@fTڋW|Yt\U\H=/6e9?&HG%$!-|PCr\-;|Kx/^eOPv`pkHQO 3hmuT)D H;ڶT}VcA􋞪Y0X&>_{EM%d6Et`w6Lk řFgq]OSXJOC"e Ӣ}we?g.c)}}YI ???/Zp9./A8#3]LV:+Yżmecu/JXe~۩ن***6_]K++?m1&c}6;_0BhjrZ֪rJHaee+ݚӾ͂, Ke Emnƶ=>սm}_XrZ*;?66UdNh =Poq1Opww:\4Ӈ:"ؔ3aߒ) db .SNӸ馛¶mЩS'WG. O]^ b^/?Y'1t=|j_slGn=i x :km-xԖ|_ I~hzKCvbYMI( {?Ii*= d[hu'tp1s4F\]IϺB{d{,XS9̬tĽW=Yڌjw#v`,]ތB-7Ɯ bbFCiyl%(Bv&*jط&[Jw\NۋmؖHB@q^Zr3۰ DRPz6 i㼃Ŷѱ9XHZ!Gvɶh=+sS0ѩz)=?}BZu*S]pְ2A@֠6v_F%fcuNX1[ b,ML5},۝ 1?+YS1sIݑM;urlu뱭Y:u~C'Z =k پ;VͷMm5[K\yVeؘM^ebn$Ϛ䬭-#w\̢q,ބ'Ȩ [ZeVIB9vbMLmF̝U20G܌MP#-۝vnt>{(4]:>6`-ۍD;cjcU(F}ajoߩXfQk0O[/=M;Q)g1NK[i)i={EUtOv\~;vΞ=kr B@ 56 j--'܆# .M@ؒ9 ,1z*cv>b^lE?`kV|ao _KDA@-P,;^mqJDF=nT s{h,f@Iͤ\O?/ )+sq}U>6#3G0eE1"ѽq!9~a> >F&hr֡(a$%z,hR 3 وJՋqܥ9Bow"omZhhE2&܃H؍+Q툈MCC8.(0)5A*yOUy^Á3V (Э_(9L>#02_2Fe(/TȾG/`lY˟QdF n Hipya>e<#Âj0ZE5j-X᷍DHmÆPK˘+ /DިCPf؟‡Lg/Z3sߓ +>ot^,4r*>˻ eXUމq'~egSxrE!!YyTYFS VJ bC^PC旀Vf̘p 0&-_k$Kϧʹutl:E,~1Pvt6Zkhƭϓ1#8a錁ڲ` jF(@ccٛpx Bl2X!ڜ!ˆ0R qZ4E 8H5]EV<Ӷ@.K  9=E+3c ?pn;+>ĜXk֣\}u~iH>F[B3ֱ*ߎ%Y{=/}򐗗G1mę޺&nk7v xmL,[@IDATރC&^e8^z G-$-{$In B?>[ Hva`+5w߇}twWA23gcSU/ l@BM,/Mq^6Ţq߸gKmQ欄rp4<ӚT?r_"*bαW叫PӦdŧ`Ȣڷit*lfn[g1' vb0%8Y>VgCV,^gR܏}aEF&nUN mG$ؐ!|nι:𕝕ew[OӧOď茜eב.ܹsB@q^ZMMk,>v7 8A>.[;)^4pۍ I@x\NoO:%JS[[MKJnaט1#JAMsєoOڝw0~5ĽCr[Yp͠0D lH? `6Ti^ݭRDdl,Dy1Im[1iTĂa?'6vZkR~idΛ ~SbT a߬?*?%^WVL0oн78plCJ{ ǧ P *θqb.}C6tn4H^nr _qÀ9#,(KyM7Y!j\}}mHCV&-zKFO- ?dϢm**,ibI՞_O=bHCVTFz"L&%!>~907eJ1yj;i9o憾 -,8>:@㐳+t6P}"sSM.ؐ -&33;9AVՑS_:vkD?"c2AW lt3Y8Vddcu9umѠ$ 0ckP6l5BRo g[I*O$DBAuo4.R! @!B@!nnntT! `@! hWyiWK:+B@"! B]].B@! ΋B@vE@vuB@! 8/B@! q^ ! {@! hWyiWK:+B@"! B]].B@! ΋B@vE@vuB@! 8/B@! q^ ! {@! hWyiСQc%7 1B@˜>\78/dvWܹs,%B@!  # Ě (qYqbN̅BFG8yŖ#ĞXASNF\WWdFB@!\Uc\j^in8/-Fk4)"BKdK ! Bu:! BKyHF! h⼴giE! q^RB@! Z8/YZB@! D@/jB@! KpVB@/K ! Bu:! BKyHF! h⼴giE! q^RB@! Z8/YZB@! D@/jB@! KpVB@/K ! Bu:! BKyHF! hiFZB@ΝÙ3gPYY~OٳgG+:uB.];C-yω+Pp#v5>- CUu ^> }yЄ~! v\?nL}E׮]Uٓ'OèF g%9~|ٷunI- |ֳ j .78/ޠ(u!p^}d܁ݻnݺ5 ÝՂ@?Nv@9he .rZupGr^j/_ȁʙ=Vn5..c2NL E(]cM}:5nSx4T6V+jqCj儗9^]k)=4?p)t96QUU^z/' ǼcnK/hq}[Exm ħ7Md\[۪;8?}[[u7x1ġvZi8/MrN:6leJd[^ix'ؼk*m`\yɬUb䞈N/ko^d,9E5";#*y[Ro8佄nw"5z2ړJ%CsZwn[5XP<ۭ90J3ut:xxsv vn݅{`T^==og@E8QE΋'?=ּ?w3tC;6LqJ.Ŗ%VNHe#z>n?+}[K~+eɴdXˀ /'5cc{ї:: LG=Ϥ71Kgt8JKgОT%5dٓTv:> V=w܊Oo}ɿݲϣ'y/ܽUbT)d~͹^rfO̢", 5;(VkrUe|`1!axwl1mfl{Bd%AO2}_*-qұe~3)؉W&[Jt +5Ž-ot'-ĉ/`(2|=85GC/`m])f߶˜PL|Ŷp ڱ;񡗱Q G>EW}8˸P^{!j^l1~cǘ{,Ƕ~;x^xBT>+<,wu~iǘC}5^G4xЉx-*Y ۸B<<SwQTĉx=Z$6QKBb|?9o<^]88|ͣxcmc]'& WcbKob#_o i6&oX҃:[Oh)nY!8Mi/U{WկcofG!NH}> 9ʎ[y3xٽ~/;0] gVf >\& COw>'ӿD*H^͟`Gp[;' W} )뾰$kׯ&vTX.yi,j{'% "ml^QG_DLcϕq?Tܝnq߇c_e/b.H{%=#‚)Xy3ޜI'685m^;?Z3QWEaު[ͻ/\y"?w%d m](XG_ذDa5Dsc׷ s? o¼{r>g {VtX' ^Ɩ(3ǍEoq=j\rpNx'QVy)<KPh兢DkV% e:ϾMg_?FᦞTF?21\"`:eMtp[1<w`#_v3۾4գn@{O p^xp"ycw>wJcϿPsxϿ$ J|{b1NƔ׀ףaXj;§,ej/ vl{*NW8w;J:;,WNyݍ//?"[l YagNwyrdgŇ6|Mzt“ίqe35Pj텻yol>E4g; giKGh6A8.RL{4 "9wW(j*3\0Ǵ-bo2 W)тkA+l#ݏ޴mҳ']p.Y+`oM>G 8bQ{rÐmOW3!McI j/NQnI3D=1՝1ԛ1>^[V6DE%'u;)0x}WnM5'w@U?I+OkQj?'#h%a&{kڑŶ-GK*| ܡ#n<\iw2wC?(ïޣۮ2YCi oclԟm#ٛoj:kH| :۸oh~Pcљ5CmH|]xq9+3 ! yrdge<AOx#:#g{u :wvT-/#"lqރb ƀFg(U0dD,^å8q0^U _KHt[&˿XP_p0xGq-φNAW5- T[]eLxjJO҃JQnd&_)qeJжY)= ]Md'pCS2+34yN1oh(×}#U/(nIkrs ^T}Gc8!mT*qj7ؼMD]IKDϰ{ݳؓ168!^}gOG9~<^)+߉d 9| m2M7e!^b1ǤzDg-jGE/>t@\+is-m9ꢴ qÀ9#,(K=zK=؃^çPC >%Z\i_a tdG cb(Mj!3{Gq²zM_`-F~㗏t>Y=j DO)jtsmY*Qbq۱.I[Q dm +lD5}i;̨6oͦ=;(Oɋ8pWԂރU7t,XeA@/Z3-|zU}vm y\{"Ճ")X+`3J%V)P y-ۉl[}vG[M]pðb# mߵBΣ@nG8^P&iےd$4;ysxL+xvПNvc]9߈O7߉&~Oߊ e/QߙqXW9z\u%u8u Gq򓧱}B7ܷ[sof-v/~4Y$x@l.Zg/nFh@f 1?gE1,!HTϱؽ<%+-Ÿo$#{ӜKg/ރWo+77_|fIM|;|:/cGx/zO?Z@zF4Peя-/pGo:cmBgF}8“x<~:ysXXQONJc[Gp'.OY7͋1~̃Ƞ4Qt=i 8*h]yC<-7>[&/`#;kL>My6hOch;h3{ & _fIK@ΉoS` YC??ȉO1eY J}~|JX0"`B<[& k_a]ϟNA7ZDd*L]WBM+}t"6gƘ 7W[^!E98F+^7zã'--)25paC 9⛷'8#uB.y χҪ ;QFz7BaS8𯟡HU*΋2֦3]LV:(Yżccu9Vi=fُ'NȢ끟s*J(-?QW5h߻G.%΁䐄69E[454`ロT%LrKTo)i/[g*,C1o^Gߚ&4j\Ft%tk%-8-VUj@?v3k !~Ԝmo tǍeb.={`h*Zq#9֊\׫#vtpcgz?$G5TwV58r;깪{W>K1d \?+iA9W{Xu.MlaSBs(/v?~%YYoL:Jv!'s:]2OYJcteJ1WATߎ /t]~Ht텻m $)wQ6)I9eL8@gꥉ}bz';"O>nÓ>ԗQXt\IE|l=`gJ( n{tîxҨ\D~QQO,[m&6*dWQ'W_}SJo)_XEq¯M|~^>n pYd8#; G[Ț߶qkX ΃XhÐ. !؜u뵱>^t0)7NC@r#ۏg\WVv\T6y ^`KB]J'p>ҊKΗ[^hOZ:`"x"%B ((eeeO0α]uU_^"t;o7~>'G5BM@/@-giC|<$x8/(!pԩcǎa6ُKdxM]OFḍaux*y-6G+Oli\3_V[}PL EVB]svI߾}π\.f}w0N M  KB@?K*iNEb+B={gh <9Q I/\\݅r^;.O6d] ߴ]vmS !  Zr^Ԑ͝z+Ycum=N_(U Й˳^e=_: eݺuJKK Y!A! L绵k~HcT WC?ؙel^}<]ly|bvˬVkI.]tտSNWr'%! TTVV|7|^u.V_eN(ˑ2aQl9=+J9nLneQiuY)9%s\.s8m˗t|s}z,s[Xױ"Cdgcn B@!кMzźegP(YYVWq9f=|WZj誶VU*z=zliedͲ19u,s<tٝN#O*.)]y|A(o,;3q1;*4Džm8]d9N. cӋޔ-I! ha.jd)Uu+Aew;"쬨XU},# ml5]W71:GTl+i;'˲sZ9,pt(YeUu\|sBy}58uJv:]˺+nJ²sWzAU絊|!duX ڬ,K_Ūm )V[8앣bC]܆YV:s],Si2^d! gt*OͲJc֩KUz*lltbq2̱M[mT 鼸$XMҺ96|O..ʎ 4EHlʖuqe.u*Ob! h>u3Y86JOuz=LDO7% =stJoi/wŖ:/Q5{dӺl..cY].UFA9(8O-6vwq9?g1s<% :DZO׉,-#` 6,_)]yM]fwwiUctYwy] W ^IT DM mtsbzНN*r,sR lc*,|cY! O@}6*,_9YdyE*V:^u;֫KSq#2ze^{#[yinI}Uis}dqPe9I7W9&f{s|)NUXdWzq Jrsg#yB@!>sU-.sJ;u.StJXӺ7ܥ:G,sPJ6vYf[=zWt^uOIYsYUZ<7|cb4Wfe{p?]\S(=Dzҫ4Ml%_! ?YYVi=fٓK9(9e٪1*NJ+;6,_MVOJ˲Jv3`gz~cpPaK3Ns9qPz[aڜḻvrB@x~$ve4*Jt9,z%XisywiWQAsڙ۪2*vlT[UiMEo;junk9́cd= gWigֳӺi#ѰXUt*f :sڕN$_Y! ]poiUZެFcYױ*V6zZLyr씾X]3{tyQsyrVy`\zTZɱ^%ج㛪liܞ+Džm8_U*yzڝyT ! $s9:g11+zZיJq66ΞtD:&Aǔv^=AY PsZSe؝y|]9*A9(,s{*b|\ub9Jc,:T*-B@\x< <=d=6ҬS(Yt\Ny,~gqT;Qy^[yijsp3HZ! Z[:=dg1tJ^O+c]V\N7ǨYʎu*(s)y4w51U({ui]7U9)*f;U7fY٩<=VeNO+YYڰޙ'B@\jpV9UZ96ʎcWNһ:<]s0ٴ _F洞JnIG]8*j:OSi]u{bgrH1t,(Y%X)Yɜǁ$xjI]b# )t3Y86t9Q6zZMŪǪf8_]f^VٴZd.1nSɆtzҙci* f1=dwy,sPuRWg:=_d! h=jn[4Ŝ_\V+o*˛es-Vh.OȓImvfvUnQ*tiql8N8_I*,#ϜVz9fȴ6,{jg.'i! hL@}7iqeJDZf%XācϱVu;%S6; 7ۘlL뱧vy+oiDwi]sS1*orL8.ϲN]dYV:YgSisBPsJb.d=6˜6xud]vs汭ASfyyz2*;=ΪJlۻJzy8+d/]dw1qPc_]-lvrB@qnƕYb=euqk,A*_ź≬q̡ͪ*W7,887n;OzZuI \^uʳpUn2ׯ_\VoSlyʚR_o\B4B@!pY]u3uJLxn:9tٝ9z0;ufYOΜ6(-ţ4#IY JS:!*sV_ZYaY](lTzw2s`*ҫ|BfJm̱;YmޙNաVJ<;[}wn' $[>ËϻQ`WsfxK;jG V0-Azx6բ:hXXkUSTo&`&p/xݲ|wVs𳘺J^s1jq-ƕKv ^]YN5XP~Aqyձo 4>cbhX|# +kLqk*;`嫹d<Ϯ:}ZVU=% qxc> 'R$GIOsϬjGgd( \'0wOMmΏ%Q1#?^pE<-@bR}pOjgNJro:&`&`'=11cO1+$ysOjEC ]1c>ʢ9P sꝏ>h;0RˬLL_:2Ώ9;H-R=z61f(Ϻ/&lrj_(1>Cs3`E1fΏ9\|>ƶ&`&`KwqSUONMu:=eq܃TKk>]achа0Ph̩\~|Т&˩>ZYdWQ=!LL⽓mkXu3k+y~ec^%%ϸS3eyЀ3̢|xʴ#s!ss&s{8aLL7/3c%덚W,jgӪ~ow}AどF 1g u먡^0rZgLL]'r_Z}ijzF5zRen=í^Uu@if 3Ƴ|br|aiP~? <߭,g^Vb>PS;מϙ?滘c:X`qn!O-:'[30x 6SM}ǘ{D=Ƭ[Z swL6Sڳe8(AG3kf,U~WYh94]G]c؇~fGW#oT%bjTw6˩>!XžΏ9X{(3=㩮00iΏxYu>rXߙs8壦qSYM.Gh1_#r,gLLg.֪'g~1>(h>s\g5,4\< %bDM̧vƞ! ZJR[OqLL`ƙiuˁl^kŕ]WmqĮjqSŃl#Fswgj30x 7#yZLz2~CC;See9Gڮ7˩,4xn움  8ƙi39TcN#byyjfs]:?c徫와 #rQx3O ^+Ռ48~Y k9w_3󲚨u^մ>⯢zg9008sV=Q>-(Ч=i|,ŕ]W%E9;/i_LӘ50xO3sV34?|η}ƜڬW̬nżƙi9hk2{/eLL`oח墦Y:|16E9;7ѴfXŕKfk&`&FuZkN}PxՏ|K:ӘSj~ٿb[ezԺXs\hV3f&`&^V.vF5rW~JttHggwuY.j(]4d3Z:;g&`&|g//g4Qˑn֜ [8gwuY.jc@3 һn30C`ͩr1řJ˝Ӟ)]]vf69Q^kk&`B`2<}|G-8kbTtlƑ;[k2gjefGhxl2k}00!pEsWGh<~sѮީ?^pCe͈pgjbj=Vz04_٣YѳL;QG_+jݺj/Yw[۵&`&`=;̼n'a^J-ϸ,WWJ*W vFտҫs움 +LoUS T*TGyѮֳ}eJJo}4n&`&y\죞.@j>ѼX9~%{fL͙Zؚ \3]Mۘa-+MƳgf5+{ޯ , < |vLݮ ?WIDAT 3Eg&`&`O?7[+f\Wα{w闱и ]j&`S/c[Wg>cF6ך :c>ҿ睎_q+3~L,WJ/_uپ%l6K00%#l}a. vqpˤ8߱ 1Q=U>}:TtY[(G|;_<3^~n00xipxާ_ʧ\|s 9ggzO8?>  _xF}MLL>V_| |X7faLL5 ®Ժl108G%縹LLLLLLLLLLLLLLLLLLLLLLLLLLLLLޅfmIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/aspect_auto.png0000644000175000017500000074607613455362716022066 0ustar noahfxnoahfxPNG  IHDR iCCPICC ProfileH wTƫ{r"͐ӐsNHIdQ8 1! ("",\]"Ae] kwyo__ߺ:?Hd& IӃCB ԁ.̵x}ULBQ, Gd8 ܀rz wE!_e:[5k9~2L& QXlD芰>'2p֬f$k'&n_VodF|d2x}H%2cl27lR2wa;7ˎI!_:Jaj 꾭wm? b)XjXh2g]qbSK[Cv4r@ 9:K` ; l,lp Ԃpn`'KރepB<iA O !6āRlhTCP5]A!4 AoO0 &4XV`l{ 'Yp.|.k3p;| |%(J A1P(oT(*CBJQ5TuGͣ>h*A[]h: ].G7ѽI+ha,0n` Ôb0m4=êaͰlv{ۊa8Ny㘸\ *n7'xg|(W2AB x" "iBaL&8^bx8N|K"I$_R,itt4IH!kaT!r=BRl)!J#:eA*+&)[B]`X APENp``{B!U!!. BcBTaaoDB&³"8U'H\Z"STU@eQQOSoPiX͍G+ DEDEE3D+D/Pbbnb bEbF>ˊۉGo_ȗh$It<"!T -)+.uJԼ4MR%/}^ ,)'CV_fQVNE+{B켜\\9y||UtQ=^F/((**T+ (,+)((*>U"*1Jz啽T*1*UTTTTvΪIe5Smԓkh`5'55aM {ZVI!m6GF{LcӬ3+멛ۡJOY/T^W}O D r j + Qvu622>eЄjer䋩)ϴtL,ܬlAc0 1/0H8o񧥎ee QNoRbZU[?Xml656ll#mlg4ؽ׷ٷ/9X8tvD98;888;M8+:\L\vtb\=\ɺwz=6y{ʞ)u-}W_КO Oszm>qIIǒxd(yKrg YOLNH~!C8џy0s&9= {'wճ[iw=.{5G?8ݾ}]{roe{J??xGƏuRuu_9F&f9yLؙg;[tZ[Z ΁s^y=Z~Vږg/tt;C:._jEK *.^.B{ejnn5m=O_;pƭ7]eum0t5oɯmuv m2l3|8240p,l0죄G=^~g3TiDoM''mzd5?O>}J cyuMk-Zz5_MFYOւCFF "#dbe*|9\:tsֽݿ G/[cɮ pHYs%%IR$iTXtXML:com.adobe.xmp 922 776 NC@IDATxdUnLw@hbA(Ȩ 0`DQ0b@'"qpL 2 "U AI %57V[}7wϩ {SnGooP -]e144Tq iC\];irٔK7~M)oo/*^ؖkhApqWےڣ9tMV2r'w8q+++m1D:a$;eؕإ++ ?R^LWx{%Xelű//e[=8qk[v_b>U_ږkcApqWےڣ9t{l[ʍG=?JMԥJ}r9z 9mL>X⋥L^68奼l6zvk_l9ܧK\rmӾ;.2j[_{/ntUKY7eX׶_XΑJAYV^hҿ]Y[?J|ɵ-1)eKy.S7~i׎Ɵ}ĵ-<˱*3%/Ǐ_F,[lIm7m 怰5r`Ǯ,p,1\kocSڑ6bmS_(Lj{ I̗962#900Ќ_'6m|J88Օ~ei#G&.2bu.J[36Ⱥ"_֗>Dvi̹X+ 62|5[-&PiImƥKr̡ԗmc+H6~>9SW4lJm/y/ǯ\+E9N6~pIr;bRN񐕱qأCXʐk/6b里69_>iZ{hC[F=ƚ.I2kǺ[_̹26"_1zcOeZDMnYأCusK;_?dAA 6Ӧ w^JYP<+>6~}d 8P9.3ʴX? ݲiӦ5dNh2br!'}n:+7Cki- ̶6PM>dUg%zi).P,!q૿>ݺiOh#aYqiC!0JR>98mp㠷ؖ=m9Ӿf_ e M >(^ r^d6ыe c61wv^ډah>N9}I;y)  mmv}|J%o>#ʘ%72ׇ>[71P7BMKrά:FuƎvy^id 98uVֺQ+H^΋muZGoX\kg`GFƁgLL{F>R3n5:>\2/>rƐ3G?=zVH5֨t<4/,X¢@!,Zp'{MVun\ v9=2I?݌q %ngl1vMۚK[W槾ԩoWmHqsεEWʴGfغ~h,TbAG/eژ:7~ed\? W&|3G9Xre>2rk>Οk٘rlW΍cb븴Ea>6ڕ6ņ;ҧ+1 2hCiviL2pԩoW_I$DžBWʴGf;e pl]?}啲xV KH>d_+O;k}cÕ7_C?Q7\ij ⚏Z6;ەs:.m?mlq؇dvcAflᎡ)JLkL,ڐ8bZ3tƳ]"=\*uu՗cR q!Е2Ǝv6[mdG_y,mcg$Rf,s@'v#WA8ZjopebW6m2x w&h{2(LWf<̍+}Hv[EL>\*1#0?d,nSR}iSA28ؙ #rʑA2m1)kC$rmыA[1k=#ig~'gJ1\lF[}ЉYh+/r.9r!崑YR}LAH?kz!A=8%5,e- /eR_`1\L]7!m26Kb)#+!k$?d&~ٶ_1WŃ:6aڎDAi .#kLv[ot5G\[|B//ؗ6`)+y_ ӧG#<2$/A$Eo?>9A6V,2l /x}llqGj_۾o~؇KF$~;}K_1Kp~rГz+s/xƵY_cm*e>/qYk/_@ Ҧr9zsGnđo 1ñ]zF3b~}9U e͵Op zF:Զ}sn푰t- A?11S>b&}k2}qKt4̺QWjVֻ*j>r^s>灾2lGg݌>a?ѧN%!wϺe=o&Fo6֜v_>q@$\cP٧͡OEn8&'}ʶ~,BbT1?H[c-m=kxe Ȍe>rkElMfY'gop05'epHKՃ!qK2?O_ĭ;~}N=2Ǧ n~G<)| .2ۧ\,d\[xY[aO Q{ЉNB&ZYiՎ-jm0GqcWseG9GrNJͧX*Gn|~bs\pFk|A9[oY8nњ;(ɹ+[Ιmя/ZRGHs\NG 产uI-]P{dʵAO$l U'evxqz9u(uvrc\,sk /kk[?Y !הõruGT PG:±u:ڐxO]ec h;C ^=~=>1!6l'zbxxFQKEv0UC;xiS2ApEy-/}m"v5r؞6ȝ Wmx c.c#O6ћsi+9G{.\d%)5돌[sX/jIc])_{NDmF9ʵ/5BnoGu?k51_\]ra^}ޤ Qn^1:cN(EeahkFmAr뫝E_?ڥȜl d6Qʴ8SC#N\l+'N; :aWP'WW.yik-c|c8Vk/}pb` WRl-3FOI`1i{h=OS |=s io2 1>qɘ(ǟriõW;Gv/㳯]>R.}r9r>6UkGd%VNv qruڡî̡lcO\ ?핣]Zp^lq{ۥ\YY׃:xyh_ѵ O8WfAƼ ?cG^7?pU.GƼjMh^\ʉN`Ab:.sBN;tE^=Bp1̇:WvK[kañ_{ťc[H_:mrevu)*ǁ_;>ܵ aL?paW6k1h#/e҇8*#cc^~DZFb[9q ,HLeNi2=Bp1̇:W^ϟ_YP DgyiW7QC7ω7X )ScŒuc[Ɔ[-1P \–md /w K}jS>%wrtprو =kb|rt ppkT<is )hs/5:ss@!K|tglrrZڠָ]YKK{cXvv{Xkg>أO?B:Uo rGq5mo׫r7#T=P3ᐨ}k(@[צ}.:|ţ 9Ť i_b 7-Kևk&w]G5Tה;_G֮ԡfS@}W]^O2'Ÿ_@@ŲXkKl>،qT>$͢^z9s3scL@:3G10w+xf}c۝_ߌes `G ۮnu;#zalwtuwFwW=ϻMc+Xoxovڳ_`Zb@ց>DGNoe5gs5o8qo棿r};*M9cK\u^p7Wps57s.sO[цv ė{H,AA!bX7lW``ۢol]Z;)v=ୱŪc! Ƈs㾗=}}9`"G?[oN0? 2}&GB_E(id>a^^CGyUea|a.w./:&WL)'ń1c*%E`x7yJlᄆG?RM  h'N!yi<~?3viX1)֛>%֘JLM,_\O/x)xE۵Jl[b}`S|֐6}+s~oq]W%1mͼXuԘ7߲Ž 'Zc&ī+;CrS"OZCken7'y+ÇxzfqJ{K>9Xc >!s}c=FN_}sA9}4ot2r2qbj1J ˁ1S۹/v'/NlѪM\5oL1c׻xO.nY8{Ą&$>x%O?9knM9iŽ<+bq#ոǮ[of5֏Zc']\zq_i716~Ů~VL,l;5ct6__;w@<̒%ͤIŶ~mα^u>`|q\&M'Ƅib5fo8֛fL7̣}qo07WOկ-NI9|[ۮҌi>pQt^vWի1Vkp uuї3i5s^AQ9Ӱ}ŭסsBn~jz'֦Z̍ip`Lx`JO3>vUZcQ~jjccMhr?xcџyw\S{Z51g5g}Iքڂ^3eZUx鉸cL>ڟŦ1QwĽ;;:c7 VYol8W\Y94|C66clq@؉e AZӞxȵ~;rl's`J?.e\1ٖ+3oL1h3/98~,x?qF=+q:ЇUgnhVȰCQY|^ /t ._x_Ƕ'df1a {'{hgb?9COKƦo}kOWv$œށTzrO"֝:.x禱ڦƄu7_}_=>?}ѸyqO}e[-0@5_˼o1eIyR[0+6Q75ƭc;y|XyP<ПC/=(vz67R{L+28s Cr|nel@ 1d ]Gۘ₉O&a'\[r3o'};cޮ?$>ֳrsx |=Wͻ!d37tS\94夸w*Z#r\NoN[&WF>j:zONi W* ҷᒇ'Nm;'_:l|]pkA<>2[%Om7΍o.:(*'\tK*T[wSJJ;ᝧ^'ͰKFgߓq陧ı2/aw99ncJ_hzN|ɤJGmZ[8j\5m6|g|ݫ w$v8^WDoHv|d^[fK_7k;N:_Xoɏu䞟 +s.~Ou1DM8'{;MNJth毎ct*պ_pg5<ӫ>qYs//ic[:XTsߚ'ls_  q%4a[Mi}kA=oTG} kE]g6qIG{~mBqxglyK=7^q`"<Z~BsmkӪsvGfjg}ؓם*X[zH3F]:.{u ={wb|iWĩl|EA}ZAӚk8kU6w\jE_2h#ءç_[?S[1>v鎇ĸ|̄iѣs.<+fqiL]Jlf[3&ɏN1aڸƤb)1qڄ;/c'y\ tЧ^RF?$_lXd.lܘ]C%`B֒'7_љ:0q ;p[0l#6rh[/0oƅCIph\UGu76:Y[k -+>I1xb||ӆK ǵ.[$9^mq& 3kO`u8F,]͸bSg.>{B\&}kyY7e-|xHhhFOc0zwx&3։gnjqofٶk]k}|G6/Mdw$]='WCfl}1bed]beů7~ħwuuĘA{+e ȻsL^uZωvY7}hPJto9.Yr}t0)TOh>{lҳH;ԄqCs$Y·ι>7A'PL/GQZޝ?8WuK>uz{oǶԓ&7ϗ~y{kcz~c{Tc+bk0|\K%}̱XN;|W&ꮟύ7}+άsm}9VUnyrm; )(nqt1\cǏcׯzF73fߺDm)֥=Oi;0;'qZj;7icOwn/f0:9m\ֹ>Sj=-sF͘K[/?r-}7A%yNFxn[:b87'Cȑ\#ǽfƤ]csK3{9w뜪lwyG fά|wq{~P"69瀐̳[?zil<;vjhˍb^ٷ<=ߗwnȆ{+̈(vr ;^ӌm\ Nǣ ;NY;̌Yo뮱n'%KcQ3l{Vmd q1#mgmQ X7̽'S⒇Z7CO.PW/X3USq9>`I6v)>p¡bz þ\'ډ 1On؉ܾևcC_l:117=}mCԆ1K1gѕ9*GVB1xixm1$r/Fz'V ^nVq"!Nj^*,Y_jfhq'9;Koc;.||8yc%κdN~WbC` '2']}]揝f0Us1.~igǟQOa3S.g Ugo]?:q1Wsv\r]q+;s1=ߌu8c_0yṫ>&{⺟;Ҋ~`/UTzyG71֓[>͙k(#? ׵c G~Wq⺫wfd:͉θ<AUrި]uo~iRS~|K7?\0{V4grg6cbc}mY>;.h_mUA8]q_x)PXWƉǞ߰gy7v]ڤ#?g]<iCrjCNжU-ҮeGf},~믏e|o * g^^vo֑Wy9hźTU\}ǂfΎ1X~}AR<[U>`c͉οqgZ{6sTu6X\zq 7o/0~=и%u3>rm]*/c[_~,qA11ytl];96}_Cg|E1wYڐ6?2GX9/8P[Ȫ9!8_*!GNaaNAzk}1)/}l!~oX$EviK#0_\AX-YuJJO -\_5Nϋs]WMc̶s>2U$b@ ֒x>3$>6X m}iK_mb kW~8>ȩ%㶦m؃'6[֐Xa cD[lڡWWrbA9&m)nGǤ87~{;S?PxqiQ?ϘkO,ʸ?Xo tO]9<^q VYxkq r.Ow+{'4*WMXwVoگlb4cM2>|q;O5Ob1_ޓ/G[tYfGm>7LZ}Fr ╻9is-?8i؝S.9"R4շys]Kӈx5ujb5̱OƿWOs&@uGc;ǡj#﮲YWqhycu{V|:@h]rcRW#qNJriE;-_2ȯʬ>cmmcZ[󞊏Gp׼8kz41'`Jn۷VsE\:,qq^V~lW%W吜0Ce#-XYWP]6?.-+9sb+=ɓϖM:5hx}z0P3qطs_x[J_c/zHoX+"4_u@?>uc{toYjb}c? i[,`G uA^+s6?8}gL,RЪp,$灜I6ton=6Ȼj ؔ˖`~oc0?YϦX =D9==yg=xe3B~a=}ǀ1+]rm}quͷ.|ꮘ7al^׃OS UӋdOxꟈﴵT8`zU1G"pzغ_<6jጋv~6؛;r}927Uƃ8r0 $niK>y6:8X!džDKƇw~A1OX/-~{Ī8u%?s>kUb|Gs`bӠzIpٯWhHס=Hߚy}/OO7>ێ#bBƝ;!.D Tmy(ρ1bS,c6iROqYr0ws"r|o݆: @ M8zUXzx>yC|fxq{_Gk\4$~*51w#*q5`W;]aj'qSW a4m8PqSNE:sQW҂x1'D*hŮZ Ż h0&q S.GVuz{q ^k 34TG|˿7Z?O!ޱus"uLن6̉{o뜬H[zl99ÜVn7*;^ʑ v+j~|ӌi#/wTrmY7?\?IڀCWxq<8GsfoS^ߴ{?m!eݐF󔫷'&\|uƱo^Uau'G$u:Xl?;m,L KZ ЋSK+o7U6iۑw`bJ~g9sL&n>aq,}e?7u?VWcwJ(_H-ܖq,v|iq,ey1|%6`[hcY̥:ñumÎXe:/{uҾ$.͘K2řy6l/Yޥ e3Dwk0w})w1v|f\9pػaEQځ W_s)T悍OF؀B..X8j_i\r`:k7>c9mҎXG}䪍>p?u:C < 7fe\=K3}׷([wNw߸]4^0hoL2.\g/h7Na;[7SߎdX]]{QCܥ~#-k1p >mm+aoNU[ |'[ &6[껙?~]wIozܱs<Nl8yy'q?̬/{6#+>zY7`B vjPV9>ɭvޕ+~L]HK+>bXl]׭Gkoŭw]`;Z:xz|-beb֠oYXh/N`"m\b9o}M^65'w'`|g4uN^hm9Tբ>bx3k t>C 36}W`SR5|r yɠ 9aMy_98h9zŮ_ [ܾ2ʆX 8~?=߬{Ίz&HrtNj%wNm{?~8ꌷ_T}O6 `m̹uѹ>>T;p\[1ѧ 7hf09z8p rs ,<чp;}y'-#_Tpg{ e}Sߗ]>#֤|*ɃȼB@Դ-W}]xC{!>\fM!Mlqρvڀ 0l\ӇSq/6\_Ǡ NEO5EFy1?8r=BIB/sNۦk,kC߶zu V6-=}cW+>۞?37 2EqT4Ǫ$?֙i5j9μj^X8b`.Vd Dߘ[n-Ά}S5\Xx){]Po#>{{M &Q2VK|p=7V5fW]L?1k>ŠUX x~Y m=jkU,s˹SӚ[ʯ`Y0h3N֢aS54N1( TcP[~ƬEzIgջc[xʽN[tWsmܚ0`p ¡:oW}X[uwћukZ/Ν@j7ĿzZJ{`yxÇd:9qmgTki?zNZ{m\k ^֜2/K.%z1hcOc`|y;v =Ǟ;gFy l $1ы[Ke}-7/6 w\8X!-'xX웫>ߜJ{Fy!Sn?q[W#-Ħa-F{G_ơ|)7>'w|]ruI_>}!@,…Wc׸eP@IDAT?|gύwl1kyZ5ÝHAgnbYKvH\`|9bysD*6'!xܸߏqx7Չ끭s94Cpk௭kLPAkz5u+]Eoiu5/FVw~aێW8/_gj NO]تQff|66uôޱM薟5yZ{aטUKo}}O͉i8Ah]pƅ?;aAԜvbLK_ʁ3`\=VȑG;o<?<SÆ\SB`cN'y9@>A\?}TG5C?kT+!ז9氵6hdJM}"b >_\[~x8W 5wb=5Ӹz'o;ԥظ1ffOύsXckʭ#q]N[}K_dczrg`` kC{_쏾Xgd֓9n<ڐ}U5Ci#Ƽ)_r9#FV=!99@h81rz89_FG\><rlV}q6!)@8kK,yb^?m E/8//"H# g?ֺ'/3!6m+Wk^i *,k+m [3eXM99"+󲏬'Ƅq]{c+;I|tȘsEo~|Ƌ|qwo ` b|{}mi[Y|_/o̱cA>=&V8aǺaޔ9?{5q KOwK7ߌ2?X5mM!䶭a&PŁ-1v]RO@^{n\cְ_!\<_E__~+q }oX&RqWZMGy9ߏ#wVdȭf%JƘ571{6kڤCggѻ\IݶX#n^Rxbͽ' 4I=(]5[+2f<1)}k0#vVēsL8_.; |'^cgc?0ATwyzdms=p}VSEßĴkVcD95^W^3>o VF^TOy̟ ȸ0#G8 HAXee¸o@ 0!CAJ,L\t ;j#/\MuK@!r22y"8C+hڦk؝X=< 4}1vL.|򢏻|y3Iȋ̻#oVy6.o(/rfBP,@f J1=F.f;mȓ}YSW>]AmK]F|se^8QB^r1|U]@v%FOb<B Cu9UliKpl6#)te,ģ-.29­S+e\41fmo0޹Uc$,w X]n]?ʻbJӗ&MӤ:+B|kfy/WjI!q؎kV"q萫u@..m6c6 +*3bMwf\q ?cq[)kr*f^Rba3>a|=&)3'plB? m9q[9ayI{l{Z;?;&fa#n ;G(6c~ru[4]<[8 uo{^ p(Lnt̛b-_6փy@oWNskfC4=^sU|5(-y6sGloǗ\sޠ9GoUp+ϵr݁'v[ԁ!rЯ薯Ğ7N 䬭ϽUZsl}hjk s#'(<59v&wk.1b׃sm]X[?˵u1=kտYcs~.Ɯ`#qWg76CXHg4fG.?k75-CdbqѮBGO]֝9>o/;zgV%Ospp yԵemԑz'+zH;<c_0Io{,cW >c5Dn~p-Dz鏍1c<r <৭Fi!C"O8ع11.@.``)99FsWm20r8:ǬF 揾Qt۶ݶ0g5 "m<-7<x/OD@.Ko1qIAi3@p\LX-;b^v.;ǒ[ 3|& __^'/rۼ/;y=+yPp'  ڙwړ|cLË1;oɅ w2|9_]؛u廃˫K̓ΘG\s.XR3r.ss/}ekq w[ Gn}ѳC֓2dž9uG,rK:ǚ([ K87> A 1  rDG .=A]yֵ@؁\ǎ>d];P]J\ x*/DbOǗE3C}-$d9B~>c.so ~ Co\?s܆qƅ_m;3X\T\~\GZ_ m}UֿSQGDP=?0]oϺ{?۳ }fW`??}޶'/si{&_˝s#qr|uNKZwݮ֕捿}pwo\#7͘>{s͉;ǿ8M[|z8PB}D=Hv>9AsV a`qLuGwG67~ ,{ 9kUz'W|XyR̸~3+Ϟ5׽;w0ﹷYwĿGMpPA#9N밻F)sG{lLyMO6N A{ן~PbMzbBKS|m{oy=k됼iLr [W5Nko _봦.c-^3sm=|}kmu~M8f킬D:SDSkވuE:$ڤ6 YVx[L_y`u8{{9q)oiiӟ>\7;?C}1>m7;23. Tc<U7{x >bߍx8o66Zv&'|ubK=?3bqUk}O#FKkA=dؗ;M?甾5z;7VupiCtn1??j/Xب}|9VW\=U7)`tmtj:!ɷ1o )Ӝ̫e8+ĤUcǦ3 trc, o;V k`Iq[yǁ\H/ \2 k?f__z ֍+G.38.Mm ok$u&Ws|mͻcyoG{վS.C݊qvS[|} oި990>!#k 9ܕk}q=>h|7O5t/p*^ښڷ$b䘾dwչH<3B1yWW]0xGU5޴A3Ik ݄<bT-b̵uQ-?E:q)ǫOI 5} ֚7X[Soj_i#vNjm 4 MY'}K|/OF56lExbmK#'}yqF~>ڢ{\\Cn~-rEVaPڀ qI|a,dO8E|l$A$0+}ho-Gɇ"YS6E/΋ey;`w3|Ǹ'/7h(?ڑr8?-ߑ_̗QgXo':hB"?ɐ ֜~iW+1y'/u審>Ɠ\GoY󝿴U9zp9 7O9 C=6Z.42ct\6}l!->fM6#1moў$'ccDFwy*aہ@ƃEo{ۢƓ`=4+˻kz^..e @0X+*yc $א7◨1Ek5# bP(wDl^w?sfvv\gv{9QWenmCޗ-g`ŤOOWt򊰣\o3s\iSa:!wnۙθ&p=OQ/h)Xy‰Zߞtc[_st.Mc_qϏn3:uݑ' o Q~M)=9X [%Ȇ?Y@_yzȆQ}MPԘy'U'Wݾ''8gƠ\^(ɐiLxs'[Ho'ɋs)wPh=@.[cǐ:2lyn_&|n#}5;r=9b0.aכ5Ǚcm"<$E']vH?|!\2oxAYxBwuӊ-MWԭrbD٦rI|r<mNr }zވof x',խcʊ5~..|!eݺ֛c/׭oĽ8ny {NZoG_7q^nSَv=QMo-yB' p`u Fgpij1Ir mE~HMW/>*(ubҪFu14*꣱jV4W1MODԎw eQ]Џr:8zRk@YlCuy'Le.Ҁ|l\ARzpp:/ _;:2y!z< \σ~9å `[8yy As?tlcrqq(o~Xotg_mӠ?|*eJ篨9Gegx5qG6y s7_z^&G6Ol '8Z8/W/}r.߳t֏߷s97;]rɛhu8Ĺ ==:fWO6@NoPՎ*f\l姣F93.<"k/}MWf:@AY_%oG>ֽTuuKaNW˥Δ'JgԮuTy劔 _⢿(wF_@|We/?lL]m:)7?Гzqvz1\q\xx|:v[_?xr [f^.@^.NggWzm^q7N7-a5A/d`9IǶEqg~yHA\p?\З<ַP ؘAH؅gQEXjՙU4s&۬ WBZr ӷݶP] ֓NÎ[?XA%%3[>v=v|l/ D~3cZVh7Gt6NH4xiL )VtըޱI`{@l%'ҕ&rƖH6mnQkWOm:MmEO_wѩ_6V6kآ@-ˋW-FtvO>MTkm vyGeG%[Y#`]肽\s0~]W .? \˅a% &|II?q΋uHL6ˎ4i<ޢI?__ wqp ?ݯO(76JJ_uh3AN}ɗ4y H5|w\ =O CЏx}ڐ kT~E8Nofb!X qⴐ"_ n+Ŀ_bqN'8#7Ż~se˦fu?'~''r'B.&|=Lm;}||'H9雵{[]G51cؒDuoEmt>ψU[uky%ɯmxh?Zw>']8qS0y! } N3 yu' 6۝ӈ y8OAO:=[uoGwڥ2?S߰p]GvI<Bl< !F^%} \D+üQ[LpkѢNq 1+ú9mAP6uwF%zj  FO:a לco{q:bNݱN 4RxFFk&IGev@W0}bgẘdoч O E+Nص=u"r8>yKZzJMdʏ5|Id'Qp推iW&l<:.P\ᬿɣ~1/3HF. q._04G /a`?zx?t':ΠO}OَD k^XFй'?wtL )Û㪯N^xԒv:$-\6cCgrf <%{KɆM<oxM#181\ҜdwxpqfS>(-ߎ}_~8އ>s^儾G?/ /SfgCː `!<Gl/hHL毛rܛ/^sǧ/ݻ;IW(۟NLE^zIʜ϶pN~_5 ML3+ſcR]x⇎5 Yƣ89 h,-t&G7G>.~l9 -2r3/GqqσqLr;4i(};IwH<:_i2nV؂ *"|Ѻu'{ة:$CW@ԕ-GtEtD xz~oߩE#]]+Z|LvE&~*I!'CA%+wƭmvtUgD˔vVOvcRgNٸ-6nkΎE˳A&;0i+cE`Glg*^.~ A&q(*Qfݱskx* vCGN nZ:\?`yxxЃK.L.[/MGhGhatyL835 yޣ'8 [p͗8։0@Qȯ,o%nsݷǏφn)曕~H>9ؗw=9zKـ1f\i}/0|ͯፓv^ueq {Eϊ|xxn}-vo?pK\̸dC,}٥qkepg>g֟o팎?x?o4oϿzwzrIæyެm)ׁ|9+yY!|P|'O"K? Ln&vёrOS\?[85>A(߫u}KJʪvf9/Yu%3nG8]hCqo|hR?z&3dRwb{&&o*^uk[i.gjCʋqp1iaxә/9ȗkZ庺9OrTegx(p'w[GhtΕ; qSn]hL"Mǟ]}ݸzxr,xT.zi|G \}̓L< ] .B&ifzex=F8?=/ه`/l4o?moq-򌟏ϗ54g.?['@f~qn `0@2zY ŦGO5rlV*7-+u'G{ Z,k36?#6K\+]Kx)8&{ޢ^j]]բV_ M*8 Jޫ폶ݱuئSS8'^r+ ȳ\aCxHlovpT[ܠ(\g\p E:~?8Ow~8X7gAW'|l˲u$~ZAz>zxrN,FּSoz㷍aR{ɳmJ<@?!np%}s+X5}Lǟ4Vˠ渾4u#lYyп ;Lr5֥1i F(n?ISsMr[;͋>X髨eO^ߞXwL`gVo>qƅ3;}u?b+Ur2D|mN}sڿlMsj82.S}b>|?~oNk?tNIW4WEםz9S6qsQv}[86mLcu7]mW}Q?_'G?se' Igj/I yQ:.Zj|?/k/?og!e:'uM;іyy8hTDdzqt/+k-ҌИzeuH'9Scr{٧߾}5ΏyQ+M)~ԐQAW)+L%qVǭ^%\"mԠw6Nh9;B{He8'^h_uvȂ8[uUOl77zm,7s.}X}g|kiK#w*`e?;e=9xp~})@4qy?|SvYahfgF\9lpӒ<9w}JziCfQh3'6|Sg___u[bc{K,Y>siktk{xYG1 jhDl!Xݙ*tbrB@ b^5l!7E_u}e0_] ]'ںDl޼%e^'ufˮ| m>i4W4 >%.i<a< ?n:#đd^CaӺc]|e"x0o6?dkKc\8CL? yUkӁV}ζixQu8f|9#<эߖ jeh6(WkK/$3g{_Y_'7=#'pjv㨅Gh\=k:P/K]˵]ϖK/wSE}P ;Ί—0?ܹo~xs_yZ4)e.N}_7&>ѿ݌b~B=]حOf73p>.l:>bf'jsڡ>}Ym~y}_Ƹ- 6~ h6,y؀g])~އM ZaΝhHYYoMK^tE&z+qA~C r 7tyc J# H4|^{ܹᇣ5.֨Gbc'ٱ#J1//ѧWN @686øT~]?q:ֆ5oN4˫SAm[Y NG2ZXL뗔*녟4fݭKmm=zSl`jlyY?~\%;wFw_O,_8vKcU~#?`\`3]s6$ IG`Iæ(xAk> aƵ2M/C< gwLǣ3Y.xyg><`Y ;lm+v=8Z'} 0`;<C<`}pc< ȓ' ~FxyC`a\wj7<<,˶ƼzZ?p-Ӻڞ7nN Za+W9u1^S vFƆEkS}i[ؠ2=<1Ƽʮޫw\VA^uq__>6Ŧ bۣ2oSmEx_^v>N6dp9=T>]4釶g^_c~ >Y-ڿN㯻:ݾ&ˆFp[nՠbf;76n]]{2&:Q\ |A:]IYxRap)$˵n\[= A]stZ9k֬إHvnGGԷC'l)_u5saaZ츘Z>y 8uj'nZҩΛ7/ɇccKt=ޤ_CMscebՒ#CݻӮ:7@'~Vи\<҉ƓgsB鸖粃qM~˳|Hsg]X?'ݺS&В]:bӑ!@ctF'cz쒷%~x.ø.xr40r-z~8yzx;wD르EEMqć2EO6tYaoe~Won"u}W;3rQ'H՗P-/QCRyw|FTßR"oů5{ sgv~?vh5cy%tʺh{uPwt0 se3℀I E} t'r}I;A{ FisOx o>#۶m[]#|Ho0`;A~/x`G.:-/k vD7 tB\z3)r\i!Y/g[(0>W?z'0::x+_ .ч9'n;<˱#ϡPj 8e0 ȃ# zB> F]d"Ig˓>ĵMo0rY,.tLAöy@,r!gC?lH^ [Ȋ\rm+3GG ?xJ <@G[yCpmO#n]MY$8c|y|cy\<(yY 0po > ?xxZ\pr38ЃO:i?X/n8 }rI~]@?|X>qlv!<î:l<3x. {kz?mg"]`vGWp~9u:-q]ӖM%GH e!hO~-׵lճWůMNʶT.ԗmrOn9>YM>/o;Ƈn4Z_2r%N'/vݦm|9}!0ퟲAnC( e}*1:2 НHFL^L]Аf|GE( a! O!:" @iРI8<- `[t%b~\tu p͛4?i|L%s9}*^ۓx/[;ϖy?ƁrLK?@x*-||kMӡ# _$홟!y~j^AO@}US;E!ݖw4y>h3g3(Cm?k}__G9L_Mq[NM״ONDN|Zw /$׮xNC:f88q<.4'a|:ۊx>֕8y\ ؕthQ#-~ XwNa= ?ʖ8v@ϼqG?¶5 ..glx\7=<:o0-i|P7H>L [>:Xu M3hG..<?h[lM?qESԻg}ⶅ}x_c<ېrK3%?.m~xxc.ВF8øZuP?3)utg&q) w6 R@S „4:6.`YÃ4\ݑAc ~c-<Iw^{‹x?|e@.zp۸ckIOHy/y`d!~~EuC~?usy K؜zGt ?ܗ .qpe/ayL– ;/dcǀ `0: HCxpch荇 O\?I' ߕy-tię?r7Bkg|pAatxxёI~ y.vck΍~Ҧ<?`@oo<"C" K] 7et=$- ]!82" qn<y4yW'x|]~3-aˆnyy.艃'I:hEXHp[/o!ӈsq,ln<Ÿ:okӁC:}\c?YEn8a+;aSM?`/zx_6F!R5W6WHOc\ẑOaI?i'<ɀ>i4xyYOx8eK:! }͛IeZ'p'|he\>u˄<3hAG:k}Cg|xAOuy,DžyHY<Aȧ&:zg:˴:^Niƃ Hh||~q/|8N#it-`cǃ4wp?`u4 :l'˷?ƃ|~tyF4@i]c\chEqHﳜNܷH_O"^[7qo?4ƁqB/iiIÏ|Nњ`LF4Cã/uU*&q4.jׂFU\(W>jg<;S|ϾLЫ͓{-ki;}<<=tT]KmѢzNiФ 6~Y<1:8ݻ3}ʨTSԚsMB;cn?ɨ!Whљ!600x,ZmbQj*63;ɟFVJLʏϡ(d ІyN.x:On/y~>3An^~6\?(GۓۢM 5>--QMrѡґۛ4о(w&6y*8cHC~mW~7J]lQ}^ 7#_茪Ƕ\MfoY})gϊ/}g.Z WξR|冻cGgOl~rI-ȫnѾX?GiR$&ٓ U`LXonnhS׮Yq8>t [~t9fd{w|cgW_iR_#tNKѤ/0⯿;NcÃ1͍QoFUӜj}HM Ā'}>0ґzgT ͌kbPpQZ!]2b8]*+&NmϾBOY<;C[{(zv=ñw bn."cs;Akוк-+ci`_5k0몢 YQ:aMWץ1 3\J DO-e )o\dlH&=by#*c`D%_,pGt›e3[ k),>[eFYOXpt6Wѧ 66=Cv|Q]j*JQ [KydѮ+[lI۷2vR9WQ"=E9uxqvPw\NON{ ƦW4'E %[A͋zRI/#\{ P,ygqN߇FK:Q5E@{QNN&E/ʋ}mo- J <ŸN !MІt-kFݑX8/Р"dWj$cu[>󹨝7gNx؅b $Qw?ĘwޤiDrT|DPnEasF5h%`㶎S;UvXEYD4?b}V!tVGYvѳr}U,Nà:E2TZ:k1 C:Qoև~twvw4N>F_6ӇJn}ZQ9hrwpsl9?Dr P$?_r c҇fM rz7=ztN@bƇv|7Oe Ud. \󚴰艚h>F{꼯[Ug'bFŨMF<@:kN<#Hf3f2d`ΉaWΙ+1 LPd Rv--ͱ<3ʞzKqa:<!>SbR%@vrY0'~,XԱmB?>(ZlsR$P;2n0p#&%Yg:y3:''DZm!yzuSvjЫ1=-X~K_J k6;s /0?ݮZ͆urWύYsѴ\}k'L'#[3qW)cZ|Vh;#:;gƘ(:US|ѣ:B0^.ѢQG^pYX`-"0M~F0с]}"aAi!R}tW]Uf 8ۆL3ѫS nKƃ}"h]!ku OɳmE؈¡Xs:l0cǛRr35Rfږm) Te0ԎzaDgTT=X˙kB0{ΟvvGf T wi9QӮ?_ Md i:{q**zhFաc+^-ܕ-4(r[E" kh L;?#S~8ٔ=;D:}cNlL3Z? ϻ؝Hd > 5Z(w:JdŦII/3u:cAx(~R6֭4wF4J&i&LD3i]9ӻv~zEgYFiz9Mb3 r*Lxi4:M«7&u֧!]-NІx=_Q_3;,/Vy$ynA﬌-LCԫNK1K툛lGJz7j==*?]Uq"s#bIoix,[vh7lTzlt0]KEUT5wH?_-=Yie/-{zU}Yc2rtNF$3qO%aKl1&0Ϡ?歂Jp.JD:햋t oRLacQͶ1ڤbaHWݭGt2=ʇ&KXAaiSyB԰{~PiƎyzGD?yм0 :a5>&w8VnXP]X ˇW#8;^KeXrAj;vjA-:4f0Vu(>\aǼb}KVI]fiS{LlN#=_eLAy ٲSR|yqe=m`p9v-RgدEKE#. NRO:t=J'n Ү4We'*8A~ i]$̾gXy|w{g~4!d`Xy+֢B`Twt`g略XtʂIrqzMIT#_,6=Sh[kԳC*b]N٩ Rܿik[PѤT :\K\-cUz!-_>,4ciLNGnέ>.ŏĬQӤ6,*F4XkӪ]0kG}ޱK>7^ MOb殴83d9IА3Ӱ1N*qNt&IزC&gH նǢ6[(Zkba3M)w~!:h=ãeVCvV4mW5u14w~ 4MQR|F:=jQ߱-4nYuJR~cʼnhLM]&]7_5ӎwGpvսh]8*a%f]:Xwkѫ!jvD96Yu,_Inv=ӳ]sXuw>6=9p{ߑ13Yf|<3rm2#Ի@m(>{OIm,qRl&"zI*]O, A6xqyHj!=5`ju*-*OzBu~_L'?c׾6l,D7|8ڦ//]WZ&- - o}[U_A+RwqiyNӪ_l@[^8T*o[}շB+ߨhv># kJtcu?=#wݡOk #f e^E&e1aa稤>^wG؍Цʐfy3ӎ bQšN٩`E8jaPɘtmH/X^Mzu2~Z1u*-9(ѣA$S fŸ>ԂlvJxIN>PN99wgMiĂNY>Qô-O^>yҢLHcdiFu_`mB3#.ETfL,қuUF W5$F`C>sC2/LE޳W?ޱsGIGWgG5= >TMrmGjsBԭY&еeYh^+:M䞓7)W6h|MZiznZN Cv}wͪ٭:BAS.ւOΏ9 |:0^ QxhiPGg-:1i6dovtcLWs6~GfZwI}6ItZ[-ݽzWI0҇pXjQtV5`uv7\Uwkި7"L,^A&ӷrIHյƧvv%.{rϴ4ޓ>D.8/vƮF>~N2?5Z=QFά+L6`[II]<bd',pK@m8^Ƅ7u|| r(3| /1 6@|sȉ^zmEkɟvttH«hҗcjیE}s?+"U;#Q;Q&zǴL1+i0 4.C%aI_ǐ&7fNO7cQlucȮphŋ^蒨vD[RPN_֮TK' DKޙNzpr1j;)Bب(OueUf]ʀ6)& ӓd24&Y0,ib}属)A }DMEiB"[re߫UD DR2L`Z);UzAQ֙Cf#8l`9exH;8; ?[ ?|}wigp 7i1?-9җQ2???ӱ|4<6uw$ Ib P[<ݦ֩N c:ug:_Kwٶ;Gz7fNCN0tnyT?p?^we:UpU1w6^ܨ@5iaQSI ԫEW~p]Q3E:ѵni\ݰQ*O-/-&.I;lSKV]mևk[ߪmkДhOG -iWw/9>t롏j{C+JLwOjݺEQsx`4e!\BhQuڑ+QuG)Jnlk>gcwRM*hzĩV?'Dl05/I^KDn[ns*:ukj㴓tcBXrI 'V7njN2M ꩼ0!j@-4o0,e .}at- D@t5XtL~;[׃-ch0a]wZ T Cz >ϥu]|E?NjMhnT${|ʉFJ{НndSO=5]5}ߜO|"-FIok˽K ٖ_771A_rzi;֬?mecuʛlUyh-?JUwJm bTSCv XA<'@Чݟ>5@װ˯Z=iBj@!Mb֖̊h6h bA>4JcQ#P(>޽c:!]9' soj]MA8_0'RN EƂp~@Tuj@ԗTyh^SUu}if$[~|:AUjAWU*:GHPV ܍.Oai+~zS'5Z蚉Nww@Qt-T?+4qo/:{Ub޽R0,'Yf2?eO _D'T2L{W2)ذu~P:PzTڼپcGD5(jN,1MB'rҭmNE,䭲Bn4+eVt#8vԇu 3~bv-o9:MENKjdՙV]V5#@k6Eب4GbE7_J?s7VFp҆&zOڬY[ZhL HtqL> fC^?Q_vagc'@HQ]eI8v;''Ϝ3df2ɇiΙO9D3^rd)Rɶ,Y2/ %RAIl 4z߯[o/h$\]jygcF-&00Χ2 d<ʤV⹂Q ޝ{LG? 6w8\{o&| bw`H&4]E0tT"#ӗ`B7$*m } aq$QAJ=à) eR %Sf/cOFn }\pVy&w=ׅ0 M`xb#ݐG+ӻ{)פcǹ.tn`̻ d^WG1N%NWF[J a;ΩRV]nE2U2 Zɟ<7Z߶oOhkY&:(K _o TaV[ŤeMA2>ifkh\by@P8"Lp{e(C`_ s%i$lr15_' }_φ+W4*7p@"줒}xŶ|axXdApGrɫt'?υ ]bwG$ܫo>+{bHi D~$*T6"E›PK=t#:Xf|,o=6\D%s?)Al;d8$<ܑ5;T,ƻ@MA|*-4:ib %D2D֥HNi OqI=O=2,A'^}*~5l^[đhE=/zR-=)Ƴ}z8q*{Ÿl̡nHҶ~ JC4g6_~5\;'q6~HXH=y:E?*k5, ෾H}62#8 U 4q;o(T<X(!-w8w FW@[oEvy=H)c ń4|Lhv) cȦ`R[|{*3/|3L^ w{&r]E Q!`%6;+he@g4]""Q /v̻I vA(1ʀ5T$[AH0:tBv] DXcI;a""hV7R>Sng(1#qE /@øޕիW Wm zAn D7xi %j?%!n[.jH헒vӞJq>*UN)R>:2X؀UEDߨfԞ]pAf8s(:r̍ۍ4>;k%W Iɢ`DkuU@'*юtF2iW?fcwn< b >hd# hdNKpGT!Q[[*3KX66p @ N8*%X:e$n#LyX5+c:rZUTe-s^fLgo!6QSz$3^?o6J֮vah໵ߨ_wհ /sx\7*r2GҊWU6ZU-9klQhi0}x0ϒ/DuY.!8p ̧eyCŶi~z5"6}%RvG[ˌ5%Kh>-8ؗi_^'^f[pBԂ_ BG$kaBq2+88Qnx^a#'v V;"qΑ6'.v]0:~ߡdمbo"֯Vkb̠P"l*X(Ek_6o2}:P;۶ս\bd0ٔ)!RfbYOh*LW| j%8*ź3 N6X;*#TКƐ1S&&f+ؾ1%ut/I '<EЯūBkعhP\ОRd+2 7}Ca ~$3JA#¢] )ɂ58KLŮgB",b@oբ^No5@1]qQCB1 WøQlPb}.v|-D>SI B'*8]]A4a$އrz8XhzBwS>Olmf LWUh 6}}G~#we޹a;L`GP߁7wuD\߮+ls]$|I"#)9gUd͚Rܲ >WI(vw2Y0=jWd]s k__=u"*hvLY_[!!Z0)Cl6UzRg0[0{i{GCs$>PA)lxI_ :720|b7X_Ny;dշ ~C W"dth'`2K}?zL)C@`D)Y *JU]A6H: !X{ $rbyp"mr^b$Y}&ʨs. H'`PJmGjuZb&Ԯ o I o];)klefG 4j-b=)C›7aulSź8xRvy޾Z#h;RWpNe?c>9בaKw/:Qr*k@'1DǺ%ӫֺ<6Q}ЩipjYҎpZ'#.$tdd4{:ɖcKپuNo-YW?::Ôʫ!uOJ?1cضgHu2h`۪Tz7qűj@ұ.ñv0'dh ΕNpOH`L(oS(2/L cq cҹ<FfC'CSZ1gf=ҴL VeGdx={:]Gc7} T lfN9 hXeFk:\.A/LYv/3;]g jOЪh@JSϵi `3 ;M#o v3K=s1d0: p 9v ^E:΁<j wd {5,` Ikݲ=|&M;nN<v05O-0( O>0*O=mNu<&cJ gυH NA~||oZ@T}JծȭWMgT󤾞mū-sSa|a ic'"Qvͤ +x~?aV=6օVl>@4潡L$>mٱ0J: ÷.tC@ԛ>#x/8]0 Qg*/h 8j0Jpj]`-y6nPq$a|ߘrG cU$?9fJ!^AMC: sqr+22NʗuZg{e~%aZkoL :U+:cvt xfn|;h]-h hYK &XCW /B't%NtW~;"& tٶ_9VB.IN *zԆ \;Dag_/ؗl~z2]ArPG Z*]&?> x q]AKqqf!QLI5f *Wo 8^‹%=DPĺӐ ~K:g~2JȮ,YX eȺTX-}vu4;Av ⎤_ھp~v*9j7A h6/%l ǀmmۡ#u"쁹A c%w^X0,2Gv[F0 QiIuiGD28a:~_\fpclD FH*&F I I~$tʟ] QX{/ K8 G IlCIlVub WH$cPm5=@?AζSA:GI7h]3?9{@5Q. ߧom4U/nbW+x:!)e '16#ec9\2O!$e ylhc\ۯg|syt~F@%d0d y>{>j+yw4/_7/\6ȳ[; v>/]OM1.Abnxxc+!u} Ql~zx^%!U&xoCjAu❡}ءEoU撷S@dOꏋqη!CS\xgѹ ]>@ 8&@=Jb8ɧj_&ԍq^/2x<\'p 1$ VCp@M'2QľDX@]jyͷ|< @,=Љ0NO3 1ӎO6WGdF_~&tN\ .Fwg>GJ{P(A4 MݶGu}tn5@sɹ-c]ُ`l)%?{`q@XzR׊5REUf,aaYTcl="F%:V!Gx+":BN H|s, 7,Vt2Ge(#_XU`[H q} %J ih#3\.#ۮbcg/sTMowk'GGTh9u!D2C$isWkh\B=n26j ̫Rd$֚Hs"ORL{Ց*ʌ^u8hhBnt c;8 hŬq& NiiB$>abk` y-%-H[vuZf*Qof+Q mՋ=}!B j=*z1-**;?CBw/p shSx1y8Z`fnEJ*a. Rfz;Np>)A?i)f0^"=t)1Өͱ@_U*y1{;k/H66 viFlZbܪcp ܻPl%qoxmZ'%ўy6dSR7(Ohuz+QA>ڸ}N _YiYHccl<' 7~%n1w:سkuumߪj:4%>q4VX?*N+*YW̼uoI/aSeԘ'WXZ`v;yaSJ>rw]TiWF*Àk 0BshQ$|#z$lcR*P #}\QpCG@'!p! %{ߘ.1h+o@=켰b׎evc'Nv&J/=0l=) |'N`Nl!.p:g|p8p/;n)Q% A`mCz(>zh^HwGZTiy$2$Z!~j:"|:yTZUqC\yrT],۽3Krdy$V ҕNGUH혬) -ASgQ|RSwFFȵ䮟n&;/bڱ=ĚPR1l{.prt$v!ٜU'%̲I1e!K5g{Jh]AR QeV$7U =F ռ9j܌<1Ha3aX=$᏿=1e0* _ xUUݎ)+[4ƨ*K! ! FC &:bLƇNlKJJַ ;ӏ>a8t=ŷ}`xvB\Te abΤv8a<=Jy*;ATlYiPxve4DP}O42Ox ~<*-xv}.!&w7fo4aMC[\ 0]>16zBh,!'qb,`{D|De^z6ϯEfG'1齿 Lo& b K y3fHRܩ]i2]cVw2OW-3E:O=]FXǸ#e>Ng0}?P@|Ye0 /iCb?۟nyx^l BA4ls`NȋA40t6ݮ2prSM=(UؠXEݺ0 aY$?m381m_*go퓅o|+̾v!,yF譾kwkfYO~,T޳=Y4`>pOW@ 3%R7pFꟸ+:˳1qpV ‰SZ{.~f0GɌ lboE!Yw)c[qw*U8E i@]x0! 21C ˂bTqug42Nt&fBNepbU] vmw`,°q{ƄT4 Ԓ*Z]P[g 9xSlp˶8>|aZһE{bQ/%'ϵ}TcAѸT/qtbToݞX[GmnGڔJ \ mZJCVZ}})vUT[C9dd u$N64"8JmUنߧg>uwI:koֳ2Cu,ZQSHvt1[JqJBPF"vؠۘz֪Ud ix"^{u&%ղ Q5 J:Rˠ.V B;- 3,~ {I>@p]I@yhc7Z^yXE7y_P~j$ƃIL`? 9,a{e~+M/Z ߃˾3'6'0=Do;vTҚ$%kҼ]sc~qvC*nr< utPE[6a3g.\ɸKbȇ7Bݏ_qih7 p[O!c~;Qa)eeL :J(E:bM*|w~m "l_we;Vd?֢N;9#'Ď-NyTPwb|(5@휋VL6xOT< -UIqv;onR[}e!Twʹ9=#'/ m5`<ĿJo~&|K OR+oITY/}}~Gƀϟ C㌡ Tym0'䏉oʜ>raw;C̉8wCov,rJbulP ] W~ %$א<@[Bύ k%VoY&8+!AfF(fI6{NcƳ&p6Q2\\;6dnt K%Qs,+ex!ݬ)΅$ /r7o|QnED k}7^Llbې]?JXE   P0EJ|Q^+XHy6x~Q Y{;̟዁1`C~OQ'c98*ק3?mıP m'NV(뱡Z%ǯأB_jø塑p~>/\S^ t _b?uKm}ᕓb†v/M1WY /ϽH 6p9e&xn2z`Djmè\{'SqK+;D2xY\8[g8i*SjU'=Nvm@$"ISmtʤv0)}ocS焀{JҔHqJe`8<Q;ʞub  5aZ@c 7x3i㒙-k\ u2>Vxg<+(8}OQtbڦy<&QĮ,Bo&y3viͤC>%SZ^ LL=Ʉsd˨ҩ@mzf`+Tpw m*uƝƃVr7 53mʔ tFozlh*%̵Kc( j bkuLLKz<@xnW'dD'dv#aMbS!`gUq;n$L}M2.CD%wn׺oVD6g\&KU.ŹNm!k5tqQZf@v^Yrg ׁyD/. iqEm9[y?ÆzraبO#?G1&J _!],]#}'?]{n&o7nise4$TaxX且Y—Cx=W/O`_uY-.d~J`^qߨ}źrGdܫ{S.ĹSI~L<k8Sƪנ|[YU։U3xA=[솯;ٹ8$uB^V3Ñv#|B=vIImUͿ3n61{-̦$. v\pDzg<63mk:p̈́,S:8NHr4k Kvvy6L\vSnџx, inSBP1~]fȉT2_sXSuQX(A% TH\X!qyd9&Egw `N{0 lAuOjݟC0٦V <ybOC #։TLUs5}y+mv!EsqMӨ",,aTbUR9e!6b{ˆзlpQ?O{OWO$ncK>Qy}Q95/yWuj[ZEN1i{zl,_T3`m ⧯\-0ItF.}wG&'Oa$w4CLju.vv[l˓T l ~ܸ$@4} ^GD GM "K·wb}],hӷF0Zɣ`>B;yR,hxNzoae ͚[>r#ow͛iHuPVꊴR/U6w ^`_) c^zp…j_BGlh:h &bIK辣lXD~!C` l$R7Ny'.;RM)RIE yEF9wd>>XKO?i6)ݨywŵd:*b]RR=mEO'D uZL1eG-R$ֺHjwc6%˱I};ƇKs,K(iM$ k]r⾪ 7d m`D4 )L_o)$an Vq4sNcp󦄵`jnP,ݺ2zTgȆ@DίaGDP/t` <~>6ӵ[ooցv*96&i2~O:0RGct8]JDP=͂7U ȍ㊈HT\E G[GƤ:E~-*9Lb:\E`scY;nEXW lAZ vƬR)(w%$[}o FDFϼ_cߚ^@泫FL|P A7c 85NhW)[͔a~;n#_A 7B+`ϛ)cW/glMxDt3ihxO BYw`r DB\4a@Ё y|,su|TMʹo-#Ӓv .5 Ab|cQB;bQ BxQOHp$D#Ze)Ʈ1%llUF;;Y&XuNnp> Sa1ƃHL?--/+y亅apX4&dSoCX@"g9 w+ASB4 i81cx3HU?;%FODOH"t3cО{i$=22{I2 ;J5ֱ̙8^oT^k8s%RCUT](7ۭ~w &moog}6 2`*l:_{ yh&V2AL,JG:7(1?{h q{E98KY*Is?r|v]cb\8ԇl+nS<{4^5blC][~1.icpX.jʝa/wWϏJth3(Hc ^ 0pFS>-hh E{k]?0=h*\X ?+-սa`P8zJxKkntU# _'Б0W^ P9~I*  o-iC?c Ll曆44׳虘PLK[>Oq% gr.}j `nX\[cn֎Qf^mnR#m~%2ȩ듡Mc0^WQ-鱝?0Y=٥zpGC6߶9~WNkLzCx{* U뎔6#=GCǑ#He5Wŋ#,úm}:*um \ό/a$ /sfi'Vkk᳟lGxnk|O cc kiKq|WA[gb)C`!961_9&[OE2ZKIqGƏ3 7QHݗX5%ޓHɔSqb AY7W*DHIvGTĴI%'ŎmY$Rod6T<"'+v ubW SQgOb< 2qr}h[2f*6pHLT{jLs)@̻2JĔz\1QXQo .V(NQ4RB=1Ҋg*Y0Ij{ǡIdLVݚ ڴ_:Bfzķ|!`@zAiaA2mϺ e8G}3' N@\Ƈ|7Dy!nJ~UIviڨ1K|"Ni]\=\*6ַƲRZBBF<:-&&PĻ)!glAbmLg1" R#vsEʵ#$qf\i1B.~u.(Q50G:*|_aJptX3s{vAz1Eݨ# I°{-լJ%X!KNO+hk޺=vp rA$ꮫ8NEGPwK<%FJQ.0XYURNQ< e~UVP Ļ9$Pߵl/I=)Jgdcg.^"bpzEBb*R8W^FJMZ sJ:N,{3HUkQù܍4i(a*iw Fh4uh.[Orc_9,T/$ <ݏ3pha7۠͝ 'aW@{괭@B+(-fYQlyX6bJkq#,rhQ&}#En<6,#⫊[Z|&EiL*OI{xw F'k١:@D\=LJ;n9 SٺZq̌TjgBݗm*/Fރʞpr Thn@nuY uQc UvX%O*Kk& /mxa̿RľΓ)E,[×2wз?jr{~qS[OUY}H 6pPFG>YF[sXM^9_0̵W`"?bqHee6LcswGևpv^kwAVCw2K[ؿ:s6op7哇#avێ91f9~(2† #L/,j%ғ=hwQH;Ϩjy UK6Q[UF!<ĭKU j=0CFϾD8m"(3%^v@߻<4ppoMj˗4YTxc4u_e3p-uIc1>2~<$pMh2>76)UI~2[W*Cߨ[=a}<=hE tvo]i $ uJ|tjFfRjwy5k<ΝL eƶ3L T"'}qZB&|T(cZ|yߐ$=߁N8j ءёhw(aM-tFXfS ufEdʌn*)@؛JINM#_iC`R28RhSo|g`}NCfJP_E ~zasH>%կ3Vg]U@y ^w`Ա;t$hg{~M!rd͇0MﺎIDP_J8k7%|k_U5%zl[ :N']s0Ǭ5\Ae3q2uLwX%~sxG*GY4O~ѣQW^i&Ȯ|J4Ճ6!aDaѤ@23xZ "v9heh 69:A%r!p; lDY xVx/"}:i*B$V! SR0 A.`03qusfTXv*NcN[o%&DvO3+m~\pB mҚbtq$^mlWy7 ĽGc@ T2@LsPCVRV,_(}eݣL6f#ҧ';؉Thp2=؞M$O` 9Eڅk!jM݌ع=hO յI \׮Z::M_8}D uCwJ6 zyWBndp8\>~o+_2rG=rf8qvZkW6#n0zHGT8@\>t_vntm"7ݚm(J/FbϚ(nZ#0dN l.qVLGO CJX.׍"BVhګE8y$H홾͹&& j̽+?@xJxL[c&Ρ\g-ZJ=Rtw瞈S^k`I-)Cv@` % cxTV=$1 |7u[;$\Bo]9hVv2U1RpY9K )5o .aJ WTkAQ!Os9 >>+E1K0<5+2ށp7ә)wZ#lG#4$U5W?Ǽ:vg8bضU'8럿u Ǝpt`L2{2T\:V]I,)Y{Q{E"vu?ʐ0|J$p\`##<$<_ze<4τ%$RG߷A?|jx\B>129jgGcXa2Q^&P5Â[&$өTb}Ry.<'[ T쵄'MJ<40m>;<^Hc^yktuux6X7b7;R0)Mg0^Eqf|eҳ8hdR/}K /1?ɓ'W mQ"ou@ l{r_YVG.=ECDhW=VsHV)9-:[=1*qUXc C %IFEF(,EmZ,Up(K(՗DSx}j+#N^Y". o&v1ntÖs w[TZm" v?[Phu %%e'c l_9oľSl?4ȽZ1XHKs<;5T[Uy/Y:W0sos Q0}L`P(ֶb^p)jL;R|7/1;0 7#%t2Qe ~h+.)[j´-seXzqUO<7= %rmVlUeM@XV%!p7C`(S)!MppR6laA5L2T$I2J[A3/CBZsnD|7f^S2o,:XX-bQ z[}%$\k^) ˯^L I;{W%YX]18 •9Rlcw5Kه23i>|Jx)g#%=QB@l^DF=nw?ӟ =eH۞~nlõ ڞ {d45;Wh ?%kM3JN.`ۻCwqàQ1@Sһ'HéIw;Lܹ8397[-lXDwMVF-̜B)aaWHr.*$+2R%&P: ?DFaJĵ l}y8@@,`[b7 x3w$PPy@'xz*!Qoqڳ$C=S'̷e>1v~y$V׌c5ɾZ'YbA"@Ts1=Gg~8l%ƯīJ-cr-Q7e̳i(f@O2J}FOEx|Fjپh_#4/8ky*w91}a%f*7~L̉jL:riEBr=Ӈ}yHpb@bzNpQMa_֏hJB-5xN2h=i쫍׎Cxp/8u0\^k/>y CHxX~K-%BD8smp8TR8^s^tnf3dS[=L1\1)S{'}? J<&Qy)K7Nl[˾A`JLE,1֪G %K~XZ1Cݯdީ,ϩ7P6:Mύ¸y~-^Yr| @N=gPE})yz|3g1َcfM XX ݟ;F`Yq#w.m Nb׍0^ Y)[1Ac)я˭5T~U;{]I(||3cyQZtE^'/Jv`z3w||쟤ݕYHc?(#527~aj0tibPI+=̚.^鼛xl7mf}@:2;nOM# \ 㓓QwHQ˨M;/!"ALJ@2J~. d|.6^/N=Oh\p׿Dyâo%9ۥb)6L~~2Χ=[AT\2sb [}w󋋥U7>S2# ڈ>O L!QL N!'^Ӝ٪?|v9mzߎj222^Gtmo^ ˝J̢ Q:n.!ÀycB|B{:c)C#4p-}mnsJcHQ[ ?mz$fNG&5iۗS@7lIPh4_6wNm5NzrxcAࡇzcU86C C C CuBPH@@@@@@@@@@@&uOӷs@@@@@@@@@@@m~zYXE!s 32222222222L`"C C C C C C C C C C C@f>-C 3uP  80C C C C C C C C C C CA/22222222222~d& :2XE!s 32222222222L`"C C C C C C C C C C C@f>-C 3uP  80C C C C C C C C C C CA/22222222222~d& :2XE!s 32222222222L`"C C C C C C C C C C C@f>-C 3uP  80C C C C C C C C C C CA/22222222222~d& :2XE!s 32222222222L`"C C C C C C C C C C C@f>-C 3uP  80C C C C C C C C C C CA/22222222222~d& :2XE!s 32222222222L`"C C C C C C C C C C C@f>-C~/2^X]](;rv j(J2!![[3<ߚ]R/4|+3w58id'&&#fbz FH(@owO_於!}w&;%YՎO ɶZ( 2B h$B<H Ĉ b@!̬?_rvzFf4NcY{2;v^?{W_} oC~N}b\8_җ>~<_΋v mα/z~'ǟG8?O~w~WLa`bHNa3pA< L mbz/|ɟٟ=_;nw><|`s|KB/ٓ`7y:\HjX9OW^zЮwog}8맼_ky7V[w kc#yW<=.qWNyOU{KxS}cuuxG);_^›'RNOrc3?͓kogŸpZ}ȓ6j0>eƤھ+}y&{,޵g}t(}T7žh;ijN$\??zwpN"N9}W#e8SE6#yMsM]mǬþRL++oz뭻{4MyHW}߽-o}[p5O~#G}38YSHkv5w损QW[ԞxOӮTxj-ׂ>=5QCΘ>OkXczcAm/yoQmXm~j]O{޽{zQ{W:ӞK!xc1~oS_ӭu;ȗ;ߚ<omF>p1apb> ML^=2~޵\lYXlz 3}לOlDѱI7hMc%gCʵf|٨7 5&oXG'MC} #t`#yůͷx<5ȩ7 >}g,Z{so[CS6jh5FxSloa%~f7)m.>o;&Y' {]Z[?jf}'?6uJ*ӗ ߌLXAP ^,.5Sa\w^oɾw{^{ {4@ }6f+:ϭm:> ]jO|Li)M싁Tĥ?7ʓ@zbM~sȵzqV<ģfW&gtϙci6a˕:9r^lɅqlwnMc͟qtlN猩<>c͵nºTݥf3xfҚV; Ts[_YB9RGI}j硒ڦf-Wo=aLɹk1gBUf=ցt]U6;:0kj6W˝6Ovldecg>l#N;!<^?_WᇧԸ!;x8yk3ԗ-uQK_i\>P¾Ő2 #={՛=ԥl.!ZwsoMNbg~4c1@/7zu1<58:k6ǴZc ^=OGDfrෛV7uMxoX@5k@F}Jl:C[bb4x\)Ool`Qw=Mq=!s஭yo-AI}N٪oA}M-}|צ}xCS6',kf8lZ{]fN> м=M^ _roO!u㼻55y>ڌz*msБ {𸀳c#ͨf'w7G|l9 >xIm=yiO)r7Cy =q}1Q_,u֮A+]߉iˊ5K0s+A+u7qAtkܚsbg,6o}fR._qCȩ^쥊͈l)Gzt/ .pt-/ramqoHOx`68l f k38k׫Kx^|"6۽ksJti<_Jx͟2zM Ӹ?$y {31>c֥v/ڽ<[osx#>!6H>9a] =d4~&3ᑻ]KY*:j[_91sGT?uao8J<Ĥ~&k6>e3Ӻgn+:ۇd&^ģ,Ƶ6toƪzǹX6}_x) qd ׬-~xꫯnu5ϭߩoSGusႥ6!]ٵf6 sj:]m&>W Gs/Xs I>a0p8 a0p`0p>~0Ty8 a0p8  By S7b{OySO|kS f7֔x{nmR)_?:)?S̤:/1m.!sK}ڗUz%~;ʆ;m'nXZ'۔em]9`<<}YzrO̗X'cfӣ3q{oR w7,u-c7eˣ6zym3MOlxgVUQ+3z'',c0F|&֚owfQ_S}`$Zk/kCބ}C\/uO#.+ȵ1<6׊))}:y F.8i)?i1MvZ}gm2ZmKkV XVGC7-1 ]m6Vhn/&~emLމ.xИ5Esu*Na0p8 a0px_8 |_h>I-`^h}\ODa(վthC>xǧvzm<[۔l'Ml6)W꿫%c.[.t-:ebhM}G}/dm߾!Mox{11&>xjYCql\CZ3t;c|7X;ٸl֠L6]L G(O-{46*Y ض-O|w1V> 5>֦g>y`xo#K/tonW >ִih#z |rm=#ᬱ-n_{yx=WZ~xh eMIָG&yiuZ.){hC=ƽ`ͧSI]X{^Œ#/1x'q#[\VZalk #omL"Pޔ`ИlmjӞ?xn:\/ ~GŽp?u7&^'㢎|^O֩~A?!y6l!:ȁ\<'?5v:o#yW꼂Cz^>?ԘuJ>Vx3szUl9t9l,nbi'GV7[rcPDk6ԕz}3W̆tILb5N#f3Rc>¦5.&BPGr塮^/'((̓I{S7NyC|YwטݹxJ}C)clq;/>6ե޸&7ᠷ}Sc]6ܩ+61GC_e7RUlf`yd:< Zzu8Zn`ssN׶&χ[S\\P^\hHR=r<&586)7r|hXp6е\S]kڔ6ǯSØY[N9fI]źdo\ښ9jK./żvC.բr珍<3fI\ucTdKwب{WF5v|_dh55 Zv1r_enSa0p8 a0p8 j]棶䊘 1'3v{ɟcVcA~d-qmVcSl|lM9ָ<:o?5{Jj&."fj5Շ~ºk9wc1GBO;̍ͺӇ>Ɂ ]:JZi|JROV#:O\{Ћf>ViOc1e}\' rmdn|ҬC[9oe&N?~36U.L3/|糨S s?=5Mmlc\^ԛx 6x^㓾ky5 }{oȩM5 k#?8?ַpNL{'>+c?xx 8)ОrFB^{*sޮx'>qow> /ǘ|z/m.EwqX#'y1K 66p1]ƈJxRƶsOӭv?rjܡ{)<9/fƮ6jX'&xĉkO m 5"ǣ^_/OxʬMUNxXR+W=cҮ^kdzW<`Z.W?~Ogĵ|M&ek7a'G+s]5_<Զy˃O^[KS?sM_6eD?׶ȅ6T_Q17͋BG}#eֆ}WGwuA/NYݹ'Vfq8 a0p8 _OXQ6Ʒ7opNװеoy3G֤45$֗oƸo:Q:e֬o:1RXPGo$cu`ѵ\5!ڈxlk9K.jq̓k}G]Z}/Lb;s2>R=Wf)f҃uɶ[Cel\ˁn7&mث|'=XgҵXư2r~q}C,dV1mP7aZ*< klvy޲+s8VO^#Wz/kkB<?!}oס=j&JqM/ro꩟r2}:ڽfQG-_,pD_pͽ)G̥i3̽;ݳB v/qε鿼}ӟ~Sm򗜃'q{ސkW5]/w<0c)1f|{Aɻ?LiڳfmoVg>RsփD߮}V;v3e,yogMX2?XCc>6tm3!{YܔO O5yfƳ%f=m~^v!9[nk郮Շ15lzHn'<ɟuCBs0';xm+7Nw9HNѷ蹖_8%14[{ƽwvl{5 g<}b[|x+3qxgNkSbGoʌOO5xd>p8QIO2s=mCCfk=NZ~}v5X;Iccq*Tl~93?M'6\cOΩ]/aj̉lXƟkc1-dV/vr,vRΔkmJW-~W5WlmZ9}$[Co#D rO}ƒO5X[^~KmR5~#tTjxm8Z}z.53] -]d\a6]b~ꚞMϘ՞\}s.21W^Ss:iy.g -f%iqy㯮cuʁ?M5jKsnXSi:ǯ\8'?5g+~9q}]צu %fo:p޺w)FOX|Rcx〷7^A?8 9>JWGiga0p8 a#KGp?CoqL,*x!kO}K/8SnyeXƖ>Z\{+'߬~˃n7y&'>ڐkO;'W[2j{y'Z-ciqMikgZ yvZ u4y6$g+Y5,?`hX?x!GlԳxdv/n7o6S8'?yM-;К/\66VoMsՉ8ORǰ{xq5Ɣ/{r$gÞX訇{[][ouo\OLX:I=1l=֝q}+UV7~m&=u{Gvs YWdk_*{ھB~kua)3JmmS;8-&!?:ں#9Xm>&qZu\o޻>ɚoHKWm9sRC{?{Z]c]f8JU;eCk<^g3׮g쉅: 5<|hz@ sb;oqVNM6g.&.i6)ڦ b'ϵ5\G ̅ZLKY53?ڔ̆tyѵ+5mkz#Ч6mixP7@ksjVk[5]ɍcR1k_7&0.cɏ-g]Js%}羦R%zc!y~s030ЧԆm]kk˜տGkұF(Oޑ.|;h6*@pۋ T7SOmO">xt>OMK=X6^ڨca3z6Ω|POg2F_ڄa⚞I d,:]E.[0G^8u36se_ɺg{+)č;j#_<_{2X4iyYyS)2[b}*}??%c^zfa 22ś涭U3 ٞ=;rC -{]C^A^xk[}7L|ⷛ?b͗kOeԒ_+6s_kng|x+a=VYzd7(6;rx81&1DmCpz)|زc̄}uœM2ktLX#nskP>ea%:dO+Rm`O_c.ɶvX'h1䑟/COo7f&~<cCOiWwƌ3_Jp8O?ir c#Ob޺< a0p8 sd%9y~373ȩiS73'4ӷG+h}AQl}{ N+̓8j#}w2}o? ¯*[B_aoggəI/uYXv \OO1xN:쯲;tmSőOtp7~?k|?mLz:K?^8j_nW<+W2~G,$O=ǞǼR6۪\zǩTƃ~jf}Ioܵ2Ǹckcm:o55loڲOC8WvN]]L?V[=?ouf>uqQqov tv&ȍ0i]֤\Luqx뭷 g?ٚ L'7Lczcl95`nr¼C|{ =X<+AgՍ,oSx;087߼?\dԁt/a'X4^lc\yOΧyj8xո >$%}1Fo _~m%?>){ft}{xM.u8lRoOۇt~4I]bElkvpڽɓqɿ8$8:{kϼ6:zhaڧs7m>578y= ^'ݔgkuFo2/.v>|k"wA&҃]_qƆmڬ<[u:R=16G6վXWCέ!<|<Ƨy ׃)$_k 0.v\XQ_yGޔ|45V''=q$꯭<ʇ_Mn764~XҜք>Ml>2]gbC$6Uyiϵ8f3.%yx.:kO]̖ nr)[P%6 xOi9C!ui@;ΖGۊ.?'1~˭<왓ez>:~^}&x ݮ60'5xā?ŵ{^'_k@N>b=`,y8b~Owwz>M`c\YNpV]bicbw6WyelElrN!f[֥ܾJXw6p'{/=顲A7i^YZ[c.f]o|֝/zNUUDG\nں7 /oE`g* ģx@G ;vre~mHǼj1|\=V9O2~xl59 s7WXWrK7!+^6ϛ:rԖqzX=V=p0C}aXlwu{ 92r}o5ga0p8 a0p{ 7'a0p8 a01p |Ϩ=e`{Ğ] c7L1jk)Ϥs7cw}~gJsw}QKpY3:x}nr잮5٩yo ~v򷧟n|Eߞe 6w vO#8r{yu&W淵|( >O˛W_}wKll''u)65t/^{힞ycTOyb<)uY[9?kTTwohNў?'|$䀟O|wO$񀞖S[z|c!_po=-k=)ȓ$^^_Oў1O֝?c!n3]Ʒ1rm٭/S6M|y&޿\R)G['39cihӞ&FW_Oo!T pawTVK<δfi:s׸K<ޏXSPug]Jn֥\m`9bxxeLjlyVT[ֆ_ެe 9еa}{Eزv3?toIGS'R%)R;ȃ5&_p2ZqS~[2JMl`q:FrZc1ת|%.&IC}MgVz2Xi=\dxĶZg=o1nSa0p8 a0p8 <7_y27))[M67A~ 29v:/9o&=n[lwu$7m;UN CS<NJ:Vznݫn=k?O)~\Ǘ]iY֘vcL0sr1Ζҗ՗1y~_;6fǢ\wA_vZR8O~x4i-0Bbȁd{';ok>N5Z(sawrLӦ? fJc.bʻb~񐚓CsdlY>{-5cϸhro 3_dGֶ72Kmİ^vkK^HksJ=Zc'yM uqL&5`OLJkv-1¹MלX؝m8Ʃvc@XuHuS \_6zre~4mi7֠Nڟ1:sڦgsH{뷺V?xwae}֞=)X㊟#kGN9z=mb鳓YVl[Sa0p8 a0px8 |H>)gojot&:Mښ?1Mz| 15'z6|Oi7w|s[9cKM6fcE#τ#zXuoڭW*wQ[IWNj\Lc'9VK~qyܦRۛ"9mmK5:JqX ބI]C=ȑy+ȵlMv731?d]J3'usdb1Nٚ͆Z,cjuL߼Sq$_Mτ>Oぱ6;8 34b[Kx>X~=a0p8 d|I9%a0p8 !ba0p8 dxrJz}iN\=vޫ};>~#~bŪCfkvο0dC]9ăyWaGV7~kQzFOlkFG2c|Y~MCkg!ͧ]yYL5,vrxmykckfl-Θ6Mb>skhxؘQnvƛxM\wjxuG<+.݋&xj6.kM9|k[X| FtY#9~gC^3ONl&l<= A @IDATo{8xdprm`EFL{*I<1KK^,ɚޫzj#/OgP74yߞz>q>?k&=iOi t6s:Nz/FXӽjOx@Cmzndk%g[ҝ4g pyѱ M~\ n_ GnģBZˊ6>ٸPk +ؘ>-͆m"Ϯ0&`^c{kZVǛlxg&jgk󩎩)τc)N{Kx>c59'=6j+kG:XbދC.+EZ5݄XcW&<Ơ7_mux?Ǻk{[˽m.uHF,l&Ox+kWnZ}MqutLV6jL_Þ>ƀO]->zOxmŠ#rhxdu_~Gzj? a0p8 '^>nia[M~Mü^LI3}cÖc"s@硯)\%&[̑qs[Ӧ2khXgQ_]ե>WOˏ%׾aN?tĹ&CVC[t9‡؆)[ `ak-m<Am-f%aiky4;:ǖ&Ҿ ϮF6\MOI }O 6^tmvsrc0jX5m\Y#}W5~̳b5zu.ԥlg4^_O̮Mc&Ge_>1dbgkѧ7[ԝ8+g/6n{۽|o[8ֳ^ѷ{ƌg}׍Eti;9cńxg ݻInr!FOι68Hxm\cXw?kensh:1{ oNi⑧}kZWIf_~uV{=;jfiM!N>\=eS1ְOk۟`=fvϤ~o.z}fyGfeď<װa=y{z/~bd[%N-c qBL6|A ϾOjƧnθZ(WݹXJ}͟z8^hsNo@{$_>oZƍ|gV&[mN%;q1cj[iJb!]8Q36c6NhoNMa0p8 a0p!%Gq8 a0p8 > ޹=#; a0p8 8ޣ ~7o}}I#;7"߾o~P6woĆK/t'߀o|Nw"n_X8pn3|3w~*Y/ zM)W.tXZ>N}vyvÑ>Ԁ>4oWZc%8.u</z[mr,kS5ryvbxVvN2:x@-V[Ss31uOؖcF'6Ƶ6kzL rqW;y#Jd}g {6s'~0]5IZv!;cZ.b<ă/ f1C>LzcZ5_\k.1V ԪƃTєVtNG؇iMnӣ#.W[,u#&׺8^i%o6II'RyCs7x=׿7|!x__`?wo 7OO>}_}aW_}O>=_goowy$k|}S?Sw6/=_'xEwyͅE.>OOpC%ɟ0i\<7,b Л/)lXnst#29@GbkOk=1CSpژ1W/?Xq>|Z]4_l4Ajffa/Ofu9g3~{z"lO|B𦧡^:xosǧG ?`%O:6Ny-3?Ew5 >Y-t a??//ᄑ_o~__o~~N775f!~?dP_u`sP7x.><>گݷ4ϿۿbK";0p8 a0pm·8?|⧑g|^=>3'\̇,$iC=S8ք?<0'?3'X|?>6>rm46/hX>QY/ #6~nFǻRڦoVݹ5bN>^Ӫj:ȳ@?71ZLoĴc+>McF}iT86:@S-ro`lI\}sd}3?foxR}qV|yoz>Y}fqNIL['%Ŝڛd^[m48S Lljq׺S6|6r>QK6Mitgk\4DZcKyq|3Z$h-sOB֐zZ.3~_C_jx\Wbz8T׊} Cs&u!pOB0w~~OO;//l {>P˿ݽ|H ,W~W{䯐|j)]ŘxQ5/va+QvI=Tޱ)͵뺆ЃLz懵ć)E6%E֍NmCO}Y#zҞ/! /m[< :c,͟xu^WZ|q8to 'ϼ^~=' sJ}^R//kdN69ZY{А޾})kΝGWt-+f1mH)]/>x@ ԭ:Ç,&13` {}@1ÿ۾ 86f~[wu1ᑿf޿`'Okgצ #$R/>SۿāmZ5Ts+}n`L>}ySd ~,N7I$P76'/P|g# |`&Ј#9f<߇pFv<8ִ1v1}?mq?oӡ7״'Je ~Ԍ>k r6LtWԑztf!RK~n̦s3ޜQ5O/絿M7c_/Zϔ8Z=̅-3e4휷[9vpGƕZ:L)y̑zOS6ۆcMe=g_Nワg/~O=OFno$& "ÇC؈(|#˷\,>ݿw1>}|lL`>Aw~w~տW~wP |,77Er ' Grɷ>AG[[[[[[[[[Q! >8qZHPL: :Z?C?G·7|"|qpC9Gc4% >꒐xik놃gsk5N]8ӯq@Gf#7_rn7SSԁ#KJƀT3߉2/M7Ao>cZ5.-Ę-{'6ެ ,ld-nxmF`_xzoXx&s&#]v_'?>$'ӽ}no퇓CZ£/F?snHĴXx{~&qiYǏ覿o=V+lENϜMk|턃/v#cbn?~obqN^ݩ͆iG:7=uk\_?+9~yOÛloxڇYO@?+qO{Jt!Kx/0mbuoX˿Iܿ>|I6_гO'wjzH^{yZΡGf>y=J=`1Ѿ1 iu5y1%+~\G䘷ةo#^+Em>>Μ1/[cD܆GkʱG_y`k!ƒB^Gxag5ۼwmc˽IJĚisJ(;KĉE I_xd _&!1s&fkzӼj?zNW.6>aF|K4ُC^8CC[kȋ>6^boS-F̌CoƝΙm 賿R/6zvy)G6@g-RN=4msxz4.cgrO .s$o?eyW fc{{iGuuCHfIF^Z~t[-'zmQlxp@a=u0OeJr3[~L9c?5E\̺ef牥>ԟXJ}.x_iLao:pݸ}<3yN!^zMoap? |뭝_-kOͪ֌rcɟӷE|cR~coio?| 9Z97&?lֆ7yO=!y,axZr+p+p+p+p+p+p+p+p+^aۨxas!yyg%gf`~`~nPß_$C2|kEaBrpZ6>@>3|?ݤ?g}b*p;>J|/U~BzLJ&j__O?`Ο_ˇH`L>pW;n暉d /pA>l aX?x n-qe6_&F^|>@~EǽsL|#0ԑZmtc6oqfX|ȗ4s>yMީ7r>u#̓m?d~t̙S;>vnmptzc}ӡ0ڰE|nxkzo{6b#5ݣF!Ə1X%7s(?͟XN,\1ٯ&>k@Gn^gq-rd /857\)y VX3|>T{(f(%#5sC1c˸_;-i'X>c_iJQxO /-?q/ J|0?go6K<58oSܿm_Z&MlO W\ݿwO$?驢ܫ!+\8rᙏzۺGqPOP/hc1{S¡H,bБٰ{暘×[#/2c|pmϗ)_6))11[?^/GOn4˕c _8f8!_G6oሇ__Mk^?칩yK;קlRemrNӾ> ǵT83Y7'JD)6%Ǐئwkq~+8!3_b50:FZ_Jq=B/IԳrƕ8_k's_r!q8aa:k~Rj:6[ZǬ s5ެY:@mmXƵ9xo98~ohTtǜD|Pg0X|f7l\E}/'e=|T“aᚾk:6 h6q0g^{y7evN?OLL믵h'~%n5|TZ 5|h 6OBR/6_bN,'yNIqƯkđ?fGϛ>&rYC!'$yog!gc?;^x[8͒x}ۇX'_!c>Չ_r5$[_/lkS:q:'ϩy~i{t`'60ai~=;ޙo3=ma\s̷KqLSs}vO_k o셌2?/ӽl 8&;|"( h=ri|8|X㯉49/9gK`o'v{7X~x:fkk͎ ._to6kjhac_ض̣Ʊռu~C7^ahQf?[ve WKf=ڐ-֟ډl-?v7L ŞK}rpnJFLE5 ޶o4,c1 k}bop/#mе~lbе`=q}[9moG/cj}J8r\g^?:s;Υ1V/p%:r!1zq֗kr񋔯q~loLx ߦ\o[` .7"φ9}9Ǐ77| q|x CXJ`nJse駱ӟsZo>gC֧F6SLEӉa~oU𭲽nnnnnnnnnnnn>ST|+p+p+p+p+p+p+p+p+p+p+VVVVVVVVVVV3U3A[V~=!kq&#Z>Ksr/^by6|f݋?{/Sv1kG~?}ܞBk=<y-5 Tm\gl9xg68#mGSm_sqa>d#G)ǿaL<ߺ[g{}e*1$: li9xn6,tt +ubaGb_[ڹfs}>p' 0уOl lṫff'-̭>ԧfCG̥.nXr_ljOm|esiJpۛ%oO\t'Si&9K#\rn6LZc_WlMg̣\͎U߾ }yz8&I<ŽM]q1G8Ȼo8!۴幾J'^lZ-}U^%Xƞ7qmBi s.3S)<'Gxl\j?~1Bcňi?3Aēy:3rx1[:M$7qCӝ56$[KӁC~F!2R_!,(~0'`Yv/?ɍ `Ǣ,jj˯䌎l8j:cxYC &z~lȳ [7woAiq^ jZg8jyY7+3om5q7Ɓ1➖6ĐS%ۼu塦m}RodkG ́?v{dEWW,=X蹗)C} L|}[Ӂep}%-xN-ƵO笽gcjV7Ar]`^l{X ('m?J}ֹvs<7{e[2۶?GLW~.C[DNsx4uqJ,r}޿vOĵ:ou|C]o>ՁyF|뿼ޚڈ\>O E1l EVN.LJjxMg?Әԩoxꐳ6zR1n-9#[:SӋX4{MT(N̝z1r|t7S.쳑Ƽ5lzbߌ6u8e?lwu1l6vȓԥ^id#8l[]OX$t$ o{kby5X?8`km~4ReVTli37~Mŧ<ڹqb~y+[.x=co~oQWǷ؋VVVVVVVVVVVVE |Q] (7.m"~Cx^+3x~3mi%/}ГÖq)QnzZ-2X}~[~Ĵ&el8tb)}e,e,!kGyet~:oC_8Z#>wӷڈS;-6f GߍG}M&ͯ>`mO[k8`g8[~SMȃ|G=;Sli!zEL㟜Lמ7`4=iKyć}G8Xѵq} ߶?^s I\bG;z֬-q[;S&㑼7ʆ-nKNuory$[oBnY~kZc%߆#gcgnikz}xS1NwZyk2c8'g= ɛ盏,{1^ozd\weG YCs͋irE{M_i~$y螶v c-ԧ}~ 0knx)/)qyפX>nH~'L;=[~bGqn ;CF9NԍiJ<8mQ;w֐5#A6ϗk~(V.k}:dM͕c5}?xN{#vg댚ɟS!1^ϑҴs4쩃[{݋x*&q̻GرFN-&~< 69+eKS p΢쨋VSll|#Vn?eba7&7}/k}_"k֛|<YφgUnx_ wofqH3:yGpݿZ/9>gy^koVFlO{ٗC9lٚ.}Vr˜`Μ6Ƨ?c> 2ܦ3߶.Gr[8[ǼHS9.hCl0.%O M<o;'6k$WI1m/H,b{³33ZCOs֗:%-kӗGx[%n^S6W\\OW_TVVVVVVVVVVVV &V~fB+p B}wn 89LtlX >gl^OcMi\rvcZ=}^:zxǴ_oqαKmvu-n&m>ɝUХ^~q\7^3)[ |dk.`dNrc۰Þ>\3&pjɆq1?t[}ƴl.1S̖jdM3V}bmܩE6΅jSn!T)bxj_‹'L5~8-ϋSkeg%7l&Oz{q`b9ۛgS%|/5KޟXX_ւ~'cfd |_ZІo{!u੥nמ okiϵF ?wwo}YxS2qpƘGgƯǼ_y7%})tϛ=Q\x0/79N&_rzAG?Z.GOs?9'|~O=yk<_xȶ=]lux,Ҙ8TLg>+_9k֙z}ȭeMP'޶.x*mc7S{_9 Gcq͝'7^:_ Ż5zƶ6#ĕ3L,e#{tН$lk/ S'l'[NXO䡝l<>GMJeNrٚxi_9M7?aΩ F#?g1 ęyNLO?XpH}9xuwϸfgt_L=@7<#[>c}r1/5piؼV)o}OFW _tOHliWƤu+:l'l_u-VO S)soŽnl7t-#Gr%O㍮ٰ=kNx|rhiXoު|y9ÅaHsqA[(6<=Q}X?wG~(ͿN?=J_Ľ$ѽ=g9vpB=i힐|mΘ~;ysFWlI\<_۽`ñ~F{Ǐ3ux'(_x79PoϸMo~Y8m_!/fwmZli'19>c裏uHbڽOUS;_g Jܩ{an$qL=K}{ Vo ݶ6lC l|$n{]g=gc~0sG=+c oto9}mAY7t˼f\Y/9%池x]@IDATƯgwoƏ56q^<g$oK-mo~|k#r|,y|8.X.~ZйsO<ݸ|![k8JۋX-CxLGhz-lzxd|^Y̴oq|m~qiyxov8:Nڧ{e9Vy=19F>O8!l"זS2lA}~ o\[w^WƤN+:F:Xԋ#oNu X[6=y!1 _?Gh<woO*=~ѭK,߮78ڷ7~pͷ-۷'^|Ky^?86<0צǎ~Vpn?_fo1t@0:,̜:?T%&5Z1ypν_].l='Zo55~C[cRkrCOLGan$C=1:d4'?9Vgf 5uqkio67ڵ1vy߶ڽDG^8%/l4CݛOmwhc6{Cd/_b}f޲'}ڧ_L/ׯDnǯsQsX}ӏk(ؒ[\붹p5{ә`Zf bLi.6sTOi{.s?pm}A&鋎|c x$/ccmC~{zi| w9ߗں$Tml{$7}$ϩ[6|6,բ:jšr10I~= -fsh3~Xdk9mmucZoK/r54c=7W_#]>Vײa7~~-:1u+ygn#iof#"?d}N9)\|fLڸ//QfOi-s%m<s"b|!lQ@۞.6Ӷ8?}6C^{:!x4HSCpGSgOcxlJBr@hb<㚵zy~i/f~?[N֯ߦ8b_[ 1۶ /1jߞL^8|͙X2[rN΍)KL~k}M=&c~F|[ xpm'%o>h?ڞ@[_"+U ,Pr:7 %?u-?[『zMjƴج 7l1b5.ƤVkOɑkb2.Q~q~mGl֕m`yZ}!?kc6з7%ԕyCl$:pd kxڶ`ϱ18fw׶'筞ڐ T?CΖf_skݸo:bGI EǛ5E~ N%9',|j5%c7}i'28s~Kf\HaP|mx#[j6G|s8 wCV?-rV_Ep*7*~[[oӷ7sp7ڑ~4umoa01g̑vȟz هyq>9cꐎyFX8ZIă'ަǗ~n7;6_ɓpKb9sp<q)r6Dz?\3W7EؙY'e= 5̯ ^̫~9xo'Jy/Z8k$㐍>'?y6>ao|YG}l@ﬧycʾq 7 qӎ=9l{׈eZ׊{r2GR_$M6ƍ{Wc‹6~d|[laX|3\R˶ܢ[]egCL-~pOpN!{Cӓ^Z+hw_rz'k2#zskSc-}ɜccd'66Q<0f[:-\`Q08:-y-ΖEfhlc[f?Z\ә[98o1걥z^-MbgKygtH~o|ۛ(t߆gݔ.yi{78bT/$?tmcfzZ~SlԠQ}kd-7 zy!yl!S-{?f揯$8󚚷ӟs٧>TCG>f_mf.[s6[ӝr}5Gulr2v[.lml69O}^t?c_`ss |?^[[[[[[[[[[[U!mes+p+p+p+p+p+p+p+p+p+p+V syJwo ~)ԩ2o7Lt=x\c't1.o\WX#cf/8'>mg<q< W06hy4~ދ/3}#mY/np>A|YfF1N=q"4GGܓGՎmpi_5Ƶ1r6Ĝoڶ;o3Gu;oqun'1ڐ^#NN0w7ƥX['%:1YW];4ÏejCi"\YlB#ƅffAnm/YoƟ9̉z-kuO$;ա՝8b?^o~|ˣ"ubaf[n8{i9X ori[}2a5BfK,;ؖ ܧ/N-| $>ǘbo^֐~ydn3|N_fXmlyL~`'9r3v^{NLywJjb;}j q1oc) GDMZ>M^tɿqb&noミujvb5xۺ992v#E¶x-cNN1hD_8CNyE@<&uY[[Ŗ5yycG2Nn٨>lz늗lnJZRWGsc`kM71524m`%cOęqi6b䐼m:cb'95:l͟X룜x8̸tĜꊽ k;&}|v?~j^R<'Qo gCL{Svy.߬K?>![ko#۩?a݄l?IԏpFǽ)?~:k`64N>Zo~noGNujnmaͧ?8=ͯ-\~ jͽ)lWz `l#9lp>C1=8:1H=mٸ}~V'7lUjr\STo69Ͻgrakp=W6~U~9 oc~P'em98y/?6k5 Ϲ/CͲ?з«`!\9v?qn>0g}eoQoqT.:?$Ŝe6HŚ?m_QlԴsg<38rwq.ǩW⛎XryKxLOyIƏ\#g3R }l(gO1Z9o)ȝ'2[xiKc 8]]˙_\a}mx4~[J}T27z6 [7Nu?9ؚޥVVVVVVVVVVVVsKV Y*7I~ۨ-WQ 69GKXz͚X8(yk1Ҁy|'[qLnr>6_Kr1k8JMafg?[NlL,0NPoͽF9yOy櫞C6 Vc57'̬yG[}Cyԋ<|#E x5/pC5M_-rEZk1Q7tmi}%x 󥺙W3q<Ƕը9z{!:s(ٚNI\C{r#ObIXL/y__\޿`|62_L[#Pq ^.jt[¿f7M_}ȸu97)=E7EگZ9/5=7_03~̗ cr<^Û{Ne=2[Kԥ3Iys5'Gf;l?7ۏ>N|,O3ʾϸ;"k.lm|9{&'J0lM$ZYxq$߸;/=׆EƲ??JXPf"mXxkP~Gڸ&Wu'b}2[Ӂڏg'n\tnYYm?n7 o #5ȗ6qY?pFGZ#6yHב6q}+zxyp}!-Wʃ",б-#)ms{66mÿmz1oӸ'-v+p+p+p+p+p+p+p+p+p+p++p?~^[[[[[[[[[[[\!-v+p+p+p+p+p+p+p+p+p+p++p p/5>sO=Mӯs%~~GlӸ41zlw6~ncCy4ڒ7"a=&r6Ore;39c>&i4>mzZCn{Znƻ#kbl;b䤜85e.%a|jԁϑޚqiqmtrx֮չ7m[b61m~s96N3`o:%&98w ~#5O?guſ=_Scǭ?x]=Ԕ\My_Osgcܳnj^W˸Of?#p5ÛyƁQ?l_v?~F+1V `.B-E7Gzܴ ;_˹m85 v!G̙:6kpiqMlX7ތ1[K,yd -1oˡφ>5GrT?\3_SO=Ęұz4|tG;2cq.sOT_1̹&?^#qo8pKS'#11rkߩ%}-|Xz1Nm BmZOt3r>Ûq}-ּi32lȳ`519ݖKZ;wzωaLZlׄ4bیSS{1v[C[{:MR\؈l=?aCHKL _Y ؏Qlv!gcM{Xn]0<`7Tc0{>}룟|_FܶOo~ꊾe8uBuˣO㽽SG-í vħ<^2F?eqjy=z~X۶83۸7ĠO^m^Mt~?Ѹ\U`.TtՆ5_t,fᘧmđ #7Y l6=xb%-`ז~#̳LS秱8kFټHpПtd}Epr<1rRNGk9C;C -66reM(][㍽O|qiŴ=}~H<>Wroa޷:$1zTs>QTz6~psPOYMqn0aC.v<Hs,R!Nk;8ٟh1wCrN{?L LWolop7V~+oь\ciG~n_oз|eִ7[ m?0䦮q@X6n^6n`<~cmM1v3gbzӰ=)m>9zb?a?Ly߾I&oϸ|xf4>6vϑy ɟ-1_͏ꆭ#<8-kH~б۞Tf|G e-K=9٣\ [7,tmz7,t-%ǦK܌<-Wį\ {[6LM|8J}yO;ש#?!g|G+Ή=1,H6vϑebrOD.^6tpw!! z_?j錄:$zgï݋F۽lQgYflqno?sE{1\ԍڽ0g&ɑӗsl<N(/p}#%:m5m~g>k\ws۽u㾭 Ə{ ^l&>/Km8=<}-17Я6Gn5o9N-,}ډQ|:\O`"gS96jҹ+[yN[QkFGg  |9.gXX,&F[룟)nz}/na1Nik&vA;{˵͑'7ϧ_9m}Mn<}<#Cb ĥ^b-js |}-sx8Z=bjq/4mH~8^7kcܔ375;snw[m=aK%!sq:J=9GH;qx:PGxVҵ5]'t?z0ئӎaao㇯cJbkO$?Ɓ|a۸ONo~|)d<؞eַ:G?z?T2o%9 <е$C5Li4b߰ʭ|7bVn~^A|K5\5mi:6)/9wʃ 4~yO<%^,t/s3obg.YW+bk =3uV8q7[%>؍Kr}8CkM_?Z9qt׎_ίRFt̍?:_Ӷ0Z1v*7([+~osNa!X=&6[7q9u|~? sŁEuތl,h|ڋ>ܣ5`G }3siy7|ۓ6>'"̣R=n8%?۸=Ӈ6Ƨݫ՛c<ٶqL4ܬ~%CfLƏMuعW5 V[{Z<|/#NF>guj.u"?5H\ztuAc}|ƃYopZwhjM31^8'?ƈ56!?'v:ߣ{uډ3}mqİd,8u_:Oy_>nyŖZ)f^鉵?Y&r:lv1NSyE#?H?еoYwo 99f`6|;O aۭT#zF?gbi췺!*lR%+p+p+p+p+p+p+p+p+p+p+TC =~|~Woo|_$ VC t,>}ww~Y/j~ڱ2> t:$:~X }rK~Yc5t[M/fnė\3V-pN/VY͟ꓗ~{^#qm旹4~'\@j"_GO/OXhoKK!'~w~kw֘@*fF-.+Oʍ=p(G'Sq$1^mR\&/S{epS?"k@ 1`9@n﷾g{#x{r$~6x]kGoyFZo|z(8Q nZ}:r ՚^֕kec%G3v?Nl6rGX9q$rЗKL^O+&.ԯő+-2kmfm'/Z~Y\-=Xq7{;ē'q|?^Nym?a~=exԠ<-_,o|Ouͯ}_4__t~>'ME+پ!>1zf>}}9[^O/`پ1k [ go:o'O;6JsOCG2?SsZkcFc>$ci'0s]Yb*'盭 ݉߆5 ֆflmSs-Ubï#UӁgbZ![#i ?bc^g.y_^8b2S>y1 ?c,n1'ck\6 {u_%_r//. ٗ}y-nAϳ\H+XAZ!66\Ȇ^'sSO=Я-y/`N_a]b&4p@IDATi-{SόK#׌}qgICfN.k'8x ޾T}p1 |Y'mzm\WlY!+^GK;ƺ=JyF.|ڳkˊ<vĘ3V?yf*G-v/%Ykb\ywewLq`Ú<:{SqWo8{dC ^?oj?Ո2>AFm}Ty|ש1nm1GGFk9ǎϋl1z|Lm\zv >7u9nSm|`O-W)%V-oye'~ⅶާ}ڧ]&svP< @'~)pc<g_ iBF S'Kv,Ę!ْ/r+ڌ9:Md|x\s_^;CF]?6Rø7m>tſ&krk16]hãn_'_k1W5^c +ENK1c>׆]b6[):`?㓗li_&kX`<Ŧ5鋇no/@\#K;d/k.vi}7k\S q)v nVPb57.|2}>y/qe5}Vgk-m:wwNA_g./Qv}: r]=\sL/xn{EY3`.nC'u:tgĘ0OaN>\Vܝl[ nڰءu?uj^o!9؜v*A"PehvrEݳ3bc}ι amc^g]bHg1=ўgWog/gm[ EOQO;F]yv 6|g,ЃwA׆.qOxԽ}Cy6@uص~mto2Mz`G?2=tmں6>ko렶ўC,4[9slalk=Mwmk ;tm:3yĒ5?d7)t<`GC8Zc|TĒq5;mW wfcd|)+cF>ٱnZ3 q~洺w+x#O 3X~C]Ǎ("T~X&ngH&:i7O~-7kXvH_6}lϵwi&c4;>tn[wlm7ɶŝ~ÊoWZbӣ!FR|y_m+1^K՗)_O׀o,k[)INl>跓JtC[ÎHL5ɧϑ/x.|(̫iCL-UTf8Ĝxkx]>Վj(p3f~!gk>5#]uYqsxҿW'=y*g9|ի^uyً^|j}: 䌝m9o}喌w]vmf`NNNNNNNNNNNI 'mo{ۃ׾>S?89??x 88888888888W'/lK㟅k͇HC݋}Q>>>kogy?7ܟ-ƪxdڂs}5U iMyÆX20|Q᫃l'Wo;lŸɾ6-W~2xЩNCGln+` 2x~x%mG&OjҎ9m:`k㾳adT_+^x˗!ϋU.B׆>5O;/S_9|猻O?׊Iէ.vԧ6Ēb0őQI_fg7XbA E+ε}0kvͧX o7F kqOgs{͊'Ǩ.oLۿ}99/ڀ= pP묷@A۵߾NX9-y;Zy'|'<3#uyxS#_:@r^{ko3k1`ǖ՞|[Kh6[O}G %/ys[61/ȎE?b<(<tm8IjX$'ň.nmkv1S4;5fPhk`>6ԁoo+#AC;́q< >5ljmNb ~ߌ'q՞ȷ>]{+ȅu&V'״ny<q[ |>/|c\xE6]&e>~8PE\j-:թе?}A91d6Sg>'|(:ƪ]0xe:/}vO0XXpsQ}+梐ڃV}L+o/c.XͿ:$2 ݇7r|6LxϾqA*oͣj/:5\sOXKiiLOYւ~;^vcY1hKl̸\XmOpR'Sܻq5oQc,qkLq8i,ЛNH:Oc͹g['x>&^Xz-洛NS`oqF6涼zS}n1fsH<;Y}C[+/?ņd'ù5:lҏ1I՗SJIE @d,u}: 䄎<~>3hᱩ{>0Vyզo젭%߹86y`%䯶j1U7RሉnK6*N~vxm>W]ݩ~;?S>wmu@Ocafx+^#W-Fm'Mo~9V++o76ڴk%?m׾ԢٵUt1g.oVXq"bA%х4| 91_ۂILӞ9"npI<fgk5@=s]^{&19NOF{ps[xS#F 375D˓8üˆM]vhgC{vI7~=|笛zK|71||h΢,˼Ƽz ^Pe₶F>}K>5Yڬ-8ĐxK hv{+9.:o#;xi2mq/Pg={m+ļi~aXеM|p+-|W}|[ljG|̣i.Lc ؏}hgJ?wrH;0F9N N->6S|-nƉ8r~ae>9Γqdz}"6OE k5F ݻ]0)z.DbaG5/6m9C&mYt'ez$qO;Ȓ]Cw/g?S7[x-'xƞc3ZlLX<"f׷e|ia^OjƷ&Mxl7Nگ~!C' 11wȚe6K aRvZu'Oe~h[MTk ~b:wڸj?95)F~vO63n\g,Cs >q s1cľ>?*W'/W~~__~fa9AޣKc9TTTTTTTTTTTI: ,<ڹz: v~5=)y*p*p*p*p*p*p*p*p*p*dU^_e?s-ܿ/'kŸlONNNNNNNN <'OóqI # : ȟ_Kx`cktNy,8n {f2j'}d-~nu?-x &tڭ|'-mOigJ=ō.mjsV?#X)]~ܒeߚ=G> do/e`7}؛쵭_?S>7zO>o~^^׼5^W>x_}y'sd~.Xȼu<[WꙷyqB>oۜ26``'cօosZioRu!cRXrab DpK&Jɗ_s3nloI}ϱPWeڤ~oaNO_:&<ƉE!kuC\˹D2gnY|o6OLr<ɧE8v[9!7O&L,-6'߬]͑Mo >k^=bc8??5VQSևV6!׊f־Ìd7t.A _ᷭk$/ mu[;A":bc=4`rni󁸯ݕHƺȫgA]3pw ,>iψθyȉ:M'G[K]y 7yPl|#bx-b>:knx }N Yn2, З{#bfA;6>48)q!vx4 >g lsBv1ǯzҌeZjϺ[4ɸfh~ XoC2eҿzR1nJۊ\ͧ_1we (t=5e_qo1bǸ/@nbY]۪o-9_K*f#^U<}VGDUՈ|[l&ȳjߤߔ|A[O,ݤmށǚ5~}j,koio]qrcxs:][)Nrj˖ЙlZg O+|˥k.5:ŅmQְwxq7]][/k>'l뾼J@?S?u:xz/V'qrD?UFN.W{8|߸\- `Z\ONNNNNNNNj@NV >W@v?'<+s/ǕnWQj4q휼yx夏_ C~WA(?;s=q*ӄڨxʦ+b;_̵ΆvqJ#>v~Xp:T[kv;HߎyxbA1?|d`ܤ֮]ԏXRtm蹭u?HYonͶ;zYAӺ!ov+YcWYyb>k5lI ϤOĥfS__( 8'6ORO~D[[.ItOO68|\ 5v"8m>T{бk#rk#b;j MC6 ڳfNU?=gdFj7/6"S&w<-W}=_>ص:3d]o7>`CÿӜ76-]8?ϭ){꼣SÎ:|<9C+S|7NAw̟v`gck chk`u,K_Phb8&sxq5GxVG5m_K}cL[3/x58g壮ش]S.FZb^6 ] r?kئ~LrP.E}>8@)% _.vşm:w%vޤT,rd3MvX|mmfNꀇNE'?1ز!帪?m-F1a /o|-nt8ėxblrlIԃø'?G[i|c+aKɇq< 5Gm6YSuzPxf):6> cQG;V~ÂÛ<> kjVOю/fM(8 O^Ƈ>[k`RfI>6<}Kɡ͉;g:{k&o<(6n|A:zӆ>5m7O3:w>'YT~@ڏ>xы^*McD??/'8s@ј(qE:uaTNxK|p==,9DSSSSSSSSSS~I ' J6]RKQܿ?q /?~)n*ZNzda+} ' qg~Ƅn3=1F2KSNTϾ8+N= 6̝Ĵ}0@ԓǀ<)8^^xnp%殯Lߎ|)AKWGK跺 iWƗfڑ'}jz3Z[юcN<\KUTMPre>@oބ[+˾Bؓ@^xBc/o~/oˁ?oStS깰Mvm! rυ>'Ĺ8A?9ٔ\W<ҴO|ۚ=cA U7@KSП甈 ?J m >g EϭM}|'C{>6ͮ)Nqb5 U63Ol[wum'7M/6~?89| c2ģ#X:-_ _98mCc4S[C]x.냟6~X)XWk/U,raϼ7> "tWotg(~C51uc1+mVl[m^{dIZ;.kc/d->j 44>anjc#. K?{O#}/#_xILں`,o7w-!x0H,<KSԿ}rfD/W>Q/~~l|WNE NP~-G\釒u9o.Ď)IMe_?+E'跸#)wleƷuI{uW &ڴ6s}3׬vǎ^ʴ_c/!)vL?f.bh<찙:#G -_G*&ȚoE:oLlZ;ކG{KL>"f^iGN8L8N7iŎc [&:B=w[,p)tGmcwW5_b,b@ڬ:SSy!ک ]5c~N>|hkVReƹ'/):ig_X\Ǭ@*ŹI '0;(.՟ '64 p˕'ִ_q: pqBmNLWݻXR@N n'a N"3r >W#. .@Oޜ]˛.t;*ԑzC]Ov1٩GI ~}l?/yT.ZeNj>3>k ` ~^N雾rb=Y<WooW~7r^~9oI_~Y[N0?>o϶qɯޟ͸yO?k'6c*p*p*p*p*p*p*p*p*`ʏ!4H?8AQX2pB𲗽r2?4ܢ'"IƓp2 QҜk]8A ="o& o[~Öq@=\}f bbc|/`g_KMb$5l4}63@<-npb?[I~[|8 -b$sHK:IuMyelؐxDZ%c3GÄE ;leT횿ݸ£ޭMIfWfտTt0Gv;+Ŧ|b6Ņ1e\$kq5 [7aɗ8"WWx`xr~5ͺBId̙ۊCưk]uaVa˸xM]8ccX^o:Ym̆>cvz=}K_ԅv+qClm]}.A,z[s+5Rw oWڶ|;V یc v6xЌAY6'bAyD6|q'f:ŎEŇ_> #lekIcK6SՅ6}>CYv&oaomw:MqNݔGеqp@O}uMַ;T>䍇MYZ^O;|>Pg|]c뼺ҩoKei;SoO5?MŮ5ҖEc&x\6_.t7:͏qeMF,kKܟdSF?k#4DIa:B~Wk`]~S]mWu?#e&q>\=j~, k9Muom ;}O`U??\%|ӛt9y:)xk^[-W'm:̥>"A/b<3ɯ?S?u |I>tR{*p*p*p*p*p*p*p*p*p+pN9A$_/E8I$Ɵy'}<[cD1n*7_+E5;x 1^i"SGnO GWLb5l3>n61l:h2.N^kyf,`o~O9&̉NsK_ɳ*xfjJK.nmW,3>J~D8X2fm 56A\ G~i8)Ugq@6m1nz6]̟5;^C7{2Nx [8RP|-ծ勼~Vr0}InX455v9iB 3`K7Aزy# 1;mϗ+~n/,`/Eشڳԍg~~CoVbvRĖ圕?ň~{a>!K[e)'.^dԟ+f ʘٮՁ>=?q[b⫕n{mՙƂlm=1TLp35ۼd ڳgŷlmRߞJ><1-8x\SUjck|)ƀSOq1|?i6KcIm#<'j]L7+ƺO]~xEI,l1מ[ykW|D=i|揸&a+`pIՇk|dqtmƕvsQV}Cw-H wvbWbbxq>˿+`1vp# { p`bA5lْO:n1}I|} ԴM9Y"j&/1L]Z궜Ma ߄TnVkɛ7RqQpK'Ӹ|fYhu@^.w qNc1X'GxSuؠEyԁ|7;me|,d^?RW̕m_|ڡǖG;4'b?!cF'f/t̍oͱ]e֡Ű걯nr^ ?ӗ o˯ "ȉ//]6UNu0v|6nocP./M_m☧TDe G;7mX:ʚj{䭔86jq3Gm0-p&1g>ڴWNvP 6q/5ms9m:`=I1q1~?-MكŁ> G.EA‚ԅ3GNyn ~V+>}HXđQ(Sf'?.6S2~cXRO(>>軭2x-u-L]r[}>2_sI8Ewޫ@+L 9W r>:ۛxNNNNNNNNNNN ܫ@JyNطϙgn-ccSSSSSSSSSSS'$~~2v߷_OB-__qyNvکR{uȯzV@>C/<˃Ȝ=LXsدSK[aO<Ƞk.c"+?9oy}c2oݪ/$K]9j`W㉩Ϥظ bN*f勷a3q_usjG2F2+J~vqp[S;l;<.cV$\3F[CxjG9㴋/uWZc<+Vrc^]: 9LNq|KRwa`޾[OƭE!oorlg^$_5H96,h;9'W[[mClGƭPV̌+[jod|[CŽc4c.SV#Ŧz.{muz zص1<Vv v|Z+V oɃ>r.=H}'\|u|%xo5sQG}xTWbΠ!qG.߬v'M,֡WzE/um้TƁ:xW aG> gb⧭ >!zR-('g.Y c~C[N->~[AYkckc'&DZ>l:4 KI;HqY1VN/|dT,>tW쐧}ųf9^軩RdiV_3I;^ÃG=k]}Qɱe]q۾TCܙA'UgoCew3iĒKMX:`g-cFn&͗y|S?Nn򡍘CI6oxw&%_;PʍW>zPLo%bҖ[1qTOd&ش]՗6̯l֯ } Yc6S7_M: vS,=x;}uV_!qԳ/UWyi+ꗶqyڈK64O,kmQب4oԽk^¯گ]NKr~^j_7v*AXeEC:E]Վ3 m ٬aoibE=; .=Cۇ=W/ЇZ+i &q_k/uI%.F\mƇ=3= KjY1@IDAT>X g@<:5i:3ǼY/+ :kπVn#nE/1S4e~xk}ˆA=gZ8s#l2_Ǝ?3gҎ8<~\Ӡ-6N`5qOv^>vS_kcc{uq!=TS/.hꊏ 3/v==kuWy 95E| s.2՛'wo󄺹>е}|lԍq|&?A[5 |sO߫{r`lݻ핸} ÿyW,9L6I9@8/&h.(Bv0+Ub=Me_gV}SK,+Ů}&v mq~oyL&ގ2'ǹٷ:|hC̆lGKL;&;dfo ?kfoޤ &[fi⃅ƛ/9'&cڮRƃ~b;`kئ/c@6'R.n|yqMXN|1~m79 =[.I fquW>ց8'k5}: ,E/}PO~SI '|yeo~P *p*p*p*p*p*p*p*p*p*p*p+pN(Wz[z9 AyN۽ܷ)8~*R˞"I;=Ag۾^4yz4mcᚯ㦼-W[6c?Ņ5S7nml;ߣ]~w_5,>TήKV9`,S.I oXz-oy˃׿A0 Y/. ʓ cb/,Z3?Cs1o[:HᛷaeC]zko;TZ5}YNɗꚵ@O_&mc/&9MlЇ2̛G?cySpm`qf{q/x(>pbۇ1xO=CxZHMoku&yɇl?S]^dRq8}(=ng{.Z\4-nlOx6/q^?iߎCbqIy@Gqw2tӺ7ص#KۚQlE= ]>juv7O {-cG72]~'N[cv` w2|A 6_CE6 .unoVijoVZE'sϹbɆGq֣]ݫ@&+_:x:+_'3 ]Ӌ"cƁ̖ubC*C6*yg ')f62'>XȚ3|K[kaYf.bRoGyRdϪ\/5Mm|Ac;&\7Թh=Vٵun^r.W]=sJf_pNNNNNNNNNNN ܫ@/wbqKƵ+79:o8g )$vH r\?VPKʍ1SؚY.l1bgL`~kmuU_5,adG Yƍ}&1[xm~q[k\|(l>؂omW,lҷksN# {FlmܙS;_V[ݩ>'Țݣ͘>฀9?7Vo+#[xK!zh#SrG[>/7iθNfog?hO>̱['YKRb1kqis: ~WU ĵ3P؜v*Ɓ=-R>c?. o: n~KW9=yC = O v`Al<+]y/ ob.<o||~'v^ӻnӸJ_yN= (`|pm*wxPjJе33+9m;Dl Z{V|y* ۄG 8mށ8e|W>FY1O6) oڼk>o軥̗lشgo\ly=vm $lBoo5 `r|y>xCϯMqX1G n-decmE=dSmtqH9 ۹g'ޔO7n(ܦiS\/|j x] ks&cB6j^Ǝ^Ni>~ئWh K=Mj~O!ǘ ,ܬVx`6j!TK][#Y#[ăQڣe #3(&}uZ:7js ֈ/c>k>yg_Z͇)V㑱L6S,b$׽u~| //`j1]v"fcXv>at82~D~&VV\l3nlVݛॾ ʕl<la~9RFfջS]pӆxAs.w_C:m-mȥ6|%/[m'ݜ¦&osStvjhCxn $uM/5M|;78mg+ƿM|&6-k~iv[zŎ۱sġ?vk8$?>Rg~Ep iݑ?`,Uqgΰc^^{flEZiRe'Hg.VӗC⛞E;˅vo\L>=flеQO[i84'$c7c1w1?8C?c+?t< ijnEvy Xo:Rkg'>gs`k4<1a?fb>K^[` _$n8׼{l6Yf_ە//XԔ+K6k}R⛋95Lvd;?ʡZl!#6}Пj4O))kDϊfk y߹܇}BK VQo)-?8888888888x*Xr.x~xm$5 潼MN2 OomsSix{<Ð |裛boS]io-߄#1_{'q .nd5y}Lg3ii']u?ϋ2OAx_GKgl7sL_g/lX+˸Rw#Wݛ7Lyk_]k[X9ԃ:~ =bhǓYUd|銳g^b5f[jx0 x;&k8}aT?'_/B7) z7?:_X 8 Rͮ= }{>w/ k/omtRf|kiҎ:V9٪+z/ciG\/yK}bsHqhظͶ;t|?DA'戁X4?fi]o؁~8G򉹭<Ɇo|v;r+1/}hlyq?|c?Mԁumktmė3<Տx7};rm jG\e|ޥ>q Z+>rᵷȂC;';O=9a9D{y?m:\qB^Yƚ]~,9[b]?'[9qЂ̅G}G?GZC}8%}F.bqE|GVƗ~&}}+Wq+&QӜcG`ō>~R&>Tx>)qW֕hRc0Ɵꆭ6;I]x6}AFcDm;eJݔͷxw/>9V)O\dkiϖ\`}dS7z;װsmg᩟8i6kXMxl2x4Omvڤ~;0lM]}yYO?tm:/|lnG;5$0 _| Ϝ ˄E2xx N\Ɗݳn/g EYBY 70 lm1 ~{: bk_8<; +tųul-6O_XbbWb+~{x =81nm{l58ɶ6Đ|u8sS fЩQl跸gq\øgc<9qOܴ[>X.6gtđf: ٨8sܦ5mJ9Jm~7/ͻfSO=oiCg19Q }o{}Ż6/ՅuZP\iϢ6F?kk-kq.ǏئLLtf}.Щn?bdxf>Z{; KScXr\ixSMWnhs: d`yq<w]Ac_x5iaAl 7>Rׅc Ifljr2ɮNυx7V<}6Om~#kq)-)GH4'#k4/(/ ^i~|MqO||O3nsR5fiӟdl؈ō<<~-&!qv{ᏹ~um|̺>[}/l4^5?M+޴`|Bw҇^Ɵaɰh7H;l7Pk6˼nSbi ϜV>mVy#U~6/Pm3#\'ᡟѦ%NߘEwm[|vgc#VƑ}ti/}0"M@̻ wṤ󊍌3~y_msߜO*^R@;H?:=bD,E~!o8Lf=ŗ2wgxhqe/{C_E-p^cEk3ɧ>d\+๭233b_q<1ČMˋgn3hG=pGGlҚcD^9|l1.>P8k;`>$m5fb}Me#VVo⢦x5|˺]yI[ sK6mcx\{|Xr3yoDqN5>G{z|I}s?s)Lx)wgg\ӿ~9G5tw'إ;>u >$}tA^0.;^zp0 r=(("cp9nU`=w:vƖ<Lf>F\ݵ/B&|K:TD85>5աp4+_}dԅ6U Yg]6[#`x+.ԡz}hv&'|0Sn nS?pdknK\[uQߵ/i#^MMsWStvu_pANYWqK:NM|[)6'ݛ9udO=ibɗ3&hkLiC?kngX:O}KpZK8nbߔZU?W٣S[e>z;?k^ߊuI W>3>&߻\o˳ oz_[ JiOJI W^\jx[˭0+ nn{ _8LU{u]}׽u8ɟ} +s>s. E^'k lONNNNNNNNN ܫ@~ O~5'A`~Dt?4 Zc\X3M=wk|xn+=ZӇK{SSvue|=#3偏)|s.qI;OYOq'Z|×^#uʦi̭tr?૟WŚȧMCt๩GNoyaƩs͏z7mu6NZ^%6w7<]v1`1L8[Zcm6]ƻv?Jb1֬zY |%^['=Š#๭kZmlĒN:om/r1Z ixB&&CGOx}9FYtrnjwI ۔x]mHyۘ\wa N ^N9q`@p{+'yh~01۽M|bJ?osm nQ\}j!G-y )tm赺۪O>S<%3~yK}b3vyPrioYSwȲn9s~-l}yU\򉩽P?S&\ Qoڈow/Ֆ>&9ڬ:Ș[3E##CXix)>E7yf\b⏱~mvZqk sǎ;6x-|s7Pڒg{ 򬁼~"#2bc6[3FpM}tOl4dQ;?`uML\cصJ .۬3еoC|n>xwE[G*>zn.{c3&_}|9G qk<%NbOvw]YDac>D,P4`ϲ<9Ƙ]|g〜leMK6,V]9_Ab^e^Knq5vi :%vۗT$~A;)A+E/$km3|WuçvP'SSSSSSSSSSS$b6NEUt<䐫bcUveIoҤVeۇ*߮xU/ozEjΣ<]wWi~gkqN2XqſM` '7uMY %NJkc vS,;_M_Sͦ))nos||!Fl?VOg^~>6/wvo1gb8ORi&>YadosR |RΏ)m&]'"kYڵ'&ujr(*14?YՎV'xm|ok:wq^*qYv,|bcI;!|cAu˲ }oo``( D43 L53D[۷Z1]>c}{:j9a渼2/߷:|_lgG;+t6;R(8=7S_F.z_ne]o[ ;t6H:Ĝ1 CvÇ =jϰ 6mac0 m ;^9Bl6?zx zV uG$O E2(siӛ1<7>Ȟ!>η}۷E"e]\m?c+0nnm.՛kN&-dB>ぞMCcc`Zx3'YsjM<&klp|m/x6MxoumEkZy%SgRxL>>SgKstɅVwcKkaҎpD*'k:`1Θy(3nRƺ}.[OAgbW˫kK܌Eƿm욏+٪s_ 1gԙW |}G~i8s;MUlg''__1( wP8^z'*p*p*p*p*p*p*p*p*p*"!&y'cNNNNNNNNNNNޣx]^?{yp>888888888xT]u=?.8hFyL[0}?ϛ?l\5]۟Gmr0[k|xmYkQ26c/,-Wg+m\񗾍AY1q<% Vkvi=mxW"kc~ddMG||60yo9%k8Żc懲@ w ?ٟݽa;;hN{\`Q1.M$r|YikN6?~x`<17 FovGP2HS^{ Sp-z/f o %}ޮ筘)'fjqSKun_mx|6ތS-zW^y-t6>01Gu.|j7m u/#_[[>OS^۟ /qC|g ko dn{1)~Sf{[dUwvȉl2>s&n:e{$Xļ looc>yjeٶCފΩ7'"f'hv`̗Cč,kGL-6&\| #M慟f0[#^{!`ZmkW1Dl ,7,&1/ _ys։jDV7ؑ%z[ \5lfb{%Ot-!m_} 6wƇ {bߒO]}H +,l6yӝ6~شm#qVm~k 3rlK>:"/l[|djt5YWŁChv6CوT]U]l1$M)<࡟yNm`09 &V'7Ϳֈ Ygl㤿ܯA|+2t2b܇/>8λ"3?3?^{ww ?? O}Jbsک{@^U?w9?i^3~qOV+-´;0K}3w'tg~5;1^ōݩowi?[.KW_ Rɑ;Y#nv-/cHό>>ҏ}~O(+퐷\ѣM_Vrj ~/c'&o<2G̶a;sؤ]Յn1G=y6~'6ލ7}pm>رk˜26R: Uf>xն8'}e?!wNx'b~Ma6}mm\Mxާma9G*>đEyL x[{j[|̺-W\rOOw'?ӻ?_+ _?O=?w /66XxX|[lx~@ Ɯixm3''6ɹqyo?fG Bp(8Y ֞!ߞ!^,N1dgvMo{VgӶȧH"˖z(Br"kn;maϸ]3ڞiv|ACǫ:0_ZcZЅO|)'6a֗z/n1؀8%.%m_#_l1>f۳9qNm|kƉ'/~"M mx,tx,mJudb/M W4ܠf&xI_abaO gleŀ^Ȁ ^A]X-Fx20oXm9a2x|0_ၹ+,T>rd<Λou:E]㉷QmSQ:=d60 ^F(OF#1tڷ_c6U.oIe_-Wx6{d[<몿哦W_ ~MkvMp)xchli ֆs|p,qW7;Ĥfbt7~Ǐw=GL=Am6_{jhvW~CIjy5]iOkWxW2/m{- LduLP|cRw%~xvX艕xoWy5}|̵8Mְd`&ڜ/8R>RR%*NHڢٵ\M[&y6:חt6?ț.yO&/8o2KŘy.cl^36-=={>qV"[w6| Dn|q=K~D3(]cG>3͇>?矬TiQNl`ٵg(=}^'bn|E-f%hk`7< O}ҎGrq탵/ '`w3|=!e<ςZ.Mo;,92H1z5mNk m2~٬:0Ag|H>1mnAxsk՞Mzs fg=>y1'=2]ҏc܍G>McxƝ8Oro'/- ^]D۷A%@gzMؘG xQ>S2F_Kgh [({p~]7ċ/>](?eկ} q\ƐEm۬6>~x<⚟}h)»Cr|Y+x4c@.O 5 E?):M|󦏮 I:OG!z+zes%0|ĹMkNg>mmc>"yIėu*Fڕu ^üo|˜-OtK:rs =^Tq: Ʃg` -1՗j|/Oѿz͗5h!Kɮ6e|CI CgbnV?Ck0=dKij/_*}k'zS9[r3el~777]mE"~}ӟچq/s^@ܽ _)U=.-9SSSSSSSSSSS/Eu>uNCONNNNNNNNNN^ <@qv_`_:_lst5e,'`h6^Qߛs/)fR1*u'U_dV36-y}8Sl:^7>`_X&C)<ץƑ1>CbMY;lm~)6C~{Reoxfa31&E֞ Z@IDAT2#Xzlr0W# [2ol>lZOm7^!>s08rC֭1mq9RnWl+EPNU|qIł6[~Նck/E o__x +r&[GT1ei@3AXaa>PBF'x ^VtK]uH&_jL?l%ym ]xwF<翈I9'&`øPhk†ng{{V0}YZl_]{j<~m ^o~k1|l/qG{C.xCmq"ՇPV3+ؑKub_Oۏm ?&񦾶~AooUE59vkxأ qIYփ>5~U@Z[` mԡ%<|ĮK>yQgڙ6I|G,.Ewlbc17۶?9CԡryW}gy*gZ7/&C?NVL;sEfECě<ϛ)Cm2wm{⋟g\gt6?j :d.`e}a>8ھT|V>mrlhQYxy(㦟`9|XK3~3bLWi?F9ԠW?lo4x̻Vs7q6q3mlu;|lZ\`R?Pkr1Iӎ>uM?شy"9a ?8uYG ?L_i<㧍rhTYk1h1L~K%&o~Пn{X>M1FB8?\.aatps"8"cbq~9|3d >Tbm[f3Ni~glƏF?/Vaw%F~65kuCǺ&iso}^SgV3i2m[lȚxm\łs8.G=iK>]%5Mkസ7}06nj6Zos A<ѿ5o1n-ω$Mm]ކG̱̙~{l}ɥ_{-#Ks{˧rhoIĮ/Oxж6.+5JmRN#K= ,{jhOljNVNp/dCІ~Z>m/yQ̗:0n>Ɲm _VOZ\6%ޖ[^`f|o16xm܊֩G_gVWS,Cm¸m1C7󀗭 ļOagӿ/!O~ث#Z c^b}EőK:C|+kG8fs]e>Mg7']{o8Sbo_],Q8ONNNNNNNNNNNTc+ݕyj cM]<|iwhs* /cމgߢ9uA{rװ6No9gR?l[c#|ίRwކnʩ 5alڦٟsu@8ǮL&\onlxf1cɧf:q/~o`gjd ۳QIyFmC_T.3d؟rqG|mgT>аk1G?T|̇~O䁏~ZԻmk|lN>0ЭmKأ7Rvš^@ Z,9oM y>n ~Ax$luh>ls|cã[#>3/3m|p_Z}c2Fؾ? ?sd ښd}=d[go}|%)u"1St'lXPsQZrcP7)vg|7z| N}1{ȻE7,chشbRmUtcv*|luҏ1ly7iP/C1M'uZjC+mՓ8Ĝ2S<1>zx3n|ƛ|='mgęrObvOz[5שsKs)yȻ?;5g!6Yk:҇6nf;h2mRFlٌ&~{ ^[\{gqǟXfy^(r/g6?mw1'[k1>v3EM_M&ZnoouJ3oŇ.x݊Yk9ʡ[N[-5gQo|0Oyc0Nx5Wx1^錡abjeݪky̸ow%8[\ ˟o=oGUEo6 o }u#nY{}ڞ Ynn`gnk7K#ygJ?lğgkȚ|8j~M<6H DŇRk_<ڳ$^&omB=k-unyxy?G#:k63143X9^/9^w//m}O'G~ԫ}iUc'TXP,\Ydɣ\m~`7I]ťFJ[f%:o2ĸ6`=|j ^7z[ixi5~g)[q{_G)#gеF6ib1g ӘSzb}j־ˁٰM݉]ЧEV9ovh~sr.uIͮ KAj#qds27y=ֈf#hsH#6{V Rm[#ŭOmoE,y.YxfX|3yj7r޶ [|!gߠ{c"{4>ƭ5u-6CM*fdÇǔs?ڹSQYkwA ԝ<ŷņlslw>lA~S\eiGz9$fly>sQGഺu یC#-̖CzMqocХ=K.WvK [_̆١=^[!OJYCyoyNq~5~C}?d6x1xK{:IKLy9˃£>9'rOn3}n7규'6rjz}kk k61ѧi;1 s>M"< < >RYpƻ2;D݁ɻ@؈xO3'_^2^\{(5'k|fLSy7vW2t7s]MC|1 s98G8gߊd%U 6?Crl!3|϶k1n6Ʊf9ɮ~<c$U8x66%|(k-mZ6='f0؉ykT>lO?Pց>q~?oF-~Ok8x61k 1'8To\{ 1L]ί6/釾|G6)\_/aOk>NI5yo弍1ylbiĝ6~5|X.%6d1@ϖz0y9.ins=cxW}j=ԣOZk9~r C<}e>P_bzoxƜq; <N6lvէq~Ak?#⾪)Ìy[1ldO[ h˷&xWts>[ ɴi2v*Ot&Z ļE1Z|X)ا8rj#˸\>i۬C,^ 6ں?eOdžF^m?/ &~C'4[M-~ꊜ|N7dس&Όq7k=7|[g,'< Ǘ76eB/W4\O?3o17ɇGC)TioWlNWN7?Ӿ #uۇ 7~>ĶaCoiXJ;V:vl.&k5VY]ŧύ|wO [j5?^[gȷML1&Z|m:h<'Z$:F`*>eB&'f1qsm }>1kznX2'[ą___R\qW|}ww'nC O}SwD.̏! *Á=88888888xOW=6՘it/| oEC.Iw]3<1Gc 8bSSSSSSSSSSQ1oK.|f{ۿK===w?׾zS?Swc|-I'8yw @?Gn=s_UA+<~DU>0<OLobr_7ºhucz_ɚg[k[,Y<"% ,mVOu^% ŝNIֽ>:w'rlk<ҾA ~i<}ܢΕ-[\}3A}؜, .&0/>[X'y{pww 0;q|gyЗ_{#Vv!(C635eNh#UW N{ '|>}|ȿ}xg,N%Ko_Ϗf-Ї>tyD? cnoC>saSԝrd<{3 5no4}z9wkr ;n@gc~?9q5H?>1&6OO)&a{ &㋵5O2`\[>Wo$n.ɏ2{j}]e8Gk rѥ3 ]pOS>Ji d~gC= }oS_ă~%\؀xfgR;?_6P|`?|Ssm`FkqD}Kij}l8 =M`r|'_&X`lBP~2FbaG4?ؤYP% ,I%71M@yN87տ41g?WY?7VeP·;t6`&nME92yPxlӶcmqSro-&?[a6,b#m =1SOb6NoOϥ!mͱyL ʦx鋾9M}8K>v˕xMRN6BL?\Qx󐾾cWFCޤ)2~8gN59g>yXɉK^zZˇ>U-㖪CC1Gg7}K#1O[٧.{)Og_hh6oFs_[RNߜ&ԝrdY!`}Wy;#8 p~~ww.֯ʯudR"3'r畻!lZ\,Qش;{z*p*p*p*p*p*p*p*p*p*U\}~ԞE%ڐ @ܙbP;xѨγP;sޕ?@l:'ߔwĒ|(2lK}K&-Ƌy}?G=ӆ+ҿo59ōm,7$Ct8'>dWqN<&<&s|VЖ177?SN7T6F||(q7:Վ6 }@66+5߿-#5"huj'V+>3}ѕt2Ĺi}jֆ8xy sFm/'%//wS*L}e\~}ӆE!?/e4 $x௝؜xf$7)yb.'6G~!Uk5 >s!PO}H÷c1wM=esM:vi_ևLLJL9|uƐYq0FF2m8ǎqox6Ƕ]ۇ'~ ?0]P%c?X";q pw{1"arr eQm K?.*mCao{(/&d&mn6) UE6ƈ#~ˇR_\Rofi<1&K[`%ŀ/.ٟq5?ȱqM}Νlإ?Ҵ}rjy\aa sLYKAđj %zWqc5}odsw?)m SSSSSSSSSS\s yՎ>sׂ B4Xk >cr(IK6W|o4m!>q'޳-B!>&&q3e~x㧞5gؘ~g _<#s?sO>߽hmd>$>2-v_)nipAm~#qᯬa68.ؓOߞgN>ϸ-9n9q$ԛ[[t2 ϟsy>Kc1,koooÎ8vʹ}G~뿾m]0[~r"늼M]-9Ѱk'ӎ23Rx'}I>X4κȱ'6_u ^arx5m wE7o~sy`XϿcHӎ5μok=|YhלWKo^Zn51ѧ!?N,ϭLn69iMMO>򑏼 r1.fO_l"XD`#ASO-2m|nl`=/1^d1#l gFt7&YʱW5^mͿgՇ;g{{Fjl kbBSs]C?uyoq%~}m|98󡏍vH7>jE^VN7+~x_P?c9q-naVr0e|ȉMk<\M 15v'mq:\>xA=no֛6==q-Pv*p*p*p*p*p*p*p*p*p*+p.=P> ډ˾>w 1x/s꣝yeWlo|Y۶g"㰵7?i4N,w`Q?p\p=f5EO=Ӧl䕛<>/Mܸg6Z}K\EW{*5lD>11z/vm=ŋ5xUMb;vzqTsXG=  mPy{N,l:3&dĉ]!4v&oZ&١zYw>4cvd_m]k3F6ۇ><9M<س򒟶V8[ {dm~om?gC]sԿE%inLx-WtL\9'i_e,H?»jtxm]0s.cc9Aۈk ~Q&&1se66Voqx1Evv0ONNNNNNNNNNN ?ί*0@mwuGhO;;WvuWBM^--y5!GցzMxI\5# z"O!^fm.}ÆGW_|yL}xO5_ĽRoU|صyt7)*J|#4?,_1X~K}Uڞl= > 67ϴ:\g^y啧b -% t6j3YW kʜ)qO}c>xqk1g~R-ck>1sM; rpŅro.ľ ͶՁi9aAݾ/]oϨ\1EZ)8mo|#yXASkjpU&Iq.Z}'5rjbg_M>gŇ?p#q3v̻mXG;7)8{쪘}.=i^)bO~[}l͟-{,y o?0631e1Lڤ */d8Y;uRǸo~R3 v%.J6?!3Fmv[d~Jg ו<3O[ݴ<цGӾ+Cvi[k2j{['/mN.O9eXékRpiఞ95_/~y0&醅Nec ;Rf!/1/Tɻ9\0e]mo=7Wb%k/U%tAy(5iw|ûoN?oVn>(SSSSSSSSSSSS"m)qr*p*p*p*p*p*p*p*p*p*p*8*p.8(NNNNNNNNNNNޖ gߖ2'4~ߍ6\[mqkځuțm]ņ_lWvޘ2쫧oIz'2£_:u}W_;bh~/Ev݆C]~rnN 6<֡-s+ϳYWcK>b=)4?q̖S9rlROj4>:ByNym\mv2?yqg,UI|+۾6=r(q܊%gcm' cSslȷl1S___FqÕ͘ryȃ⧽DZn~AfxȗNέ6Oj#ml#3ƉxʛdW6ȱCAsFsxy+'Ї&@X :=jAa1u⿯moo#qdv4|⥜|#OU!nGs ~bd'n2~fce#ms">/t6loT,l~{%_x]6me_=}4y[ڸ7NٰioqͳG|7b#k6$|crmYz}o gZKc5rhźHѵ]͇[g>cC5e]z1' m+m+|TȶFƜkoC%oo1+kK9'nO?|kMxZy_括Ox7gطIٹ|'|`QƢ<\ٰH{.tk[ ~ۀo䇬mRb;-͏g_ԀI5מ8l"Kf8'>jqW+K\e)?Œ> OIgLJǴ#6˭Wtj|+˼JD;|XaL*ζŇNj'I:`aک7ii]ob@'ԓ|{hE1Jܧc:?i4N,oT͇oܯW{~_nrQOG!n'~t#ԣfvf گCض: Ň|{jCٗyv+㘍n1~AHj>O#>5֬>k6ĭQ?tѧFGuyO|iG{ _8QgmM?[>`]grwց~{?μŖb1#ߞ=s4:mqzfJI!vcgb7u`q:3Zu9G6߮՜'Ӿ~uv6g3>0ZlxFc6w]Hq@A_Wq?B >XĐq;7|u[MPĂV~N^-&t6l=j)e~g_Ձȫա[>ijn:C.|rb@[[iR=xiM%u"2|[ S!-nxWqkA쳁EY6Vx|K?/l2pfk/5I7&\&<ˑPm|tbI<۔KC7m8'>5~%fˇMkD|kuV|чvWqFOJZɗʇflSγf S3rN|-6'~Žskf'gÁsyJK1Ǒ1^?}y./ogn48e#6/׌{5Qpokhɛvbal=6`4}mSWt>FOTTTTTTTTTTTTU <{+]DowO&Mc'mw GЇvoC:k//RN?yȓ#=kyal,KӸO $?Mk2O4q͕בĤZ#Ywy>Ă8Lv|kXMwʷad Hp͝s>}T=1g>~{Lm^#֖s#PqZő£ѿk_{L+xL-5ɓՅ5kgiD\u~7c5vmĐ_<^&z._z5ޜsߦۦE彵g~asߊQnm{v=jl ggouenÆGScsųH'.!ᄼs 8 hɟ7m^>gD ٕd3,;uN=`n~Om|ߞiϠ`5 cxP|K9vk՛x[>Cpxy^Ƥ(uXϬ=ㄌ<>[>bzc|]rlԹX9ϳzrln`xO?Oiwx8AgsnegeW\6V,kcm~o 6iOƀ~OX4N;zNTKm^w>sFsW66[ iCL676ۼzX4tf 6[-GL!>y>N,h6R>6W~^;oA/s< /cV0Y|)1KԇU|SֹKէ27qꋕ=W /^5[XZ>WȬE瞆Vd4>V׫ϿQo#;M|FKc3t市-}>O2|څ?o7[mؤW㡎j[}];ӷ {'SSSSSSSSSSSSg|SSSSSSSSSSS"3W<̥;/ߖ_|mV<7, 7oY=UUjrx[>h3{te<^F,?M8̙MK˵Ļ:nm5k5Hw{{ܔPrNPK[M>oJ?8eh`2wy[ cNd??'>y|>kc<#x{/Otb~09O}{SQW_}/1ꭊ|-s`Z:]b@mhmm[OI ?y4?LEUy~7j2p5Cc?rnj̻Z4۸G|Ԡxl76|0_hĒz:2XXy8[PEM_in|m)A%}4, Yj 69`K?~oҏZo>7saڽI7̌>zO}7}:؝< no| /@?NK ٻRzG&=ilX$_y!V&NN|ޅ|g,fL'ZVVVVVVVVVVVVV%͗;0X"O]yR6,}nWI"5趫[ƟX?e'm1sjM~[|e_VHݙ-F/Fr#5zb6Șsɹj)\VGݖԇ|m᷼A6ncV]U冇őXڶ|׶Z/R^_ 8n/jm Dk>ӎM_=i?>_6X6CyҌ~{}bnvY jXM+23tEY6&7[MʽwixyF`{1_̵10>o? g& V;|!kR=[DmЧiG\L@̯߭T~%L'|Y#ۏkǭ!'>s8l8S:p[19ɃΦ﬷ɇaMv3i.m|82maCqV+2/ Mf״GĞ2nSeMxإoS΍ap3M_䄼})+Zbc_ldG>e|Y[7ک7)>gkqCkZ^Xmys[~#Zܘonnnnnnnnnnn^\#ť o/ȋ![ n <}iW[vr91l#J|ٶަ4x3<H>^l}OL{Ȧͣm~3d}X|=oTc[^3'Lo> lxA[;&Hy(1v'<'[xfl]{LyݶZ mֈk{YG]+5T跷៸>yUy^o(2i~m7nbC{ 8y ^K?olr%ނds)mކ>mXgC<ȇ}c{bB+B}3>|6?m?‘R1Ά}u퉭βgGNsl/UvR msʨ $#6a7[/ N 3.ϑ1Nw{KxMOgJ>ɛx\HG2.u3SN } Lp Zu}<w~2圧}$%e!y #~O'5b^6z#-ʍidqY\,Ķ P*z~ledoXn =7ik3ġlL82F6/ߚd7}4|n?g_2b(X}mK?>y!|2bA?|x 9X&Ę[ݦN;jG ڴegL-7/{oo|pڗLYe>vXCyڵ|]Y;bB6uH|6f߸Qpdk1g1j-3enV}|1NczN,KCB_Iɱ|k#[5Koc7[[[[[[[[[[[[W%ť_d+4䕱vՋ+;);xϾ8/*<|eɌoq16_։lkn"Zv- _?'m?;iۛT,2ҿr(ŸsNK|^~x | 9l7Ϳ[B- M8-fxޤm0s &v~&ɟbNvY#Nw=߰ooQ1+&pzƉ"H=ņdzm;mPR:ޣ{NҞYH-Y<2>|H>4.3J,?6ݻX7~ԑ!x<sk?ZK_T_ w<[1Ӷ=f\R f}W{ 9,磏>zصggqϚEF|CgyL.kz+$/tt>iJ ~o'd[U,ؑl1o;8m\Ů uݚu|Y]lg~o0W<ĝYb!kw:gio?q6> krG*ƤGqm>!?ϓ67}}K G09Fޥel`k~G<(qPÌ\;1m~s8f??2OgKp ͦnojupC@]gv$r(7yc6'Ϭԗfx؝`7X7)Mn _qik}O s3oG׌~;u 6Oy?1p@~k.Ԝ-nnnnnnnnnnnn ܿ~aO,WXՙi\o\*Rƛ:ԙq4uv鄅Z;0Æ~'qKh3vǔ-6Rm/< }|K_4m6ɮ] EKl-OcC^˫k8S|kXʈ1A[Î@%6mR/1sc6tSZ_k<0IVueOd[ x"oT[|l~%^ӗtk~keAN|_J~o3mks5ڔwꍽ1d|?ōXj+/PwRdxohku#6G-#|ߗv/#%g7?/Ciw>T[igio|_6Vhay"oRll<6q|jlKS3(wg3%eqm$|5oεg/|;m'myQvyx}yuM[|hX0/сO<@-n0sޡϳ8m񃼭 ~_`ñ5_6|?2"bhuo0Nq X6~g״7wg7i#w;Mɵ=r!l΅lzm>ڳ\mj<Ɩ~z/#:¾ƽyuz3>ryC{[M}#Zl/x6^H?y`e^صgk>n1{w-/Ϥ/2K_`#:-B\AK(#7-.0!_6kuG [-ĞiLT˟t# v|hsbԡRC-x[ -/iL LE\شKcǦm7b89u&4񴘴C}ya TI]ϳޏ=O]}yP}lS|>@>$Gs?ㇺyз5?rA:me +(Ӟiyo҉ꪞts91eqW_*f+O*N>|+0u/E?F}aSɓ 3Ly;W_uE?k<~bÖ8ƾ&cEM6qȑ|a >^iC1ąo?m7ڶsh]򁧬amr8em&.v-\#+V?|CxNNyް!~Ʀ?>k;_bn)oa`5FZ>Jqoca&6HRmŻ=Pmgc1ooECyhm8jkn:F޼ .෷j.ꙵ6k66xŇ[;~AgC|YKms>o L>v[,(~K5o>Po!yO_ƍ. /i9o_ ՔVCw>h΋uC'c|qFMm9V70\`?l魐Ss?Q/|g^iG?"3N?2eW3m~6iAҎ}>1?kaY6~->=[.G '&G'<+>o=󲽽V@ZnVWd|޿K8QգzSL|FKMm" UYM<1ϑgMcʉkkꁙCԟx>/~M{~ꦾx9qg oK3 8-_x8|&_$tCY|1oQWو~ o3o>m2y׶AۼF6k?2NN'i+y>E2Cg#/9ş>wѮՏqca}9N@q72l'zɬ;e>ش8S|'-fe<?i5JrRN8'}lY夃m?mOw)+p]ϣ\yHzpU5lW8HJ6𡳁?Lװ OyEVݔe |Nf+:}k11yxʳ-SIyOv 9)R!&bnxک'kCɌ:xhD=A'e'y[̴#6eƛ:[܍)6jY?I!~ҖxgƳn|z=i+#.yR:-/[mGγVNa{4.5;<>W~?npz6v< 6 x56 φ>P4_bl%&C=gC{ξtڑM?m=mSl17R sqki۞h?'>xvt$>>or%.ME|>+1q 0|jn׭Q⃾?x[LG296<58mN|#6 ׾bRju2̙xy7N2_o}Y}61N?𑵶ߌO=3- Lg}r.?u<@^61G5>kڼ'538m.RqӞD}no~{1S-wtʰkNx7;J?8ڦ>Lg|'<19PN`en`|k'&;S;|85ssRV?xΆ>|hk=\^ډ~mHƟ:휸<>/-^>^.!7Nym~smXȈ%ugMm5C-Myj[OkbBŕj0e=%6i}_6Ay|!>͆\+>|iӶo6lN'.65s%PlOm3fK]92l@Q-W6mO㟰-G[b6_6OO?Mc.ɟx 1O=Ϳij]IXŬl#}51cWCw6|cGK:8_5i{#7ٝi#mtC {+k*툛m8[Smsm/~ۺWR1OqIC?nԓxe]8W}hG?qЭď?lÑ 6}Ae,mm_Z'RȺܾ`rM@j69ӆyxܤpb9xӞ\ky2/ioL}}r{޳Rcm #'l^G 9K>r//<zi7#xy#qW~W/q?6bAzcI'[^<>5ނh7q;ȩmA.đ9gL/t6c='VYy ohvgv`ă2r~fkCa|r1/~~c_M} :l{ooqT1xŎ Q͍Pm#]}i~oG4s?#Ӈ6_yޏ_M]1mu|ؚ8i3}}riVO-p3>1&%fl[{b~^moENqϩn#qd,lv[Ɩ>GGb&ƔǏ1>Nو\iJ<}dARCW_Mɍ#}G~l髝+Oc] {ù1ovH=fť4-yl->x7=e);O}~iEx*n>5VeȫZjR]y)x/lƕ⼺<8[>NYek8m G?}(;C8;|4X)O -WkXߕ6d#?hZmၕ57Vxo~oaţgw?8ob;]ԧ={߶nu{|x3ضFGG<;rj<۳ndڳ3Ļ؀k61N^93ޟf˹8?|mAܙ};s/"elO[kW~<uׯ|(߇Ghj~\sd𓼉u`M|G3?̓q3POy)Jt7[S1GT(-Rlr؊=xtGgف~ډ8/މ8٦ mZ>dNMye􉍸[%&V7ufk k>%b1{Ly69lژɳņM0ymՂP8u-emC>#W { AۤXi|ZM/=oqccN=ju-Ɔe&}7m>b$rdrɟz ku-1e}/gQonnnnnnnnnnnީ/TkYV++4pe]C+7i|RAqu(SGɦm4޴5niMx 6q7qȸ_ꕺS,SGpxW )>_sxڠa(f'Oj=佄b(I)oxy6e#GOc*G_*oJx)ˌC=8 2xVokq/𔛷}p6,uHj3W=svnÛSOc2yqI/c.oGhG|9;sԿtCEX9/cysI>6<3҆2>͏6sd3U#̍g`QgT`zE ?-b6 >x#}o&M۠rA'?3?ڧPF|+mv"gZ[&&~x0ў=DzjܯU@IDAT A[C}yFŦM{&:gNyekԙeoϦ=[+>mwA?Эm2bxh0_d`l[V?~l>s>.ӗ9ceMG퓬#\OYg2kG>=]ۇO֢6+t6̇Oڢ!O5ޣge )#k[m=qؗ'lZ!.QƧîŷhWfx%=_Ƙ1>'ئͩ^b8X|kdRdxv;o|su5{dl{ʑҖ%bkvyDd꼦ؤq6FI+uf#S쏰c@gC׹s총2k9մm^ 49~WkyL9fM5}@aCH;ch|oƳ }:[:eSK`eLCVj[;?ڈx*[}\d)v,CWlC?%:}5Y?}iOn&#>o5lё׿u#..37nx~'g{DbiJ-_lZ}CI|8o  6_ɇqbo 6Sǚn5Gy/kL{iOu8Ago{{IdƗ~q> [sЏ>rF,|6:#e!cg-gH?}GY v_מّ 2l[>lu"^vqc9LWr6\1~jJm1?uAm%Ֆ~oƯ 8>@ܼU!ֶ9kޢ~߾_m~3[Ɯx?x0K|I~.p [&mFc !?AOq!bZ-6⬛P66شas+vMaoq }wkuk.ز4s6gڵ||;ouOÃ&~I/ YWcڀ1cCN~ÃwCXzmmo|}$1=9za{υ'dsO*6N&&o?R7~1;Ax@Y9x:F̛:)^Cǔ|m1kxlk- ~ovHC;ЃOt6d\ .?t6cN:J A6pX[#^t2nOaÑ8iycCyMm83Q|l˸ĂG>8WƂG;kZ>mq n~Ч:ٶ8N%N!Ki76AL-Άw?#8ͧq5zAF]o1gij9q`rh,Cb75nmmϚ_<&|&dߞ<`?V{S=p>N{&Q?sx\Sk6`FOf_q#,CgI?Ԁg9|37mcr~-k<"} YJh2ghkԮ}Ov>~l#Kwo"Wb̺bx/>WM۾/l˴VY)Y>Sg'W!nl=ɎF-#zKXpaJ[A\Ru\lHNo2bSf#O:u%_e ٔ3fmdY+sPj}7v鋸ISþ6Ɵo+;c;s4hSld'?ec͆|k䤏|kymmMGk#ߔ17?|>juT)xZ Mq+msپ^"1 | Kz?i*Bժ͕ӕf~k8\i˫_خC+Kʲ/fZiʠm|tiRF剟Sk'tb#|η8%5HYŘȓ>WN5sn88W쿄:Gi5vȶ8<1&}-[1tkiL;b&8v`WZS^[XOל\g̽}m3 +fߚ\ Y郕|x[a536d6G\mxENaEZS/k -gۆw&)6w3ƗbENo)gy{[;AݷH}ޘ4`SσLkC=/ ;κ3S1il_98l2ᡇ &D\s_CY`g7t6Sց~.FޞmpmNLIڑ"d>wgԘ[1|Q~8y]3ϩ e} yD(`{Toj,88q;ųY7K:8:jxmN/yzf8ЁF=o??SLj˿6!Ƥ#w[෺§n__!g:۞݂ō|הƝ3v#?Ǝh/Z0ZMoyO4-fk'kmxȷZƇ>vAs<>?6l)/vr.b d+Vywk~iMޗDT+easa@_^W-)K9}lᙕ-. Ԉ{׳VxA5!'_O|ϱy =ō,5~.[sRX58`J{xWElόiXm# ~|rpqqθ͇G!qKQ|%M}='dž7u=g>3K-x'n1u{v[59s] _kuՁ[7? > o->S\1퐵|'[ b ~ZxGKʷoa-R7!DeC{޳S=>0~~ك,mj?O|8l_JgC/_mS[k5xԴkc>N,OD'fzCgC$9ny^p 2x-+_J|3~=OWrַdm3Hǎ$fhqk< k9/3]?ݎjuhq3xf|?♍qY i?Ŏys;Eqm?ToY>/m=QQt?go uC'[6䬷x8f#W󜸈!Cg(&{ȃ#E|n󜼈BJmXǭF~:qq4~zGm%rΦ-nt/~iKV팏~'f[?29o)Ά`6)Z@gv-F.[01*'N=>N[[񥯀 jا<bgC6t6MwK)|RriY@M)c?m#~b`QOqRjkekbLJ[-Z}lrO=f\/9o1섉>18nqS6'W>8fP5î)|IF1kya}A7#r'6I ^1^I9v--~779m<ԭ2P¦١qk۰5 ǔãbsOqS#9/c%l6ߌs8(ox9]㉅CxN5G;1oM_S./sn]s>8F#O:6\*}~[u+p+p+p+p+p+p+p+p+p+p+p++pKx> +1^O i)ι5W^ e)O]rjW4Ͼԫy 96'̖ƨmytvO]hkx06ʰ˼?}!K_SslͩVp5[^}F.[>SsviKکekx-oь-Ɖ8X궸[Pxi_m>;lW2n5^KK-S6CN-OOGVGyCh|Ǵ[#O->ǩo`Q撺SιL><|en:BO۾2cf>mk2cC'=tb?ܘ~V6W'gJme3jϬ65 k!/x[l OC[ A{y/aCY/sOkz⻍7643gu<ywZ0:[Mso0Ox6!nGG?f);bgtM}q#cdaͶa/Eӎ]3GW7o~SF[KS:O+5`Z-3O{[,(yAƁ^y8'3KʡdGSF=y7<ԇ'uFzgA>O~`oyk|k#ryDXۦA5gQ^c>BAYnH `n|dय़-n_o?F:B~(}yǗ$s:Fmsl[M'}>r6l,s_0I95c6VWxG?_ KOK(ЅFG1,`kc~d2bhx'C׆ƈ/9j nؾƞ_w9Ul63Nr16xd+kcx跺Xo~A|Ӹ%k._kD~3&.Чno7M:跼#}ONO[?OӷVVVVVVVVVVVVUDƷWknOTkYT{y~k4m VN Xg_:cO #6|϶&m6ִxMf`zԕ=NubscH\~G36mkxL9N56ĖynkYP}NKxțT;锽N8m2iƚh ߹~,)Ĕ~3ެ>Z4x[^~geCۖV?@>`=i)넧ޤ5yHd\C4ϛw~Vyowjo-ȅ̓&!.6u҇vEmB ۦ P|Z_锵\_{6Ժը}( x9NԎe o`oo ڴ|O|Z8joSG?m<ԙ=] o}WO>ullؤ[~'T_ffȱZ;Syv/-6yxL|!/yP⢮-Ӗ7p/P^:>i t6yq>oN;k?u8;}.Qs]@z&qg*?|bca8XTN 9l}2>҉IΤ'}6E义yIC^#Tk+F,8RO8:O?)cS|%ީM__HKsN'G9N2͆ۗVCovɇLtBg;٥.v~ 9|s?Kg5YiCx xN꾔K r6/Kg}m~#`lIГgB[&'kpn^UD Y7mm v2<-nǼr/}Qj;h6l2FRMfx֥1<}4HCcXgO:#æ n1_+qٰyk"|*óP6U ^ShIڶc! xrh`kgiصgg3NY74b,ç>Gzl]1c|=63>`pbóG>Ac=G=>Ocl67}rL:RWˆ}(<)qK#02Oq7jthk6-|KX`q!OXhmPMo?d-vymfÄ6>pڇ||ذ˚6<7&kG>_ڒĀVe-_yA XM]kDws9nʉ* /m.oa'^oƦӈT{6B M'NۦbѰmvd ϱSC?m>lm y/M{YOm7>r09fO][ So6? OR/Q~)`x׏s$xw-_<=hw} \^N'M,b<2?9|m&mr[|ʰm-7?SӜ{VF x6YT.m1P< > %|} d#F𠳹9Is7|G65ŎgrBҎۇgFT~__;n^ٞl*Qz'>Fx4ǮAɧ۳ ϶.gǨֈ#e<k۳ԁgGs\_ƀ <<l20ɦ>`oϼG|Ǧ=>۾Ƹfۆ|߆~=ӆ-v~6mA2)okgƓ{20[KP6l뜘xtƔͶ=d|>fn<5X ϗS63m8rNP6XSc:a|Q#I P<,аal8gc>_l6tx /SFpskd`&v[κ+MM]]bs\%%ǖ/&vF9ű&Îq>䬥4}2xԠ٥i#cw8t귱CczkYVbBŦ}jv 胕xӆę fglsd 9K]=$l?ӛড়/?WhpB޲ n?~v|ȏG~q<G!ǼʥÏnnnnnnnnnnF_8|.Qp/y᧿l~Ϟn{==77|tk'}}g̟y?>`_[>??D[[[[[[[[[[GZ@OگʯLzd3P|9ywݧ}5~@>㵕{>mA %rk|Ŝ dꡓqS'?k>@[Z(f||iKI[>\=oY𐥝'^b1;ir$ƃO3{mVơ?*ߨxR胙|x|oHA:_ 2UomgOC{)6 rl6iصWA_mN_P7ݷƿ?ڈ|F<?Ǟ<̷|{8N6W_=m~OoOtKKO?ݿwO?#O]^, dSRp>M g =Zkj6S6 jNMFq>ޒey>3+r=ímE?̓GQsQbIځ_@6<[8d%5e̳|:X'zҌvP7Gqov`aabWS|qh/0Y9ӭ1>xuI1O}V{=g\n~kC4tv V?jo}8/-3gO*H m:!Os>xĝ~dyFݘ߭~Y-<.07Sgc_(?`GN vʍG7'JC?v5:ȇFnwM<_moQ[<%ؒo-.dUWڗcK?`G[/iۘ<^oe$8O?}zCg?QfG\ ,XnE M/ŧ ?o+p~cEngYpƯگ=27~ٶ.\Y ^G\v5pJ'1െ~6x ʖӧ<^kZ/Z"V?SmmxN㔛<)9e22c>ŝX?';Iwe*4m#ߌ]fG :7#2n骇ԦkZ (zn o?Jڤ6_mv[6?Ԭ rm{!2Za/0g3?[k^~]GMG{6^^T}g^6yIvr(1a0AGm?S.*䁵&KS1ZN7y~ԛ*!oReiNegٵ![G[9~}xΏ×H^j鱐O?\`,d~Hr:1 Ln{f}ڗ6j ;xzV:roVWt6ƍg?6G|j~gΧw1nbnx\DI5Hm?O7A ~Wl<Ձߞ>S3#>#WhkصIȧ}זƸ1_sCi[Gr6-EuՃbu!O}ϑE s#ncM=pgS8/M98Țԥ^.0Z^欮x#?ض/3}=:oarfpSՇ'i}#|raFf#^Aű/|:6ڵƚhu2.wxͮ qdeC9eک`oiGoR{7eۗT1&q^ocw1&FX-aqoGkSEぱ̯ɴE[P?QG B?61G3A㧫_qo?ņ/e3REL~rre,Oڸ:' >>cI|onnnnnnn^Z?G?ǟF_ _|~CrV)~p?n%/\ٜyqKG [k\]ӭ|FJ\\%G]}jyx8ocxezRҌkCL;jx\|mÙ:=G~ävY?ixʕI'lִ&'/d6~3zdž\mn~ɺ]yK_W[GZ\`7n14^m}/|[m>[6?~a \,ssl59|K:36d<ԕ:Oj|<颰q?||c]>\'mՅ6rsA~!b-#-g W+?'O?>0?'ğx/|g}zn*cJǂزTS}j60XqѤO܍?q?~P'͆!㯵۶tbMn9w'c)j2b"G'k)q؞i~iy§-9|cBO=kōy8e!2bL ٹB>6?SLV#|//Fؠ|Af\#qw֘C\˹D0Ayn㙩̗1==k/wdpCgQ[O]Qv;6]1Rc첮ȶ}Ձ13:q7G6?䜗:N1}*7Mi&--/|4; g!#mo{+x{?^|s3' .8$lmރdm|dC>|.ȹ&So=yQޘ>qX|0~{:Y.n`R{Z`,!<&{ZI8؄<~knҩ F-u&5|&E=a͖XőjTT'Lu6}$oڼD|jS.8Z,E6h8Ĵ-)rJņ[C&qcAL|Io >xX'/Ke3oq?CS<ҭȷCW"+~>ӚAfSm}ϵ{7z/o?5| ^[9Rر?$&zĝ|!mic/m|?/`r->M9NS 5mͦ3zkG[ *?/ݿwȕW7~(جx[ /[I y+p+p+p+p+p+p+p+p+p+ +p~O*_]9#/ pu~JTG`3 #tep__Ɏ?}@ )_onnnnnnnn 尽>h~q4~rϳ#sނzl7&>g>𡳡|󃜱ʆ&o-d0Gr|&^>q%JՇ| O,yj14}lhg@8d2zaQO?gB\?q۹ὔlHOw#K{4}71rNPk/U?imvh1}p.cGN5|ˋ;_F?k->~ XUU~H( tJ(GX@= MSLP3%z@IDATJ-3DOS|"@(۟}=ܙ08ì g9{}u[g~G|GN>VIU[\U^N_X~9M+0}YHq{8%|YddA_b8W_Ch~ix2,yg>IӘl̥Lu"TWgiʩ(}X紮0Іt4TQe4l\;%K"zBi頣 2W(\C^ zvLa~"/Gx`099cHB:͡{ND /&Kgc[0>@yi{ASce㠩0y\19Ч ~/5f|(g·>I:9vw%pK{6wlL8c5iI=zӋk_ͩa; X{C=}t|R>i:)^i7iBKqKGXT\嗆+b5!.֟N} "?(?pS_c;c ' |hC~8̏Q?)KczF!N?OiCzk8 ,B!fdhk{(J.Q.cZeg GR4YiȎ~4niz+xJvz:Q}Vcu$I>k|X~ ';Y!Ocxj:pҪ|xz/S>a "4y!>,$媄SoCl pMӃA8~\Lqz%KPMK+> qxIӰXxm)Wax;f$c*WTvm~L{CGc6E9` 4'xx$:/W[&QOE_{6?'~w=$?|'<O8Wi{Д'asWyt{Gხaw ydOC؞AwbFg(܃ qBԗ}=:a^4G~c2mVc>zr¼xVX}ݻxW间>*?| t핋<:'P}Jbv6{IޟGE~cHyh>^C~liE/Eϱv ~3W94~ *45>lw+Wdeee y@y$AmM4҅\zސWo,i,4ciM0_+U?H{*+_S#"wOu4A,\y6VNIûO>8d@cGO3Ȉ?B9СBDT-^DLIC0 :HfRx6g uW^kf›@a3#ѿK/zGzW;6X;FFa@D/&dLiC#@T،e#φg4)OK lRk`el`!` >u'{@!12ս Fl8̍!@c,=7Z+nX`C/zC|HOt4Hi4>%|V5<šCWBXΗbkWOW t ӡ꨼~!?)̏-GZ^5mG_0/t}&O! ?t~>ܣK 4 -.9+a1,BА4*+ tpbh269J!X^!b:Mx<:M'ÒwE#1W ei^\atT,"|#kBTPxS=B~s2 hw"}bz%ib1YЇ_ʉ)WX:t sLo%eh)} hc6-X9쓔OG^̑68k.MC ”IX\}1zMX|3KI9htc81# x4=Hq᧹640i84LP;7 Gة{ʓpA/N[ij?Ə#9Y~5ce }ϒ3 +Mμi:8礏:,/s:i`&wU#;OzC5ѧvvI| @NZ9pSI+' $OY m۶drȯFqԳOd8c83 ״7г4L=y9cVqX*toFWʵ+#;uJBgmi_}xqq0洁b~@~Lҫ.aGb4iIS2Ϛ&|7h~G2{C^4J+KCV^p~2B~az}V:5´1Z_/)61G9(0>ԙxh 5\C@/ăOBʂ.BqC~+_4.IOzC>JSLX|'?6("<,#x<-:\bc 7&/-ą:h~EZKضy,O7腬Хt#l7GV{3IKG~=0_2cQ2?B9G}S=y ua3&],s5ʍ_7_M:M$-Gҥ=Qk?K82 !ք!W,3n1WaSӦᔖ-&{sL].;fC,[E͋: x&\ꈋJE}*.IIHGŋUAt VGe5 ~ P|&paLh<ҪO8[^^A=͑.7riCqOsKTcX9⧹X4 OY  GӃC̅2 7:y&O.M?;g,Oҫ1n c:Gi4%y&nKi$RNi /dϘMxNXE/K5L4~iqr3,Ter hba7\kW5M)~}KKe-)= >HS>+ϘSUI=1$Ч}DJ k:eobcuf6֒ifzQwG'+w3²k@B_s!` z0eL[QkFG]&/m=5̘1Or^ C0 C07:ȒTf!v6wžDь}JEJױcGlŒT,0 C0 C!4`ۘ @3#@YrqΖ6n!`@C@6vdlz~qS4uX C0 C0JB)c[R-!`!`!`@cG^B!`!`!`{3"0 C0 C0 ƎL?C0 C0 C0"fE0!`!`!`3{ ~!`!`!`E܋`+C0 C0 C0;;L?Ch}!ݍ>XMVZ;wz*g;x9^IP>nǎ'm۶~˖-=9wz?&/#]v|:!7~8 ƊOw_G?:t+{m6sKoӦM#gힾu> t 掀WZzo%~CC/Y{3#2˿!`4>cǎ-[Ej!rGg yy@y쪫;g?ٜJc_?|H8fr1JNy뭷|GI'IGH:J0ԾoHs 0׬YϜ9A6Bo2iu ]vW'?o4Vxs=yfygw3xo_O8_I<=xz5j(9#}" ͱvY ClѢE `ЈQEOGAhBAG31d`L HbNb N1ݽ{ ~sSEuFxM uwwO]K:,Ggt=阹?0 A]qOw1xzD_aǪ}|_LzI'DͽxX!`@}P >Z:w9XJ.tU ӦMT2Pt3|衇Q K͐M'__~F1 }Wg&={OzѣG{N;JfL|~0#%mw_n/ \yyWÆ 믿w9Zߩ_C3 .ޫt<'5|C9"L]w#V׿gy{/}w뭷~׿J?Jܺur+Xպs悹ͥ-!Oęe^1 $3ubWLKGE FF'ch.\C:#-D2C>䇯0^|EI. ,uwYgN꣆QHǠΠ#]*Fkq@s@:FLKlUlذֳꆾlOAC=!.YonΌV_C`Hqs`'x'alʺ8X&U:9xaencO!|ڵqFT:Y:d Ed0sxd <L#{s@S@w{^Uqjm.]yPo!&aFi7 /W^3* bO'|Q`ۥKJ^zɧ;S=\l.5i  AcP>>aqD::8b%|AeN:7?sWUH™T#NY2`E`!o|z7g4Y]]{!u|ӦM~IZ.ɺM8LagKg$;az6,ڏS?0渴^POtO'|>>BOu mSe|9׿2̍!`- ӟf2rֱ+f:!C/fP:K}k^4IGʆ}{~fz:G:N Ayt|9XHd(/:dAcB`Yśnɓ>AEΙ3gD}g֜կ~/mcy!hFƜ!?c M#}a|dV> +=}|髚{3#92˳!`48Щ1s_1y0$%/_NGQ&0s-b$C۩QWJ~7BzJ^2Т/cq64t>I#,4襧B1-ݐe5;F ?7? )-Ѩ|7t&ȌuzµjԺ5QOq0 }u:žuD?? OZ zSwY┞~zK8X5/|^~˿!`%# p~@x ftJt:\t\t`]t7u2Ơ}D .1j s]wӟq9w\ob`6X$`'>^C&A0sHlhQڵk}}< A%u[ou__}Ϊ~Rw;}}ի*!P^V\{d#>#F>ӟ[~[D>jG_IcFC3]˺-Aa7!`8b 11CJ۠NGnjFèvNO:bxFJ'M3aMM}>R{ϕ0@`Yn447 CV>Z}4cڳjg>Ge@{ CGtB\Nt^C\@R:Դ:$m]#Mnf.Udz퐡~p%͒+:h?6Ui}}&hz@ڜ6ҷ@#,_)0 q|gs^JS n!WzX1 }0rՕ.QG^X][ڴi>yWd۶mNHsWaKyfW*b/cǎ5o Gymgq׳lڴI*++er1Ș1c`Μ9SK>}<>r! C3=0 Ch40Ņ1עE ?F}DWUU ύ7z#C.NA¬T]&3sN@~_$ϺL&yڽ!`!`F!`5ڵkg>hܹdϧ>)?3w{cp ҭ[7xMn_m| \``8'阕dk}SO=gFOf ڽ{7@c8$ 6QcftбGt*/ ijܐ8Dc*Y; 4.f1\^> mz`֭[Q6l]EqC>2IPTN^v3EO܃=?XC$=Ѓ3%]r,|5?Kr!>8|)WdOZ.á#:BUij~#eJ:t1 C#`F`#.S0 C mf0;8o18gݥK?_js=0[  3h?73ݻ{@3IIcyj>=3q\pph?yoX̝;W^{5oaM: k߾|zuXI9=F%K|~ `KC P`0bxC ?S/~ѧXdٳ}:cb`H1Ê1ΞD 40~O/GrW<__|Xg?@NtCy[^3,fO(8a|z9 rыpaP_tE>刡8i${F㳟X ?_Z|5K_/|DYU~sۊ+D(~Wߟ 0 ƈTL'C0 L:B{8Ur'r:' <claP1E|Dq=KyFo~eH\nǚOd3/w33i1XjuJO3>)cF:3x&q譳rS\` & j2?ȇIIC<|ȣΪi2O 7ΨIX2O>#0 FpL5C03=`1F3N R +A@$p ~ #.aa07|p?ӈ.SL̰qz!C< z+KLۈqZͲO1u'8 fAsQQQ€z衇! 3w&!Ç` {@ a)^aC z;bRLp޼ygF%آ,3u`v7|-[Z 3NieI' 4t#-aiUq8c? 夳?|z^/|OIߧ0'Ls9^ƹ9C03X-OaȈo c6t3 C蠛 /00t3¸с~L6 1&0fHǒC5 1nUlNz``b@=7a0,bt& >Zi:+L ?d!C g1Džl!Zy"taРz1Eaꐩ:Ϥ2GOT3y,it \iU{tӲ2?J>){A@>4)|YK45z=!И؏͢Vw?(gҘat{{̒+EƑĥ2j0p[#~qQrZcꐱ*yaekvCN"[Ni~LUmY/GΕVŤWY~)-nCC)~ӻ,hb蠛Y'ސ˜bf=X?#Ѣ2(_ J~/A<0`0&S~z?#A3S5r=u,ed/ iUSO3iă  İcF<'?Iopz*Xcȑ#=7HC=zHoA^8Uʍ0pg>_Ճpv%tcax׿ϔ3={{+?F5u pyyϞHpGug1^q=}?zj0 ƌ~l=V2?5ܘbœ5ϟ?S1A^\zt˖s?Xz]1?#Tٶr)j\wQ/g O^7Wf-y>ϠtrCj|n95}YrdJYrq2BD=!n .ɫX1&t:xjcpiX_CK A.d#,Ë{M='3[ # #xR2L陁àd6 ]>CD^ZxŽ: ɴ jP7"a˜%CYb+9)^O7:0#QMh:UoMg!`l  ;sO*$r+ěhO ][yɝb7-!d S%'xY}fo4G؛ȒƯ :w1HĘU8+#F׾5o+>?k׮1csYgyCSj'e̳FcG@}Gv 2uF.ie{M +CG^1H?D#+QVe|Nիώ§rc|.ɩwɦȓK쪫'ymQ|ʫ>rE2rKUaʵsFG䕝L5D5sg!@D@ u/t0鎄CaMiZ  )fx F 3K\sRiҪQA3vAMN!_LB&䓽4xdi%N1$ g'钎8zD6Y^ JC|#-r!eZ1!4` . ܙP?ð4yn@cG ?m6b%tiL~<3- -%G cyҷD;eηzɬd\CM gH~yy3k}eH3!oaoUE,#- oqirɰ8î[nSgˋOY{! 5'0 A o~o ]vePt9sxc #w 0X>FG]@{/uD? ,Nd.i`̐'fF=DfFq`'̦=~<0sR~G>"I KUDGOƩ $RFXƌ':q> XЭG>ҥK=|ʩwy01!?"Pc2Y</flx!(=G;ߔ׬l㾺ʁg }O9K?%OKO!CZ_/>.]O: 3fi>B{ȕ#(%^C܉3_|b=73SXs#óWѓd{se,ǻ_Zڝ e0-maxCAI\Kz=S!U900&cXa`~&I 4׽nJFFFY=>7#=ljaL?h0cv/`%ƑY,0PcyA@GrqaEx##ĸ$O?(7.dᆀ!`|-Re\P?U⻕2x$9${>IAsc*O)ɟ믜?)zk[ɊkOwW-zde;9+Gi/!TCww1ZH/ F~P&;&oLSv<\1!^)/|toQA?X9FČopXTC( S# .= ~+9g9 nVaa쳽ٳg{# !Zj|~Ɗ:f+t5Vc$r9f~, ;V~͋;៺5uV#MZNSݡ"gOϓ+ʇ&N,w̓r3jU2w\?_گ#'w:4{{-売3[O,Nā4~JzYn^%p\ d{.UǓ䷛y$WN#a?r'tsZΧ弶YFˋgЩz9Sɼ˙)VFaciNU{uQS9CF䧗#˪ЫzN.nݛ"0O}ynlvo}uyɳrdȿɯ X vc4=0~0BpKc ܓtjh8G.+mc &{0*~ FhYeA>T1602CHG2bBy@Oc -):uS: ' 3  8hg05r1W#4Ѓ3'M:#♝ćk;C,oEG !DžI~ F#|UtG⑇ q0 F׮iS"#܃yj?IDATPF9ZHӇȼ/u,3wd j䤅~>ْŲn3ՊqWC$m?$qa*t%LZIoRBF1Бc>cU~[}ma!`46B륱gɅ0/AMrR-7w[/vcQaG,U,Q郕.ňstn<~K:=d[d '[%|rɩy5E[1K?ޕ)N)gFLB :*md@EY&q K/bbd0Sž1 7MS0 C#|)k 3n9mI~՛k噧YYNJ'-QCaPsWd0H^n=a<<~ >3V3ɱM.,2i#ry0Hk*w?9+q#rӽܬrœeRgYȲm̶@ae >e|B1\B{C0 C`B 7:MOmߝ 7Z Ӕn69[nUm|!w;S>L܅||dHfTa=f֞f  @eu G]dkOl4wRݞ\|eq=i8Qۢ &{^z_{t t C0 &ifg%y_zsTtXz&M?4[2OKFg'64"Ы͚.,;>x'aI#,Ex~IYs-Z&JQbcR9]wh=$hɼm4BY!д`/͘3 C0 }<;W2Cuolǘ9b;MN>a[ޘqI~O[{QIKqZMl~qmݫ,<:` ײ'e҄ {qmxή4\ZNzˤ8C^"<9 psBkضu#KC0 C0 #ԧĝioIHȢB[[4<>%a\DaPE5Ң3S\C9Ɓ*o˒fxM_ݪSbg+\q2Kj:ڪ@]/+S{qt݅OY\l||lM<vg!`!? M/-YygYϽvwo~Hs7s %o-U"JK cdB1b"YR){_.UeP?T}iwKrA~mY# 28ͧu]']' F2gWxfRsnew!ys7nD.  {@~rq9ԛrz)5IjY@Gԣ)u3BCᐚ)0 C0 C06E?_̻Qq?G̹Gnwv`=včtz\NZbby≅c+d3.N Ymys&ٙgwʌ!n9DY j2Jgeݒ+ &oǹF_c9+q?F{~iN_){ ^f!`!`D(;-IP5G.ۑExa"˦e6/dp~EUQ=S2tS;8X:2D/.z?\8}jyT^=nYViqi{|6vg!`!`H|Mr& 嘾gpp'̖7M/Nx9DZ&yq-5' ow?tw#$"31zʞ?0wM*.~cˣ+#30{̻oU˃`s`˴HIg 0Cݯ.):=U^1O&tHI* xPJV-,(=V{CB`!`!`EcXIn|lUiE[iQ[:?jf^ޖ-i+I[-Y&yuKV|.}k*\J[3 #@x`9ӽӡx//<#/sK68=XNI,(-ɪڕowߕw8]ˉ/,dphSC0 C0 F׮؍!`!`!`@dFeA!`!`!` `F`-Y!`!`!`fOiy1 C0 C0 C 6 C0 C0 C`B۶m۟gy1 C0 C0 CH `3 00 C0 C0 3!`!`!@vk!`!`{ [ C0 C0 CH `F` 5 C0 C0 C`G-!`!`!`$0#0!`!`!#PwKpnzykҲeki i{ءҮ];ij(E-KS5scG1R#9Ub*@llڲMvWhV:}kx5+d[r92ܡL ʖAKHףrEm]J^޲õW-uڵIQJ6y)彛{m n4X۷ojl&yknvmhҥmcۚ.MFlݰN'֥qcU/,;\ܲuk9arQGI0,ۓbջ=#J#;IcЏ4`3/>hmTܫ:ʘ{EN{B;$1Ş5Ui7ɨ)BT6r,!S*DA{A+ CұۼH*PVJ_Cow&Pv:VD͏ϔ ϟ߽XƝuYn͜|,c.^q/E_ϧz̼lLx0 %ϕ1 ldk~,?eP!;3OsھJnW,Py½ ˪ uΗ{ .C'F{$W^nq~lJQ֧B _r\^@'51TX3׵s"KkOZB[!%zU}zʨںNryMGWuZmw}LC][:=זJ*WRh.]]?׻|K! ~[&"NqҳCfmȼ#Bi2?Z5G:\TT+~MzaV{X-̈}wݺ'GM'/^+{>XK^Za\ܱg##Ǎq#OYNg %7obs/Ͳa:JzUTf>0]ޜU 3+]aƛѐjOe۪ U;h]XceYwLX)O_uJ~{5;򷉻R$nKWd̼gxLyN;P*d 5'unL 2~d74f {&R&kB)To]&w'Ȋn K22]>2%em 蕞#wr>jػ\5gE:a۾k>*rʕ|޶L.*\=8>6Nzpц5&>zX!^fN$Rd2h5se3/n<1ٵ],34[UnU2?w CYБ2~D̶`6E%;^罔~4yӻ UÕ`X6P^{x@<kf @&ܻb-I:k亞~L:72eYiѿ<)KVKG1p;aM\쾔eZ=M\RwfDٺu+nnowܳ2 ȨYv׿X0ɹ.[ߕݺEO>qݭ{cf6]Η)w5\n+w7\shٔ-^/TjwBRW$:=JIT}=tt{9+_.3VVЁe@+KgĺUsFn׽7+=]Lߖ[,m`Ye۬?ufy*%JYvg %9噼rF֬^nu|UW77ɟ ڽ;>Yٝ?4Rڧ֤h+wʲsw˿uj7/t++Ϭ}KdJik D}U1Qyᬆ.yAm; 7BE Yn׊Y2hBft6tɵty&_,"mߟX,v=X"&kw CZ&O!w^e[:V.IGf![qy6|KJI_O)o:o~E;N2`?h;]"ZL,?goٳkR*a3 աU[e[KMdk"k˷HvܚjYYy'z)5۷nMmdcqv/bC{i[=yj:[m:]vNvF-]2} q_D~P68#deLɌP³ASJw˃=zꟓ= Dtxw+sxaoX#7MG?8B>>%w=P1|ڸa+< US,JH;jCK:DB)2;#Qn)'dQÿӱGwC]I2|rpbe3C*dsHѝ qI,yתݱ]5e2ii&tk͗@IruS`vG宗Leod .(},îUyޏco%Gi'gw>ET ç%[~TY]i~`KonkuG= kOٸ\6ŮkWm~>wkjGY]tRFS_U5L {,yRښ|괻z DQRet>~.~ -5^ s873-aJ=ōBVyIY^!t/ːV咱y^׊$;<ޏ-_0AKJIm~[iMТt8[&]gn"x{ˍ{}-ʉeHVUՍsNqE:)l']*?Fʇh)},a9kI|soI߁d R^Io|Z5KYn]#,I̤_.κI`'VE'fFzKW.Hn] 7n_,7] ?_*Y◸od*gPHҾ]𶋥S2c[.=FʊvUHdYBSn_ ;U/<|*%z2ꬎ.o}?ڢK 覆;wl }`ls!{ʳ\^>}wnrJͩҩS'e_Yd_VݺuܼwclTqYofջ˦9\Z- iR҄[ܗw +_LJ-X'WԵs+%cV<*Δ,#7v|vr|C6<ɺeTe+>rfDaGɠK3y\tu"IW2Esos~lyO&ۘ*YvUѷ6m`ߞrd9અ@g~=ۿ--e5ۧ DߓNe6릇4মH/jisF?kߺQZ[aUo4z FPFMrGݨ(DFcuq2YU~ U(Ib,OJZ ^y» nK EKJIU3k#3';(/=dfeN~튁}Ҿ}G?Rsfwmzth a΍q¼ %eB6ʟx_)B^+%dH2yڴJRy@71Vq{V!evI(m.:Tnjsd_˘;D{t&Uɨ MqfܔȪĠc2<{D2y[>|m/8ING%^ mM 7\͸_~p[{ᙹ$}*=avTfƪ22)# wᖯzx\Hô'fqX*cɴe[)!\':,Ufu$rgAө%7- (t塟eTL0Oղh qd;㠜AWWe}eҧb_}!y|^]7ޥsUom&#;&eO8%n/x^^Kԍn_̔.I-\&_>J=w6E.u\sv-*LP7Lge2غJf~R_W6GVmM+қeŷ< c"¼vuK?Krm[r, tufT[1=!ʇW[>s^qu4H}C> ^c2Nזc$Y!uG.$gieַv4ou̓mo7{)zJRۚ&ۇ eKܝwm2kky-xAQRv| YpT^|\|`*(BfVO~T=VHTOm[# E?ݭnע[G<_ΞSN_|@*n.'%zi&+o&mA.M/ˣ~YzjOR`*y Oư~tMnP<0VnT[2.waQiO+%' ȝم٥'njRֈ'.>ct_ PP2w(&2_.-؞-CߺF(ew);A3p2P>ǧ:$voe+c =ۧ6ܲL3oot\feH7/C._kbϟAED-y_@˿Pl_Ğ2?m ])i 9?7|/+ó'̶|u7 &K8cb9[P.4qkn4q$|Idϩpv)"m;)ceke)Z9!Y3' |D~z%X6Vm糌u,m7Y>W3;In?Oiķ>.㲴%ݞ\~LO{Yr853;ܞz=˫FxB~${g$zKq}&Mg0 kSUĭ7艆*"OU^[I^YU}ٸGPeNTK^`̜gF2vk:Ы~O^ZR.]Zхn~\!¹q4TK pǃD+k 2c22q\ݚ2f\4mY!S)i$)xLuV~YJ1NJY/_?Lq6T^~pFY ؾ?ɟ6תL1>3Z0[UȜ}%0O0u3~nu< \Ϩstv ';&oɴ/ߡ`mc +?[Sp>+ˮ)շym ;g% @WltJea S۾,'s~]@(Z eU?tlYvl\BfdOu{D$$*Ǹ?W)|(CeyrKpGF}Ol/ R^}GzH'RH+wSm+C qUk~V!31Q.Ϫ{F`Վre/|ecǶ,mG|v@4 堻Uc>W?Dˊi7]PbT榙Ϩ>J9A6*Q9x2+.irYpuy]1 EuH1{wMOg b?prBťga@ |[_-p8]Z ϴ)wAzw1/~^Y9?b\ԻlްA6xt#~ٟUS#njݣ_Ot.!CΕ"c.oV܄wK'"KvKISCł[,xh'um:W~;ٕ)K_-=1};fWe?fMen7&U奥Y򛗦˹=VaɽN!Br9.1}cr`eBSKWMso3P(r}iڳ#N.tq-B={}d?2g?\~$ _ Է9{Ԟf:Lz-wm3}:/ 10D28yf m`]|T$N.T q5ӾuJf:>pv E&vYuyfqCI-ʤIM(A1@ܤ~{w{ބwf޻߽;;źWTtiOkLQL{0ՌRm#]%!0p\U ڝB= 25EW+N22Ѡ-^عR6#6 zE;{hN˸%ޒkHjSQU.ձ yvPv{yHJY:u|Wn$2Nn'}XJɾWo@jMKх$,;wRvƩ-Y.X7kVnFi?3^'D޺LX]E7>nG"5&y]<H.[\ 6;Q}`-K}Ϧ*5kVUʖʫ ’,YHY< (Ք\"2/,ڬ\}e5Ck[O fGv 62vGlҽ1j ݄9",>t=y(\NG6A:~B(ؼB~FOb2IUn|ÒmmAi_9o8ݤO@%"n08u2wM_EFhڬٚc̈LtB~m#6N8kzd|︇ I9S?@=]V=]xA.)/.ټ%鹒J0)i8-(!iڋz7ow`^rQ"lhp2q\KB?IPQ]X0%fN{vi8| [XƩf[^% UM}ܦ0Iy"W+r8s}2STVe)NJ9FCiaFh@w1-@KV+*>Z|m:*r!~h3E<^~v7oV "7fU4n dXs^Y-XJ+&dUV $if8mފI>#Z#gȠPa7l0)NL+Rޘ#-dލfHBe/>xȷ "qx\6,ِؠvZg[mMTi2";c<5 hQ'q[_ l &WUU V* -&ķ2oUz4lZ9SH/ ݥV[/1߯]cxDsTk3dnq*%UuiI-؀࿪I:PrB *ærH!ɾgq9OJ5+V18NօK u.1K@>oy붓/5*j/X;G06܏#-OaE-{Mu/H>ޠf:vH<swyoՕɱA *z otl8.\LG>ᎊ̱DQ c5jlRcN˚uk{} ?,j$w,Oen˷4w@mO_~ZⓊ;7՚s O|9DYwڕď⑮k&Y/K9c_X_*_ =9Fj"r-.HnVlܥ!}PtAo[/5xWhdžE\|7nooh~,#8êj H҅;LLtbMmHR/C U_b>E T-J%Eumb0@w"G\EvhUކ~ڱ 2F5"!5^ XV]DnI7DWG4&3Jh"= oX [YޤUh!檼va.~U/hjvn'Z2THBpLkBUorEGg->#Ĕ4 sȖ39w~E4n1л+UǦf0.xJ!uT7t[g#fϲzL>fϼ@.mst5%BVOWF+xu++P:;uBLO 0fDWѤ|* @1,~/֯MM/P gR3/gգS٤'+n$qb@= ȒN/BsbQ}9wy}H%^'0ѱQFnbw'|2ya dFOGmb%_'6bGg1*IA."8)j!ܻM3Ox/ @HY|-NT \;Bmj{Tgjwe,\W/Gz?"!$E(&+̓GoU"-&Lv`q[ . x$bJ_?] ?_jZ=4SVs_庥5 c}p2z[UQ:%1i8&vW;J㭪Z,K;MMM@WaLĘ$AzӋ-._a}MCgX N$3mw?.h5q"A.~G>KQceD|ޝ!l Ўv(aTKx5͊ib!ӱrK݃i3f ץzdzyp`$>͍* ~/ɈBՖ(Cj'N[4"-/}RH#u I{pK)MH鹪x-^fzkOi:஋KEMjg#[Nb[T)V0UD@,,9ME,kFZD Ȝ"f1v fj` E &+|GʞB޷feBy&([Zy7{I>"hIa"] PG-ݡw>#ɟFO$Eɟba/!!=›0GUvbfpF_ gܲжCKܛt'%شe̚}Q'[كAl(,Fobz,E-;L 9yz 0{aI$(th VTK|cl C;([]hٙPӤa';kHELh"WN Q9hx!iY>5Ozpt\qON)/Ӏt(q$&68M?%]r#Dh hoVZ鳗~>Ddw&[[yD~?^:Cb==*jƞ'X*gĠx5~,c\wΝe9m釛qKsî+_nkv̼'5 vJRšvSBnqCftuk6#XY\xtZW>WoPvSYγrg/e033'V) (+E;Kõˤ'i{36 sR&p s0o~&2ӝ؜I2a.XT"voh9UVGЗLΒ`F`F F`{ӂ A,G^GEP j/ۯf'S5;0vJ4~$VBgY>pFszB1#0#pU!0~/5 \y7B P|dH}dܙ(#0#0!@l$)`Ўbl1@:(8 F`F`FIrA$s(6:\٤kc1#On"XJ~0#0#0W:>\cK9z0#0#0#0$ ?DfgqY#0#0#D`:@ 2ptF`F`F`f fp?0#0#0#/WeV>e#/"/7΍MIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/new_component.png0000644000175000017500000033100213455362716022405 0ustar noahfxnoahfxPNG  IHDR8ٮ iCCPICC ProfileH w\Lz%"%kWQI B,`EE+TWZQlbdQP*oy߻~wϜ9@nd Y("I H' >l?e./,p8GP$KB.q oq MDE 6]l( R39iҟ49|6(-sd\iO6;M3{AD^웞'b/Y$_MyȬ䬀e>%fӺP1bl:_=˒hY R<$3bgeQN>/?R}dd{:0/+@^8L k|/" /~Y?bQ&5ݟ5Er]5OO DDses |A.6b? IJ br x cmɰu3%}@?u-eh~hPLc#G"ʟM@40 O$  @p4('Yp\mHx 0 A@TH҅ bBE@P2 Tʡ :5AB'%h Ac[ 0 ֆ0(x1…p kCp|߆KxP$B1Q>PT*%BD*QVT7u%EB}FcT4mvEt.z%z݈@o F cq0q4RL)Siǜƌ`>`X: c3˱{m v;4p87\(Jqppgp7p#Ox^o'5J|349~D0"B \2fBp0B$*Mn(bq5JD@!'P(OJELDi<|R*X+ j:n(V$()z).Q,TTGS^ަ~[CO#ScFcMfRͽ5_͡qÙS6ZVrZW&uڻiҡxdl93KuMݮ{FCbT1zZzzzz&k  z u >0"1F;>345Q7a<2z֚2Ú12]75,` Gt=KgKe]+UUՐ5:zu빆sn?MMC[ 5ݶo8v5v)̳Ǜw=uEcNNN2i0Eg*Ο]].G]rrtmvo27n퀛ԝ]xi|eu뵷Ȼ _oo__}4=wY,5"/\4abߢFK,6Y\%YKN%)&%ccCٵVqg'%ד;sU𞧺VmK{+}ҫdfِ9ՖN>!Pd rtr rR4%wG(XT-ӐTd(=&Ҙ W/[y/9{V Zq`%2ee*U%FWWg}͚5Ʈ..).)৖RRQu9w^-\nS^Yug卶6NmJ4q---wzlmP(޶`[vw$T9rNNNiUHU.][v}W߮iۭ{{{nۺO{_/;pָ `gu1u0i׬/ h6F459555k5on[$-c]?{ժ@8"9_ >{ycYx'S5x"Dokwoֿ5;YsJ%:vv7Ṹsx¹~3.re+W::\mǁkN׺;_?xǍ7}o^źuۃwܻxWz{o~7L>,~yTXq'&u4a?:RQѓcc_,z1RrU鿔yuui|j%ɥ_q_}TԔ-bO(dSSx%u 354S#,e]gGU @2Bfc+z +ߢ<lo/"kyvEHiij6n&LM}CjɝeJدhr1x GxiTXtXML:com.adobe.xmp 824 707 #|@IDATx} UufDAAI#Q "$R!#  ؄6Z2g! baJ?PQH aP>`sw֜9Ν/sk=… pGpGp@ۖ #8#8#8@#8#8#b wpGpGp<98#8#8-pZPzGGpGpG#8#8#b wpGpGp!FS,\pGpGh8ǕMkzWuGpGhmR psl˖[Yӎ#8#8@C hX|ḽ~XIʚ:i -GT<@1a"d1#8#8-ht<M3t4|3Q|KǕSo7]'n_rw9uo^&rx8#8#4hgieyGu,4̡o,#Gs VN~-^Si'ݛhiEimNfdz8偎={8#8#XjmT|4sf9[{c椩i9k I&nlqeL~H# t]ץ[/ys8ц62hVF,Gu)D!Db[ӎ#8#8@'hκ u #<(d r#JERQKx:`S2dVN:.q4mP7ȳK9#Qz(Q>[=-7j p9X(2۫Uַ>>~{gǎ0.s5gf^_f O_N:{ʚL6ȑWWKnu3?3:lҙDmM'_ KHMquYV7#<{8X(N&~AWթKnu3?3:lҙDmM'_ KHMquYV7#<{8X(N&~AWթKnu3?3:l}ԩ={T\\oۯ+xt8Fd}A1xA:b8 [D::LW.XSNN:s!K&;!e۶mY1.hNKvuO>}!(S<жmE^Ԟ~ G;D;#M][;G;C̣2>ʘ'eHlh@#qld?DGebly[Q>`g̓Q7ri~@(~@#Y^0QleY'A>h |igyCXdE[&g?s9#&8ǐ@#>բwrT,\IgRװOW(WƘ-+5w,xȬeVh['L{ˤmmCgC}uٜuB(2_뇺Nl}9q J|7bŐcJ9|Q:/sڢL}OF?#7&b2iQǍi<8JA?u>2-'mDi}}9<\s s2̝iGw# ;{@Ld4RX<Ԫ[ʀ;$y p(ϖI'=P *Zfa׮]?ξH\(B2#Em)uہ2}G>|XHۓ3`|B[4k}Q}͜uXl/)c}V?ApWζz! zG>uCNAD=@oyǶZf4X?$K `ߑodd5N馛yiGڹD9x[ЍKЅje:h!Č#K=~ǿ3gx_5,bXm4˩dV樚eWxjϦ|Z͝^ MՖeTey4[6>mxhp!^xr‰4/ĖCzVĂvUUU &.!e3O#zmb:aGb2C[~ G=h ;p#fM̀x>rsXnԃ<{^ϣUruұSw0ꂶeZ~*6 9HE L<,sl,_{UvF7H!C$g=aL\h';cǎo-#GvW^y\{I.]} ~BEr:|P^zyew{^^>r䓷kL/hlLmǶ^{RDn0lb͜-63aiG4#m>`g r2G$rhǜmalb)>}C8L8bȱ[{mQ٥4'ҟ\ڿH?S)mz=;K'N|@_qNɻq\%|=?9s 4WМ/oi$oAA?G6-&_f8dh%$Rz]f^ANL l8Er:ȣ4ys 'Q:4Oʡvl rYor-bq]$`}S$WJ^<>.9cGP z,Ҿsw;1='YSL?eҟ?.Z$S%\x] cœL9^A ]4OKyG}#؀p\H`b5`4X`}Zd;-bF`5Z~G9@ ⰴO,t@|Fȑ'0Q~lu3*cECN6JA eP& ~;\8IaŠɱ7_YcHѣ$ӟ }6S7KZM'sek]LL2O Bx 9}=:cї wt@G_Y-v "!?t䠬ݼJv.(ѱruސ@~bQZ\Jv5 0@#Ņ2NxyXhDlNuA? Y0Xf[8|dO9lCzhi;9mh:_樛?ȑڰ~Sκ [ȭ_}9lSK>js3:N!+?tڶ _cJ zzYrc'57q8)(m/t.v <_?٠qL9ʾ?J8ԧlIC&臋z4}dB[_ (0lt}*6d2R:'9.8ąugI|+2Aҫu]r j'^W&d~em ..;]+B*++I6V ^yٳgA;(q!B;_7eˠ~7ʾۗ#TF+n(1#&u=MB( 6V]rF&> 4}C;CBv `m&X7r flm}$e`>O 6h8,6l7JM}z~AF>h& tQ |!g.d,C4Ol`Sb+@#1.qM}dXBF>h& l/Q* F>q9AìM ꪫ}za6 uh~@fL6gdӿ̕kKUJH֭3ѓտ;=ϝSX}l8~%д6Q #fȑ>Qc|LfV8VdGzDkB6}x_BzY.h$?/ǿzNsnc.s=!< @di =?X@ e =LOYxA:%(ItȳtR!1z81 ȏt~x(#!nI'7͝rǹRUZ*Y]58:~Ly<+s&'NFXZNN?~xlۆ=Q:K̡>.IKCNCrCXAz^2q'OȐPm=s~I=2s胏?H> 01 lǐ2ȱ_-X*ֵLRwvN|O.>/! HjsVaj'<_U_'"ڼ.,7brʿ>do>qП~J` iZϟi)ɖO.kb~}yj]i 9RNo.C~9~+*7$[?[9Ylސ2Х2tpZ QȲz֮g\nV%mN꓇<G~}vۍ /ܟ}]C:)GIӧAaһ/*ONl޻' nunMMnݺM /?Y&]hδѻAXN3mjyA;`  x m 6g'\?lM(r$m h2r`솓uBm/Ѓhr7~gD><[}ԉ6!o|8|C#1k &ݯFO .)U?ˌGgx_2xm,֘acgˈ?3zdZW_9猾ym7>_{IM>+[\>w>X//ʋ?c[{LF(wß w,U'fK{\sV_q% ~]uq#j؜aQg 9?С/%)uP< Rz:aʖf<䩎8]I}'>wpPF'878;ch]:;Nq =]R}WhN=nD/c:t6 x6H<7/E},Bۖ K{= ;s9#'tCB9nFl1iBd%O}ds#'t e.:-yF lʠ}ģtċ~?/$l#xu>$M;zJ.߰=Ǎ[?ѱ߷{r 7D_ :#꣛t^x3%U%;,=GJ~$~tM}JɝRL#ОrC_6C6_>suY|rǵrR|ys9ӘH(F9 #6<xSw\  $?[ibK?n;`&&P)̱XBp !TfL.DҐ5Ijlc;ei6,m4ؑPڷaI 79~]EBk7w8kMF_ GjrTWi0]~Ν<9.HhEh\$5 A5s:H+c'Uȱ3qk\O}#GF 7(n!pb .lCh+>4C[P䨇7L(q ?}% ߰ hء_ht!fr4!Aǃrx}<Յ ɶo2| Wq FW"ɖ+u.c^`b`^!UBBU<6λJԹB깪+eoe?P?(C]S(YƓʥ숮ŚNlo+#T9yrfq~y;<y~"p9kr!-_scE=N搆 lXf vOHG@zu ptt@F9s"''9ā2h)CNy[ 97R,e/Znvd]UUՃGηˑSce&7X< ޖvu% ȡN@t&4چ6G{^5PyEڟϖm:p5Ӕ=ȩsimpB>y~C2,Z] zLTo!#i[]~h6nVOvEl;䨛e?CC[kvAڶ~Q 2bz/HAeZ{_D[=_du>9׼6zδ x珿%k~_(}rt:u~y\N=[O#i75~E?qgi{jql戼tù ;_.ٯu},?x`~pme8&C?XpqK|蓆/ց.[ }l< X &,3DR0( e!AY >l[<$C|6Qjzh^Njаh|ԡ,.&mHSLxLz\1=}~/V[z>¡@yU_pRko-Xl 1칀cqW^_77xcH tC_Y'#|ړ[o߼/>MdI[}9 `N d́,O⁾聏OLxAQ$e^u!@eW76c=Y/3gmCNC~P7ʐ>N|2Ar'xB?x8 =a |֋|<聏!||/X64:o*+{JΝ9'?-cmJֹr94K:ϟwզm{dצ}Z{䨜*]/.+g/|Ϟ_\UOL^Wwt{??_";A]YҘs>^Ɯ\gs eeqhz,'m |O~W_!0A޾s#[8<$w9vX]6ڎwJk[~#zW菄nD\!@:f4Ɋ}IKbl ?HȡG;gv|j:꺐Nϩ>ʓW?Mȝ7+U~C>'tT[(_>wu~m争ߖB<_猏_ \}?X0?Zf 07(ˤl%4gs:7ndZ .luѐ@S9uH#ySuB~/Ig^ Ăd+}l)gx L= 劁ڝ8q"X Fn "{W DXA_H\L ?VGN}M# \-(}ߤߊv{c̾'upw'0l& g!g;Yn|>2q;䖦[h Sn$?K{hGtK9a?A3A:6#g  P?J9!AF]oo?,O'v>wt9?:-jo5@ywn15 zz Fœ<;[@}9q6q\)_*-eSk<9o̓N*}g:zs=9A~omQ>۫K<@r(V_U;Rг2#AƔu?)LWJ~|H 8ROG}e@23|'|M46!B p0x-՟g"[@#8 xqDoàk\ѣ^ʸ A߸-}!;7EЁ;lm ?^y< >Nj;|7htYF] hAb|$̺Q+zА!h>1'~A>rXPl#PN5/l'h c`Nq.r>~kOyQw9Ua}cY&±rBך*Ü;yBתcr\p>~ù}A~[|8< oy0w^R>cmmy; Μ>/6ȑ=T:#q/=[\H'7S{e iwd4?Am6+ꠏӞ}bvq!"#?W`-W<Ǹ5x po"ݗSOy\h/$rq * Ds(24A$),X@pAIC;4`E%xt8!ى2&P'A}l rh/}h~!G=HqB~>Ɓ? >s2>T#tH#tvH (CuA}.g_`Cna߰AB̐Ц?|n 4}h:Hqۉ ?|Ays$$yu`^E_4/Yj3m"9s\j/4j{V߷~rab7tx~/gtnk;Si]/po8vݻm2qPnoWT9?Ą4$'\Ss4_~yufs^ $T &,q67䃶rqr8Ha9\ס_4#jpi!&,;j9V V4<61նmP@yq] 6 9o<а G=e~G{Q;amb{ :~lA;${! ȑ4(C#? 4qD)I]v*倾6c~upO밽<MG>hf]Ǐ;HaiGiu$ҶJjꐇyI$/hh@S < ҫaˠm0C~e^ 7Z&+&,Nf6(c 7tAO|,v~M7ܶ<$ ߔkI}H}}bcl;1 O^?/?Be ~I H/l7|X/<q$NpTXGp^O|14Zk"rcH>}`:M| ч_[oy@h xlQ;1!A#Y6,aOY ZC2~D-:sfL9uHו3氳V j \9<7%@F9Z|sO֍2ў}@妞>d;parE[qa`vPF}Œ2mcS Ѓ2O;eovؾݰc;XxЍK?([{ dLumb(.a;P'mO9Ї{+7~d;pQyge"6`܄$3&/d!G2nh胴bS]X2/A0mne!cx ,#&﫮'GpGpGU#Э[*o遠=ЩdO?6'.,y Z~֙4EBD:[]9 @_'GpGpG5#`Ǥm :H~ gRg*յeVmN>rhiGpGpGj@#QjCY4oDi4vypGpGh ؽ/ߖf9dqhGTZ5TmуzQ>m-?@:]O#8#8@kG{dmI#2ˏhܾz7kBG#HRu9ʧ@'ՁvGf;8#8#V{=0D9U9XfN~Mj VGS2$4e)/GpGp֊q{ful9ԎC9T|3kY:ڠLdia(ZGpGpֈ#7f<8,3kLSY=l4rKM:ՍA4|zrGpGpZ;v xsX+eGpGpG5"P׾ʁ-FbNeJt2J_W/)T5,  (M]mmt O#8#8@kGeW&9y(ou}GY pllQ-?d4mrpGpGhd#f!YSsNʘӞeg7UR5|Եe3Y9hI'OGpGp֌@}^fMzIŧ<)+5q<ж (rrS#8#8@E nL@dxVꐾy}ԧqqեNVzէ8#8#Toș,myiH38)Eu5"NcGyL{rGpGp{dg& H۽t}Pͣ^vG"PYQ."yya.^#8@t2Z :(yTǖ+Xr@dw䟎#C*["=%A+flYM?(`Fۺ^䍐7Lr'Gp&<QF6rm$ht))~k eR8@ @bOdy)z/s|Bf{{ʝ?I7ç͒vht@V[,i8)v#8@3!qt̪잚ʃFHMq7}4wG =9 3ߐ9G^~fcd񯞔=+*(xiSBo<#3(UȠ+N}3QejڽFF }@Jel>X(jj:@D,aR {h~(69n8;ԐvٰtyG2~=2{g'6E{ʒ;7ddnҭH9Tw{T\}I̞-[keKDR6̖SJo :r)SC aV%-TSeɆ54j*wʣ3awlZ2;=r䄠d*&.-Vʡ-2_8Uy+VI߳eLVkj5i6Y2F#e„ I}[@Tٲz˾\X q^85ɊjTYXjɴt4gF2CXTQSQF:6mÇQO#Eb27bꌓ{!6Mվ5cEHe2`5Ғp9mٱdW>?&_jlB?S$2ɗ*ML\*kUre3P-/djQH\|Z˩ {„BpYY]f[jkwaݥbxoqm'.л Mb zfmrۥ<_K}TY M߯Χɖ@SX.hGvmgڑ1A}1G̓Z I#XKZ~NtuSF>Y&ϖA#Ah.jF>|XA<NjxpfA 0PY|fO{RozV7>*@^8C\* yɎÇჲ~A +: OʖʴlhhMsfmiayf@oL JENnN+ o[f, a#H~"rǧ&P&] GFaD?Ԡv4V-AY_d2I۟ہO#4 ݴ5a5ZY_ 8-l8-Sr ѷȳkSB)jĕƍCHANpg'M?0^'K vߝ#r^!*i =oРGLS󁶣]wCl/ʖ-9U7k}iA An)nhn]-.=%"W>u+o:(Y]CW/u4+)yKg~69ƙVlh fN9#(O9(C%%5uϭGb x$*ĀnKeꠡsps]ﶝ1eǂgȤ$Yevg.U@a9k'#Ѐ tP>m9{ a`f'R/J7 pJeɁ HT$vo<"2X ?|GYudXu, *Vg![|fX: jp4ۥkCH|i;U?Xm#8h{Kt2Gp>q@UelY1_F$߆6KG"}-+}sǫ^WKs<5>%L/n; $z{5_HN R78zKAy|\JH*LXf}R.KwlIaѪ౰c&^}H)Jp;ղ ܕZaq'  5/`X"ʑ[S#">ApmGAͩzGh&\$77Wvw^C啒%YyUVKEeϕܼfy.o*Gnl?Tjrk?RJ$MUjwMr5MoUeWTum9P^r>] ~tAwkf_7x+<9#4 ,5|q`'e !JWsBNe;KΦmGSf]:#<x1ٖx }[V #o GNevS|}"![cGʐ!ȱ>. Cmd o5G*&dg9; LfȄʆ-kd6vN]Z-f#P6U6h'auUȖ25TytXTX-[Q |ؚħ6Y2jݳ Iy+w@JJE OKxCRvcwꝵu#R(۲Bf$翞-wP:ɁօA2a>˸A:UG? *o}i5*ү;ukzul>ֳ5Ԩ(QX5Sb{XH,yǍ^~T7kܣQb0[6%C[c~l2]w2]냮Ef̗u۪Yw ֲMɶ ו d/55\6-[AeCmز},L̵ DL6qhF^-Yc]U3G \rkN׏ܞ}#ָūd)?>dIG^eTɯ/PJO*Y*0L~ d B2*_ סϫd12.2N+-)CɨepȐy?\' uX sف݋eL~04cƏ( 9$:HݒjmN#x!x)oN]' J,žqOC3[;]?~!RxYءr7Oka5.Ћ~N6?-h{LG{lq@M2*ѦgZP|~#iJVO-X e=G dQ<^0݂gJ٧ -X)}Z~G =&E*wBCe=W,{mzq~bYgqrA)+*e" ·qޱwY`_:zơ`R~ȃ#}0g<|'ϔNpZ:\  z}y%*"Ȣˏ7_ IɎS5#>g$ڬ{In&kFѷIIM쳏`"UFYXXѻ"W'GVrh& TRwt[4F ^EvpH OF;S   xt \Z'ϒ{6Ii*}Qv%q=O5~u^X[;*͗S>'w+.$e0ͲBY]e% 8d 2e}lYhGS%+KOJ+YR.+HƭrL)= !2ArX~P/(58($*IUy[挑=DWɫJOy UsV ɗc᥺o~= {h߷c:xtO]LOGŁ_shiTɖe.=eɍ{R׃O~<\rox" :U ʓ 9[X3 C2G`](g+0.3%CA6_'?ȤY2 Ī_^ÞϿo7B}#/ 㾢jp/$XSMHVT$ ʼnH7ɟǭmwh^6c<|X6/•KSN뫰3c{Nq8nNLb]q d[Qkx<&y ~ɂuMZs^yU QW\S[ߏ*9H!-F8>j:,,~7iϢOHvLz}}V&+Mlѯ :욷89;]rTTBA3)SW5|a;.V R#g6Տ )^<]\t$uQ$% ]!9%}?20|.,$^q_M|)7G- 3?ȬݕSsbb}M˗Iwk2}eźMe:YpoHԸ9&E&ӝ5UeAT0e 7EP]+?jr_+|bj羪T?CKltnlLa`}irSҗieְ5]&9ayy墠;S9Gjsߦ/ }IlK f J]Gd^&lN^۾'i/?s5c[/$З,1FTzRJរ]-|* o|t>?p5$UXTV#}s JiL׾tO:G ϔɰs|P>ӎ@@`μyDͅ o( 2yS2Q7ka>hDs-"P%啒錩(b#\y5R=*Uy5ST%/YYߘmuJ!,P,5f*ϋe:QؾL)O'U)V?J ZfґWTjЯ"h_bϻ4:.Fn׮5^rZ$9H, Y֚J'@S֩Njqbͨ4-yY5J<]L&ßӻdu`UƎ_\o8geD&V2>ӿ5szO䠣fG@Ahf@9xH\4~!/Ԉ|6#p"?l@)K/.5njP2z >@wYݗZME+7czG!S"xLӪ .i+^Cړ#ZqN(8p X2)֌ 1*ӏduͪX{JZכ^ #o\/qYi~(7hxj;G pYtF6BU]8#pȕs?(x\zE8^#p9 }gTo#8,7 VU_~PVV W^9ew;wG5az0RXUm$Ur5U]eR >x9)z _=V˅#\ l~>pQ^w8d$+jTh \a4`VFD%ic+6$榠fl 7n]̯ʦL\#b ԈH$L~3 8s|===|sV 9  U{QX[;QvOjqXO$@O@ߨ-8[J&+5$@$0=r M N@1X ƌ'my\_(Z<*H2@-}MyHH!fBgx.Kkߛ[ъ ̟? Sa2"%RU HAF)EvMz(pwtlsEsdltBǩh|SBUr $$Hͮz==ŧ21~ѩH(©yc"    I(]I]׀o"A_ vNy([ٲ)I[R\^ݫ(I69 qF"-2cj'}HJI$O؅(ڸI0Mn|_3-i[wCHF %yDrpP ۰|;UA0d#fAlHHz"@gVӎLeVLOPD ;I1mh" -iKdZHh(YN"AxA=,@'w5ӄ " Y#~\C6!X>ǰQW=Dɏ8 + Ԍèȷ`Ib4*ot2r rΒQQӈ=g]b G^\G /@۱,ӹ9 <˜2'#.F,5s&Q"t6Ei)3k>:1՚ѱW°@% MZBn$`0}Q$q[qa&Oe/.?/G.m8ꗡ 4ڍe R0gj%$@$pxG^1\ UsyZLl6dFaIGnvK7wр?R;[dIE5ں8>@fڟptQUmvoR0ܑS13Hk-=K0؂ڥ |x~ę:AVB12߆V*MPB0~ \]#nuotpygu_69HLNDÑefQz!W{KY cȗurvL;apYv6ŭEYګ 'xK25;weuZD kߋ8S.毯`HwH{n;DsbM3d3/tS됻3$@$0xGpG@^%e؜..,.zQ>s/Bl|?0L"!yC0v_7-P?f 2r ,Al]tw[Q@ow#bRrZbH9HH xnj&Kby^Gݡ`|؈[0oJTέnŇo˝owi߆9#ӿ%$@$0&xG]'3/!L%q=3)ݼzw$r9File4rgågM(} VmT^,Dɾ}ؑXPn3jFᣜHHHH3Y'mϟ0 EAb$ cMXP"S6"ɣ8щƲU(݊C7Q :pYL UMx!0&Fgj?{{O2?3!75>dBi;6#2:ȫC]Zٴ:7('   (A?(њu x )e :[6b8$G3Ұ ^&ŤF6g^8l6ܑH"A#=̘K2uH!v!6eؽB 䠸j(Z, MBbX/NnXFD$@$@$@$@HQ۴#qYŨh>)<6mD={~jic9uHnHtpnI @ccAF;'~]vCߥ7iTz ?>Q1~o'~.`O[>pD7:875HHnr_|TEo `/~_[M+_@kJE-1 zTV^G"|ZQP5Xϟ['V>Ԯ} " tp. X&~5yX㗰މ.h,ߊ(q>OmϏ/^TT|#UW<2"{k%gQ-SUl.8o򴟚 8|&ǧؙ^y݅I]Gf݆HqoҹI-(ݐK;k񹴳,݌d]9O!5cTwܿ*W$~!+{yn-B{Gc;y[3R7Q wUtpFҋnZk( &IHnRSBrL9MfCX,zH:*J6w+xtNk}Gx>6Ͻ>%%S8/2k6l}q̋Pl+Oa1 l%+W+vg9Y| `mGWn{LEſ;7NȠUoD 6gކtjawR|Q}{wX\ OIu 1ϛwy;K`?Y;1GB 9l~SLMN@ PHDGG#2<9%J|$Ӱ+w$.S׼#Ar:L3lF$@$0w{rz9(za)#u/ -@2Y#+(~P0'8%,[?ϰxDܙsX0eoC(wmXp^ⱫDŽ,xӦ}>t>h waW`j15OZҵWZ:)ĥŚ}$|ӹ8vJKgN~K/Ftv|j;&͹[d ]3%ĥ:?+l|'ԄΣ-빚Jy,c>bsg'Xnl'W~PзcgO8[#'9˽tO= ؽmLWQ$ͭhEϊz? Sa2"%RU Ų6:lB5]ɣ8щƲU(݊ކlNGi.tg1E !!0&Fgj?{{eg&M>dBi;6#2:Q֡l-lڈ?unVh&$W4TG+V%qd9=-Qm!u0V4w~{AhobiϽimG{s#VEG"cg܁`M+`j-FƼWY׈iX: 1؈]lUY2O$@$p ڀ ;Q]wu(1} wƒŜ֢Q>KjQu1%8yy͡xhlnFmes97,G#,zGі}O<pcȡO_ʑA?o"$vU֢`6ʍ^Ŀ3uݳ4a.xz92PW] W@0>/'ޏ%x칝h[^`T 6< ܠȕRm)iz##kkxy#i=:>_5v)$Q'@ghMlo| ZsCقΖHIzʐ ^-G1/M=}_;"EZ$Hye&ۇ#)% > Qqa4 9;3Z{B|'M\5`v0~4q~Ly8tlLe&G^–e)<5l;H- JLX0' )0&L֜۱8a*&Y[=hU\io{VSDDw8%ߞFg" ڌKã.+{a?mgDOx.k[]ZV6m?#w{k3=( mpW-N<] hy.9Y}ZnƗ+X1f桽/WgF7kP\Q {^y `o6~R uv_#;ica뭓6w=+d9WަKO= rQDg6iVoÛuܴ:\htf;(6נ:JdL5h9ޑDvCgb΁3x1 ,Ah;hv5KigpWr,W̙HK7nseJ>G.\VGRRp(pؾ_| =oN6 0L/Ce(/>\~ [a`̆eYa"&ѩ#qOP6\_|.Β='Mte6RbdM~&#|]M?Ci,a2q,!T(||%Ʀyj#>2׎lOS_quVyC:z޻^T^?Lgid*y!;̩x 4fo}s9_7(ڰiqnbpJ_K;Z#y1LJ.ټiMx̬'0*lv쑼ˮ 7,kk6}q6Q1o8-WSRˑf4s3ᏟywRO]Uk$Q.Hnjʙ;qwtns|t6Du8'~.Ω۹F9.җsihѝᴑX _ z_c;s{QS} gNƟsY6jbJo` Zd:8hk6ŵbolɌ.T< $g;h`쫪F[ѳь],1@U}㥦q֚7cـo"N.)(G<p~YiX.%k~sW/ztPn -yrho$KҾÚb   y 䵷}<`L$&&"19UGtG|E ^=}/*d!&<["_n%M|R1)31 @IDAT֢,[֛I'xK25mю "JatU}HODth"㲐3߈<ɘֽRUW fVa8lBic"3 '> 8W"N*lB>5QqL9R?d.O$@$@$@$@7ރsWs{p.ä&6 ,PᐼCؼCyGLȈ3-ݽh##&E w*+\$:YVឌ8l ?$PtE-"C:hh?#KbϚyx?8_6J$@Iؕ2'f$@$@HN :ڼMbq-a6an2K#gt; >Hbju[=q| "enfTUM}q֚7J5!ը@$@&JyƊaS>JsS.2lT$ @^xY8oݗfDbb"QuDpd{9WyUA' 1z,rNn0 ̌k`SZeiɭ'ǒaջ4>T'":4qY^d̐m$MpgOoc=s$@$02])c#N0RBەRQ;6fDuy<+A<4uu U^sҖf;墛WҿV>#ˁ5A{OHH6j$oAz;JJy(wYcNnwNU^>:{zz6^RSSf0C7;*JhXA?^K78 ݋K FĤb1xaH&ىp7N?~xd*s… aZ~$%% pOm3---˳r׸aO.z\NCi*TvẀ`% k&At7<^ebEv|sNJ¤`aMJ$@$0jᾟӨ!9އyߡ NC~JY";0o|k aи,yY1nrb.6wՈ4,߼S?q!xkL$@WXڕj̗} \pWf:_}uѴ%^`<vL$ RW# Gػ&6"äX,XcR,2FIEFڕ2PLhHH`htpfD   M`Jy1?  3rflA$@$0讔=m' rE2wן0&$@$0&]Ԯ(Jy wQx5Fp*\ d*׶$m &vԷTRWvr0؞(GG$@$@'서 G"::!)[@rc' 2Wn* lKyhҟ4B$@$@$@$p3~:ʶv647[ Qp x@h1\]XWBGԌ$@$@$@$@$p :8  P ƌ\ gh2+Q]|"1M]]-ttMX! E1U +WVr Xb}دjZ/jw- vɜ`;<\FNA JVSttrK/>y90U\.%   KͭhEϊz? Sa2"xR6ayC!L횮QDc*nE( ߅S8,hr $$ȁ3tc8Fm3(9P)ƖފUɜz[ +Nt6b({pg(}8YvX 2yHHHH|!(J&u]7BB9lAgFLx 4}!LӀr؜exy|ݷpG"ms2cj'/EJ!؅(VL4Zu5&yV{-U؊W`$vjKis&H#H^+8b&}jX:+~*&D$@$@$@$@`gp>U۴#qYŨh>)C-ΌJvb9e:cب%j -$4,\' 唨doAb:H'AhS_Jd-ZS܊UbZQ7X Uc|ܝ>?X5q 2ˇ=g_wM8i 7:8DU_5&mcYs9__mt2 cpE4R3 d#JdH3m3efŸU%m vm@i=(?yH/-Ef|IeE-:2qu3q@cK "@˕9ڼMz-a6an2kz n(B#mEn?QUmvoRq3mT,VEAp JgJ; \_KG>`=Gix`~G2U@qBlĶ\UC"    $@ǓƕgqZ!̜D$&'2(DݫaxW,ĄGcX:Oj;0e`ft,_֢,,[O%F㜔 Xf"}ڏa :K \Ɣ8X wNS5yq*'՚er/Ď XwȏCEVEvsd@G3 _Ae `)HG>뎒{J]eSgZ\SruϺ잞 Ԅkf|Cɻ:n z?><*,ݰ"&Ab]ʅqðga>d$q{j@ay9S/JMŗX =cF8ƍmI?~xJ>G.\VGRsCFxOIH` pS=$Wr\pUP u=u]:;ש~("tYT>&w`R|k aNFKݹъZ]%;=5 ~2ї߬9r<.۹Q& 097J壿}qF|87 iyXU1 xn0x"V)}~ ݽ_<$>I0HHHH":8W6I4gZ$@$@$@$@7:8ybed3 r$@$@$Ix`HHHHH&@纾|< ':84[ xv}7y;3cb 'b<^ɿߎ7zßk6<{ 0}?\p$@$@$@$@$8f??kGM6~4~p؊w g~Io U#@''L|Vx!U_*0O#gti ~ 88 %<ܻߡnWuWN^YיӳAaz5'|?VH[6C{'PDɎީ9q۱L$pZp7c`YL%A}\pA;V+ߏ$UeIC*$@$ppDEETt&ouuu=u}:;ԗWNC~J˜^xSEC׈I\><O'gWMǃjW?Tsk*IHHHH`L3.ǵmn`$@$@$@$@$p 'e^f\fK6# Os#$  "     q$@$@$@$@$@>JEK/+;TZn(0 D$@$@$@$pe\?6T!$<ш AN>ؼudSVW*vD&[_塰;    _ػաU@daS>JsSۮ } W{棱 :͑ )X ƌ\ gȢ2+Q]|"1M]]-ttMX˓Jݑ Z\u0""~m, Njd;ҭUَ׻a+@^=q26tuth} .Y0?̂ʜLۇ6ƂZv粓]p#W1    A ϕU{s+Z"4Ca*BFQ$`UC1cbQ&,o(D]ӵ< <\h,[ܭhѾ( ߅S8,hr $$ȁtc8FmŃo{=ci8v 61فfAj5=V2v΄)E|Y#:;b̛̄T| hؔD uekgFjH%    Q$pW)TƖI]׀퍧9lAgFLx 4}!LӀ>9aᎴE yeԆO^_C Qѭ&p9>Fi^ֵajLYWG #jܱsSwOvyy45V^-y.Hތ*M"!)V^EmVzap:RD$@$@$@$p`g.fӎLeVx;.Zڗs"仹Z&BBCu RNJր*AfL3N9鋥wԣbM2g}Zᐾ~ &Y_g Ù8HHHHn@B7ܮgqZU6Y{sVk7w} u[+_zS*"_n&M|p”%&qkќ-XȥX"\Lp-R8'- XZdi?{qY6ܱ8#;*%`w{6YK`{M64)%ʬH^9{8jC΂MՃyӰRaWc .F-|ParZp:PBgHHHn4Ae `I g=䞇{u:;ƻ;vJYיӳA0{~ ,C&Ӕh!y˹Cꬲ+4,llQaG 1 j#IC 8 w#(zz.%?db˽HapM]CdFC(Ncܸq6Ǐ#AT?ȅ jbHJJxFC% [jjj~:ܑGEETt\g>ԷB]Ge]Wolu*J4]uV2秔1_CX,yoZ@F ߟ|Y˕шf4ndiqXsP cܨ. a0йQb>EHHHH@'0fJsSۮ }[2 '@'FFOcF|IE륭2+Q]|"1M]]-ttMX˓Jݑ Z\u0;ïp Njϩrs<٨jsƆlG)sڡ03f4>K$@$@$@$@&@'D={s+Z"4i Q5XPl  Qdjt'pG'V8w+Z4ņlNGi.tg1%T5'BB"aL_& 67Q۱~ wեeYQX|:(7~O02SuR @AmDkR5`{)$j)e :[6b8$G3Ұ ^0UNjas9fi$yeԆO^_C Qѭ&p9>Fi^ֵajL'ذa5M{&otiRko{XZL$@$@$@$@$0M;2USX:ɽ/̨d9)͝_iV'Z&BBCu RNJ?|Ҍ:_&(<Rf"    D ,,Ig=䞇{u:;ƻ;2NU^>:{zz6^RSSfϯ!{(d;$opүа0]Ԧ~v#cmW`ۣfIǸqmǏښ>H)s… aZ~$%% kWtX -555~?ȣ~*J {[Wgr6:%YD:+Jgy۲1_C84AKj{y۟COւ_Jӥկ8"p/I ̣a6aXE~̑ %2.&     !!.#'A$@$@$@$@$ C s)9     n20 K/ze`C"<>ƮIVYs7ߙ z쎟##    0ЫfCUNB#-}\ƎW#.#6:+W"k],9J    #8pw/C+#6"m՛03#c u_G ߈+R4 LoL`&o4GW@__N>?Z4I,f"  B^ ŹQO ƌ\ gܢ2+Q]|"1M]]-tܫ 8>3zw$r9 W]6̮_y[93ѓé[9QuW9[>+mQy܆ ۪!>9;![Xdxg $H` PMGGN:kg}cǎA1 X!@gľ7?+B~4LY(Dj(FrL ?6Jل (2k֓G peP-`o64|:Nu@㳘!!0&FjvH]%;P-`IjdݴGE*qV@s19wm!u0V4w~{l s$@c|嗗Jɺ/S@$@$@׊͵n*R>(њTd{)$7)e :[6b8$G3Ұ ^0UN_,s$cᎴE yeԆO^_C Qѭ`nܫ8PnZdZv`Nqe&A(␔zqeYXf3#o3, !#nch LQM;2USX2ɣ}8 I1mhF%j -$4,\'pWOQ HeM{t _bRC'_ "u[)f KkC9 :8&U_5&mcYs㧣s\<˜2x-- M`lDE;:"ʹ ϔV̒v”ރMqkQ'xK2އpǡd8M۬ 0 {w->!i1{_ Svɢ=&    2Ae `)HG>뎒{J]eSgZ):g]gvOO XjjjBzzz5dbgL4wH\:x işoCT,1"HsSkO#1`Lμ6Gp:danP;l3 \sǸq-͹Uk{w5@A}$''… Zؿ?5~:PHawQQQ?Cr|%Y5OzY۫C9z뇒y,"MWL%3sG95^={Ux ġq"XW+سc*# #kOF'w& ꑤ?~q1ZeGV:7~xQL$@$@$@$0|_߈7{W;g~I)|y~% œɃxD~-HHHH3K^&b06j6Dр3B$@$@$@$@$0B>&     !!.#'A$@$@$@$@$ C(\J\(t7&]*qvC$@$@$@$@$tpAmÆ G"::!)ه7@AS8W    KN݋~* )mu QH%q]6 ] P$,0"6B=33R#Nr',Z/my\_<yhBmA^3nxJݑ Z\u0;ïpAG}zlTIUoCx]ygFzx2Qu`,ɚ)6sch, ::8xߛ[ъ ̟b?,dE@ V5#9&lB5]ɣ8щƲU(݊C7Q :pYL UMɁH#5nqc^55,)r,J?6`1EDrJ, DǗ&&J0sf妅^J$̀ބ_sE MhD*) QY{}>{k?zg޻v&=  +_+ZP($d $B#EsX.'Q];K2I`L 0&0~B}fkb߇7+Nc5KBy'L/62H"cZ yw":==f^IzY$S|_m^ |"+Im2:IOa g1`߰g =c>-@\!'`$W>l'#<3LB`L 0&,@XQsOE2F1#.\QK6IKhaxx4,3ѷ@s:{,X#^!sD_Nc"m7ҢN4TөB%98 o1=W8*w%\IG/ޘ`L 0&6pu|ǺhqA`ƻDF`.NL1 LNp[,K N`zZ6V:寑dGi=Ц캈r/+^lR 0&`L \l%wl!YYۻkqRaIFnJ˜ALz>GtbTdD˟EaIh )Lge#Nf$7 ˗ӲQr v?\F%,iޘ`L 0&`/w^D<`$DDD "2 C#4x#`VxȒHD(o jz^'%Wɾ.äQ~|n [aRBZ_i9A~b&F''!)nNq;%#J^xO wRahZ>l)/20{+EV=`L 0&CR#mm^1O+~bػœ#"n%^ clkk[Clۿ?̙gi&D3$]KŴBnu1W寜t~,^ylvaS%*{1&07%n^'Wԋ"(qN|ǘ6mG#i8`L"{nSO%x޺nןXQʱV/Z?al([ ?ŭK +Ol{'p>zu ćW kdl8^w_~V`Vha=`L 0& 0&`L 0&p]`纨F.`L 0&`8L 0&`L \7n `L 0&8L 0&`L \7釪Ù3gp}}r0⸹8I35!LL 0&`O >enFaxQ0*1M-=N$k'J{`L 0&1`k2;I gūD397x{1&x2p<&`L 0#N6`XO`zabLnr9/$/M"xl<=X_@9ُ۾6޸ɱć YZ9yhC$  #֩kkȐAv2&`L 0 9|{=!3C][Qލe@d` |Kxb_2Ia;[g_@E2&@0 /_D-lFd9P2.žaZb$H HzȖ|L$fb/Y rQWW;q05JœwȄo:]kpg~I߷cι`L 0&'ay~ެ8)T. +e0 ir|w;+d3w":==f^rY$S|_m^ | r<azżY WI`5g<{s4Ɩ' y0-1e+282B?rLYO 0&`L 'oY74!2,F1#.\Q #F,QehÆӘzƀG* `ZhN;، 4S/e7ZQN䡾)rxDm0?B%`L 0&NC(8օES.YfHd?dz`P7m5^ͭW͙,)q R e5T]zOcj k4#w|F/f璩զ`L 0&`Ó#8c-EZۻkqRaIZn3(ߒ}z"vTșN A(,@2.kb9V#ζ'RXC0bÀD8\a)4N1G#P Pbd?':d$9ַ `L 0&z@ r"Z@I6( iD޼0^q}ȋ:M!XM)ޘuq&kwcmr.&6-ί?DB܇MF,(ۇĈ;eհ?#Rc古w(w",s~XWNKٍ7\nO0ONE ߨ0aG`7i{pv1&`L 0=CIJ mm^1O+~bػ:*O u1p}߿s4ۻחi!yΉcڞh;qx[,\(_ ;o_Xu ~ * BH\{'|q,o&eS# ϵken٩avz0_d&${0&'N8???p 8`{mrYH@IDATP:1m4#i8`L"{nSO%x޺nן)arU⋽O9V~Oq+{Š^<֠!\ʣeO>F|ɠEnr< Ct}&pfKp'cijo($8Ey,Fx/}xކs$)Á,er13&`L 0wcW~֙OK勥[\$( 0&`L \16pKLoWR2/ LO\IR 0&`L xL@<`L 0&`L uQ\&`L 0&6pX`L 0&n38Pz9z>뇼8I&`L 0&x΢\f&5 #>H`L 0&`L=6p3Qpl'} FNJ rR=J3&`L 0& 6pJ/ǦaɓسnԞ!˙xaAim3A{;zn1j$ ` Y<.v3ʷ$ _: 0&`L \3Ǫ|{=!3Ct]nEqF"2Gz7{y,},>&Il>*rፔ0I>9~n&h#1VRY$$g(xvF_*FOm`L 0& ~@?ay~ެ8)L. e0 ir|w;+.J}'/«ٌi&uE2G慈ro̗lE#9mSO3`=mdL 0&`,釪ٿex*V.aȘ[.c Α!24ai&zƀG* `ZhN;G9ĉs(y}1-QưxzlvL 0&`L`0`kd:D>]c]X4ѸR 3u$2sq`2@K䠆b^bpӋⷹ - ΔoD'P%dir L 0&`L`p`/rgcEZfXz< 'vTșN MJZ_բWI. Q'k4`53&`L 0k8}YE*G/4 @abA7keG^|ID(o jz^'%WӾ.äQ~|n [aRBZ_|Q6G99OҒb̸s\)}`K-JM!{&`L 0A`JoByW %NJk~b='nbO u1p}߿s4'ۻחi!:IۆJ4{3Ν낯^v &W'NnJ^,({qN|ǘ6mGh?H޽S^u;']QʱV/Z?al([ ?ŭK +Ol{ޤ#Z#`%*jZA4KO C7&`L 0&0Yޘ`L 0&`6pjB0&`L 0& z`L 0&uC * L 0&`L :`L 0&uC\MzA,pl>y* 5ܪ|9յ!`>f=i)ӏg`L 0&@_ 3 oF~HVImuI 7Sy}'؆ QnL 0&`LO ӧ8pl'} FNJ rR|i0m B[Ļo{,}lz9Q g`L 0&8}ր)c"}0 g"?uˑ4,x!%yicӰIYzjƐ~ m嶙=Hb=r?y,G_b?0/Gr悴Qcc9u&<=)}r/Xi &`L 0&8UJ8Ds3C˭(HD\TTƲ}o 20s>b;q )mb9F`soJ'FsЂ="1(9c9sДS}T6P(v2&`L \%TAi&}x4Z3$,qn#swWO2/\N^W'L6I/d 2/قlh٠Z^R0,Nڇ-{pL#Ӣ!wP5LI'ox=3z`L 0&@ sf1:/U:K/4 @ab23e q,`VRxDuR"~%18LGڰ]&%Hdr31AQ>?kHja90&*M)Jj(efc?ð#1C M*D%~ɀL 0&`}N`JoByW %NJk~b='n1p+a\Jc[[ g1gΜ>KO3!s;ڻחi!٘йNAоf'Dv姄Ss?UeKg3@9l2jN,>@ĉ 7 +e/#ى?ӦM֟z$ bL \[vqP:~[/U(nX {06s­V%{'6=}oR߀Fo*leq#J眍=qEi:7P.F7"ngS5J-XCȸ7N >`L 0&zM@| )OTqGh;\^Vz4oL 0&`L_d9]t9&0&`L 0%K/. :Tz4# &`D ~2&';O0&'lXN 0&p=9r$nnE~KQa&j3fL wy'Z[[J1B2n|uѼ1&s-s-H:~g"2&8h%ޘ`L pz 9&`c_8AGZ\l&@? #\N 0&`L 0%ܘ`L 0&Gl#\N 0&`L 0%ܘ`L 0&Gl#\N 0&`L 0%ܘ`L 0&Gl#\N 0&`L 0%ܘ`L 0&Gl#\N 0&`L 0%ܘ`L 0&Gl#\N 0&`L 0%͌|T5Ļ/roKM t x.!1z畠/[sܞ)ɕԿWR9ű"#VXy+Wp}kZW#OUY,-0շbρ'NoVcAMR**O%/R뛚MH]=%x.!1*܄o,=oL91GG/s>SrXޥ5ozu/ľqW&Y}|4ħVaϦp;\ Е+ǴyAMo;652O2b큳g 6p<-LNuTH>0xyy^ȾLK/:=0&i%^5y>f6M+RbHxsfCZEHlOb.,Q9 n"rUjFIJR gxWR7QbAYCXYu)X".n@Qqb+i }U7 _SFM>k(|3}ih ]6pp /DEz2\<75 eVbVHSbȅo"ɥ4V9-iؓ6kDZSX&c"qִ֠Yr&Ca"y<+3p:fCH(->ܖl\,=dȱ.{_egbĭQWn¶ v*GG aN07#blX^*jO5--(hM;n=L劲`MDUG}>&o斄t'1OHi&VjS&6Պ3IS5rh*m,VL)L3*6AbR 5 m*$Vm8T"ɳIɩnڳfLhBg{ɷ?r壐ڡhZ-~J61jq<VkCMeXacqsɤqGYK_gv] H# <^MmZK7ڽjEq)^mκ:luI;֫f?V'-=%u_WVt=uSH'bP6Oq4P3g:a6'nZ:S{KH+蚱Yɂ.tMHAti{p҄؜1FF 5Z:"(Ɩ1+ {=/1olAwu43ɝعk'xIZ0`rk,RÈ{dtl(DK>];3P}%dC)H{m\9 Y'ڐM3vPZY/Xd#A55~%څjdd#>7wxcx* ѫPORsH%uH Dje0aU]ħ1r3<ʺ:FaHl^fĸ,@ _RTe 1N%/aX9aS'SLޚM煊!HL?r*w$[:POiglAUݧ u*y)e)ʃ8X #/#~g}sB)R_/[uL)VSXP~>ƪܽ0DH,e!K벑sva[ qJڅT+܊y+xW=| 5(:XS.̛ F^x!|>MI *pʮt]UbpڻK1hh:ށLl-mOPmî%^u4+1a,̼`ͪIT;vaGzSJ\~2't(/G1z)d 2!H)B ЂJ1Am0;ȘF: H T\JA?/hZ ݝf[zUM X24 '%ؑ!XVy`T$xE2\I0u[9%TKeN]^% ݆(m!m {wZD6co?!H#OǺxS]8O]7۽CϪ(3oɄ1h{W!V1Z3{>jOU?tWญY ;JI!BX6sMa?VqڀD1Lt)7X.4tJo9Hs3QבF)]JO#♛>vD|`{Q);Ӂ)0\uDQeGOQw=#Nxïǟ l ^#xK @T.?"g,f0~Xs,4@[0:vN0_ΖLdk %ڌEb̘Xj95RT2<&XUp9tZC c:IELD 2r)TmXs0be3ӰD Ig VKl8@ǖ+3C"`/!G&>nL2wC͙ 13a* ^9>t/W3q/4X|3B1R˰tF8(=v\uˬ#}ًgG`\p(b"Ca,bQL$~44bg;" :KU4g&Fr8Y ČGJ <3@LbP٨N(-B]ųxSx p<{?^qr}? &wecHͥ{LuUb$ԘtטAr+D2B e9' PU+%vAW $+8F8-2g C&v#'xru샆$,sqo6[tkc5_%p3y:'elcړԣK ?4;dYhҲ9Qoy h&F0 ThWZר[, eq阒SJh @_/df7\͍N6bVڣ&/]IFDif #)Z1$BK1&Qr%&bh]TԠ咪)_>W)^\/j8#cfl:-6uxZHM;Vэ$eq2Bh;}ߥ݆ "sySCx#he榦/iLfuL3_ lj.%;b_5BF𽽞iכZSV._lG+J-6N( Di?.eG2aztspƴ[d"'!kZ}71?t6rr[E!16|xc=>;\%k4zh DND:z=u NJse:d1!t'fe/ wyCgDAH]4ȣΨ@9:H[i*޷= {-֯޽n\ns%Bb]zr.c_yuH^RZRFܫ*Rw3)4V-O/`Z.J/;-r7^:*Xt:RYJq[Jl?mRnRڲz\$wticKrxT月Hу=v_4tă~خQzA S<4!c*ZҼTJ^~8g.W0c &FW^TYMe律J8*:dЩ: ތ81'v8=Ga ~b- ;2r@DxIP ĦB:foi9ÑX/:mԷanAJj$a$*cl%åKB1n1SCoy!ۍhl;!͔F?p%uUE/)=kW; sÇ{,OP Hmz`ѵ>&ϥ]͡4+ S.Ș)KǴӍd=bάN0[pt߫K">'yī غFJRmF$ATL%[aj9/UD.t֣P<؋vYtVscDb Y&ttDf/O"dl#7Z+PQ #fMfStttL lDEGʊ6pzҲ&{T@G+[4V}r)szT˓jʳ+#9 ZbĦNk%/DODYȞSj~̌gPy UAb%ZK&$mBhĀĮUZX?`;Rz`d?rKv`6<' =dT1 ~!#}m2u(۴/Fd6=J3SU|±T$d5Kܵ|lS5҃2WC"iZʷ/%tHXV5u^22 TsKk *z@lҢ=9Aq>* ƺt>ѫxa@ՈyS4hsS9ُp3; >+ PuKƈ;@/ИBdDt:oˎx"IytJa$#M=]".>1wri(ǂmR. 1zrX|tRE:דd8܎iD x2ݯx*y{W.*cv/ﳆcĆSKl*zوu4Di%~ pݎ4!Zs=랏dSWC[dovMzܭw G-Te?Rp1LtBv-ڜ3um8ĝ`zG9϶k*-ψXiCuG[mj`cdle!FŸ^\-C~A7(n׽b( ǟw=V^9wu!'[ Wi9 wm4Ӎrz6acsf-%ҲAyY(/p$Fy(A=P-7V `B>!ޙtҊ,zxG]f[NJĂ*ZٺxP_/*,T2#Ϡ_JjZ{IfcpY,Z)Gch ,nLf>Tf1]V>'&=:&P>4;)$ܨn]S+lT]F<W' 2X"O-;^3W?-;C}Aewp]:Gz18ZBȖx `K JJ,jД_B  Wy뙅Sk~y" }u`0ZP-{KC=u21&p s{5RXq(W9752C|V&~ޮ)X_e=Ђ J] 8;q*F9ym\RqHMz&{+~]j~>^b08zs-_ eֶl2YG% \K٤odo,G7@@JI//e*ףE|E,[CĔ_`׮lLpV0o o]7OR9KS!)@BU36*AMGFa[ݝta/W{%v#m{"oYKs$&8dhiƔ8HzJL_QA/&% A=Xe%H_o +"FN{MKp Gab+NR8k_c_&'f#,V:oEz!Vaܰ' 11X] `c`(\2ɑKl'Wx(cRPWɻ"B!˯`tS< 'kP҂Jdڽ;y<ɧp(Kn[Gγj_@} 9\!} +:!&A7ZgZӁ|LMN$>cgeT᠗qkPѬ/=dXsC!fE$RD\q1ZWVQXcՇ4i ڤc[s3IOҕ[_f3Pۋ[|d΂jVH}RwɴDR4๩tXYY=|:jZox@KE~{+>!nV(}@o1򪫑ChqpdZ3{7 TD.Ƭ(> |kC;r=Kra^8$v}>Vꋨ?)c_CWMu2y0Q:ؓel,Au^$uJQl3!No8Bo !YywaqdELDQ /Hǎf)Kg J벱p^;vaGzSעNmgi >5 u4, )ˍHrJ3S|Aʀy2wJY/X.:V?*|rQV:(3o&&TA#dZ[)x$w/L3qc)r^gŮ_G' u(ύX-q `-ª$ER|ޔ0+ (/ځ ~Gy$#1{8v݉%U٘e4bac4@BU&kiӕ]>n܉vg@V.=S6T?dK'1H;H، -v05a_#PM]g?AAItd-,EUv)25/aM,wuҭ95K#R}S'SNޚMgp<ᮦW"A@z,7)ڙ+QT'ڂ' )sʩ(/˜3X9ojoBLFQDe+*'[/ŎoR2EWǧ;xV:>_HeM<'uڟ9UR_D}pRb/ڋz: [GMuheN] KR ܄ʝ~?'R"?5^ 0~%N/vWW1#4ᑈ C>>lbB`bl)H:>ҏޗa`X*6SZ#*ԽP ˰tF8}m~,&)99p*pk2/!NkmZ(U7 uXE1ca6ނ?ufΏS򰏕 XA (D:n)1.m&]Z0&t6ZE}ӍIP$3 b[`L` 8_"!i\wmo1z:sD<O]74cUiV6R_I)a5l%Sj#Жf:?Vn)I[f| Dg+. \gOb/6aڞs4pN9W\gvRO1HFXԌPk1%bQ>1Z_2Ga`tE^ O2 jD(Y>\V΀_n,’x̛MIc:@cL'-1Mہ=W2caOU,Jo;' 56\v t)lfPt@6O=]>OPC]\%K88xu.:: A璄Wp4\O͔ʇsW@By3Kgk|7VݾrJDeT>ef #$ [FKm)H^de݆EwgW.3ѩ.Ew"QO(?X.^w{NUñH7a,;FEٴC%:x7ܻpZWykF)NۢI1:GQ]JNٯC=0Mqmmm؞EH5-[SzuVK.i]\\Sv:JܺC__!mV7|vH<0@7֯vf&냉Ш{5x5f0|-yz/j3NPzs{n]11׶oPW xlk:BlGbQaeYHAwL۲9+¹X2mţؽIzW8pʝty|m~;Z:wuϖ;wlτ($P[aj9- oAiVZܣ6[tIXy!@ַڄ5s ZJ"WqKl|BIף$#iAvZZ57C*ВajnA^Iu7<0f&4!h>frA*%->4ǟ[$uU HUJ! s5r^rcUNZ;:\_2T"$uQo4t]iƞ3$aҷW^#oGX!`<') Kri밒^N+ޭ\kQ3Rd7} |B.U/ԧ7`ZZvK#c!-mN$CSCD#]iۤu/nVTv0Ow:i~Ϣ̔u}2u(۴eeN=bIxDzT˓U>fD8}2gW⌚ARR H@cIdumf;,%CNJ{7!Q@6]W‘xjVQ?z~hjKҬ]gntX\s{wotMOǒxܙ70z}nMHD4i!3 ۗJϽʩ+q+#>L]~Uңg5x@_?56uJ']?zN]# V¸0F>_CaL'`0(ENw }HuHӋvOq}G]4Z Ȩ~oXoDYhSjO̤[ɩ޲(G:_&yHO}:]F{}Ьx8^wZ}%}È5OL2!Obr&005PN4{^]U(nX {06s­V%{'6=~Q.l\PtxƇ.L C|?(eIr;t#IGs != +K݋Fmyq#r6aoz'nLQ9d! Xek=_ڍqγv!R æoXT&w*E-Kry*bؼH:}j\J)zÝ5[.Z꽳ٸq>fHgpʪ Gc'`*Yy|;9SyMZ rT&9n.ec '@5 L_fpD`L 0&G`TxcL 0&`L \. `L 0& `L 0&` 6p0&`L 0&`L 0&` 6p0&`L 0&`L 0&` {V)۷o8#&`L 0/%Kx ˒ p'NĜ9sz0&`L 0޽۳ʉ/QsL 0&`L fl cٙ`L 0&p">`L 0&`L`0`g0`L 0&6pp`L 0&`8Xv&`L 0&kp`L@} Y"֛1:VԷ]-b☡`UN? R5ȕEbL 0G(/`J| 2?!\nE.hG#??<h?q 3׋ߴ!N4|z k@X&;D!>@%3>Ŝp'0?d~v-m{3A<5{;oMO qwV1fxyyuZgbGt,S1:x ?hD˒`a~#ުb"Z_kP3t\~?,_BqM;&{ 6f{!T\=.J=r2 Wuf<79rȰ;oƏw iX|~NvJFނ,Y4]"(w)9?Ȇ0yOΗt캝1¯kFܟ(_ͭeLJ:ю} OŘq,`LGgp~|u%fLcÏ# u]}:Z$ FЅsY6KtqK[I?{Wu{zJ"*)(1،j(=!&x^<7kJڄ~B5p4+b\^C@{QiGjbC 41Lgfϟg LFg>0ZZ뻞w@1:?{899g?gl.CycOp翿_!@w_Y1Z(OH?mU.ۇ'yJz;'shߣ}'c [ѧEXI_ӿ%ڪ[wk+y!<p{3 C{9L 0vp> ydLx0ÇOߨw$)g2gQeY_ʾ[^O4˝vL˝tx V.8XZF)ѽmSx-dy_f-c .WH 7ᏺL(܌%~E"7OhLHuLॗ^mQ5aZ\OLa}X zP}OS_:%|2]a'|`s#@^u吟UE_AQgw_\ioiQYC럩=brϴ1Wᄜsh~_[ ^u0dL 0y'7X `L ?˰h~[ V|lW/K} 'XG@X_M0|| 0&& ,eL \O>''\~V`L 0&`;8 ;ɑntgȝ#mtΡ4r9(O`]ӣhk\3>S\D]UZ0Le3 -#c{J4 M(bcajȎfZᘋmCOw~)XcZ9lA2uld*6y@3q9r>\)sP](8rTpGPQ ǸHfJ%nye8"+edps եswp.U{ik 6Pş(̩fpٛJa`> |Dwy,;k䜔KΆ삅KUҜ]_z_{av& zN#9E]9th %* 3*Ėw } PK6Wlh% gS_؍i vELݠ܏Oٕ L(m(ˤkUc(kZF7uX,%xChwg?ޗwh܊QCE%5ݪ6%^ FRb;'VEUۨt9Ie2FcDXg Jiq1Bè|HSיqcKZhJQN[rM͔kXuZ`;.gDs':d6rtyDɿ(;<` #qZȴTuGrRd.<~nvjMG&=ccK6|E/aWZttC-W?]:|bT׭cf)Ozx`jk۞1mv ,‘䇦rJ8L3\ oݘT<|:Qk06KhVi (34+iwxmU4Ih~<1ÑQJqfJڡS6)Iةد:ئ=}?*idV~/2j7;zi}1j.ӧ/kKdwtj솑͒ۚ <Ď9BQQ/޲}/:վA~9v?۫AE ߾Xj_$P~x &EԈaFPW":ajZ=.ѯawRЪ/>Nw`' 'ŜHCW;сь'޴ո}֔&,(Q|6DnY\򾃰`uɝ娨jp߽ ̪ŞmYh̨Bu& g]6aIG-Qgǽ-#/-7KQ~9]ra.Uf?q}]L'*vJyr]3*]g'1d 93NeBWO=R9oAݎ=xj]F0{ѧzKBi%zncF >&Ov9C egӎJ҅R+mߵ8bGqfo~9\ КaŰ 6'DwYlT/TCM(-]}W_P/pCa/*|s);:R>x{PdzRq|Kq޽~.&1zkoƼR`_( Gܐjr^o[8r@zlބm{`;87S{zo}Ai=$z+.xwv`Oyaޞ4ªn㽨.(x>[F8C)n^]ŭPk5Bŋ&nD{kx|ӨllsPܴ=}D dtC+g'`;dC_}HMHwZ:k9]ς V,#/=MRӪ/iK_S솑|meFvݳQ_¾Mv~ eW=j \~ֱAw%DRLZ˩Ҁa:? ۰a tڄGy@Fl.D@to}һ|K_"!K ݎ=@4c7M9D_!os[(zn]84DL4 4,raX 7'\4䐔g^N^l]݅Q Df Nyt蚔[-[`InB^08vLDMtſ| r ?KiO6‰y}V!Lh+(S1"&xݓX5^W=[f>>'\m?]KfR򞫑VdܹHJ[M1_?A69ʰݙI@PXd')J}RiR;&=hio=֛;Mjo~3tƄvX3IgYRr|خC"7~uDWuE'֕"=݌z T< lEfI-t|,3t>Kbz0q:XM-*&nJT;ҩei}qEvuӪ/ mX_|*0Y60}+ dg$֢޲A =;6{Xn).a/{o9 wr _e26RAv;fwikL"l9^ Ј&2X{Y \C"z`.ԬkB&aF-5k !wdIN9-Ht7b܆[h<)*awL!lz80o)H?r,S 3#H9\My p?VDm>e Zń2!! :PZ\|;3z U7 t9fJӛCr.nI],s~|۵zQG+57 P MiqSXF; q"bI7[.ʷKκJv)~B]#dOGKޚq ݁pl*K%&UOҖ%n5VG=bTEn[Jk:A/u1T}*j@N8yߝW'+sи̊C9YSџ{L+iFύL@g'h ݰTG=ȡbEVqP5vup:1=95emgI_H2}=_>[U4v{ĵ"NLt j}K>V;԰?L=[f*sA<ct;NI#s6kO``tQKJza?4Azh`%,Gc-N$FG ,] a-ٿfz\`0913N9Gi%sy$`{$ZηF3>sKGrgO,0T6Ts،m^3ԶX%x>]K>F8tͪ,(ů/ -N؍ڬu4pxYݏN'p&&Lg 7ٴY2R͢FxiJLͶޯW^_c .ͻN=#bhFZR~umCoxgRO'"vprb,=OQ7<, (+*At5l 3⒐W=z" 5bưmXI4bĮW>2|WС=ŗu{XfTb.<-K/;H(5feԑXr H2\;6G7(S(NXd^WoGA696y4:R۾敨C|Z_,9;zym"}͉4:؎BzP9#% I&do`p%k𥉉߽-ȰBtwݶ\Puv~|, 4GVf%A& f923J Mf%#HvqeTwQOK ;j7 6DsԛnypKD:FE|DGڌKA|k9,X96` L *2m8A3Xv 6٩IHHZߺhNtե~JX^#YRsД[[OǷ^tU?ph>9TKW\ʧv橒B"~Do12)q a*ulgNY7/R׍'g\Б֗Rލm~p`__ڶd/5%ږo (oz:_(9Xmsal} !z_Խonz·,Fe.+4T[7lzi ki & GA)T1nXCJtIlz{bBOj~@O^PO~6u~}_QzN}^&~}:xWaLEdsьE){iRA ~E7Exx^$f_bϳ8~0NHy3)[|H/*f,&AK;ǣ-%4+$h.Ճ1MALa4fKqbB#f =xٟȻ|#S/$e>]pRy#eS!%%t,T?h"-?z e8]FuLly )Im׾uXyHEivGkrKjF:#?ʥ0m44䝉U0,ƉG}hоC 2&g.>1{&]߼{&@%{1gCCNQhzX+}DO jX5ΆzM✺)) _sr(zn#?a5=ɟB\l;lV$EߑH_]Lq"56 ;G=? ''򩯢fS(b1K4\\86fڼ47vpyT\ia\#azi .O}\8par|&͌L8հ`} ZFޞZzųS ~ʔ3GQAfvmՍAeoAUU z}>;.zkNSv {vEDKN|rmƤ㋭lB^_i3ģ1 6#v0l#]8&5 i9ǮR%qf:kAs;f:l~ w])WEx(](84kyzuxNʿך%=7,?3ΑقX@fVExMa/}D<ǻQBԪ Sg*QP1_k9"%>”%X*Fvb~;V`eZe_~تpey30ah/4N/(~'(@%@|-o_n4HZїS,_ ~eO|ۇ 1lf8|B hA鈦 ܼt/`OtH}-Eq' z`?M}wnEW/lE[{Q>TA%^EIZDSز!M}YμYf+L,*s-LbQfDzθ8 Lf|5O0=eE<;2 6!My}Uŧ.ݗ  Μ9s-G:kPR׉:yEcMnǖcXJg}ݺFx[1BRnh !Dv[aYPT}?T:ZJ.z%6=̲:Wn-g9O5PƢcmT9v{H594 +oPvHQĩi鎊9:C ,I)pl;NTrJ|;0xD%hQ+^YUUѨ=7J^^%!jHQ|TIׂBK;LxNNSe1NP}kXo=я+yUXKTkפ'7pN FA峔QR–eDƢeDWQ9t4oEKKޓTM$(euQ_"~GUhw OsmjCU|ݏЍQu@jM+|GyɓȻMk;1l}VBmɿ|o*SmdKkIDv{ִɺVXCi}b{'W?AkiC;MeWQB[jb Yh[(vщ*,\TmM6'K kjuzmiT Ya TIRRx$|TuLHyxbWOvX]樤30/nFqNx Vgʈ1/}R!7tD{ֱ6dl Z9[=}ݘTf4TvzEg @KmV;dp!㾀=RqoPF]@ykumovر>`^Bu5N%-<#=#+zPPv=@TpOOkc#,iu`m ÒZ#hӦ尵 w`#.!E 1@_Q h܊X⥐ӶfdT&kaXSP`_iu3<}9Dr#qE)~]g'koƼR`_m!*]qWY%mp@ƞ~ k u(mLEݎz41ؘP/h(EcpEu^]z1Z.4$'O_i_d@IDAT>bJJKEűޟƘpRv7L7n{u]Z=10,ވ5(VҽWä"O9'V^[zWTڇEdm{0Hoc&v*9z6х{4t`V߉VLJԅ3Ovq-k[G}@&w>6#)F|,l&:i*/D^xoP:#ۿiDEuN {]x5;d|7^+<^:9gưNfD7uQll 8838ނIXmOή)']3Ô7==h(BCn*SPovMvٝ iGtȱIHFwcP0[,ưB>ظ ۤ{|rRJY2`^lq'4=ռ{`j$|`n~&*vt0]}]ؖDmjqg/@i#r~[bdBy]־^Jӈ bkG]HpJ1P9^bg༣}D4~XwKI$cc}z+K@-K bnX wHݦTOGbr:>O]S1 b^:X|芞Qh.nQ&㗣q!h׃=Iq藠q'Xǚd$3a1 G*xb]&_('+biwVsdˆs$ur""^UH,Z;u[ya*COΝnym^غk@G &m5Q_?A\6İ1͔$Ŧ7vnC?~y=2~c)g| YlCnK*FZ&(Ba-3Oƚ-"hTYҭm9rDǥ!>DkW*ش*fZzaP2?#]-ąTAڪAb}}:6K 4t>&HaCzb4mB-Hy}%i:61*i I֊UKmұu8?E]Ҋ V#MOη u2Iͳ{r{n# 5;δDnEc)+p%.Kp7&&5/L%K$[Mս9i_UF]@fSw}8섉f²@ˮF&}#NjW2F34 ߃4u9 Pyu\`JKhW\B}j]4ՄZJKwoǁirLd>,RɡDD=oчImF͎!?frJ3l(e%Miq~"i-'XF ڏ^$칖Rƾڧ 4+^!t%EyQK-4d6Pܣ쑾3N#5f)tTja KkKsp7{̞٬bD%Z]?څ]vgi貽^xO BwyV;O %dMxdG=ܬ b^.Pa~gm@M;A_diz8PۊӲrtl/sb-;L6ĉf ӆoӒ=RV+erj=I_~G1;~:tGOp)Y 1D>vVH!ĽLG g:N'?5ʯg _,k7m6lT @_ꖏXöjw90>sK7+nti&Ǘ~:K<#~B:qQjOمdi05ƧHܠ==A]G2_=KzPͪIy~hY]tXm4:mec4hN!RPW,A jM7"Gt'}pbiH4Gޡj//ҽbwލ&AF4o:8@/0ȗ^t[Gꗋi59:ͤ@Cet_^A߅XhsB[1RqQ_ 1)(L#?#{N.GiyVAg4Yv9SFGvlټ*=6|պ*r ~`q Z\i$/p{-w9moƼm\x IK_AlWⰳq)X1-0'n٨W78vp3 {u0 h0H4nɖ$۰ɲ't+ꥇS`5 +s1Tf Q7$QSF, V{dJ_ 6ȡqqIȫE4AZo5C,oXݶ:4J'.: !M)H1eⱡc*,7עb UF½B'NM2dUEreDaBqv*3sjhzx2 eHuY0kaRs5٤ʺ.[{_tUK,9C0m:Z&msH۝ q^Cvj/A-UX4yS:<؎BzyB%!Ʉԭu3ei_ Y8wh7-utY˕.-ehDw=XO( 㶪wwvYO^,ͬD^B3IWP[Y"@G#eHmc듕>lih=L1Tbٱ2f(`Oދz[ٸ֧CG0bhPGjʮd)5u6Iw~=i)ϣ lol43S۾ƳoɷJKZjr4L]Fjו߄m7ns>!γ3Cյ4"TSR*$nU^IzDo12ShZv 3c %lR+(>62WH伨՜y/gpt[vG%;YoJJ._˸/ BSX>J_P[e`#RLF*zȱ9Tdϒql#[ |O> |E994}_QzN}^&~}EGžW cH5SwhbǴ~:%.z`wq a]4t FtP4Fr.rҒhDGy{A[TbUzvm2P~4"MD4/F(3U$%DJіdW~#sM[Ջa(*ճxq1&3 D mBwLսjDV ^ӫˠ վniEkzyF4m7e:Y7?ݧTttc~*T!ohOA]8 :F[}U4na'D|qSO_bo\G)󄓦A͉vR(KJU fGu`xN\2`>i)FW@y)cYI>{ZCaWcuU.R< 3vƦ}5=pG(і|dwݍ#/Szsf{PBcDB4FoǩBAu?4ߍ__'Iju_=Vêůޟp6kb_}NIaů8'6_C;P> @4sOX脺h#)ph2Nۄ6rMuYQK#'x,XHNK mPoۜVRӋ+97" F&tP&uu[Ԭ2h~f6niCJ/Z*#}6땲I8MJD;9<7YB頷AuOufAgNS:җE/+A9JDe[ 0mN{ON&F3bcj _l>KҨ¬Rγ:ERr{s;Ȝc76Y8F$8sz&pJ磂uK}*1p*yDB%~h`.74\pp3GO_䥡 ^^ ^ 38\ͥfL 0& \ xN7&`L 0&EAV#mopM[WP[.zUs'ꪪPUׂ (%9'FW~+0}v>7"ߩIZz|!nfɱQ^`L 0&p`'uȮ pU±ytB\ӫprEV~CPf b6_ҘF9G~_)yp8\SnCyR1kT9wBN5!^9]2Y GKOr;RK%>aL 0&p`'5Low. 0ˉXwy$.oh_Q6z˵"l 34kǓnEף_ qPڲxۛFgEoZ &)b&i(,(,yԉd1 ;} ^Rv_ؙ1_]>U-v;4i[~b|lEqx&`vpTN3u:bc?KӲubNn)Gr8'PeAQ)mvTYjdY5v~l19܂|%?%h;mѼLS%}MNc֍r%NM݁!оsj-5H209αNdVS^dQ%HsIΉU%hA[P*Z֦q\Pp޴ᡌ2J9TuKˠ"~Gw64:`"Pm"uwvlWb0cLiBw^5:IGjG~MgHizHǜi=j ιء+[=z]HK34R}DMI>9:ɹͽ]HX/}0fzl3aOCg)~>uv7K|םT>Jdit[N.ĖVk}8sdڗ -hT"ʻ*Ѵg`S`L@%JbV. t `//ž IŒzPPv=M;q~h(Ec:TYQN(l@_?#=Ћ҂R`_m!*_V$IM96 n C:8>jY+UXpq+Y%$oa>wy9xviPk5#8mkFFANX1h;}H]b) `%i`x[*7Y{$֔&w;uFi`,^$H6~[Eŵ(;}}f)R<(\8=qq-kh%x; ν']b_:[@}e8F_nv~<8G1hz4z :g_$eQE$Opp󡖯}]Z[UјMq~@gc݄wqhd! 6,j؀J)}˷nT4S%XEKӾB'_.=0&`\uӍ-[|k@^1`]"1|XNt7OMJw^A+u+r-8zZѥG꜐ 4Ļ@{piumru[L&0* I4DSDYnNN}l^ W@n.EQ؋>xO2*&n#o*]Z8)Q0,!4 Vm^Ȥ34k*cnNa#E҈?Eɨl4̞B«"-ʔ=ޏg[Wc4QiE!9s> < Vc;-k [6ܵRqp\rMps~rH3bQ緥 D]oeKƣ /)~:&5 ,K 3LqBg<5õio=Iq"vlCG}hH9[|(|΢Ĥm#,\k,yT'AǏ.DmWVR?͞1KI;{yG0& Μ_,)%2qH-!  uL˱LHu;rN+HB5dy)mBYj)CZ-r;Ш'chۚ-97ȭO˹QùT:EYq|575uཷtZnO48L_7uHyYD_&IliJXn)Bc_,<ʄ6ξ"ȦYTS6_@ĹWҙe J'a0[/(,dSG$2,G/PZbS8):-?#4C١#ji$R\$-sWn%j X_}uyh3vۆ!3'šY^&:ijl6D7&`L B'R貽ؓEm|<:ztEYm~ vj'!V+KzL 9036p?by,*e:N!Σ@[iM]iI<2AJ6CeO Y+A龌gRsQ'uM'fo594;r;SbVާ: 65131Ewt!Քw`L \C$u'&F:u|>m/0::8O8:kr!un~|+0=',uqZx.n>8Jo< PJu)q(lU_.QirtG/&8-0MXG!m]th{21:"}g&zQ$O4Kp0мZ~CK|ڍs9)97ΰ 8ӧD[((0ՋڄF qT7/`L 0;8sD]s ̕Fa%̓F'hGEd{J\~1[ Z_,By?+ZYQZIPkY6Y0kac1%13Nv|oNp"dGNCpv<2bn\Z*[j3}˵[aif%zNMJ9;dumA[(~̓j؄3⒐W=J8ϢPrFiJ ۑcFy^"шN-No݆2zQ-yJ1eb+HfN٥7,DcSFo+=y/i툼dyꏤ]C8yF!Y15oOcsA-6Ypw>Lba0jLn1ƌPuv!K뮋ܟb\(3i޷K+lfSVW;/eI5o|H5?g]is ߝ 7 lM#u >{p]|i&o;. f)~qɤ 288Xt-d?`Af>Xos>茉%r63<*t}un/+z6Xg^/uE=i_ٝ&; ^UYuTB\ d_/utݒڠ+ow]-zڽ+|\?7M:q)5Kflϭi~=kz" kkٮ6-æϷ _P܅ss/9}5Ycok w=j_BXsI h%L{Y."R{*yۇkij/-"W|Fꊀ@1Ew؞p %_ZC}r<`NLI,$lhPЖ^3_Q):C2dFSnÆ] q`_\2K2l=JuaʘaZ;V?b6gh]ڼV۹]d<,|bߝZo[v&^w*R /cw}yQ@h;H r.J6ߡ (W7*zQqS\" " #'Nj/6mn}&٬ٳ{6Uφu˿lܔvڌiÊIɓM |ĺu;HֈzdXJ@wpFײE¿c2]okpՓ@M:AT.8s*j" " " " " "T4e"D@D@D@D@D@@@" " " " " MC@N\J-DD@D@D@D@D@" " " " " MC@N\J-DD@D@D@D@D@" " " " " MC@N\J-DD@D@D@D@D@" " " " " MC@N\J-DD@D@D@D@D@" " " " " MC@N\J-DD@D@D@D@D@" " " " " MC@N\J-DD@D@D@D@D@" " " " " MC@N\J-DD@D@D@D@D@" " " " " MCiV@ lܸ~_oa;vhcǎ#8Žl޼9kbG>Tԇ49_~٦Lb~z4{y(pٳڻ8؏?ަN}&188:*pwT&&&k;&^aKm۶&NXV?*p oVui[eYKYq,D@D4cioOz^z˃8Qm۽{ÝP8UlfVτ*Xa6zy6ꕰyzص -g߿{8_ؘ/- l`? D2@~E؃yji1kح;_εycY|wj_^[Fw̃߱W|;w yKuPlxZjUe5lr.}u<ڻ3ٚﰶG9 v[31l!W2xtl ZYn;.k5m\D@6oF{nWub `ο^ƹf*{;?1Yk{T2 i6|ѭߟ'4ػ6-O?/[ #7_hۙce})i4rv؈ |-69tp Q18^m7> Yoyh]ֳ<-`Ø_ܨoEf}k۠(yE@D@EMw.撯)>λ1ފk-60h[BzŊNŖ/YJF{?>ng]bSGIuﳧ3vl`UtҊQ,V|&PwgC* %m̝  \Z~hhZZGo=jC=1aʡρC^`]=f.8&o9;g\;4|-1o?w[ɆfTڞ]b _ћ>%lN<{\+++Xw`õM^x,p>p1w}XK`6sI5͵qkr?h+}cɣbzmKQ+C^oyws׹y5M\S0raXǣUm_~pN̿eCϺ@&+Kkъp6n*$Y3?dwޏsc}ژ/}HTH }(Xgľ3fL{b[vXGGXŇMc*[t}nh>ۏ6*̃vw,Xb[[[*ۇ d={WZaFqÂ\){3[ݜrFwKacÔ^/oOI f^~znMs1mhLs~Ei|d ٲqzyd||:ߗh Gab>i[Çw,𖵡w}m[ڶ/ڮ>Ϻ\g-W;)Z}6u!P||ʶ?83 I=˞ oX/ϵ]:Zzlw0]Oڮi?l;31|3"AY 8og-՟Ymֲ5aza)ͱBE1yֲ#<7ljo.woֱl׷XuΫ cZCQ|: ak/[h?sZ{ ̵P߱_yup휼:/'\e;۲fڮ/ۭ!g{ɜZ·9'pE:R1?_B玕 VBD@D`8lnឌ^뀍 _IeD1[h=8{3N(p³P SXi\^ek~pW͵ ;׃sڇ.jGoG?a]!/`J:ez>h?a udo3@Ž #{ӎx[cfJLj LJw' L-Q??6pTS'I^{^0S(pQϋ&?wGapڔx; 3גmo%lO64ԲZNO>.Nv<~w}=zsmskrpBqާ"pץ'+6B;=Q 93s[. =}m0W\" " eKw s}es¯]veEنgk?x}/D̘3߷+7fW_ l9ێPt=]o-?#T]s_fOc??)g( y/ ɵgdΝã|3nߒ\cF% 'N#[ߵ>t$F7T,ICOZ=/nÓiuS(0ٝZ=-_Q_H,o)uC[_U6 n{w83-|XWmIòxN\amJ;7;$3 ho ?zPԱX:o|-UKfS/5.qH e{NУ~H%ްO \t,f. ~.|c[lCŚY[n 1e12K.;] աzb}ΡW:OhOm8]sIqنMl}І~'#W^x鲓g ʌ?XfB^˿q}l3靇NIk(s+|I~~%ŝKC^w/d }pm6uֺi^ nqKVzmՃ6ᑥKl#w3:n+cVٸ6џ)}!~usgmTrͤ÷هJ~tܛXן }(vMq5k[גB`O[#-[7Zۺ6pG#'YlY k_^nxT,d7|6z/^}%E8 Wۨ6X+5N}i햤kل eo/vӶlk);^E@D@}u+l<6ߛ6iɵ-CɝY^ߺ,koևBf{ꩵ᳧3/x.wkm+)~>xrV~gv&ھޭ[f :fOwON{%kǚ s^@#k1b?Lm/<>>4>fRD m[|C쐣 #Ua[(<\oW ̃BA צtYκf>{1/D,\<ۯCqan}3%[BP_תV<>9 >J rO?_ _XWfd`Gp[X_r B-pŹwM8<lE?>-/Oεh:7|!B,ge~̡7M e a;F\`;zo!pZhӣ% IDAT`pmϩY4zL>:|Zt"kڥ6a,ThfRČkO _c6/{7&k.LH@tGm f|ϮcOmۘ# Bv҉mS.a7}l\VFg]~-6?yOgϵw/ cN ;L=9\,/{;f3 _o}_?M }z/d#vtsl @Kg(sXP} A:AgL,37߼>-{hvo3 ?ںl+#rĩo+sl??ݛUK`S$~!k mx!7ΰo 0-Td-=ÝӒ!puzc+fŀ}C̽WyKc.T {O ;Mm}5;/jwLoYލ}@Y_wrٻ0fs~u?0{r31\![`2=ZN9/gO޺;lب6UKX38?Om֬J0ru8PcXin1:xȬ[^:e0&Z,0rkDԭ@(R nc.5oZ;KbӞ]q"qhќbm> .WV"4qgI#0aJrfJ `C*#muOC1 k?/C/r5Ω;}xOz>eR>uO+$0vX{뭷a? Qmkoo46:P#S[90Ydh3'#DfɓmݺuG0pK+kBܐ*pj[W=e{;r&rDX78ZKehbGu= 98XƣM`/kf'xb+=ClƍxGqwr"q(zPS/#" " "Lbꫯ&Ύ;zicir 'xTƏܱՑXڸqlҤIj#~,ID@D@D oݜ:ujHzT6նV0aBG8RYTߙ2AAZ6"$~aQ8.+" " " " L@N3_]MD@D@D@D@F8#k" " " " "T4D@D@D@D@D`P3.+" " " " L@N3_]MD@D@D@D@F8#k" " " " "T4D@D@D@D@D`P3.+" " " " L@N3_]MD@D@D@D@F8#k" " " " "T4D@D@D@D@D`P3.+" " " " L@N3_]MD@D@D@D@F8#k" " " " "T4D@D@D@D@D` ZdhPd]y%}" " " " " C{=s3nBS b 1,{ivƸ4_,p/40i6?V@cų7f&%6ZXӬڝ5j8m94ɚpjyo_VԻBA[:eJ^&B28X|-YROQAMbЧ:l(ucL8&;8HΓ6ܑz $'$l^MRD@D@D@D@&@ YC<it|&wovAR::c(%t5!>4i~A\fWKc\@F86Lց90~6Q6lz}l,}ab18禌M{|Vnse-ppR/d!8BZTbm\c.i[xhN~IKSN,c0zLI~:iU)wbcMOS>pn?s(NxcZ%u/;W->}611m^9=ˍǕotI`iN`}y{l۩x#4N|,r@%u/g7GKG;%cGc7ڽz[N]d ,*}yFK~@Z/)" " " " Jɒ8/y2I{$j짍ZCAOKMG \chBmI^D@D@D@D@D O{HtS҆~T:CGsY̾6c[%}ϥJ + HOO{}# {b\=KÖX:dbBG\* mȃyا ņ :]MD@D@D@D@HS"z{T@m!E!ơq{;u8}j)p0yĽ!m^,J $ZD?tC.ڼv6&)" " " " Wy6vg}rte,H 2!gV>3>F_QYK'$6}>:$c ;䨨sl^G4G=͏lž3m1qa~,16FyPO8G>cz8~86OIAf |<7}DcEmC⠍}aaFuD@D@D@D@F<)F?>>lO{8p\ hP?_lT6),o /G?@Nol8$QE .)" " " "0 pI a{YH(i~Bx!灍:${=˗ ʿ05DX;)eJ 18y9i†6΁}PDeΪW(N{M4/ץXr >v(;>ce8߰~aSeaC !#X4XOz@N6ꔰCiIƣqԽ|h$ڗz8土pP@}L >by#}_ƆNuiG,u>x ]MD@D@D@D@J-Mcc17|hz,)Oھ*p i #PD|Јd!0h +^8A;P9^E@D@D@D@D %5٨C:mcmIiK>=a|[&oiΘ14 pm<%}ȡ}楄:$Z,s+}&]D@D@D@D@ޔ4hzZvYPOKb}4Bc~Jgu?vJ/KV[8qF>J:h1B6G;qNe??Sz;t4(s|#h&_f5(}^"{[o,A錉;|lQb>] Zf?YAKdXy6|C;΃>1>.r06hzt$E@D@D@D@D{M6Æ>mi:m C4w0E L0w,~6')" " " "0 `CgKڋIG~cɽ2b>uJQLZKVc{ezt4Gu!)Y<?&)aW Akƍ6H#6o6}B?mS }b |Kq˕Wn|!$(@`(:м<8gaaq^g4mah|K{d؇:|>)GҏN?We,iGc\7J /IJ64>:!A:9e!8y.f 9?ZYs|>N@s~6mU}ڊI2}X0W3vhi6)~܊J`**pل>&Mm^~A?$`E#aCX sı{I;$h8"" " " " E/uJc2>mν0u/1q#sܫy1u˖8Y'¤:9a790YPD,ԃZ(Rhc>H0?m~,lh<'D mD@D@D@D@DR~oL=ۢ#^D>ƕy^z?t6İye L2mO|̢ЎX4#sC#&Às†%/ tKpJO=MztR QD>Poi㽿"&M?9dLBSȴ"1:AGGgF?4 bD@D@D@D@D\4IrCgz,}}C>uH!17%픰˜u!KZN_4OK[9>HYE \@C /@6)uṽ؎x?S&'q/]HZ SN LR:xD<„:bsikKꈥNmD@D@D@D@DR܏buJ|>:3Sϫ3&YIz;uH4/j\x;;?ўUx;c)xh̟ ?YnT>P/ъ:HQ1e^EL\8Xڋb|tK m>hF{"RŊu,;A{y< Iub14_1;1;a.0!t15Kr%} uE_MD@D@D@D@!i:mĹ`{f4/ 9rg˽r,%}u>T,3In,hb?KrC1>}XO>H@xsY:rG\S3&G=>|V$NK,G~Jư7|6:EI;%g%E@D@D@D@Db{K~^g~oñO4c?ץ'$:اX}~qθю>z;tGm= ҋ' $>ux }O[>ı>7}5=YZ}qX"i:}c^gJv|q" " " " #@M۩c_ԙ#KxNRs$3^\7=sQg,q,N -mLlO/>ۥ@`g鈥/K14[?-qh|'Tgox_>Ϳsqg11W0.`ccNǒXdQ/c#$r}e~G=͞K<>$dEs} 1>1硄 xĠїUD@D@D@D@ c{Yُ%{ۙ'syyi+%+/#jȖYU8[NA1usy_1ӛe_RD@D@D@D@<~c:%sK?>:crI\=OC8I>%j0NXɲ1c}LlK')" " " " Esx׋8)1:}H4Qz[~ի EÅSclY9yɜch^{mSZ*tv걤v!裄i,5sZ$R9<".ޞ{?<{=џe~" " " " "P `@~,94;m>y{woN4QąY,;SGAg+gMRD@D@D@D@j%i|m) [lgٙ/׻R$k/r4xq%]Y؎xoҙ')" " " " H+ld.0(f+OcU:JƧ&" " " "0rd1,;{LFYn]iNrGոbEF-͗L'ː񸸟1l1#" " " "0 c~<>O;Wc7:U9HUPx9brb<" " " " "P)bB1S,α'9yOz88iEN'"˞X,OUN cdr&" " " "м rW\8&se*r'ܧNrҏ%axh b>K2N]0W30XI (6'2p֬f$k'&n_VodF|d2x}H%2cl27lR2wa;7ˎI!_:Jaj 꾭wm? b)XjXh2g]qbSK[Cv4r@ 9:K` ; l,lp Ԃpn`'KރepB<iA O !6āRlhTCP5]A!4 AoO0 &4XV`l{ 'Yp.|.k3p;| |%(J A1P(oT(*CBJQ5TuGͣ>h*A[]h: ].G7ѽI+ha,0n` Ôb0m4=êaͰlv{ۊa8Ny㘸\ *n7'xg|(W2AB x" "iBaL&8^bx8N|K"I$_R,itt4IH!kaT!r=BRl)!J#:eA*+&)[B]`X APENp``{B!U!!. BcBTaaoDB&³"8U'H\Z"STU@eQQOSoPiX͍G+ DEDEE3D+D/Pbbnb bEbF>ˊۉGo_ȗh$It<"!T -)+.uJԼ4MR%/}^ ,)'CV_fQVNE+{B켜\\9y||UtQ=^F/((**T+ (,+)((*>U"*1Jz啽T*1*UTTTTvΪIe5Smԓkh`5'55aM {ZVI!m6GF{LcӬ3+멛ۡJOY/T^W}O D r j + Qvu622>eЄjer䋩)ϴtL,ܬlAc0 1/0H8o񧥎ee QNoRbZU[?Xml656ll#mlg4ؽ׷ٷ/9X8tvD98;888;M8+:\L\vtb\=\ɺwz=6y{ʞ)u-}W_КO Oszm>qIIǒxd(yKrg YOLNH~!C8џy0s&9= {'wճ[iw=.{5G?8ݾ}]{roe{J??xGƏuRuu_9F&f9yLؙg;[tZ[Z ΁s^y=Z~Vږg/tt;C:._jEK *.^.B{ejnn5m=O_;pƭ7]eum0t5oɯmuv m2l3|8240p,l0죄G=^~g3TiDoM''mzd5?O>}J cyuMk-Zz5_MFYOւCFF "#dbe*|9\:tsֽݿ G/[cɮ pHYs%%IR$iTXtXML:com.adobe.xmp 970 1032 8$#@IDATxgeU;Oѝ]66kWW|AX a|!IB I a.lDpcnCz73jZ;PuN՘ǜcGgk%[2 $@2 $@2 $@2 LؑD$@2 $@2 $@2 $*P^"{@2 $@2 $@2 $@Ʌr@2 $@2 $@2 $@` ʁ&@2 $@2 $@2 $Pc Hd Hd Hd  B9d Hd Hd Hd y $@2 $@2 $@2 $\(2 $@2 $@2 $@2 B9d Hd Hd Hd 0 @Fvd Hd Hd H\(1 $@2 $@2 $@2 rn2 $@2 $@2 $@2 <d Hd Hd H@.Md Hd Hd Hr@2 $@2 $@2 $@2M9K"d Hd HzL 03.h̓ Hd Hd|f ]r>w.v.d Hd H!ކ;msY[ ୶Gd Hd Hd`\LrzP{,[xdi@2 $@2 $:ȅ: D\(o"L%.Hd Hd 8ǹP>\Xc,%@2 $@2p0pAJv.Q 6^","Hd Hd1-۹h%ʛ^wwLd Hd `ؒ \8o t,s!A3$@2 $@2 $k`/sѼ6 RU :d Hd H,xN暋\(/Y^oEvqd Hd Hs8]Vc.3P8 έP{%!@2 $@2 $2pS>+5yX6By Gga|g;vӔ $@2 $@2 l;B6rPa ^nv֭ڎ5{ Hd Hd`M] f]ak޴`~υ2'tq['٩Id Hd شnV yw/&.7rfg3׬ZҞ $@2 $@2 -6m90̵VJByܨ8+z]&@2 $@2 $ۚMYF6*FY)B]0_ MX$os#b˝͈c5>Hd Hdc` P7"FXB\,_P -@ިFY9:c3cL%U@2 $@2 $[ ]usئڨ덳^䅴` [lEz8`=qJn2 $@2 $@2! giů7z9] ~.A^٨֐2Hd Hd  e7Z9v ,Vxc^\PF)%@2 $@2 [Э2o ⦕k|]0 -H^o->|gٍVZW%@2 $@2 \ l"p uϲO ZyPކ ,0g̲qP1xsLH2 $@2 $@2 l"1T9+,{]gQӂY(o"yEzMf܈rh?̙2Hd Hd`3!9i؈\bL"ؘo~+1ΗyPހEZo-ÎW)݈c'@2 $@2 l?ֽ`Sވ=s9;s3^9ͣ_by/"yF`A}\$"ؘ#@2 $@2 $F0oggL?Z$>+r/B,Y΋ÍW(>;&~2 $@2 $@2g8fln}yq-] ڶby.׹H^ˢoyC![,m,q"73ZId Hd HEf9+,PE|C><}ǧn[(s^dѶءxC:kr^>l#(~ $@2 $@2p3oQL ś?9YX!ۂy՟Ei~<8C\Cyq2 $@2 $π E>?3 oY 1+f5 ;S74%<86y!uMvγ΃1xy0 tC9n,ʼnO $@2 $@2 l$.ffهjX3nQ,*o[$ϳ 1x b344nXbbj%VY=Hd Hd`k3VZ`c4LacCgߘ'P>k<"k,Loqc^ߏc }x>ir,XK}2 $@2 $``fͲ337Fo5O9/m8b}^"[u|>?cw{ž6krQ~~^@2 $@2 $ },v- jC}ָxc3=E̲GndymfK>z}&{ qE}Oqx߈c̵%V$@2 $@2 ln#f׎Czi9Ĝ!]ec?m⪛`I $@2 $@2p2bk tQ~,mRND\Yb5|.mD;ɳu;YY8k1r(Ƽ:cɡ8`Cq.Hd Hdb`vL?!yuxcq3.^844yq+>[iݿ<k^N 2(׷9VS~,&1ۘ^@2 $@2 $Yrl7KNJ?b#Nks}9d;Ko."H ybMk1,,;(chp=v,v1bYϊd Hd H~jsY1^5F>pNli3^o3K?%X1?]ܶ[;kxd0:QiWF<~Z r,{׿d Hd Hg`bklb̲WgY!{;Q/#>/#Zmƙ/fEnǯyHpl+;iJg?cPz]93W[KPLmď>CYy|R $@2 $@2s؂k}OEو>9cqi\CmCyf}mڷ܎ yy|D؏C^7mM9{614Cts8Hd Hd`3U? -1N,!J#iyO؟Xs\: 3f؏fz9WLя~6ҍg^9{^%@2 $@2 lOZf1cH?:z|Ǿxdԏ]]/#٢=zcL>Ɛ}^L=׏`owg-Qq.cJ:VWW}q>bb?{">v?^7@2 $@2 $?b b`,7">K}h8J|0cD1?Jn2įG}\C}~WR=R]/#&>{=>c3M41\O#@2 $@2 $@\mT&?NYO sE8l`m1zƌCFla~Xy=z#4-c?ҫS)8VGe 8bELa͊Od Hd H`Cyu}/c1L7^jyhsK|-{a{'#eEטo~9Czu!6iV2ꌥ|c>Nr,q $@2 $@2[-ZX^ߏ3=o?+8+wKbF[zcO,bgBy;=z}asYczÎ+ڔ`+q?{ܾ8P7$XeUDZ=ޏ.O9]9/N|d Hd H"kycyc8kydy{lYVbUӌC_8eĉAGxe1l5(:c-[}쎛F<1E|*`+{?Jc=.wWvɫ_/xW\CݸsC1X>__m FbpE|я؏?ͣ=&ul lNdc_):+ѐUg^QOcǎ~c>cvcLHbWq^y60S.Ǝzl4Ƭx1P]٢9ɾX1Xio揸>qyjq+Xc%8Ҹ+6_<73fWo>ƞyʤKϭ(XJє?f<17:em8VF}kG'G#9k:66lc\J1X 4:2}<',ʃ%dm?DAW?8}ab48c -Ƙh{gbMq}̦^q91?z\}}O|ƅ4B mcl_qc/<}Sc1~1Xtߏţ1!coDc.c+qc-٬'6A->>CQ3.:b_8q;M|Ģshq7ƶ1咾>a9lօ_':Sqv4bȟxG?.V>:k=:4C38f<%x}<Ĵ/M祶BO\k&sYv17qǚĢ~=->چdy_??ook^cᾺEr류!:0c~cu4RrL}m;ֱ/fS6 ;feUjs1;x̧Oq~>~~ꫯ&>so=nJ[}2/ oL}qX/;y]apiE9ڵk%Gx4~č٭ǚECoc? Q=-ony_A׏Q/17:s? qzģ/bуflԁ釴3Q_w<C2ڣ>9|bn}#x64j.uS~q7~F<'%r7I3x,COF?cI_?FX:!-΋9q0"*}78/;y?^8N868f6߽{w뮻~k_zHqYz9N^{c1{}H77S}􍺨c[T?Or, f#;[yz}}fTهtW-c_ !_wG>?f&8lO~z`}M2#sCg_9ԃui#/b~3P茥>]ސPl0Olu֠aGcuG=w4v|)>}&~>f9ҏX1em^ J!&bc791X;cq0cw glzHFc`zcȋX %94:ccj79 >OmC9aQØ99$:_>bsɧc<i3 _6|8?z _*uKBOsY619Dz~C6QËeo\8W^( ɕvV_︗C>>> byƺ>x+~=-h=i_m8i~1ꅨ‹yy ͜Cx1"ƹ3!x1ܠ3}q)gg?b+i6ͱ8'uH6j9/4lQ'}pcH?c^䊾x?e ،݂ԗ!~1:}R?qr)ޱꌁ^Y~G=\Ju1:jzTߜ_(?}#bEsAgnc#CbLAg,4Sΰ~ĢW<m{cqNl`s^iآN<:`=~G?1}sԙ/CxbtG_~Rcs#iƳz1ct@#<ͩg9/ yQF`9FňF:ŘrXi1a3Eg3x-ڴ6!٨ӰExtя9#zG~7cxC+&!35`3v R_<5J׽uϫ^7>1ߘ[!ې5cO]C+sFV1Y5<چdPq/ 1SVC翨^#Od6/8 =H6/i7  Oehlӆt.6.䌍o\lןyǹGG1i11бbb@cGnM#'bQ~H1̏f_ ^}8G|#^L/1B_ͱ~8⩋1=q@ڌML@cn|yr<#\yȟc1榞}.lg6$q>1G lyOޫF.>ջzccwD}d:"mzJc ѱ_籎gJP#CXmf>f<}ii1\Y[(L6 c> я9Os<6z݆m_upbj9\఩""6 ^@c< F}Ϝyѵb%6}ć\&;_HYuƱ1wHub+N 2M_cjcܡ>:QGs0l=`,ͱqb>/1G?Ea .bf}Yt̫_@Ǹi#\# J6cnѣGKaЃcK/i|߷\|/{}>rӼɗ6j>qJX1f^mhG6s0rD_>BcǦ:X?؇C1ƉO)OOAH8%xaLLmh_99?c_oG>lA\Q~c0=رڶgZnYWɪsi=qkG7m6J>f\~_wX\wgm<| F~Qŗ9Qa}axQ"C|̝qa"KXc0K<6>1cRjo|c3>e7.F<|>!g^1:ئb㣣6c_Y+k>펍qe~AOs>H2v^q}\k5FŚ4cf %bXG3xڰEP:9e.]e=w]eiǮ;>42OQ]6Z^sf.c>t}.tNW#Wmȹ:$~Cё/c؞<̅X4b#8Gmͅ_]2[y{s,Xbq^13ccuϞ=+73<~6tlcRYk=m ;dkS^XS޴ʐ6uO~uq/و1Oy0h1{Г}/q,_|.4qLE5#q̏ L^#>F#nC54P}Hk"!}>r)Vi> 86b|t`SxQz$ _1ONH}}#Y}5!ӏcOΦz._ZR[8co^ABCSck#s!߱x$:Z;ı/Ic#?x\m=81?xqFӷ _~<0 e=LO ufQDo|zxڐChC2ў׭PFM 4^b>m{m[|/.:ċ:&ދv6ǏoxQN,`~䠉#:0k<ՋGuEh}ȩt^M>v|hK\u` Hz4}ƥO#:DZlXZ+vp]`3>x7ڑ\}]m? =~P4cX}dj Ѯ?J-֍. 1669ذьZ/Ku>w( Y%5c`|y>~@\)=N<>{C.s0c|"YָiA8փ  _kN;s#^ڑ\}]m? =~P4c~;r-GA?c>m<ϳxp01oO/~+СC.+O~ 7PEϜN~?Ԋhi֎$@+c~ K,]ycgn|XͩX显%&xԎyD_;iji7?ztS]m֥/6u`Xf>猞 )N}Y?=Nɏ]F=}kT_E= &zkcpGMCnرtX]SHØY/?}✯c軁76qSѮ>J"F,1?⣝ߐnx[w{zA֬6 3de0=.{61:ʨWhm7onRL7?.^hwoȑoޕڵTvx9Y8]>YNWno(o*E+qmmJt4loSTs/Gj>\g<)-y2s2g}~|Y7w~䎺ڻ> 9ٕ'N'NGBԞ۾__/oZBL<I}ܿƈZu#D?t6kB?r"zm A;RE]?c#66X_샧946Ѯ>D×EX b%}qrXֈNG՟AЃuM=ɺMNUaICkWF:嘏v$MR]?nlѮOckzNN7κV<946Q^׏6%6^V}縗cvpp"E 6ܰA _ɟC \qEꢃ咃ʁ={zWG)w?hL7Wޔ|.r7XP6p}qaw|#i]w]s|%GH~_>Ϸn+/x V 4.ր|ᫍ߼O?_W.r5זˮ\z١ा#+p=_GR>r7迗o{w7n_z8B?cufy3n-,j ׻Ogʎ{T}ˣ(r)Z7zbS?BO6lR?č}̋Fu u(#^`I;6͜bI7c\1֡8yO! ~ [_jwJ\?zmcWx~E^Ą76R }xuɁF?4eԗ= _n<_Z6p-b,~831kι$1xرoݱ&mq>c KCW_*au=Vvm߱񺐯e"|>c# ={YIZ_ufU/nԺ|״q<͡tpG3o>6Y#g<~V)b~ _'kwvx7\|ezظ\374/byG 5p}A'u~-XyOz 2/6`]Ґ^;>7amK7/cq5[1>x{6C}NaCO_}lb=X&Ɯ1b-moN[7;>Jlp7ݿcCg^hɷXc]l '>UM^ٍXՅ4+Kxewe6> qJL8rf 30Col<7fz}bh~yaCz xVoVR$n.8_r۞Q.y3˞o,n8\J3azѲʫʮHw5P>ehy,߭ߋq^b~xoEǐv$vbŇ7N3!zFFLk?ȍ"6KC cLX:y4뤏?q͍-jk &'b\HkFlC$: {G4M39{/J|A37_1йѨZbYg$ٌ/^o |QІ#X\5~>f-G׿~eXc~f}7c!6r/>yOؑp1Bs҇O3 ?'Xvڌ)ZQDG3YyPO [I=} iC/=1 1}39|H RMi.21fkxpK}3Ƈ^bgWvdJ;ǡG7f'Ros-%>W[ϋgf1߼gʋ Y=1>1bߘChߏQh_={'>=}܋&FB]|s†;祐;^goo][vrʝ_=r>Yc.'O7pizkէN?YN~?rխ7+HRz{ga9`>~^nֶXqkoŹ{54 XbyW`ɋ4!Xlֆ[/Q.IϸXue߮=屓W-?uIQN/,GN)w+Wgrweڛ˳Vu!S5fNn;q6O<1bbLr=2ֈu|CCZXsFl9o%xYsO,5#/gYq.O/xX 9dq Ԉ1zS Xz8j\c?B??ʻ굆|G<'GŹC^/?|úǸ\3?_Z3FOL?X 97>mclzOc>mp \o3zئc C$ٽH.h^yf0pzλ)ϹI}?RN'T|4IYS+d]0 o۳>;j,bP/H&/?FʇK/"#~1>X4%3wiOM?9yx\ZUr\u?P>ϕ3oV!P ?`q9 %6 =SK.Ѱ{ \ץҗXkgn">67}%S&Gօ4<)8%x6}V;uc' ؽ f_hB's z`L3cq "9E[N!'4<5X/Cx<_676cqصEX csws}X`c<FVk$'u?b`>X9&Q_/86ʨomր$vs9Fg}k# ]lS39ԁ#sqnC;cp1/:sL_k\% [9 .7}sj_*hV8m}T饎q̣>ꢏ{;~Q׏mrF?Pc1 )ҏi!ƈr#i+vNsb'0:NbTubO?[]~r`'r9-5/Յ*%\ꅳz3?Ts5'or×=ԭ$XqBrw/\skw.28|2wŜYc<4w2sEInbznkg C򞏿\~]-'N(?z?ll]ϙܵlMq6*O|/%n1?V/ØZh ?|ŎDOc t#F[ >al k yⱫɇ}SGaƘpkO,CCsm~0u|d}ڌ 4ZC=?coGíEjNibܗ`i,(aoM/|v}SK/@IDAT3&}X˘|6|й?x޽ Cbo<@`hȕ/sry<O|ï8ܲh 78jhy{^yOEllFF x^\39e$?'}ur|΁Xl4||/!_b\frF.za.Hb/`%Oca͜)ּĢO,qڈgmgl ,c6'I:g4>80oDM<'Ҵ#Iv1G.b8/bo#sU'!7 $\mCz=c_<=>ڦ7G6o[ϼׅ 塉!ݐc(ŊӴOFcc޷]ys"/^t?#W;Ph,=TGS,V fVߌuOs)]bqu\4/p_, p >x|^͜@/8i`yCCG bCb,kBm+XT誃W뇬㏗Kuqb2Gvr,iNs>/c/>^>+Iz9v 7c=:*JpƖt4Vb15l`CLt1c[">ģYks.9O66H1w3>K'ghw.ꏿ_ڪ\8~|Gz>TK[rT9'6Z3cI0X?h5wC:0體Oo_ {^~~==Blj?nPJƳB\]'=I/8g~gVx1>?Eظom_{Z=\?Zkc2+_A#&3?׷nC,x@ww}Cg@__r)9~~n;so %7*=!2~gbGOOonq]w__)z~E='ݏ|cO6cluSy"r`\b8kqրxk`O9 l9/zAz0%>n~^-rXQ?}y}Գs8a'jrBWcjc[ĠwLhy5F̐.ڷTyKbܡG~ow<$Ց~ǚGLc)-ۅ $U6.4.\$^psD|c-W[Qvc[\;dœsYϳz7FgPNv?~hUiIl7 t v.?/bgN|Ob8Gc& ptF/~ƥ<ݝ^.`Csq;}~_rѰ޵_J& ^}ٞwOt]dX_xlC-6kDa~;}c#~`Y 8to\h_aN6YG}$qյ/~φ6S'1mmg+N9 zs5*;/jr%vpt۾z[bNqmth>x# }6 ӷm|1xl,rY?11fk㏈ڱ|bc䧁:4r+LXͫcx6Xcjky×qb16}lOİ9'd}c3>1A|sl]O'ݲ0cI0}Īu룮 mv>-7zălONl=괩޿njٟDE酑 xlz**Xx;Y&X1KY$Z3ibqnxҩ`]`s>;E7kBEHȕW^f)#^E̋@bE86بf ꧯ> y#_,{.SS_];];a{zo.v?k/S;>3Ę66,k<1tK͡c :~cZM-̧/՚ޱzG?%zn^K"?̕!);띴+ҫ{ݮ.?Q-R~G=E5Wm!yߜoo؟|?#5\Xbqb>4)O^sM~xZý}ܑk=lօM.gl}A ?xFGFm6=fE;1ɡG|__iiGY;FĈ)Z_AZXl Q M?DZVaCO#5(-GOlǸDO>K_?b/7X%C%pcizsecC}|-3zdo0%rEfMqGڧ֡>AҢ͘ b5ho%NXOT%':z/`s·7/Wǫ]PjoS/wO.N;Usg|!owIs\7s5v|_??[.pX }Ԉ.w(!.6/ \_<Ԕc8s4ʰl#'*_K]ն򳴫pώO8=v]W:IfqSnp^Z{_]$ۿ93_u΋l 7E#s=vYǐCc86&/|a>wq?,xAȶsvx9,9>GC4&~1'~'Z 掎:}˿gwZɉ^p|]d[wEqg5X OOT{L=j&}R|Ќ;cj6pE3M~97 6cR8KCGS8q`]=QOhs180KL1؜>`4tb?ta 4lk1zb‰:p4ºF (&l!ш~bgph)y>H9//ᅟrfxGqu]>,wh-BP^t9]?W5zg맏DuCC:DCO_FXt` ?0h7xķN̅91fA:hҴnS8*Xi r߾mq{W?h?TK1<:T_اjM'j_p|~ϗu_Qu9ot^sN<iyViy+>\:?lH\9nnESRt×%|>$7 ,\?B8_|bxJ=AMQzEXy]eWK|bӾ~Xi7uq!r|2 SQÜ-ٟ4r'8?$БIcÇpEwė} Hz}WUWs2Ǎ\AW44؈x?iByA4|_Eǘ漐uC\u1/$:0KL燏?sXڏy'm=iI#8-bA߼q 1G zcQ M`?hj|,xraCF3_vW?0ډoE/6b;65 s`G/)zG:hҴv@`6xyNh ;c |+oE[숾SYc$؆ǽO?q k> dԷ@$}NW_SNߕĥ!a .c.땷^ ꅣynpʹGOwTwrꏂ-_j?ibB]C+u>>TS+87栯9܈\xa;tP >ĥ//b͝u{m׮z'~^k<>~x9Vy=qX9$P?Prœ<I]pAXnj#gclpiQ/昼 39 5E%pG9i6p赛|訅l3nl YkAuOE&SVϟzǏџ,b\P;s>?򲗽/oK+Ĥ,~bv1# u`鳑kw~, eꇴ^$9ߚ^җ'sZhb;Wk a;ݗv_Z89c;f4ؑԁN/qkV4%}b 4N G771>2HljPϘ9: |"iH 5#:2 ,}l6nqO)9qu?soXbkB:7pVڢ/x@OX1>`~s#b!-ΏSq~iaZlߤ۠^<8i9Px{ш)NjN7\_=x9^}NK.Ui[=Dا%luXդ9 SDO`rl8Yqjg׵/HHnQĦ/H!oh ιguş<c6|p>6z؈sk~Σ~V3\{ye}X6ZG ,^_FW95+o/jcdEOMI Xy'ΆNi-1Q\sZgl< ͹bF,-`N`CB/l3G8kp/x ډG隸o18G7cLlڑb\G,F>5u0x$q?z͍ƍ@g X> >M1W]ۘusI+be^0\Ӽdžc 8$v'8}O ~B 1#x}h?1ܑǹM^\07:v~o-yZiN׻ɧ݇oԋcAO=l \ب[I93^@ xx& ^}賉SOp/حE }n]Vۨ] 'NX;UWCJ*ۿ#nW'_}cӓF]̕9FԥO :z:H,} uYXƀ4.vڐ@\$vYc04| ~1c4m|x?q=8qڠЙ>>G_1۹.MMkV}j?ks8x`̜&5W= W}?lԆxp?zXfl|h-=?Fbk1WuņX[ 4XJ|˷#^}9+kǣO{#,N9u`NE^XNW\n&]hA1y=aAr/g?6ARqϸo4bb]c'x4d~C?v9}x}6bZT_ϸkCR>Hij>ܐ`n?&_~oC(gaӢOٹ8qBNj}/89C=ٽXpgG$S;cSu[:t-볞5D-*wwVojz;u}I?텈Z01f"jX#Ƈ.ĔEN0Hb!/l O<"kΆW=XK\ _ڬcqF>|\ЁElrx٨F<Ӝ}g~6Gj3jrq~r =k 6$͘_cvbA}?i绶U[$yg|j}'IO Jr}MlQ[H` ޢ6`[}a:7QP{\r}+}{C !@Hg|r~Yg}9gsV~sk5gob#1ck m#> 9!I'KK_Wk˗/8m&>9Ň/⧱5k6ؐ/ !u]|Xyŵ_C,Q .4Vs pCPvrG{zU9&wps%şsSÍ.7EVT䌑@qGOLh{{.dC^bKtbHt̿-+E#[ʓVקNjǮu\{A}aFQ>nWߵ3O]|W'y<eq,}b6!/u@ ja|aƆ>z`=〈'WOxEl5/Mܸ·2k,eFb"VFXyBG:8w$tSױڰWN>֤k þ5[+0QYC, ԯnƱ<ו`mċ<#se>xk{FnϽmZ ǜwέ^ԍc3>~_3q}cY#t.|K7>{zO)~q|1/Qf8F=hE]Óڴσ81x/L3/}6|:U66sYW9| jcL9>3q g䃬@G{ _dqa !b y~[>ȩBc ?譹: 8N2bRG-}a_҇#7+߸Rf[j$&2j%mdkZ̭NBS:ױڰW;m]|qI|"9zmGOϾ1'6]X[ͬGW0Sl7ɛ>ˑQm6iGfgE#^89iypsxe?_UvֿOˣȢ3u,qWeRy^y>Y6?Zd95E/m=Q;is3E`sÅˋ,eM%k@O}ؓO mK_xZy޳=ZOmi-߲$Տ^\9`̓T}mie剧 u6XYrlŗuvak|3~3zbC C72ca1WP 376:^l5vCx'!s W=t3kd*N1V>rǧ!> ^;Jvr1^sm2 wǮ&~vLa|Fo}X:?B͜3W|k7Gt_7bC2IF78K6tΘSO=qu5ǿ! ൂo&ΕcRXᜓg&r6D~r[XyNa9m_1 ^1'uCN8~?X1qZ/;Ț8cw#]{ꐻCiKi1i{(Æ;Ln\|e\b.DZ 7y9d-m>梯8pqH~OrȘkr+G>9 \~rbC0A$[yuًF j}vP+fSot)o6si<nD|8=5].hcGy8/8!Nf6^??I4R~7;|C%S?Qy\<[gv #˻~GG/j,[9h mn(Y'cBܼtM=GLbqC  n rbarzCȠx ?9ggUnt]? ~//|vGyvQ%ᩲQ/?@}|ɿTN9{"?X0H#68CܱOkp|b / >>8zkў>mHmcG쩝G 9z-2r:eFc7r1ğ#s\\ǎÜikG}IzͱYc^[8NG؊˲Z~})3s aL^13&e/{Yy;Q>f˖-=_77ˆ z#JnrЧXiC>c3wڳW?yaѷɅ{O`uozubʕk'P6\G|c!^敛Ɯ~酿C֛n0f~27|˗r3|\Bߜˋ/)r?cfloy[z;zOoot7xvaAf7sO>d`c 5B^K۱99c|6-ubOa~m[Cp|髧adց=}!GgY0z 5}m.瘱휋}ba.c*f4:b+~֊yZORFQx{"?2Z Vg\m. vu7r+1@_uQԭN}c񻙼ϗ{qS>wcz/yᦏ5clٲ͂'67ppX#6^0ic-5:Oz]9y/MP|~AڒSY;xqyŠ_Gv'^ynS뻯$o*/;{7hMɶbግvSnzbю%/cy?X`GŽ6y8_=:|m.wL"mt兏xC,(I-|ȍyCEG8y1}wZ^a1׏?d̜G;vbj`nov 6ld3Gry {819:GȨ 2VENbHc:Ƅ9ՋkF#.colŐa,d\Y>L8έuѧ&h3.(FkEg[5^SWK6؀sw1%ƴ9 ˃04Gmhc,nQf(3fEYȚ>clm9*LTP7*oٗ7m˱ ξ6L}xq 7p#8y䑽[F)>ǗvqȍOyZ%bzjml!kqǞzr*mCز^ Ҡ;,?Xyǟ^^xKK^xz9{ yCԄ:_ѱIA qP/CS?|r70mTNhs`:sіD<*7;~yNb"6C>$N1T.ZPN8qy%6;Ae__W{rڳjwƒV*{dAL[ϕ?u\_GSj9{0.6Θ{j@8.qSkQ?8r}!lfgb?99!9g`-~}OMs_{fv~cqΆ;Ï!W!`D^d'b$%vob[%7J\SZ7FQذ!ѾiBNlxSrތ\2@yʑᏞ\ħX=Z'H߱O(WGL1CO%S裧ol=*kΑufٺ(j"F?1d 9bP3xŏOPCNL8;=1#IC9ǰUF/6|Ƀ1^ֱaOnN–Cck;;fWg]$Ɯ:p㚺|^MMLlqŇ/zk1/uqGё$6SX̉y|XGoN{vm/ QR&v(Sͣ~țv]FYu]RYFyZL':t5yC7m& ~w>r/,_Í>_6{Q&/*GNcC}PD;|G:ᣓ\8 t9;Z#:}7>m$q.3>j6bӸ> G/cCǞ|&.5jqLgl̉}lM.}aY m6`>ÖnƠRlqy#Kl!ćܼxjhsh?9>Ș>pX"20Co=j>8_uB?؞xFv{rb$5ە@IDAT cs@lf?jK8 2LJr5aL>YO` 1s>xb/bszyy 7|yqk!'k?hd~8agt#g%799`<l!j"&}+79}rjNDFCD\U7NзNlA P獾qI3e`l/=i9>yuAg u3>\$c7&:HꗨҏbZSq>qP/G["rHu1C,|& k|@ܶ_}ӆx#hsk.X@Wv؉-6a0^hHLLۗc7:y6uMQɱ5m}[sK/7q2# ]hՎQf[o%.rM1{\!O`NZOj.xa. @㇌6Z/mrak<ŽvF;%179=eNc;q{OvN&*>v͍G<#cg-9Cb=[1S-bĆkX1ю--dzcI5n"\GnF7)/>o5mlvG(OtAЧW>>H,m"m߼'c]<z6^x8=8`Ew+a ֋ >PlE4db#OX!/''18KM8Ǹ6"72Aˡ>ġfJpNM ~gA|׼?('zm4#|!l!7ѴXyAb>bP9FoNgN\G[+'& G6&mO[cŜ>:pӜ75u}կM/ ꆶay[Ǧw|˳;QU~:;dj;G4PU7l}eǀ3VD]bXzƠ=6AG58!_!פ2!C]<9{ON[Tx&$z̍?1z8غM3Qd'/cǗ_F!|c1$m_r׾AF>b}散'u2Ǯ=sG7B07>x r˺#2CQ2m1@s૟+l xbCN'q=ag<_/Ɂĵ[⢣~ ;X\[ƂC#8qŁ ~#tq se}$P1S/ t'mscޚű ye`#^7|+6r΃Xz7>rs9qrb*Cz/<~aF bhӎʚ6'ÍskgԌ#ǴcՍT'ck#GgkG=2vmxmre{ .Lq2C^DGw֐㋭:8!u\bϸCj66֠=>Æ[}dQ}c7itZ9G[|!5Ç!G, =qsHĘ <ړ>!xYO"cxXKrZOL :s9kLDž/95< mc1b..hO[qOu>>!֊`}\7U.<ש#<[D/BOܺ~2Ԁ/58'ˡ^Q?hc-3f>Ő azAƊoEƖ0O% ~ϗ/#'18}| 'pQ g.m7; m`,p\1=?Cz\MF޴&3:9裼Gmm^MbG_ۑ( 4}6tڧd?bA[/H"b/,pZlq {^}'6m|6Fs{b>^iK_MyņAnlo`9^ǂ r8ncჍ㥏9S}{lȱdrǼؘ9g| .2k:!_~mo,GBǁ-KT?~]dɁ S#cTwc"7rFG,dFLǶcr]P5{DL&mhSeI[2J, C䇬g/\'&x0vFk%u!m.| 22HΛ6ؒ wuS 5q[Lq_`O;Ʒfb_l.߬8ȬI  :};}_[bo,G/shSDeL;brup_;=1dy] 98Vh|0 D5œ8?mqGo\矸4rckÁwx?)Ko@qD.#_6#'Vӆ>Ȣ})ӿ+/f~QԴQ**i|{hҏoʻtɱ"m&=:/e"ǐ!ܼL}sp–xa*/0w} /ta r\hs!3/l!ӧ~;q` '5CP3>RpK!MDŽ $&Ml8pc}!q`^QmrĦ^"75S  ?0`v:~!q tb OVcYsKr 'wX8F`a,t?ȈamO>r|̅ 8?8Ql6qOtw? ojp:ZyImAʭڱqZ86 1nNjaր?^fx'>䈹9'?7mLpd:Gnǂ[BؘzEO9VFD9F![hSk^n~!tv!7qMle pǏ5K6:ñ&}O%/:rІ6lbG=hs1bBCLq–>1?A#~ӟ ?R5Y'Sƍ m0@5a.k9b⚦TбCZeWqascIY3:&^{XKY_2_~uen =jsȵ2]ri;F9~7;qrpɚã6Y-.{M'ʣwqo췵lOP/=qtQoM,rb>m(6^.>}‰*Xu qf=G+m_Cb 8.Ɂ'V##[7}Q@GQ >>v謟!bS>DF.p&W&d؉ yŏ6i˱'!cANq'~a] m8s˘{z9Qs`.볏oIlVǜ oӆ/cBn~mS1Ckl ׶ 2kZiCga|@?b[ 1|#3>}ęxP?Z&&k.ĺZ 1s<g0G9+dĒz:Za=!5:G_<3zJhICN:ڑV{m'rM^5cwmDMf?6u SLFf,;rzxٖw1"͊Md^Ñ 'V$"O\dCs!1ӧ~zGs\8ҦjL|6$t&>dȭ6D_"3c!9?Bb\93t~c}m\XpcasCր91 ľmcǞ|uXk?E=1[ơf挱'kg.icsh@$udh`>Ñ r}I?d>],6`e[.~QG[sݺN胳1uK;C7>\q:\9{q*Gi1qvK hqtތ6~co xi%p⺂D}lk&C\\G?6flD_/\}ƫ\/MN x(G ?~WEY?2$ڼCM2cg|x`k`".=:FyoӼq@āG\[VmiCNCS r|[[CG9E?0rcYCQ=9DHcc!6툋,֋->m8vc!6c kZ﹁uekLbB5dB9?քkX'۽n2|!֓<^S]Oץrt'[r\^}Ʊn<];YmdQ#']#SuQM.6Y(ؔٗmcWf;w6~˛h;FkoI~ aR"$@"$@"g~foe:|}hVrqR/zGnlll2.PSߔd17Z'ݘMo>س~:}܄ڗF[m\.9(S.o'%@"$@"$ 9nkN[hɵA.u?/|(7'gR26ɌE6&k&5J$@"$@"$@JGYlG&:P3Ψtlam~gn}Q6 'g 61rt< b\fmE$@"$@"$@>ݾžm96ܳ(~ư1#ʛf+f18>S*ij,lucm6dˉlkהk\;I@"$@"$@"0fʔˣَ6!ylz.(f?]vɜ>ᶛA<ƈ25M'y"$@"$@"Meʭ~ ylGYh?Ƌv뗣cF9ND`MD6#b;ց\\k+׮_jG[&OD HD H}ޣۧ؎( g[g?\Qn.u7vԫk_h7kQT~"GyN mokXkD HD HD`^>&Qnf\cNǺg*tkoڜ m~6]n]m8b KJD HD HA^]ʺm:GӔ7Fȿ_|(O6l~ߌm7kEy'@"$@"$GynJmAi;>y>垬DfL?6@MޔMD_tMhcMD HD HD`v\N槮+^31W?~beF9Nh`Sw2>Mf,9h7mvMYmb6}e7HD HD Hf(>Ͷ6rRk'ho.>[cL~"c Fy 6Tl>1f5`0_l"$@"$@"L}{MUy[XO;uL{S1F7ʻt9>7Oߥkʛ}&)k>6}eik_f I@"$@"$@"0;x]Xa=zmz}\{um6Y3Ơ6mvȤ>mtmmXcSOrc&OD HD HGt9نKɛQm2'( #AgcՏ)oʚ}}z؏CWgh?v"$@"$@"0݅{?O7l#m6ƈ}M~LylSܓ>WiInwqþMŠdQlǸMfߦ_~R"$@"$@".ޯ{/ʨ0~nb&S>Q.=1<ͅro"'Ofܦ]SOhДهGGl'@"$@"$G^M,VeݴfImvMmqDhFmq'% I 7M'#6y[(k󡶦Mk_c,eD HD HD`#ͽo[-6~:c|e]c^FyPٵd-N<(&ǜ*/o+K$@"$@"$p)ʺµWfW 6F;}]]1~13|mgC/ڶM$kfmOD HD HCxn[]nяvo]8M?7mk|ѦEmEm[S;y"$@"$@"{g7}蛺}m#i?DDlηFy&@7]Nh[.Y3h&#WGt֙<HD HD H-S~V<,ۖ7Ə~rf2\(OvpNРv]&Mf6]J$@"$@"${y"mMtm2mb *k7]Fy2ö_ u愵ɑm)kD_Y/d;HD HD H]Qn;V Iy5cD{]~~>ƗϔgՍdDAOO6}??uMfߚk<HD HD Hf7 kMϦXȕKl#Øbo[_^Yx]6]r%OD HD H}@=znVIyvKN%Ɯɍ 2Ӊ1d [Xos"yW\Ϧ/@"$@"$@"޽fQNɣ/Am0btɍrg}hb~~ucQߴ~"$@"$@"<ѣv"&k/o˭N>ϧT'bPa_m6t.hC@"$@"$@"0 zUi&G? ۮsM}ޟO3Ȥ`3 _."}OD HD HD`=mUw"'c~F1It\ඛqu鵏v<HD HD HfG۶գ'觋YC̫l|\;#m<,$]{t צ+^v[̨旲D HD HD 9OooJMƎ83נv|>oO4AτOzF<HD HD H6#bmkuɦMTWS}Q Ռ9D~]im66%@"$@"$ mwU^6D_cDm|DqTU61=ˍ3(4H>Y޶8IJD HD HG`{sTm#HSguDMeš cMAd}#mD HD HD`v]>h5'd|&<ƈyޟ7әP|yIׯn*M$@"$@"$dToDsS߉h{قYv: B48E;XGD HD HD`~"i?W8D j׌1UӌVm'OvS>T'L^|d6HD HD H-z*k"58maM2qKߦom+FmD HD HD`=zs0qdGTڦg^G@ ik}mP+俉@"$@"$@"0_ǯc1j}e}[~|:rsJr,Aɢ_;OS$@"$@"$sxo{b[٠ͷM6hn.=Qٜr77ӝxs #n5x['@"$@"$ɸOx?Xq~g9b~ZW>Qnu9^/T6Hi$@"$@"$>~+Lw|]1c5D93W'̺橶ُ|/H+FʽwQ۵9Ee{ $@"$s=OqOe⺞v^l&@"0po=Lnk0֘ǽGahqb3_6 InŠvř-?6Ts_"u٥3/(W7<=6 j8,|S`οxM8+L9e O|;sݩQnE&@"hn7eAj/ko5D.G?ĦLMs)G]D`>!еd 6K/|M9MWO|[{[>sMϛot֞D H޾{x(ol_ |( ԠeO'l;@"#i]?߼{|##6;H|>4n)i .MD hCțvtM[ 4߆ 𓱝˓l\s֖${8|(G폔~r5hdח~D?W˿t}Z{vEKNxɊr'o*[N8myrG7?6#۷{Qy… !?,=w^lFʶ(=TOM~TyᲥ6;T>'K!^#emeSO]u GY,]ҙ ZNk~֎;7({ۿws[OҪo|*_X|U_)eOUe9LsPzѷ--GSpޛN߫˭_% Wumt7||#o-gtՖ=x<Ǡ]6Co_0~c+˚u(Ue03R5jQ6_ʟ_,;Xz]uRDŽW׵q|cWl^G}dXJ#0CD_l@R ^R3^K\<%sƸlS$< aG So[[R#O7mV?dߌ[7Kz\ݦ/XNK=S=c=y\e^9c=n,_ko]CkMCs>6r뗵享m<=E;6<{\ޛo)]RR]\zN[-=f癪>ckt7,#Zs f5zwͯry7t'#Y"p ^gC 찑#;i>\8&.6lS$G]QNc A:]\?Ya][w7/tlV[o3ξo/I)7McnG.8skoϻګWf`t`f{Yr#{03Q ;X^9&9x%g+o~ ]o\_.l$Xl%FȻʝ ISD 7SۡdFAM]"a僛 =npÔm\֞Έ֬+W]uelukx >\6]SZ? ޚfKU31޶z|+kx?_%\ iǴa[n׮Xk_W\ͪʶt??1QLg\iUkU/uzeU3^øfx=>oo;4Hf G~5k&u@o4Pgi$ ÛQn_^7OA3/o>y/~syYF;o֥7Nfұ;ΰrVy?;Z6Ͳ^zE>8FMnq1篈弳O)=~?m߾q(?W7;IBR~w?~ҳ._5>ڣ|bbɴpv3FR-_r]ݐ-;c-PMߙԆW\>|EǿPn[g͝F&=P](ݹD`D`}9g[~ibu_.o]>7?vgԿW:~i-=3Xm݂e*O#<~GO{>zw{ aPZ0#C6>ܖohz^a<̚gzn=U:k6 ?[8`_TPD HxOFHD`!c_*Sl;{k+'u_Q-)^ŝj:Aळߵn*wwcׯ]^=EӨbduC呇+O8|[9g_*#$@"ի1?CL/Z˩,_27`&isfo<#ʿgk{Ѧl۰yYS;o~z a}/.+Vt|lyD H&@<Ԥ!KD H :\x.i`E9k#3{~(x[|{IUPRӫ]P{dkGwrIc3h2EJC`|s ^ұT~\n6[}JY7+j.J\{AY1菔~h<]no33 p -O+3%gY۶ \Q^˚5o*'<\Ѷwk퀒-.g~?ס/n}HeCxkxԿŏ\5mTiߚgz.8 Zظr>]n74ilZϽ;WnͦD H}\S}CKD`C`ЎQmXa]T͎~|.@)W\2^[a}3AtFd~}}M,/|sהy\~C7M?_z9e٤j ݪgztY^[yyM=\ス|kƽa9gAnE>@"$nlR$F>͏??-e՚5Ow-Ww?mXu~rݛ)}/vkOIy+ʥ>z)OD }r/.M`Tt˷[럹KW[1X'ca)g|˯\|k[]5Gプ_n 3U_wD[CQϹ\Y-?eK;jmťֿ]Ώsֹ_pҲ|ٲcrDǝj>|Ox}µRV/]Dtc '1'hAi'o,-tA["OQ"0rnj$@>Q>f<Ǜ$X^M|pH+A)s6gl ^[-#^KO7{=69rZAmu& V*+ (nH}<v6<=Gs{@|X~Xqfk2שt ɼFyR=;'ɫ˗6]?Sn~r毕k?\p2\9pvVwhw~ԿNjAO-YS֮]S.{ϖ);?{ӷL~'944ZÊ3@3hrp]V֬][֬~|yŚAH2t"0Kgq}ݯlړ/a9cY|AYP,[Q^q=Xgzf>'j#4,9O Lͯ9Ø1ֺVoA9;d MEp `-D b XrT# ZA"@[r-r kA(bH @$}+{~7o3oPSjF#}+[kM] uzP)mt8x4ȡMK7os8|+|6 K,-uF>w+쑗yg1vDò2ОUh%э}}X+eAZF.bmjLȦyY{n}\nw2}쫢hNi>G!--RFеT~y98|ZN^q>hsA&|`2a BVЩC7y"^Ǿj"/B4/B"GJSI|q0 \AaUj7y12Wǂ#؇C{=fw'S9jA 5>AgN/7g7w7PC.$P*wIzg5c":IQUPNF`l< Oa/).)ka䬵(cLn$v?W7&Z|t=mv {P9l;ޜ߫ܢc#ʹ\u30xK7/Ö0e5,ckXl/CW?,۟~5JY13UtuҶ,)˕vJA_yƒ] TR7E5F$o${Vè9Z YOv{͉I`D]!.MҐNvqs5ۃgWA3W]#a8&*&RG\Z\tߥ}4^3ҡRx [ǔd$L] };_1~Dؕ P9]m_;O 0w`$6qOd]⃥r= V 'LzPs_;z{cž`kʑ~x=Nz˵H՛=L55X{xpx-pGi4vmAH@ Z!o*h!|[-/tp0cPvX ]@s'ʮbJsN%QO:^54*1I6N7c1~|-HeHNYb2cҊT[ڡ 1HXY07GY_Z# DXH$S}cֱ ]tFrHQ`o\͗O .$F}[>qRh3'91(ę0\/%ч>uJ@"͔p4L/81m 2']NnAqVPmy"kFr,]C/SW"c: ^ɱk&~Ai{gObOKMCkA|-r)0j'r#=!u̯؁a58'r9xg5rNY {b7TlJs4ԌySؙMݶ.ml8NFFb\dNc6\ ζ|C(di8En՘͉C;Qs"Qz?ro#.ubU˓f!K;ugd!oRF'c15$anR_JNNƶ>ߣjn'8^$ܤ>`id l&e^6֥bꙚNZ3_Luo/D\언ϙGF7\ ÆܺsB߅dڨOEg,H9LwoY;0~rZvxl/#m2-If,_kӝl2ruҳa$ڎE81iWkL}]eajc\ژ:?p*6>GKoKW_ ߋb E\9~o h3 Wb)n:_7b 3q׃AZ 3 `C9ȀYZ!i/!@, /= ul 1:%Ul٪Bs6e9ßFQYSWW^gk0Yfq 0FFY(D06>ꀣ1aVd{EQd$OlLb36B?IsɪXzZ 7m jj<ݶpzkvsl;,4LmFB׮kf FDDir#ݩXWm{U7% I3U]Y&_]`%_'iÙ [9o;=IM2JC|GL{{c+9L4Cy\OhFңG %Ѳ'pw dUPQڧHRo8Du٩[j_&ԲUNwzg:=.3OI~A!1|6$$z>#Z4쇫玊Z(G&p|s0Dcr2nkBKqn3+퀒CTd%݃5ƶV>,6R4sS"jdR#n$XbqLLɝ TUhTp<|uU,:v&7RvǦ&=2kRNZ6SqbdOz"]e`%| ю]~_Oj(߮G4nGkkށg-֬:Tm,bb2*Zv>_j"L8wk~PL,߬E?;)⻢uǮ;d wA0=:U KOۈh3ݲYɈ샱S*l͜v'v|f2f8^՝c.*k&']{#0#H?yY9$KUv^Ox-RqNKҙ b M-6lYy MٛaXJOYnRX 8(Fv|un ܿ.) ٜeeS$M˶o„7jzd6hK9h*Fl}:-0ʐ6" :˔lYS%$9LRxo|W[[Zou/ZoV,a 0ˊGa0$Oz *q*! k&>X%_oL9G"i>o>PE n}}?صȍ|i &F] 9k?6"21!]{sup;`yAb8Yx7꫾ÍaV5nN@Ѻ5]Rd1(ܚĸ U_6mB;[Roč+F$.vlLz|0zunΠ87b¬7!S=!\cVҔcPd]k׮LzT~TwW7k0ȯؑS 3]4u O&.ē^‘$eh1J7;N P96 8HAR>7jT 2oKvݯ(S,6׺7^'kAz~˩gfJ#^^g}կFеXH3f0g$a󑧑zKW7\};k&h}M<17$#f'x{ ԕ:b5"]S-@7fbdZb2Kv-X]Y{he?t_U˵CUe'9o#^pov=;V"V]U:H5 jݶN~Iiov;<٩+Mh7SU ,ԱUj!ɤ'AN)Kho֍e9FK28K05 \6E ?W'gmE~H Mƪ, fFv)S Ou`''"1yVJ(~vD=X*W1~LN}IWpTWȱ>O2d~:zz3_7|P&QIAf 9u&^,ńSO^u/Hq~C%\c<#:cH,KemuE)4ڪqܱ>蜔u]yAFN|Ew,X0 VxV$7|SZVprՕl.¹HtmݥI=-[tU]>o~ǴJ6srxw*'L:Uϗm ?=E[@i}s5ZTECp4,`o !PtG=]Z=Gy.EEE;U/d۔5R9ic$Fh6N#i$Z>)2QPPsHK݅U F [n}'ΗFţ 6Ld<]1h5>{.qRnp>ۿ .݂LST"fd*/~4]QS=`ڂUxX5i<"ql3L]=ؠŴC˖ЭF͒8"l.mGM>'T,f+Uvwͳb՞?󐓧 ͉B Ft,ilFZF>/o Fƴҍ*y9<][VѴ8lvÜ{U|**\>?S>qF. ˱|y2o? z `زe#VKtrAm_}QYQFt"~r;ԍ 0xG?I6X*WWkeTRN𯙁B;o7tYaͮw=ތC_s3pi١S& nr:w] qbgoŴt=Ƽcd&dbGCH;Apx3V5v,OĎzvOcF*c3R>%=>ֵlv\PT5z)V`mpYnS9Ʈñ"> sU6! +UCIsmfq@lQJAkRWet[ط;-9(u8aaXN֛}sY`'2PE >q3?,c1p*źY쀚uS~Y?&yI[0<9w6zQ*ʼn#kW'>C3KŰc,.SgEv81~0Sԓ6a+|%oqkDIz %R9bIi-Ij9vׅ-km[Ur9y9ǩOu[s퇮nW1))9Afko"1kBWYcZHy.Cֶ Xxoe T7Qn\^ 0 !}:z퇱8eK\ݫ!-'G f #&Wi(JŢvL I +oH_9lh+8ِʀ&ͅmuiGbcx'qkmJeͺؾzfϞm$-Zp6&1d|,a۬*IګPوw%GLl؍=t#dd˄iB ٴvE C.;i KGrڊG>3>iN]z}ph;RA?S xrGm/[Q`tUõ4hEulת"N]TdX7^Cl}/-aWQhlX9A f܍CuEJ t&׷1x9z('LUKH ^VhbcS_;s g,'zW۵Ȯ&|n(J!D}'r{r.ssF(0\9J8*~p9>|˗˙M\\}"B#zZ wj3(U .!**f7jF򍿻ܮ|kiwg/[zE5nfFb͔^rGUF׽t`{0=IΦ'Ff.v&Ņhܺ9RYRoMkGH("CF355w0ڦ'm?DwMS/(ȇ2byѸԺZ=@۾pM\R&m؆Tsg/to hDT(ȡ>cDݣ؂s`"f3|izAeH\E05|N^iN9)y97&ʰ8jY1tuQ=&HWaYڵk+X(1tOu._3T֧j a"T'8:^+J^OkڵH$OӦMo# ѕ_wwSd_)>\9WJ~ʮ\ɧ;;rgEoA6},l([ aPQq|t•0qTl9\(7,Z_ ewS*ȏ̦".\BXl= q~Yn) F` }{[O. 0&6-bh0\S,?<%< 01&n 1<ӡ-┍nfUi[6ьdR/iFi'. -sF2Z6?`"`L x ^y, 0H~hu fn,)cǝu`@;bczP'aL 0&H<H, 0Uoq66bL] 'rFf\ '߾˗aQ8geL 0&5^_,k_(k0\9J8*~p9>|k ,ZL@覰6o2 MhV>c `L 6FYX^s%D*_>WQqUd2(#`0 ߸6`L 0&Tb$`L 0&`L 0&`#2w&`L 0&`6%eL 0&`L 0&2&`L 0&`6%eL 0&`L 0&2&`L 0&`6%eL 0&`L 0&2&`L 0&`6%eL 0&`L 0&2&`L 0&`g/J,sPA2:+AQQi Xa, 0&`L&P'$uC :uB` %?;&hl((˃0, 3}X\j=˭r9`L 0&@U Ø eC] 到1 HO6`C[b#H..~*pr. gaDcL 0&`0 A:h`c aΎ ʁ& [ F [ 21Luxu<&`L 0(d- CQD%oVaD]iN@ H,B@IL@pJhT><̝ 0&`L(^2KQ pZnQe_rPv͆c|$`ٰFKiT$QgQb}RY:0(1&`L J*`P# >2s{=ޓ46MY(#b1MPY E,#nebL 0&`U( Yd,ӝpRc sZ/6ǒ%{F^b pmGD]3&mK&`L 0&9;Bq|.sFfr8%ʞP4^qzub}ryE]zWzy%3&`L 0A\Y%tOi{pb^6.Ð@@ P,LDhy2-EPSB7d{`L 0&oĽҽe {MdM @ey崱X3"6tQdC{G-%PZJkɅL 0&j?q/Y.>toiy(k{NvL c2ƤӀT WƄ6|k@h{7k W֐Μ 0&`5%s(//.E'1oۍgo/E:uƖ6N9]E}lor\oWnݮyֲHC^u*. 1Ztr@- T(Q1c'_/~QN= !U9 `L 0&j*^\S[rM 4m^xft<3v"<(&Jv+k=!=ZAv^x  H~o{߰6?8~5gΜ*Dzf}NJ˻q_s6&`L l(׀FTP ,암j#}u_żT_'@AS\,,ChDnnwgo.…W@hѾ5N|eKo<|H#Qh2RʲRu5-1f\pբ=Ԣ W'q6 ߢUlu gIQZFMXQ3(~ݲmp_7Ŵ%våiV6-:Bګ֦K'w/%rbCM'M/p:`kѵ?ZNto_E-ߋf?G؇S"O9ծY mNS%W=nEqQOկ͛ О7E=_p\ش ň@v%`L 0&j<6k|\ mhn Hн;`)"j:aS(z _>F"A^uKuS$%8gCqf'3}SxE*.E5V|=w!g_1>KR e팡)>w6ź6Z%િOc:FrܤkJeW6^{ M?4MOjfKm셈z~3tÊe3k}@#<\ 22⩨PsU4]>QZ%^dԝWeu՚N Tк].k^L<.NiН2^<S KLU]8)c;!yJi43p8FQղr+qW?51"\q1:߼dѫ?O:7tP_`{| ?CǩH+OMa}ON", GIG@R澋Cǽ"eޗЉ>qBsE߼eQk MȺP[ 0u(4hQ};DZdO`L 0&P \[&OS[[IjIP'FlYA/y[ti6;=P.%cs'_ .: +%FH~הg 2yC[*ASI]<wGѵT`~Woؚ!*:~&sHd}USH]t3%o}#gX̓Zt.U(dm @.~i@+V(>3&`L`CkesNbq,;b>]f{zDmeƌx'κtxh]'jl ?$3HXO af%fԷH uZ;(+u1_ouC^~`O: PaXi"; QZeL 0&@((uޙm0 )͙(J)u!5$19ꚹ-Y5go/V^cB['Qw'Vb%U1X fu`?MF@q0vFaDTLFUD6ߣw0&`L x3`e52LEԘqIR6Bb,4k:_GNƍ=?c)Q>HA&*Q4Dgl2-X:Vp&\bg!q0][=8R5Qc v;SܷEj0&`L hPZ\ D4&ܱbͮc(ͫR:MOG|xǷa'(3pX*>ٕnYN eh%vӰX%l*@f.Z_Cl8vb?59^3 L 0&xu h[IK"ۤqUʔ#D=}G bI++S.5_h8a6kM}lfͨIgab밲MrE"ZE9_$Owv*O{&a \S$$91$[4닡 ݛmû1ju[)^!(7C$$a-11P%i)iH[Δ>}.̚hT3 6.^s|NS!pSJۉAD i 2,©a}w%ǽ=Xlތqqv%( qw0bhҾHv`?m.Ž 0&`(צb]UmjkOot[WLuȚ0vV#)uvc왘gƊ U2*f g=6 k,)=z-HJ.LR,y b216Gc Aw1r/Nel0.g)֫-G0Tu~h$ne3Ʌf$#&]Tfy!|wFr8>[TrpCux+jɤTO^J9I ʪ?m/4[,S(egL 0&n]!x D:\NcW;}|.xr.%+G9\ G/.٧]|\]#\nbQ9r H%h`(/~t{^ ,|aA>X. ]FiaRk9M=Gӝ 4j N]2͚99] FD6jHP 䦧[Nz3 %\)=&mNhBv!<rhZb 0FFQ#mia orLt0R3&J̡(oQuђ>-"uC᯾7mt!>e+hȒS}r]չOwv.)~(+/4ޞcQXsz*sB&xfGaBڞC>Е E`ϊ9V#OX{dQ)2rLkF/!|zƞMn``L 0&"W81 q`3}~oL"$/)@ncXu wy9Ŏ 0&`L'M5QRnΜp:JKKQ]{AL;'ӕ,,6 u֭Y6L 0&`A!rPPWJJJT+CѤm^ݛ5s&`L 0&pk`ChSKe+`L 0&PK(jf&rnڧɤw3&`L Hl(HZl(j-~;lذ`L 0&P d-6:Rujެ6`L 0&`L 0`C9(XY(`L 0&`L Vl(֖co9Hv{9n9\a&`L 0&PP\?7߱W c_, 0&`L 0{l(s&P lݾb$˪}w|~&`L 0&D d1L X\)@7ďC0&`L 0&?~= Y@iiL%(,ׯTR T?o\BPnÑ|%鲷i ^~V'L 0&`L ʁRnqH..6ҥ|>}ߝ:ٹ(zb!]0H|_T3qx_f-Ɍe88 w7X}(1q& j* `73[Vmt3dy mۣAe!!+YClJG8l%Μ9oMdY/< &PS [d(w ]^hn5z;>K"Cq(USmmF"CzJq!L 0&\`C%`k;yuAXö| %k.Y!tCG88&KY(>d` c9sBUCUSuOtْ+u?6 T/PjQImN<x, 0&j(6 U\V}B` !W|1SQER3,^JuۢBŎ ›J57Y.f`LV!\Ϡ(6B֏\2*\.dq,2a4[pP2,Ҭ_'OAS_ͽ Q;&P DΟnj9s0c|ߪlNw|ЈPk,v YGėGO|E hѬ#tw&?t_}{y*-pP.z+6MlHZ<8y /ERD;OV'M+~3/%M?K3hѫKsCv/OAް)upg-ΌKY*G}w3ѓ98f[8MB:xGehSanCضsژp7=z˷*֟,*w8K <I_H}umh/fd}}"%4kޯM˱l}՜ ){>o/Qnl6ѥs wJϑϘ`L&ݾ\j"PIFpeddF^o.L٬ӖS4A&M#ʔߤY*fn>M!#,2SRf0eCJki'zvW ziHxJl']oS$>d(c+?E¢}Zo*F``)@_f^w_v^m$ 􉘳[1.Ɣ O$udfL[USԜJm_Kyy*0wxmh5w< B ՚w"{b9a6|7%cCǝGOFJ1_+Ww";hO`L$ &cL @,r?ɹ2^;25H{BVz_V<;4iIh_x فOM(<A7nv}h`g$73&2xqtҊ>Do$]S#aA0of'\NhhH [4W{IL.Og$ w-JM 2優-3g@>76P;P:5-aN rNsf$+B\ۉʡl7}5z# /`LV'% ][dqtBGrrQ[QBS6ew1cp & ҳMXs6r/֭{*Q|"ҏ =x)ψ%HXVbʏPv(v(I{;пi:uՓSq5\ƴލ-Ҁ=!uXn/ʮzTgZq|Ƭѓm، q@Pz^v16o,^hb.$/IFeY1Y\l#w-O#hr[ds6A+A)70A3}_Nw :zN_f{XHxTW@嬯fة9.9eiQ_Xg^Ʒ߮F_;&e`L P;W?Ak6ߡ~kvKڽw_҄ysq x)bI&J=glo[[lj^{yj&#}˯u\ ێH.m۝{8;O* dmm^ժRѦP5#poWHV#c#>/V%Awb5dҫIwSߗ֯k W 2L ^.dʽǥ%jeL 0& pv&E 0o^}yiuPy}p K58_Wn.Ûsg犧CNxgq8elS~]ǚF_T-I(:T>AwF#t,Ayӱvt “Acii2گ:p ܈\Yw7Y6Zڢ~iUyF_ن} ۹wt+'~<t76}C_ -a)fO=zꫂCAc:b%Ǹ'C cL 0&`#dL :ӵHGJh, ='YLe~q+1"o%e&zr`lBV<7&+)S$;h ꦓkQKmݗ`¡K1y<ՀuދXpGH6&]\ȻIm6wra8.&E:=^ /#䔝2^&`bA;&FP7IƲx=T:d, /Fo\ӄQ8vQzG3LJfW{Re MtyܤZ.&3E>Wf<^ M_~wui6HC~6WPkIn=(2I6B*e|f_5Rile(m\l8dBUjNZئ_Y'Cђ0}{eW͛-Iɢeec"2ۆÜfnfGL%UwĻ8tݎ/t>a ޥYܶ͘3,Ҧ"DZlpG87@ m[Qo[4sm::FLǻGs{x^6]8ӇJm+w0uZC?8޹BCdEd[eMcN+|ޅmdb""λAol[;"2&MDn8F+YeׯFiSi1~E8:/"c8f/Vf5ޒ֒^#d ٢ QB ؋ow+M{dTZi?,1C*?zN_-WP4m)6!=Km^#ڱl8r& Tâ8/+4TF=״&MP]@:DM^ W*+Jeiq?v4bl"K&b0gc,M=bi%=+zh8F싾R% y T;]Ôkah}5wr>F8vGj! ffH 0&na!t !D}'r{r.ssF(0\9J8*~p9>|˗r ͸pʑ[HG-GC^mpT# O)۲-n~01Q4R,^%ޫ,)oXP:ardᯠcZS;f8t YL0(64\L[6@%[^ʘ E#"ODe6Em,'uzo~BM4-tn14Cuc ? W"H24h458C(ҹSmVFm%jn@b=gΔӹ0SZzMФqcʙ`DBy&f^{Qx) @b\U9er˹lSa>\2t|"ZvDC8ơL o-̡(oQuђ>-"uCwq}7mt!-}% ѕ_wwSd߾,WJ:娤#Uu:W)8ůEzsswӣf7@Хs[&#ivl]'R5,Cb,[dҥJJ\|9,.8QH6]{! <>jklݝz(!x?;h/`6, s[.+\·Zj= Q;{skn{m0Umcq:>vL 0&`CcWh彘{_k:u]͚AlA1 *ӹ`- C cqu☟=C-e1&PAV:9,x8`L 0&16=F kFdE˦oD}3OCvef2zNlF[]7h2DY]~>̇b=Z L 0&j6kHC@]:*>[aHka2 +- Ξ9̳ȽK뉯  аP\)v~woA(p`L 0&`Ll(ϐ%0H0 ?zr0h#>‰_vXs"LJ?4x4'Cc؏0؎ؓu ( GܸIӌ/9`L 0&-gNYYgqy &xs8&!+YClJGipֲ8s5o#^{9]0@1/b IzpʍgcߜE m=:7Yj`L 0&j6kHC=9t Ebtd %8u(>PGuYd) cY' lXJ£?FxhC80@X+f}! aRwp?ϣTA `L 0&p 7 XE8tBZ\RZË>!0\1,>f?JQ.)5c⥨C˒}>,vL 4aL 0&<QJ53 HHHQP(7 V:\pQ \A,k-dq#R0iKRQW |ad-ɲfujjK@3Cx3HHH P(72`>H+E,.)*cp`>ti]rJe{u\[nx*d)bhn*ptضk?N.FY F艽;p n4u&qľ@#!_`y8c6nފg+P!R"bĈ=s` >r#r0GDέZ{,xD?;=WwCtcͼuỼ#~WV3qFk|Yutu/"{ {.87 eyU +3T`wĆ3'CBNHHH@G]$@\%ԱXl9In5Y8Rp&_L65 \WZagv򇟿?f",2ԥ޴ TK{;kjg&|x?L&t]s?!AV&ղ߅Xu-WR?b`GW?J1lǂ>roζWxGqD&K-, 0n1zdcdocObӦguݍ2v&S ۏT];t%X-2?u g}'mCaf.;p_ͦLc'vG01^{cN܌ǕfmL?Zž-\ 5O3,O}l_n|'|ǧYG0Qoh/.یg߽G1o] @$@$@$а(']Vl%My>ȉʗ|~6gZ\;___̘2 ~g/VVO^<j ^OјM^Z+H!*52&,==ʂ|fy2mLQs/lZ ٳlw& ,ϵ#Mn@Oj&Z$@$@$@CBquJypL\Gŵe6߀E6S%/0t3]O雡0axHtMu=kECYښtlW bc-_RcvY-Q'?5m=LrMC a2q_l^wjz?/NG9t̍y61] sq{gdždwszO#   #@p,ıP>Nׁ3Θ=w.Z)8W/S5Ɏܕe^"״qBȰ3OK[w[8T| uK~3C!pQT&>7ڎfro~/^C٢7FԻ >-CKV8V5}~5̎!]pAenjv M_l1]RBw"EX-7^21{OohWV9c$@$@$@%@\_,O:oU$ ,Ų aS5#bҥRh>4Ac/ }.\C)`.\6+ k3l4EXOCEt}몥|`axQؼa{}OSKM{=`}aHU{1EA#E*?nS֙'#)>b{B[MQOȷX~[Wm`qGiH$@$@$P+N Hs-PC2E_ҧ\]oN7G#e( c uZ3yut{mژЕNȅU;aۣX;m[p1 oj98Y.#ڮ᧭ƢQމV:ᘵ0BCzuX( @P(  =9l󁿼P\bѤ"\L6]730vC D0و6#{_[}J ͇@)#?;t?pk.nuJ 2=~efO3NX6b;6kب`KhH)&Yz׏O}@[p>j}x~AM9kVJO~mv>=վbJ,6N$@$@$P?$`!>՗-"2ZK^?9,Rbb_"5j F0(.J~ҏxe.f@͝)i%~j O`?,Qā7h} ,䃷XH'< >Y_KF̠7p glY"xj6+SwN=98xgNmBG rncoQ%1~hcbς{;3#sqW>Μ) ~wbR=Jq?&ʚ{oO6i׊Rw   z~3 ԗܴ_bZ;oe],rf~Y¶Rl% ^-6bK-ij0ňC<6V.77WvsA8æ#6nn!T?io?]qlғճ}7mDږs&Zdܴ)jF`,i-N~!5Ī%r=g*?9Xyv"[;9ܯ۴U/.4ڬb8b {sPiJQԃ{T2B$@$@$p%phJPf<??pC+\)F/VCf5bkHҷC\;k=r?^MvhzTĄikLy86sD+G?>lvdS_R {T|,)DEŲȚ%:~v;d{al_c8Mqjխ[Έ+g䀹rM})J8&*Y=& xp1q^Ǎpv˫K_[`+ǷQ   F!S]-~[g 4O6qYww?/߭o MU~4 \UHo.qѩK'zN,[aVŲ:ll_TO<J EqlqSߟA ѡkOn At눯~7AhFm7,uvtNSk`[_ЦctB3HHHr\>&|c'**uӫbn0!#R[)[enlr"0jNYKK??!^tjnC@M@ޥusS:*XHHHLBX4F? Q{Sopu7mT*rx w[] }[kteC@M]`jP9 @-(kl@Ku8'"EL6;ʊbKN +j)-/v ~۴n@WNu1@S!lt'ľt^_DtNhJ$@$@$@FBF Ab6UBۆbt]NٖHT-o $Є& 6HHHOB ,VYyWrKʳ~hO$@$@$@$@$ exњj%Z i@$@$@$@$@$$ pg|-l "@|ȳ^      &ISkv(sf      K#Mݰe$@$@$@$@$@$@WU*IHHHHH. n2      @B*@g$@$@$@$@$@$@Mr}7l U @|J      KC5wÖ53ոt.^ TVV /_SOZh___h4-[تP"     P({f$R'ODII a6! R   44۷Gxx8-b%    P({Nj$I"(**Bdd$o. x(}8pr;whӦED{f$@50cˇJagB*H$@$@$p=f_n-I9""A#7tڵkm۶n@V(.@9~|{2m$q!tT&-o=8r$L&޷"1y$QWQ   kًL@I>qe$DKҧ᲎:XIJ>q Vʉe Jv7e[|C1{9dt~5]1HHHyMʧNB׮]eҷC@sIw)19.D¹HI55o[b]$@$@$@͟rA w>DHH?^$p#1GBy{Tc>@\-oP^CつX(ؑU*@lT ;p֒׺koĄk[J޳s\, ﶍ(:[iiC~C߲t*6r/mL_cxb==s#2& Eɼ!N+y!6N@p_%$.#G/<Zm-.Jy/;>ăqcaZ_nd$O 7r76>]Bs)@NNg=R? ^/Lpz'O2pWARrx'  #N#8#WSR|;C"`ڱzm[(USx4k7կ0xx |kd˼4U$#=Ul %Jb$DN6Y&l/du1`r&}($C&H4{,sNK.C$Zs&iJډ8 և쌙5Om%cղəDOWU飅d"Yf`9aQ>;ĔT&6%t =NoBaHHH+^1 '"YيXv_;sBdčW12T励\[ؐe4 COn*%ț/}eOhӎE8MMA^hklzz*)CyY/Tk1{@+xmhWj{_۫ut)Ԟ1ujw@IDATHLCE(+)±&s쓨.G\Mx Ubj!֕w/;4#˷c٢e}8 Zi[e|!|! R'źs ޡ.[pXw%ee(ڿ^aݦXK߇+*GβEhmt{-@ oBFIHHB-/NF@ITh7 lWZ}v%4jEy ϪEUǥߏ^<)/=6=aHHH(Ύ%Il%:hKBI ͅ ?~ qB.h2`` t?{g0|]n mLXse::޳&նeǚGJ.B [Uz^҈^_-QWvT[3ŗ'yf; uNDRArgHCRb/ݨLDcƤ79}bo'   OP({B6$@$%GYgQR\KSV+kK j̛}Cn ۀl7"  GhD$@@h9V |0$ Ľc_ݧ>[wўUUȥr!g W?)ǰY.ar^ܢ3rxU%M2p(b\LvUJeA{[$qЎma.{0B$@$@$NHH觭;]0wv"CRhJ%5vxѭ6 fSdLliJ**Q>B*߇[|;ӑzUk4vlfTsSv-ˋջZ#4"doA ok;`: 2?$@$z&j^H`ncՔ?cXL &emԉň{jnTX5]LϯNSR:m썉j&wދ4zS w<Ā 5CqrY"t'.A"]`چyĨq*{5q&r쎱zMxϡ2xbLu"edo c$@$@$@u&@\gt,H|||, ta_3$0K:wGii) m'x17iN@!=c]>rj]>,ҦjZ,{f2B=9P\jBii僱Z1zS P-vلadU9knAxQO1 uqs=6&ćb[KrR̛:um߮=K''i*V~ C oFԁico+^xlJl,MRlp"k_X$/jw1f u_ZL}KBlZ LӪmLL߄1   g@-`0hFV\JyYy}X_di6U=Of"y.97N sf|r<8Uc`ݰdn`ChZ]Dh@W~?Ͼεu7%96b|M!mx/Xj[S܏Ҫ5c1w\,OЄ4(,ԲXl@Yx< l J=%kR^G)YȚޱ=w`^7fWőgppG    70<' l@@Ξ=&z[އ- r[H˫CVX*0j)"}L_=x=98؝S195ݏK0k46bzǥFrMV*,2Ƚx eF-~R^@UJ FE3H%-HldĭVa O᪭ǰ G`(EfԅQ _ 0eu6fvYVL(_=|_(> #S.S!sNB#?z/WwwqsՍ_ k      zr=( GB{ @=P( \{(w ԃr=( GB{ @=xzcQƥKpETVTb%UU|x-`?Fe˖ IHHHH'@=3 'R$W Ql*+éS(.>ҒTTl6C{6 0h6Ab%    P({Nj$I" ΔˍH?ڴn3gb8tΟ?hn"=A; 0ϗc}AP鏄'ѯUyf.݀ @!  fM?oc 9ݺObmѩSd4M [oFЪ hՊBA #Pi"fm̺a!Chs6W3ҭcm&:%  f^z@ 5EE',# %M>(C@$:}kuhF1  hTʍί(9} ݺum.K߲Y \kkK 1 HHHP2Y5NbeXC|!bpc[!b ktZV؁'-R-HHH  +U]PUbrcYG]j? ԍy]5Yׄ+;\ Ct0%  &@?K` (j#=f1Au `.uỼ#++8 ΝGwtLoXz)|:xR:87j؁*}c 7o6D ňjm8pLgőg@xpwvuw?<"z ,5_þԣ]q ~ܹC-;$Z$NZ<ך܆6E[F#[E_maM.+eaЩϺωWZؾ j;HHH%w?]`" 3E VuOQMVK^i~coNFY#ZոfοW/̏7lfS?>WD؞L`|?Li}r3 ¯TWR?bicw/,V 8'/܈t]wvj=Y*!5M6)6Hĺ&xi( #>[}?Vq@s91}ᝁ f1}|8ƥ!;c" =N')dh._м۵Qz7ub4[8PO>odll]d<Ul~+/i }W) d% 2kt{,K.CpiWsb9[~PWF~]zmQxs꽏ped SĹb" 3 eg&L!:hhlE,׹a,xbٌc?ko,Ƀ񸒳1X<UW/ }n" mMhzw#gqI|l(λ1[Be  $@$@$@zz@= (z6ů S߼J}eCxio\LbpZnV<OJJXc`8/'~>OAE^PFϏՉqy 9 /j qlm:Yψ`mn+n0hW=G[-gZJ=D[ap0e췸m ?cbO8ba ?`-#$@$@$@(0HMîy"vY:JZNeht !- ]Q]Ĕ`1.Z*ׂeQ][ikXO_@>Jt-7zbxw^G$@$@$@rI$@$p';xH\3\8MͶڙz:V-o1OOs\7|ǡߨnÿ(n>޲^  XHj!Qr}A&!Q7yՇ\cqֳxG=ĭ U~WsQ,]0kq1LWNIn8kzSbd蜄7G<|UF<  pGSa ԉ1}cՒs~WhRT>ؖ"bRIyp pH>ۨ[#ms76lvqPq 艹1ĕV Ӷښb 'ѝ+6oKvyDOqLG@6MMd*s|1DىY͋5Zb[bQl9#k#߃/=jjR;t17IHHvʵ3 xDb'qIJ5FW@;v3#sqW>Μ)ʼn#ݧX<~$ ^3xA(<#b}>/G%ג31|6:c™#[G:aFX2$k!N5:bD({VgװZRsD٪ǰ=b8U.5{r=wv2V_.¾8u\fvyxTw5x1 xIBK`4'WZhA6ʮ=IsC!bhzv%ƚWq>#dL3RG~*>;)t*,=+0i-Yֈ=ok77-Ah}ؾF'Edmw|"[]s;k}W qznw[}r1ʻi^ra5KfdS1G~֣v>kbyۣ => hNHk @3hӋݺ>oY^j Ӛ#?ƮUxo ꛈɣZŰy0.fE[_wy Ti~5/5WxsbfugM!JݽSp짍:JnJ vޤnAp+ ;[}9&M/Bi}l܋09ڬ˞4Yج?E; g|=tc%uΩc'zǸ򬿻;Tnj @!-(??/]PKWZƛ?zaaa j%e۽ҧ+qʳrWʋvkzV)yJ\K{%8g{c.MqiHMPA>څEΝT,[U'E,9rT>NQ1u14Om:FM+rQmн^6l߈]φ6WT=kJ`7wׇMqD2MjE   rmO+{t=qbjɊj"˔`A8tmBYu1@bRDOo)}.| ۂ?~n|_$[N$@$ДP(74[hݺ v<\ىr5m㾻uWhX:FD+Rp rM-|AC0] V8ԆYڙī6\'  P({F$܉h4"m8Z_ŋq%kmٲ% AhK`LEn M `Ӧ;QU}SnPVqpp}~lj2kR=R0B$@$@$@ 4)lĮR[W]P'KE\[#ZxY\@ }Sx;r qpK 2ukL   o P({K$P)lbVޕ { jLV5˻AY>_/<0ͽKl? 4IMQ$`O[D>u#GIHHHHrh,%%g܏K     PgH$`!pe\XJ]娨KQ-&-Z/BSv#ڷo=k     !@0:' EryyKqQ?c'qyxބ-ZMe:}h:) $@$@$@$@$@'@\@1,Ev֫+P16 %1ɵ.nK"pO8ib5c     #@p,:& [8x 0, !7" B(c%T.PնK೼E8|jd3U2..{ νcnv(=3cGT|[wElLxTC$@$@$@ kGW\QtY ZGtIRB{ q3Kq,.];waN=ytsJgµHe!-ڷ%xa@ȵF)oO[S@IHH"]\-Νcѥ'Lbr{xj!~%mY/%]T^4cCún"!b>髋3==KK uF# gw@yE=k A0UŨIR_1lIL2țM)/_},5)s~ >}od] HX`Pn[<%>$-Aړ KB}y$P,vb9_CĥX[6#RDiKRQW |ad-ɲfuj*F/@p=0`30zj#>3`#[   P(_ӯRȕ"lB1k8bwǴW^E׮\e9-2:.UU5K.fF}ۏR4ݿu;ܒxb#P,6:`ٰ5tˍJ勵|[=qw~,x&|n3z݂F%!Pm[@%VzgnbfSX;hΊEoP 'Kx(ؑDZwv|< Ӗ="~ddW |r2$ڵ\Z:DQWBs {ѧ]{#a-[%X;ZHP=a=8{c$E-(uN$@$@ǟSWh0rnUBɖF^^#,yb:i㒿"$4~DYX|򏏰3oVŔB$Bfޛ6m+`xlpjd$O dD%/e9jZymT{vر #qu= 3myqssl?>z΁uĪ}ri&a+ämygN#u!,z( vj˴<+tMɘ$ܟ :eK7ct\la"̸~mӎ?RQrfVOUZOL@ ӄ1cX2jY~#VtdCOQ(;P# xB@{1 4˦\B(+u8quiiyETTV6Z|x^}^lfN6LhJ-BHNNMEr٪Hr.GkFL_-ss%m X3Jv"916lU$˄hT`N!))H ,%x/RBo_j.bރ%6 `Ҩ(kQ?5X/㒑Շi6-*떨F|` UE{miqx_/[܄BDrrj:Rli4sx7Ei?#iEL#  hJӮ-"v7vS=v`GQ? TiЫWO<8R"F ͉@~g9ZTaEXS둪 ҵDdCYy E Ɇڟ]lLXU:}UrVcѢը*T-ea2{:-Umة MnjI9rLy+\x*u$=nY=BdčWSrQ}5_\=ؐe۬u++%V`kXk3:gkC>>iO|wEX{LKGr9e9=M@!fEIYq!  e'$L JZ9?7~ '_~gۜuksl|}}1c('A][|  ͇@a_)b/6~- ִL¢1]QZ rr0I 2s4djR9w ] sVCڈ!Ia* U[-bCslƣ ʊ؀'rpu9ߊ@r|/{)L̴qwwO P#M~~Zw3Cw(kearXN[w^ BZ^Hѯ= #mS$b8&սg%XaMfdU-܀ ąyOWOxU$+FW;tb}obyO '& ek`/Z-hv-*dݱ:^#NCjV&9:!  hI8y^LuT\[_i \nc.0t3]O雡04<>Yٰڟ СOb]J5%L*gݍD짦LjǶ=ҍnp*ӟ rvw~Խqׯ8vx@ !bm< ߞ/ذ& Ēgm]qGlR|` ]Ҙ@$@$@$P7uR$@ ӵuL5:vsRLv|ĹR~4\Iv,V[0:6p[*I^0h* `cIh &/X ڗrkguz2?m57cs2qW蚽ߏʳ̗CueOW#1j_I\# 6\1$_Tgh^.,eD l1Յ 7/lFIHHj!|I^I*uMue-ȴzv3zh੸wGGiM8ʞXPMLZ+ԿgbQ8wrm!w [}-.H^!<Sv"}FT ߡH[;~Ñv-D;\D$FwZ9aHHH P(74Q Q~>V,>s:qTFȡXtbg5a,T%!/_%&w5EU+-ULrStřT_щTq'QҊ d]ba(<`&$ma#w(Qmmb~0$ $9^l߮Wle1V ao_Zelܮ,Jue!Æqq[ 48k8 @Co /.yp8A NUΘ Z"KJy/Sfh.:?' NbKK0aj衱KWlAlMS=b\U:ea'Dl|C'e6|˜L^HjU[\}3Qkl%-|' Qh;Q/O <~fG[Âá,3N~r$"t r/w3J$@$@$pP(_1Ԭz G~>ʖO2 +j%}3c=@ts^nY!(CP=4; M0WP LM ،^)c/W y6|&|h*{:M'߀\UڲL7DOT:Am4[Kv뙜 Y3runK"5whCb v-׿f 94 \IW6f OeȵLO˻"،/I~m_~Gj[qe?G<YC3 1 3wE{!.2?b 6u&EKf&!X|ui57L˛}M[SǠ4-SX}"TW1q*'%yɘ8q nv88wݴk53?|hݻ9a0 EhHtG4d-.›ψZ#$@$@$@P HvF[| Z9F!W˖ "\8Y._Z^>oY°Q{#2򚋬UqO Ze8/kh[i[rYphDP!Ō"l\ekl]yP WK-iI3;{nz41UTFIEކoqv;91eߥj]T }>nk[8&w.QV" ^ETЕZ-J--6ڼeFn^vSel,MܨUlDEEAd93΅<^{̹Myv8>0E-tXvǍشH[CS@ĵݴ^ ;|jkot>¹0֘cXkŽyڸ1qYF(4%t%MJX+am6ͱ6^W¶WB WLy5 oe)B̸^GOwرxk&|Ơa'we"J+̳"M?1ʏ?5_#WGer].Qh 0}4@3Ϲ׶m‡LRic2LeR 0OPEk+W!ȞF؋{,䡰̄Pqy<w`v&L_ 1n>(Uq @{!wPfҡ: ]B|U<]}=Lߘȅ|/)oү+;)Jرҕr]k]q*SŠ/+129D5oMW@ǎAH]{@={hIJ^>6,5F'Y,EJ\ eJ\|N|5d[t8Ib xy,2G_{E zW4{&\&Mt!HHЈc@E^\g20k;w ϹR£=6dBrKT5+ 4ɤ#h& y7!& vpz=ˡbt|˩Cc4s(#㲎+mH[Ҧ-ge[t@@FDÙ 1[S0"WwC~xq~Fn^J qx8z Mg¯0?dmȶZ*IK9H1tyʊ,R+O-њ>##v:buII **PB .Jsr_5Fqop J*1,]P$bsQ4/7#G$@$@$n P(Wρ{"jV9lO<4@IDATmO9L^Ҧw_ypD+QޱS$@$@$@$pEP(_l-Ps[#F$@$@$@$@mq˱ MBmd@$@$@$@$@$@$Ж pu[~^8lCaHHHHH((a 4rӸ @%@F_,TTT`ғ"!fWQx #"    / @/] x♗Ѷ8_V>߉x1XHHHHHMncq|,m=~BeHHHHHC(=fH p2۝Ҙ@$@$@$@$@$| ijjjpR*+*Q~".^@ee%jkP޵X>>X8]TlG[T$z.     P GZiH0l)N?GOTO|UB<ָEOu(1O>sW 4,) $@$@$@$@$@'@|@d)sُӧpu^%>|{d r+rZYG!{#pm7'2F 22'岿6H$@$@$"[M`F@.;>j"bx 8,}#U\k}dXLN~x%/1Pyvi!P43-B(]1w'P%ӅB^QjC$@$@$@$({=v$I@@XT[ 5+;_>R(K6,űPR,K_>ۏ 7: ~#@CVtjXʿnq%qRNE#   %Sհc ((8D^K5gx&q0Wq!cyGI3ʕLXoQ+5a?EȶHUhUegIHHHQ懀<@hC0MbX$WӮ~d!~Y^2ͼZN&GK'=9,RP_?̲@f~8֬^c'a>KEGN<o&G$@$@$@. P(DpRLBYb=|KX#DB>ի ,d^CZAqϽlY٦lu8wdr˪b$ 'D=(>oQ #&v5DWٟb10!WJ{6<[퓌W\.v3d<^}@(wF~?(brчyxqEa^}` {}9' +z$17%]_a<{t~<)2MuƏ%IHHVՆ@p(${:ۓ?ɃX8'MԳgW#,F E~⻜lsY,xE"$72  xw@ҿ;}Oa >>G .J>CkYHwfd>ouG7:\x4v[~1yOdo_wnGx<% [ / Y<ޮ/cG+1nQ^qaxBYAHHH@AHce,{iY9!k /ET J 8^ٜJ*; sڢ1nx%7`xLt|ە"y@Ǝ(-F*l|Ӂ7"H.MӴ!Y'N+E?=–ٸ7qξEgL{,˖8V`HHHPnߣkHƎup>T2߽ʊ \|'Jlߢ<]'0kX|l wYͻj$@7fmƔ@P<,:AYoBah}K_W-3q(/u(< k3J\y"D!4Iݾ c$@$@$@r|u JZq8 ?5 vSkfl/ˮNüH0^6=L`}Қ҆ɭlKeu?|i'mΘxmZ^–WlMbw=nryHNQz,0HHHPn/ûh)ߠwoq[u* }Kٍ8am-.VPz[H<;lE< !* UJS-HRٮRqj}XA!Y~CJ^w%:Byǖ3;Q-3:Cє y)f TtKcHHH P(`EP 4GxNgϩ:bEngT7D~]j\|O$|_c!mI6]+&s= nCRӷ,SjVcŴF7[ml1!wfYk4`6+X[Öu@^?!G_owW'U' Umr\W@qҵs\%,Z_>#zf8[ 'V}uZ hی A`'[¦\"4p 6DƯ/Crb1[?jt\Pͨeq:-{uY $@$@$@$@_.T O|$QCu>>8&r`W:ںw}M2UC'm"N G>,p[g|ӓ=_+?=ylYHuSQx&|vޜ6c-?aI7 Qke<4}6 @P(7k1\k1r6XRZqOع?g[p督PlRV/툸/ېmѵ>[!{qNoLe,ix.uTIEa60& xO,nooĞ8}KT|XEiѝnL92gڨ8z"K"[fNruv}[W){NWVͿ36s=2 $@$@$@$P77 @s C zY*@I_ KK*Ł]^'ZqFz]AEi1@(6d[tw0u_j-V;lIW''c`'5ܐKKcذNdqK †Xi3[9[kBoŸr=Zn˸[f:\pLkL\[1ĵ~}an-WzzqbIaxjpzELG<*Bt0 ={DL Kq&@T%:yju}]0cЅBWUSTsP$ t@_0HH2Xn%]%d4.C&"##BKV(J~}cbK۪+])J9UW)<%ڸ,n\n-nOT$pE |E^WiIJevZXYB,"Y*WBɿH>{8AlPswCΖw0ĽRFR7WM#$@$@$@$@$@!@B-N_l@yn/LHHHHH=PnocnuxY$k;~ ma     PH!"PRrO}۝Ҙ@$@$@$@$@$| ijjjpR0_ą 0VV?w|d% U]#$@$@$@$@$@!@ H\QaDQQ);ù'P_s/ųwu:Gvs     Pn>CZ "yq)ns""0̏$:ȸy+rZ?_GEܽcn#oKcHMtVy*F߁GLIh6U  ho`+}qw˭= :rcB%@L*PGdXFZ#rY[ǏX Y  D_ۥ1B$@ `8fϰJ~!=a6HHHPn/]>rOO AqI5jW(cw|PmYcX|otw:t& @ T=8`HHsȞ'P^^ث\y /T#į|DDQeK&,zu|φwl d[t$mLFu+j8y%>vHH. ˆ eK8]M5UKlRWIlEX+BYYY~~G;\bbȶH[\;U.P -9y+aHHH^\z]/f@T +$|8˼X~ %BCbm>KKQ!]NC8׊%Ǐǚk,?$B"lEȶH{\tE 3hOy0F$@$@MBٻ{JH+EI>5 e!e?]NU ĒB$Ffᔶ2@^̺9KU-iEY~n&̀u$c,i5EğS'R|$ٱ"fO|rF&8c^fy Vg Ye{HNljg:\uLTto}{HjbCʌȢ<7$֪!ojAD-4e.+[}LYbR=!FMr}p5q!9s6 gp cK3Wd"}ꍰE)$@$@$о št$@"`>Keeӧ,չXYe+#˞:ukVeWVF|1Dth_q"ىw'b${<|LH^oC{3R]d%Rc&aӶ`6NiSfbٗ7w*laF Qi/c„4 O'$;dY1ҬĶ-"Y7*64v{5|bl1"1 1.DRyTW"R"gX,H)4_ 1ei/ I[$@$@$~ P(wϑ,cZ9Va D^GXu\|'Jlߢ<]+"`8OlӘi:Su VU F hΚ8VR3ؾlz,ظO >Mxe22y&+%XQjsuZyHOogkΆ,Tf`ݺ]kn?g;'XCU kQ΢|*gj\Oy/ؔ2ꬰSINo-ePG*L L,.;e";9FrSA݉lAUvV.Yg"=@PscHH9 v=K@ Z p.~j6zF?O?6 _]Ny/E mW&~YnQ%~zD'$"NG`&` Q6(ÛFfn3b}:D+jCJvyp%ԚMڃCds ~bAad8blzx4õuHXuCBkL[mowX3ے}Y2u,ƫ4Tnnk,=K}i>I=~|kسBUކ*[Yde  hs(+倮$<<+gS\ჅA޽jUFl>8].NѶk'LޝPql|7Ω+qȶQ}_+ҩ?I9b\|jk3 QDR<WfspX]{؆3sY-* oWJڔ59:Dn&o:Ch0saDĩZ$S 창dL`˖3I=8Wl'  0Pg澈~{mWyFw.NJ)6UcUqKr N]:J>:ʁ'G5Ψ1Io`eU^쿃rYeDVI!9tA   11A$@#?лCu>>8&r`{?z_EЉb5!hIIt@4܃&afU'29, vwo'ʊO>AܥKʤH+Ж#UTj PW:d!XXZ1f4N;uk: 8hZ0*ZTM !D_2_dEZXBm;>[nxHOq^"ɲZbSڦkELpLtrQƒl~y΄5oEmywL횉EjNch~1ıu|K*Wnc/wl;2 @[%f9J@/JB ̑^,6-b Ok9eOEço^~fHX"d qg-Sĺ2][ޅ +7G`3Qh6w{4~ɒ$@$@$P(w]AAz,3/~# Ka,&E"mG A,gBYc׊}/=[9!!&܆lH0\k&D>Im @$@$@$p%P(_ lЋCt~9#Gsq`nURI^9=x'OG]k>mȶ.+qTۥ鹑U]kߛ;B$@$@$pP(_lGXwDll7eر(3\xI^]!bYn+m#4#;!Dؕˮe[tm!]ձZ'rqNݍ&XHHHEP(Vm%!! DTK,O7,˒myAFv ZڑE!iux;N$@$@$Xʍ%r$)lbV@V;e+K_TN@$@$@$@$@$QIc$ f     hu7HHHHHHP(X%     huVrq90 x1({aHHHHHH.? Ϝ- x1 e/~9 '@|E      /&@/]#     (/?sH$@$@$@$@$@$x=vuEuu5.]J*aBMMMҡCt@^ǧIXHHHHHq(ljH^R$W Ql(+ٳE(**DiI L&|w~~~9 ! LM%    P(NjI%9,Er^Q+9b:l0:,s#8v.\(G\\Oudэr$@큀?ƦL <> ނǟy׾=|8F  ?IS헀\n]TtŅRh_tڋ P]4r>U:$&;RjoRlU$zG;._n)=B9XcHHHa 3b hܓ|iLDQi3t! OknB0in3 xw@̑"GxpK]ߴ|v_D7cH?I|HH5ٮ_?)J#J⦟xʤ޽{a߁AEv GhƦӄI)9﫝jw%XOų۲   pX"pR!p-ېmѵUHzLN|b㺮!muu,&pͶ} 4rٱ& +>v^7}}OCʄyhu cKho [q2~pQqr;\lC$@$@ PnIP:WB{͔;YSL8?U AzP" ݻGg|?l>~]{P|?&v55sy^A=0oTNڋ‹2; N qW4=܋#Py^\D)nqz6pjm>Oπ;L?Lv.`tq03#~o Ο<q yx"ݟgl:k鮮J08]P> I?7KtuHD믿Ƒ(.l UWZqT=>ΜYX)8|$ܔ`{3N$@$@Ͷ,E k}OV7YI۴igvXb58<鯊mk{2ϳwE4xWs1tM8N};en@VvxZsBx S?bhw? gw.ÈqK_ >O%#v 냦03/4`gqsl}ڲL̛|K~|8Q9mx'މF̼z|){4`)rwM|Ia!}iE*lQ~/q~ߔM3qEԫ9s2bm0_{!|od~wϨږ"}SҰUM,kEOou2N$@$@W@+.%6I@ Y4ZebKt@4-oMAKD0Hc8svp3ఊܷ+E477 PD*;_ks]d*; x0V*lN8{znEŢcS0MXn^󙇊\8?+Ne挱ɲ pVZ<F;~|ipaQ$0FOK]:I[f{߄#  $@앯j<-ʥIP4mS7քs'a`#fv}j31v.z+w IdYh:kX}]pO{p}cM_j'_-m__u.!c5sX,@PSڴ3Иyu_ C'1i@< KO"=U(.c:D$ عŁbco[_|$\OfsElvs[?dgqNKN'.߅|Y]:݅JNLmO$@$@^EB٫^; (S^kn:Lv>ʲ_r)/+0ƉK].R_YH}hgfk.0'@l胣 [gQOyT[a t$OfmW-rX*G5+IZeLiMoSSكbyp:rOP."X#n)w_ sU~Ldm^w,+n/߶m=xmےvC–Wa߿{HHHP({;aZ9)n[iXYC0!U ,y{S e77Z\*fXA3Nx =4BZ|'4D%mo[dXݷliO-7NG2̰*i^͈j[s?6qy6-);Ga5&OaϠM|k2yǖ2HV;Ⱦ a%> \yWmd%qS(N#mI6]!m]-K?Xm'hq9xzL?Ą܍?Va):S-W6Cz DX~_ުyHHH2PL @;# >lt~%lŻ)+[ Ư/Cr1[?jt\PިO6U,y:lGՒw2c/Z]nɀZ5]AdKU nXH۟5ڍ&`μtxVɶSf Y)(k IH H?ְ}JTc)vqx$vU5@2T85[չSPrF17ҧRfDC€$[ƊEgw-  r^= MTY M^]S8$W!V+??gqBu{_"fO WR?Ki%Jm7bE˓BON՚/ njI]~;gtӶl {w`/3\^}ޣg.]gTu9w/٥܇IH7U1W-HJ L{la6B/@IDATC, xO,nooĞ8{}>3ou`4$\|o␧b16Y]î+?ī'ӄw&#W7E',n:*#ƪw})eU(_-bKe駇>[3_XݸzL5{oMo5wCbϡ\=[b5mzS\z[UpY3VͿ364=aHH4wW/lZ-:Oq ZyrSVl+e-D@ܩ`Ki1k9ՆԙHm%!XxEJ26,F,|SlpH̨8`;jzcp˸]<-GqgXi%N'G9m3<aN:c/++x:Sf<=ξ/Z{s~sR#㩖㻂cyff[*udl]%©Wgwϳ%{p;a`Q  bCφ)`(=w6,ǧM kmH۲ oK X lU]ԃ-I_jimWĵMYYt0ϰxfc} %9BpC?īe{tĬ1*[ubV")N,Mg*~4O?AJk8(_.JX,Xһ4O"ȹ/S!EvOd~%~tWsɘ8f4z|36abY .V[8S \I>MvZ˚SmFŵuJ\v2G)qצ+iW2_ kӵieQOƙj/RUa!^GOw 5w?s}ҞM6N9AET7gVMX^s%0P.֩baxʺ'6="ףlN%! qS$zrk}qiQU=Ϧ7~.@HD JJ*S;n4s +APmxH@Elb7!5_E@U]b9X%38#B@Nbp sP$m03Nu}f0l vK`ǻQ&G_tO`_tao] X EߋV<5V_Ҧ+ak}t%J}Qݮ~]q*SŠ/+12u{']fA>zDucy=zڄ쭻?wOX>q⤰}q mѵoptuvFP7BwCΖk\&޴2n.+29w\k*s}8zFsn|+)K#E^l:4ѧmbNN N@$@$@@3cs$Кrot!z'Dm"YI)))ܣ8r8:Et1!ۢ#+A oS/㡵Ivgeͽ.VYHHH\Pvͅ$NNի7NR;0iMqêy߳q1!ۢ#wS#-asm:tQsIJtE$@$@$@$R([,+$j^Q 2VT\ĥKP]]$ 6xupJ 8;m'4C5-_܀!&e @ Pna4~Ha(N6$$,kw'+ĔҮP$+t7-Ok*)Oq.6Lue~#   VFB0v{ Ha+Wse_M%'    p{X$ Wґ X P(@$@$@$@$@$@$@\z`iF 4gUIHHHHH N9"      fPn m]m_2˭@3R GGG{e9o2ИüL *=0FwGB88L>Ď/ƙkz iw;TCr xP NE8}QfO}cviy̴&E,ϋ@pm7#''&^(t G &8gz@[ |qFFcԐul|wuR\'t{ef\:Svog0ӊ 3"3>IHHl(m( Po5ʽɊ}Lg29)$RyI:{Mǰa^3r,e!/R+Gl kr@bsYfV Ŵu_ S'mKZp WD5L0XLǞ c[0373V9gD7.~ ¼K L^/s mĩS6b僨ڷ7$c`+7{Z26|%Ltk8!& V>H[ ߠ,3@$@$@$Hy9aA@ Y4ZebKt@4^3ѷ_[.`:V L1S-w8̫tJ{ &`xRG{hmHu%3 { xE UDf@TݍSCErpL9i66i(S'םjtQ]Ļʫ^}vɖ9?n9߂APH37ͳ{ԛKO$@$@ PnK@ xZ$+"[ˍ ^Q'Ag9lS۩MP>LF +P֭[ZT yVGf; /ZY$LZ0ܖ7{ppzx;0+Hxht&+UسiڕUcQ=jS}*V ]nOrQU[ۗى#vJBX+nʚ,TŒ%Pu&K3n/> 4DB!B'7(Sў]a+I :̲xᅫ̀ߔK-nƉK]U 3h$&٥) ~=2q!ЇDaخěFRk_׿Z˔kE0:_̬oRzڲ=w_hhFԎ|hYf>VN]² +p&{ F&Eq!a}X|" 2޶-G,{dmi_fۀfjKcHHH5 e\JM& mKLu%8-؊|GM$+e)SC%>  e'$L 0Lk]F#%Zaw{7/ նf>1A=kFNd`- .1k5e5AM*^W!zw<^_Gָ^R<4ܲڮ03ֲTY# y0wT"f|{(īO$-]\沖Ta_Сv|5ڴH$@$@ry) $ QYV|uS.N^3gVf/3 6Tf11Ad;~bF+yt>C 8q !&LhY=P#W]Un!HHHe6 2IQ[myE0Q1g%5WK\1r=j|eϚ)YcXTkv0Yz"^*,np_β>tFb+ִ.GpG~4>~O;Ipao{'ިռN$@$@Lr5gv$@g7XyЋxɎۇC> vf$E]+yݕ“؇WGo K&o"^,/$feZ-a)~>eA}gOo?b\:jߴUmX_rUC|  峢Y P^=#nQa"pUd}ǔ9 ,Eܩb:U ɖ?tca?Bkʴ]0=V&Oč9ZgyO;gE< oHb1^R}sɑ >4f;؞UfQ`1xax{ 1ƛ3$=GHU?λ͒aw>[/Fk6JB =#A$pv_>\.t ZMpnlU*/D@oqsȉ8/܊GCF24o‰j -jʕrHH <,)F-qNh+s} .W.кurKi=@~xy[c,=cq}n[3$wo3׏~sۯ8~w3ӢVI Ƣm۶ABB:t`JPUe$+g˹ػw/; 6ksgs F fmЭGDSshٲa$o(Pib/zewe?rL@$@$@$@ Pυ$c\`GO>uV8qeeK'|Y ))VSU^t$ }!P9ozoˁ@xa;OȊIHHHHB4+D$P1ݦM4i]>~8N@$@$@$@$@$@ JcR      GrݫSHHHHHHh(W =4^D$@$@$@$@$@$@ @CHHHHHH\%"     ʕǤ$@$@$@$@$@$@u W, @%P<&%     {h(׽:eHHHHHH*Ar%1) @#@C)KD$@$@$@$@$@$P 4+IIHHHHHuNY"      J\ xLJ$@$@$@$@$@$PP{u T JcR      GrݫSHHHHHHh(W =4^D$@$@$@$@$@$@ @CHHHHHH\%"     ʕǤ$@$@$@$@$@$@u W, @%P<&%     {h(׽:eHHHHHH*AULJ 4G01%ΠHHHHH /@)oIGUArUP=e֯6ꡑ Th(-'   5c{@F,f:0JqdO$ujڨ>JʀNɅ4 @XT_QSh"KT}MT}O:4ʑ&Jyh(1߉+ױS.ߟn'ѐF3[ @NqF&XfS=H h(G(;#ʝגKOAw6}./ @P6 ⍾c*g9{K#@C9r,)C@M/ +{1 z5oc%!6N A@ (E@V y>k>'^QB&@C9ddLPrrOgpZ^Mn|NȻ$jtY TD;*YMo/)t$i4#Md*qįAjM\*SP* C@j.e(7mXc^p.d%2WL\>s6[ @ԷϏtkc֢<5Cqu%4á4hc6v7"17b h2HHHHH XPV6tks${+H H4h0/X ]Mc @\IHHHHH (E$@$@$@$@$@$P3h( wJ$@$@$@$@$@$h(GiP-      !@Cf3W      (%@C9J+j 5Ý D)QZ1THHHHHHfP̕HHHHHH J PҊZ$@$@$@$@$@$@5Crpg$@$@$@$@$@$@QJrV "     4k;s%     R4b @\3ܙ+ @CHHHHHHj \IHHHHH (E$@$@$@$@$@$P3h( wJ$@$@$@$@$@$\Q"(öoa՞"!~qz'Gl-QA(܆ 2qQ vۮi5^k_{3eRwu-4W+!QH!}T?Sy,|},6/7%hw ӮzBr xZL4E69 ]B=9!.Lzb1UZ+oC9޽ wmCΑR1bСkw$zm`[v.JQI9AQ~(ڝӧ4b(GO)_w[EV;;['sn1 %vx`Kq' eL{N׭DXv=r?͛m)4ir%]"c;ݸ{}}nH8#zɩӯIU! [adF (ڵw J"b4Պ^Te2}LskLc׷=.bLjEnּV 83_,hl35#y0f4No"jUe+:kA*Vx/=;sʽCrRgcq+{vC@'79"0CK 7ɩ#"arctȈR3TΎ-r `y*H eep9F{c9 t0vٽ?|E6^zûZ 5FK=M Rm=h99NZ;yZ7Ѩ0A^JZػ |<âTFhݪΗWzbfډミǟ2ƶ+Es_Oo˰kX&fhֺ=%vRow{ ";瀱3 lLYvnq<;o崥VOVdM>ȶjׯ:oU͈i_V=/?%6ڑ\Q %BfqW@/spN;dZBW!Ǎ:Wb^{\g@yۮ)hu= A+c)Ϟ߶9 _y%D`wAί0ں}dKw۶+V}fΫ+ktXlF$Jks-;}svWOeG\Hz۔o^vGsb5CvY);|t" g3#|N{DZ׽4kKWP|º8%=B'>Z!vJǝoB&`gb|lӵ7e#!j$P̙3ή8?BÂx}}[̾tfonp3LmM:np=;сQaIlOdžWT^vB`,W^IBrԙs0wg+IYH^ ~/}kQ7u[^Ie7+wE"d-ObN_jb^EX#9uq)2ERbk]TA"II 0.v+܄@Fre1\4F)MW!SضH?duX Yv˃rƑHH{P;12@3Lº]eyk󔛬ѩW ^ڄv:ۺ ϑt3ٖh7នNzwv^ϴY'i9/ۺF~rq5vMT >%>7ewY;$wg!ٖȷmlꙊe9K6(Ox0 Q{[" ަĀ`k|gMƻ%xݝ:Sۮp!B[ eJ0dt;ԙ8+G4}n$XX22 YnLMٱ׺uϗSi6<^)K7y\zL/cܪ*N;W[eё@5UY>};mvX9"$Fr`L8N\nCqExdNj˘5/^+Uӡ##U&y8i=.N9i$bı1y:ܷTn/?LGFJ5g֧z 1?i$5h^Y9ͮ;8:u;B_/gu\飥 hƖR2α:QCdתmx@3Sy>Frm"%9GW$9*<+,n:'éX S&Ze⦑s,vsZF2&.1 LۺPqusdw'hFs-R\ %տ+כw[R&}6-1=$R4loF ηp|,r--CyNi$+9R½{}JCv 偒n$Gus<\>y)G`[n~` T1U m]F={jtVbC@͍ tjL7vJ]32c%,;s*kԚ鼷y]lD.xn2clK*>ס;%dd,{֭~ ]A8٨ǥNGv? b[,[K܌`m>VϓG{}<_gMwawn>r:ʸM^vce XKp ?d?Kai4=˰~c? upj r~ 6G4U~bt1}L.F=g{ zȃ)/aLϗ@8:\9n{ٙۃ|sGk1KhO1d~7 è?#??3”w4f>w(j3svkH8:zp9v lgaԓԌ[é'{:=,}a ;-60~&sg]uurc;3mcSư9Rpp`1S5 *.OVYF^)rpksg6V=SD!pb>{(}C'SrفBg)zIMg.sK{MaW*@%P<& @;cfj%!3c2w^^eMm.ɾԨ>ֻHj߿3QN}+Y~=yX%3FkyQYXdbf{tW̷s6ӆ:b~%aؑ?͛!Ng 4n`tZZzZpδ^b.8y;N>e:Lmߘ>͑tJ&hbѮs]k+RD\;4 . =2Ϣf`{֌o݈[R#m06.X4nf25ޛp1YC%-Cgȃڇ mP S`xu؝j7gzL?"6w/sT+q[ИܱǹE3^N:9Y:uuM GU2a$fR.ΠtI! \ʟ)0zl"ߩ*رvvogsP/sREg%!/h0&P(:. )gnbۡK t|Jygh!A}6>ڢ Y \X ʃKPO?7Syds!= W\"޿(=$P=h(Wgbp%bB͵;gקDҰgkpy&;yŖlKt= 5ϯmƵi`\r5decZ߹1J6jV rD!0)ގNGنVfڪQ?cQ%P} kǐx[{ܢuUu[uja/7LǕ%TE(Ze yvΓLN|tNwlVI ϣ9[KJmKn՜>ٔ5w>-NddԅaJY˸fhMJb~m@Fs`6!\"YW!~6"kB3-œ7O7oQV5ɼfcM0Y_#Os|kbic2}н2 NbXSMUH!_EN?P!*AfqVpw(QdzeKC;S:[F~G ٯ#edMGcqѵb ɫZ}iS~' MΙPfw]-- j4x;0h-x{iMohܗZΫq:ΉH`HWi}M;' gor薚* ude-drՈVh\1f?8\w 3YF,V.9u\#¸UQ TO0 5L! ]q b|;Ѫ,.wokWK՜<5w>;o/RuG=w󉋭K~$kwo /Rqj9Y݆2ڹtLVrjÎ#~\U2 5 mx ~l\OMc&TzK5Y#wqj^%j&2z+.áptd0#@IDAT SF@\nhi ⺐b/_+XQϕ T,4Vz-/y,ɗ5wdǰ ;/i؋ɨvɓ?xקSLB5 =c9^hwޠċgOtьÐs&t/nqH'KGj㏽$K*=*_3׶Ҝ,F~gjQl W;-N#QŻO>@õ~a TAy"l\ eԯ+a1xCk u, r1C'0;n]2P9~>;T^.׎i_# 6koYENO3j,eooQd~[vP|eE؛WGDtMnOڡ v}1'j!Q0';:k(=79{wb+v gՈa?6@[StŢmND ੒s4@^ա@ %uZ Oo8qع3վ.߻¯ j =dvE,rٿџL='Fht-͎:۸Ƀ;ԮJ|wP~>-m8V <^[pu{H$Fgtԏŵw/#Y8l%agi M;eؾE蝒a۪yw=S{v򰭊(wh*~7ю ꮸiSmӮ OWF+Imk /?4F;{+~0iWˇ>l4Vu.nX*:Gg 4\j|Pï2xtM 9e_]9<"w7/nF-w)9fRs8fB{n+m=Xrߵ|bb8fNO+םA髒A,%eɗFxw=Ĝ. <ڑp-a^q_!@D97擈$ҷh\iѳ~E NUIUʔ]; o.}/KDFQp9v,$?aThoD|MoX' f?Rq %{ܳX{6W #]еtX1(˧9HVjMD<Ɣ0䏃& Iv?ֳl-\<I.~#t|&JEI^ θɗO؜Sjڵw9c}~wbpb٘:J8\'O6ǗGe9uHoLMK.nG܎wM31Xc%x.hzVឣJV!J4 WM٘7]ѻ´v@iKLmvX)cڙ|Έ XDY$w2LlJ.S2[Ǚ~ǂ`p'4M}0eM &gl3Q44coga/wri嗳|姭 p|PI#=KY ՇMrp w3}f뎄4a&cȤ,o^s*!.ĵ;Oj;JyS,աg\|I%7:-ۉF&G ^zgzJ ̑>95*US\5C6dz >c W=[2x=ʐ_8zef`𤗰##?jo6ԪaPޱ9f </ kKWWu.X87qRʫCh?xi<aUXIZn=C2R~:z3e_3[psܚ%#}}3y߾~[t~}_ ubz1 zKCmm}gnezz_{ eEHHHH(ʆakjUa*} 3z00#      ̦@$@$@$@$@$@$@ zIHHHHHH2 hh(k0%      l$@$@$@$@$@$@$HHHHHHh( F^      6@$@$@$@$@$@$@ zIHHHHHH2 hh(k0%      l$@$@$@$@$@$@$HHHHHHh( F^      6@$@$@$@$@$@$@ zIHHHHHH2 hh(k0%      l$@$@$@$@$@$@$HHHHHHh( F^      6@$@$@$@$@$@$@ zIHHHHHH2 hh(k0%      l$@$@$@$@$@$@$HHHHHHh( F^      6@$@$@$@$@$@$@ zIHHHHHH2 hh(k0%      l$@$@$@$@$@$@$HHHHHHh( F% ӧO%%%8ySaGWw ԯ_װaCרQ#aP겲2ON8#HH 7WbcIX.͐H` ֭[-. UDM޸qa$S#!;X=}~=sh[^ (g ;X0ݖ2ջfx$@g)e ++Yu`]^3rgpJq7 45zӪD}55R*#ڰeF#N=S$@So̧frMR&u#h$@C9k: JݫSGqݕa7GFvMUPm)E.-;RKGUjڵ}&WwcɲfaN=ѥ/ѮN .?0V9zmø&kFё@4,k: JjOIwtv|r);syw$, ‹h{N5#PnYmUSZyVuNUTSHVMU~jHVj:i5C{p5_$bФM:v:I.#7:Xv-?܄ch&h;'cRWwy"pKyv|Wm.jWYX'^? g>n8z٭Ux-CY]@rF);X]^ԕɾUV$k{I zP&$@un,(bqο1,>ߢ{kmy^ǥrR{):?mtQ7X͐nMͰ#8fjv,Vc_ʙ{{x=#cM~DmK3\[ uM"|8LYo(~~E@w| 8KqD{7\k~x]:N*ޒm=q:)exW1ޯ>ex(P6e՞=?Y4lղ`$@5E@u؟HHhw0qn2ڷ[>ŊE2vRzQU;l> #YUQծ2͟qسo+4#W\gP( l_w^c5r>FC9 u=W B۰O#A1Tc;EnWmmKDFZ]vvVcK\W%EMjűЬNq\P6z?Ձ5d57B®qx}fЬ}\vpѥ.>/L֍+fb_& [W1Y'PX4mriyGb=Z>qcu+W=ovM.qR6x j+?ʪ~4MPF+Z3ԋH0 0P*Xli7&]tⲢ}}8,3Bl Z +Ԟl};r41HH=2TC.weO7"g>QCR2(<uJAW%f={?_K^olK0hO\9:fU9:ZFu\e3S'WVڨJ4*%.n{~|+&XF-p?}j EaW[pD*Ct(( N=Ѿ+'kabH~ Ͻd0k*Sm(dЄؾs/qRNNih@Ϣ=[[^<*!ZQ'ǫaUm.+ć]Ѿ((܍̗FeƵ~|[LZㅝ:ҎǶ/xf+ׅgvOvR9!4iw߲<Ȓ@ .A+S펽@Tf4X~?aq-WY:e;G8 ̢헢i(#K{kioLoék8YFDŽuYE92ft5gxVү1?(Vӭͩ5[:λCԩGHHv0e}UJzE{jvaUOÞxOz܏O?|=4GLb,N2ork>2&H/=~m"|,7)nj5]bS0܏οxxI6~?|-]6+LpTU(-٣F)ZZ&`<&˪+fQ;W^NFV-/ކS}mI߅O 0~aWC=^hVz6=56f`Çw)?yΟ/12N%"O,~pA?#ɱ7Ojx{nxCܦ{EX~)^ʽ7o%IL~Wܿ?D$=f~M1 WO uCUmKx.vtOxx17B}ӟt}Ѥəx{DL~][>ne/ubfR&=8*YiϜ9HNAOK.Ja$p=Uİy!-Sq3;ޑu 옣91=8\4WvIWMݗ ';GUOt#j\~(\~-_=9u.Ҟe|(݁ZO\ݥ!w֑ұHjd̯\{?QSl7H6bg_CX-_ YI9xx+e$_>Bp^oJoS-mcm/jKz8fzUU9dЌjsk1;|$b8vݲo2`izc OEl}8ꕟZ|3ɶ2!*1Mm Y}ӯB\׫oٕ|fw3w8ORr?I_Yc-m'cq^CwO㏋=. f>=,yM{/Ͽ!+;-emup KQ|3?=)lߊfs%j1Ƴ_j߽#<.qx.G-`5"ŒM˻KF>*#p_;yKk r3:}̊g ׌=#Fb|Wơ&wL}x^ |&^B<*tzVE]WW? 0Xۜmb/d- DTԅH0 %U mCmfiW%ia)#=fp7@XvUûnX>vto Ne*o9g5gK͵h BۇN5={TL3^2)eydʧ zk1NnŐ!bg/5~wusJθNY+ d: 6ҷ ?Y?%>f ~9k{ c8?!sL> #Ln}6+OOJz]e &l>c?MJC׸t <=:6B'LLNtH[縇׌dwIhԌ4n\cѠsA&':;Uc%G:4s/Ğw fX" 3c3Ckfmм{SghNUM3kG{ KX_Fvͭ24B @'PƗ-ZvH`t;jFg{LK w}}ORF,~-d;EqQmm\j+-8Gק,^UղKXxypq*=T=u=xzzzcZRAأ4uW#z=;[>=G/{)ź< _X#Cq~GO\b[áAڠi+ۨ/ZA_ދlKǞt%~$ k'~mT]Y _#2$f}=_qLOzU6gyYw׸}b"jgϜi*J';_RnzW蝈Қ2l~t(üe3uxpū8_`:іWTҏU֯_Gܚ8@Gt$@$@$PVV3QUQYw {D{.Gztb4l w~=,Nl$Z%%(gݮej,~z|Ĺ@KtjY-}έK3 JxyΙ_ ty ϱ"PϽ6W'>[A'Y+TW&[Q'_b7w)6}U8#evݮ{$ MrP]_ea52WC7Zߟ|y먧G~8)_y. ] g^N]HԵc,Jk[gjW&_yކӑ@\ŀ)HzneM⼍cO`̰-!p@>it#G f1T;C@aWѪPv+ѠyW\6mJ,u=>sߧl;+`=߶e`r}ު޲گ״O1jh%Xѝc(Rۡ1o |m)cѵgo4&,X=\VD$ xg枈.y;?mUZ8 lOPv=p˱[~%Se9CzU7ɨSZb:,5{spQOfJλzz؍fd}[8,g:z9b# oDY}"ʘڴޮɀiH PbO$@UEmgMWymgCz+{35#y\l XROPCTVmiWT^2E:#!>O+2PE\+#λqQ(< Qx| ֭]ǽC?a>":d<poSqk䣣;,+_תӥ"x4zL"5U]H R4IHH vGlT@Zdd%bl[:] 3dVlbk_GGph%}77]wYz+#Ǻ2=*W\R~EػC? ly[zhѯՁ?(r/j >R5K"{th"7H״c+ߤ#P }{?q’Kw+R`Y8{q/47> w9y# d[Oow 偊zD)@b ^ptJmle|[WY_/_'i*^Mo2l+5Xeι)WjEq.Qߋg$şvDԍHjsԼGٚDLlޕ<݅WsƄDU_[ImQiw됕/On?bd}ZP}?nL5yS)o./WGxk˷Ͽ[?S>3\MyW+=@=|9ۊxHٽ9;uEO>%?]>7^>w7iWn/ݚoo);-g<7+ozW~Y/8HMA?7|f7'?u~/M?߾W_>g~yynO{T]3_%O.?w;Ok|k_Orl;ֺ|ų~>÷V[]ˍo<|ﳾ۳8P׊qz|㏔//ˏo*?׏}M^__*|o^Yٯs\?9XƢ]wnp˯W[?}]5,׸0׃2\4^|M0M&6:7082z8? n~y_?|߲__?=ӹ Lf(C?R~-|m73Jˏ=ïY*7 <ԔK[^G7xwk??2 0>g[ @+j#{د/;|硋^wφ y-1=Y>>R~6_+}oC_S~\W׾\<ۇKُgW|9.{\y￶_}o"G}~;'嗿R(x+8Kiﺝ󀧗_x/{P?U~z3_uS>US0AdNC{=^S~g~~zg9_?7a qÿ|SS?y  w~ʿx鍳HN?5)RKyL5~=Tyڣ6pγ:GSa>,z8)]n%Wzȗ,&`&`&pu֗u*>N|G?ϼ_?\8OO|v,{Gf߷}NGsEg>r .*?z}Lu.~#T?7<{OtMS{ix|olCsh;J93-nE)_m;ry'Dy~N^;{mOWf6|im]+/xwIk/'voۺvٵ/U;=.z_P_Tp ǥq_y(y˷=?W~wSw51ziy/<ײXywW~r嗢zXgj肽܏:/.tY3X?אC_SZu5hug&Ng-=#z檉>j[~CcsZ3KyX?Sԏ=L>77\wQ.ru/B?} Yιr嗗 CZiǹ[^'?!y"?~wy{~E寇 g$泇(9pV2ן^{G;+Wr݃wVnf].+\{p +C//~u𷲁xˆYξr}(jxw-/?yutu76ԇ˧ g?sg+04awo?z7ck]go79yWڗ|?yʅ×0~^9qئ{=q|0aw{=ػxc~|3(*>-sq3V-N-uC}c>k>-94SxmǿwLLpXhd/?qñ6r_\~zzc?C="\zK;;O3/2?|~ސn/o}O_Ǖ+нY/;] y6pFç!_Zp9Z{j8k l27xo&`;A8;p /bS@wY-7<2(~8?(,p<3[[X_}{<||U+EÛwo`x W l7;c8 Y;8?|+7|kFy&Ayd&NgK2a{<pk{sx~f으PGS'`KyW͟"?|ƅ?}[GlqQ'$0;Z>8$gٿ6c>YkFy&Ayd&pϢ~O]xoV^70{&HYsϿ|y]:jg?<ZO ~}Tw'RzJÿZ ?Z'~|ïm!lȷ]Ay:?g;MaHM+ޓ VA/.9Mz8[yK4=? 69qх+v}osYC*9yk_~痻>{[O/_;{R<<[np8NSil_5 _56ʛxW'0$p/-~~g&kFy&Ayd@IDAT&;rgwxuwq촃{2^ ß+~ ^#G=.rN?-yʃc?2h !y]qWsԻz&`D`j1adX|5[&=1Ǒnzǻ\5_>Mfz2M#L`k w wwwq8<@q_xH?i?{mA-O_Snx]oO_?v&o{ʳw~f2dLL{;<(g߿~.?zk#IڄQ&|PĻ= l%K.<~M746뮻\z饇[ytȷ~{>r뭷{&8_UWާ<)w}wy\nrC4| B8,']{yN ducvHOqxY|=嶏R;&kqI_~>(LpH:W\QЇF'[rwwr&qx/ꪫ 7Pr^_sYGn)o+;]wU8|/300UH>g~k>_<|=|o(x5Wat_Wy|P{+388(zOD]s5C02o>y5>Ι9ge:82/d_|EWz,_;*_fb iz?|gˆt}хuq;N<0%Ï=ǁ颋.߀CދXe*>?=aM.zk.}w_uG i_O.JEɹL*PCmh;=c}'.nx}895P1vr(Jx׼g0 ج7ybuybn/LL` EDKMLLLLLL`Sc&`&`&`&`&`&,%z000000"NN_ |P^MLLLLLv;u;}1&`&`&`&`&`&`AyY700000)>(Ř ,Ke LLLLLL`Sc&`&`&`&`&`&,%z000000"NN_ |P^MLLLLLv;u;}1&`&`&`&`&`&`AyY700000)>(Ř ,Ke LLLLLL`Sc&`&`&`&`&`&,%z000000"NN_ |P^MLLLLLv;u;}1&`&`&`&`&`&`AyY700000)>(Ř ,Ke LLLLLL`Sc&`&`&`&`&`&,%z000000"NN_ |P^MLLLLLv;u;}1&`&`&`&`&`&`AyY700000)>(Ř ,Ke LLLLLL`Sc&`&`&`&`&`&,%z000000"pN]ɻ{. f9?6kgM7QE.쩎zj:Zb&`&`&`&`&|~YZXc"j;;ӯjLLLLL`|c{ԍsʝ*2 1hkYmiiLLLLL` ?~ԮHu=)jZ˼D{9A~ULLLLLLMA]>A_pz^wL<26{o ˮGs=o  g{*y^mrQ{\sg}hrlAhqoh5z900000{&| 2FM{֣ٔ6gzz-_o7|Xdî10000XL>t:5k ldoozm9(L'Wx_~x7I&`&`&`&`&`"gq<>;| ,u=ݣY>GoAyB v텸phjuK_?7y0000038Ň |G9>s7u~2F6/U`~[3GEoz^v.MLLLL`9xƳ7,>tLGG}Ƨϲ8g|Q~|}S޻N #ac=9s @@ q\+`F-iL}k|m5J|<}Sԇp ם>}󇁍y ,N[nn~_WO}y=CM-mc9j5YNctŘ˗5ZoAơ~6g 69c2F =e\cc{-׆:'}k]kc1 Yr/xU |UF-i30008KPj~E=s\e[qqkXz2X8,>04Y1Z,9,Fm4C#E+9(O ,֋7z5퇺V/=Q} C8G Zge5}̹.|؇ fspKSm3ֲ>u:|klVzbڋzYm[mA7(?|bMk`?9^\=="daOcOuS=z/ۚ ?ڳvu[aG\boXͩgsU [E{zU&\C7`CEz Ok55V{_=5:0000YEϳG3zzA/҃{j~>ScYs.vXn]}blN6Yn<(wo j[f42:Y~_o|Pƍzf]oXsQ-X[K{&ӎaX]e]ۚ L|w9}=zzi~J}J]\O;A7zA/)5xL 7簵j‡~l=0|nWўJGMLLL@/t^Wq?WccP4^A c\{\kHjvnAWu͈=1Z}aQ?БϨae>Ra]:XC;kYy[uGyPnnd$@+ZXEC=P16ckku움 gy3F Q9c׆EVj[^-qϘZ1;/ne@]֏7vL?EGm>sִq9بgUWa2D}7{o&`&`&`&]Zv\U5sɌ!Μgh3Ƣldm֋1XfmXNk߶2nԃ$\Vs=l]ZyE mc ujA-eaڱTkLLLL`w3i3ֲ|.E~@GU֋LØjSC׫eM~koAy1c0޼euڧn-zٞ'e lVxyژ9&`&`&`&`Ns:C5<,?q'l4 l&U>˱-ۭAjZԲ>19qrRyYߨ|պFoXwm:(/7b`֣ڼYVU˺li}#E bS᷻۰kq&`&`&`&`K ><ϟ3Qa:1D?Ï9/sQøa 6״jLLLL`cNm4yV:3-bFK\xw+7ގGuu9譃[c=xc:8D>f Z?sTC52{ymMLLL@ٮ+`~ܧb>1kQ-uja^Z}uLLLL` 9c|\-}"}ZjhY`Fs1sy4NyioZkꡣ[i7 SLj[1GotG,q 3cF󪼮c]z8W5=Z~AYZ5w #cNIիANqSVu5_ǔ\61\s;kyjZծKkԃrEzuuzbslO5}R\r׆ ў. ܪ̺}0000"glW1sZ?j`3u"~MG ,4|+G[żZ3C>՚W׳k6L"г֯^-j4c1֩@={AÜ4?`=5m}hYǾͤdk&`&`&`E~UŜYQyfcL|~>Um3WgYskVLsiѣ6?kqZ=bWoکv&Cy CSh3]-⩹Lb 1t8jYZ/hceW ]O?B}000nĘg쪩oYPO39-u"QrZC,FͣqΩUK=荱.ӏZ5iͱxP^476c Q=ΙbȡchYb4S-likZt? 5j h=>?fcsNcsμZ"s:4851cֵjkVXs"vP CUtY.w+<P b詁cdLgR-tOa>-]3_4 ٚ =e;g,ZB\s-uj՜sdqS K=cg=@:5mMzP1v-pug^c8obkjA|Ŝ1hiL!uV{Q_uSLLLL`sIg*|jcNa\m-}F_u=Gk5rNiهjn+M8(f@=Zc5j{}.汎j:Αk>Ɛc-Y=K-:_m&`&`&`&ֳb<5]qV}1~:iS[eq0b<ǩF5z0OcV-r֏oAgU;r _{f޴o\܇Ƣϵ'02 sQ8Qc8{f0> h='Ɯk>1YƢq8԰X: ձGl,g1Uc-Y^16fFu5G sg0aMOV9}Z\lڨamf~nb|Pt2|-qub,{4Z}1pm#ZaUCy$X^]N-GmMLLL@9/W^5O r8mXjc\9Г1O}kJMkl)y@E-r84ڬ&֨^s>ƴ/tyXL0?g1MLLLN&}.b.Ωg|B@sZ,ƳX,G c9g6zεcy0?5κqP&@jV>RqSkk >sU~:9-Ïs柹Wi_g kLLLL@2WxͪN5CsO>XiًZy,_AmjKŏ㠼Ԇ+ŀJxMC5G^5uq ZFA<ZVN{70008ZϢ1$xͪN59}F=u- r\ǵk:1P=bcLSVm:(f;2}M֎x918G<;}ugZƦ@!7cZ􎨝::fl~koA51ޚg9b_1tҖ:#CA\'9f&`&`&`&`$P{E><`>ZiN}]q~dqƨ>Sj\?3ǽPl,g~Pƍ!#_ƞ:_=E 1GԎ{:&`&`&`&`G@Ŝgcj^sC^]v{{"* U +*8tP)^N1}t,1l#{{x9 3Cmͮkۮ5000L >\}2b,ZkN}htG1YmrמGǩcNַtuq^:OճnvTvpzhn,7ۣSM#=Ryx=kIk1Ge*-BO}LLLL`w+Ji\Zȵ򪅏sckH>EEbWGM?(/ ೃ85>-bC29}թ_ӌձGUY8֗뭏u ڳ\|1WkN}t~6g| ӟg>#AfOK5^{.[5ՃM v\CS#UQ{V}\움 *𹵧jk>0WkN}xh}XLLa zjէFcu4W=V.jwf~eabMOG{9\9y|jϡ>cj?K+u/l}1i2=cczlMLLL6樋=5N9\s166͆jn&D泘G:9:ػεjf+֣[GJ8(d&8GYqNiC!=PYbq~qZq`co͍\LH^E~"}]c&`&`&`&9/*t>⚣K53Yjj,1׾DW5Ü#1G5mr 2nLʱ'olժR5W5q0Z^ScF+ĽĖ\{elכ f5E˾sbY5-M+yP&u;bzt?|ƴ&19,kV,q=eoΣɣk-3>5{c&`&`&`&`GG,@5uѲR9ѧeӪ1XisժF}j@v>TZ:Ƴsuq6'Hs,2N[Ad -Gc>Ωg<,b51}TV움 ڳ\#ժO-c84Ե,sk8sf11z-Yӊϊc,Ik4sYZ,-)`{)}Zs9^{ѓ?k}-GMr 4d1Xbv>;s5G\}֯Zثg-k]gLLLL`[u9-iri>c,rA ;WcVfznMe$=|\}!1f1jauCjgrcVkuz-{Woך90000$✻fZ樉V\0NjYqv=}E~q5~S;WYgQ~ƹ&=b\kspqcXk ~&j=70008gl7UZh[qhTW4he\m+G55:h0Èv=3N]&֪6nԃ2n|=Q5}\@UӴjk=8l\dcsϪEy{uۚ lܧ֜pN:5Fec_a{|ѲNk&)Z ͹ZSWO6Q |jF-Y\ciY8uj>bf,հ_ט]jZ: ;48emMLLL6@Y.۩jէ68>-c9=9jT_41ǹZVs#y 9-qqsZժ|=='kQ)k렖:ڽVe4shy"=#z8G?ճ9us85000Nwޛ\{eVcy ҿg/ɹZ&;f&`&`&`&\sƸΩE[ӲQ,,: jςyŲc[ͳ^sՋ J?4$<{1i4F}t~3G<,ca=ҟabMSV:Ͼy졵ևujU^3yQ:jTgLLLLԞV}jmU6ڗ2hTK?ZXm-D_|(bv",GYmO,jќƸ/q_ȱ1ZhϴڟG3Y]G\0:WoYM\7cmMLLL>bqΫqiU9׬ŵoԢc/8iz19,{U֞S[#5{~Z{l&p1c6O=♏쁱ÜP'G?i8qaKcYhLmOn&{6 p<8kcsiρƩ׽^g-|8G߄}j`k s\טc:? >Cfגe31ZԍêXyԷY/0XG9U=cjghUŘלo2a.zXB}Vl i2b5X?ⱎeoq22F9kmAh04Xc991[0000"P{vY8<5j,Ӣwg5^kgϼNs aNj4/Siq e՞qXS?jsa8XCyd=V!Vsk>r\cS-r\P}TG519rnk&`&`&`&]ZsY1Z^k>PGxg1Q2cms=1aQ~SW=4NCgyf9gycV573csccYO8yUSk84ScjZvP]$E cZ,  ka1Tx6Gl`oZb[S`8E/8uX,ϸIud4;1GNuj}ڗk3\gZ\m-o&`&`&`&9jrSv{ŞsN˽rVV=tqN=-^F4NVSqrj#k,ފ\mql^gZ/c:=q1XO6[9X0z1\6=#l&`&`&`& Z,cӂ}X\f[y^uu1v,<,\q1T|L\->α2^/Qx{FcVC}<2:Gc7000 glw5m1h>-trӇsX~x׹u3c:qN].R귲& \&c0c8aס:Qs֞>71K#AZij~؇u\cg bLLLL`\Ź^{~qЩ6:gsYG:gL-|G矙reZeکz] U] C]7p7jc=zWb?c"5b}XG-Ekb`h>ssŁX\zn&`&`&`&=sS|ժOcZV<ˡVG\ೆ3Yq_Q8-Z08}X }XcΡqbNm-o&`&`&`&9jrSvX㭹z|hG,3m՘@Lcs eqa94c:{lSse^O-j,-ZZlx<ڸ\#ZƸ-Qq^xs l/aiJN c19-u"cje=cqa1XKy-N,5YNcZc m;[;LrhsE=-^'ћ=؏zYGxE.47z̹ku:4X5000gl5moΡbOjZgM:(/rcM 0V'58㴈Ïkf9j{4Y?հwBT9\ע9cQ9&`&`&`&`O<7%i0G3Gi3=sa >iYC8mF>cX1[cZljΣEMye,ڨaȳ1؇cVksD}S|ssD&`&`&`&`C@v58:iΩCg꨷5000|.:S뾨e85YcĩU>{"9<deۤ_Z;rSL_2N}ZZNY/k5>X1~#sZbƧٚS{Xo&`&`&`&^<zeqչJB-ku?ΩQWľS?t>ץ8ը& Ԫb[5cYz'ԟQMxò|m&8=㺦5q02mu ?AL5Նb/U>:IJ~Y10000#0|WJ jtg:Fe\jGk8WZwaTģ&yų|qŨ:G˼9ΣeU98{4Sr=c~$K ;xijW[UQ\̫'i| hß5kEh~KSmOX vSҴr\;ĘgZhcsZev,ǼjYe޺VX.[3qMe7{Q^ꏭZ^X8؃yij:C}4BIDATiأNbMZ5򎛀 l/ֳ^+W⬦'ij>Fc69}\uzꢆSzv=@u 51jY>aCc뵎iYH kǬ4{zn&`&`&`&;pѫ1%u>^3-󰭜&٣#oU~F9R:oҵØfk͙zgoըZ51tb==ku}0000j8j,Gm\}5Ўnlh->j{4帿޵wy] xP>f6j4mx jjz;V{lG2000'ȱ^|-wb,εԇ&Y8-㴌G<,sѯi謹eOKUm<(FiZXۃƳ5k}Nɣz1kPWxVu|3000||6\fc=YMoY>4ӢN}_fO+7EC؍ksӴS϶񩧕8}m`u~VSf1{=kXc&`&`&`&zcz_5իVfy~t1d^~|56tʳܧ;CN=LccoVA=^|A5{G{g?a^s58#tMgjcVW=LLLLϚojzcq&cXzڨszlomg#l;ʼ5} 6ڏz-]-qyT֌E-Y,gjhzLLLLvC-)jxcڨs`5~P{1}[icfMk^q=1]+sqNhڨ)Usj\{.[kLLLLՄ˪K8W}̍Y:ųy[kPs|G{waljzu.##sf~M_X:[0000Xk{8u1?6zժ\yoꩃݨK֋Qk YlW98F/ͻP˾qN-m뜚leza[{Yzi&`&`&`&}gW_#欣Gac.Uc~|l)UzM" )F21~&ܘ]vѺ8o&`&`&`&=d-ZV$3Mo=}d=2Z֌c?lOC-.qmS[=1M9y-ƹjX󖾕~\c&`&`&`&p4&&lջ˖1k\[)A~(s6el~qZl1={g,-[Śc랺Z"q۲{=}<7000 _{hkSdYN5C}ըߣW7U;6]?(nN.Y&n'ehmUi rpk]mOf,S7YW߸{T>9 L9j{tQܟ .i˳gz[0000X%ܘn,9bQ{ycޣ~l!ޚwI3>\w&kKjRG{/R{xn&`&`&`&z{JG=gXy6hձT=6%o08'˟rH[6ŸW:ִ{>S={LLLL` L=MrY^}bq k֥eCvMƷLG=LjMѲXX}~\c&`&`&`&^X|ޔL:=L.S3ݶ2/xkʸ%˳iz5]-^ThO&`&`&`&`&z=ڵMknuSm[ɸ>(.`8-'OYvJXj5움 *L9NѶHZM-.-ךҟ5vɸ?(.x˳i˜rikm,S[뿊Q۟&`&`&`&`G`C客G$LZm-|v=לfvɸ8(.d >-0hVM+1ck0kqM[0000'mbVV)5Sޢu=vᐌٙbVsX§CT=4V3zY7u}MLLLL`>MˇvԚzhNɇ[<٩2p^^n>-Rt*vޕh1OLLLLL`lc/RH hݡʻzQ;yPnay =_Gn'K7a70000XnW΢=^ݿ]<$v;NvH ׳50000M%Aqhug\îy;Pƅt@Eȃ2.~ .v݇u_]n&`&`&`&`E`VU}xɸ{P_ӁyƖk(Z00000X!U^[>d8}vl]s]}uc&alΛ <k9@Nĸ=o9d_IBe*>XZ{^Ż v Rnáuxzb&`&`&`&p"ɡsIGG):>C}'Z7000]'pd y,w(/ήZ!l>HgT30000>~ζqFe}# 6ȃP-Ayy:lyu؇maLLLL`ep(|P^JNyLLLLv7}P>{P݁U4N xsoʛ{oFw(" LLLLL |l^nv7xw&`&`&`&`IoA }o/LLLNvO͞x>(Ofz p^n&`&`&`JM3's_>(6000008wLLLLLLN$OmE \# $h000000kd7000008|P>m&`&`&`&`&`&P#r&`&`&`&`&`&`''MLLLLLj|PqLLLLLLDADv_ @52 H>(600000AFq000000Iy}&`&`&`&`&`&`5>(8n&`&`&`&`&`&p" QDIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/aspect_square.png0000644000175000017500000072314513455362716022406 0ustar noahfxnoahfxPNG  IHDRdk iCCPICC ProfileH wTƫ{r"͐ӐsNHIdQ8 1! ("",\]"Ae] kwyo__ߺ:?Hd& IӃCB ԁ.̵x}ULBQ, Gd8 ܀rz wE!_e:[5k9~2L& QXlD芰>'2p֬f$k'&n_VodF|d2x}H%2cl27lR2wa;7ˎI!_:Jaj 꾭wm? b)XjXh2g]qbSK[Cv4r@ 9:K` ; l,lp Ԃpn`'KރepB<iA O !6āRlhTCP5]A!4 AoO0 &4XV`l{ 'Yp.|.k3p;| |%(J A1P(oT(*CBJQ5TuGͣ>h*A[]h: ].G7ѽI+ha,0n` Ôb0m4=êaͰlv{ۊa8Ny㘸\ *n7'xg|(W2AB x" "iBaL&8^bx8N|K"I$_R,itt4IH!kaT!r=BRl)!J#:eA*+&)[B]`X APENp``{B!U!!. BcBTaaoDB&³"8U'H\Z"STU@eQQOSoPiX͍G+ DEDEE3D+D/Pbbnb bEbF>ˊۉGo_ȗh$It<"!T -)+.uJԼ4MR%/}^ ,)'CV_fQVNE+{B켜\\9y||UtQ=^F/((**T+ (,+)((*>U"*1Jz啽T*1*UTTTTvΪIe5Smԓkh`5'55aM {ZVI!m6GF{LcӬ3+멛ۡJOY/T^W}O D r j + Qvu622>eЄjer䋩)ϴtL,ܬlAc0 1/0H8o񧥎ee QNoRbZU[?Xml656ll#mlg4ؽ׷ٷ/9X8tvD98;888;M8+:\L\vtb\=\ɺwz=6y{ʞ)u-}W_КO Oszm>qIIǒxd(yKrg YOLNH~!C8џy0s&9= {'wճ[iw=.{5G?8ݾ}]{roe{J??xGƏuRuu_9F&f9yLؙg;[tZ[Z ΁s^y=Z~Vږg/tt;C:._jEK *.^.B{ejnn5m=O_;pƭ7]eum0t5oɯmuv m2l3|8240p,l0죄G=^~g3TiDoM''mzd5?O>}J cyuMk-Zz5_MFYOւCFF "#dbe*|9\:tsֽݿ G/[cɮ pHYs%%IR$iTXtXML:com.adobe.xmp 922 778 q9I@IDATx \U$a 3("*"48"`+8-OEpvQȐ)ȠLa&sn=UVn~}Z{[^{SuTxb+0\ W`p+0\ WE:[rxp+0\ W`p+PUc嫼dɒW,SGGG  rer .eĕKqScmU_-c ~WlKG.U+WKV vFGvdˁ,~ȍYV\Kmbss+ Ŷ_r*oR8'dA9 18п8dŠ7rӘ88lK{OcGO1˼]o> N,9L6mHN muP]F2ߪ> W,!q`6ݺy'|?6 %)ʾkN˶}qW Y]Y e} 9gkkACrr0i&C^_0v-CIз5wK6%wll1r02VU<%o6Cŕ1Knd^n;[nօYOuv; 8Í<r%C:P+kkݨ$/Ŷ:ldGoX\kf0kC1982>R3n5:g.y–Hx1w]+ph?yJCu85 gqB/CnS?N(K ^ ⚏Z6ʸ`v`yiO,:bĕǂ;ҦW6Eҏ>:.$㊇KN}ξrL 9.R&mp`]?i}啲xY+ƟK+'0^22m\;qR}cÕn3r?"8ecٮm q /F\q,Ȍoc(mJOkL_!Ӛ3L2xTԷ{1X]>Sb*:DR܁8 P_"tX}28T~˘ږz%nU3G?ȍ+Gv0bSC/b냶rc=쐙#>g~'gJLee.Y#έ6Yh+/.9r!崑YR}LAl;.=v ?lఖ԰C^˚K|UǸr}wІ 'rep1KR.>h+GV!k(;dOٶ_1WŃi:6aEAW׿ט~K|еc\?AdXJ2v,C$qš'Iz+s/ǎ/׾cϲwT|_iGgC/|;HLr"GܚG1aF?rJj#rXз9aW}S}黬 5Nq5DnstO2q=hvcX0O[ +צoi>al[ckfݨ+5+]5Jy9/s>灾2xn}~cSgWDf{x^Sj#هW5/+5nG/ \NT Q٧ቹ Rb'V}Rr!ix(1*kחJ_pnOsN=2^ϼĔ>Jv#זxhC s_ A[);Qڣw?9~їcor}!Y9xjо&Nu O{<}ևook\1q^?'~ :72eƗko8v>mlPxюƟSfY׆>GI])ǿӺXKw0q8{xjB\Ժ.k#S.=@W|Жyd^IC\Pn\׋rq譃~[_e,bemm{xւk5p\?A&!:cOU/#.Ju%VgpnRlCGҟ/lSWWW!j h׎`G[%;.dApE.<%|iK*k`۱A=1ȝ O-8sv\9X}8b_d:zQK8J}% Ko=w j3[Qxrk~s?N{0Z@suʽ޽IQ׎W>:N0b-FmArk+΢cdN vƠ Aic<8:px帐׷8T'0%ic|☓1Q=m 8ʴÏ2p-/A[y)/}r9r>5#2l+'N; ҧ2'ġWP+ׇWxlZ _6p i Wv)WV[m~@_lJ·:xykfxut-í#5S;eeYsl1oR1hk#/e7?U.Gc^~4/eki'|At\\8tE^\!>̇:+Gg%ZpoocXH[:Krx[;狺zd݇6ʱܵ c|q`ieJ[ֆ}q6R_pXr}0UkGde$qruЁ+s(|Nrtl޼y&T:́U`iƍm3}ϾT,\2&1nXs1mMc-v]vx]sRỐ򁾱_}jE[lAhG[m{^7jS6%wrt~R9.f#1 +LJ? GYOhG_z.gj!Q;Q΁Xצxm}.:lGrNi6$\X?]׈M:\kp<)}fz[Rz [L|9X91wL>/m6>w]w]sU17FutEOwGD7w@,e==1y]cjbp &G.N3?ݖ͛#bTOՙ9-Xb&;Ƕ/q>8#`G s7ƍ:=y0;;}ѷ"6-[]Ǟ;1vy~|'E :'98J[s6W[GG{"bA%ru)A=\{U}s 7Ws32ĚbkG5P7&!}62Ud\՗1u+N^9u}h[1~̋u0up|961֧88Nu_ʱ! v_^Ӈ6r茣2WA]\{^Yfb;aʺ SSZE)hJC$?bY☴V[{b>ebL\}\[o '_^#k'8(<dM=:so\6'.m7OGCYSm>Xڐh O>ci븼a|PN!}7:ƭ@.@,^b3F6xl91c*c;7p뻌۱x`ћ^76x_ؚ7y`O}|#sOw.f/zubLQÏ|0|nyttָk _0E}VFlZX[?rhmEh:;Ě姫&.mr]l+vZX?7/lNiWڨ9'潔k!L[l:1>kBޛ犎qbO&L_l/КP[FLlNU;@^~2|mYLhԴ=Gl{/qΎXmMc5Fj *?2++ rH@yž>XT97p뻌t̛<ŧ>ch3/9~$YrCv_,sp-@FoCQXǬlJ+5:ЂWh ]8+1_:Gv'tZ&1f㍢{ڴ'{byss',83p$œށTzrُψg"֛8*dffŘ6_ʼŋbaļxncq]s|Kc]f}0(c[`5_˼o1aqyR[8=6q7ɣ֋DXR_׼~8>9O/=/>r UN֬i\*QAlq*l@p>d8bCۘꈫ?0b6sE;c+hSĆ'\,7kݟ㒃moW|?g)'7ax:|=뗇Wͻ!ds[o5nq9"v=5fXQbO. q]9;~|5oC=q8uDְ+m.~8Vn|8 KG/zn-hO9ѷɸYqۭ7Ƶ]EKg[cc_?yێqziTi?댋ԃydmt>Wszo0?~GLhVWnO?0^tԦUcvU6=1&/ncw٣[tObCue ɮӎz ^jٹ$?fqmXWɠ2VCԴsrǜlq]xm|˧s?ИPΎ=:)y7'wqYs//I#ߎfzU|Mq 9rLo͈} N%Θ˛㈭'45P['{QZ_xyaC<`kmwŶknϱ U7.üuA/=փ'96=|k+}t֧}=uY~:~5D<44$։qDwKۿ[Mg]gayil7]a︬~}uȴCMk^#W,vS[S] {H$u!^8:c(.wL{Olcn xIة/)m#Җ6vj]Aelkm2>}xtcN |}sx1mk86\Mgp@'ѹ<_ƴ|gu֏-"&lex^ZFg/S;/Gv[}X/2/[ 0.*Ʀ:jL6dn46]ctfiN5EWϴZ;4rٳ8FLqc ccʸ{ֻU#~h`<^=:>sc]W &;oMl3uX1{H߹cƭkǭ'N=ƍw_G#%~ÃXBzK}X֓|8.}!wc/prcw і<)Gg>K zua."+;,Foh[/0oƅCI{[9|їSW9dm_] 6¸SYŋgG,gṀ>mѱ ~zbȼE5G\pO[vGgv g 8Cc.f\xq:~أ~M.u Z{·yY7e̽:/p%oaG#wM˟#<g'=6vswuw-o:ym"4?u3A I{zK?CƔKYߋ_ywo.O}8jĘ~;e ȻsD^5ZόsvWwx>S|!]I?̼83/]l> Wk ~s>Glڳ[HwJ X?֨qOϭt>{Mqt :O10Gyw&W_|7_.Y=ۭVOܘ?_`sJ)Վu+U郯NaŻ{?kSר>_Ѧ9|~j;59na1)}ś6 }rg|gaJr@k9@ɍU6ŗ9xpKm(1PmI9?\}ɏ?rTw/Y}>}s;fF36o4ǜ+;{  5Rgdbw:V |'wY/}65 ^ X^an856r}u*{%M|67um_o\LqZLbXoz1*0-+9Z~ [q>ܴ6k#CbY{t*Gd})o˃kܓ1xx1;>_b|;͸&.Y3S͸\/ǵ]~2SH|#Ǘt_3cVZ?*lEc=:ظr̘w/~jPyz֧S8[g7^wms")hfqU( H횴BRS~|7w>\|V4gr4c>'f֬VםXcUc\+Xz8_ yYq 51~ZC:#}\ǡoڐxaG֞6 84\WtSx/{+/n_0m?tUUmgjm5q׮iG߾&-s/zrmRsm*k~3g2+V*FόSοqݏc9U?Wv[|+.|kE:<~dqtrm]j *jmhɇĸoY'6Rn mjC8q`kǟ;Cmcc1gѕ9*Gf^@+bR ԉ֔5g̨XC{2_A;Gj"/ :3SNZ3}</{>>_\UjLudCX7u,86Ѹr+W#{9K~ :|JN,kX 05G<~|ٲ'ƺoa~Y'LTu?Kz?Qz湔?G=vwf,cRr}9|҈Xg1rl~&?Zb~c'3;ە'*Lߋ?]|sG+wGY7`nwĤWǨ1#ZtE|=Tۏbt6;wmLƳ|bs`c`e&>.K.wN[u.lhўCr|w}T#rL`cGն/B9_~nZ( DvN\˞7ZQީHyy\|t\Uo<>'D*hŮZ8{n ih0&X1/.;z+u_j8--o%7*v~n-Ffw59UTG|˿7Z?O]s:E9 '1̉{o뜬H[zl>s`s9 ൕuΩr$qi]?͘6B {羪KkO+זus_pMi_[ Wz8~q*/GJ2}9"h`WwڦYKG˹,r;W@;.ho0;KB0}k#3g5'|}e<8+ujE/F7RPX-XeْXZb|%l/|xc|s)~σww;b)ձ".y'pI\y-<,cy⥩[]61:A_c;sn O-ɥ$qe6rЇC,(xy8BUA9c*s< |K|k\/ɍsg9m|:k7>c6y#:ѧ-aC;8cGUzh5C  7f Jkg.ښwa\6GIh`oL2.\qg/n?2]wmmi|'7e̹;kFZb,6p9WFߜZN`;,88:8j?p7Xn7^leKm۬;8cĆKw}"ôJJgӽsg{Cx3[k V/rle܊oWa]bW7;Z[V'*W"_K}lƶ$Rٺ([c5*{{w]F;`y@<3h>k135'T6 Wֻ%q~+1y!1XTUw\Z~wzl~0 m}}&Uυ`cfrg28}dƆ i?%UӸ- ɫOe\ȱm{yA3Oy%գo.vm]vӼ*qE=1Ub-=7֯Bu:+͛ Mʵ9?/9e-☳߮:N>[y?ـA[3yGGN,Pw~_X1ѧ 7p~]sxz-kq.aC;8]C!sK2e!{819֠<נUQɀ/=2o俵[.'ޮQ%ˢ3:Rv'3q=KQ'xAzChs,|Xw܈7qtLW~:@ߵ*VD^Cr91b:%&`|W`B͑mO.bS/=96V8.Oa|5<|'/ey KYe͏.Me qM&G;D_%_]91wxCpm5ore>؀oH_pg[x*O 9}86158OPڂF?ȵu A,zȭ)2 ΓO|% Է5:MXֆmp`lZzkgN_o{Kx|')aQQ_*NcXgՠN.(hp_X\o8yz?zag㨽֮j\_e\ѧ.}cZo`;ʨa/k7r!{ 3t@w *>1o_6n\]_ֈ߿$36czߪxGU>>rLu8qO JfW/tC]E|sġM_x9{^\o>{GMeczu>udazo>jh͜~,=c̕MwL]}}~?sc~bj{U̩sW,N6zNknM*6:šr˺6v兌xuOZ[8-ܣe.P{JjH/ ;z8 N:u!~Ί+4졭  ,/v灵5i]V'QEr >xWOKi?1o؂#oȠ=J=]1'njm467_Ik zmo)ἔr]mp]_؇g.n߱3s>ĤF՝@!A7IA>3y3Gu/e]yUY[LoG^_ ^%K8*=27$d~戾=w#7jDȋ OfWKuд?MU)jY|ߑe^׈#ojTքwܐga9= |P@IDAT6'}~KZWl| 2t-qq.cX!?O.Ҟ7WmЁޜJ1/dmc<': <'/bm};N0/kY=7M|Hpe͗2qc{8c:_Oq Oo2/\y5vwEO4_ [}Dֵs8%pgm9ǽ=~Ԓ?W}X=_%X3rt!tWl<=X/<㝳⚟?Nm-GyghlOmZ/) 1Um׾.NPաZ< ךu֩tܻ/ 4j^3ƭNT~_/Ucޥ?S6Ui}c/5y+ZqUKomOόo!5 l]p%?aAԜvbDK_ʁ3 >߿fѣw6x%~x?4b\SBEcN'y9ñ@>A\;}TG5C;kT+C-s{㛃vްɔԓ?D>[Bysmjw֖p\M[O-xOgJL|=5z?bcƘ5|fV_xՄ5>P.*;Ϡq#d /eV|Oֆ<j_^ MZim}w~ٝs76z269GJt`\3!Ny!Snj%rF V=! d8A\8928 O|cÁkG_?6C桝5R^9 \,C vb 믴E-~_bdx@92>pܓ}tw~tt~r{X3'lms'WM?zGAGB5/FSדȏ#tg]Yq]՗޻rqL0>N\ʏ=2vڨ91W㚏 9лyh;%g,؀a,BN%92@̇8rΉ@ڧy:~s0_9X-xulB me؂ӆ;ߴmT%S_z{}6yWw'!cbρ/b?s4^䋋Ͽg<4Oq>qW"6KyRŗ1%+qٲV;7JS8 79k'#u\D߸qJ}WĪQcZu \>Bn濜m183ϮKI ֋zύj5W,i׳daWU-8mhؓVz'j1V?A䌑P[{7VF^TOyG?2O_tGƅ9#ml2c!'U=S O戟AqNL#@8hW噉Uy!ӑyG#䥃niu(DNF&+Dw2@J(ڼv|wXn黀̍b#vD.|rl˻eNZG^vfy;ʳqH~y7З7:NGf kcؒ-zԞwozH}bFf ǁ->q.}݋~cn1.81ȱ/:5}=cqOxv][?fK"_xZsޭ_ywL|zӾ;ߤipWge]oM̻84/J-~;߻:iJ:j6k`i'ŃAVզ^S#zf\w.q?qa[ kqsvrM֬Rb0k5kk6氷7]z{ݪD ?8λMk3AyҾ17 q[s?8Qj8{_esZ}(?unݕȫTZ}:ǿ9fpkac=79-_{J(=lq`ܖqa ֫󯍏VUW_<Cr~x[T;bxǾ?暫\ov||h M}g,FZ \{p z#_3H>􇜾JC㤠Lܛ_5ZEm17rrW&בor1.v;4 \[mSV+?k,ϥĘc}4j8Mu?vzbk ~X'i"Ss?bb]Ԡ}#~xq+;d-7>urky.t'Q׺QG~=9oW8<1[|62}>z"]% k ,DziƘb1c9l<`'vYrܴ?|mW?&?wA;wP{”z|x4"w-C&6 ؼ g]j!䯯3/\ HjFcءs\<>>} Gn}ѳ؛#֓2dž 9uF_K:|icMs}[ėq Wo|!#|rOG/8 p':ȨqG rp뷲Okù>d];P]J\ x*/DbOǗ|U3ޖ'd=B~>c.Z޸2g8 Nq%_:sNx\Z\~Ų\GV_*WYjL'G==yӵiO+?+;/=1'f\8xnُlWu's}O?ι8P9:%㭻nh~Wg yco?G7f KJ=eع*!vc8[|z~{=:5C?m 礞J}V^{(s=zDi= [f_}?"x}Jc#buZy͡GD\/uMo^i٭=qXyϽͺ#׾;.∯h:; Ě9bޜ3\5J{875/?g:OuRt!͋5-A r E|8cn>Y4{ayqӘk$^_iM]ZۣX[-zz^hq^YS/u]\`#cD;^E+aqȦ!GhJ?{dl9~J\5Pֱkc}sFi=&%fEpvxP:sv_z.vCXگ7)<c#q}̟/ |*E>Xǂ?8}[ֽ5w8rꙿJD2ڐJXgX>|W21ˋ.:1?:6.9'3LH",1y_Rƾ-F; ]%ظH",XucCΏUV,,r{T}񚺼4)rR_f?OB?ӗmҸug}hsX7J`$3~Ԕxu+_y183?&9vg;/+|S]zXYQk7OeȍRGs1rr2O.HbkC;0 h9wpl~yҥO>mnڂi'm8"8Ҟ te9ׯv"rEN_,2aJؖS^02q :dqp0Q*v҉1K?D˘S7КQu& [#f/xs綮=.ݦ̈o /}&%$>JU71z`n|7H|l6S/Z96Nq߉ }wrzٱ(蝹 Ⱥ?9@ZPiҷ-էnfl/]Yo9+^˼(6m_nR+S*o0:V6: 5J0oۘXyiNՂ>NA1.?9nXg&ٴKBMo57u 85θz8)8k%WXZuW<=8uW`u/6>Gqz?KSMuǬ_Nr?ֺ7-0|ڶxe>{[9÷# 7_kci#‹N}6Sa;sWxlw-c+7?=?Ԗhj^8UƯ>Nʵ5wqĘ1eik7s]\Wv-V> XOrh_[yz>FocKkU79/׍Nwm ?%՟?״mxHgXym}?6bV_ ҄hOoOe>?[㛍Ot~v7}"6]y|sX{f{>‹>{Go\9i6r_s:j.8!~[EVaPb/㚓Bf>o􇌸88zˁmO vB/ _?e`D_2[~}YbemsXX#?>OJ?b8~}3gW6w\6M0-t|căO 2qo?SeL D'8dL`,/Ej,ytIuu'<+٫2my6!t}|b'Am+yqyEQ.79T.surB<~/^2?*InGfx=!o?.|ȯv3q>h_wF-qqZ!VƷ'\yq> zm8WE\}sMwF;Qm:n)?'HA6O'&WŹ>ayA@okB545~t oIuoKbuƠ\^(ɐiLx=oC"ǃ/]O!WD94r8lבws2i|'wqE'˯ɖ*=} 8%c-X:?|nCSV/l3>3ǩ遬Aq d9N&~4!-J??C]yܧ է/y \tWV/<7ve|YAzīNm*'C0$.җ>wҹVn\:5fhQW;IRY߭k9>sf{|Q,>qx?4n~+m*=®#jД٢|"urC8_p1gu@;pF0YǦnM&e>ceG o' ~~'m yn*ܼ}C/pIC:ݶ3yDY ɜ+AEcnGHFE^|i`A;z*uA񜾉9 -{e*Diİ8y wx#z[pp?@eEWI#u:յ*x:Z]j҇`E /V$.4>_5:>MPVvtvCgc[@7mՋ{$JX-eG] hpLegBnTh+k޸ԩkp (~w٢u:#,@ϟ4Ȳ|^u9C.Z?xZak/Ack~yπ|\x@<[_g譓僇\rC=t\Ҡl 5//!qγgΗm @?.=mYŰtMpX?OGLXwu18l6.¨FV>|auq֋ωz '#CWZ -Gߌ\Hݶ+œfm}lǼ >2;dup'"*tt~J[ǝ dm\x_Bqeyܼi }.me;nw?iDAmNǡG_SVjuF&iL]Oܯ#GqW/[><,5r{*Q>+o@wNNZ򜕋a+oVyBiɯ?ʵߺҶNODu?I? .HW)}x胟T7tSNg:8};ys+G\Ccһoz Tj4n+ ߈ -^ov ;oQ~:j8K㜯#H|ה]xenC ۫nYfqi+T>1^x\L9~"!9yV[`;*JO\Ou׬HyO?iѩ?9'.rgƧ_vy\|:ؖo.s>r=iw nImWG%׏gpm>`!ۿeBt ^{>|نey#t\;B6zCt)Ql[\y׍tg }ɓa}#} ؘAH؅g1ƞQEXjՙU4s&۬ WBZr ӷݶTC Oend, C Voݏ;}Q Ǎ*M!*5 ^(BJ5iA5wu;;et1.\?AeGtmCtccGNSa]jtWZ4P "pU]Ouek=nQ#r.ct^4W:p#qmc^02уʱ-q:MK59af݈Oqʏ~/N/zm:{UqMpGcjU}FiU u2שaZ rb<:iQ6+G>S!`7tȗO[z߯\gNl8$s[?NR+ՙq_]?WtVu@?>NsBݩgwxo5xԬƕT@d.$Y)rQ7mCx-tCYf}kK>h\x\ƶS+i;)OvNpOCzխn}nKg_xԤ?C>8'}bu&b[2~=n]MH(؇ַIZsD=Grp99}N+?'~?&3/d02t99i!?؀9o66us!Cct))M=e;p}qAbB1Q$<o+6zK8ʶp٠#˩8/%j>47qrϮZ|8bIsmњ5?Q8|-.xG-ͦ#|P[Oх~8>{^儾Gk^~A\' pɇ!IBy&^А.?O_7ط_.\_wOɑ KQt?Ý #N=#ӕ9;m`ם^_3ML3+eAcR]x⇎5 Yƣ29 h,-t&G7G>.~l9 -2r3/GqqσqLr;4i(u623oߑxLo>uTw%g Nr̸M :~hr:n~V.M󘾵Iu[4K=r\N#-;أ|X.4odaB[.Gxӹ @ģgTy/\ۂm1i!r-ו{==zkm/xK|#!N[ך:rN®82G~eig~-q=~x6uMq7߬cG})ǿ|uȖ-eƠqgQ,ok'+K-xﯜU=HO[ٱ-~˟}q>_t+ tYʋ~0|?ŭ?o|}}hr_x?lMvY_cR:rVWC[uG&.ٟ/q;JqBƇ'xg?C&upN,fobB#8!8֍YpI'_!۲qyGChxalas`~ @Jg[ 4uuicf3IwF|oq5z2u4{5%FrʎYșW_]_'K7=#sjvȅGh\=k:P/K]˵]ϖ/wڙ1 vgůa|3߽sbghR=.c]οowU|cW݌b~|o8p]حOf73p>.l:>bf'jsڡ>}Ym~u}|_ƸҸm'ǹ 6~ h6,y؀g])~^+'ڿuV0ZN4wuEohg"ZFyէixߏFd:NWҤ_3~r6򀪪xgnF|?h.@ꪼA3c'@fOV_-pE[lxhfM?xYOp߼z<i]/I^J.y_yAq:|+:,J#HH4|^{ܹᇣ5.֨Gbc'ٹ3J1E//קWN @686øT~]M?y:ֆ5oN4˫SAm[Y N&ZXL뗔*녟4fݭKmk=zSl`jlyY?~\%{:wEw_O,_8f+bU~.?`\`3]s6$ IG* `Iæ(xAk> aƵ2M/C< gwLǣ3Y.xyg><`Y ;lm+v=8Z'} 0`;<C<`}pc< ȓ' ~FxyC`a\wj7<<,˶ƼzZ?p-Ӻڞ7mN ZβcD4.& u$aeڮG:g`嫦e~Oͩ~Ѧ㛞ʘ?)NcÂŋ>]ݴlwxlPSS]۟G c^eWk KWĊ һJ.+~aR\GQ\OK_gbS]r p)D6Fo/u /7ө \vUw{~vqA|=s6MmYb(7W1߮B}EGo9~nvݨH@p6\`6"[_ޚpu3uѵiKޮ.ޓi5eXċ'XL<0Z7wH.Pޠ9:5kV{$ݣޣQ۩9Q߰0n-<ؘZ>y 8uj'nZҩΛ7/ɇcckt=ޤ_CMscЗĪ%GDG&k{] *lEzz|pq2x'g焰q-e̛4gđ2% ~~OuLS%t#:A#?C&7N%oKp]qeyCmƃ@ݩy^|]ppɯZ7h aX[p0qw&?2 Ki.;盰Љ+el6./DxӅկ.;=v'H՗P-/QCSuw|FTßr"oǯ9{ sgv~?vh5cy%tʺh{W30a,7\0@IDAT(L8!`yQt'rcǎI;a'й]nAxЁ~\=wMy!ۓ~JC~&_ [7x;r鸶iNTy/q@'t' =".o u٤.@>'f!aAG&<Ʊ\p,44~L%y =A뼠7ǺZ.?N:-А =4HzЛ2w%0%wMO딧qyǃУ~2La/hMNCO\g_QXL >W9˳N ߶mWM[_+_Z?~*HN ]ou.qEnP#o~%+_jLGPy =9\9/_)bPOu>q?uۡXyNEƙ(1\@Q\W6X@5%Lo#'߱QiO\q!Qv>H8޺e">o|q::/\1?¤[xF<'zp?/4<ʃsA6qփ||8p:8mo N'.+t?z&˺[>8NC_\Ҭ~+x9xO-].O: ?饞nOzـEXu__N[V״e~ɚR)16̰v–<?ۏ6zx^dlKxB}(G+F:"c|xOYE/!-]mmڶʗ8_+k~3);d6@'^rPV؇>ꫯ{Y@2bN8&#H' 4PäCםy#:" @iТI8, `[t%b~\tu p͛4?i|L%s9}*^ۓx/[;ϖy?ƁrLK?@x*-||kMӡ# _$홟!y~j^AO3mW[R[bC:?G?-ɬ `(*)1x&5 <^@ lûA z}?\9 9 ¶8qq<.K͓0x68 mEr#}iyzJ~\G<O p-/E7>~˱<֝SXllxÏl%P3o\mMKq>Ȇ.|5\x:_yݡu`Zn||;u @Rgp @\\hy>~?k ?gC/-hYamagCl;۫'=l嶈-mGڦK)n뱃0@Cx8&(Ĝ[mvE?ʤ? .R9D'l#veL\'y! /~I;X6 =/%>K\<`>[h5-@\0MBxZG+x7 \dX7dcZY7k@͉˃:ߖM>8? >,Bc!X6x4qL2Zt®Wia~gY.wmG✆|x4¤, ܼ| \]Xy?L ࣋E[a# ^)uNvp@p >t\_B66F{=?QWp#&z~hqЃK\h >~yL– ;/_ZbbcaT\4::xHCxpch荇 O\?I' ߕy-tię?r7{ is4 Z4xO:X6:R9o[X?|sy1On)?[otx;Oqny.R+~ m~ÇzOIBpy1eHysxq{qMk| 'y7t<@aLKزiq<1|!lztxChEw,=R'\ wH4ܿ0}>%/.\u#' ~Oy}#.i@qaz80tC7˴NOg".|L8 ygNthΖ!  σYЃ ?x OMOL C})$W9>'}Ʈe;ώ낼<-<@ ̏rN.0|p/rGm ZtƎiC:it 2-=1No; ~%II<#,hЁxB_4NE/OA?>>"g98;ĹoFCDD:oH#.~h^<?xaҊn^ؘ0\_mv֨+QO7]w:8&vH:?17_a0h<08ՆщshzƷ|O3'/;Rpv4~K|~* y:O`' -yࢳIsH#L~sDә\ЃEH>ȰpxdIpg9~k^}j \[W٧F\2Mlgh]h8N -rMN1/5-.ە!C.qa^0':@GZ5_hMk"oz;|o?4ŵv?egx:O`ؖE^&<61\t6>iiIÏO/ȗvmm\zDYmLS >ø'~@:B(1/p_E~y,(bP?\W<8:(T(0+&ScZ ikz1x8ZynD:/.<|&E7h=wN#q=/~xKt].Byr<.24\F%\G.xI# 8-h|A-A|o v7_q8Jy0/ 0o\:8qm/.茋>G^atn=#aΖp |3' x86扛%=OCadkǡ+zDzSbs: Oہ4ohYO؊8lH# P_hn\K .8+f F[ڎC c[} #~t<āWr)NPϨ?/'f{.-,PX@a (,PX@ad֊L(,PX@a (,PXT )… (,PX@a x[X Za (,PX@aZ rȩE@a (,PX@a~-p}q8E@a (,PX@a/l L: lkkm\>?Owlߞ>S<Ŋ.MF E 5А9`A(@a/$ MӸQ吃(&|Ϳ1VSfS)LjI;j2UCm:M -?{ʦw`0O}P{Dkc]FEq WTg7;qn ,whv>4O ٙ~7ΝP`(,PX b"q{`!ӓuT~h|ንɭi_oGG\/DΝǣ)b…hw( tWi Mo~S F~Um-:;e9EJ-WUcΝ8WG[+InTG}MurQ4DkC}5t h'OOi$GcgO_ ꔩTJ'MiV}]JMr^?P|xvr녖}W'xGwES]m5'Ƽ_PtBPmVu`QQ5ܗY}cMy մrJ(5vǼqyfXl<$3 Yئ|K?_bѥָ}yE!2(x(,0xlF*@_Fт.f뤏a-{E]),_ hqʼn z vvtEHTDCOGTG`Z ڕZhP B5ŃϢe`*hQkCmM,CѬ ~e`T'd2 k;,K,ƎN-UNS*Z JcA-ki;}<3An^~.\?(GۓۣM 5>--QMrѡґۛ4о(w&6y*x.,0iHC~m/J]qآj-c6FuzojG61Um̮iY9gϊS/~W.Z WξR|cggOlyrI-ȫnѾ0GiR$&ٓ U`LX-[k~$i_:&xCZwGj2=clx0FQ8;ͨjUͳI8GIYƒ%D*3MaLV/ ǝlgm$SQc@8 r봮8|~8%16VƄֶ'Zn@TWWϘD+M~`z'N7BI7y5`t$;O{ᄩ ]R*l\6 WF4+={v\xᅱP7֭[ _!}mhhH=(G}4lv'}>0ґzgT ͌kbPpQZ!]2b8]*+&NmϾBOY<;C[{(z=ñw bn."cs;Akוк-+ci`_5k0몢 YQ>aMWץ1 3\J DO/e )o\dlH&=by#*c`D%_,pGt›e3[ k),>[eFYOXpt6Wѧ 66=Kv|Q]j*JQ [KydѮ+[nM;v2vR9WQ"=E9uxqvPw\g[}ح7m}2@+;OVP(ʋ1 ּ+~M)GN`h攕+b~sS#Tcw -? _|_lI`؁دI5< Ne cXYԙU9N,b-J_ۥb~TTGͬU:Mfͪ}6'C5v]gaqϽwӈ-Zb% >Mswˣ!VX> 's䱤 k1H=PKXp(ElXS^֤El7PxN*/ŞC/p -]RT='bm`x:/#FG:>Uљqr]c@76%I=/Y(h@ j^uJN~ Mr`C3_:1/*r#rw4)xQ^+o{oWXP[`"P :y,t4Aҵڣc8wGbi<}T? t.#01hQI17|Q;oΌ"]H:!&z0˰Nڗ xi'&M&J m\&C'u+ S3 D͠+wD:ؑG7OG.z&iS4mN񾷧;X/[ g>&oGϒaDU8ɈRYkUdt!4 GyYiЕ=]ңe:}$覩^ѧcʏ&w'?ǔM$ E%?1>-}Ȉamִ-w߫wId9< D/f|8kLx PPuNN;J 棵U%qV"} fk{jdTC 'fģ:84j)9o&L&vœ۷%<5Л~lϞR |5;g4.0Az$UN2:Jٵ4džc7;N({I6n0/ƅx&\OS>ԆOIJmteїœcQdA 䦛; kIav⧓@3:\hdϔyb\[x"h㷩64MuAƠL`/9-$NW4?w^wkcD7ʽ_>;[giUW]O>d:1ߚ߽LyBw?}04G)*zS_1<:DWӳE`pMc|=hE0B^K?N0(pշ 5hgW:ܞDк&#&g^C>Fuv`Ǝ7;ǥf:k2r5-ۀS:yau ÈNRzL3I Bw`?f'x;q^"r4]`tTTЌCV Z+'Zh2QK_]夷ND>v~$;Gڧp){pwt~g]Ug: ~w9;ّ|kQ<ru6ȄM ӑy*,|&1͓_fLU-tr&DN3s]QNtl[_ihMPӠM}RfҺrwyQ J7L Vsf5fUҖiuW];oܟL w8OqC[=< kz(9jOgvXR_4hI,݂YZvkW ;4b7=:66;g;{?Tл*z}E`GŒ>Vé߮=?r}XoHnөRa«R?5f$7ʘO(,=k "O5{ R>0~ya@w0j}L4pp\ ӱFTE#8;^eXrAj;vjA-:4f0Vu(>\aǼb}KVI]fiSO&lGzN˘̧&ãeEz&>#d[?;;Ka-}`,Z8K=avB(u]ƂJbL_q–5 S*tw3bu^NM~u̇JGacZ GmU׃cY* &k{λ7oO'PQ|tN^_va1YG3]˥ P*wvخNm@[^kڏ&ZZZjAPV; ~aYu\SMcr:Ϧ?rvnqMw(~$f\ Ϗ&MݵaQ1ڦZ?VlF~Y;]W}m~'t=Ԝ!Oi蚎Y'Dpսh]8*2MB_3!\pj,;U5ZMZTFm{L:Inv=ӳsXuw>6?9fŁ^L,3q>OrhsaI6y׏ YI6Pt$68)Ia6~Xw$x̮r ω <$WSyNM{vŐm05:h~'FE؂:_N/I{ސ~ ?C>mӗrkOO -St෿ئ^ve܏=شki/6-/E?sҷBۡoTc]}`Ls{ :űTa:UߟƞЧ5}݄2PQX`YIԘ0sTBՌջ#tBFhSeHjٙidž@TyWPcHP'Duiw0dLq6$}{GҁڋJ,G&:v?-LrN}CXр a}aO w zviAKy%M<$EuV\';_3 4YbV,بWaZܖ'IdW$:`ܞ^wC8,`(:c:;;׵jjuoTۛNsc{W~wm&B^Pd dl[^9cs$P}ZWS;Edu;CԴ4ޓ>D.8/vƮF>~^2?5Z=QFά+L6`GII]<bd',pK@8^Ƅ7u|| r(3| /1 6@|sȉ^zmEkɟvttH«hҗcjیE}s?+"U;#Q;Q&zǴL1+&ͪi4\ࡇJ;eI_ǐ&7fNO7cQluc-phK^蒨vD[RPN_֮TK' DԋޕNzpr1j;)Bب(OueUf]ʀ6)& ӓd24&Y0,ib}属)A [}Da+KӄEvg8?ʾW!ԉ.ЉNe47ӵRvҫ-3&UuzGp(nٸ9kr̲bXwpw @~@~ynb~Zs*X/>m~~~cNfiyum(nWymH>ĊAv򷸵)N>|yѻM5Sttt3.v-mmOyuz7fN-CN0tn}T?p?^{y:Up1w6]ܨ@ÆiaQSI -;ԫEW~p]Q3E:ѵni\ݸI*O˖KI;lSKV]mևk[S߮mkԔhOG iWw/9.t롏j{C+LLwOjݺEQsx`4e!\B#hQuڑ+QuG)Jnlk>gʣwRM*hzV?'Fl^wLMKrR78}b[vTņ3o} Sl4BU÷9c^u,YK+RO,Mi#M1MKtrVQ[>Ĝx8z3;؟|pȉxӢgAW\1&s]7*twi|l>uZL_qC~7xc2'oMy'?$7ӵ^wl_Uc /?5ƴk{61RzMq]~fIRT iS=gVԶD~A@MiEUˈPzFdD-; A<OH{gQsx%o )Ex9pb(2Ӡ߸$SJԧW_CJ=KP5 ٚE + r J5W zRU9DlZhn\wy*xK^{`:iԂwdDLpsʏl٠Y{xٓz )\d9ɲ6))uHq;M*q%3Q [7j>w@kܮ͛;wFA-@TC>vު09$_}B-!+`; wQ"M*+ޛ>u\YW"QFvu[dv=i"f>'G{Z3^:dɒݒmyL MAA[a}o~/+^nݛ̓9]0Q60vW¬'*]-!1pMzx.%(-=!T$;_ˈUƍ":V5lwk*-~,+ѫ.^߉J؝|1gJ]@.$g-QG}=oZNT摨ݐ+ny$vD?;J0%DZ*z޻6|G1xw݃0asU%#Ƌށ!uӫ_ L>$t%PЋSB\ ē1*Q(\Y_‘D (64 K56Xh;O ~x7BvdS炛8shxxp] s5m(lƣtI}])xNސ>:綻ө>]3&L0'7{Y_E8uG2Z@*&04$wZ7ST_],8S)gd\yd+(?m aynlmߞ֒L un,%|ՁPy[+ne6+ke,ssμ0a `d#.S-G-@dKH.N}j&?'N >/]3ܔA&ϲJ۾>򓡏](aE)&e& ߻$oߋ6I"WAg B _?CJy&0 WPB)ЯW]R6e%|<* }L b!!Ꭼߡbx50|Co ӯm׆I*f1PBԮ@j]x==60.#c=2*x{P]w"~{`CDhDy17,ղI֟cՃM%Ɖ VӘ-e}vcEĔ-Za'( Ӝp ̷P{"c!ɨv%Kתm s0_DBӑm¡ Ruo0W/g E wރ{ mHmK2,7>$EI4hr1{7Gx[t*t\ dS0akƦ.G:ؐa|^e+@|*YtQnZlw%tzPl?B`Cr_h{u88mo~`"|gHe(?B?JU0|Nc^.XU$Ad42 .-2 @IDAT50nxGB7*}"tɍȴM> ϝK/$gdτ}O?Nxid8?zagM$u[4Lh!|;Oرel_v0?%΋V4 fLP6_zUBgܿzI`&*0,΅Pn:hIy!tsŵwm4lTUW˜kc0}ĕ6mJ[ŏ- Fd[ *2~_zNQ<;ŜKm:~y' @doZ^ơZ?%ox@Xy}W6 QK_FhS;1҅vT)a+tӵa$֡Lu.T!jN2n!4ZUN;ܳ0/i A7+qY\@$w  @}8AAdB_7y8cd%mN0Xx ˜B ɬDPD"ZF:)k!2u/DEDPoLR>Sng(1#qE /@øޕ["qjA$AHoJfwqk_Po=zhq"1 _J62M{N*VWE ;dJ`b껗q"=-۸̔qHtAwM i|w6J(7YhEf"ƿuU@"DTb ^ye<ӥ$ X1xh^q|9/F%UfNKpGT!Q[[3KXN6p @ N8*%X:%$n#LyX5+c:rZSTep^a+ހC&m(HfJ8m]#*8wQa^ܹHYoTE곀l [ts6v/Esðj =-8ؗh_7^/8!j B$kgBq288Qn+x^e#'v Q;"q<፟[r q03yu?ҫfq|#%m,O<\y`tRne &TCɲ 5OŸnXZ[U` _AaD.U/цBu:q |頇j&ܶmMM64DJl@ YŃ=yP!WGX8~Q<"1Xf2:~Myl LCi9eI407[) gIb} U@/,]^ {Gŗ"Xkd8 c.X(N@d;0j07y9賟 U$ǖ%o7HǏkbىfi2 Bgo<] )ɂ58KLŮgB",b@oբ^Fo5@1]qQGB1 &äalPb}.v|-DSI{ BCSH &>rz8XhzboS7__ ؐM zny w1` žp.V|pD׃:3lT`s27fڱ0mi ./'i[x+H>8LzoU$C.?+H>8[*# s'% prWaW9\]*Q c0_ICB, DU!0-2\B T(l#0'3]:{{ ?#+ؖ,]ӗ0 \*QU68HPgKiFA3rI-G-mlGBs]7V u^m>' |0U5q:e++b-jh}|x'|I&(vw2Y0=jWd4HƋ6>lᝰ1({׻Q*fVɼOoVl`kT\CÖy@VһIs.Uv93$C˨ve>![1]\GOQd<^<Y,Ƥf'Cđ)^&Œϩ*`nژݎC/-sX|0f o".Tnx4m ?L7Tn#M+Bl_&~TK׳GߥzQ{󦯁jlV{I0g k3h\c"×`e9 yR>Ξexbkl4AiZWi6`smګ%)CNAam5nb){.fl&P'(0_ ]0AO!:i{(p ^񡘘è eG.0Qfg&#i|sJZ9B`jQ"ħ=ߌ7Gpu҅Q 3~6DV @ ND!gb ?^[/C)ȸIlDj:϶K2RIzDFu(zɝji+wzv!%2P#Hu dk]L jz>YC:΀<j pd{5,` I۲=|&M;^2 W`vAyƅ٧6gMqBbU{l~gτH NA~_}loZ@t}J.ȭWMgT󤾞m˨3a|ѓaic"Qvͤ,%~  xSJ;ѻ9m |h{CH|s**E3x[_;oDݨwD6c(]2ؓ8(v2SL,\\ܠH–x.ELkHp0G]0t)Wde/uڨWo{3?07&k؄Fn1B0O%.-0w|sЇ+xh=zm=svj8p'@#l"Nuw&3iAgw8JfbYWELO lYð;cL 8hP_O t]M_EUyX.Ε<qҳto Iq+=`J,k<xV2hmgtuaT_ܺ_EڰZ/PX1޷"1[Vշv Y{[wTo, R}٩g>}2wiO\tc`Y>Lxfn寽mpX9Z2ˬk͆s΄}xec6NWz"& tƌke7 +!$˗"*z܉ \Day}KXxN03wWQr.TŠYZ9(CRvGR.Es]A &OCT jt ͂U{ 8^‹%=DPĺӐ %o~2Jn=Y+u[ZniAv ⎤_nھp~v*9j7A h6/%l ǀmmۡ#`\!҆1’{P/d,C mGgv[F0󸓷 QiI5iGD"$aB_$]OdT[ |~@QuR+8DWNGPM˷[[6sՏص &wH;GJnBquME?wG_vת cozIH0Zg1+\{^<%qz!<"&L5YǂiϞ*_?<;C IBCݗ/ʛ.XTY-nN)g*;sQݗE'p?1KϝD<^:ЃazՏKL?|~hƐ*nmRO^b,+|g7^GjAukءEoU撷S@dO.9ߒ M p{GscN|BpLWсz41ŜqVO?5 LL.x;4z_xeh0H=r&P0S0[ɟfpE6DauYW0@aR/gw ӎO6Gd_~&WGw⋡3Cxo=h(ta4 MݶGuٮ~AIU\, Y%s2)_OQI@x'GvxȦe.! ,l -5wNC5TVai=ۣWA6O<,G&\=FrnXb||Z?GoyY'Ǐ0wq| "[l\3jwob H؋PEnwa6&1?k~P{R FT80%~It#7D3?x S ̌`}_l)Ef"h 7ډթz!zTf n7jg Ԡ*D /F:we$aW*%zHQ#uZRUi26+ϡaE5Ɔ#GZ6?m0YDfiV:Dt2}8.ADlY4nZeD[X++-PG$̿`T}:A{*8Z()ࡡ%̴ so{V]si=.Q6Sߝ h7;e>z)*rWB"d:*IMq- )kɠ:$ 0JuXk"5Df4# U4 q݄ {v0CX P{XU*ca[TTa&$7z^ B'j8ѦNcL 8aq܊nUL=, յ.**}S>3!RJ" aDzWbTsCH_:s9<W`T-> fA9ef40"$wE<'5TH\ LpDGq?aA^[ [6kƧ[ .Mn)_!b_C5^vm<%JV}+Sϡ~ mCC NH* DD+OmRsZ^'J"n"!2bV1AnEzܰCn;\ERp9~;\=$ގzb]~>4n+؈Z3d6EL;X3V*濔m$dz_7מ96)<P,`wlO 8Q:ꑚ$F1gT6> p*Lk'c$:^|_b̎"IpeȠW^u< b5A]i2| @Aj<79c-2>tb-^*U7T*T=ؙ~ ġ{oTϿ0~Ó'v₤(9;9{rsh]sC+wdž[R5 gA1zؕqzG<(AS=ӨJme>cLv>PAkzK<{==a >̔$Z7Jg쾞P+/XGY0Nvho}`dae~&37j~~ ުBY(c[qw*Ua8EҀ`BFg.,%b0 2)GP r7ebWL0̘yzkWٙ'ݾnSBR$L %Ut_P[g 9Slp˶8>|aZһE{bQ/%'ϵ}TcAѸT/qtbToݞX[UGmGڔJ \ myZJCVZ}})>vUT[#9dd!He64"xX?JmUىߧ=^וr>Y' S!bYf[PòjYKDNmd"A;nJPh˽lϕⴹ AmxancRe[ڊVU nr0LʧeyS<;JCv*^`׈ +[Wa?n|Jek/j8YƕitV@=bţv!f^#X2tS0|*?"h6 Z1o|@S$Q.k̼ "Y<ս`z:+ݽmgqlIUs~`Bs*B ^}_)`uxy;R+ mͶ}g,^ᯞ>.!utPLD[֫a3]YqۇBUsON Ő5 oƅLJo<<Ɖ7 #0v®8Rˀ˘vBu*"Pu H<2\K}ݍ%Xݓ `Z;fڮ2hpX: v8nw,,84C 5!œm\XCe{zhhN:ة8xs۔ڲK7QoBUo|[Lk1n>|\x m\eGWN:,̆Opr!y)B*0*co 3aA1ݔ`|*oء05>MӇN {Cy"]P;~=اDu[yTC%_ rI$mxW"-jg&IڵmNiU𐌠~3msN|R3ߤw=1Y *T(c.n Bs7:jg(NչNߊC26$dμP7:z|º ã4T1 L*n=_ʻVg}~o k) n7u_8FJcrm1:: yX萉BٱզWC {KmW=22.Ï]`+J֫>}.0}gFr@x'X_8Sa?U3/"?4gN-aoT3"S&[̂HsKd8n`H 1K _u '^eyJd]fD"D 9LAP̡Lj76=QNx$z*M)Tf_QNy|_g+!J!P n:qތ{AǸdo)E⹌J% =NSrؿ)E3OaW|Rjayu1viͤC>%Z^' Lz sdK~SQU02`8,̤1W|V))^ .zwZ(,mSlu]L0/͢8$$,}0tzu%2Y0iOC?s!&z9\ezG͛FB[Ϫ+)>n$L?)sd\$8 Kb/u|ݭogK8*(PVw9݌.rgb-cGw 8s-]sE] zV4 TrݨItՔ~˨Jq4{-t*FPw28oVU؝mHJ~,1Ќd57_j'_F&x|ET<;ll4QO]Ʈr '-Ε..X' ^ӌם|pߔ"_7 WuI؈%mO+jmk|?tުͭxV Ʌqbgѧn :?˛8¾kFŘtv(/tv '?t79~̻ ͖L޾Sѥ7cRf D _aSzy Vu;B 6W\7j_n^Ϫ^d#f'qT`-ڄ'f걹j5ߖ`&!kVub a,@gQ7&Nqvn3>A

    djx𽣏s8|pc{/^8HZIԂ6ڧFո;:]:jM]䷃< ^{aqq7L/K[M D+P]o;' "YЦoɍQ{uG7P}g=Ē2F͆aBm9 N$?qBk[3\&煨z W O8ooZBzM]|ŇPk6OqMRSMϋz\@+Eej /l>/$zI logУd { +(UCTfʺއ O(Lԙoo { ڮ[ $g函S?H>B;yR,hxNzoau/˚[:t#ow͛iHBٳi^:ًl=uqbR@:g"9Kܹs-ԵΝӹuy* "L}G5 ۱B@K!}{)eS˄qtʲܦ"ɅHPP=͂>7U ȍ㊈HT^C G[GƤ:k%޵A-*9Lb:\E`scI;^DOٍ؎DH_hRpQPדl:N- oN ] 2DYnG͓`RMpw?VxJ7ň]HVFեdv0ʹOGJљ܎q~J$\/V:n 냊7G}WޫMx&t=c`ǃ{i7m]%>urҳtn&s-K|pQ2(`7}>xH<6Lg^/Cƽ¡>P*1:S!5Hz kN-BqJV3eXۿWD[oWBۗ2Ja7SƮڙ_ Dt3idxOuiuw!4<1}-Wĸ*: AtBE}"Ey߻Ku|TMʹo-#Ӓv .5 k ūǢ)vĢ&>.t f}r8|8{l=۸zX:p#RnmeLT֐z,dJ~)|ӟߖh[9~ZZ{~UV]l^9e l}ao*yPZO;2~a$xGŵzl\ׯ)qEJTg흊SzP18R!F"DN;"M,>.vl%j~[%!Y)7?a^[Q,|B5nD={=/_-c؇X%hj7iX{tMV캚hH1ļ+DL 5%U@v8ت=#ũ=J[:P~bٮg}],Krפ]V P  XFZMknHbBmZ/~!l3=[!P}tk&d m/*(5b7G_\{')(0cCN MO)\2Q2!$jat6KT.L#ff b:<ԏQw&).a{-լ@b5,:U?ӢizgVzؽGOӅoG NЩH*]p .k <%FJQ.0XYSRNQ< e~UVQ Ļ;9$Pߵl/I=)JgdVc_ RpzEBb</Lݒt9SU1)Qc6 [OƱ5.p6XnLq/}|0L`ܕ״1U~?=EV8n,> T(O?$GHG>w)nx첏צP- %[ Ebp6+l'l:6FTfho3 w4Z7cĹJukm]jkg_y59M_{%$ w Kx93/A O"+bĮW`0*?0z|8O+wB{^$kkGN/UB,N?p0\B"υITϾD '{jR¯}lJ2uN:z>h曝yזzpY:{6oXC: &6$xm>ߜ|.QjwҽR /s7)+K]F5M㺓M,Yͳ(xTE:vG+[&PbtBF:sd!.X h#̂ ~eStFc8h#B!kH>bY,ɗmRƤBkϚM.y {`} jOvJz<Oe&a Wp{L>(͢֊cfR=CʐlSie.2oT{ ΝLUbWXg %ԡ}#aqܸ]'Do.deOz \v &b0_0>ڱ>^X󜮯k'ƣ{#8;@ua@/; ۙ%hؿ:sK~h|Іku)? v4x;}?oisae ³χ:^XBJ'W{.YQwm)kCx&[}U  }0FOD8m"(3%^vC߻24pxoMj˗4YTEU ºHsRMMy?mUL|I!I\뛔ڪ$P[?^m+!oTl0Ѿml^̇W_zoi&&0uJ]/mEnӛI Uy+IyXU}Ϝ9|f:m>LjD>Ov3 tkZ#ll#l~C1p^y<azc h@h_Vt+@PW* &6TPW">UCeE.vw`$jG*]3I/Y0z,q߉m+}Jů;"8w\+l  ^4-*UQeA|IVy;LJc߸?&Sm ,QaLG'JD[k{ 023,2>`۷g=|&m&PFPi_fUF* ؁hw(aM-FXbSUufEdJ֮uI@Ccn$T\'Ŧj/մTk#^|h~{)4̩׿:lٰŨTaWeQ^^0>#dgOI+0; 9yW/Pqq6]x?uj2Zt4ٞ_G#\!Cw]bפE\B"/%rϵ~LzӛnW הYX2ٶNMyc֚ g܁8IzCViQߢdoVb|ye ׮]j2JL6AvU`oMKUR՞>쯵 a?Ψ# C&}BhslA(A Ui\ Z(4- &֧YD`;u=THCn aރ](aXgP̨cu`H\:6s^KFMω>k w'-Ȑ& ̏))MGGeڮ b=&APp ~K.?2[d8#T<$j˗<{oqy幈$?'|1 -^fӆd꯲ cG•je&щunFܞVIZ N.bkP q#\!/tbl"Fz!V%UQw#LB76<._+E0~0 qP>W.Gu!mFȡa|>R~(,ntm"7ݚm'(JWGbϚ]0nZ#0dN l.qVLG_ CJX.OGUXAXVW_% qq23}F,è1?|Z/%*Sa8zr 11`'>JR nܪ1ֆq\ UCx$~lw]V:/(çukA !yT{nsRݴS܌*&U,QHlͶ%b܅`a,V Up#1zv>_k{qy1ЭlC6$+nzI"NymLVdV=q蚚uMG`\'桐7I1򱶻Aj/}K6oF h#ԫBg+0ˌU6mTiΤ!M O"$Wz0>ZZPJ u{H}#,3%tڻ# ΑZ,#1IpL_}}f)'䖏KF-)~zs_)5$\Ζ X!p 8 nW,+$kwb@D(_=tp&N& WmAzFWnaLjULz\q(%"kN I–`$>ӳ&-puhK^ =|+a1>7^y-:Y9ؽY9 lldlO8Na8F<$Q?UFׁݞm]^I?q"Pvk}Qbo:wpkHr9B=tˣDIa;_Bd:pF;{>qpx`L2{2Tu\v8V]I,)Y{Q{D"veHI]x)CC8R lEy3 ϗ^zͳaD!$l. }:0cb#%^_S1,(R@ŎN|o/a-kzkhyT*1v9R)Z-`FL^qZ͓&~IwO7ӀMމjqo+6[V*k]b5-qV#N`8tPћ7ծx::~BVؼ(1>D}7c MNp/v=H pH"8ƍzbnwKJPp3*_V4u %%e'c ?-ؾr>}S$~=ki{8c:d ̱c+q1b炇y:T[UY:W0soKs Q0}L`P(ֶbp)jL;R|7/~1 ;0 7#%t2Pe&~h.)k´-se\{@T'MXuO=B}.ainUY#'Pm9Fuyfh`Etw*%${ Aʆv ,)PBƂ$IF)`+h`Ev(#SHk6a͈fr"\$E+#Z,VAxq / o yޏ#UAr}s< !i 2$PF>pe8[ĝE͒wZзXf6;\ : Aݳld!D'J+Q{Gcm Gb`Ce@Sd [ws{;hM#Å`|@{@U@{(<0[ _lJo+W>WrrUGc 4%+-qoߝ^/{7SĝkjSS3qΆEtte$I'"A֏[&o^I_`v?h`G[G[v `_V._ @:1ˢHmc 6?|Y=1`$L\LR'\73udnn_348ݺ=Q@P7|'!!3_oU;QRN:1 ]b嫋j . NW;3.ȘL-ƂD %%wTC7"Q\56%9L`_euТ ^\J;ø)-ve/W>IIB*-UbE#ifPZT$6n`>zv9]S_#Q_e~R?}S IԈ*$x֞%!x뎌?we/cZ5fv#fIM:r 8j=q2WxMEՖdv樛2هRXD3 ΋' YB%Bq>z'>rNz5V/9a)jIr|yEo<]\d,OL&džr7V^bywۺȜX#߶eP{@װ&}S9U?o&bkfyͳ!$r,w: Y&0Ph1d&~UԳLu)C Ch`2n}1;h"Q}@J‹/Gom2KS;?˳37}SY¾ {;BO¥˓axHefzi ~P[_Ll}Uam@]c !m_Tq%-ThR51vOCGe\6Ca6xה?NZ'ǵ 2 t¼iE~mFaX ŵPI+=̚.^鼛_xl7mfs@uR;nLN \ SSQwHQèM;/!"ALJ@2J~. d|.6^/N=7&#!ZcH"2[TWc]*}.ibyd+O'{>ww>m 2JWئQW5̝_\,0_iQdAxHF8wO=Tbe2^2fR8w 9 V;#icppvT#!!cO}$q_T~`9h7ws I̛\z2KN!ݭ̠Mo#ls3n5T?TFj*鑘;IM#SkҶ/ n؆ h~5m 22n-:kc!!C=pm@@@Ure222222222222n A?OޚRrwg͒p  7.C C C C C C C C C C C lG!s22222222222 p  7.C C C C C C C C C C C lG!s22222222222 p  7.C C C C C C C C C C C lG!s22222222222 p  7.C C C C C C C C C C C lG!s22222222222 p  7.C C C C C C C C C C C lG!s22222222222 p  7.C C C C C C C C C C C lG!s22222222222 p  7.C C C C C C C C C C C lG!s22222222222 p  7.C C C C C C C C C C C lG!s22222222222 p  7.C C C C C C C C C C C?{g#Y鮱'%$@!زaqGa %Ė  {sw =0_=D=iu="222ӎt1{5;v܏Wl[V`+ l[{tߝV`+ l[V`+ph[X9EoMԯ 7WgFxq}COFmŠfovzħ|us[*ՍʴWX?Pz||Y9\}D5-wM]COy~ܼ|7i+{N}ǵ{_ޟٟ{??q^x\)mܧW~:>Oɟ)طs =ot\OWs)6K}:(?C;v+p p?|:6'6=nP\Y|#㊯IՕZVuXHyq߷ [z\GJ]uۈboT.n}F jks O=y{ Ѷ^iGVE Ml=_qLy\q#1u;[l yrު_为: ۓ7om ΉIXMOAz'eq3i~ŧ4[_ꉙ&zg+K!<ם]OLb=8r|=mY{V 𴉧iOޚJz\l>i'7+,m[ۨǗB1.}6*ls4W{l;'K~~tAEVa*9'<Wm_}<=ʋ\߭}>_{>:+ixfZq\ўjoѱ~[X.8qE;d:sv;G[\TQ[w{lT *EWo3}툛iþo=m:h{Qoij{4͝3SNz*T#?#y7_W[sa^99X:}uvn~~g>s^y)g9$9I.rf]|qs<7L9;}G>?Tn49M?#L:ϵ9Oq3_.9∛bƓznԴM]E7})/0;icqsϬyNHx_}K~ޜJ'SOA ~%^ңy4h/z/'#7s>Ϟ~ͳaE ooB)O W^xyD㩿m4O_W^YOㆎW`g6;ImxsM\ȗ4cG9OA <>KnZ2>~k{g338h/uu we,$ ޾NCU p7A'q68~y\uOng|_Q6C?m悤uWǸ[u߭l+p[؛۪l p/~&ؼ, /pyŐDtع 'qd pEJ9׼.Z%yg͋jɗ%/@cEӟ>%^/Jg[^cO9#7c/ԡ]sG|37;GLm^ī~}=[؎|x K>LA`Ng#<>s^;|3Oo3>yƇ{ _6m}x6ヴ}=͔>2_}} yo]ɷxm>:oy&;GHbun+x[1:־r+px6Yk+ l[V`+ ld;[6Q'΂vl;z/OHX#fm͹b=-}$bgM3Đ[_[N">xUDEW_nlG:mHc87?ule<}? sct48d 5ǣ9[x79w7oᩎ>+V3kC&ǃc_Yt3ߣS'׸#+k)_o%y{+GSwc #0.tT//Nbc,us~VݬX[ _$m8 k9ʹHο`?qӬW(_¯k$ѷV5fɁ$_5~/vTg0z1~_x?#iׅ?uk?Ï;)ƕ)F^uxw4cÿ X^ty/aƟr].=[GQ |U]έ3TNl8qq;:yS֍Rџz!TQ^!l惾9Tͱ1eq@ߋte|UO$vجأ8𙣸)8:$ 9'N N.uHtn1y; YKH쓛qu#z.g^6i9ӟu.~͵<" v95A뀝xѸ17;OmWjW2Gc1ٿjMur1O~拮}ƶgN/WRqMGGiy8tGGuHѵxзΌˍ:VQU4*n[V`+ l[~x'v&x:*MΈ<)>Ň'>U/Lo7N_K'ކ[H0mb 9_}}շ}םNJ|Aڴ1Zz8'>i.3f>Gk3Ն_sbl](}kߺi_[ۮ[gf^V@O9sXuNy(3sq䅖{}cE'x/?}yxoNc3~ ~^Nؽ&8MO<~\Ğ~7Om,mGvmWIsVK>8{/j y)u1a.r8\̇ My6WbOyO}D?qa,1}7&3|Փs;״;ޜsSr+(*G#"rn[V`+ l[މݰIl[V`+ l[w{yl[V`+ l[;Q]x'v&xr+0.tm3[u tLZ _5;y\?M^4ͱ >cH[q!`ɼr te6Wڑ;<޵apXC7sD\as+ϹqCX3@5t@IDATy1nq/Q(^qSWg Bf<#~C| ~=9e?ZyUG#?lm+46 NRaG "v9]_z3u;͵9y/9}xk;[ؕ[GY |]S^NZꢿdxެPN'[t=a?+u=/{r3~vA(x~N8.2c16;gtM"7]/rGqaJt3~yC1hj'u7_qH~F^?1K/xo\^kΎ[ryY_'B_aA'6c8&<6[_#k_=1눞mB_,8b|wįgZ~7~mOyLew51G{+ڕƸ/8ěcՃkٟ6}w+~<q47C̏z9O?}h_}K>k }s?;s n1V#Ƌ/xx\wטʭmTw`[V'\&N'QItޓ:8xZ1^q>9AҐW6bڱƫM%ٟqU߹XH̛15VOx{r:&mF<5BO̱yBqs6wL#*g9춉T>μ+~zC?c̣c}ՙ㙏:ԗzsͽ;/8|=+ƸWnu~f}+ l[V`+ l[V x*pS,s>Jo+ﴉC_[G_}cɛr1y96J0Oxj.}btJ>}ۧOu/!؟9>Mc4?ŧ7o pC9)1}|m_c5guSN1v壎!mqhGs|=x)׷>[3nvcVz>W?9+?_x+7v7V)k:hWX#ѓ$k;/έk:Z ?9ZyյXs#|78X ]{=țIzvc!o.p;Ɵ:f4Վr(u}%/>GNy>|ƘL=x,֏yiGzs,xX?0:}={:5q$Wnn{x]3XOZL}KՓ']g\8GHOtɁ?S sED1aLy_7 cL_-|EiJʋxc}tQU)If,898)_,v&׹8x yc Hq\ vwU9r\3}GĨ,[1ݼ;ӎtr 4"Ĩ~e&^9:T>ꕓS+;Ƨ}GM8l}^IuҜ'WW]3frV'Z0f3bN;:/ƪl_՗S)&\aa_[,}c4z<:z8Jci?'6c}۟1Xcח1=}$zr6zd[15Ʃ9AbV󝶎x؛GY <DtZtY剚`OʌY[ag:)_C&"Gm _C$O֊/?\w?k䬎\4/5AW|\N sI#d|)?y5Iػ;k|Gls?+ϑ8Ø砾xS{yžaNnhSLڜU9͸ki߱ʣLjW懜O_?4>kN7W>ߵh軏W߯ƃz 9_meSn?w<םX4>]#9_mu-+9>?+Rg3'ON$M-gG ȧ,l+ l[V`+ =YKg>9/~v~PbEc_<)C}W/&g|^/bK1Y=~W"_eOYZ!q?ysusWu >~i|=^Y/dEyZ'8bgy7:<_3_᜿*}o8^࣑c_0$_s;v|`u`>p ORoNR=!mQ~r1^?s'^g_'z//q_awCG}?~yA.? mXx~.y'٪cBkl{'Vnn{x[\3XNT<oIq'ޞ\E9ࣟ}WH87y3+~'1scx3fyڗ)n_Np3a\_ӟ; Ib>SlXtΛqiG{]G?u>k uNo+ l[V`+ l 'O̮Dw)֯򔘧>=)O)(>}RJj޼ĨSOskI:3R'6snE^:e(1fAOM9/]$\o=N3?O_dqBcÔ)Ń)+YuHrTo_RJₙSG.}տ}0G<:}898gLɁ8rA:q'@־#JW1s~`~7dyt.sobjoNW,6G:p8zɃħ8x:.V_{&v[ۨFc+Vk=ueamC@a :$:q2tp@fL,)sܓ}ײŜn>VkAy1n14$GkyW2o#f/w>kg=ԇ|Y뎽a-MŅ9F]mĞk^{˛8^|ŷ]Tg^zbՎ a訣aC?Z3yT/ j/A5Sts^s(#nxv79'>F>)5Khm*D;yK_<.g>XSi*Ue_ ~-dl4kGk~]Ӊb~̷k:B;|/%RI\WE\JZclim[W8漎s1SQg9u<&YWc|d%X(WFr3gŸ:;~oI>A|)cܩzy?sՎOb鵟WQNocLE~m;'K}#GeQGu#>=~O.}Cg}宾z's+o1~Rʫ }VnB&.a+V+k-\7ɶkȸam>4YsK)iN®h ]=kGח f|-2NES+rskke}@.\}<漺|fYKg"n+c1=N5W1M5ć5^y_Z~3?tb1H=ԓ7:"qqKtŐo>k sK.ʃ熍~pG<66sm1'ژm~cG ~t \:cl䄴uM+ٹN1/_S b#Ɨyڰoε[|m V&*<[g0ѓ܃m\aQG ϼXVtyȯ)/}ON_#:0/v\bHqCcs+~`GccW:(ՋEx&xίcpΡL]_c~_}㙧xUi+Qk7ƑNu雗R%ߴ)/>[bx\اJ9/y=F>J0&9j>1'78zSjG>W;OZ<{7>+87/p{VW1w=o+ l[V`+ l?}h% l tMx#mg;O_ŢwӷXuSQboh,G\y9.xlLd1Px녞v85cvn39Ϙ[9wq;3W y'v퍭^)r oyOߎۗYև|uW\ɧ9iC>o+*7w+TyzbF1~Gv͛\p|󟿜s`L1<9΋Ev;תte/p]D_W_}x?eH]Ș9ȫ9?j/ Icp~{r ΣkįkiӇ㴼#EC?c6㨇/8ꌤ |לʃ qÆ_ClG[pAF#[`ß6yycU(O<]{)U8=q5VR+_uk3!z*~5_c.H'eMsc#~6 y̵|glT 8W޽os[g=΋JҗÓ\調DY\9Ŷ/3<:'~b'5Os^6|kC:sc. GOӇ>yܰ?H9["Y;_Ysxǹʡ_{eyD!v~ݾ\Ѧ]X/Sq{ ŗ}Wbk~m냭cJV:nr*~lgϘj};_L_=y0/ykpC~nG˶m+p*p;]ls l[V`+ l[[zIp+ l[V`+ M7V`+ l[V`+ [/n t)n*k6݊^?ę\`ЋEvxGkCYnڧ޸k\uGc?69c;9K=K^u94'{p㼱?;yW|WɉS_*[qo/VY j.V7K'GNI_Cɏ_&F_y~ͮ "t3?t͍1 ٱiwjEQ"#Gr@figSLb> \ѷ~ȾxeWG ̙5玝irA}d^q}c,iM|߇uգ\$F6^_6r;~=''}C`n7`#V?t`gk`mn{x\3XNR(=^U$|qc>`ExNr(~ꌥ2Ƨ~cy! >~&֋O|H('u>$fщ ǫsױ(b+۴cN:tş|K{}nO}:o Bk l[V`+ lOQhgT>Va>E[Pb#n4O<|;ظJigg<8Vxc+S|Jb:Jq ݴ!5pXwdhSn9 #o1G7)U}>~~/w#9tW>|ϐQЕxgL/FXc85;#_}))苧h. ܴ{xJ-n+x[`-CzF 5]01fIp]x\^G4#=ธ ,`mIg 5Scx&˶M|p~Z(lZ \_΃}؋S0zN|ag͔#kGg8>1cww!]X9uN|9}F/~P/!O(s?{ݸ_Lkcیt %k5p9̑׉}?P?Ǐ97bMxv}]0Z388>4lpC;p`<1Ư̫5^t~`ѷ^8Ss6<6_}5W^=NӼϹx7odo^'<ۜy V&*<[g${rxT)cn1v$]>\Dxt}wJpgi7W\CXuTrܱ#sñ>?맏8V7qsq ts?r'ܘ o`km&g`3e]w.}0#^<G ";M ~ }?W99c_is9Ss}}_7~`o`q?1kr;6:n_>t0Ο@?6Ď^|! ?ӏ\ԑ's8gR*ѕ1MqJC>caclL Ʀ}y)|&n]' ~yuo}9n r3^7l|:wqꊝ; V&*<[gXF B!/˗'A]{ \X ^,8b6Ї>ty5T^ ?iem97;Yps-aHZ58ػe|]cFGBߵMlCux94lQlʉ;pxҧǕ><}7YW٪gJxjOu:c9`US#i>&͛r'YwrS⪣~13psٶx sa[߭V`+ l[V`+OO^}Y:vV_Ձo1|b)<}"V>ΗX>r͹x<؈Y1O9ч;r];yU}mK;1񨙼WML8OY qZt 7 9מa򄋭m=aؙ5}Gc7oȏIֳ9v !ipv1mMܸFI#xr3w4`&Կ96Kkpm }_#\ 9мͯk#v拽y0<]+P:%ܼ:6}y|zmٸG9:] MmTq9[ xTA޾Xe}8irRn;S''ppBE6c;*E&ɟ\vr̾x%_yzx7<^8 gܼ\7GFߋr`l>?gM9~J'+8wbzW:$xr OMcQ|5[Ǖi:@W?Ƴ57mIQڮp*dqW>k /O[wyݘĢG`ʃ.X.c~s 1~O<ڕ^%N:ܮYVa*aXw+ l[V`+ l M6ɭV`+ l[V`+p;؛۩l[V`+ l['&MVɩkfSϺD ;O軮};yX]X#Ӿ>'n+On}+kv6rc^r͌N=ueL)^duծ/:smzElkɹ?}㵯xWع~_8yjS覟!_c}y{NG .jEU}q<.Κ9|s 51N} 5mxlRqd/8[[y;G1 ^ }9AO_kobXqGsӦzHmՁS`fNkv憍X_gsqȾ7V56rOЃ:kG/\x㍷V >6ty܈Mԣ9a/FJ]I,1E^c~m;~a8l=o v&+|[gG'pydvd3Ok,O6+erLsN&Ɲvk Gy=c9(Şծ9$4/ s}}4ԓ[hbگU 5Wyc3Ƨz},zxVQT`>.V`+ l[V`+OٴOJxգ'_ק}9XџvrsWG]=ݾ~?+/r#g1W}yOuv}'x MVl[e8afu74N];& 0^`pΆkz+^X;'u'fּ8v 8ָvWs@Gky܉ӵDX"z I^8hh '/˯nk޺Zęk~]Fܮ c̚|~ƙ;O<p0o֠͆?LJ<ģo\dm?8ə܉ i1 _7qQ3lѵ̋|ya-; xM.׾\f=>^i\y*}oz!s2v^7ȣ6A=g,}ֆZf_K /렵÷kNsA}/rS_>|8GrL vi~6ƽ‡9c0P.c:XzU~ٷxE*<εy?cg?}CL88p#ƭ}rڝ˻qD\R܌aӿ8Wx _0;VUsRӗ;cluSϾX):n_s~bN;c1T^Wsⴑ}uD]_tC}ӎor~Mcd;ǣA~Z5Xq|W+^y<'F_u@pgp/Wӏq>>EO:Gmn{xU\3ZNV05\vȋޮ}jy^0p8)ۈ׵:3Vl#z|h@Ұu:9U\K׋VņI5:w2p4Uq0/vӋ@v@IDAT[>|a<c~J<~cmx0nׯv4n{lsM\8?\Ï#cbQb?_? &^~ynWYGXx]7X'&9/;O<7yٛڑ}]ytq_zYK9ϣG^}_gg/E̹sQkF?>G<ø81_ڟص5Gku3^R^q{y|[ | V')Ndl8Ly{ ;'[6/;i&w}!i#:NnԢ|nLyfboB^Ɓ_H|[cÃލ1>#nf<ٝq#~u?*䟱&3qo'L;cbw\jg9A*7>6cq!Gt쯾&\3>50_Wbn~wcLӮ&yWg+ Sx*˭zŃxSOXo%s9Ny |XHTW\!O}M}b& <6s^hsu~<♳:$!mV`oo˵x+ ~=m8>thmT8kuK\Ǹ'\|z2 <>9__5Y?tsί{SmWUga6ԣ֐0WNUϰ?<~=>Ź/br`7z $ _$>O7L~ȓxN^'`6WN떱>H:Aژ ꉴ_%EѯbWkWnno?{lO}<1Q[O=ق)n^xx#rWcͥ}0ɧ9M 8WkӧNŖqfM_0pG?tDo;Ejy:/ծNn|ϸ5E(y79&9Jl]&Z8?b70O0l< mlGcuC>pu wL,9O?q%6~'~iy;w9=5bԩG+frֆt͗O~~W~tD/_G>7?n?O)y#7xKt=8<Oۿ}mOo8I =:slx]P“ 'ɁM$暑DZ kr Iuj>vخ;)!yG?nİ $mO=qXg~MU$`qC"u ޳\SN!B}a:)^I3]{El9`ǧly׏\Z!it11y!5Ttַ~;f9.tg?׏u*} vj*ٵa5=N$~s"s ^Ο ;<ݿr#O_p{Gl3/tG1q|ڔ}c'r}eקz||{/upWO7XʼnA:ѹo{?/f̛ '*nxӋ8N\}_hxD?><Û:N܈[[OoS:>&0ŗgOfD|+|ȁ7+?@?੫''yĎ 6&9ȉ?݄gƛ0G1Kr4vIC= ey>#u,7Mo;`GX/mc7i,=[rW'G?wcӟp阓<™ ys>9'~]hުc۹C~ܴ/sM,t|3ldL~+^~9iJ+-g^z8;GǓrWQq7u3ô7';Cn  }MXe7rL܎[ | ~͟__=ĉ8O/I"}|>5Ӿc?c'_~S;9qpǧ'D?s?wz*̛7z}Ǜ7r9mm~7~t)A=1 *q3?㧧c͍ɟ?/$϶V`+ l[VW`ooiqW#iz7Q捯5 n|҆{>ㆍGN>Ƀ>y7pJ|qen6?я^9c|#>Y@En|ˆo)!'ymǖV*EGlp\cЇjgڱs%b?o9\5.}%9QGj?ꟛ_lug\~(7u?cO^d?ɣԆ?5{-;>3ս?n?Y'3 qik8.]dmŽ_lKy?9K>pٔ3VG}Q+}?mOrga{߿<ʯW~~e a? ;Ѳ!ߧy?ZB[!5b__ O!=EnZ'WB]Iw拾9n3O>Q/?q'd0vMۉcv̅:Fر/!?o.yn^fo~zS0P㭝vxqzZCnsa^`d.9aG{є\εWC?a_˧QxZ5/㮝.UJ_l-p- O:5"{R;km飫8Zܾ 5kP~헗x{<=;N]Z F:t׆m싛\?=ฮжb۸<#?}%0^:8&wK.yS϶"<>?ǍCͯv79>YۿXx>*> ǁ>L\HN9YtwӱׁDss։ 9'r6p\;1+?Nl;وIM<.d0 Vll}?ۏ̓OO3\41+~딅6җsmtx1gƚ=Տs0VVM_u(7Y:v+xGç1KSvыbVOYuwo% Cl^ͺސX|wRzWo&^񳉧~yE鵓#_z[g\ot[|>rY~X6N}S*C3ij]vVbwǬ}W=//o<Am(C۞}E799y_5_˗xo9x oRן嫝Nb~Ń7 7cő1'Z^uKonnnnnnnn>?7;l>&g~Z Yi76>d=zֲ_o1|'>-&V8ًq0/Ӯ|seŇɓ[o #vd[x\;ѕ;~΍fC{ }^~ɵv06'O9..~v~HUƂ?N3.2IG^K1)0fL.]:4]~aįٷ?kpV~CxXŇ_{9c$+>:ϧ/jLsg?_q'y4{bؚMxĸT>~H>O<'($7~7^>|{{Nl]< ;ytѧv;LO^Wʫ'|*`>y%9gЅ[SnG5Eyo{ڱ/Փcv0g[{:z/vYr~oo>/K&~:#gg\`-|n{|~EC!ɎUTd;⽑Zk eEwwU|V yfKI:n¡߇58[wjy }/L?־8E8 uڒ;lm}a66_| ǼQ]ZtIk~v淺wsj'oUx:<6G+?'~1׽FVcVw'Kd38͍ žp#[' [>.675|ܐWQ9(~ckȷQ[Og/J&yq1ǫ}n̅͛'w^=|Ҧۆ/>Z~2tÇô3ɿ 5rW@/gP8j䯍///s~}:z>Lo7b&.[8jT'ʦ:3pā4wV'ݸ`eHw޼n<}`Gɇܣ$L7 51p`il,VE?Mj8w]3F'+IӭGa7vdK9۵S? ǭ\ۗ{QuFlCgS׷޺ſ?GeuI;6ʙ$>ѳNEp^b^MjoU!rO&[ol7OJϵ?'0VVVVVVVVVSU޿a?oO-SC0??!<L}`jiØ:_CpcbZ{ ˧y[%7J6z$f΃`_ik}-|=z֛Owۭn'?b=y(pAɃOz"?=}|jӹ?ݿw/o_/K?W:DCR|4+_フIpVVVVVVVVVcT_>[9};݇qo&O|>'Ѓ-M~(Ö,~[ [CO>'mNCE1؉灌'd>TUrXp=C|c`eH2gF׶ v'>]-wW#h?tWCqw-R~~;\C7)'6=eOvq>V_^̧r}sց߹?ԆYbMn-196{~㓅u>dh>QY_3ډ8kkW?ʷ|FVE_=P_3Cdcag՚X'N&~i(k6^[ok|a~p"e9Ț?IW>=,P87^?g/O׼Q׿++{4_n I/W@|}&g[_ I__yy`{:(ޗxT|m3<1F _/;CgZ?6p߸lVK΋py/0Ycn ?q,7VؘK>0f'v /< M80-9(Wg6Ja2qaGɵ)o0mj6~[ ^wH>^~x7UQxvg;g‘X`|Ә6,T.>bs>%9O[Y 9yA`w.8)8uS漣{!)SA'8o<}R)O'E'4'k@tyƺw\v{mΙ;j.x|P ]-vխS_~#]ew| kc+wpm>{6>[w>|ǣb 9N->(=_m/XoϾt+ǻ8Λ0@{~6z8"86/޼0Dž9V]%_ sdpYͅ_8Ex?v gs>{86Gζnyv؊y$+6q8_'Ic}:ϝ> -\>>Y]}t0O>V೨}@0r eYaIco?$e>~M/61-Ƶ񯀹f'}c򧎾)NfwCvi雿QxoO|:rXc}? f雋n|{}er0~Mmmb+Ƹ16 p_0E}͇=awfe}OV೮g:ſWM.`f<΋&9}W}c7l{I{3͞\m8zq燗gr^]4qulPc[-٩q&}t3|Z[T>qLJ ǚ׮Ŕϼ8[Q~w鋷k}4wo4o8,ɍ򲶧'~?~<6|vk#.RPݾf>k8הc{\L㵶վrhlzyټ C,=߾T!s G->j}Bf{"jtq5lv\y'NO}n> ܇ϪVKR..t.pZ'' 2>/v!>c;mŸ:\Ʒ|'N(܊_~+>]#U~XF19sya'"3Gݴ#y7i'쌝E|qYrˇ._>~‘Gփq{]'jV_'~ #9q`T%S\z~yF5='[9pɮ>õ>pY/}g~6<9ڴҽpޅIW.O>M.ߓl?VG' ][[[[[[[[[[[[}>sMVVVziзSOSM>M|O}TC|n볓woC8lȒ5OWg.|wV~ovLo6aіl}IS>)wa6O9'Yjç_}ɟZ'ta$+וׯVQ9.)FX{e5ޕW :ym>ZNqOlW.6ipڷ ]ml+KIo,=g?-'t'-r}E(z{s!~Ӈs⒯LOv/m,0Պº[U>BW+p+jpXCa}CLkntDߚd.x~tCwޫ |OO6žGEK0|U |;qc.xw\xε)f>L}9.rkj0:?zb_g&saZӅjpwS}ّ[Tue2s-lg-h;}㍃~~'ߖZۼ}v =_a06;y<r6_ʽ|5\n|ț]ӆߵlϺgo篼o? v~ٔF}Zwc[}荫&>;\36~Awٵ=ZdEֶjlq2}[x?qgbs 8g>)6ź[U*yqnp!ؖdee&# Hg cmNx6_́m[vƢ+ >y@'?aꓳaW];DZJ<|&lsؾYc߸P1ny=M3 |.qj-/ؔKte~2'7O968ظ|ٵ9/.'N0N=}ٖjm7;u;ʹN& >yq֟]ѳfy"+kMv/~A~ڄOlˆfZ-]|VO56F9э][O~Xw+p+p+p+p+p+p+p+p+p+p+p+$ n xj沷Ьٷ0X8ߢ6ߘ;rJzsnda/ҭi͙kky}8'V~~u&;Tr=SnZ6zvɴӞlOq^Ox[r#ӷz7dw,n['m|jƿ\>ZolqO6O NMl|֎Y^mg-ZyD=&_ˇ,0峺SR'6'.;jg{dύ[Ϫ!ŽTΚ$Usf̅׶58|j.֌'p9YbZ7%[5dٟc[]z+*p?V%/έ]\F+IT.]a>&_Fitnθ/YfzuX +>mnٞ ;y>[OV||x#ۛf8 ?'{s|.jT Nq7#̭l?h9/&52c=Ǜo|_tp3=Ӿɗϧx0&G?ini^n0Nɳ'GDO0Ǭd3ź#}#- ~+p+p+p+p+p+p+p+p+p+p+[[VVVVVVVVVVVwMio[ !z5{HߚN?kJlVxlK쳕8?]V+b'fǺCݖ_Wy^[g./9Kw7~6ѵK=Vt|_Yš6 5tTg? W9Odf߸5toaПdɓgd^36G2OO+?tqП@601Z'|5X|N{ɢգwc۸ٓoɓMǮ' W邭~m_cUbh.w3?FEx}P~n/~]wŅzٛWxⶽAxx?Ʒ7{MW50W: S_єzb/o~? 0~1Кn/m>|vkf2l6qjա1|B\?c\S|2&)C6 [9V#,ۼa 60[3}clo_-=?Z| ǘ~~Okמ?okcX+Ἂj_i]~C8O>{aޙw%<=yOOoa1?jgzon hEe7|p\Ӎwde{O~˭qݐGGy_6!ZOgǍ/_~'Y^)tSk刺y;oMXl}*旬x%_Z.d0k>d/9sx!\O=OF~x|Ky %˦Ч[N6о5aKy>٭VV0 ֧8կFcfp>l_kώ.04tG8ٞd(~|kN*Ɵ[| \[[[[[[[[[[[[/L'_]u|VގnVt| jvI]!S\;٣G'lFhGV ]gS 7gM(@+W8U>]Џ_bC63ԏ9/_?*okfns  OMMN7y+~wopNs'~af_(欫\[Q2'ɣՀ~{xň?'f.U-V24]}~N~emmp-UkۿVKV*7Hڏ..~G$ >]kAg- 4L7#_?7=O_y1a)ߵ(|6?6Vp@k5zK٪LC&8mdͮs5~^k5"__xɹ|~g~s&)ḃ]k= @Z:qu5ep]~zc\C|fܧo]_>!S8:|_}8a0a~U~;á7Cyx̳ƬCo_qÀk;ñv-z(_ZpO>&Ɓ/58[5̎^;煵g048_Ȫ~lvf6 | hVVKS8/> _@nfu0 |fKOaW.ύޜ}5ԧ/k.J=olm{XǗ_2 ~qY?bVFm}Gy_:vp>}揮rv87a5U7b_*fq/F>6xXՏq{+*;" W~Gn[/zi˦{ ,{Z9>}\}k9нm-8vwݻpD!>-#kg.rYwٟ>ӛ~eu}ScjcÈvQ6?^;H5]x:팶=4?G>;wq0~7l6ftdGV#K^?>٬쩿G2gɲd9NIOJG;FmCqȯ줧 =&?ᐝ~q?k8+[qR|v|.VK^-s΋^ARk~ւ[[}!絵EvUkW軑=wY#m'nv>צ7vժ5.kEYW_Ǯq5?pv ޠî!gk8'5׾k`2N;W`VuCgꉯΨF#q.JS?S@~=-փƥO̓}fvgv^O6uU/sM;YM5F꿼xb;'ΘVKZ.r†o;KBőб[=]75 Bv{ N+/^Ǝi\nš7?8%o:7A?ۇ'{s'n+ovG#%ۖ>Jf+>ۊ_~Gɷ=mlSF;S'_} nf;t |k"żT~7[/^ }ɾ ߷+-CE^bW`o}'v>n}oO5Wc1ݓoC٬S6J%fy壟N$ߺm?RY?Ռ <Wώ,9JVM/`oX(ՅI-74yaq߾pOdW6hKo>vCǮŻUM ڮl޷[q"콘7Okp؟k6kk7߮w"_[}¡oYcu68ڰ]c'] fSxpvM<oMN+aDW~DžB >lOkv|v:q|<`ڟS&'6ܰf,_i~bd_'y~['7yj,^m7C銯_vml|v?cMDy䳍q^|ί]X92xbf6:VoK8t}֙mGX{ [Y1ynp}*ѻvQBO\[/~mvɖE]vt߾\܍966*0~y ?pų >dbj}~OlaY:oqjZ~|⋇ߛb_:gw-w|cW gsRwL!z9ߜO=~ep;m#ߖnO]X'Uɳm}?5,p8~67|l[yQv7'eا58'*>n*|`'}|6vo>n>b_[[[[[[[[d@IDAT[[[[YCltnnnnnnnnnnn> ܇ϲVVVVVVVVVVVsV&sCn:_ چZ55amHZj'o m׺x~([}XwW/lN>썛I=H.bٴkyjs s fQM,~>͎xXk\1Ìf_'ώ#mmfʼnS?#9m|a֒_EK_tOɆ F8N|5Z|^g;',q>8'6m6^mlqgZ;mv̑,󉇵 \ѧ8k{!cTb |I+bc.l7^]~g.g9ѳM .ƿq58??r%om|ӯ˹w7$WҡE?t~v #Ɲ_3~MR.??6_87>+ᐧG{O_F|N9կU[zm,;&C#7'_SZ//x~U:>^㷿OWu?ow>z쌿sou&yo?اOHWEW7#Zxw|__v.X|׷c/4ƃ5_W=s]d]}8g+9̝ƹZgxW;Z'|q5,>{1|IN5:o | +ǎpnn~+BŮ~ZΧ _4_17w".ʯ O~n X{N |>&hN9xV_-ÎgnشC7i5okq[dOZ >ln.wi\l wi5.6d{(ƈM kƾ\^yF;V8k>O1G[L|?MlO9&{O1瓝~{qsGlc+cG V=_'m++)?vFVVVVVVVVVVVV3$3+rT[Y=ߔV7OoAZzJv!+S[_޾f9e>~مF<=َNm?{8+SWj\l![.a|(S^0 F'Zc\ɓm {e]uVj*39^6bCcæ //|K_ġIm~b%vʼn'yt>k$+|6lokx^OG_˧ۧx+ /t1~m)]|vSO}6{MV<'mb ?jnlocpsѲV\f5;k^ȑf<][C~ٸg/ָlÚO_7o~jq&i"}k_ /9Xnݕڃ;Yro_7KzրM<|؋'n{ׂO?sŌGMnw8;㒏9;NΜ;Y_jmPhmegv Y8'ͱ!qPS'kmY[U |,ċs+p+𥪀 ͅy^X{S?qܬԗZ~2No?ɳEw죋sڬn|1熪dOM؊.o?OƎxg=ٵѽPsk9gyk|({~8kd{_}N ?p>ts=q}|#6H-weDt 7?qi?[y~zT;~Oe:ɞiS.ٞ<9y;mUN,a'^xbqwu?L~pEaϭjx7[[Ϻ7ތi|iR}|޴FX?}z^wgU|zk\ӆ8ٞ6- kNtq'/N>S_%c^ۗh\/l|N]$9q +|W;{1Gήjrٯ7ٝ3Cx_| W)U;ԿktqWV8eft+n'6>-_N٠dmO;/%+Wkq![_?:^ӟ'S&ۧ win 'YyƳ8ՁLP+kGO7(op55ky=|>5G^M8n}_a5B|>9ffmkG}oLFߖ1yXjlenWZ|j_կوA5~F)J 9;>>I?y;7[/@ڷOo2-!< RdMw&Gmx>(.|7b-֩+xm臟 Y?dO;)SlO-<5[>}JYYdφiX>{:/S |Ɗfk]uonnN!i^" >4Нkg<]o[jO"p5Y{!o|9% _ K GnmQL~^_ε?laXÄV,,9z1_sC<|6p5ua~ώiGz*}c6I!5o_[˷XnK޾ѳmbګ~0&8FӝW^ήկ~8t Պ8_kGqͧa'v7$kӃ-8U |O{Ou6OWȟƴ9ak9|'=oWحCƂ㺰[g|__Oc}~^*`_> ?->/y>qre\ވsڋ r^vOn/+ׇ0ya_}0'bvg󌿾nFO6> 7wN4yʷD8?`- ;Nqﱗ ]q^_ C8[pPM^wingͧ쓣o-N5͆O~!q=TtăYIN(֐m޶>"[ܳObktq>?:OCnk1Cҧ+3v恸:m/y(#glZ |+7Fl=Qr({y+p+p+p+p+p+p+p+p+p+𹪀o.W?Wɽ#\խS|gm/}<^9 |*Z k|7\PZ?BXϯVA4؍!Sf[h&7qg_Mĩٸ[nJ'?e1b60C ۍ>OuՀح%wetSwmg)YF7+%qǟ8dޒ'G`ZyɾXa6s ?seOVWdQz|mJk]kst #8OS e Pm;7(Zs د=7}~}쩛.r~MM.(~EQ.??r2V.rv" |wޤe1xM??6o~~&q/yzq~gm7NO?|NEׯm}j{5cw^ `ŪV|~{q>iɚ|~5rNW/z߇_WMɊO2޸1~`+iţ' ٘ٚYn_{ >'SZxGlv |D϶2{<~g]ߍef/>c35b_ӏd5׻N7|2yo>l6#[S8v ~lj3=?¾_ӯH>~VsX.]p9SMO/Y4'RۉE.{u^yS%K+ӇS|yƃQ8lԅнdW?YXmxm1H>Ӝy6ڹkDg}aQߘȞ^.O ޻0Nw\pa9ev_/ߓyaFxkp{a5W/?qʓ Eٿ؜v'/z>\`?$~ǰڜ6dpoOu'_O|os{oH1a/ڇh|mIxMWÒ/fml}4p|ͅ{ҵ?|6Ln'Vvэ ^m㐝|v[bEq˧_!}߸ߖX;0Pڑu1@S-&c-~mYkX}b>mc=mu|U3Ӧ\lM8v>AƮOo<9q܌_V5g/ˆCa/o>p1sSэ^JZ..ػ6&Hq2=O[Wշ7nƐ=p7k8Ak's6y7bƮaMڇ}%ܯ}U^`Wָ[rrrF.b ꣦3ھV<هZc5t;v6* Ӝܿam> %ׂ#kw~d&wO5|ۇ^x.}{6f#<׾v>o}7G|1:O߼4G}W'5ڼ(]cgܻ?g_ިoϻd֘֌ϸ'\l+S\mةVԦkA3>0γyϳɣ+[|{ckn`3^6 | ܇]ыw+% ^ۻf\\P]s70(wqֿy~N{8N-d\7P60QG ؜-r GmŬ۟ÒcYh'OvV_cּ|_/^}~+;]~/fWqNma&{I.8o cgO؍;^S|O )|sll=?ښ5J_ώ8QjѭK}n #lŶwڇm;u񰊓 -}Tw:\m?>h}Fh# |p'\kx+p+T7`Y={[MSߛpN{ڛZ~X'-v0ᜍO[lW[}tٞpvWb.n8 ?ىAvscH>"_Txqp?\,y7OO]ӵk8k_lccLuon8g_zk7m |Nw㿫f9O߅s^y OΓA(vuXZza%M/Jކg&kl#'ƞgJ߼_z++p?vE/ޭ.T嫌],[26]Yc?ḀomŚ TcgX/.!Z(L>Qy-_~Np0gG6nnQkQgkܵ<~-p5L8F!57r_D58;W4wmۚNmdv9nʗ_;糱4kO/N7O~ZwͫrsLN0t,qٷlOxg_eM}w^q_^*0{y(t+>lƣ96|϶:,Z96'mh;cʞ^ؤ?c k[̓O.] | ܇Y͋u+% ^lғeڭNEreb3\n 怯{|oPe+HF~8N]S^ܕåƆ=~biatr6`E<KVɇscӇɓ5Я&O% _c#^yym;=Fl¢7|dzymN{%_C^g}>fd/GiLO8g6ao 8;7'3r(}9Qx&pgwO-nw16+*p~J^[[[[[[[[[[[/@'_tS 2 [q'CGf}ۊ}XdOo ׮k1VƿjϖolӯM>!Οb87oc7(ѭVq~aS? ccۖ,9 Ny%+&iXy>KT[[g/Grxm/v;Q>OϮ1qo-?;{ۭ} ^Φ{N{p\8_\madkfFh7^Ϝ-|vۧ;[1|6|6>q凾/vn8l8'橉_(퟾F~->%/6guO_rY 8g<{R~}yfc`qDg1}q)Bx5bd>'3jr:-7ܛdưOv#FqV~\̵ᭌ :?bDO8|X+?}+fO8ٔCyEzlw8d{N-l*Mz+p+p+p+p+p+p+p+p+p+p+p+9}숛ƭFCFo[[[[[[[[[[[IɎi |Q+`]Ckg;]PڝpVA:mZrvA_Nkllnq[/oLf6'C;W)b#Xx:k`% ]kn@>6a>æu:ﲷo}Ŀs?c?gx\H]}M+gTz6dl_}r}k}y ?I_O }yx'F3IϺ߼an77W'oӊSpֶm+] -6,~d-Υ!cW |*YJFŮ"WABW>CWG/57x~ONx{q.u )L6ϛIǯ9+ztgoPsek˾*b80 EJC/9N;?-W2|i\b7a˿Ay3OݗxmO'&^oVb7:. Q;Kߠcj?EY̗ڙ<͵|_??:;Wðン/䎧m;.q8m|yow루vg 6lkVuo}hy&›ܯX|-]~:NNލ8~g~gv~8N rՉ̓qy/9E᠚?N6z[t |~|!'ϢVǥ.69ԅ+z y}TxBagñ%Z>l_L=? ;=l~6mdq֯~بo%;i9bƿz #|֏n.s>uS|(|ƑѓvN0;{=mN>|ÜZ~GGӭv)WQgʊ.~;g֏lkq+>k65}\មʼnm}'o<ٟf>[г.]߶>G'~axk&Co,*cAMP7 *~VVVVVVVVVVVDZ?6|=3]2׭~wӭg]^T㽱7=Η5ZE[e>pƗڐkl7G>ϱv]hoz#4$r"|~~,0/6 }l7|s?']˲[rzi+W~rI_Ng`M4ƟxF8sO6&= boK^q. ~o>f~l_A}k/9?5't6 |*c ?^xvflױ؛Ⱦoᐵ{ss/wlr6(xxᒗbӭ~8_bQݎE?dgͥC+> Ë7wa۲ҝ:oʅ pѶ/~t_c1d8^abC<_k^F}ʗ|wO'N~O֧-q5l_8..==/nlZE?k2cosfe٥[ھ%g>/X'_8ɖ>v~l_KK__7hVVM\ܬjgKy1ͼ 6]&zv.Pu~+nvN8rր8LOke ߮ RE pv¢?נyv 4}݉{ Ʈ!L.OkКSoL\(zwƼʏF[&UOl$126}-qG];Y{8:nkڶ޵l}G 8]w;y]<ƹfVabu[6O~.N8x{rpnS>6o}鼶o`vv\x( s |FzlAR'Nf{:&_ۿxw\ws\uqEl9.-etɒGӻMr_ G.l3unǣ|;qSAz [Λ"=ausnOxeK3y #k<Ѹ4w跥G>)iN|N :tmtn[.׷o>mʥ5v^^ٻ]^DgMr j($ 2!M(Mv*z ܮ5h?k{g{5:kOyꂟK?|YSWadF|zY]91lOx[/g=קmVE1O{qQ.,ƕ)?yX_(+w>xcn=]w>oOk~ݏo $Ν S.W]w +pW]_ 1777x__M//Jcx+Ayr>]+dĹ>E7jtp'g>/+A5$7}0W]UtӗSv- +͞FvSUh}z-|ugmz)F}ᓡg~8e|N͇.~S[41CW5ʦvg-^X_uZ>Q9B.,.)\\í-~پ._s |xcn}}oO_v,7ǏǤ]'J}eܓ׵k>}T_\:Ts#On9.p sܝQGnᒻO;Wov{8ӣ0Q~KxPs}Ҹ5lOqȷqwn7|:Uvnැzh\9}C8h۠>?sڼY;uzعl]cȅKv$|^s\p:s/w\ч>Fr+{m-w=SƐaj9ܰ6?U跆5qw=;NԮp֯ZՋ]'Va(iwmޘ@;~E94/uw NVXv"9N.*WPgL6>qI=olMW썿>/}|24^bfqNcw3b/gW>~W6v6ƟpԢ~x-?l7rdB9bB&M/NpP58{M>l7l>S;jꂿ9K͹xփNe)~mcq;ƯV4S\,yS~-lFY2-y g]wҍ_ 17ƷSJ>w+pW]w +pW{oM:xj=ۯ=w+pW]w +pW{oM`+ =ޯ;tn1_}{'M +pW]w +W ɾЇ>`@sM26A5AM_ |*si oDͥ`oΆ?Z~/܎ EXlZ؝>እɕ{(G? f۹+dZ~LOOh~;&+,2yn[;~y~ϯ%P=bD=vY9~Oᐯ]Pr'큾7d5rYpgrm}Clч.nOaX?8c)X6)hRty},dg[_% vza<5+V.O|߾EoLٜ>O'3d'w:O+*~'~2ܗ}ٗ=n؏ܟso}~^UQo 8uBsrۋKɗ }< vq`軀_ tSFr_1c U>8-O='\zxl~5sovըxhK({uϸ}a3y'ɦ&m=#?r槞'sKu /!_wNzlՌvVߖlPyi9f̫N0õm38B>\v7m}Uz^G}>=qXW~\g쌡xgw}kti'N鿺r#3nd06mgpٞi}CqȭճOӫ{z2Uۺnlx85{\I.N1-nv7+*p5*_FUY:I znZ;.y eLuW\'vُNiDO_ưƔMhzt/*k>,ճ9Że+dֶZaqWZL6&;qw;rvmi,ůy9y燿g1xg/V͇ag-ܓ=G=7;M>W~ņ!.> _Ѱ}ӿ.jy7+N+z /ſx<]+导~ݯ{KKOS !>;-mW]w +pW]׭oMN^3t>{ /y=WWO^x{WW=,},ОrDwey:ھK7 jӱ|`N8NrʮO_ ;ZQr5+ߵVdqfUQ^l6̙ lQX|OnK'G_[_^[5ʃL=Ywxgᗫi_NRlc.p/Z~(ѭ_1/9g/JMQ8NˏOv伹6z&?鎽u^^ˏjl[yz|cgwsmOr9me3gXoWT'i:H~nKձD|ңWA|m98ZygN1}ˮ%M |:*Z~+3;᭟y\y(yW]w +pW] Q7ZxMS|zGxc++PO/-3EWzɖ]>3٧|[vtۊIVb~|0Zʿ'~>w76߱qkk'7lCG(e<y Q'm ^4lqWpvɥqg9c׸a,ggkᆵ>򊮟і6r_ ۸o4۟ 8nqKVn[h|+=n$_mmMqnsZʉ<]x~g;q#ƳlvZ_vw> xcn7؟M?+@Fj(;?nw |pr- sbsrd牓ubj9w~r9W\uĝ1[\vϾx~6gsf1}v΋q{rp[/s y­7;?E }w8n'oMŋENۉttNb5* ycxV̾x趝|*Um*V0١#/ݙӊ➾˳}*VbnXW^F20ʟF>Wz,'>;qҟ!Я]~2Z,vsX Iw%K?e+a.ૃ>]aV& d0s]l <CrOF~?~amƉSɪG9MjW"|/-n"Wdnf7~cLm9Ϸuۺ,v9mxQx|挿Q,| SΜoo]1kw>xcno8`ˈJvPz@;ytƼ+pW]w +pWgR^j'+ns MO|&;Kzr)_ڋtOiKW?6Zzr~ɰ Px~ˇձ)r('vt+˟xO6v8c8gOl,/ʃ_z\7ӮqX}N_-N ZKWeL3)|Aа?Sh/-ͥnrvX~ʧul[[՗k%Jv+~⿸xe GeG.g.2Wf}AV/Y!׸ഄwgJ^ۛ@;OO=p??(ȶFKKmXW۫}ʼ/ߜ n~9%5;#yAbk}Csa?Ic;xՀcgi +Oܭ΁q!s?x8qɍ~[c6?>;v³]+Ǹ̑CVΗ7acdՙlcS~^ke5~.`uՌvc zqWzo+>{/,6Ƽ`C.Nzx}//LS?ZgƼsൄ <ǍٟOcbzзWqĮm?PӶ~t^0 ZozFaDZnj;q8+9<~ؿʗ9oIOW8 xYp˫87++ԫF4R?wS;Si8~/yM; ut jjN :8{1Ws0odlog{] u8Cms,d(Y{7m{f_NG-r<OZ'~Ƌr 7gxLϴ>7]]vbx[2‚Bb:)쓉BVQfeclu3]77wB76)NS닟moLv,[v9w1Ty<\8j[n[9e'oN~e[-|/Gԍu<ƞSt?0O[9N:2~[m跏]ϔ 7Ouoǁ;;;BߋSǾqoַ苾譯گ}?ӏ'p^3<~e?sC{Î~oߋ|W]w +pW3M'/=Ɍ'HJ‹|ʋ$'nҺss^U$O||:Wz5"뫾꫞ߍ T{,w +pW]w^ 7oG݌|w~*7/kn {yE͟Z_?=x-y%M}=^W~WGy|?y[o}t_{{x +pW]w |Vൽ ʷs'{ oȫG67}^://|o}[At'4lx7}s+fE܇a+ ?z~K|E8եƔRm6/փ~ٝطwYd Fsҗ |>QУ[W0>.vJ~_;ܾ9#50/ҍ/׎m[~--ɯ縒nَ.>kW{oVƞmI-'<ȯN^O:&/qBoKjo q~o{un._鿠NR.z1'7bNrl_k~']t]@1~{EXx %ox^5rLN14p/΋(|GmL߱[ κrC.rdg^oqA';~˟lck|c!Sㆱg=fSܵ/>jۨ|pOWߓ]_wtmᐕ>8&7͎=ևl_-t\>ʇkrؖ`[Ϩgw>b\_\~q>5%A?Qxxb-nyuxzꙝ19δMSp k١+ˋ9kggFtW]J=WQ~jCӵFMpR}}8zz8;9QX\HӾIuqr VL :;(:vQމy}Jz (lj=KۘyR -.*G?y˄Oy}R#K[lѸPquՒGoų'rb?m4;4d5?u_G˿8x&9;S'?>Etq_dwc[ңdڮ^1W~j網>-lO\UxΥqK% m8'e~TƜ>F(|rZI- lGlWG8W/jƄjW3e[7+*+ ցU3ˍ>7ρMsprh́Mex^uWDaooz`+pW]w +Q&76n<=7}g/zjs9\As6k^=eRA)YO*ՑV7XGnݨ4 .xn4]3L/SރڇlƷm-.=18jlN+޾o<~=F;ڧ[z>=mOlo {'h>pqeVNl*^tW!珮^vل~+gsaO~ٝ~ɯ9>m_ɦ/7uFjk2=.\z8>Ǒ~=m>ň^V~c9mVo{7dU鋳dkS.zX>[/nWeroؽM~C'_|eu-ֹw|qc~8sUM;ީio*BeO@'3=qlzPщ6žfX܃sV*o<)>#Wpb5xMX9oxB<l[dp'k3\t<|ⱧX\n~Մavc WݏÑz sCa/S*7m޴9yT–~@Vk?@BWm;ԫ78dm}!=?zc yaq-qn~~蓮S"^ۛ@;`9X?; {pupyAŎ1뻩rAnoz|Ct] v~a.]t$y⓱y^|~ꓽ &+Y]tg\v{·.ܥkՇ][v=(]\2C޲:b,徲O9F>#T3r'}b۞x哣ro]=Ǯӿ]_ 772ǝww?jomv@>|W|iynozsz[ЇO_ ϟ؏C'|\ӹY􋢾ySNXn =5SG~.m4V;Xnw +pW]w~9*eO?o ]gN tSO hc57 iL_n5.1jgr1/ɶ-fr[N]'e's_|ӧ?m}ݚpBvO>WF=||RhK6XytŪg;&-g_xŹQkg>]'&~+Ϳ7,(y0zo+ Q?@T-yz~O7w梁W3N./kIbUvx vEm˯h9fϬ>獛KÉN'jÇ#gsZ[ߛ1!YY_,ۨXƉjrݺ‘Cq9]vNt'96C04CZGƳMen\yOÕwcray6sEçx;Ny.:9Ǐ j{fP;ȭי?TC1L>凶 6>Ml@]{~ܹmngf>2tqv{R YO:.6Çɶxܾ?? Wൽ Tv.\ ̜:("v=']MM*fOO>?S?!ѻ`X7ިvM ]|vֶ]S|vW~N+ynhc:mg0W| xe/vىc˙S|Ta!xpüW%'[|R)Ka};vy,#S80_vAY6NEx^i[ld岲_,1|+{|f4pOV ՖmDy):y5|'*n8/וȟ^}gμً߉,yWMN剖\_S+zu Z?^pfϧB٫ӓ'S~֫rߔzZD7^{-7)|ꧧ-Ԟ:Fv맿vNOO^j}6x6@_|2X?| bS7Zx7s}A[V5ֵowkQ,fȳS8Q8|u][>-L2n/$_+\|"L7?g:F.Gx' 7dk 'csʖP NlX5鳻]wS7&͟ob>?o~O6߈5N):j1ۿ'H.vLܾ Dw_ֿYpş]mWtr- Ծ>z=۹jmaɃR!Q4k('Ehxg/R;x`Rタ_mkiQ~;1VlTp`׺/&}qax|*^x^;?q,:|suz<9j9sNB5q<}F/ZvnaDk[dWtssq/[GlgՃvpm ٹ'Zhp]?ҟeo]?q_;\z>W87[~GY5,8|w;}gm_Կy|Wa_?mnqƆA8W[Nv-[>K [v}|ua$ۼpvUNUCEjt9o>%k?'׷?GEw%+|||/]yщsu7ƇOdzFw|q]6|0[S J~<}cYկ\Wϭ)sg+r^ݕ_brISy_d'VW/l~l.W#~=;F{o k&3EV>ɯicnwݎČwb+y8>o#o!S~asL[|o܇&[07mi}+9{`~.Q[shgkv`e[y}w|C5vW]{=K'QNpl:-=zbﯳyŻWk}1e咟@5z>C8aƙޯ֥wUauZ|ϺGAWahxl ZWp gCLJ#*k2NB_E? :uG~m7&tW_ ^v}ѫٌxێ[l|_c%_a,mzE라0P6~py'R /|Os8W3积>՝|AW6ˋx Om/w~Ƴ?K e/_g}Wʁvz:qvx'8oszuTg!:^UB_.orc+[vWVw+'v|O@~ Ӣw>jdN|ط>>>eo..bǟ?X]D]lvٟOom1L.fǴlȊMt/o(ܫxWz8|a}_Կ'sXb8mOSԝ{/6+;,tg͊>8pa;oRXS&yyX]W/:g׸7}*|ZT(8؞mas_߱3Y]U~ '}(pӓ$C6ͧKhYʯ\|^M 17/?j O?x_h!J=]w +pW] 17PSXU^>2xM ~_x/{w^uzb)9}$]іl?>!}Z$7vgthOzd/';>V ^\n|çkIv$a4ۥo&K0ڵUtadlQ>pw^eޘ@EٝoGr?;\;x xgy[h[ +>ͅ,.PsSu c9W矋ŅQ|{v`n]q;~9;}w ۝[X;WcWh ܹ?djՌ\ƍ?>n_Msb9So>ˮNWN~AdO"cW#߹Ot6lٙ.[SEwnp>uLN?kDoDvhc=Nl~j.9drh÷G5NX)&=ިY7g(NJ昐Z^1|7>k<)^'mVxdGßq%G[fy͕~qN{|cNZt_\º҅ɞO^=ig}Ong}'V4Ћ_L.ڎWRf9ٞ3neN99A_~>97Fӭ_:ma,]?}a.7Jص'|._p]N~kRټ4D'y~x,iϻoMW_~G~w??UПy 9??|w +pW]w xcn=r^͠@Ozbe $_ٛtVg?מj=E??lPOc,Ԯy픋a uvʁ>)/=r #Z|k)nsq2>L4d_`3\O\Ɖ>uObF->eNnbcu'Ymll-{q>lwa5+mW-Phc(o&{!?llS8+o[V׏eKs},][YvZc][ہ6.r/Kٜ̦3uğ6r(t8NtoozWUV 4OỾ7wǼ//{̛9/:~Qnw |pG?xImO|u\0c皝'0z;>D9,;DŽBv{JFV僊CwNNQq%4Qp]y9c߼O0oXM;G927mAO~.tsgߓ';t4n'm>UWMK8Ρbw5gܯmZ5@[>;l}9 Ccz ևmWv~~/m4ہmLW/\s7:gEeR~vsٷ>M=u[q'V)񿪻q#:zg<sUn < 8ʆ 17v^vXa}=?3V8;ozWۯ}Ϣo?sj>Ⱦ.J³. J^hK\C4f150PvK":f@.^ٕSlic?eW}M-~9'y +h~ n٣]k=g^ٱ)_T~c8~>tS8+aA8Ol[^묎nzg~+6;65]>ߓ6Нl۰0;}gg)+kח &|Xŋ>=~e+mqӻoMؙ=-_OMV+pW]w +^s)KOx'z/g)~O9olOI}OɊ=_VgV`.>OnÇ<[ӿ]z_ՏeqZ>]OwZL|]w |ŽtkHk\#ˆ{7,NXWz8A^F6ɒaF_Vnk_'_';%W3g?0lpAk8ҳ>^-.q}XrK8)0c8i:}~]V-pߖxxcn}W~U|B;Uƺ+pWى.خ18'SIy/EX4ףFnNGz8;+EloNV6{ ߹QِH _.g}'C]̔Gvn玱Z#؛8ju^Kp.y4sո֟ G} j6qВmssJɊwCգG)ɡwuzWm|۷msx%6W͸jv{ `qݺ7L|q-Td>ĵ6^O'.lsamxI? G~+=YvpD_͸w+{w};ǭ!{ӕoBn^rnkw 17ÁC`r&]NV{u2 $'w,Z'(ړi.Z0{nL磿Xf,ot⇑MYcxŽ2one9Y vd+#%G.[,z"ثk~h3gM%o!!p6G~<Ӈ.~9Χb8r穼a9U[ž}^]v_~kYpus;1ҭznaa$|{;̧(p)ǿ:dwI~ J;\٭Lڋ/NyoߺZ 17[NaOS\~o7]w +pW]w>+O{JOt ѿz%3}e+pW]w +pWV 4囿}[}ʫ;]w +pW]w 17|}L jm -9Y$Ch+s,ҟs-~4aa7G/V;7$,-+O9_/׮&aAOj飧<ԽU>tZc_*崁=S{mtmϲox6a=߄n6+_\?W}Y+o'޶fǹC~zSN8`.ʹma^ɮbYy[O}\u=Um>laѭSm'hh )O<¨lv_kloO_8 13=*䛳>ߍwmܳ#}Nng0 Ƨ;s91_fW?EN/|-5?ɮ+&ٕ>qş{+z+&~*[~WSk{{{o;筏|#/C|~ER]w +pW]a^ۛ@O}|$ɟ6e_ݟO^w  O}jeYhk~MXℓm|>'OwsoFGdkfe)N9cǶ庶_o%$_Cz3ƎCu&9zbcvj\ʶ>0vZ9*lR>tg}tb%K |^wqXp)7 j1'12]{6bSLqko4vƬ_6-xY]4sa΁pv{E|as687v5.|vN~wuG?aW\u{<~ܞC&׫i¸7!,Y|ۿõ~j _ۊF2a5$:n"Cx;W6lI~}@wɧxRa]OMG܏l%:kQ6ߕc{ֶsٵbc?0=>gluo'?O,$`sM .vB؃ċlo]OW.2cOoq]Z_?}1vc{ է/xgWL3bdiKӏrgϏ^ъ^ʎl1!t <+N}r÷OuO\&",V.Wts磶^sbgֶO$_Z|U/ݱXk:[_|)±'yTNpF} +{rɏr^@IDATڕ٧Ҍq91yЕ?'^8+[|ozWUVൽ wE}'tOj)[~W]w +pW]7M'$uw3OU\2ro8辶,-NtuG 6?Ѝ_4<%rq[}>hlb=%?jc~{§Url'] s\+N8=[W>'㷾Oa퉳n8xm MZ٥׏f_8μٮʒgf<,'>{_WUTൽ 4x/UR*+㶿+pWY4;:y]cp2 d)Ao_s3?3/85wy^xsm;'=CqjW9n{#D.N>x}-IjC-rsòV^`߸k; 9^bз^0o/8zΝ^~¡?ǻ9=j9|v8!lԹ'>|u%//'#SNlvgwoB?Oo=CrOսFoj' ^f6U]Or g Wk5⣶l|~!b,;nx8otk{v+.zO2~EՇ\lŰm/}/>{Ewu^ۛ@;cşiGv;iw >E^'x٠{I9?= s_oG5>bv /8ٳl/ jWWvdvkŬ1ϋKt"b]v^7l'cҭx[ )*Nz6jO?);}ǔ18ص 'fwEO|Qb݋YNeO~gٮo6нɱ=iXlo'W.)'6+87_>?j[I}W6k 7vO?H>w +pW]w x+zUΞg}Ӯws+x>y>ݧO{>ѭ]OeTd?՗C8bo?2l| 26Gɷ;aDvە-nu`X|{n8g}ã/&bfxJL3}E>lC7w}Tr˾1|/hxJ/3m8l-n|)#[XMe)|O{0[.W.> j٭Oߕo] %O!_%/r#oYb:s[ۻWUTൽ -cvvrev.svW~\{?tGv ~4e7eO('YsX;uCgu3lw7y{o6_E_x9hKn{KŨoLi>lT;qٷ< >AfNZw|lw{'_Y8ZS?7Ց+vWIK~Det?ta]0j\rl^7֎~`Z僞P.QNoc~گ\"~'{q80S.nwAN ~ xy~;.6Ћs?%69wʳhm'9qwsKuv87ǝu^o㿺NT=]WU&N`[CsO6XvP;f}]Wٞh!ٶѯl:ngn|}]ٜ޼eÿ r7'0,+ oqؒ'jqx]Xպ0P~n[]/|'/KV+v|%_Y&J\߮l}y[ uS tkmq/;>4<;|e"o㇇E8l˯<-Y+K7{VVO?>k9 ^bx:~qϸ|8dZcsomvw'W+A~~3(%;n?$[[O]~R+pW]w +pWoM9~:>9+J?ٝ~7W]w +pW]7oMGN+$_{}?#?M +pW]w +V ~u7u} n>mo]Os)ؘ(ѾQqȯu2v-0䲾#;QLm.ϴal-e |#>W9Ođ[[zt^O~m}24 X۪`C.uN>|Kg}sia/ȳYM8Ov6mO8vn!p؇Xt/jk/?26g_|v0bn<䷸WpG'Jv֗L;-[Y;Uqà+0~d[島)M~_ۻWUT TNOevOw~i\X_NbY.rW؜Dܞ`;!qj~?#!v#xZ~5pZ?\lrz@Whs2WÏM5:;a·^M/\o)ooEn7}3ޯ7fwK\9|QfXj$Gw7ǾqÏ]y°|ŒG{%cKE\ⳃ}Xo:l ]mS _~>α>N/,<]Gᆡ/!:2kɯ8tWGX;>ݶW:Wcm=~Boq'Spˏ';_O&;__e~W7v(/aS??~CQlP7+*v0=H;X90]WWT'EI\c]ONlVd'fX;n=/G~y^\0p#31#9=lɯ-p8tѫz~pOl¥?exqqBW8QeK/o& v\W!/9~drGoo2 4|v&{;s) +]hP;xv o7\z7_z|vcP>גI_r-lBVƹ2}+goMz__O7|7<ڙ->z.O]w +pW] 17}~R|Cz&з4֫"^_TxU'=4 keF*ٶ0C9ǵy.Jo,6[ve0l'U-U≳60|Crsj Q|F;y3l鷰їSy=zueszm}GOvb4[ o}:]'f6a g>{'T.~?VU0&_̵oYڗ涵Iv6nKOm6nq@yQwMޘ@?G>~տxO[~կUo- }b޾wkpo 7 w!d~+;n'j;N$G8h _#>ykZ:/i˿˟Ise&FrDָ99ֵ^?qn򒋵sqJYVkz.qK/]'Cɬt'M0=\l>p8?p'}N]w*_y{u#mmbL~c/٠m>0WM~;欿cg>b^Y3ޝKHg,1TMG޲8W;?]8u7qsEä\T?` ߄߹ [<0ӟs Olg\QHΧum㫅kg0Ol:?nsO;<.B ^E!o 8Yu 엧ed>QxCB'q{=ʏyѹ .{)ޔE{DZpPXyoodϸO|ZÆqP;rc+kzg}ht^ y>ZS_~>ṗg[Z/̻|^?W|}\j]t}maFn1곯]djێK,mkhms=e;qs!'m ۜO;g2>o =wSqƣO֦bjޘ@;_?W=U$>+pW]w +pWushxbqs'oPOb}-y|+)WSTKSO\`e`>A>뷰_SٝOfחn[z>IƟM}}kq9~2Ɨ=i]#|b5˙Z}ǩ;w=d/敞쪭_uMw'nx':v뷸/?Kѕ9lGˁNnѧV)8WG[oL7M~L]4=r"ӗO2_tDOXŃ6߸L%qqֱmS'9#͇mˉ2}v7+n*E_~~P??xE۾Y~1Я{޾wߙ[ۜxiz{:0P;19Kzୟ*r9{X?֛s!mj@y\L@[cp9eM?07=8YAG'FY7s;NJ^' GT'>,'(Pi;g)Pџs`_A5^?nH.vȭ۞ݻmYvv ?$ ,A !B %'/msn~5{{oݾվU5Θs1ZksO>֓y8k8Ne=#϶pWdV;F`5ƻsRc?[\W |̼ "\l]h/N,/0$ {9o>pN\p=w^7S\Ƨ-#BŴ|mS=Ð;/>ts'ףr-ɳ6yawslk_oNڹx壯s s7Ƶʼn?F\s\U}oܼu/sϼ}V;7exG?$S/Ooya* xb));W{\{p}m&'#|td g˟M֧5mfwu'j#S(^%w#.uv>…Mc~{]?sߺm?#kXknqmÙq# v_>pg=O5޶c=m+or5Nqܗc:1_{@}I_aGS뗓~~yv^:קV錗sN_xc-OVgM د?t'N.= R |+yeN:^9_;smF69ʏ|3'k \ÄK;/}Pgj掃~vfdmwMcy3x_*ǫYqv}<9װ+|뜛b.Ʀ>uN{+oVh~[:_+7"^n~ߤ/?qwxucTs^o;afzs?'K‰XߍvIHYs2ⷾlkg1tzq}>IVngl5mۘӕ~|HQs෵0֚rkt'nD?tl'6L|.>< #poؗyk?Vo ϯ?A֌4ZWt>ϾyO'G:jG?`$/SU]xj~u<˹Y~_h?œIq<'+#xԇHI'( (3NX<%S}_'Kk }O.z C:k'_dwuLd#Ǽg1 7v??'8}vuJ'l|׷\H~¯,#ldqĩG\oá-g<C}mْlt\]>gaNh+Sƙo'/l{ӝGƧ9rw|r{yygqg|0a{osky#;=tsq_X_7q-_9OHʑ8agX?qw|+* ʯ<˿_~i'^@~?> 'QoVVVVVVVVVVV]M}?|}m ͛@o}B#z>wqݜn>mxn/nm'5~pz]wqɏ|PDwGpl^m>)_c-}k+I0ϱ9.i'=G.ӖO=NNs:g+&Iğc~Zj\`:6v6OjLcKoVú痾|%ſ)~rq$axmd/_|)^1O=|pWS]l|=egla}K-G<ɗ5_wmoZO@_?GOo_ ?'~>WOW>}_yzsŻ~~`Yx3Ts5f|Aifo*y ?#9=zǾIpۣ\֮_ȧ]נi }dߚ“|X=c|u/8}Oj:72k^G~gϮ!pͻZN\xѷő_ٯqQAuOHћKog߾>yCc+OcX}:5^ʭ,Ҷ7y×ahmqO.fC7}l~ۇyo}7f;6opow==O '/7 OO),x+}ɍ7t>RF7u>P_WU;t+i@OSϧgSo˓.<1l;kO^HOO}'G$].#yr9fƋaxn.]|+^2/X>s~?y#=jlq󍇾oˏ>i[FW cD*g'ώqM~ߎͩ?{kg'vc [ 4Ó{|\?G?qSAx'̉NGm|vj]]3!8S$9cÅM. |jz?<,+ǛZ??O'sW+p+pulc]Λ"^\7\8nx*m :}_/&iac7͌7 ޚwsUS]-]A$5ΰ)Mkkv>ƵuBV]=;ܾ9.Zc}SLCe[=kȏ}3|w.?y=ZH/1G{y1Cۺ3&5xWOcxj֑qs89O{>k Wpz/oRƛyrH<Ϸml^|wwWQ>r^~b9<Ð~۪M`'BiB愂Bɉ[[o]^u>9ӹHOvY|\@<xvxGn~g|ߴOKfwc7So֚w0yR^ >y'qS?;o!M iofq[nm?#)~ϣIj-)F|xm0|ԪN:l~ƪ=:Ɠe<?J0s<9w72~[meg0,\R?|W+_sr ֕7>[\qW=tl|qqٯxԾ tNGzSG\Ww+p+p+p+p+p+p+p+p+p+p+iM}hq;VVqzk'$7n9x`aog,OOwsïqruϧ嚝\f/G~䓎o|Sw\OO oeJq7s[֖߉_ڶfΜ'\UG8{˹ɫ\k9-.b%կqrxœW |T }io_OUr[7b W]puc˅Žycy횴xNwϝ;m}q۵i7Rɸm ֢5;&l_=yPnC3U ok~}u&xɦ_I: Yy_p'qS^'myqx.m~D6xzAzUXx|/^ox 9䂶u]H`=\HF#)G✯ykN+_&΃ױ0d[0xvOsGO~twߤq\i7ggI9ſpny~qo/O0`OI$Ӈ]U<-0}c|##͏O9O5al3>̾1OZjٖ7ەo7 Ioo? ˊE¯w |*})~$Ȯӭ7]޿Gjx[[[[[[[[[[[S>o_<wb {XA욍/G1V+}?8Y?s (np('X-;_K-'Cn~|W.[6G9g)r~>xO03WxƎsup˯KI>y+Q?;&:bxbqCq6t6NGO;o <(~x9żiѾl {][US&e[[? u}R&{k麀 _Oj[u76~{_\5Bo/Cހ__d??bAoE/;ÞG߯5n\ڟ7ơoysž9s2_o):yÉ-ˇÁ滿*$j&~kqpV?)cܖN͖˯`O>/蝏'xy U Ϟw^vű>g_9m}^%G{sqV}V |+EO)miV.dmH<|=W~cz8N>7OX#G-ɳ HYK'11/n4_^湼H78_w旽[io~֝Gy<\qyQ\~g?|7l|}֋/Ca˞ZY||+w^u`o,˅N6|VO':>I\/ܕo>z[VVVVVVVVVVVV$57[OgΧbn~'t_8ˣx=AN.f8l'.ƀ)>}}|-Ɖ Knliq8_qhK|= ӱqÕʗ}xԮZnsضmlstxkaVs;låntڱ>Ɗm|8smWdq;_Y]'[/n'Ggm]k{4kkIoyb"G= V[&[V3WkJl]̿<㮽pa5eF͍sp^s\Mq7 E\] r5ǕNkZeO&XZmlմr o OxҼ7Om 1fNku$Ϻ\sƆwcGkv-cZ:R3#ݼw͗ܵq6o7'?tZgemgNmvq_ }89GaȚ+)FGȳI1۷|˷|#7:s7\{Ŏ'[:2~Օ!w#įy <ɹeyk'ͫ~GG1q^OcrxgXsy=?'jIMx>+o޴) llMwF.CcpgN≳z8=-f͍W6|,-N-vq-d4\]xoe|IFܷD'nI |:+WO5=Ŵԝ>~}bs⌋Փڞ"˷k6n3~W<N~͋i~|M?xXm1 q[xt1۱''zRdG9.Dy4ϗ~<Oιo,C>OJp4'8sđ_[=yOa[0]T |][[ HZ`=F0kE΋W.d7 Z"vk)|bkZG7۵B15smq/u~|fi]R'oX&@ w[GVq qv<xL4uf_XӶN>66.L GqzKkJRS/| OI?w $>x8`m-aM"?:O LJ6?>80?תoR C'kH qq6?z‘{}c}JӚѪۋыCsm1|W>v^=i+Oٶƚy=oa ˮy;?[c}^sЛWX{>LҾ3̕oM۬V2؍F7Yl{SuÒo1ɏ y5N'鞳ö-|k+G6>r?sڷ.arΚۛ58<|HMnlg7쎝G.l|Z^goVVWy O?u&|+nU˞` IOzoا-?%Q~1l/'{47)=]c0l咝n[1-oN[_X_#qGn}gtdY=p,nkqݿoMۭe\UE;ʶk*5w]']}kFnmF[~#7}?q>A] obH )3=;pmJHg[ymϛ|w|gNxV{_I\ɽ}u!8[>pM7^z=ؽױ3v'_g Ŗ+_yx>ϼ5_S1~Wpƻ\ۼkn[ߏzy'ۊ13|’ۛ or1~Jp5.G:}ʉxүkuFW..&;c?v wř/N}rێwۿx[m1^[[[[[[[[[[[[wM;knboMۯexg+p&v+Wu d8~xb'|G8)7Kd#\k_G~ՖMoɶos$Vgrg:6yegyCpғ;Osk<7&sr-_^|meS>굱 k׷OW~uV-Ja;'Q$q̓N?7?mD_߹0 /bؤs8t;}re|+d(_Rǽ|q=x~WG+{}zU}__GoǂƶykۙK+1|~_|~˹78·~_wo=qGjcRӯe>)7n^`{|8FC/6v{݊G\7ȫ-ްI>oz~xؚ_+ov]w+9@.\.VIػH7.JZN'?ȇg9ҿL>7G=Nnnj\pqԏ;s%.z.6-|.{SO-^]wqӟ˵o}hWLGf3NԳir9< V-\>ٓp^&p˫_Ώ&o}~I ';O_ :>Ə|vamyXY:=Qou <ʩIb-fA1L2PgPYܸߔ O)eVܹb="=럻ZOO{k`|H6ON?{,=7X-b/OnO8=Oٍ7m֤~ܤ\7fm}ͮv|m/^;W}1_}X'\|'kOKf?==6g_2|ҟclđ~Ƿ+IW +{~MZ/pޤz.l}_~ggҗoo>]`I~oͿ|c?cO7U5'}#o9CEp.xaVW܋$n8O5>=ss6Ҽt57Il,ύO2>׾/?kⒿZ7GIɫ3tҹ~HE}ʻڵ:yGW[pi'5|=}a?D9/)bw<C~}Fţsͩ嗔|x_>0mWtdxٵ}yW'ϵo?'4?|'}X>:_5z׼Op~n~V}OKQ=Y]S>ߍۖ\ˁmOናy2[WI#x˕=\6F9Bwx:WIƸna'~Z>nkt klq|8V 7U3B}QB͜nz3EӍ[|jz[C//?Y>YS4>q0=3~||7V~{~k=:ad |x/;W/*^O7^Xnrxzq JO`yaÃۋހs tBY~%F)lI7擟 Ap& wm|l{X|\\$Lל69Gnln'Xrf.-2{\?V+:հ3{~qxï_c]aN>.'kO:2.jq/LmlK,d#7o~C}qwï>w^,νODz6rø N if;M3L<1p /?%?:oXk;w:Nvuˡ6=yX\6XK# 􋿏x WrC>(:ί+ӭdomq8ǶQyo>= ^(}'ӓ~;'Ł1.싻x><̩-W߶s׸H# ܯ_^;~o?n>oo{oB+>__u"^^gpb>{puڋfƾ$ ~]Χϋ#5A\"_}E \j8ί)myg͏-Ok8 ChJck骙^Kl~mg~ש\|s^x55@/ŕy<ћWy_“򩉷kgYyNQ.I㔋㥜Gibc}pm5on6⨁׀:}O5=/(~s &tdxhx]<=/q%^~΋Cqdmq@ngm]nos,5O>Ǯ->P,9;>uW1[[i?wZ;M_+UⓎt߉ xs1ӋB{_z2_/b^0W..Zl:q|t1.@%Gl1ccÝr|U&gu$>qWGh5q2 \~"R>lv^0qfWraIu򎃄#?|g1^~>xN.cuϟtS8/[ܦYR;˥|\?ql>];ǖsaO6v[uԏ;f79N2ka1ʏ܇5=聭=G=l8w$}[G_&ߔe1*`_xh\0c޽{W^,7 )_{M k ́/})O>;~'?S?;W̛Эgo_~SI'-~/>|i+)Aj;'Npy9|9+[ji[[[[[[[[[[wwg_|"1\>S>__zZ䍜dW/_OF~OO?)_~W?O_)o:_U<=u]+q߭TOO>}3?3_uPv~M7>}ec };}zCo}w:,כB ӏoӏqIO$P^}}YS.ߤ 86vx[+Iz~FGTQG.;/y=ʍ)և}}ܜOgƥ?H]_ w'CfۜNLؗIy<=lÏgùo7f,Wck;;c8qݎoɞX͸l|ۘGeOӏ;f{Ky6c5LOkYszoӑ5_#xC~3>_}vo9? 'ߘs? |'ӏxgً͟3_WC}?_~?O\ܬ{{~hv\R:dxw<$Hv~)ރ\T=8/D>O6lrNO:n!}([I59i^(GG';œ4?../FWԳgs|W#zA8jK;{:nn?|kmJuVd<]'| l n_Uwi;=W+~NN<_$Ld+Y|sm~s~}su=e۞7|N_HI~gH<;(+GϫxX?)Ư#YgVmV ||tSOG^p{q#|%O<{}p#EiM?wqwo'=XS <X{__ntg.x6H}ҝ o ǖ=]VK'}$9G1lX[>ޓZcxNZ$b>l5͇~Yl>n?PG|G&?N?)s Ƙ1 ۘx>G5Zqt/y>Q{:6/rDvk>q|7~Ӷq;u\ {gލa˧Glٓѱ؏W=bVmV |ռ\ h$:.WΣ_;7 .߳bIc_cfC>tnHdvsל H3޵'_W3?q5y,_Ɓ) A;jlMoMK<^}xڛٍ! C/޹Y4g<|4bgsmѹvgGwjj:>[gsM\+́_5kٚ_2_1yS! =vm|:XuǮ}\'5_R ~T~ xum[d<ƩbO\וw[H'U\[[[[[[[[[[[[wwb7$n>Բ'fSݝm'hc}>g'x9~O38[qN{qw8ڣa~T68'Ao|l=g?o ˸_/~{NW-VW ëW'丟N<<ombrcܶ11[d3?m='t~W|8ǹ7~[b/}C>dǜ~3o:œ ookN͇u+ۣ|76~[[\uֲz I7x@l.n΋ 繦mu.zwіkؗ>k/jg|]T,ksw#`ݰtAm9m֮ƄVWڬqSՕx}5#Up7v׎XwV~0ok6G%/}KpC_ߐ9Zs?xS(~_;9ӫnf׷*;]1uщkcWmײ?y>=^6xԧN^ݏkkŷk:ʥy^q1w|m~ck䇷yC[d93w9o ξ:<;>vZ1^>z~mK:O_\xk+ ~/je_Lf ]Gsnp{8y6y=boV}V |+h۲Ŷ%ggR~ۘ^Ǔ|(Nt99]d1^; m3N|۱~7vZ7aT=7O~K|6n,o95t=mx|ɗlcijxr#5>7IWÕ}N{tN|8gued[[~z_O {d|*JOPγI#Tx1Upug,6Oms?q&aytmo2$=97^xg'ݩϏlS?{t_|W:^k8ŏyf':'VObMOʟySM~lp^ޝ1Y~1Þw 6l5l'[c5NY娿츖xr?„aVmV |ռ\ XYKzru<8k6HpV< ZCu666|n6™C7D5Jݸ|_`G͇N.Ƨ/wLXLkY7}7{獵&ޮrg?v-|]#7o ]š|5l[T=,qkS]UMʇ3w%æ5dΛ˥n5֝N|;9׼c?Xs^û9q|8I]q[8_kfom=Gk,G oFS~+Yj:a1 {A}㒟xoArVmU |[< ٺ1srݛae}/NW;'#^<&Mrv7?O?r矮}~Q?s>4~]\7'g$ut_ }zu{܊!;K;g;uY?{4bo=}ms˘_swQ?xV\}\x8__' N k9ėos,^bҕOq%7q?`I?ak|FwnVT^tb6XH bx~۬]6ynnnnnnnnnnn 7 7oVVVVVVVVVVV]ޭ\8cεƭ~ljpԊ'p$L!w=FGkA)980/Zs}aғxs#ÿxN.|-{8t_Lֹ>Y~:rּ_>G]k[wr=yL?9>x^\<϶:b^϶/Ǜ|qCtMceq|w~S;mOɿ~q6ĥ.y6n~'&<[p[Ń~o?{-{x({d.gnFQq+p+T5⵿邸?MhEv-4xOW+q|$w? +|zμw^g~+z[3~;~Ս WEkjb>{&$)/<WGkEGj94GC/NvF!~1oϙG_<|9앷oMۨWʅ Rlt4o_vųrӿN?+6>wuN>xܛ.yxwR#?[7@I->xNn87ir(RM yIqG;6.38;芗|iO:ͯ8p9ܳo}'Ǒl˳3ɳxv83~~8uT'bw/ݫMK;j_s*>y!ŅIk᳑\mjsvЛ/)Gs{ۨ]6x9nnnnnnnnnnn>%˧$UO9m^DQuaWlb,vzz6vp3Y+}n_g~o>6G<˩z5=y̫²e_qò'w5:8!ïn\swmgQNxI8Gq<Ə؛9[|6tp|5FnV>󨏷-ͻ0,g+ltʏf}ЉS׮(|{/Wͳקjw$Ɠ\b'-wvr7oMۮUEZook3* ^yRC\bXSkґ][gt&9_7c4&8S6W#_$5@{uǕ܉F͚G!߯~y{7/G]S Sp1@8o3kdcO9Z"fgû3^yE8>s#k͎_5ۿyx·OmV51v,?&lg;|pqOu^Ü=G}H}\|?tUk&_k˛߮u4Zky.߹|óC^Q~mؼq|뵒][s/[ 7oVsZIۣ ą\{-v9MU]\=̻- Yҭ=r2~ēoHzs:āqo9#֞KO~lZy-VuYH-sRg}9Ρ$L k]t'ܙdKw_$94wu)-#^~黱w\>'Oq}ng~DDO^|sL=s^%_?Zoo5H&;˶-]b_o3rUj˖nnۯol}<_Oq|kq~gx֏փ9_u=s{~=cě<ß}v|qNͱjɯ|Ha}Iv 2j:Wksd?Zx']K#Gkck|}omj\\c;;?<ʛOc9kYG>cWhpn:g׊o~/X^l?cxr\SI8rcǏ:G|n^8;;>8̱e휹oGc>p§Ϗ}ont-\ .q;jg;G]>^5]߭1}so_:<#saW:N;=o|*oroom磞&7֏^<9?Y;_N][OM'Y}+9 Wm˦W&yt]~v7Y~:|Wtb`}#m̓O|8Γ7GW ?8׽[8x9yqxG\8蚋,9TS= }T-bOs\_ 'm;>9Ƶ1G?1?$_R'z k._|ivaV|򹹝qW1V.F-a7x)H|b'|6?3G5b\YdixժOܗ'|683r/F=xIv`7Nf#q?oxqւnq[>*fcmf+V A+wnnnnnnnnnnno;|+p+p+p+p+p+p+p+p+p+p+V A+wnnnnnnnnnnnb/`7[[h Ei m2uOj>ߖ5|K|ūҟsfc=_{VfMT?=?m;[qܭ}U>q՘μμ嫟S;>鴕η&Ű+|:RomyY{[N>Սܱ~[#gۺ]a5۶s6}<Sܓ70Ovͻ|ϧ<ȶ0c=ƶ.$]1oԞ0t3[ϻM]+#\-rݺj.v71t5U8xttӕoSRsc ˍӯfWΛf8qZ\_ _+nkbkp7Nc'~`%t=r{ J"MxW6C>6{}8{Ba0w[>gv}~54S<m<'=V~rַ`Cnz+5ys;3@IDAT#7I?zurL'uo8ԙx]J[?r=y`4/|mo>_]|ʗt^4,~5>̉l8hq5<¿O}:{=_C} }3y>қXn> 7We/]p.Ĝq>qv~99w!n~_|g+&|\'fW~Ik\ qԟpdd6q&/J>%׮>yҋcӷO?uC_>aocӞPݿ缾kdoIJICf Mk6&{;w;ޔӟ91Ġ;cq6W;>wȏm9^>>[/b)j]7{6Hmp6IƑ|݈+{G2ۓ| Ӿt-) ^T~/"*p? VVGO|h:r/~d}J7?ZP.l;/mIqeOˇXNg^u&MLO1N##۟O57!'$7~Qc7*Ns~b:,~cӼpc-b=kg r} #^~O'tL姉\;lg^?lgITɍ~m-dh2rG_Ϋw%rZ5o~ 7?L'*bk2ua#wm >{a sk\5Qdwט.ح'{my En(5<[g<(Uo7H}8.ov\Iy[#X8Z/SM~|7-n2:Yd/[^]sɟ_pDo,Ϯ Q^٧XM>ɧY͏}5ϳ5_R3{?O:kjx9v~W9i4+yz}8]V<ۿo/y _\3xO)p_+zw~5qyUylI|uگCo4_lr_V-+pe5/׭.]Is͝rCxj.4-)8뫿ϒSgZSڶ9wCB Jk N/~|,#އZ1`x5r`.7xseߓI>ybǑ7Ͼhߜk=ͻ$qUs} Wi?C|fqy%~vymsSx7N/䟍ڮ.xʉ^$Ov/7\wrv _|w}彾k\׶ۇi+q2_H4p;xȍg\L'qg~cKkkla|yIbG_q񳙻/ozvc[)xHǾ⶿/6s;? _xotqsH\O|l}]1s?H1{w}qG[Kw[kS#wq68O>g'q]<|w9^k[aV}V |+mҖZZYBbkx_0?1zkVxVlnڛ ZdzyZ"qw Eųk•/<ɧ@zu\YqNimb|uc@y\{~h'V\u^|jO[~oڧj|~_D>㫆~뷾g^^a8;H;!-&u*&Y&{<*]Lue7ƕ?i3tźV-+ݳ-Y/׭.64 ŭ [9/ndw?K't|0.Su'X;Yg'oMIcOn|ʧNƵryӹ~c'L0;ƧXk97_c-d{b䟄Vm'nglޖF;1t{ۇq,_|uxmv?[s:tᲟs/Exvf('L:=t+{đߋƇZ=y~o.mg,? )nk[wϾf|]'. |*k_syp't=U͞~O鶿g"4 n^I6sn |mM}gvi? W7)}Vy7d<ư'>#?)>{}ɸٓg}utapbgζ|Oq:r՗ no|~k{n&❼|.χnY~:S}`C~Ojr z>7x1>r+0Y\HDx1=ig&sgQcע+7iw)ͧyC[nteyзy?v^[w5<ܵjWgomk}#25^qil8xxsXK,icpy|o ox:[;W̓ܵjp!r 8#k'ݟa=Y/YXoip[Wys]<;'ܧaXjSy#gX[}V<_ tb"~ޠQN,+}jcRZ|W͟w„#fq[_<'}Nc>x6~mli'O99~˵Ә6^ ꓟ^Ws}#&{We\¬ds3yh;}cRglZ-o|z9nf{\㰍/ncg<[b׉榿Ų&On?[O:u<W_y+yTwS*p~T&s+p+p+p+p+p+p+p+p+p+p+V |{onnnnnnnnnn> 5 |*` 4[`50lbאOZѶ<ƘCxcm}/>ک/oI6c|k\rh3>[ӗ3w7<ՏܵI˳yawmMs.N<ưOÆOOxn_F>{opbf9;Ť7ͫx2nXKßv^/!dc/o9yӯqvM5 د͋OXq9;xkKtIşokuma^2fqg_gS~~a.r͏~6}>8FcoW |^[/M8// ¯\W~+5^;ƕدGj~mnlt&=vX\78 7ypaY6cQ.+z6>q@7_|ɿoWͻ_\&767R{:l|jWbqOQ.aVUɝ;;X=jl'Mm~'~⽯ͱy_9u'z?7_OmwyO3~u[xsUg>ǥ\<@Sx_ǝz^ ^cq'~|&Ej~w`կ~q{+;',/y쯡oïęs+oޢ߽RUՍv^*.]ÿC%ǛʰӗC~d[ۋgZ?mo"nqٖCL3>O];|.^W$n`'qf穿Xکy,O|aɻOe\?|2bt?Y|9w=>l~mxJmsf7O;7mO<Kی8x`][KTw}8i? ]8nkO23XrUvn> {yFܷM>I&r+p+VϞ)dϧga<OHÓ^O p9T>;Lsk;1o;vN0xg{Z'.mǖχ7Nٿ'9xlkMo,qNl~([nlIzW{Z1'kٌ=o{ 8la|Bm90'Od>¯=ɳ0>km:㈧4QSj߹_~9gbKtqf'F.r$o޲M[Vr |*"k]ϋޮa׾gV .te#׍-nMF85.Upy_b5%?c?=>p{RxW+R#5&Ss͖moVҋo`sv6k΍`-soY}x{o,>Q(rE!/ucnm_8oz6口 Lgu#!] űkp >$=q<֦/Z;qʝ]=6N>80o?ij>l̍g$~ao?\Þ\}'ykk,NBwrG8ZݾڼYX!ۜOn[\\\x?6$l5чۼOs!k[q._; S-7VYXKyicr_z9~[W |^[/Y\OkA\v =֜Ź63 /?Rsַ"9Λwlߵ`k|;%ܮIdOM^xkjFN^loy~I^+\j͍ݶV''Vp䮉ĹOk|qSbw_ώo'[#ո<6?=cט{:\x&m呯5mZaװhM0'o?47_S>4Ӊ{7oq) K:Z}_GS?~ocbߩV/~r#N |^ |)*e;/{0]\mF)?r/ڗgԹA38x<>;9-Üy75Or5ؾ҇wck%a5Oy/GO$oqkg.籜~%L؎r 8j|s>#OYq~=k93)={S<ӯ&)S ی׌8a/g^GM~D,5C> q)ξqb_Gc8XcSlK>ίgLn>~T "U~E[7[\jɞe#{I ?8q-.k\s<>zԾ)ɞZa*^|ȓ'Еw1+o߲VK^];V9\wa<^2x 0Z"Ef'.[_OB ;; {6)rNڣ+ntǮ-d/{j֌ԇOrkȍOv]K#]+WZ[֔|g;u< Þi^pqW~W7{mzv-s]mNۚ@Z]瞏XV}yU |I*E˅o/~Ѕ?)t#]\k}>a(7v>;kk>GX S]I-\E?Wt|ws.nrnjoO]ik-ߍ#/)_܍C_ωY;9Nל՜o$<0O>>׶}qU^ɸV=1;?}O~Ǔv?%桯O[iX9bnV-+Wd\erܤnnnnnnnnnnn> 7O]/GY&-7[/N]hu[:O+nXegryWo}]s3f+Luxg˳_N˿OY^}1eߵqٲ8Y'~ي'tOr~򰿦KOʩq<lq,[9Ocܖ.Nc'bġ[?[vu;m].1 Kc?y-O<'泌t`8qs:vo 6~m8צ%OLd 6>zuLy.~yaWsm\.s1b,̙Os[ UVMgh79oNSQνfo}gnHh-wrPSsgSM}įfx`>kQ=N׹L'ɟ|_~[Ǟ;?7ƫ{/I\I>x3<^VgN7L)l+wxOԭoqI7%Z ǯ ͘m;_?|yǓ}8ugr ͻ1[kt^':8V\޵}>IClI ipKsv!Ǹ0{qh'W?#avF%I#O^pqbǞ/[c|W׵VO>ӻ(Wweo}onnnnnnnnnnn> O?rӹ"U'IGOE=iqO?}Zb?\{t_bO#ͳґ8_W-W퟼ǶOOŸ8`o롟.߰0k~p;#VqrƵtg4oO2L9{dxR؍}ӎG<`5d^Clj7.N\l<Ԋxy'6 Kl_xμ x%Gxz85?۹_fqǷ\K7 䍃af$|ְgoY;îz~5۶x7Q|7W}]3qgyS>rլiLk6m5>w!jY,cN=7r6P=OՕW~WK~-.> R<53N:V19s4fNzlu5;>=yqH/\Cnrh7p\:*g}xķu7㢵֏~w[UwoxynnT \)F${71J8y瓍ųSl8MD1H¬9 |7wSF=yތ,ܛCk؍c8n6'{2L¿._c}~OC| Ƕo"SNҟrj̾|_ɿ86>'~'9.b>d-[c0^lH✃bq~s7*ҕ4[~esm[_$lμ^O{|Y> fc?Hco_k|`VxgGKycW9y M>y eT͋55QMƣ 7paX7.{/vkʗ\wi^p[xح 'k^5q7u|x Da_OaqWm񡏋}߬g㣿^4,y΃.;^[GXؼ'9Ngvo~?kґkq7}?)qL4/Dcqil^emgvk`#z/ynGpC?;sOCI5qS7~7 ䷶3?̎׼C<|{^Y׽t8ΊGn|;>Y^z~AWngsYz6[ϫ*{yo$pbh{Hұi-Q](m\]0w&o/=Y~beߜ9>OSolx/sͥm`/SO1^%0q.x˧%w)pqW#~X݉Own?WnΥtxq;r?97^#p/7A:y=a^澺o|}'̙Cs#XoS^|x|(xq_}9Ƨxtǝ=$9vc$V|W |Z>VVVVVVVVVVVV ]E_)on~/+imOlɞ2oae)'z  CqӇ00oT8&~[xO~O!xxꓞxg]mܳίգߧwl;/.xwӸř' Ywq-C|>|{lPs9/ G<_c'-O_)~{VW uUE @7ۿR.Ivkwj˿f __|u's'qUڰyÇ#wN_gvnb/^wulS?S!/qwZ@]g-XxopǷ7Yw{ͯc ZՍݚ)q4v;׊~?9~쎏ptps1~ v5q||5ڨ5nmG>5e]<ϼۖ+W.ƊqW3es1/kIMmZ;FƦ/wR3]c*\Xꘟx>Ovx |.m/ۭ.|]Lr/x`/|CП[\.{G2rm)׶}i>_N/knOw9l˵P\[SM)d+:%Y}͛nIDl7]mґx7s-'m}Ṁnsz;ZqغY]\_63C}I/9c=O\Uk cAlg~6>ŎoÝq창o1q:s2)>OpXN+ґ| Ƽi[}U_ EkMm˲"⻿:ۅp9 _#϶Bk܅+v+qkz˿qau0|_=͎w6m.w~q篕Տ)@|{S/?ﯫiwy/G=鷮~=CΤe0W6uk |O?cyjdx1Lxegˇ^Mj:<[FY*$?(?y9/c+<8P_Tg=w8;͏mU3ڥk8~7~W''LsfΟt恧U6쯞;(N>JŘ7.I<{+v&o>Jm呏S}ln~kx''uk)+oޢM[Tr ^owV|}6.V/m.5Ńݾ1?1|~Fa뻉ͥl?u[jyx`Y[lS >-]9柌I_94^kraok|nqO+l=c'0pj^js`c?INjڸnه^i&w_?ٟltbf>ΓZ_,|Oqs q޾1cl^+ٟx6x]̶;+n޴̛R_[[[[[[[[[[[[m|n@ gO_{rp;fOxO[{Z~>0$4TlpgN;']'L?x9irx%ϸo.t7nO|wJ>tq7|'[ G8$ϜxyGs]Su|v˸-l?t3q7/;^nV-#;{dn(30?k}N>mtٓ>)m}W_ɰխ\w\&JkN>I\gV*pU%/ϭ.d??~͒ kk"iMKJ7mErupւ~\y.5皏\kAyGۛ~ǜinfzیw-olCsX|5Bl/)OYM&Q'k&}uPW̏]z['9~jO7猯gsצ˷mpXs\dol"-&\1ofݕAjjaXO+kμͳ&]+nMY;xa7/Rg G3]׵m6/d~rSŗ k}ߴ8y1ʽ|eoy[Xem' |1*IiOK=|zI-})nx>sp8ҝ6mŬ~jN̳OyO8?-Բ6~}WO+kCo}w}x+O>o3#[=xdcB~ƛ)lxx~y#ɓ[}U_ ~|ٔEi ŕܛ \O ^m(Q9FgmS[+m{3OYCR5CqW81V0M \=~oϡzn8ƅθk^8ȭS޻ 5^g>xvMN+n_1|p4o: ȏno&k_dzk%kұyF$5۟̓^Cm<'~yǏv}$lcc-^o07r=>Iݾ/|xuϹ&oy'O3s』5[k|5lg>?y⮔ێny~yZXñ翱y;.>!95^Gme+yU *{yo$pQܛ9* aT' d/B۸8MK\0O>lN>鲑ℑ[>s7lmo`-o՝|V~㊮sPbAXnqm뜠cǻ}<'iql8ʇ޼;=[ͫ7?Wxٲs:6ƻb=%Eo_3?}b>ٯx |[^[[[[[[[[[[[[rܤnҶO'̈́OOLaPH@IDAT b=Ts߸aַ\ٶ=7,Yhsڳe'bp7&ωٱS>钸ɷ<Zxm4\b'cΥq$lM#OڞĽ~ْ) ͻ807'ĭ_'\O<W7ݓW''Ń y7xˇFZ,}&|rQ^HlNÊZ[>˿8anˏ\r~ڇ[}V<_ @Yz.t:Y+u:yڔ )&tZE}[ӵv<ݛ]||?Sxկ7B9<fk8SgO8֤}yqճZصl~0fH,x_h?yܩ5Ug}g~`͝bWp6/+OrYcOطصs_<&k^|;g:&=NŰVX8>>5Zs4VN[>,6yПkjm֗Զ6x {u?l_/Fvs  f5}/H|d^lk0 M]S.Vg{MWcsp7Nw8czoZ:6/\Ü~sV>s-^Yj8zɮoKmm\֪±ZÇ̗^,bϦoK$xO]^xaUq ?=7Xc%Y=r_9=G+/?|GO>tr)'iN' ;GxQY+I.';xGNSmO-'[:[|xrqO듇}q8`‘dOqoުM[U#q_WWy 7׾S?S_7͜{__~~տٟcMO3ϼ}# 7[[[[[[[[/UK5/d=y+4__ 7TO|W^)OUk8 v+p+p+p+p+p+p+p+p+p+T~5GϾ|'ğxW_|U~ݿՠW;}Gʟs~^5 N |_or}qޠʧ ￞iq>ݳflO|B~_4HT>{b7Z:xZ~b{ډjlÇ|9>o؍glb{U-Yb_ֆm׸wq-Vn?_ձc1|o.߉'d܍8/0{O淒Oc}'/[_t!,UtOv8m&ҟH}_+Mn=x15ybV*pU%?bSOdN/oN/^.?_~ʅ?7?Ǘ7Oɗ/ ׯݵ@h.ln"΋ ԅTR ;}]+0]Hu<p#.o˽SPwԦ+;?ep}ރ7njxmn^gVN== wۭ[U |J~<^ȼ؟͋+A0:^ܴ%^`]8^\y8] p\tl8vvfYc nabۊO_yPlv/jVM`nO'Γ7]z90ͻN9l&yro]זIˆ_́yCֶ:u̖!dAы7Jxnnnnnnnnnn> 7~=“+_G|_z፛7tמ~yR lOzӘ+Ro}Lf߶+j~>qՏ F۶s|q;m=-naX#ߕ'NN\7|<ګiu.lث?]l!å -W&yӶ*9ZY6syb~+2޹>+żfeKڽ+,MW///~:?7YY/I}tO$^ϿϵUwo6p:f.nF|]E>bq (]H|RMjr۵0nV1=|c2?77rH4n{ ?ZW"I-N<;a7p/?8u0m/C<*om7pn 8/6\ g pr"5S;n?q5v֏^ݯr];Nv]noSS>=ɍ_bM7.k8s.9y/x:q*/xs8?Ġ}yn~;ޓ'?R&m{ěO܋qvm{·n_߳T,~ͧ쿭 }9|M>[<ӛZgaSSn}n|9sͻlۏ|M&Nch{Q|Wmw..>s)Nv[W |~|_o^.Ϳ7s?s/DI7^<3& ɯ=z ?Zկ>g忸[[[[[[[[[=l/oֽ_."&~\;H}'%>54_I'X:n_WuHO|=hCזo|My}? Eڧ1~=-MSRDc\%4?r8dO/~d+ٲY?s6nqē\_si>'H9y[sa;6_^lm''ȑ'Ӽ]l&VH1C.rm/f^q}~ayÐ\rˏ{ﺦ_[1Rߘ,F$][5}Z]/v]37чM׹W]͞/Ɖ1<׆=D|o,;?YNV?L1[b~<ˡxǛڙC.NCo;װ#O-z~8VaH8s$xao,FϾNőos"kjL~8̓ξqM^;OO{܆I8s6W-H]Φ|6OqE*>qKqSv/aMO|'|OnM$O}M*|hݞ7OO~+)g}ÄlTKmGoqjLj잦_:}=^ ,⬎ZOwZ~Kq-c}\˽v}pp≗CSǧ5\qri)WɅc^_m>ٓ'1q.>:66doyB[/Od8eg>G'{s'W']~|7?3ՇÓ/Gsy6)gq5HGgW_\68r[-;|v:xo{.O}Rk|_}'o~ 7?l?r/Dy~)xɞ [/ ^4\ߕ?p, p|bWG'ƕ||;b70$Տ |b.~w7Ή7.[2+q4xCFuog[76'•W^?}Z?8}qVka*W |!*p~!vMVVVVVVVVVVVm*pM/˭wMb7$o8iîo{kk(E;w퉱O[񚽜kn]d:|w^oU?1t^k~Ws>L!ȡ'=߳yo>a|m'휛E||GOjr跱xx҇O Oiq18~l5,pwƫ ~q`ltⳟ\'j˵}xVMEo[ ~uLMAEsM2HU\y'"HA&G!:d,H0ID<b "pG(,H(Fq " !ʨ[>wg{fܩUNWuUjÇ `Ι0޹PBl:VnTҫ?vT<:ln3 ;^CBdíjO}'cGʢxAq+jZ*+~K(𻣚6h<; SyЄuȤD|"B)!8b qx?| ]6譨ma>SSA}Q1>s⇿xڵtSy5v(#|! ^򞮝LvQ<>MՄ2"[X>+_!/ ƟzO:C=|?A1@\E>(E>nÍ_Ma{Yꅦx~Opc=FL? Aϗ>I>oIV룆m668v~ j/y6 _m2aj'M8nJIG),̛]4Pxk8r1H0]͋eJ_óIKT'`0҇~7j4;JФUg#҆~1S(˨5nhG %aKi*~Vy4|.y|*ڡ||ĉkܨ WC^Gh[ qQP>Kg҅xtV$T S;2&nGVGju4]ޡ[MKyjQ5tTt|4?xUwj?vqSz "`J`MA&g?8&ډ_/P9A5QNx! ;l;Qn}x'MΘE9՟³N1xgd/}/Tb[M:^ EB j^[| ڴixGp)?reWp":D:ԭ-[& >&?gT~CnB<89:d+/Y > % A.n5ݐ?ghDx';3I(fj ۋ0M/>i[3QԲR4x(}ǎ7 SxǍL_o؞)=Jk}ȗwcc9k!b%OxRO*aԍ0S6! BB)tXiщѱigBƠGibWiItTd4-Cw|?~az*64v4pJ|xҨ;q{&>0Q>ax* vHZ>j==WT>&ʗ(0++WjO i6#-dJP^Sy~ad!Leuw}tjrCt&CûʪrMacFѴ_Q;'ickZOG_y|5> qit/FMGôyF1гJ WoժUbY]MĎ_(np+_;v9n#M|OI]Xy!^4&Cp:j 6.ZTQ;<#{'Q+LD(+VZukxp6;״/J v3 锧']M(t+] tf8<ޕotJMGvu| 05N&4WՎ=37JUhy ;]OiڡE=t*g5ȤO&Z7j)5E1tRu5Sau8ƫ/եkzPskxԕ\[]!\{nsnhѐ0%!f3 Ls=:h}-o+ե[ԯB!!NSjHbcRY&@!`JB1!(=_];f?ׯ={ P\!֯޽{vUkYA60qW-R]pz4q j0%Z8xΝ;wn?ԛlu։9kǎ~q'U(~r:f aTnYUT-n} `J`}niN_Mt~s1j|7l/o֭_H K@1](XJcM[aZX1cW5E7l;hC(hJ ۷ϨG?@E7L.h_w^ f @k6=yh 3 իN4ܛXK@fVUY3e2ȹf)M2)Yѕ￐hyKk, )հ^fX*-/Zw1^j,ƔRR,p"6%~ !YM4 3f @EB f "@_B s[ ֬~UO9S&EJVzAB[C߅1Sz r?9se/WyXŤuTW}]~% :V dF~5r(4)~vq[V%ۇR1-vC^4%0r6:@?۷o}{H Ic4n ) ̙3tCUP[*Y'p γ3=Zμ7dcJ`C.D6fI8c )-[6LBWAgˌկW$!ի(Zuq,cچ7%P0^,QG忹3c ^@D C0 C0b5lԩ?7!Sr$#Ak׮vhJARMC0 C09؆)޺uk!`!`V1ADNSAL@hX&!`!`@AcmɥT0 C0 C0 GR0 C0 C0 C `J`) 0 C0 C0 GR0 C0 C0 C `J`) 0 C0 C0 GR0 C0 C0 C `J`) 0 C0 C0 GX[ @K_O}6k̇ݻӗ 0>hOw衇&x~>ޞ={M߾iٲ6mIg>}zm۶Tn"/ '-Z$D}vz1p 3w/-W\9/0qx>Cƃ#Ta7oLa"@yk{駟A{FhӦډ]vy7&^R_>jz:P3ZC}kjh3oJ`-:`Bg̘1m6aÀ[/ /R9c ܹs=e|uG7,ڵ~ bqFpѣ=O>Y4It|GB͛7˟giժ<>=Tw֭o}׾}{dp9ҧw駋* p{q {4G)nuĈꩧzݎ;|| (:׃;Nv효ZP.OG) .Ԧ$ }Q_7^ydP֭{)hG&L)&t;0_oh/KJJV@5(ԙZVZZN:~r~$W9<b< QQTifA g}Bl:6lPB5 YaP+1f`E(Q%^7t{wDӢEQD=K(‟І(hy0:LСD1Nc5a'|i%@hG/z3ev 6NF'Bzu[R$NEGAh{?~ c헇4RL lgb\蠚V?x?-P+_N:I~|G5m4ߡZIy?_wu~_ia]qA_ TA͔3.'xOKuj.ϛntS<ԏ_~_ˬR~LnP_YÆ ~06sLC}Ɵ: &P}4tP?P;|Ҷ&uC +sG>j]bG=tkم+=)={zFdG&O'c*:Q3%KWg`Ƅ"J`4 rB< vX]Q^83#Dc+zcRf=eVx'Nu慎*ٔW6|IĄYѣ^0ɠzCbe?sMv823?¨'s`sihQt'CԡtmQ`S X飭p܀/ԅM66骴_SƜx0Q#UĢB9 1EZtx*tttO=njDlr`wf]Y є1u eʏK/QF luѻ$^Y<3|dҀ*f ڡ.] R7Pih3/YW!Zx# +ϴG:q fF1VC!+2M|:^l9%n"=Ce|e0*6AʗgVج̠BG8!a$R BK=oiЄnrf\POܲeT .ֵmzU%\iӦC=`2Gmh跠RF󷘆!P0/~Xcˊ +` Q҉EG6x`͚5P otQ6XA#kU?qbgϕW^O;b}ݞ%n3 )Uj͚5~ [o+)(:R]W2S`=xnP?o!"Ü9su:>,s;1[^J=X0tPdW } a[D{;X-~tզ 2lJ`Ae0^AygM8.L1fpkğA@8햶/VҡY~)ھP>9aPwtگth_CG^B&!@J-(L=@ӧ{%mܸq9r7-QXcD'G v ,YEU9j!'1>?RDǫ3la8owc~@kDߗ/% ߍd;n 5B-_+<|O7uzxb)SS6P5$jHa(*f 1zw@Ӭ1^v?&EE-A9d+'ulAUid rΥ1$Qڰ- C~P.M 3eH١Ɵ(W0= g]S{n \h;j] ꈮSgJkmlݣꤙ|otگ4ܛK/xTcԠ!1PFi0ftJZ4^la 1[EB A?at! @P gZ0;(3X'\YJ Wڞބm 2k/3ÆG#@m^VL[Ļ_E|Z꘵_,CKŷ8.8H@IDAT@ nR jxT p;(*:XҸြVvL3kȧjǪyhЅ!Of>9[>RPF&[;qL^98NhGA`}ж vCm/ngOXϴ_zcՙƄyMה80 C0 C0 B {a0 C0 C0 *0%Jx,0 C0 C0 B*Oˍ!`!`!`U"`J`X!`!`!`)UC0 C0 C0D*@C0 C0 C0 S <-7!`!`!`T)Uc!`!`!`VyZn C0 C0 CS C0 C0 C(,L ,!`!`!P%V !`!`!PXXXi1 C0 C0 CJTj!`B>/}K>\_|!7o.94~gxGu/yAn۶OW媊%۷ϓva^NGU҅ 9m?#kt5f?>Yh֬CmY2 C3L 3h!`@.l߾]ƌMA<6BP/w\tEҢE o-v?BoQ>h[ʷ-A{ᇥ]vtQD}Ywy^|ROd˖-RZZ*wN8AF-4Dp̙駟JϞ==>]vS0 E@ElC0 zE//=&M4XC!]^^$օ(7oJ &JY6FBªTqClݻWP?Cy< 3ɐ C0 0%!`@@@VZ?^:tWYC9;sgO>+_SNΝ;6 |e%p∜cUm_|ѯz^ |;`"C-F/4f!p0"`JXgC0 ̠,iF=XA-}~~`a 6ܬ2G<8s!x$8GOy(ГFH;J&~V|n#8/JZa2Dn0Hi9pFxZ_ˆG94gj Ch 3 C`G6+i('tW3ر̯Y}QM<+`?ѯ=3^cG~'w~1Ĝ=WV\Lw \p^x'wJԩS@z[^{+L'F]tA`kh~RzF/Uvg*x(l3={G|A)VXQ9F ?x 5Es|;eF`_ 2q sN+lL(8|+t(E.ƒB}Wx# <E~>`AZկZWox>^xlj3}׷UVD(>}|!P0%e!@@WrT@ЕPP38G[>YӁ?qe .£:]AčQďw'?w&OȇR…r$ `/اEB97c@CG@ y_ɟ|?A`9@s+g@蠛/0bPt3GCBс~4Xa22C<r!J Sy +E~³Ut2-4]"M ?"]1Bޕ7i 0bu LԆ9P0Г_P)VC*N-#l[5`O+'<=0F ELj*tCrXʘxQCݤo0 @Fo(.>N <E7?%KVmi UJW^u!am.'dry}S=.+G''Qҽ㟥  bqʟ?*wfU^Wֿ7yk{;]ϑ>Hs;V#?gR),hd蠛U^BbVN74gPƑ?^90(~a(s9LJWe(!(m(o;Jb}yJ;>qn...s&\Q e~{q+.#|oU3!`4t T tWz>5Ћv{}mJL͛)7v'o,GԲ&6UvAT߶*_yLz%QOK$}Rf-}-ɠtt%}*zǼYD&Y9dDcC@)a>UeeBWh3gJ 6J㗎EGAҬ!lDA 7&ByC EB!F|VP(YBV¡GAG((C QPp!H?  5r4(Y2+ /L(sБ?⨁&ZO4uy){[m@CF` z]˶2>Hfиri"QD9gsykұjWg$@RphزDKMMvGeYu${ɫzˍrVA"i߬\S(㩼_E>X2Z58$1!Ё7{nD9}5+V(BacΙ7Yʠ@@OF|ܡAH԰Z3(Gh"o ŋ7VPX)EٳW֭[F!gxצ >3dχ `22Fqw#O#J޷m>?ׯ1cOw^QlLuDhrGɝFc@}#Xe_ͻL${umAV,ˆ$ Ͼ#w^3zqS.&o G|T>{R_S6]8^yM:Kw3-k_iC㻮 KF&^'Gf/mN Fp6'P>+ouJ`UӚ R;8TΝ`3ֳt\* ; J:6e2ТH@^WmPRPXY]W)*'4̱b(Dڄj./M>9HXAJGVbE|/4Ő>GN&-V)0ЫBʭ4n› /i S5Hz@`qT>ޣ~3C0oceth}>ɉ>.K(^&-<}_U i{eΖYaX{ܨ:Nz)jb39K/ܥrr]2Qu))rds\=4=Co{}@~WsHL!}($P8 B O?믿+ KĒ [UYDFOƨ &| JbƊ'2F  ,d[n>=}ʭ;3Ab3!`*ލ*?<%z#6yJUqĺ̹ػ]Z_ euCT:O}Zyoʄ%^^?[ZO&J™+te'tfGJ:f\S)B2䦫K /n&K@Ù'O=Qz V&gȰmɓ2q'Sܽhz>{}0ST&G8P*B\={ݤm/CQ8 0  (V(7(_ BqRn=4$D@Z:8V$ΰ2(Q{M~(4(GOV@M҃2t㇛<]#el2:F!p[+a((O/Jo,~ؤAS3!2 OKDS'CV]$!P$$?|I2h9"~?IJ)?\|WF7'S?{巗96SuC=nUW)+#+,sZdVUH iD}cWB0ptR̾RH'aU2kMuwUK~\| ) eortL 0LC /ܟ{(? V 'W }WPP%#T)*IKS?x_tE>ٳg{% "Zjl?O~Ŋ:V+jPjX@ǙILkm(6GKB7|p6k,/~JJCczeY0aO' 0s;txe G9VP4Ң4?(sUWtH%6!~ Δ2-{=[GtJz:g@cEFu޵snÂUK"MJ\6.Mh!GtU;wFnwqU\r^TÃ+=czge(IW||Kq.2!ש+3ܙ=$assSE,{~GEGΨz]//Nl+4zULfCͲg `J  Zt beIo$ J+>UYΙBU m%-VP8q4s"?IW4^&Y~K58/G $M 5rE_ (z*۰aOF>¦C8 JqxB[X]J1CC:A|R(xī>'+܆!`44T|/Nxɽ]&w?6~^ˉ5hT"rcAr\9+V.Oȼ#}b9Q[,lq8X|u(:ww{ehi:X'ϥ\<8A~rӄ>5/I9hk2wk?{_KZ1%o쟑S! Ζ㫉7 WDvinLGyʳ>WگX~qCJ4*7Pgu~ltx8zŃ|‡|BKr:F`kUU '=LWi Ch@wirP1RWJ*rP?w~21]3<}Rȹ_#ݜ-]":%Pq5;BHw!sOɓgLp{_a-s#r0qfKd|i֚6‰ PW3*bչ t AaЮd(((K jpaLtrDaP5 e2[CDF r2+Ujr/Ҩ?>j2Ɠ ?>?]g@CD 4DSdzgƍ[hNNfEZ1('g5~Um3Vѫ_F`OP\u e!72{>ڹ3~-t7)^ߎ1E dck/YG>#f,r€A҇g% rA|(YTuBrR4, FL-7|ӟ DD`3c((70 C:v\^Øv.߾^^~% dE^|2Ektwce+{WŚa$F|ʼnx]L;Vc)>Ȅ }aByaVl[g8%mܪ=sҧd VfJvdŮrnL аpl#C"*+pҐ܆!`@!V? .ivx~+4[^~8NNʳgip,jW'|r-sUwf.'믔ٲ3$VV^G^Kx]|{PY!nב{,]7s0M,a799ݔ:wϙ2ܝEΓqyI5 GCơ -hw5mzMϗOϼ6c!`:N eKκ/v ӿ'gi#-ץDa]BSB˒^>(Ф-zl\#`Au86|dI໭yX^8W~($hܗCem*Y!и,͘1 C0 tl]s+SPQfҩ9vyGzD>quL['NIB5o㾸g杗'$AմWd¸q{Kz&\N۵Pz@WW' ?֛JUH[ c3l-M$]Ss\7Ŷ<֕ƂC@k, C0 C0z䏐N}i 2/TfmGM @sA)M-̼Z&Tύº.TPnV}v}{tckdWWtY2ݻrV<XIF݉Ӆ3ku.*_}1ZJhc! C0 C0 j\i"]z'E9Dfaݹ>\;_.qK9)TݖʚMHs [aL1o 4rݥovQ2{7%HKo|Db_JxƅŕDɠ=QeerKYp}L&٥JjT'^B rIU t2P#ez`Mr z1JƀaU@GԭOМd#2f C0 C0 L 9s_Lg-e>*s<*.O׎{N]vne% Aٳd#7\sW^7CnPo"{j\yjB ĪqMN7ʅesd܇ї<(\JE}ʹze2t2{dyuK,_%/6UJ^#CKd…pY2٤?E~ c/M(@ص/-9 )?Iw)ٺCv?5pH'!`!`(/q5 yٽ_ e~Ũ>>{ڼxBxxES]>ANzvSq4uYZF{^oj<_i{V~1Hu1;%+͟gOVuW{WTqԾꑕ&=j&|jCRs!`!`P}OFrB䃕OS8*Afs'7!M'SޘwoE>dk%Wt`E:G{õX޿RS&I[nrB_Yyܔf%f7J˪aS9i4ћ&ԻT2TXFeۨƞL76jw82qZ'$UT(ybfgLX0 C0 C/ *Ջe;IKiR_;)Po^6ϥ?! ۸mlg[m.fK۹)J W8՗e"|r?-J4Ƶ\6m<p9WKXZȪ•egC\ȩI-0b:Ɏ15 C0 Ch0wY{1 C0 C0 C UvH0 C0 C0 C KL (#3 C0 C0 C0%J`!`!`@%PFf!`!`@! rM] !OC0 C0 C0 J``0 C0 C0 BDB,U˓!`!`!`0%00m!`!`@!"`J`!0 C0 C0 6 C0 C0 C0%Kd!`!`@L y!`!`!P|'0 V!o?MKIˣVZIf5fH=)G;IfD TɖmdH-;Ign]J6/W.ǝZ$T}izٶB>o*Һ]GԶU km{\{Ti۪E!eӺ7c\\;g¼ (-["; 6t̲m̽<>v~iTȎMIrb6y-͛ѭcڶ0,IXžG\=r=7{͆?#'.Y/>,|bޮ{eeRV:BJz%\.a@}u&ٱ@ZU\۱U6mڑ%'K[3cX ={I(UM+dך5{S+>m Rbg u_*Z'+pC%!?J~I=Ig'N=G^2 t29}L)/D*tE 'JِXO>ٷ>b 1(+#JbmLN7ҿ*%2]>b%K}Sȕ9DžǙz>ߣ-FP[D,]*_z5ǯIq˥h7S1Ӛ0(_-:u+"=jݼV:̭MwGCEV'blg~;G\k"nm%=#w|,m:wN&>uZ]*hRrIqĊߘ^2ŭMrEҖMQly~TXM$D'G>qBw{H]Y@vbj9S ybL D3o/Kؘ0J]r6E1W(|YrN531i򗛊2f>U6 f W\zզV{WΤɬ;sn_}\\IWlfr#n|U[o~J3+͏~E~rQz@#CTגTՓLΗ= +'Sc{YxyN~'O[ Kݵݘ8uUC[$emP,f*Cʮw%yVv[5KΆL|F~>8?NnxOZ$EEKĵ_hR<`<薉74roJ1ruR<6ca2ȳOOw8I*Ѩ%sp.Jl)Ֆ%2)6B+zGM.o]>v,/wQDizfE0ػ7S<ѷt۶CЦrn i,UO vZW}A 1gСE^Zta?!SFPFU۟xmBvպ2]^f\>)8-8}R)LT!(5 EwH}nr4Z(S^!|?S&*W K%0ƿIeMy"u?X7ws&Coe+W<GC߱ω7-ZEƯv^=}4z*5[V|&oe4mB<{s6HZsPGs;f!RKOkXۺN|go[ }~/.k&Tl|j7kA:E#~튱=I}b$&;+aCJn1QJPo*ȝ'+shר\<+iwU?>z?L-pGMҤrOE{؎-K2<yϧ_'N2W`/s{{Iuv~ٱ$mӫo(}Q93v¼yv177%pfk9D_H;:GzӦ%->F.'d@q cnWZOw0)_ϑ4i)sGp!]fS1c8?1K"WȢK,iKil*!OV5sKLfmy6ڲwo]#ȝk3B}k |ۚ&i^>TlZ(CLIN4raZ$.x):Y8SJқey\*]n7E2DޫҟʷR[c+yRtzˁ{>B>q+lGPAloAεKiݎ+bK D ^lLmz6t2MK/K.-\#/K,Rw!է9u]XEȂ o*{b[/xѯJKg,;d%~m3-ݞVM:}9'z_Ku4Y=y$9)}S<>_vR`y jcTWC?51`3'#OJ0RqKdۿ%nw`L+eʒ;b oJVgE._ʏ*^̵83`"CM9~#2dB)xH w$6`US;ɕKS6(/V^}"oىj1ҭ~)*T3o{Å)!OL SdP|rVIr—ʧ=Romaw7; 8cHC ߈Ýgx9b]HIpzh)"mIcdehW]sC:%iW$1Hn!$$zg`z?KU=erv׬Ù*bV˻; mz塅kRf=)O῕YEũǗoe㽻nSc?k+>6rؚק61J(;9 ;8$Y(>"n⑻GE$8gvdć-M~Kos ):ЫD\Z/_袷n]&plʊJdRLԿ7{zk;~}̘~d/,֤i+R򉓖Q3y#QR^qudyRh7%-'9 sätN !͛eբi1~mY\766Z0^'s~tY|a;s+ ~ov,wOuY2E֢-_V?߫:ɕf7U?ekЭ2mI2G3ٺilux_t#q[US)}cEQgO[t(E˘;΍[f2?͖|T1#{ySE^ՆqָHngd-%@Y>~L77ް2 >1o:7Leۛ,J7'Jfc&ægXHoWeI7Zu,v;VZ\YB>q4'M%rypcq[%ĢPGa8 \Sc,ݥ}Ʈ}Ii?55yCb{~$st˖-NzLI\{dI̦8NB7`0%*^1p:+ }Und|>F Iп¯?P]횖xF.)7,jI}$LonKHvbŹwzz$6^)uשּׁTҕu70-˼$Q~OY‰IT‚ *2֓V y"yTW CYp_OC sJ'I00BgԊ._P} 66UGdyY%hY|Fc151yG@Z;˶dՌt@~A% J`Xw@GF?{+pƄzYaCKVqQMF;\<z*Ec] (MSms(ˆE{̋ğF pt4(G GMZ%3۞҉KG鬭pd^bL[myt)Eck–͸&L N]d.sg;^.tpCtβDGCyB`_QcbN_=@}k in4B0>+}Hl:΍>e:s5׵-ѥIJR>M2Ib6_i"0U \dvV}xD-*%qCIܠؑխ~f`hO&J;T>ZdGyyʜ9>hfe\EMcXsh(]mzD *.{ոfw1 }oUb\Tldwhn[_WGHv;jyűQW, uQNx}gp0Zz3@+fS E)jSw:1R.8Ԧo־d\3xUYXMd+M৤UVuM$pZH|5&%k b8> o7L RwSCze J=@'A+\Ec9/Ci~tۍdê az9>φΐFWqá ZcD )}(ُ+5 *~LO"8iXĞU΃XCO^",a)]٥MI wFo4Uis?3J;(nhPV44%74I_3:"u}Y%z$`,I%}\kݚ{ocX(e"U+u7P(9mE:T7CX;G1ƩnKr-oMjuͯJ6Hɡ:vK>%pNǔ<$/ԕhdoJgJz$۫5k7>bW~MyҰxeEiF_zdhdCǓafTmAaMDU)]~-oGP {d<ՙ\R|2֦d0l%լOG謄c&YOK_X EWL1<,F'FH\ GRm&Y:ouY{dM~c׶o uEosY^l*1JOw;ƕu ?k70Z BIzp cП>lh$[{HΏox%擌O~]Y$ZĘ8͗7yn$Fdj a2v9 1e֪jC8. 5KzqYޓ&bomeVy}9w/9퇮Ch"P"sM}UorFb/Ј΁Xbk w]a3 1CkHׯƍW4&z_)zW_~7X;”.Wa4˪VHH<T5n/_[NXN}NSo5zlAJ1B9曐~)2o3) UA5~^&u C!!9o:nKKWBBc7xʧ_!&GKOLG|FA%yzW[.cWVɆ_xWIӥ X,"GWC"~^z2Jkca}|pۚ`r(d7bhRu+ _x<2)Qg3oWF*e`M%H)_qm $$l bIt _yqF EǦJ^K'0ѱz۠FZnb w;|rVoy "N!8Gh4A'lve9U_'6bO_\q-1 S4/w&>3&/p_@_PцP8yF,ՙQ-R>G)nFдɋC-fȦI{'WYܸq "ӟ\  q"Ƒ ]XQҵr"ϻ9VT#[~s/Vo~FA[GJ,`=߇u2gPg$ AgC+QAЈњ=Рx%u\$b"/Nc=ؿ+ӳTٿJ)G5kRvV4ӘA㇟f?@{06%kk"]&z=69.c\1""½M[AZwMXyhWH @Wa1IM08~q'i}`[-K:B@>wč<%JJ[~8+t&'y~3ȿBZţ|)SMIoiZMA&sNg }P0¥I-GJ֦Rn(ʽpfZċNyBNs 5t݇LS.5X2Yk)G&}jwy$5zfE;ׅQblUR)^X(U!@Z]S4gp ,MdH:,>LlTQNUҮrjqd1}򰳍r&L&ľ4mg.`E{ +'PH5CE6 #(ZG|FIe~I!?|VyDҌx,Z`N1 jeD1jMGkmš~8D@Пjqm}RZؼ*/.%(th8o,&_zs}ɄzԬ_iڙPxJj06XhbP&hcQ(m'e(ȹ>[N4R=45N^A=86'iPJlиc?>ؤF#,l=)g?9{KI[f:2Ym+}nP㍟;>zgLs`XO8d+%LX7{ӟ]կ4eKO5:{Q(N? n{JKzݕ0(QAGqh\l94wꡅLRDņ-<iTB"-+W$&Q(2w"r=Q:׾/ǤrzHy\hLDH >aFOJ=&3B^Z-/'6蚓S8NYoz&|Ȅ:biBgu6*[+;tTӓŲgnnG'I\^^  _}FD9t sɲ" 3I5߳d`^EҰ"~;E~1M I{pQ}?UR-/G$A YDlЗ8  =NL>7i}TsE;:ѺI[ݬ@΀SZ#9ݔ" ʌqF:̦d)ZFԯd\{]:.Bx9 3[ #(˚-YaN*Y63O>$j #V$fL0@xW/Ϡlz(F`F`na.PXU^ZPW6!hk- <Fs-\TffN醶OຖйYO%Gp@򊦶zn|@rF`F`n;6C@?)A:nf83;ԝ %1___\,#e8Dn2pF`F`2g]jO #] =;5AK,3BI `F`F`~ "CH'U1gl2P| qf"XtFH##0#0#ܪͻ;I⠷j|1#0#0#`@ fF`F`Fg0Vvy'p+gF`F`F`p -qN 3sU#0#0#0LDvy/G0#0#0#9v8Լ@F`F`FXv \Z4F`F`Fp\3b@AF`F`Fo"|#ҳWIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/cube_playback_controls.png0000644000175000017500000003265613455362716024256 0ustar noahfxnoahfxPNG  IHDR7Q iCCPICC ProfileH w\Lz%"%kWQI B,`EE+TWZQlbdQP*oy߻~wϜ9@nd Y("I H' >l?e./,p8GP$KB.q oq MDE 6]l( R39iҟ49|6(-sd\iO6;M3{AD^웞'b/Y$_MyȬ䬀e>%fӺP1bl:_=˒hY R<$3bgeQN>/?R}dd{:0/+@^8L k|/" /~Y?bQ&5ݟ5Er]5OO DDses |A.6b? IJ br x cmɰu3%}@?u-eh~hPLc#G"ʟM@40 O$  @p4('Yp\mHx 0 A@TH҅ bBE@P2 Tʡ :5AB'%h Ac[ 0 ֆ0(x1…p kCp|߆KxP$B1Q>PT*%BD*QVT7u%EB}FcT4mvEt.z%z݈@o F cq0q4RL)Siǜƌ`>`X: c3˱{m v;4p87\(Jqppgp7p#Ox^o'5J|349~D0"B \2fBp0B$*Mn(bq5JD@!'P(OJELDi<|R*X+ j:n(V$()z).Q,TTGS^ަ~[CO#ScFcMfRͽ5_͡qÙS6ZVrZW&uڻiҡxdl93KuMݮ{FCbT1zZzzzz&k  z u >0"1F;>345Q7a<2z֚2Ú12]75,` Gt=KgKe]+UUՐ5:zu빆sn?MMC[ 5ݶo8v5v)̳Ǜw=uEcNNN2i0Eg*Ο]].G]rrtmvo27n퀛ԝ]xi|eu뵷Ȼ _oo__}4=wY,5"/\4abߢFK,6Y\%YKN%)&%ccCٵVqg'%ד;sU𞧺VmK{+}ҫdfِ9ՖN>!Pd rtr rR4%wG(XT-ӐTd(=&Ҙ W/[y/9{V Zq`%2ee*U%FWWg}͚5Ʈ..).)৖RRQu9w^-\nS^Yug卶6NmJ4q---wzlmP(޶`[vw$T9rNNNiUHU.][v}W߮iۭ{{{nۺO{_/;pָ `gu1u0i׬/ h6F459555k5on[$-c]?{ժ@8"9_ >{ycYx'S5x"Dokwoֿ5;YsJ%:vv7Ṹsx¹~3.re+W::\mǁkN׺;_?xǍ7}o^źuۃwܻxWz{o~7L>,~yTXq'&u4a?:RQѓcc_,z1RrU鿔yuui|j%ɥ_q_}TԔ-bO(dSSx%u 354S#,e]gGU @2Bfc+z +ߢ<lo/"kyvEHiij6n&LM}CjɝeJدhr1x GxiTXtXML:com.adobe.xmp 311 167 B"<)IDATx}}\T.GėTLЋKjJ^Dj A֤׷k$"67JI/14Q&ЈG,3%4%BIF}83pf >k]gzЋ"@ccݨjRPn!Ez)}(R{Տ"@>{Gz[G^J#@-D(?5dׯ_8P={838h[paÆ 0P,uuuϟt钟JN\Q;{ҤI$T.qﰎ^JѸz>]`Kf/UUW7+B׶$i[`X|xz&i=RbE+%2pDTD4&MV?,Doذ=+ɡ0~5,Hzӳd2ﳫSÐn8T*݂#'x["'vs;DS4I73?Ө,X(󋋋xs=Z1KAO=115Ī BJXrP\@CRO/ ڒNM^ F-E. G޽{]]]srrR@W^Jw7!͌)Jct47a ҅1 %O},/ϰ%Maj0O|=m+HxZZmL4=U;M2XRmɨ;cbrkOn gdCoǴ8Jlj,rKQé#k*Rb_+buW[bu5͍. V 5qDiii111]nhg//J6>ve蒴PTAA~-{E-Whx3CZy8F%a3fDFF8q !EyWe#>C}0Sz9JxM87*UUw_LFKQ[Z}M6g?E3N" ʝhO}nX})TV kY$Dف!ؚ{jK4_~]ll}r"2kCF#9Y!!e۷.KCڻ;I1%/x Μ9s-Ti.>.u/c+C95B-^,h6A{xDYG3yӺL";A( UBxuXE@ #eB K-.eMꥂHP,R KYSA@saMP(z>|"̗zx0 ы"@}/=u,  ~ ,"@ԪpSa^jh4 EP/*TEB*K 7F0&FPRʧ„@8ߘctesz9p@w}G3\rذa>>>Úd.]3Qi]Ν&8PhuR/իW٨a]`6VWyGchc m+ACvudzū8鵂HO5}<6=ؽ#UpRTJԙ8WV}pkޖiڤM^q( <h[7|sŊ7PWAK5,D%y7pFzN $=PW_cܗG (U%Gg0HaD}paƳ1(%Q[h^^^8vMK 3Ԥ>)A.[=P-x쨉IۆwRbɡIx3`Ǚw999!!!ZEzkWꩣrkqh:(Z.ĩ\67㌸b[:- Z0)]jnɑ ja;JodUTu wGI5)rK8S83,!WAJF S~0!%GZnZ5L qHBuRLڷ5ˆb۫UfϞ6^qycø۶n WNݲe& (iui[I O*nrYI2 2'7=bQU~`D< )~ڃ4L$n~_qR}7!:yV<+(^VSb?YNM}?z+T[YȄ5A$EQ?h '3)3u:/b~TYSaBR!ϒWm+Xr'#*~} u|u;.iRaoA9S^C?y3zw3uԙkG\kx)$.ҴVӏ E_!$<졁͢,JB"b"K'0{y?ʴe6HN,EE+Dw8,.9(Xpo~TbQDdFj5$oH#WY)TI| D8-J(Ãv8ՎcGӪc\IfҸI3(BxMRQ4H9$6H]tBƉT"=x6;=Tu>Ŏ0)JdbW#&Pn}ˏZbx8AA?dk c4[oFpcpH]ȣ[r+!!G+-]VEnLd:"Tb72>yILTeוă.U&$1&.uK~9ZL< PbO rqBYH8Fr >r`RP#HKK+++cB؈̼I=j;_U]%ŌGd>ȰhBdJͭW(23N}c<^,,)2K@kJq׺kե%&cjVT_5_!YsKEmy~Roy@FKG Y1g.tR}د_?.[B:"cIhHg͗z'g!7T]Q:Ɯ,HBG5."*Dy"/?omcw%Vh˥ncEQqCAD:.$O*TͱVcR'IFר-@r${UVR7>Tr0ֱWjAOCAG&,]2ݻwtt3&K(JQ56*DbFR6*q9JP"@H$vfJQÖ(:IFy1__ʔw'>8c &ִw;wV/ݻw̜3gh639i8m4>7 |"[;+VvGSj/B+S 0Ş5Vi<1lE6qΎ N-m9uQ]`Rۙ9dzSmkԞcccx.Qnݺ.^j>60dȐgB oo}o2&… /__ꥦYra+`@9s-d;Ҵ .>.u/c+C9JB-^ LdM$¤ٗ,ց20< < A[,#rP:FziQ m^j[t@P/#JA-Km?N=cΝ8PLE`Fe3_a JL@m-LzRcYE(R`(VDz(&!@$h&^jE(IP/5 6"`EZl*"`KMfX=VNE 竩~o& G<z9p@uVi\=2e+8p`ذa>>>VjPakK.勤.ĶS(=i=]Ž̇%.q若_jG5MW Qii..IKyed8 mկ kh9fЦMڞzY+]yĸHsF}%;q.U8Qv3 kԵR ڳB{Faī_:d0Kz#`$`]u8IV;F q}J y&=[wCY吏^d/..±kZ&Z&R؜/#}Yl'&{}RS7H$0"8&d^wwryhWCU`Ǚw8''''$$DkvĀW:?./"y[ճ\cV}P'q7:{&'Mٯ f1{sRl֭[[lā5!NUɱ$v1΍d?g2Sx!*(9ph/8CXuyV>$%M r%yFYaLuFoG#du}תRWڻ@ x [ aO~;SK׏gE*сN]#yʕ\xKr#K"M RcQ$}S1Js֒IS >RE{[ԔUzABȍg$5.\85:L-^ 7o!CyS}́3S ׂbh-̈́3l{D?_w.m_N8z}".*Tg?IT'UϹ쓑GJdՊfW}?6&;Pܭnb@K[Q3? UӶEB"G7'CngwR>IވoJF8- G^罩X~R5K Ɋ{:DϞ лUùl;@Y!v`Y:z`SU272__Фa##}hMNKKu,^;y? Q-UgZT= ]Ij] z$x3ي (ö)꼋OS,]/]>V/wm{W LԐ•V[p εϤk,]g˖-P[k;^G$.Z$kތK'hhj[}d8#.ԲEQIMxlzC'EK"k,dI zY6x~tÞ>5[WH2t͟3|r׹Ǭܺr:]&];Իw輼Hjj*71JYzJY&I$k>*ERٙRK|oބuG1_|f3zꥑAuMDưIEssrQc*33sΜ9fV fprntG@̈́$y # *+\ZO W5 F\}Nc.C ,X<Fъ4R׵|z4-i.`J_ 84|(.-b 3Wrwww|p<~xvJcFB nݺ.\-kF9D`Ȑ!gϞw߾}ydE… /__55c+`@9s-:m ;.>.u:$@1P"R5B5*@k!h:99DTҏZ< < לJ䃀k4P/Rʐ" 0KR! )#@G:nܸQ[[O?/8uexxxu|15Йk-,7Q]]apK8+W0?N=FbP/;ԩSp &uE8pii)8007KJh~[HS?8! 뢠Db& B5uss4}`5 jS>&#a2:uɬiFa .T*qyKA9vX GF;t& _: (% jZg`" w6@m WԽnJӪ(DYX Bl9ǐɓ'^y)ǕX#4=J꥝|#G☃cǎq5*"E P"Ӕ*j <;ۉ=>~䯿*h"K谼trH+M~__,C,7'nT%^j T-oXw_v)r҆ѷCVW \i騗v~ ֦@h+z)/(QB-ѣGۏEO>m2KMf_Ҁ0x9 yB.xZ'RaDD[V|Ol4l0ML56ܣcYA=: 3I١P‚X ]apP,QIX9d_|w3<Ř@彝;ϝ;0ٳ;/yפ > V?_SZjڴi8֚18~8{#ۼy?'R'OX' DªjJ<iKf߾}xn |Q())Yh_p!橧^IMP%,ߠԀ2s, `&#”\ ]x)v}1G)//綾^B#?: jeDXze9*@AM:L0F)aP.&-3575\t+M*]u\‹ҕ:}hd{)Ƣ}/_z.sE،Z\=l9usoP* f>~Ma|6K-]<ʿk"pM >ݗ4],@}=ӖQVǵMJ/rmt].[z)4đahb6<<nƒ16{nLQ}oQѤmAbPJ3,"(4oTƂ`8. ¸\Z.QkF?{zt[ ŪŽ[|P=mڒv 'KK%tC6RO"srr53̙3z-sڵ9W*eB (Łolf`2.#L ¬\.Q'_!zkucGޢ\ݯg+Jkd,o{)4F^|icN̩%Ν;uR-SR5k A (c~ )(`,NgpI }rʒˈ| "|Tztؒ~j+'Ο 14uEk UdF 9176VX!xy&5u@+$"Ճi֮] 3q9%,DZ{S7ǎïwgIO]Ϩ䫧e1'e$|l:j{esVi]HE۸qXC(TMuCz `DTR#Z mVGLwt1Ȏ9t?Q<]ta "cV$fi'AV|Ǒċ^( z4 0@%G{5K(i P^n`(' "Fy<+Sc\IB+ =+ՉńBܞ4Q~wXP!֬]/Bf挍 `$}BM 9qc8\_Gjm[(ʹyE̅ L:4p~lH%Ն x"X@H~KZX@R2gE  ryIj {Os,͓HO!gç,ָ#4֎)?+їC Mu Xo|AͅkᏍ[>SB1&pĀ',Qy3AFGsPBP;Ձ9n G≻ñA EھU?Qݨ{<,~VbG2֌5&v kZb_ O$+at7cYcgF%7o,,8r:^{ϒϗ/?"M_AFP[HLmAEbbj+ |8J˕(Uct=7M_FK?OU&*)*)+TnSPQRUQRrB1  Za-Ƨ <'p'P;s{Չ\":՛Ԙjjj稗Wdf021t4ttD:ututu ttQ6 k/Яѿg chj{C#X ύTjӌݍ+oMMMvveP3{3IIN*&6{טw[0,- ,,^M֟0y䋓ZYfXod5ժꍵ5ۺ fMk[3[m;] /BZ>}DW;^r"8y9-vjvl|/st.ϧMN;Uו۵ˍ˭]ǝ^Cσ㙧gW^^Bc^}z|}|||}K} ?@ Xp;P3X80a©炨AAAMM!hԐ!B B a ,0lcpߦO+4*bAHzCQ^QkGG[bcfTǼ79naxx^|c)!&a_t雧ΰQ8Lsg^>+c։Y$c'~f*XIIۓ-&Nו,5yCה)}%ywSӒgϪ[?ok1EK?ѿ~_ACGǏ?~z6<3/&_}}092"` Y Jhq9Kb(!XzG=D/ Qa1Lo;66ermC02VR_##;FFbp:[zw~lo( pHYs%%IR$iTXtXML:com.adobe.xmp 2284 1620 eiDOT*(**ȷ@IDATx E񓍐%( (:"⠎ n"APFEa7! ԫW]޷3UթS{p                                                                                                          W`uBQ$       ,:9DE pA\       0("bo` %;6p}o2@@@@@@@ P:aEt|N*q       0(ۧLhx93w       00(C]F/'vN       @,iK       04K@΁~}KD1Pu kJD@@@@@@@@,Fפjz<" hNz5{d!       >P2h_ۀStQ6 4:o,       S Vy@8Ŧϗ kN;2@@@@@@@BX #-߆`WW _%Q@@@@@@@*0ЊGz       }-WE'       @BG+{b{ZYså࡯Uk>er+h@@@@@@@]Ҋ֊k}}ip({lݣׇf'o}T @@@@@@/0 )[}땥kUzA__+>͜y\+k@@@@@@@Z'_8o3{Z{9R%yCء穻G_]]杏8       hWH]OKs{<)ҲZ Wyꮯ]~H[OoZu      @/hݧκ:ksPw}]o}uhP,R{jeXUm߶b?       CKUH/kv^ɾ>1e<__oE57 qu,~?YnJUSڪא'9       /Ў{s~ޅXm|s/>˫Ҷc*篔;yUNO+)7:6]Q]\ze %6kkmfъv\G+=@@@@@@@v ‰fڔܲ^tU]7̎X-/=Rrc{m~;NҼTЮkoj~Y^3B15ֽƻ׸tNsbk]8;4lKz^|[kS1       he!FսRuN_Z^+k{-kNN9ݖE=)GyYk~V_׮}Zqv7:왺(hN߾9vkݣ:j8mSN9nܸM]|{q        X|Ǟx≇;rȽ^pWwKrͬmPmyq;L=wR^^H>Jj53eMYN|9]/-֙0f̘W^yѻ^&Lab/{t́       K`Ĉ5eڷҥKo馛~{衇^eE;yEyqseku^Y{v MּMl業:))ϛˋ+i7/=?4{CV\)Vʊuް=       I@vF%G;*W_-q/-ѣ0%o./^_|jG5yVzޤ<+HJV__Rzo{Oƻ'lz 7:/":A       K@ wZkhg=8=i1we;g^J^\c.ho :k=qn4Qڪ_S47׼k5\s{}p        Т믿+tr/-R±aT뺼5e{|~np}8.0Oy!}r򒓴ڪW_w>o]t~{(w(d@@@@@@@{'tiW\qV:+Vݷ*{鑷_c|-RVz¼"E}0몲_JnYN|+>lpUW}d=<~ꃏ @@@ G}կuO<\R6tZ׿ʅ^Xk-@@@@&_ˇrܵ=^]+4USgEsw}6_m~U+ߊ?zMSSuϲ:s5e1ׯ[ofmO?_@@@$?_>]tu]Y|3eʔ)rnfѯm$V:K9昼i    ?u;skb)1j\sek̓E R3~bfY}oTݯ,h>oJ<5Wkm|޼y;v{8   .hc=&{Í2bHyɬdv굹tttȮ*+/Y|MYUч;J{uЂw6E@@@+1cFqK? SbXL/Zcז͇mp}8n~ Z멺WY~|\x,7/];!   R= v^yEd+5zlVW v ;yDOY)TsW v+8@@@@!$t7C̽%VLmݳ(^629J{U9oXAH> zU[47*h֮`z:Oy]Ŀdg:2r2rd䎯[lѮK(W/_.+WG-ƍuu2aB3ݻ~37ߞ]]@X]zc֕AvdҬd@@ a=O&"yf8yc v ;Zs7%7xo\r(ɥa@@@+yk{(~17gllW57\9m8V@l먲_Yn|\,^7[_Oؙ>Ǵvs#GȪ[n 'Mp/Tcƈ{+Y& /QYFxysVYl2G?YAq7V"-` dM7㕬KK@~2zemwcF+w+}ApW9ze-q@@'o|=sW3`_`gTI?2}nmmiλ߹.:~stvHǪQxr#   ,&w/}NXOXc9Uby#vLg|\]^ʹh*+ijÚ[yU*-ϛSbUr4W3kWeyEysx JRƚu v:{LbLz]Zih]2pʒݓv;onSO… ү"kuw::1}4~)S4ܲ7e)8>.0v6h;+WvsO=-'Mlb   XSs8IW-&m,]D>ޫ`/Ͼ1=`5+j\r٥;Ƚ+\}|OfsU)N˳#vZɇO_u%sd-_"_n_LoQN=ߘ:90ﺟj966ufg^ cU!/߸%7r    0h vp7`O uձZhLpm#Y4W>~nQzֶdΫh~u6i5SW4!W{뻟:sctM磏ʪO=1]>y슺.ˊv?>NFy9}eqw}wd+α6os+V_W$n?y\eXS237kËwԵV£/l5af1   0how=a`n/AYߣ`G:^z_ª7N8Q?;vl6~Qhтw v+O>Q;\NjȊg.ѣ֕-*xp̂OθQ߲{i"(cmemW~{a~q^Z>?yyQ:}/ v󷪟_!_~+㤜uw,? 4_5G7pGSf[m*F\H^N,wdu3d o=]g-leSuZ c;jSvXceLwtJ[X)SVv.:mGr;{K^~l=aKZ@@@`P \xM=a',n6‹-]P v<ȼ%^+f=aO\D[k-YoD{XHJd+BX" [ O?qʨɌ-Ğ5G/yN2y>.}ndz=j܍s<#򸻏Q֒&m,Ǐ%<'҄dؑ<[J^ rEN*{X[^tlkO,nNJEn7Seڔs]$|:y<&(FMJ&ڛ݁    O`{'WbEuǸhlQ%q;bۜߦk+V*k:w>eyyX^+WBʞ3o޼yVG2_ɸ4gUϢq+GW'g~x\/_.naVWXqB+v…2n8dMK_..F&>]G v&X'w?*,тWg;'$Gov"  Zࢋ.j`}=ӮkOR\.-o|C^Y-yߞv̈́+f$X~5_*g_>6KN<21q[|Y` z}@^3 pX_F[W](ch,[w{ŢLqGC{7rv׽aIe8S䓯. ۙ[Sce9yUanѸ9]=aG]_͞}Fdcn\$@qk]T?YF|S\2ބ r vb;aX,]T/^,fͲZqw*@uԬXN֫hi2w}[??MJ   j/8+ع0IIgMq͚xv9ԮJfm=Eu\U3mc_bqws=/—<|sDxݏʥ'#'p;й)xs'}5Brŗқz<\=G;=Ȝ5=3Yr9.eC@@@` l{*zN_~?buFў?SrzZO|_nҶp+ΝGY^l>S0Üph:f v:OLصE&zG~OvޥZ7kBY<yivL6MƌF~ߏ/ڱ ,9sĖ%1#e,q;=z YN4YW~ڸ/딫^k!@@Vs?'zRV\);;RΧut_s4e={vR>]^w}c y;_&%KS~}Wwjz>/)9kL—u$^+J+J FIs\M6;k9ʌ쫾m׻^g7Iu밣w ʊnX͒7{~ ww1v?5h?Ӿq]?y fd_h?~Cky{[cX>m7(S'r_5&ĺOM@@@!>zh:E|l1=}x֏msm+H=WwъsQ7p~s<Ϟ&=V2}EF/ϊJ>'w](+WɣKF|뢺d̙2rnl}+ u(e/{Yv?p!2q devL[չR6뢴 +_u= vY3mz=f[nLاv9 {]!*Xc~ ==v?:Gt^7=E粓;J[d;]ss:G WsYWfWqIn=\to6 f?\x ,wcּ?}ت9wO@@@-[kjNv”ty݈{H+X[I/)ImtΗOQ^\, sq^_Q:=awXrΗ;S_7u}mJ'5us,F@@Z(~W>f;.[,{"q`_:7|sVny~%a+fꂝxjB9kLWs+i|X0W:Qfɛ#JeSYfO',,QcuR]jaΊG]AϹاW<,e'?oeOk    0\^SxWj\܍hyWjFF~QHdV+e*s0V4NEyysx2s[׽'<7ɵ2wqMiO30q=#*~:6jԨh!#[.xWbo:=;yqy9k~v?=o{4pS䭛)o οb97rg;?uY;$J3zxg-ۮkz琝s i{ŽM=W3B@@@`P []pWKr4GW#Y4W'_맞/69\$/تsS7*c?76g^꿧j`G'Nl͵ZQdg-Aӽ+U(;eȪ?+2}f>/_.<@WV9aN PGt>g뭷e{\OyF/? Sve'kP~b5Ղ{ gYE@@@A'[ߺHFO^Wf~?/F(*YjZDy3c<2j`ö ;~IɧJmV0+pnW޸J/JZ&7]p\Jg|XV\ӕZ_x4E;tb(mܱb+Y/kDsr/rΡIAN@@@`(x;)O5u~^81=½Ϣ9KɱܢUGEab &{[M)ϛXml뇹:ΞY~9F~Cʘ k|3X1̮Uu< k?} n|F:;%9}KdY=-'rdJ ;8lվ:]^"-{H.פqg~b>މCNQS>d   pxܲ'տϫ?y)XӈY4Wv.彲e}u4{Eyysa<Qǭ￿y1ueF{&yE&ZvE*.dޓ eթliwj3ܞIBfeL̵bkutqu}z+.'q7c={*]vmv]omϒ-Ƶ(<-c@@@?.;C3gfy_p,q[Nf[~B.sGݷԿ{>Vؘ̚3^L@@@A(0k,+ة`&q^ŵ[T 8<_Z+.j\)rb0V4Ns0 ;`纊C4}7kIS\юbwю. ?%YʕAH{DS7?/l3&Zc;V精O?:{qn^'_};ޔ WRգh'ooh:+WvsO=-]y    J`Gq:dm{΁&?/+/U/nr.п Z;k=pxme+9)"y9yq|V~~7VrƑq:ތWSvaLWmUDniktiQoyrÏ/5egL/|N9suۼ7eFeqC;'ou3\t 9u<{kٝA@@@' v?,yŽ_PϛWԹ0Wzϼxњ~^^yk*ǽk.h\)rܢqʜS֏b/UY}oD^ȟT6e5(V[^d{^:Kj]'+V~5> t ~_Vc}M*cԩS5ǯK5??VnZw䄷Qj)   J6xcN\}7E vbO5??#@IDATevE߻Lƾ8sL;EjA#?~J?:@Ie2qXǝyl>ɋgk/_7"}Ӂ1moӯ>$s?s~VL]l- ;Dj'DO{^y 9tW(qn?2 A@@@6lzwVu~[yy_uy-6><~1H҂&=W*0}eX^qVs`Goh{ȫ[M']Gd (UtvE˂%˥ɪ?YG?y,z}b`]>GM2%{_?[kD%tjw}aG[鞤z[n)L~2žj΁E+sO+n͛~|eGhѢgܸqd꺾U///_ꬻY>]/?"?ˍIL'k/njv| ?+eˤzo6r}qy@@Xnw7o?>c|ew J ;_կ?9n,`'o1Kdy(3j9V& *bRbC=&c,ot #e7]AU   @ 7X^[eArm?6%'\㏛]իlAQK9n?b*_\̺{Z^'cļy2sOyRܟ86&&MrOH:gm# gh~Ւ%Kd+|"-C c8g+8qbY}|iXܼVǒ%V'cȦko,s&n';OQe   0~zŽl+'̞3{lE=a>8޺ZlAC vַQ    xK_j;ܝWb&[0f؜Ub͍H)9Wb;v $:dU{쓗1,6֨llm^[˳X ;w}/    T/<`綿]zp_w׾V6tv`'J*;p@%"   ^!EvĊKR[0J,/׏}amzm9*VV #آA.ۧh>6s㲾[믷Escmv. vT@@@`@ \q;=.~v({    0n;+ر'-ٿEs~^v=#Weasyص+7D+N#/'/bXʜZeOq;Ɂ   @H.؉;B    0\ξ'([u.E95~byԜpM8;Wil %&7w\JiE[e cc}NVsw" @@@~8d}uʕ+e̙p r9Z"@@@@` lorf;]i%U[]^iױv Qg,fEsUr,7֦#0f"I5';e}^N,sc[[6˳XVP        C`GJV51:_ ܰΥ]=D' 6wX,*} [ cuv~r@@@@@@@d`[+JV59<5~byԜp?;So#&,hv1]~lZjx;Y{A         !ٳg}̽+];Es:p{h?Scօ9EfG"df.Z_u.̯2scJsyg_ENR#       0 v ;ZTb/+2)jm.5}_g[1;kEsS6>D' ;e]QN8˜?ҏ冱Ա6X7tOާի):%s        ФV"h]vX>aG-Brµyck֏<gI]?\)9u{+읺&//cXoʏcE1KiSs4/{ε^[XS#@@@@@@@hQ$bekyo{c=bXQ\#`@wRҢ{n~,f(EmޜWs0/6\ce:k.&)M`%$]G޾x2\k>om,fs~rX?a@@@@@@@@` tOѻ+0)r-~?,z)XjD{L"mpM؊Ar*L4WڼX<X(fsUX[s v @@@@@@@>` w~X!ߦ.oUic~ya&66`PwRs-"b}+4krmJcSƚcŬ-=$E#oX܏}~\֏[oooþ?`g;Jȁ       *ivE[~,r5GpΏya紱yEB͵{^-VsWc~`n`g{uvݹ/*9~ߊRlY[J<ay ۺ}X$ܴʸex[,1cux8'PSJ.       C@+KHwi}kX8eϱ~-s)cC([[w]mGٺ0^46_1^v榼I        p;G'XAN՛ar•OQ?5_#g|50dv{u([7Ƌ\Y櫴~nQ? ;۸~        0\;^ƱGQ6l?+<%^wubzs6sᜎbb=,7Z>a?@@@@@@@6];U*XQN^Lmcs֊\~ټ5"鱢|+jc.1gE"=urbsa[Z'ַXJca_Ubokw`G=E@@@@@@@! v ;EE:ZpbE'ַVob6Ws~_oX8΋/˷u6vX^4f!`=bb1\6/\c}ͳ]x^qv9@@@@@@@@`t>aGKG-ox9?4G8΋e5m];iHG=bb1؜ŬsַV_4~l(fsڵo`Gs&r        p;G-zŽ(XNY,VRZ9[cvbEsE.]E ubs6ksjNǖcq _sŊ~u_-ѱv.qc@@@@@@@F];v&K˜q?6g?1=lƨ1m96ۺX[w]6NU٣,hޟzyc?[Z0~,ZG8班`GF:?@@@@@@@@`w{MyŽ(_a]Z?~\z~1g\ϚU^^U]Q-iGYnѼ?~,~r5oc\GVO&p        *񟰣9VDZay1./ٜzшc~n*{Z쑺GJ^Q?qY?6o1mw8}-{}+'|G/@@@@@@@+9mv‚+6oΏmZ=[+ ۔]h$:L#%//'9[d}kØc~8lopp.|;*ˁ       0*؉=aG I—ʄqQ?u/lG՘gG޸,nX?5/$:XNJωSbkX꠱KF|jō       EkOѢ+<)-om˳~cy[65_f=Rb9epYXb )c _v aW^y ̰s@@@@@@  HpiM[rqm}Ҷ\WL|R㈟X.yO` %p`#*/6ﺴ:NmH#(ltes[Ƒ|n˒Xao@@;[GCjV\;U`@@@@@@r!-%爀7!6 R2}8sd;_گ}iK->ijOqv'G\Gr}[&##QytyץmQ1;ergײ"‘mQ#KbMQ@@@@@@@&N6mj+Vx{N;11%D<fE+QWe%Ugm%ʉI>b|K1Lĥc;+}0'Pǐ\v @# +x嗽` @Ldsl-J;yjO OX>qtI;FT^61+ԲҶk>nO'1a>'[:2eI,̰g @ZHTHժETN:itQ@@@@>_ |#}i@&x> Q%D$SwI[j6f^~m1ܸ\tI;FT+iRK;p+/gK[k_sxŶ8dI PD 8 _F'5sӕB9bzïӟuL)zs7:DG.IyZAASeNv@@@sM< 4ɒXE(GϢ;8[(-5;i;&<{ eqƐZD& 1qLjs\>[LqWį(#m nx&α9?0YhMhK:{h۩+?x7=tR^4g]Hbv|.)+gߘNO+w]D| `_p_ %a    q8ǹN %a1N\AN< mO&-^>mr\#~/NLrC9m81qLjs\>%[LqɓZ͵u\%/'~-‘gaZ ;L2c;{"e5T1wiy>J5SM7c(Έ^2imz*(m߭ƮtE݉7gsVb}Tzt z8`]%q#     & mۺ<W;y@DWțxHH v%%Q9nOX.퓶ZHoLSaDC}P[<%a3ϺB%GSEMA{1n&i!$EGP    5vڵkWcjכOK6Q{P7)]tʙQIu{b-OdF{Q]K{D׈"u^axer.)ġ8\|׵qAG D"Fqj%N+/ʧcbgS\mJѶv]>qujmW;\>,}m/Kb=( Ս$6-2B$g<R7oo>?y5K$hߎTAw7tfDV]:Qea u^295fgWurUWSކi%-dRm-gW>DZh昏'ף/;-q    G@a;r#FNt}[H'Й|:\ъ*2+Fo_;ON(yKT4j!-efV/vccA Tٖ̈4 ;,݃B/Yx>G@&0[m4bHG|&쌳8%g}Ub|ׅ..D$yO`$wn 5$ f@QˠYr5lJԽmzʳk60UjfG%FhR^q5ns7ۨG;ȴxˣ)r<\e    "Z|$q$>( P d .z讗xKKٲsI;|!Ou>b=ܸ]0cO>6P{6C:Ch=VYu{cZPne2?Z[Ӗz;bL@@@@+T\yylftk=֣֯CxC讉+ˁDyl1+Vr:9Q2@m>i;KR̼}2[s3gfd΀:l},R3Y潽X;U_|(?AXaǑ@ 8;,4#ƉcE}pi-'1/|<ɑ>:&89cv6@-qLjsl._Hm+f GڮNݻw Ջ@׳o[)m9\2|&׉9.RQs?kVMJT(+D}ȍi{yT[D{?}OYF 44ٿf>?:]zխJI"JfGUV?LMqJ)q?;|{پ 9~Iuhܓөכщ*s"lhӯ5F{܎M    pZK۩oQ 慿~'->-)VW [iXŵY}ܓәF$S95A e#*xZFs&1a7Sc$4~?Nm `{}O@s'?-1lXjhwm3K{EOԳzyNY*;щ{ӌl#mtٶ ɖA@@@pq<?'#ع9/Uf>n4/m[گmΑ.ļd$GHi5G,ɷ#*}v[|R|:,Gf;N[\69.>m=+1Փ@2b]TrwL_Ahf ZhчoMÆuzi@4hG;+%fGRWjJ̴V^b"#ȌN_q}|:U_om4$OgOO'lB]J6W|3lwݓTg ,#dZjރ4@.~לW].M+/Zԓu0kn23ᘭ==7T-o=M?d69zہoGsRzd$r vԱ-nfas`Of9IS"v*6/-{Cۅj<l .|Ϭ9h\ڭ_zzͽ-*eGQf]?O}$4nVs.S\c6 ؉ i    pX g1]q9!x>.$P`F pXLOLJ ѹw%_|am[\0uѵlW\v+Ӻ]#-Ӟ:+2/?f徥ЀDq%KxiX+-"i<O <?\%jK )y̰5,&-~Ws%.c׵+}lsͅV똗r3\Z+:n?D-qLjslnk_T~_0sE+1v< TkAoYFTj:~m}t^c*+Oo<$z|#1KlJ>;@{Pٞ2YK\;ޛHkVJK魟R4V-FN$Ɂ )u<71y&3P\?{hFA]ِ>$gXzŔ0]{+ZT#F4o8 &`z%I ϫ4:Lc}~m1/CZ7"^тSE׎iKt]3?wǪ!]~F"7{+gm%ѳsX(^{fI!<`fRҿz6y~(vg:0    'PhN6V; HԢOi;22'73qO߉ѸmRI1脓O$E+`b# i5i?nmQѵE<-eK@oLSҁc`)K~wY>sOۣ`l%2oE?O/lϽVcbE~a^>Юe y3HKvr>    Ph?q</U@0ch>2 HX%J1|xQyZrum.ֹ^G7S̕/>e?VsgqH%Qy0Ebqkٶ:ORG$'S-bW;l#GjI ?HL~iV2;n&)?&AI@IDAT(*G6sΨO?19}'%ǧ.jۖkԈzh,_US0N Z`~OmѦgSI~*]pe;{REL]    p`SNyljzE[ɶC!WAڵf96y\D3ˎ+ˮV[l0voYIORNFԲ~`oK*o"7t_֭BK3S!OzќXCjoD0fɩs2'gQ*}+P3ړ-3c_Ͱv iWzd"m/9ÎKfˊ;:vmϸ}h9fU^_B׎}[k\r ,as.%?-ec^,%8@A d zb0I`9Ȯ$DŤQ蘶9_Moǩ~ma>s[q#8$wKþݚ=lSې78 >m8i$׶!JPS/ӷ*i^mܸk># vJXafI.?1;;/b    @;W|@YĻ4/yK-={W//}gL`g}ݑ3sLdqK%AJȥ@|.-L(]uN-_ LĹoɞK]4 MWTA6tPhۚgϦ7_h,p\BO6Q1 y?\1.5ۗfs\\ڕ/1]}!cDbOl>eSKĖ ˓|f%ʑ>o/ĒX#?H6?He6M;Ѭ-֚Y7S槦[̺c_*h^ic:Q{SDeF,$) bjvoqgة<*P5gyc/[I5)A}T|ܞ>e?yQQFe Өu"M[@wG     pTZ&X?Kˬ9ԯi~˵tNSС/];<$Wz/3+sI{?&^{Rd`ܖg?d#Ҹи|2sqyzJ`5 Wc9-MɹYcE{s.F,˞%]4)dFo%dՄ`'+\HD@=5Ϟ|<3@@H v4>Y%qXhu[rjηcq|EM͹\x )a6u̕/>]u<'Yo;FT+ft;jWLؖ[jObvE8O9WQ1v TkiۣH?-A?N?>49]?{K-Rˤ.罅K{OKg&[4.)KZUagfO>=~yF+JN+>KiM|wZ 2/B& uIowќVgi֦QfDHM"$[\Lf1bFlwV}컙9(@@@@(NΝ؃2zYkeieߤ~ч3?Nl+3. 9~DMv>Zt̷ٍ0}یu2=2/|C&I74s{O7\4.oyIfIjm6 ]/Pf&OsC>)}}KnSbm5xa456ϡӕ=<-h]/OsH~*h"ʞz@$L</U@J!a-6L1W}aS3$.5;NsvȶVwTLg}"cDbO%71m3nO\C|Ҷ머ĸgh ;T7jKǕx<чK &ҳԱ=x4^pY̦>_;߼0C/? Ͼ` vhDG9=!4֫Igӥmƨ977e4{t)'ў_zJ^l_uTry9"l{RgIrz_/Vm<_T,,fޛ2F^Mg>D-G[tܲSA@@@B v4m=M(N}~<эG̮w5b"i׊4"VUNV}]ަɥύP_`dsFo#2_OxJ/>BuЊb#K K@  ,6VfpFAk8-M#ܠ7cm\?&{ vr.    Ppx>0 |n /|d؂'*f5ݜ"K:ʧc.[|Qŵdsڧmfmqt+q_'1sqmq&d`@$gyqm烙R{McXp]+J:5M_Ng{;h|ft%Olfjf1zӍ'-J7=/1r3S|3S.4aegt^Q_bJۃs\#mfY`4"aO%$` (3gYcw0Ӑ^_y'rܷ6%88۞g8Kqt N\RI|p^_>_`9Ȯ$qs~1ħls}a~ɕTi_mǸEoGtf<'Eh8r0>dse1nm[hcclՖAi}^Q-]W誖i>h螻{)%4sʻ1b-6.M[{+A͠13ȋb?ݷqM=4=>d6MEͲYo[azRAk?Jܯ޷mtםyYtƮ5i܏Ҭ`ߓѢ[S3˙;- #F@;W]uU4KbLOSi7tK6^I?G?"uYo*s<}Z=Gwm}x烈LmIxo|j9p&l>R>yp+&w?;bWA@@@Hq<?UQ0'9a1]'cXmǸE{RQTV:iX$w۶2˷vrdL;&m!3      P`ꫯQDž"1,}i0ݣߌ4Eڕ%r‡N      UCB[c9G>Q1ɦfBmhtEv\8'KeE󜶈QθcDbO3t,ʶcI,ZD8҇\튉@@@@@@jvjXƋN=X֯'jO?..93Bs! E@5$`m~O]6X&Ssl;m.2J̸yO`#wb:Olȶmǥ5p}@TP@@@@@@j vj̩N<-) (3'pqql9_r#\vLm[:7ʶcVbO<'Y o;FT+ft;-\j݇mW[R3C3" sm;^      P% @S%OK)vj̩ā!ψG\|u.ݶm;bSQ6߰}>͢|K1\1ۧۙlR>l◚7v+}l30uE@;\sM:.L ?,U5@@@@@@ ?`8ZmӸ|:S\mq;Z7O l[>dK<ڕ>헶]3Cc?uݻwO15 ԈXe*{jc      9H v4]̇6Zpm[L E򋏏HlbR>Q6'әqtf1H%QyLij]~i53}fa L~v, @"N:U` ةr;      %qX|bP1iK_6΍VwTLg}#cDbO3ϦֹQ63}Bns}'@@@@@@jtҥ&Xp'X@^2vXL[?nm_jdK礼Ό6 E-qLjslng%MsٶIvjrmfNݻwO捣@M p,8K3Ts=Ov0ͧ|DX#5Ic|o׮Εi'\j'ʶc"ҿb:3n,ɷ#*}ɖx6e;N;,O"ʱc|\r ةrF;5@@@@@@sG  M&ƑxTybru(ێq oX>qtfI%QyLiju.ۮv6>窣r옴`'+A@@@@@@@@@@@@@@&)mnKDv\6Q17O lcDbO3Ϧֹl8W62s#'2P@@@@@@@@@@@@@@@F@ vc|X@5kJq|:G\j'ʶc%yO`$wmu۰tعԺOmǸ%Jl{qtf1H%QyLiju.ۮv/,_6wv>60Î              G`5Z\m>-ysk}-v.e1ns+Q1'n,ɷ#*}ɖx6eI̔m-]9v`@@##,aZ]9OΥ}l;m.Qb6A-qLjslng%Msv|Km t6}l\{ɦFH v4\f>< G"}v[rعԺOmǸͅ3DtyO`%p`#*}ɖx6eI~.pm "EI Ih0\|:n,ɷ#*}ɖx6eI~.-б<}.;~)}'5 @9qG˷(       ՁW}>O!Ihra>]u<'E8r0>dK>9\  GQ*m,Ni ;CtV򳏡3p|F0       &&@@&<?2 aDO-ڧsΥ}l;m.o~+;*3>~cqLjslng%Msv|W+}lF vX~裏NzQb1?HV?[qDs[}с?7K_kr7l@@@@@@@ #<ψ  5dB&m3tʗ>-w^Nv@@¢A/6N_eۧbR>Q6ϰ}> ZqLjslng%Msv|j`ܹs'~ zT3`u 'KhLY5$ig_|}>A@@@@@@(8*|3v\"/,WƉSyOΥ}l;m.a%*}[^cDbO3Ϧֹla>ɏSD8}̰g:t]|9\~  Տmymsa34;|h^I:s}-7'C~Fo`B@@@@@@@# 8|!nrxα}-v.e1ns +Q1'nwe#YLXj9vBr-qLjslng%Msv|э>is]#;<֭[Et 0?@ 0;jnk?3Kk|qLzx7 [e/Gf Uc~b|1=ԥj׮-C8|@@</i`'~]mn|r\u<'?A+7#QyLiju.ۮvO"s]~'mk`ݼy3]q]}  ՐgaA vD3H _t{dz}#u{B 2ww>:㓙@@@@@@@4<?ı=@ { ؑ7+W-6v($_bv6ʷ#*}ɖx6eI~ZD7vou[Ԯ]|CjCk;<.Of15٢u>8Zzb!LxV?q{Kgv~Fv       p `J<OStI<l@@sG {!Ih rKLqtlcDbO3Ϧֹla>ɏSum17|ڷo5/7QF[G>Hf:M6dl(   pt Ga'5w? (_E?ooB#vɶ?Ǯ`' @@@@@@1|h>~=5,ǵ>o/K[z^_gYgէ#)*߻>:d:_4k;|Mk娥{|ΩV</,hvԋ%f0SI4*&9\}[^cDbO3Ϧֹla>ɏSum`'b-3m:;C)H,;617-z+#2_z}YX LМ5.-}m63! @@*vVOZtKiiT|4{t=ow~ص%mR2р[˧vSw^JW YK;ƕNsFFq4`qԵrmZp7]mue鲛Lk_Hŵ7W_O i^e,΍[qutj{υW`7"bP 7̡o^|6cY2-. s}x#C_9 4i]_+ d/PWGx>^سN`FgvON+_bPGF1\1ۧۙlgS\]0ǩEtcO\CX҄MhRg0p67aa&{f p;;|yӾqԭ/a"3Ng3ӎqoHk? 6}r8|A7;> |!P;[&NO=kQJۓC&x|zebBt)m2:L^EKLk]M"\(-w^%:X=2-Mˉntִ:}ΪԾ'.1>WzOJ=iF5uVL. V *z =v"w ' @@@@@@@ 2ZUmY-P󆩩,{޾u<.hqMVZ {ҹoѴM6Zѡ`xnJ>QIˣk~DVvdwfU14-ǹ3gթ FT2jCRXpv9~2=T<:jIO[lTYZ Ya97D-֋iꢙ/:tj7lJ-6Lem7.AK\32Y"ڂ|^(k2mDz`k~z;=)23bZ6ݻ颞#h@s=筌6;DT ȻD;2Bq </Y`{"P/Ba|^|5GWD=*>dKn=S^N_|כCڤD;^KГIюI:j{Oɞa/8J`'}޾s1uN or0o`I P z3?+mv&yjh_fYt41u~`/I vЪQ˔F+m/;i`ݱ4;zч4S'&9D`'#\{')}r#nurEMm|̛~,*`GaQ&tB&m3tʗ>o-qLjslng%Msv|э>igdi{\ƣӿS7b23e));w,߻vOj@ePeiki23Aר9uk;!:.#oe;}C'P|Wl!wn5{޾@̶bjX$uZN*3?;8օb6  w]1<7%]Wt}˩cO5AϿxDW]p, Z*$דy|;6@;>$]̰ @@@@@@{sܺ'58+xS,1wxӘie:L5希g\˂#,o%7pN`&2!Re^#,j}o!fҴ,5-3~S}3Ptr` aCK-nL#毥-@) ϥf`'"i'MTo$Fty:0NLJk/n]{:'.[M7Yi F-5Q.רž#%9`Fi%mW;嗱2f6umsu(ێq cX>qt_,cDbO3Ϧֹla>ɏSum!q\틩o_{Tn2ýܕ0O/o v5iωtЛ>9; mY0.6\v)-jwVũI&nPS|u>>6ly ƒXO|!PxNϡVf)ky{3K|#gfWX)S;xrxJof:2c/I 2zcZE!f=/:8Ȑt4B~ґZܑ 8=7!Vs;dC _IzJ+Ze0NLJkqxk/Ysn2+[r></9`'Mt3fv;''ȕ/1]}E#cDbO3Ϧֹla>ɏSum!ݺZ4 vG,v FJ.ѡ{wZ[@wM>Ig̛7{_s#hg'Ƿ)&O1Yl\ɍ:^dLSb:还h[X @@0֣f$־6]q4:o9Iۓ.FSZOe@,L,S7:(Eߌ%*@@@@@@B vr} dY>j?<@>tEgP~ǟS~OL|C\2y)ށMqr:ЌoOh9I_z?{uk#f?,}eO9xtLmmPEщ (ܤޅ[KP:Pnx O[_WA-K(C}|\2]{>WPNڇlTOXՅLAD-6Ca>9A|:n'p`#*}ɖx6eI~ZD7vo N"%n S7< zj$hPI6#3tIۋM7L||BoLM}FR/3mW3A[y ^&vJYݴp 憩>pHMlk_@~ؓħ7٬H\^f#NF#wɭ :f},<_ged5R[K)̰%:򙞷Hi!~d^5<7Oi_oiPF?O4SA{=ukO v7F+5Հ$A&m3tʗ>|ms1>dK   p$ tFcT\*_N~FΪkF(N:Ysߢi=:8eд~;:y44Mp%W! o&ιI!)3K\,4Hl4:/,eF\3yqyמ>opTx>^سy*a6gp$1tNr:>dK)DUhɰv3@j_/d8>|UmVӱ Zw< Mz1؈q&:8z{*M֎${tN>} hTcXOw M]}»ɕ_?Gx0uh"5Cljk/%:\מ>]N67LY}xa;i/VKm3tʗ>-osGF1\1ۧۙlgS\]0ǩEtcOļPI Z GVgHųl]>~ - mhglX0.6ʝ\ܝFn)}βu4kst۩SshnvV\0w>L9}dzx=fI`LJo8: -9t/y޾|BGj/J[fJJѷ[<74}"AQNrxl3^Abh!t';ͬ'g7d1Q&EB޿lMM&1DM(>4itP.4)+ew89>\{ 2FvמYR_Q vu"=[줽!oKvON+_b 9qLjslng%Msv|э>iCB-4.oN/* Nw@M>8\:e;};,߹^h~,_'ӶYrڰEz~ы&/FڅV@G   Px_vg7d,/rdqN| /&ev@IDATɚۦhW-}Fh;>$|!PHNϡcy{b }Arv>jcԲԵ؀gL>7|^ؓN`F޸0dma>sq*4jșwZ3z6)d\΍ٻԻ({t0\PԺiSYo]lZ0:u3Y96@8;>ɫ6+3Af.-QՊtAD<6Ca>9A|:nN҈;FT+ft;-lj˶8n\ILwxW.H Rވu2b8% @*`L M\}Za2y)ϛ"۶X[MM7[#Bv97m{X;fuCɿu8D0gfI+׼\eP3c_.5cވuFvJ?>5Khq}ާv j8 &999?oOUfq<TrVoѴ^MYvr+@u=Rܔ!H.Ed[VN1K]_e)g:_EKw!i>cgI:j7?h,jÿU70Jc̏O]kf*V \{#}Q~l9RDe}Ъ^| 4!NHeIp$1tNr:>dK\{_{Pv^3~j3t&8.bu<'wv.F1\1ۧۙlgS\]0ǩEtcO8Tϑbҧq7i/4:^aY+_ځJM-1:RrQjÚ{*ko^BA ,<Jp  hk<"p W B |\(-+%(HC! T"gf왳9ᑰF{9;kYᰄxNtsCbarF`F`F`F ?ŽR}h|MۉUKG]s{4s+inYJî3B.ҼhsTMeƲQa2iDi$˝XhDNT87Q~*/M>x 0$!8}S@c~GISLuл,Tz??So"H"LM q;TWrvy@]{rŻ#WhR~\4dzm5=?9'"T{Og y+~R$` mtjw{TG*S9_+^6&,#D нe?@:}YA6v^lA52Ar%UuE^WI $xuurLSFhRQZrj{Df7aIVU皡Ym+KQqV Ьe31W0#p"=[VyeJ?zS 5ydo:R]aWAɄ #0#0#0@ ބy1C+Dh4dIK qT1>v$qKt -DUT  QVhN@OD~!'ގO?AbNGԩ^6 WݖFbe*/6W E(zu(t[]Q䣸 UdhܲR]{{WƩ!%CjvLgRx:aŽma Ik7+Ʉ\j_D7b#+S>Ty]O&tɽ2YfN;uF`/x c4A#~YE!x昼%̣EF J~*&cPx<(R㲈5/dxSӠzj;/L:5=IOW畩hyYKꊼ'ARI^,3aw-gF`#WApp׍+* !6G[/ǻ/2U:} ]q%8 |=,`F`F`F`דS3_ZJ5]=_/x p"2-/cIU]וdR?H*I7^]+:CJ/}2sFn0#P;(Doz ӽɉY/H8T)qE4tmGdL[P*HHH]p0#0#0#0?^|  rk|<#0͊3a0HPU޲LחR]:=L~Ar%UuE^WI $xuurL a(((wߍ֭ A1#r/ /[Co@۝X(T EV^:Q;՗+qՂF`F`F`F@dFx|veUZLDA~2!) T/%ITېV!0htu^Z~2$Nr!)))n7VaF`j?PH,нΉ7!ŰT=P)0 3#0#0#0u@ǯ<$#0uE ?v\*޲L^ SӠzj;3ٕ3Ar%UuE^WI $xuurLa… 8sa{ x۱#0#P{y Ep}W:,*k*zYN4!ݯԵ»Nx{?񅶷`F`F`F`_GyhF`v&ؖC 'k/TׯWǷ1EG$=]We},+򺲟LI%ƫ{e\;.]ٳgQVVmۂI;>n\0#Ph#3$<6'^ touB^]Ye_N\\X8CnMN0#0#0#0،#0/LqVT3{~2ytNMmjDZ1fIOW畩hyYKꊼ'ARI^,)Ž*++q9iӦhٲq1ގ0#/$Cz85/dEGTW.QSjG._ :o`F`F`F5dFx|gŽgԼ[ ӗujTOmciG>"2-/cIU]וdR?H*I7^]+:Gع|2>S|džr#W:)0#6na|o~*b)o2 ?EPԽ i [+#0#0#0#p# 7U90#7"⨤5/dhSӠzj;/>5=IOW畩hyYKꊼ'ARI^,9Ž ŗ*|gyG%a7)gF`n6ė!*!!7F`F`ŽgԼd޲L^^SӠzj;/ (}Dyej9Z^ǒ"+ɤ~Tn:W&LةƍMF`F`x!駟O>Aee%D^`\b#p8o:8Z֍?CUx-3w\{xݸ@]G~F`Fv\D<޲LӗujTOmcovuL>"2-/cIU]וdR?H*I7^]+e&5Y`F`F/s|hРѬY3ԯD"ͽLVyMwWU]*B']wwxF  F ^cF`F0aEQ3j^\goO& SӠzj;/ (}Dyej9Z^ǒ"+ɤ~Tn:W&LةƍMF`F`" :ӲeKjժq{FV"0WDءo_38)un_"[I+ߥP`jnԊēdNש'0#0@T"Lj^-$:}YA6vK#+S>Ty]O&tɽ2YfNUn0#0#TΤwy'u"7 LV %7[/ U ;PחTVtO}cI|8uㆾ<<9F"t|b#0#h`ŽkJeR;oO&q:5 rT#Hz:L-GXRUWue?JҍWW'd ;ոQ #0#0#PJJJPUUnݺդnzUxرKr)GgjíB{6VчPe_R{ro+.37np':}S\f>IF`F`lERwԼ[IluNMm켽eKb#+S>Ty]O&tɽ2YfN)`F`F`˗QTT-Zwj "Ž<$9GR.SU.j*ҏьf& ):"4V"1|07&nܘׅg :}3\e>GF`F`"5/d\SӠzj;/jz#+S>Ty]O&tɽ2YfNMZn0#0#Ās)t gc:ķ>5ϊ#ƔI2nEڐ )5uW5/3t`N8`q]#p!MtTF`F lR7ԼWe?CoָsJƾ&0htu^Z~2$N2vߟ0#0#O?'O#NMubKG>"2-/cIU]וdR?H*I7^]+e&#0#0@ |Z*#P7/#ި kʅLzǡTٽt O^›pE՗yI:O_!37"nWq5jz7tx5Ak30#0 v\DTy]O&tɽ2YfN5nTn0#0@M(?}D/%qfh"]S`kAox%bwGzmڤ SS㴥#QP\H;'6|sqB9tImbe[/a yYƄZ<9М݆'EmWsݨ(kY{>?66WKXaM{b 5 LQnY״7ZY _7*Ǧ!O&w>C gzK*n?iʏo“#磌ػw/V,}{|m-ۅ'~4]j:Uo¨a$\U}O|+8.k.ߍE?[s/ވٓur]8sI=LkaUuF`F0aEQ3j^޲L3:}YA6v^PlA52Ar%UuE^WI $xuurLS0#0#P]ۅ~<Yt=̕S0aE"p5 Q۰  HU` M&U˰v.07~FgG{gqbL B6 j> ly@v$g<|51VS Q ^A7k a[3G\yssKJ« DC_]qձ\L!R$ѓZ9n5z|HQyxqOӈ:vBvY8[W0)̺=ZH-_"H ozr#>$@)(Y'+3E!֡x gVlTMsj {Km,*I*lh?bv~̀QI $Sox;kbHp#0#0u&[J/j['o2h*IOW畩hyYKꊼ'ARI^,3a'ؽZ#0#gX^qcT"w,b4< +7]j^oN#dR2\$#N_^+1d֏o{I_2L faV =iu*YnY'᝿_B^ 6iBv!>Ʒra0xJf 4h`ʱnl{LhMZܧMޅ?Ga K1z~zW&RFZϺZ~.X7LDZgb'8=xe?m~"āsv32!T_S_]7W(Z"L02HBrŋYq$xG#<6=I8dQJiP@(q]xzt)>ST1*`K&:~8N6Θ5t]qwIO]HnUxC>a)#0#0u&t${ˢB' "Ih}zڼEHz:L-GXRUWue?JҍWW'd ;5k=#0#0Q!w7G3i8/U%buYK+y #R5pkoj^4r;1u#yavnaKd|klnsznFn^ɭ9*5y|`SDBK(섅6OA3UTy]O&tɽ2YfNMZn0#0@ʏ14: >3H3s1'% d8н1/bK+3i lDm@ ꘡MH4MĬ0~N6H6cS8tNG[ދ_솖MB(-,D@w|zMѮ RSԂBn&B(<~VO?]]dYy9gA@Ѧ}W$w'єhFS-=u9w :zQ&M]SR 3yn^xվܳ+(y9F$܎{][MzŅ(Cv}Yi)\0TjK9Ķ "؍Ej /8BlQF;2ĠÁ5.ὠ9O6&c 㽿OSгy"Etm'I NI ^|1+iJ-C1yNm[|[G39TQ*W}*saLypӖXc+,u.҄=2%\ Z ]缭_LEaV a^&OCSw*]t j0W>BK!^v+z)ӦpU)i( q;3Qjg3X008J Uq:zkV%L㣞cw~aF`FA ;."JQ~d:5 ޭ/"L>"2-/cIU]וdR?H*I7^]+e&p*#0#0#Pm0~2)c<7@o!ɭxk]2T~xΔ3qO} "T#lD,;F&DƱAhmhE88S ׯ"Ltའ1|B67CZp 0Z,lidG?,N$臮D$U[ R!j_L?ÝA QTTfk}<0T?h@fmfO^90,W J<`!XZ/tAѧr>ϐ:|gtu:k_|o5ԡ2:]8upȡl(?Ƞͪ0!شxz7٭^(MxfE{hɖfsOsH٘1|yOݫC8=SLbG4t5nIaOshLa_~cT1t(95}=yI`B>0(zKDCOhĦpMRجa6K})۞xv'Msybafغ v/y?~|ky|}Sq<m=!:~X0#0@]F ;."JQdv:5 4R#htu^Z~2$N2vjzr{F`F`" :BuXGD E[߅N.0 yl]9 nҏ88? A!<Ѧ?k-e>yOֻh =c`9M?eGCzeV<1󤹻;9ťWY2\C\m@Ɨ'?n X5csw7fkq6k,ulXN7瞾(he Am=X=jxuM1bc_;<r3 zTWW @PGP߫T%1Rw.8BdYDicgpf^ ]dc0=B7MþsRQp4T~$MϨKUH[Hb U/Ŗ\]k&p,d}/~.|ub{&#:F݌GH[Zu&<_;d~Ha?.}!cz1mQHXD)ru0/ELiss F_C>Ù̝]O}$a_`\nQ !oӏx~mI>Y'#)lzK}[OuE45Hr\9\7yÑ3Uh =:X'%;fuC% }#V׮ީVۚxG)>2⎔( ba;YZ~2!) T/%I̝1mU`a>"2-/cIU]וdR?H*I7^]+e&=Y`P%Ҹ6WUVV^Æ657ba q*/3=!Ч3E-CNzGL8PgG0V6Ph,}uNgʱ5sga)7. K{EXo_uu1 cuD!e7hH+:Ċc{ x[NF' 0dʀk cg~zE%P{7KqDT"4}EYӈHjK|X.ZbugLuډ6|:wSB9 - ,;g{xW#sEQnN9VI{9ۈ' DMPE߹ZfSt:|]n=ydžkF`nx<v\;K.[[C/41[PL>"2-/cIU]וdR?H*I7^]+e&TF&WqG4@#ԫ͛mzrόwqgFgÂSyrsYbmx} )Ly#N¼zq H+oab0"U1T(=p`j2tZ,N`r/}hø?d`\sc:{:N26sL/XW׾bjš7D#$+,_ѮqeVIX!  U9NڳG-G\F4xα$v'z<ѐCœ]!aBd`SNL@IDATx%0&y#]'1E@1֚I {ꑥJ5ΤP.څNSf*cɃMx1ax^Cx;A 5~"q B,z{5)+v`Y cteQh6zժ'ԂbӼCI}!toKd@5 WGFn5dx/ O1C;ٷXB¬xT/X3xOSߔn#Cυ7F5׸!0B6Ӄ 9%X"!Jqssm)}m v7Zt(>I} Cfyf-[ҳ^ݫWCEq8(SYKQgS@r9vA4m0$as\6.u ܆g'Zp^,{z4lNM](BFVez )isŐNy($Q$Z$ e p+Dti@FN< Zc fJ.^=XFdpYƙTO< xB!q*wCcfc@}7Q(FFE&T`g_r#$¢X0KnT*&rwZٱȎn;8&ЊDsXs*FV&S48mU;Ŵ~i_l\Ŝmthg?BϽr_"7G`ҋ8X6T7VuK?m^6Vy;;jhW -ii8ef>\Y 4puɔ 6O #8)o1MщUD)ŰW)¤¤ԯgN{"#p k2Ey\{cC ;.")ej^He?Z/TׯZ1ooEԊ\Hz:L-GXRUWue?JҍWW'd ;IF? /x$Me>r &@h}I_0o\/a}S3g8Y$jSv(OvDar'0}2Pr?Fuvr`⃀jsʓk ؃ߚᒴbu:IR'`6w{YKZFuhY'z &MY!%؎l հz l]~*)~k&z| '?YX+{ez*BkPI8GabBRƈ%Epc([=MCd߽f"Mؐ9j(7"\ 7aWx ^FF d$ G̓Em,b?6AyyCٶ|O<{ICy~R'Ɉgsꤢ;^h~\m5B ;9֯ vO % eQ\s0g1` kvaړNQ.?M?]Dsd*K^;zjNYT6O.G?e@>:uW@D貼( F]z5ֿ:R%|&Fb֚>:>=YT/uZ K_ To>yoj"2rPd ,Hi>T=mR#+S>Ty]O&tɽ2YfNл7|L .@]bX : 335b˕~(Ǻ1]X=R;d((0:KT&i%rWk*FΣ⹁a>+)#$2J㈘ۀ$٘X8\sswcA#^?~Go#DPfHGS{_D|[1 e]yu,?aM{$w~WQC&o7C RӖB +TM_ ;NJ+e^AChBr"OJk{QF5پ 뱘ؗe YME[y:Qyq~^ƯW ;IEУF/::[Bj+.!Z[Yk T)/ ;y=! ynOY!L89^f]*Ѵ1ax`6)LSK_s 3я ?`œ%ayeՑcpHGj18x -wJ9i9m>z'f Ή+ ¼49=:9ި1F=`L؏<搶<M T8SK4Q $I- oLfy,_t=݆џ\reL=^㛖B0ʺ UdgScDR~_(nunD^ Ƹh*v$RL߈}T3q˩YaG~eli[C9%gCRVQ5᱇+Xy.v,b5_Yޓ|IKcpt\âxsK <P]C{OZj\7癞pQMKV%GCb'4L{E8KeY ՊSX|(E8 &3\AZTy]O&tɽ2YfN5nTn Xcx@rӹuR z<NM*}|}R 2%2NU"glL^"2o.jqkb~,gucj#yzCp"ԋɑu36~~pa'<ϺĘsZT]Aѕ*Y(a~<>}1W)TwRu>i[QlHػTEqW;mfdmޘ~}~ U΃ u(k2I(}aL6.1LzCˋkTj.`TW") 38{UN&$a/Waf( ?E8Kv1_0XFIYAqRO+e:T=PF]k!a M5x #xCy 0yN:dXN:R' l>Tgs߭+Y\"J{ _uЈ.JH 0 "246 J?G}ѱm[բx 9f|9<t䑜;]w];9!θ;僪CvaYqv T3-ì{}a˙R곡xiNJtzgU=u`f=5J0MZ~=F9QJ!z9 )![@ێ]Ж1OwbLT|9DD뱣/τFC)"Q]ŏaDI܍c9aׄqB8ZI&Z *'}:g1B 0ku4}ݭZ'c*kDPE) >;cbA5|J㛯Q֙R$wߝ Cmڪ ` =FLԗ0ك^W:kÀ9{Da7(LGqqv/_SŰD(*9<LNᰄ<\T zǬïKv,$dY~ЯíxKxe)V'?s-5We~-$DV乱quCbQ Pهp`[oe>k.cQ<7geQcxd 7; :65)spƼK|:ڏ 5lښ}T4s-A-ݰTvT2aH$62z0H0H=Hu73'W%."B[=']{!E} $^mL0A&鋱vEdn,䑜kgcx ~̦;W>9'u]5$hfz '4E^ ~ǹ0, fNR;R5~1dе1KހSBSq|-E`+DgS/ӁbEF8 8WMLqfN[~2!) T/%I-5Ba>"2-/cIU]וdR?H*I7^]+e&1Y%N>Cu]T)Rd`"<<I O0.ڲ?dL5x?rBO&N:n6,!<(.ǿU\4sSwTDZ]-0sm.f Q^m7l|K\$[_,\bq7n8lx}tlVsc>g}NfӇ}A]ٱ:ˇ[bٟ 5} [qndɝ]O66\Y7Cܖ#&WO+PL:QC6<!X &BAaRa̚xcE!"$y1x߱+VE4 Z2QpeکV^|;%{^c_>tDع@ܔvM4b9 FÃ=Ea؀yHv+ٽ_rRot,{ [lҎrcYgV_5̛Joj(\x{ 4nDyotWi;9E-}K[{&+SחRr7BOH*T ;o :65)wݨ'czsF}ž#N!-*zz60T€k Pl{+w[כ֯7~H C!ۄ>aS YHۧ#V~I*ock%RLmR\qP1+5lrw Γeuӕ ;׍㖭_Qc&UK,2[μn̥:*ʙ˱5s^(te`pǥgJ"17zJU731WEC &둌1v(, 4 m|6x rShޓ|&:'fۓM.uqhJy øgF?(P3ΤZ3w-92giC[FIf;GW;xbn*BoZ,3$IğM@(:Yd/X'ks*AtY sj|ZXSt**sиI/cּGԮF_nOoS#k$h ; D"?Y܊]0Eh zj_cp%WЕ?[8 \Dj;2;-keLޥ>fF,xf8څ .}#1)"/ѐ7navyAx#9>"EE :WZmbL"ه{<N۳i&$n?9]51PDwKк ExpLX ]y\R_껪ޔ){OyQrC8w!H 'u6ɂIFC5m]Pa>"2-/cIU]וdR?H*I7^]+e&;Y/IGdӴ7w L5 hY'#k'FuBH\zY'Q5#1[YEkñv;.;279Rm*DdcŜіq*pw>>ضi@3Ey#ufr6`d휇oPz8˗0xp֒!xhꡈƽ7i"d=; )dTeهؤ/(tGJj[JO!%m.A3%  vB؟_H8;dˉxnį~2 .FhS}L~s3+7Ёb?y/Mkcڨ٥azex[)0ld G\~E_`FHt$io3HSYYo5HS<иC[*| m:irB%'`½v_h-[)wb\gxq83ukP1P3Wf BM3Euc՚مϤI;wc0eTxcVNX#Wᘰ?ξ<+'x<^X0aGݑ +oLVF:" ֶ]N&htu^Z~2$N2vsr#lb+S\sUi?#Mo畹$P6yE(@%0&RCp#"yA$/E%%kc鞗6DyLooXey:qf+SI?]5>DIok; v W[6Ӎb' ;n,B^#To pWjzkЯǒG c0ҖrN~ ̽ x:S<}؟)BH5ݟ]Gx>f(#eE;ؒnF]'|?w5 Nچ@%v<FKR dOw<о9*JĻƂmBPq>壪f 5mOGO<VnB)!RM&j$iF -E Puisvc+ 'k㟵>s1ǣ9gKushY ; v o+݆{h]bS"< ܊zwṲ:h@!> 0J!n׊#xqw7 7馡]H[u'Mc"KpXpXfE1:QՐ$Jh.2_FY[;-vgtf#Nq|viž3^Sq?O.dWڈxYz` v0u=ZI^xDҧ* \틙ۉxCyOwŽE)?:~0øEc]'˟+Ji;&x gQ{oӺ^#PT5|̓0KK4br5% O=;ȳɩ?ӝ0$eTnѿ#FOLzϕ}{,w1nF%TsqUi cV«SDŽU4ȵ(G&6=ȶT-t KkoSt1=gs"9vD9llT Rڧv,!?sAɥ/ETlFj,KE 1Qtk^<aG.$m,,C8Ehiwa1TO6dMIxR"5@~r)4o-VTJ9Elgy #w _g쁐G%6KWt>?&3*yF͋[J/4 HM}Dyej9Z^ǒ"+ɤ~Tn:W&Lة]"$aP.GjA~ +D$'\i"ݤXg^nOKw`Ci>Ugw%1ߦ*ԫ6m۰&Ѡ,BW"+WYmmZluC?j` ]i| F_-t*XBM%D>sϝs̝sބy23sf3{|3QQ£DKPΝt3m) >uYܦۇ9h3+ L]gnè\ki3y>Uڦ8~ jvzL[ЖS0q9Ib4^ 5/W+v18N>5U\Q,ӊ}J"qx=텋Z(}|Ί)g603ƾʲqi1'tTEqgXDXDa:kt|W$O<y^ce1!CC=`?2mE?o\i@oTP\h!Oݨr/|ƶig m/5M5Vpq?|n"t~:x;&RӖJnTJZkܶVQ/[-H+Tyy-dRH6V?ȯaJڶC~!w}ckۤ$p;eBgt`FsL[F9EIQooX;HG3m!?QInԜH', s*iX|ɍ1D~NbH'BF>N5\>UX0aǵ.&eS/4ZƖ岚m!ϔT=,IU_!t/Jҍk63a'D"#|Jju '}S^Ĺ첋J>4fAPSaEuu8,M+onje"܂ IQ.BǓkU}-}1cK*kpHt$eᅩeQkQV^4PyC e;P0qnJ(}QI1җed\L~]9Lpf47sq^-?}[J)-㑇+Pl)mFS/3i,*j@MID))N4~PArDR-MQw8IZ"EAh;u GҖ(Y$$_rW;r?Ck۱h\m~5pgJ>)y'tFaǎv@~(|(iPԶݍE*vk&8&>+(v|T|wO Xb7e~X{mK~oEcq슉HZCҞ,HQO,|E^5$#us{GOࣖ1=s&_<.0L|>xt{`5,t?~8H՜:uLs@O4=́C<زyР_j6:sFè7[Y#qK F\su7Ҋrm{བ8M9\+'%7]MF^ߍ{KI_NceCYq^N\Y浖d;>=8g` {eJ \eevquYߠ~1?1h~~<ݦdL ٤{٤Tnt_]I ;Af&}Z(zମNqEzxs`CQ;C/MWǽ4CG Jj"/vHEJ>S+ێr^_Q)y32KJ]*K8BT0qGS%Y}s<3t핉m|ڣWOB7[:. x}sA2} vm~G}sE"&b0EsK1"Ht Gh "$ddIhM0b0NhoMQs=v9[ZvfMxTj(lYJU l:|k:Zu(Zf/Em?;ZS"$HleE[Z}w Zk?{S>'N-doSK[ODK;9pN[[ A8{FHԦg Q6}vFTTOgF_8:M:?7zhp[3sy'ǬcSxlD8&G"xOrd:~>#:#@?5^a?i,Ie]R:enSu)I2~'tq6z~yj~j[KaA36UO$dRW&&tMLIurP43EFGÃQKŃyGjZn|y'옣D;g"ksy!-"+}@ͮ(B~Tl(hQK.%ǔŠE \cd㣎e|!!,hk JsTQVDI g|t2sfogGɟ_4 i0wqz3h?/m} 7v_%e`Ҍ@;c~Uy-euVdNo@ut!@Z5W5/zH~ +NCpp\.rW'ӵ`YXOS*E$1]SK0{Y_#Z@DqԹ}V+#וr7(<ӽmD= ^&Ӛ5t^ =v\*FñDY[ }^k/$U;$ǣӖûU>6؀< _ RěGSQ.&sO/5UTC^=6K-8z ?uS%ͅEI( #Ѓ4o,G3FO*iWd9ݗbh>vVV< U ~}<~-r;O~ߘv"oVtLaSMd ":K=bŽăމ?7zp8ZE[݈%n*|/4s 7 Px=\^@ |$Lqq+S1UYt+nyjjWŒYG:LyM2?TII $&n:vR\>$ʇ-b3V ܏Q;[#(&OK#4|AE 1ZnQ`%%D~2_.Ү=ճqCf3r3`m,Tڜahλt[?oq|[V*td 愒Oἢ. {qG$w(IF(:V&&BtoO m(C">x&Cq]SxkCv:>qiٴ~95W7f^Dwa`zGyy߸jp^=_0==ȐVǶLeO>_"%V|Wè0/Qvof@IDAT|uD+;ił"?ӊ+6qv<?V!X>ѿ]̓Ob N^?7zݐp?PsGy}\]qqQ*t`"肒|uJE{LuI)5k3䚙131h~~<ݦdL ٤{٤Tnt_]I ;''=- ֶXIIzΣ_ƢS>EDЊ((܌hݿ7N#Fƈ`[ ( D%?-oqEHiW Jvv]kW?,B16I4K+6>p#aYJۊVLisyK@w֖6ڔgvlY)WTFvɯAђ_X'|x;ڇ{FEgrXI/Y'2 ;`MqHK>NC_kzicڄ3b&BZԧ=NBҼטHagEYN{Yb<{deTs1ezV=Q3K^Hln>K߅E ô!~)<.ƍ,0;_}_paRH!/UMQTepRi;peXOqY+2@Ds\U@@ag^ՋuW^w Nc?+x}Lޯ&˕&I 85oPdQԌA36UO$dRW&&tML 01٥hG31[%X/L+b 1/lG}~,,!b˃1b&"(v _u߹E{rBDi^f922 h;gWFI Pv\h\̼b,NlKj$Eyۃ.nzF^^*xiVΞe ^9yּO= 8x}\ ^(@D ;.bbHt&/4ZƖMKgvf@!h~~<ݦdL ٤{٤Tnt_]I ;''uDڙOM{M(+L[ƶ񬠸{#VER~>ӡJ'X)+9ꂩ؊i=qtS d#|YP#" d/j?՘ZT!Zk Vӿ:ǽa@`ô%-EH8!&T]d .(j(BF^:\?tB8@Fzxq@Evhc ^f$kԼ>v=@Ä{JXM* {ټK}*ywݒD0(?#J(م)NB"[Ăgpvl,;jeRTY^)_؇;i+i%k0Ԗv+8"K*}w!BWʹ}$G+j*>Ę|P.1#±c҂ &I;}~H!=kK,Y\J/ aWWvY?m>-_>|0~n1~Nב~Fx}kTx}y}`;#0aǵ$.O^׽lrL2OMelY]I A36UO$dRW&&tMLIr{w"ЎȱqPc1~DNb֖hGkgF_8Z#tgbu+'rm ҊvSaGcN/Z Uk+2F&LtFIl 20l N9SV*߳|hiziΌax0.?#lJ/iL4 FlߖjShn":ha#0'v#0|p\psNj*x22H!䨲>vq:T11,=fF Y,b0"T@'b> M>,b[`^O4.g`ŽgTY{\0<5 ꧖eAIZ)Oz"Y'B6^6$dmRgN3&#F`F`"駟IYDi%rx+"6#_3V͝|ť!4@ Yx)\5cӘ2q _`ĭd"mfz9>0&츈8* t&6<5 ꧖e\??SnSDO&U}lҽl?H*I7ɮۤ΄Tg-gL*lcF` O!KŽ*#D@^ <BfNH@Eg~>9#tPJF7 a #$v\D<W]10<5 ꧖eI@ !ut'e~2+deARI}Mv&u&섘\H@kfOW1R eaF`FtuuAW#눗|0_wwyE*_ @_G}}@B@>/{Ui|@ =2`#G%Ϩ^׽lrL2OMelYPlC!h~~<ݦdL ٤{٤Tnt_]I ;!&*a"y1r{ HXF`F`F`F`F`>!0D ;."JQe19t&'_iP?-Km!ϔT=,IU_!t/Jҍk63a'D"#0#0#0#0#0#0#0@A ;."JQe1ܺeS/4ZƖ%6gmHɤMMI%F5uԙbrF`F`F`F`F`F`F`F G%Ϩn]ɩayjO-c˒bBA36UO$dRW&&tML 1Q#0#0#0#0#0#0#0#_`ŽgTY {0<5 ꧖eI@ !ut'e~2+deARI}Mv&u&섘\`F`F`F`F`F`F`F/0aEQ3,[׽lrjeSز$؆B:LyM2?TII $&n:vBLT.0#0#0#0#0#0#0#"Uí^695L2OMelYPlC!h~~<ݦdL ٤{٤Tnt_]I ;!&*aF`F`F`F`F`F`F` LqqT*u/&A2, (!??SnSDO&U}lҽl?H*I7ɮۤ΄0#0#0#0#0#0#0#0&츈8*yFp뺗MN SӠ~j[BZ)Oz"Y'B6^6$dmRgNEF`F`F`F`F`F`F`v\Dta)}&F`F`F`F`F`F v\De* tC/T+KS̠ut'e~2+deARI}Mv&~Ad]G2L/fF`F`N&t7\?#0#0#0#0#0#f"֨U뺗M_iP?-Km!ϔT=,IU_!t/Jҍk6 N |4"#0# /LK]\ #0#0#0#0#0# LqqT*u/8SӠ~j[N["h~~<ݦdL ٤{٤Tnt_]I_v{+]Z󷩩ɞ,0#0@#cdYMY`F`F`F`C`ժU=w2>#0#`&츈8*yFx뺗M SӠ~j[Tut'e~2+deARI}Mv&~G9r\?&չF`F 8xnɄ F`F`F`G@|W2#0 >ޟLqqT*^6yYeSز$؆B:LyM2?TII $&nz%92"#0# [lvA}F`F`F`C@vx}0F`XgN)a"&/ <ݦRed=O:2A2, (!??SnSDO&U}lҽl?H*I7ɮۤ΄0#0vx&0#0#0#0&0#ci9^v㥋I6UrT-'yB/*m ꧖eI@ !ut'e~2+deARI}Mv&u&섘\`F`,3`F`F`F=0a`F#LE$/]L"=Oäj?Y86~yVioP?-Km!ϔT=,IU_!t/Jҍk63a'D"#0#`! #0#0#0@A ;g,%#0&d/b]'xbyMե&UzG˳J[elYPlC!h~~<ݦdL ٤{٤Tnt_]I ;!&*aF` &L`F`F`F`zL=c-aF0a&xs: KHm.0ZO.?_UO-c˒bBA36UO$dRW&&tML 1Q#0#X0ag#0#0#0#{`N n #0@G ;6ƋeI8^DznSu)I2~'tqmߠ~j[BZ)Oz"Y'B6^6$dmRgNڿtTK >2!޵G㫿AѸ*/u{0#"LI@.0#0#0#&$ &'3#80a&xs: K`yMե&UzG˳J[elYPlC!h~~<ݦdL ٤{٤Tnt_]I ;q M;VL +1nD?btG+#cV|7("oozkv +02>;*F`F@E ;*,3#0#0#0gtvx}3;#ci9^v㥋6UrT-'yB/*m ꧖eAIZ)Oz"Y'B6^6$dmRgŽkvg_ivYu%;6| v$1H"cv'eVm;69 dBN 3kM8l.3 0w 3:`F`F`F@ 5bs?A@r7zLر4^/ /F[m.0ZO.N/O-O-c˒bBA36UO$dRW&&tMLر'jV~'Yc,|>~f?O t CQdb-tę'9dmKe̯p'K#&]l4Tfmx˒K0 &``F`F`F8'V^?ӘO]xw!`ci9^v㥋٥6UrT-'yB/*m ꧖eI@ !ut'e~2+deARI}Mv&u&&kQi=m# lӅCgvCvrn+ǭ I[\$V5eqi?ڍ`?Y&uLDqY LBg(*ѽq`J4ƕ[xAuwA }Ɯk{h$m_ )\y\*>¯_{T$(ԣk4ļhYZWL7u녮6c{zXb"L}fF`F`F!8g^q^R ;6ƋeI8^eznSu)I2~'tqmߠ~j[BZ)Oz"Y'B6^6$dmRg cMѐG9kwQSmQfPW_|k^9ˤ0H$3Qji'Lz"4*RĬ<,"'(Rtvs0+Ff:of&ʖ"}ij)ڊHI>qۖރˈs|څ,E|,zSXSY񍉑9e6j w[i j(y i'㨞Ndn 5F$څKqJi{OX2>e\9h ˵W8P"6rmLX' Y9(V8|JOz->&ީ7xճ#}Le4iIBmg0ag:flB,~R .'xv`dĒ4>e7?)ƥµ-@ec(͍/`U}l2GC$.ymrŅI~5F;Wi)3kW_!]-ؽ$%Z92ƥ4~m'ĺgkM8!7}nDͿR]mS[r}{ l¿IbOhܰ2rFɤM%sqbN-~wl0&Xg^pk>f-xa*;ki^\#Euz?lY9[6va)q4yoU-BdnO16.ZkƅQ`L9SيzV\MySΔztvvFϦ G``/m8s#`2=~+ӳsw#ŭ-!5z>nᠮقy}\L ^.`ŽM"xu.PtK9L<ÏlgSز$؆B:LyM2?TII $&nxKE#G1cduqU$.GXϊ‘S|/nB;5$Kc;BLrnRFVd$EEaqw">Idqyh0V=6z"SEY#1X(bSExUX9j2nĖc뭭2#\ .-iiHzqdmgeJZnc%\Jd2{Wb2(Hԇ^wէ*-ơ'4_9!^b^(6>IH%daνAѬV'ʃO)FŚLTu͖y}\m^W`!LEA u?ݦRed=O:2A2,H#A36UO$dRW&&tML)?ZFR0~ 2BgjSrnεX$*yfʬ2":·Ћ|h1Fi"8 C#KL/q AxvmAq"hDc}Sh^Ĩz#1.jBLI^JhM^c9フʕ;ɨFbL[Q$IJpl<|]҉VtT6Roo5#2/ZeŽ3h.fYWcҨNDX8a&8XJAط% GrOF|/ȯā BUo(Ժ (' d:Όa`ci9^vAL _L2Oäj?Y8D;qL} >HJI|Le=jKJ\q-`&8XJ1c1rV/-I/faG%PnLHj?܀oz6)WÄs12#Jd‡i`sz_^?IR$Qrͯ""`E vkE]d֪g[pK\8bz_Sj;_Kbiچ uΣXzyjMDJk 6pcA7H=_d)vmSt/{S*b=,*T*|"l=ip?JYsݳϺ3ʀ=sgJA_6Xk^_Tmsa%F>.0a&xs: KFm.0ZO.?_UO-c˒bBA36UO$dRW&&tMLءj]`&*Mh%ȼU&}u^/4p"$Uq5*}ZM"ȩ i*NTFqҭ$ Ym2sn^~9LAiGz! aǧJx~y5G㌕N4ܾf- -NŽkK:Gp!Mr ,1&BuS7Ȉ7xf2IdzuDD`)E nUq3QMwo݃ٓoJTl=io{z7[E;H=I] ]Z!_䬲^Mѹ晢sBPۙt'GQ]p9*b7}T˺@s)9EIGq%{B;[d:|:OfsYV)*Fa8Pj)J[|#YJN+R l:w#D'&$ lw.=;;u#rOcf^ba;>N?mڲ~U8vx}\H3}&d/b]'xbbyMե&UzG˳J[elYFR=gmHɤMMI%F5uԙCr[st&sGq`]SYX:/׸GÖ FqWfc b n ðug`F{nC;z%H 3іX~Es2> zF bqL*f3.cN;g!„#d³ʸ˭Ծ|1Fqs:tlB2vyŋpEA8o(X|)jlyǩx`3z{?*'ް ,)#0`NSyl= PlԄ3"R@uBNhu{ _/P SȷQA32VӃxl 3&.|^_1z;+r݌@"LE bJbu?ݦRed=O:2A2, (!??SnSDO&U}lҽl?H*I7ɮۤ΄1QgA;Zё^InK4DZ *NmC+ xJT}Hvzr>Z-m{{VmEqVmUAF$ku2g|1lI-vT"(QʐJ""ouU۬*cg]Xc(L 8Bxk13"Bvrn,*_FBMYM3Q>5M]դkX~1 ң䆣x묈g"Kף&Ht?w{Ȥ9' CkiA+#>. ;>~F ,Lر4^/NyMե&UzG˳J[elYFR=gmHɤMMI%F5uԙ]ؽj65~IZonzn3 ^`Ei~IzŲOwj/U&wE-|a0Ef]J(J`"/U.ӮwvJu'dѶTiRo\'GAh=L x=s!\&tߍv~$|- q (=T{pq (9k,,[ljڎdbJXŽ ߻Xq{ !(N _qw!{3oS"q qک_Fc"ۧrt]>; ϊ]!'|9o)ghi X4Wľ2&sm! ;qsG4lAq7>;jH"n#Ol&cetJ T@(eEZ=)wuq%-sTIMB'6TZT*lj2/,6NKЛD\8wb8RL zSI-bQ~#Ql[VJX׎V5@|XkD!Y`=X0#3.dMt,=v=ӈn< _:a>.FL]smAr̺txtPǺʚ$<(ɇ\vsH S77=NE9u3r&  N!Dž&ECdDVaKyh-%sgk.ǨsB˱W>r Tp䵅^);[df67</DO9Nhރ_?Gơ 2^:}KER+wx3fB^!&68Фar/b.p:*{j@IDATd_^yϫض}!#s>O_r"ZS&QjVl2~d>/:L+5qGky /@߸̉Q>?#* t?]R>Y645l}o=܄?.Awᅘ0bL oȳ&N*驧Vچh hWqVԔLlhm%KW ` #ӜbmdŪ,-Oi Dc21'AM1i݃'Y+:r- }طSze[<+bCY(埤rx^x}\ ZF]iy1c=4n|Ys͟&L& oċMMEMrMxޚ³fs¡`(%MHAaa=:"6mن_|*W>%&jB$=}$4nWjP_qۑrng}9xm6n݄w`o˅b_s fg#&ī/a;}?ϳtOӾ|%/RE py}<6\aq^إ #`D ;6ƋeI8^]m.0ZO.?_UO-c˒bBA36UO$dRW&&tMLQ&jEcybSdŬ B-o؞"Q}RWvr*a綥yFNQtQf1xTS/U~-wUxz[nZ&sr7*72sQ,Xio6}4mdmgeKH+OUa#28Dq9Eߑ #m8m&,Yy(sAk+T:v+|1.S ǿcR|U1 p7~bΗ<䚯xL_ a3ѯ~ϑC:2VPD&cXz5|r&#`NAM>wt|l=Ӣ/6'?⎚~/ffS(ΉMER; l3a[O#%`|c:06TX/l'h+l=ٰ(֥ܰߝSz)_`޴ $(_:/ڌ义qA Cey}|>![kyU_?^aN#&-VW[]ٵƄϱm!"ܵ{6ʚxByͤhC/S;Iĵ q$ K*=U*ZqwGD*7[jPOa_-l;[xm܉X`ba=.Dۻaݚh`b5dfeg+ƹLtWS11g&nj1L#TErEG*oRf[KD,(CiԀɢy< 1Ay=>疫!vr6 9d?wt5 ݉tUцa}cSeTĜkpcguO|sa~jpțOk{ r;Oȹf&n2;",)#0`NO>9mw~>JںKMq%9ZwD)U N_W饵kq.̛bNh[#FcR|t.mJ \V+ۉ čϯ@se1̦(.Af%#]غc5(jK/䣸nP@ii߉CLJ)InQl[~Kj.2J~E)EDkKk@pv  Lt}sVe#qn{y3}2c%E[v05W(k~[V+ոZvGHط`v$#emL-%n(V~9@K^iKU41?5VtHh_:StGcm[|'4 Eڵ~׵!툟+|Hytm{>]X~|c(Jldl{0g T@J*(L>y}zpW LE bJbt?ݦRed=O:2A2,H#A36UO$dRW&&tMLNё1C:?<\pXb*x_#B >.AyVdtQNuPEɴI" bN`<|10bjOBƐ!ewt@ FFJFvctP.:9#F ;I4!8Ouvuf9hNCh|L3)ٰD|A6|8ut6bcA?a%0<ÇwH0a'i/R˽ŀո:~e-*jHj(s"PqL/Gm+|&?{' E|ssܝ 7o;ܥiˊEjx >݂uIQ:%TK[8a1b+񜇫G#`{ݓWs)AyHY^e~sh*L؉5x}D"A lF Lر4^/ /Bm.0ZO.N/O-O-c˂4??SnSDO&U}lҽl?H*I7ɮۤ.Rg)'O|,E0E{~w(u /įlo_٪K"hzEۘ-e6 I""(B*vWmOJmyyA$u{w:ek,%Um k8=u["od+vsmTp%>?޾Nl}8kF͹Ճux:-E'n(wq%bk VΞ OJ]<9mKPBqDLN}EX N''o(:GSIoOGPTܹufhv6mm%ogxk"Ro[nRK_W*4bEMy{WWc/WGբNo}]i9nN:$9S纶1c-rd$ 5MX^l_W15|E/eu}.ZmN7<Hw}[q5]>툟+"s/ښM65p=Gl9v;d܏J9 r}8n - bTSǖ}f%x}?";0a&xs~b2~Mե&Uz!ueelYFR=gmHɤMMI%F5uԙ@/C0&4s/y1P]ǖcJˑ{GYn [m/M< r`Ѷla49i#xb1b'L|ho6\ K [a4,m~^; '7VG{JN#RXpɴELHI/S=:1|H97([skܒT_ qm'UA Ϻ KO7caƵ;vwhyj-䵩ϱ5A^:DL3ESuQZ'粮ª7 p]~Yo%3銰cM@@0q#~vl21ˮpt1W<ݦRed=O#YA2,H#A36UO$dRW&&tMLIuryF!&E<붥`^psF #-tN\[7@ۧh-S{o^uy|6`cSM"BM )J?1{>)ņAS[!PHel+'F\Ps K!$s!- D626f[3{ͬY{іoxo̻̼¢)mctňY'=de3 ֳvmi]~?Gg4I^N9` 2L؉G^x[>3F;"Lc"삘1znSu)):A88MGO5NȒbQ6U?Rn($~v&u&X\޳I߹`F`NDdភwPü_ _vUm)-/>5N,!upv009 NB0ж4 ږFQH a. eE[acb|ٜKXlT{n;shk`):ker7@ )kjs脏l*,5Ydp)lO'|_wzΎݗv^7C_Gq4I: `[ gAOkW?N_mJHuE ~q: RƤ[(ϒ#n Cx]P{."FkO ɣr),hS dе$kױ]EB/.aGí3r=n/q{;̵wEF=+0Y^{e#ݾ|$m9>C$ڡ8%j?vvbG)dLq4&bɮpLnݧT]qJN. MϮmFS8 tFPOz,ٔjtMG)%F63a3#0}&D|Axg_ {gqY[l-QO[KqjQ7? ySC{%ve{J"onoېĥdގOnK _4~w$u/ʝ'$8Ko?E[c1xחd|~c6:h;~wivmEB6"68?5mG+<Qڨjaۆ݈#/ĉeJKd'uv[a̵NZ_l5bJ}Sm0S[ϼ]uBpɆȤ6Jv\n~(joȎ: +p/t)aVk~ 20R7~L]6d;}I|Gۈ|6BYo*/ޱ cl9Sѧy{nZ ;9"N=VZe@RTOO>ĵypmu[\݇3 O8i< 㮡[*\z`ŽF`D ;D1uIӧtK9N uD ٵϨqjGCFPOz,ٔjtMG)%F63a'B*#0#`#+A{ٗam/  N>/ w,bBRg -|ݻ] tNb1gm,/utR; >}Ǯ@Ŕd|JF1؊^;޶3E|ڼSec%rR@?-֖];OlB~8uVmC3եz:-FF'jO_Q潘R4!.!jAw"Nކ3 }zޮP'GxǗNԵe͍9j9u{rv s${n8Ӣޥe'6GkjOv^X|;V$;rm}ckx}1{ [яwsw׶,2΄͇Ȫͫ:6cCkKlϴO1cDD]DɂV&Px ':3}EӻI Rh.RZSؿj ]ߘvFYvhGVfLmʝti=]3ŽmkЀm(Wz5a'V z;+f#N.\!ۋݖ6rrݞ858+1^v3{Qs6yzmdUmm܋{-̭CX0a#0#!Lc": Ǥ}MեTɺO"g8#Kc!Dm#(ϧT=LlJ5V~&㣔tuԙcrF`F`Nĕ=Gl {wrJe:' $NY߰q>.{PvGP=cq6|z)[)Ϧ<{J% 74on7_{(/:F^ҷ 8J>TOj;lEAٷUA굝E @1%P͇Ȫͫ:6c#J[D4ujezMHUж+^Wy[e?x죞{jxeĀ\AGcHIrt eGszf֮s"nOfoA Uu~_%ʚUs{!?A#;nwkڳymUo÷8J=H}o+8?~<4cNߘg>KF`v2c$.&Z6UrR$>#lk۟Q:,H#=m&K6+d?dQJIcML#0@F ;'_{} %,_i*-I j'6-.AJ#rl C{NQl_8[lc|!m_C |G1G-}S襾|}ٵdOӂ\/Ⱥd|=؊fMV׎2ĤEQ|d4 ڼvjDl"%j7me&-^Xb7<]0nU(ޱ)uwLrHFŕ;͸wymE"'{)X ?]9-M01ےE#:M;O=JnsDiz@A;6>2Z[yډ=u ~_( JkE/x;Q ^ՂCtHOwu&[ >5>Eg :߿\'Oߋ[r8|"k3浙@טB}."ċ3H Ch*+O`F Lc": ǤtK9N uD ٵϨqjGQ6U?Rn($~v&u&tvr}F`F#=ԋs2?s"nrf LXPi@ʋzl)-XW2k%+*W6f܉)m}Ź\d]1>1lÑSlsCc,@[jů%!ڼv꾓E[U4 њx@w$@l&`Z׆>ӊĢX1lD5/rh[ڰmt&BNp:)ԯc\+_vu]}g_x6PV1`ޛQpdj-:ES5g10Hϐ]ݿ]1B8C&e209#ày/J8D~RsK+U=>k9|Oo"w#ӤV{D%>(?=mJ#m[2>!vd2#|0a!Ә9&N1bunSu)):A86A>5NȒbQ6U?Rn($~v&u&X\`F`lq%h£7?̟ٮŀ"^:t/ %vc7 Ps`;fg+]ϣdJ֡hk"N'[b>lc!*_6ԯ$vrmعy:ke*}ݶyćBV=XՃ2`h30mJ솗3Pp-MʌDzaX׹D}A>Jo|B"&#1cd&A'CE|}i"S|%[ !6Y~z+ǁ(5dn_&zrp֮>PbWWC}aA$WހSBaֶӣ=*ZoR{f.A5D,9މV' ;Q.{]k|E5%cr9u{✽yHb~_gHgF"H\=אַutm* ~J@t|PFZ-3dd ,M߭hU vy3#|0a!Ә9&N1bZunSu)):A86A>5NȒbQ6U?Rn($~v&u&X\`F`lq%hQ4kT<;/34 /%u^R9%ttÃQ薎ӻ0l݌ҥ57ʶ[$4=7okU hۨŞmRͻpG?Om/M[h0 r@/yVOu?a_V1U!ߧ-yR{>z|tj ?7w!_;0Ϫ&a=}d/V&m}Qcw(gdƢ*kc#/dk#v</XMeb&`F`>=iL]'t1Oj Y ]Ad ]qdA8?nS0Y)X!&RJҍgmRgNgW-gF`0L؉8˾՜0t؃0Ӯ#_ o{IƭQukQ=KZg{7lEToN(9 ~V /5Uc|}KmۓRk"*e.$( F<*w=Cw.^s/,rs|19]6ҹyFN+xw]H_\}I{hwچ ٖeZd8&edҲr h>QIMQ{_V+F4m^kB-e)2$7ҽқ$fj8CD 2EٍR2pe`Fe2vgy2Ϋj6mkjjmZ4{oRBm׻R(zےaRI㱱3}_tN #{̿S29}9u{^92Cq3GV߹]{v]LצW2}7BߵHYJuťʨpFl͔ />l`N|>uF`O&8d1dI8&]̩m.8Z'H}BG&g׶?Ʃus_FPOz,ٔjtMG)%F63a'zk&0ctyCg̶U\FAw};p3b-cF0a' jĚvXq5^/wT+ogDޯI/bH:k&,tf),ϊ'ʬtُ''!>޾g)C49=/n>s$;)s60B~[pɾ6[ǖFmn>U⁊:9fw!N?1#`ŽC1sLvcŚ}MեTɺO"g8#Kc!Dm#(ϧT=LlJ5V~&㣔tuԙc-66.r ܋{4|Ӳt -(̈JC ŋG.buj;=e)x/VJG1 x2ű+E&l=zC?%D\cj=ߦ0Pro<ދ&`>ͫ0@B ;WPa5aݚ ֢Hp?}ObӵQVZ%+NǙ!T?JG)ԯ{Q.EÇ+1j?ЗeKQ"L+{k]n9oad[9&  "agЎbZF7(z1o&F:x[ZGKCTO/C'?ٞ]n|v5-G񌲽5eG1jc^D 2k-wʪ.3mQJs{s!(G͓+/SiCt'QzXPUg8m_i3R{YuLXm#Uh>wvRFz nYA}=vU܁uJiWmX9j8pzٴf9檠1pGK ׸?w 5x[1~o ε{ &~GË]#?9iíJk<jHu9'oup\~v~CM{q3}/:=MMcr/Qm Z31pc[f Uʚ'0!G_nӽTG|<ఋ`:Lq4&bɮpLXbOj Y ]Ad ]qdI@q 1mtҟM O7d|RnX?n:vBMƔ҄;y m>&CKӪ4y\w|JTzn/1A|n:$__z鉊!؆uʌ&K60#IIz8s ͿiR|In05_o FZ[~KWps5\CE RO+JkHnzRhOv~9]Pf]mF'2t$[ oF޵DݎCN {U5S8uWx?yOA61RXƳ`D:+'>\<Jc8}]mZ8`\ Lc": Ǥi}MեTɺO"g8#Kc!Dm#(ϧT=LlJ5V~&㣔tuԙ#*eG`~,m!e9Ǐ Q9Wm';ߏ6܂+foQZ#憰sύg.xc|N#%ŷgw<wVYGz¹9_ą<'_xi{{WP1͏> [6 qwczV׊ͤǑ#|v0aÞ{fF{Wh%`;&?CNz喺ƭ ȔqA[֯bhیE؉WdF"(?|\U`XfF`ŽC1sLvALXfznSu)):A88MGO5NȒbQ6U?Rn($~v&u&숅!|›50jr,%{pm} \vߌ=dÆ|'(imgOOuAǷ.Ÿ9/ا1s.Z$ܔ+XR'7#>?v灝#-`N#0@ljڊ<ڿ+t[mV';(ڲ<ݤdN7#0=v=|\Y|\EF`0a!Ә1@IDAT9&N1pݧT]qJN. MϮmFS8 tFPOz,ٔjtMG)%F63aGZ'sxj\8vUؼxRW߀dCRd@؀^ F0a!Ә9&N1jݧT]qJN. MϮmFS8$8B6|MdϦTc짛l2>J)I7z]I ;b*?8 ݱ6øY3q.H0`ܓx29#մyX6` ޻3«'ɛpr ~׾l–/J[.CI&qk$"zo#=co?{ ew+5޹%a<&50e\>HKի߼ ?1/J Z({.l?ewgD1s7ë ľg!>21&мXt,E>{[?'e%^sȄEdvzlXF`H+~nش${?3ZQoxGç^#?c&Mn`ޏ@vX=|\{]ϐ``ŽC1sLvAL_m.8Z'H}BuƩuYF:{Dm#(ϧT=LlJ5V~&㣔tuԙ#VB$l&cy+\0sYL"¼luύg]&cbd$cрqc292 ;T0f di.+Gŷ:F[t;cXgF":3<.F`F-$6bTkF`F@EK ;|܂{3q d`Lq4&bɮpLXROj Y ]Ad ]qdI@q 1mtҟM O7d|RnX?n:vBU& q/AkO,5ʰܽd XGĜ4d{WP;l,ߣ,0V]qqf﹅+o?i;f8Ty-sWgks^hfY= ?Hv ;m &?#۾CMom\1w;Zf!҇ Hsq,ﻟV6 )\ (/fcw *1 /Ӓ!v$&\2@A ;=gx#0#Cmmp=1z.baK|\GuF+0a!Ә9&N1b)>ݦRSud'tqm|vm3jZǑ%1m&K6+d?dQJIcML U!d|e]&܁uUTezYKrD!Ϲ73`'yg6^[pP`k~n^g\4kO\c?_m6_չ@IҼs򻄻MB}<|w*qMүe"dxr@oֳ>LAzc0⡳[WiZm%;rv$&\2@A ;=gx#0#0#0Nv8v(O #`ŽC1sLvc}MեTɺO"g8#Kc!Dm#(ϧT=LlJ5V~&㣔tuԙ#R`We pkPT9xz)Kpsr"7G2(&!$gsOM6[mdM4Q *[H)a }zn:I2d,. dءm^a | 3޽fQ;\a &ݘwK^5ĄLt0&t1#0#0#%:KZ^ݦRSud'tqm|vm3jZǑ%1m&K6+d?dQJIcML Un/MtQr c?v[V ?v% dZ-DV4;=Er;0sq胗OYb(6p~ujCo4r3?=&OƄ{t~Sv\BSfBءraI%^Ykq2;G`vߜF`F`F`.&@U#`ŽC1sLvc}MեTɺO"g8#Kc!Dm#(ϧT=LlJ5V~&㣔tuԙ#Nlɍsłğa \C눌#*đk0^K"fωrLơ1D߯Tzϕa[<7ݟr̄U6< :7ƽ[4efa;D9>7EmZz0oK ;V67S-W9dŽF' 0KݦRSud'tqm|vm3jZǑ%1m&K6+d?dQJIcML U!dL&($w&<7"`qRJy[aF~R!w Sh YT)-p6Vjxs &B{8~յب+ԿW:M;b#/Xo+ {1n؃9hGh+-De}Oa Ad9SLSchI72aGEF!0aL`F`F`FO Y?Y&|<2a61@F ;D1uIG6UrR$>#lk۟Q:, (!8?nS0Y)X!&RJҍgmRgŽX !c&8^BH/plO9L{t[xfQ ;A}[ _t",%Em"ξg|CyWf~l1L-!E#xN?]ـNҰ6ٷUmsD&},+N*HعxSn76Br ;:t3@G ;x#0#0#0}vVrq F"Lc": Ǥ unSu)):A86A>5NȒbQ6U?Rn($~v&u&숅?Hڎn [䵔7a^8+u$P1Kד%`f`,ly M\?a\h;meMlyfk=w1)sm6D"\>֓<N;x~qN,NW/vT*Į!Q,0V%н!$ +T3a f #&-ڲly۽mypW#83aF10aL`F`F`F Н;Y=CVq{>p)2@G ;D1uIkD6UrR$>#lk۟Q:, (!8?nS0Y)X!&RJҍgmRgŽX?rFعznA) {2p~:$MKMՓ魠 صy߾ ?ٴ N޵t=K/Tct&gqIYل&->@c#VPKHLM9݈ Gf$MGV]a"ܺISr9<|DPvҐp0=&2#0#0#z a'g?|˔O`zLq4&bɮpLX-Oj Y ]Ad ]qdI@q 1mtҟM O7d|RnX?n:vB%BƬ'@arCعJ?HbK y[yږIElw;ئO6s5ݴ&Ƭsi]DTi6{ 疤33غh<We.+oS6EPΰvRh+V\\ɁRGΓOζǍaŽĄKF90a`F`F`F#?'\`+#0v2c$.֙m.8Z'H}BG&g׶?ƩuYPC !jAq~>ݦagSBM6=Ϯۤ΄PS:zr+nob˻?lcI8!%eta/ {oZD8J)I7z]I ;1R%umH]ky7`РAA $d[EnNÑ_MC{vz?Hcɤ!'{Pe̎.6ǹvປN-#ZLD-#`Nw!#0#0#0@_B a;xw\<&F`4iL]'tm.8Z'H}BG&g׶?ƩuYPC !jAq~>ݦagSBM6=Ϯۤ΄ 1e{4ť_);mxi`-F`F`F`F ^C ?U#@iL]'t*tnSu)):A86A>5NȒbQ6U?Rn($~v&u&X\!p$[ʤIO`ӽ۝E*xn!6 Zː߫3)EF!4YݦRSud'tqm|vm3jZǑ%1m&K6+d?dQJIcML؉P #0#0aW#0#0#0#}`N  #0@G ;D1uIH6UrR$>#lk۟Q:, (!8?nS0Y)X!&RJҍgmRgNUF`FF ;F`F`F`v\HF`z?Lq4&bɮpLXDOj Y ]Ad ]qdI@q 1mtҟM O7d|RnX?n:vb,T0#06L0#0#0#t}G0#`ŽC1sLvc"}MեTɺO"g8#Kc!Dm#(ϧT=LlJ5V~&㣔tuԙcrF`F`F`F`F`F 3<F`ޏv2c$.m.8Z'H}BG&g׶?ƩuYPC !jAq~>ݦagSBM6=Ϯۤ΄ 0#0vx%0#0#0#0&t0#~iL]'ttnSu)):A86A>5NȒbQ6U?Rn($~v&u&X\`F`l+`F`F`F>0a`F#Lc": ǤEtK9N uD ٵϨqjGCFPOz,ٔjtMG)%F63a'B*#0#`#^ #0#0#0@A ;g.x$#0&8d1dI8&],"ݧT]qJN. MϮmFS8$8B6|MdϦTc짛l2>J)I7z]I ;1*WaF`&J`F`F`F`L>s#aF0a!Ә9&N1b>ݦRSud'tqm|vm3jZǑ%1m&K6+d?dQJIcML؉P #0#0aW#0#0#0#}`N  #0@G ;D1uIH6UrR$>#lk۟Q:, (!8?nS0Y)X!&RJҍgmRgNUF`FF ;F`FF ՑD qqzN!Lף_\`F0aO`FW!Lc": ǤtK9N uD ٵϨqjGCFPOz,ٔjtMG)%F63a'B*#0#`#^ #td+~s~ Ȼy0z :-@klu [ٻ欵ά@ʋfeG^wc8u_d,ASllXڛqFc03QkOs#DB ;` F`F '0a!Ә9&N1btnSu)):A86A>5NȒbQ6U?Rn($~v&u&X\`F`l+`"R_3x[n_Wz?Tݟ7YX_>jw ~/pvI!gzghAG 'rdwԯ*b`ۉ6_xH +iTC>#?g_/ǹ ,-h޺B$O:ćXXOkm8S9tG3akVFB ;/#0@_D ;D11%bYqMեTɺO4A>N8#Kc!Dm#(ϧT=LlJ5V~&㣔tuԙcv*WkȻa !!v\0NdU5^8`Fo 1|@Ha)x"Di_ŒޒB;iTLB hn˲xέ+n*4e+liُĔZ-Cv+kg)ĉerIG jI2Jم;&,pUPbQףzv棤"]{6\9gaXPrq)Q?_HQp>*'Wcd7c.Avx,n`F KiL]'t1#Oj Y ]Ad ]qdA8?nS0Y)X!&RJҍgmRgPhǿ*Y)߃_ @L{ڎc!O } O oRW/'%գr_;a=3= fA#˸Q+hvXoO̗NJ'E=Jk0{x}s?|ӄ;˲$sͳMH6aDI5zԖd5Җ0Q*|3FfUDc~ *w~YIv : & qqQ!1pkL3K73h,uf|10az nS}=PN5EsWo3s<#~ L\`n@~>`F`z;Lq4&bɮpLXBOj Y ]Ad ]qdA8?nS0Y)X!&RJҍgmRg=2&`\yu62՜ύ 5~'-UF`^vzT0] ^~H/H3v_PuQy5I"Eq~E8W?T?Vlr*~~ K9@QٞyAP'ެUZ6ukN*w<:I˺#]Vi ξ2;ƾӴ&?+l 1br+,-3# Ț#? 2#g`ŽC1sLvcZ}MեTɺO"g8#Kc!Dm#(ϧT=LlJ5V~&㣔tuԙ"#ųYGI5@ިG%d{X_p;݈G N\U!L&OVYpxzq'޴lc{WPe'#0@@ ;cx@wG L [+P.}Jw8i3cFU;r~ߠ=Tw${;#xWtR1P0<,JSǢ_>{FZ#c"~̈́ejߥ5S`:[s+b;eJ um%5kU>DkklА{H]>u\{H1*1 h3N|+Hup|AX4F?h~͎|fuڋpOJ_>p/[rvhiiP֐B:6ן5BJƝhGy34 _+2SIE8F7l osÇ1_}`oD=thwOK^0}Z`YvxǣQ#1iL]'ttnSu)):A86A>5NȒbQ6U?Rn($~v&u&ܺͱ׏pDQiqeC7O$gg1fqUV ;i[~N[bܜDd62#`Nt>eF k:;&ꇈ*MDIZwUh$AUktpak.6q>n}~&qx jȩ` MWv M.6nj6PƜS1LJH__AB4֭BEYh o V:ֿ|ʫeiQ3 DƧ0sy4PZW=msS[?`؊)[lAjfgK0wem<>^V8__ܑ*5f\`gQ2Zõ8&-ZA5֗K5d-kUf&ZcXǥyd%~dM( N˼BW,6\TWcB[Mo'vl,r{,mʪP;_P5h_^!_)S1t_ͷ䀹ls/Lfv2mc}hYԇ|k0Fi-οRCTψeFKdOQ?0#`ŽC1sLvcŢ}MեTɺO"g8# HgmtҟM O7d|RnX?n:vhRM[){ldϡ <靮k<3' _GW_xއ;<4uc݀; ^Ɠ52Q& a' 'pGW{C0rT>==YyL9ĺzngmkeEv?BS=to'F`ރvz\0]7ȡWcf'%CE9~/ETDioC o⽫Vu)mR(yL%{x;Qϥem M_}) @j[!mްM]'QE뉠pN5E{2=b^_bXO4bJ+^#ZK9DZewQl},6tmx4cIhkZ{k+P1(͚x}afTVuA N^KA%c#[a[bpbrLZQ2fkK$ݩFQF!Umt_fWx{R~~Q/.F=yQ]۞qu,Ǩ t_KaQPm[')ǿӶ#m 3XjVuUd0)Za4eLSVJ]֧TXa>@?Y$|< v3#w`ŽC1sLvc}MեTɺO"g8#Kc!Dm#(ϧT=LlJ5V~&㣔tuԙ#j㌲$vچz*l_>v ݇M;b#vgneӁ RWE%L=ɣ/a')d2^ ~X%QlGዸt !.3N7eEY&أ6nWQ ;%v`wR^]PX`F0a-#3B3=2ӮSVYoͼ]#_^Nu2UmóU mrk"gm5%Gd 襨]|b%s<3фgdU)+Y-<-+gva؄N`qk5S)ٌm);2ԟEA}N)?y;?ZnßIF&|R\ML-:ZG4>~OtU S$RO ~|T83u `51"jKklID\KkvPK?`zXs +렵mvUʆyw1u]_Nb"nD%V&LX#?0TL^:?= Gl0d&nV[Te3Ad3i,upg/:Gul܊%nN]_SGkVܦJ"^-sW]Js0m'<8& P'6q}{4eΙk/dGW :(kʏLGdTj0ӦUkH"ʘ2T} Fknm=|}[QFx1.?÷K8 QZ3)qkoFetMOmF!ǃ׉웟c^F`Lq4&b.)QKJm.8Z'H}BuƩuYF:{Dm#(ϧT=LlJ5V~&㣔tuԙcZ˸_ƒ/jwm=I~l3y(/%7} 3 3+p߿Ai9y&wMܙQ%֯-2"yԖ{(+zL^wnv#WmTƭF0gR7,E(P'?k&,>:5Nk u?ey&Oyg]'C]{ӔCDvЮ <ٻ$u}WnCyvxzNؙb ~35;Dġ<"}ҟl(rR&E* ;c4G7m0k0p.^Rc`F7!4|.@W  _+m2:^$uJ)^Qz2kmJh[(ۤċ}J`ZӖ<}RUV(TUvD){WvJ0gK Ja%['ͼc7с]ðNh H2HX;0BPR T>ԗ-$Q'}щzڪH$Eu#%ڷJD~TY&TD +2njlz +i{Vt3tjT=OaZfsXOsٜ LI_Pqa©ʪoҌY[i.%[TWc\ʘ$5bh/ C%g[u=k/-gh$ByٔkHF2mzOX{jTF~J+)"ݡ:wD9^"<^r{owm0}xzǍK~>n#qiL]'ttnSu)):A86A>5NȒbQ6U?Rn($~v&u&ȅz>~dm么unG%%ޑD.֥(!Nxj& 美iϼi־Mȱ ^mg&\~m"B%L~v?>=թ ŽfZ? wHz@&EعLt1]L@?+#0 &ar3Ch?8K M"D%rW#C[\8{ﷶp)D2yvWb2L(#D " En^ q I &ff jd;.a$A$Q[YԗԾT =D8Kܸʱg\gi\TOoQStWNSrGæZ'7}NGɺbWVcmԌ,"L͖뒟 :[I\"i1:mEfQ36[$&, m'#1 (suRD¦}H=.Ę!G?+#AZ'Fԙs{Rc|ʬb-"{8eUB3ܓ^!u>y])QlV]m>eWf #v!]AGd;JӉhKx\s,mg@;~ hMvf}}'#]vǍCF0@_D ;D1uIJ6UrR$>#lk۟Q:, (!w\t*BP#88@"8&bˆD''7XtY/lDG1\(h0 @"jP!d* 2 dx߷TSէO8|z-3Zcz_#;_u-g+ R}7f<6~8~PO5tjlT>7ru\f(߱|ݩa%j[|%X+/GU[,{;Mj j;əɛꖡ[y s))0*ՊEW?DlVۊ Krܚ7VN ~IJ:XrkJbk)Jv:7mmmDg:+)~+AfkheNUP[ʭtl)_z0B>BNpR\ȳ:O9w^mWʫ!|o~FmYU3ߞR3CCn-{T&{ڠD73q3'jRn͈Tx{lZ\=A%O%h;R vx|\HH(؉4>a/np|m􍝦}l;}}ĉmra3iFRILjs &_L+k}1S5cƧ`GLT{2d{>@IDATe5>? ny>mӃgɚ< F%am?p Q'vEf p~WؑrUWGHi U 99+ߨ;xb"j**G2Ek֡& *2n9y(=H bզ2̞Mzf^]}*k p}F2#v[MUcx})k2:+  6J `q v~MQ h(؉4>a/np|\vΎIiZ'ζsG&.?>E#=W1MVjb>IkD7v+nnjO6ݏ.巨H< [VFsk?* `6+^nyǮ5CaAO`'+hc ;ƈ}7)Nyqni'71S%*[SRp `K#cN *j}J@0 ܰ$w25J&%T,ǵl7s@m}#UV\Ҁc{#ر&nZPi ~v$X_'gULc]6`^3!Զ]CUsv*3۩D2e0zalֿ] Ĭ2Uv%l^-ό+/~޺CUץKıj{^_g<+-bԜ-~ij0/a$:}=Q+0wJEj߫h̯uoc|Q*ijBNUf|4ϮReM_ xuJk`뾄_[Ss3[mNۡkŢ]}͖D)kR DHMh`S~MQ   v"1OkaJ$􍝦}l;}}q9'iZ4#qu~!ie]/fꓴFtc׺vY^^^ݹ<|q{CuU6n_gQ`,~x|ؘϼ/-[b4~=ȣ! ; v[bǪŕԛ}H!پl*κ`6}Z z\ ۈS.+ B‡["#F@2ߝs<էkzo0ߜz1ޞ9\dcV+1)TU۝L ߏgUT_Ю V̎3QN]uwWov8i-OնFJ3zaQ9l_w±([%f=#fBmR /3{S\AZ \DFD.B +*+ L)ȨR?TUlyVӁhRc;N 'oAʜt`R KU$c!ıɮTy]}LhmӄybNϸqkWSgplB{Ţ=֪g=ܬhy;ĈW6C%qH6 (Z2+̵WȯH;//ovo)Y` pY*Xy-ke8O1M 6K9=ߏ3aHH $@N$ s|q[5h;gǤo4g9#Nl {Id6(H:F\+gǤ_6bZYm$ص3>;9Umt'2lԿ9 ^?1<83&=[CooĒ;`Lp8܏ A"#}%طNm%mX(M~qN/'v*QOg/wAdHH`%O!eٸoĜSdvtZ$@m@;zewϛ:|?   v"1O"I9;&}cie8i_qb\;LZ'D4H:F\+gǤ_6bZYm$ص3>;֬}oyIնW/ݭ5ߺ .į[~<^GŲf)/rڷAm5w*z/#؁؊KUm`_7mǷ>wVj>qWx1{pF/ [GMjeZ5^Ak@Č ǟ;g; E@~=Ys*Oof~o#j~d.D@@cX|utg?pe#9[ /փ|L|{gXu&[rW |ȕ,Ҋj5R.ה@oمa(#}\'3\5Sxխ/J2ϔ[\Jt,6UØjD/y,Yti,͑qR~Y7f m+`s+$g!bיڌZ}i:bVI{ϲoSa+ahJ= /=ӜFƧpRZQ(%~Og99}3;F@ӻo%V|T͕sI1M}ʾ- GuV Tߕl9t4dv Z$@m@;ߏߏ0F$@$Pi|_\ S]gǤo4g9C_>Id6(H:F\+gǤ_6bZYm$ص3>;Dnw&V+ܞgVѕՏmfG(qwǬ}]Kן/>!pi`G{C;ό]ga/b #1?݄`Ͼ#5Wbk p]1kH4j;ܿT\   VHVPyK$pD3~X-7L^m[glL( h~o\|F ~و9#RXNH)eVϙ,zkJTcv Y#vx}vx2kW_U($m\+ `(Z{^ $LGH}\Wo?L3?s'h+qq7FIH`'9-z9;&}cie8i_qb\;LZ'D4H:F\+gǤ_6bZYm$ص3>;UL=>}ǔ ?aW۠;W\)޳A^>ɸζj_J1 wuj˭Ֆ[7!BuS)99'FXv^?0ro`u)y土 @"@Nz8*aK+P5"\z%POSJLQ\] +ٕfQ*0kŽJ̠*"WLNzlXsW{frd%:mU_N]uw HN,;Q=[ *ժ>܌ۈthꭽfb=US1喯aXIwoږ3=b?:6 sbDV3-v-QJ܏CKRpͥU;sF:'p]{P([Tsk? gUIEqc>SoUqy KuajmZ/7?.ŷxTjճy +oТͽ.̻Ly4YBBG*TDmy5 +Y9k\Vhғ׵ڌZf4 Ɗ7VڕJ+ +5ht4}3rV _{ s5\jzuUuސ-{JYㅈBmؓ0Q욚_k۾ o6H}ʺ4lYRRڙD} U=S=`& 6MHLe􍝦}l;}}ĉmra3iZ4#qu~!ie]/fꓴFtc׺v&֥|*SN;|նY;ԶYR2[3BF|Un#oNmi-Xvp^.Gmŵ_m%#i'\sO|Xz;^}nNMӈu&ݛWV9q,aN0@$@$К PӚ.>{Nv8_65@ST:Jږ̶Li55͝;n#pr&$t= \gP^=ڵCӁ8ө(.{o]lx%W97uE5N8$'ثlԣÉj{'uW8g;X \TxL'xq@͏NWAt:רǶ:{sAͱoN%ܶoﷄ)=>pgiWc}(O& @+!l,MaH* v"1O"c7vV'˅ϤuOdJHa$#Εc/d|1նL}ֈnZW܎5MƆ}h8^65jNFӒNs>ݧIsE@;u#yΦ};xiz=v? ==H# VPK9#[AՙKܫx|WN!W}+'_⮑D$@$@$@m@;GߏHH`'9-9;&}cie8i_qb\;LZ'DDF1\9;&BZm|_'iƮu)I1QمHHH $@g ǚ@f=z'}:\%86<ؿc|j:NxlLjz:nwA$@$@$@s-U1'  ' v"1O"9;&}cie8i_qb\;LZ'DDF1\9;&BZm|_'iƮu)I1QمHHH $@g Ǜq)R6jna} ֵUӺↅnKuPVI$@$@$@-;-HHZ v"1O"'c7vV'˅ϤuOdkHscչrvLl/vOэ]1SY$@$@$І Pӆ>oZ z<.NJX)koׯ26o׭[o6>#)A$@$@$@-;-A2HH v"1O"瑝c7vV'˅ϤuOdJHa$#Εc/d|1նL}ֈnZW܎]HHHBp& P^9Mm Z=$@$@$@$@$!@QBIHZ v"1O"gc7vV'˅ϤuOdJHa$#Εc/d|1նL}ֈnZW܎]HHHBp& @!@Ny  OHLId􍝦}l;}}ĉmra3iFRILjs &_L+k}1S5cƧ`'De   ; $@$@$@$@$@$@$rPrHH`'9-z9;&}cie8i_qb\;LZ'DDF1\9;&BZm|_'iƮu)I1QمHHH $@g 촜g+! h(؉4>a/np|DvΎIiZ'ζsG&.?>m(Q t:WΎImŴV.3IZ#k]q;f| vRLTv!   PÙ@$@$@$@$@$@$@-;-YJHHZ? v"1O"'c7vV'˅ϤuOdJHa$#Εc/d|1նL}ֈnZW܎]HHHBp& @!@Ny  OHLId􍝦}l;}}ĉmra3iFRILjs &_L+k}1S5cƧ`'De   ; $@$@$@$@$@$@$rPrHH`'9-z9;&}cie8i_qb\;LZ'DDF1\9;&BZm|_'iƮu)I1QمHHH $@g 촜g+! h(؉4>a/np|DvΎIiZ'ζsG&.?>m(Q t:WΎImŴV.3IZ#k]q;f| vRLTv!   PÙ@$@$@$@$@$@$@-;-YJHHZ? v"1O"'c7vV'˅ϤuOdJHa$#Εc/d|1նL}ֈnZW܎]HHHBp& @!@Ny  OHLId􍝦}l;}}ĉmra3iFRILjs &_L+k}1S5cƧ`'De   ; $@$@$@$@$@$@$rPrHH`'9-z9;&}cie8i_qb\;LZ'DDF1\9;&BZm|_'iƮu)I1QZߢSwv&L$@$@G;G#!      #Bc-#28 GGHLi􍝦}l;}}ĉmra3iFRILjs &_L+k}1S5cƧ`'MiegDQۜw +Ή=y7okR1S..dHHZ vZȃe "N<$@$@$;'m׏1;M+vNg:'hG1\9;&BZm|_'iƮu)ѳ5=1Rbt\ҫ6w>Y16Km=y;mW[@hĶ6cp͘" Ly$@$@$@$@$@$@-@*ߏio>v&(;'m1;M+vNg:'% 0W1MVjb>IkD7v+nnjOaެx73i۟sf.x06.'~)15Spv3wOoAZW% " ۗŒ9IH 9 vb% m;|?~ ( GI"6OHL9f􍝦}l;}}ĉmra3iFRILjs &_L+k}1S5cƧ`GOTZ-gJj9}Uyp Qx@ SE> ;>2 GOHv~n<$G&"(DHLG`􍝦}l;}}ĉmra3iZ4#qu~!ie]/fꓴFtc׺vY'z/;_x}S=Íؽ-N'쁒\DF{t패Ⰺ׫qѵ3ܵOxχv '5[/*ٸw?qڝp~ڵkN셮~ܹI>(;߽;vk)c|Owuc*j^îSNhOg }D]qx>gpX3p;t/_l9=w>Onߏ=xx5O<өk 怄fb>{ t}2$@$; AHHHHHH>GF|?|? @['@N$ s|q[svLN>qӾ>6qwNl#@)cչrvLl/vOэ]1S'CaׯFk{鞃Pq˙6vDŨ mX}llPץ|8.ŖD v@FXjk7g)3 W }O< [T`u@<6$@$O|& "p;|?|?~y^  v"1O"c7vV'˅ϤuOdJHa$#Εc/d|1նL}ֈnZW܎=Q#N{<f0}˕ኌ!d> OiN0r;+W|#2`ڮhƺJ!2+}{CXi]kC4ًifT5^+~4 •mtM}s)(_ oҾ θ%JyP!jp4nüً-kK'LEy]ϡNȴ(gȆQZ9~ֈvbFRzbhPa W}kmέ=t9/;]*0m%8M Gy$@$@$@$@$@$@-@;|?fn8ߏH ;'mӴOm累8M\.~&}"P@ #qu~!ie]/fꓴFtc׺v vV:?RL1+< AYV(ɸk:c#;wbBj,,mVgVqoD"6R"B2J\{gY=;7bOF› qIUqt?yd&Vlͨ}<~}ԾT7b(b0)`] S~\Z+U8/V%> 5s|qzl\xҜKܥc^յ4} K?eFUfs%zxq"5}cx ڭR{r܋ +cY&_9?y:bȨqw 6Vo0 ߾W%,N->] sԶZ/=+6InC38H*~4gY0:U៮>_9 F(QzWgQV/|2~鸸{Ṥ h(iϞwN$@$@$@$@$@$4O|?|?H%`'9-9;&}cie8i_qb\;LZ'DDF1\9;&BZm|_'iƮuo;ٻw)Yq oV ڄ`gZ傺%XWzͲ.zD!6d"&X%ضq36Ɖg/-Ž+`TSCP|φ2gp/-WRVu+>?W Sf)jB5y_]Gaԫl/N`A\D/zھt1RdU*O5ޭ1 έjE".qx2#񉄊y.2**7UnıWuA+nr}'RjfOj,O8@4<]*NWdWqҝq'3b߉_MJs{J$@$`HHHHHHZ v~\?Kqo9x%$в Pi|_\b?zuvLN>qӾ>u쓴Nl-it:WΎImŴV.3IZ#k]q;f| v1qřpe޶jۓcFOQGT܆~m ;z̘c 6}'Nb+$zaH9WLV Sp~9=M@)}5Y1H8JS'V7o1]/(1`n0^ȹwh 茀ZIhZIH/Q ^n^}$(aMFͿg-WĉW:=٢`{2K$KSZ@>pWj$@$P< vg$@$@$@$@$@$@$p4Wq?Z_Hu`'9$OOΎIiZ'ζsׇN}>m(Q t:WΎImŴV.3IZ#k]q;f| vD15ؠMZl=5)Cj7u>Wv\X=&W|Kw ^'СCN@n<)qS"h2ֿ.0Vn1Uv['4z6K#+"ޗLW+$).A77 ByWD 2+Iylr%vZ$p%(9d1ڷoC 3+/%dYݿE`hn}ByqyIV$@$ Py@$@$@$@$@$@$@-@;|?~|?9( @[$@N$ s|q[svLN>qӾ>6qwNl#@)cչrvLl/vOэ]1sK,!p0ub"PZy+UeR{.uRc*ImGXYw -\G#w 4]%%3~cv.a>½j SJs.}/ϜJE#y߇87'0+ؾlf\ r vwϟ}##9?|+7HH(q@CM[ވz?/nYUt+ @4_3MAkg{^/E $@-;'mO1;M+vNg:'hG1\9;&BZm|_'iƮu)ѳV-Bz%ڸ7m@m)Yx1 vJTW#=J+nx A󧧱XGW=.y.:=g})c?V?? Ԍ.wnT~ݒUg0%`'s5[[{>+dbP,Ηn0><;tGKvn옑ߋ];ƶVHNńJdnؒ E$x 1P<%Jgَe_8P Vf%Kz_[`*Pz1HHHHH8R~<ߏ<{^wW~a/np|HvΎIiZ'ζsG&.?>m(Q t:WΎImŴV.3IZ#k]q;f| vD vu#\bԎP3fp)D6fWqQbJ=W]''wJtI E~ܻܭ)q{66hۄN8wu0M%+LI'Qg{)̲qEs@;wՕWWVYM(>tm7k1gE6[)&^$,X`'wèX-A[K# ;{`3{ 2k- {д]Vs6HHHHHHHh8rqǏ+&h(؉4>a/)IӳĮc7vVwduOdkHscչrvLl/vOэ]1SgZHZS 0q'OFqd;QUs?)waDtl;Jf=7ꂪ[?ߏ'8'{uâ5z*1ǍC ds8}`,0%`Gndn,l =PtriO1zнJB:rV+/8+H1Frs(W\txF,X FNkIO˜J^NZ!hb+%??]Z+%5g/ ih (I0 ڈ`it89 d\h"     8`']bqqǣ|?nvؒ FDb0E8>_O;gǤo4g9#Nl {Id6(H:F\+gǤ_6bZYm$ص3>;z E`G^]~7mT˪pp@w}rkPӾ[K8qxzjut vԙb}QQ5 _ 4Z /}if\_k.rع8֫br;%#]YaJzJqx_'Qg8w.VGưߐQ(/oǖayVPcy#vQX gY>WVq7~|1ӆ߬ BY.9c2G#/X:SWCUVsO)]az_+/>];/Կ \}ׂ9/[ (ؑ4lh_{-JK;[m__{cuȊ w=Js[_ ?i s8匳p1O}ʫM$@$@$@$@$@$&pD;|?:ߏ%h(؉4>a/np|`vΎIiZ'ζsG&.?>m(Q t:WΎImŴV.3IZ#k]q;f| vDUBVFzE[px|պJRPF?RGLA?ZeZ.,삨ʎ{xn}4p*QeVoZW3M9߶.ސ 8c;U]_mujK8nq:Ɏ鸀P76 rdzEfc٢_?u)_\v<Pm\}d4c- 5ֽjEأx1PL Y$x; @#j?݀i PQ5)h=M^Y+0k?8Sc6ޗ! R+̎0*б,7ePvL\EBG,l_71={X6FTϏ3vLzlIHHHHH @Zߏ8ߏc$@$%@N$ s|q-LIm1;M+vN;rO:'% 0W1MVjb>IkD7v+nnjOJ`hʀq5GEv7/ƔqE+讍u*SOU֍VY_Kaf]T4iLͤhK;_ת2a.j|V9rx! ^&S=W}^ܪ%Yv)E8EWVW9GǞtP f!kE`|6Y>;0gptw܃zrޗ{rE;h#>Teи%,]e}>Kq¼Xn>;_x^f%w'ڭj1`gsq׫ʧ\fhe_/_/g1ؒ M/$ٲMst߁焊K.;5;ZG X27& Ь51[VcдeaV` **gNjuoĜ>eK$@$@$@$@$@$P@:ߏ[o|?B?I v"1OkaJ􍝦}l;}}q9'iZ4#qu~!A g@IDATie]/fꓴFtc׺v4wq?7q~JPr)IFp,ǡCI \AuoMq耮%Rtt<+nu`#>h<INO#^Qu"I]aD'|NByJC }rCy4      0R vLQ\~qӾ>6qwNl#@)cչrvLl/vOэ]1Sb˱'_H v<JVUmFYGǗ6[ (.6-@˲[{k|C_cʶkS c^&e<6>ڞkژa)ȟ3ǢUL[      &]'#$@$pPi|_|ӴOm累8M\.~&}"[F{$#Εc/d|1նL}ֈnZW܎Z?&1Γ @ vTaQ:[6 P+<<YK4Aak+nsQy!(Uz1j0SKMGh $%@NRR:x??^= @!@N$ s|q[dsvLN>qӾ>6qwNl-it:WΎImŴV.3IZ#k]q;f| v;kh$f/ިjnCn7cr<) T$x2y+,AAUTPoSՁeX &62c]u4mŴnPƤ]Z{. 2x $'@NrVJ8?=^; @k"@N$ s|q[tsvLN>qӾ>6qwNl#@)cչrvLl/vOэ]1SbK oڣ3DA$@$`'(ߺlVyTFރ.̿:^:N4 p-x8CTvju3x1;H$     PcK$@$@G;'mO1;M+vNg:'hG1\9;&BZm|_'iƮu)ie  h(I`'q.TO|31BU=\ӥJFE+bҗa|+;fRt<<%9um5?egĦغQ n-k1v"FHHHHHHGIHH`'9-9;&}cie8i_qb\;LZ'DDF1\9;&BZm|_'iƮu)I1QمHHH $@Np;Vgdj6-˦ b;I[snzW;֩Yq85جLgA      ;6$@$@$pPi|_|ӴOm累8M\.~&}"P@ #qu~!ie]/fꓴFtc׺v줘B$@$@$`'Lpl9%.y[IU׾1G\M^NJSQ/n1BZZl [0 ,Kc|yaYTeNv{RL EvKui'     !@N:$@$@$pT Pi|_|ӴOm累8M\.~&}"P@ #qu~!ie]/fꓴFtc׺v줘B$@$@$`'L80=r0sR-_Z%=hrv1*KcjF5y 恘m-SZU'TI`nM5jYJx?nC`lnzu`@S=61q-hUeW׾in,|*;^4Uކo35HHHHHH\(qQaHH v"1O"c7vV'˅ϤuOdJHa$#Εc/d|1նL}ֈnZW܎]HHHB'hhp N,%bUϥ7Կ8pɟg="t@yq454:C'w=!l7l'   hzr/\;mA6IHZ v"1O"c7vV'˅ϤuOdJHa$#Εc/d|1նL}ֈnZW܎]HHHB'9W+8Z`h qIHHHHh7o;Ec;(9H9 x Pi|_|1;M+vNg:'% 0W1MVjb>IkD7v+nnjON.$@$@$@! vL`?cq <' @ v|t9 v>:< Pi|_|=ӴOm累8M\.~&}"P@ #qu~!ie]/fꓴFtc׺v줘B$@$@$`τ {셔un#0Z6 vZՑ @[$@Nx촌  h(؉4>a/np|HvΎIiZ'ζsG&.?>m(Q t:WΎImŴV.3IZ#k]q;f| vRLTv!   PsgB;<&,CciHC "@N*lGS:>lX":ݿv}s˟?ci1I$@$К Pi|_|=}ӴOm累8M\.~&}"P@ #qu~!ie]/fꓴFtc׺v줘B$@$@$`3  v wHHHHO?2JӰC:sbitߢ߇_OK!  VMHLc􍝦}l;}}ĉmra3iFRILjs &_L+k}1S5cƧ`'De   ; $plPsl$@$@$@$@~|TMC9~b.t q^yz\0IUW7ǖHH v"1O"gc7vV'˅ϤuOdJHa$#Εc/d|1նL}ֈnZW܎]HHHBp&!@αγ PgQf`G]uy`g쬥` A  6AHLyd􍝦}l;}}ĉmra3iFRILjs &_L+k}1S5cƧ`GOԦFٽ? OS x6+[^Ǟ? ۰g;ؾ 8qZ8i95)? ǎ;G n%(i%A$@$@$@;-a)wc;M Q^gi单n~^*vw߹eq};_ǫ;vQq|Iw߿o ?vww^﴾\(A0B$@$;'m׏1;M+vNg:'% 0W1MVjb>IkD7v+nnjOz@ G[5L+f}xlw$ʚ*^v,^I$@$@;>(8<%^# -촌}$;wzY4aӲ;E{ |}7_WďFN?9 5+rʩ/7 n_äރo}vA$@$@`'9$O?ΎIiZ'ζsׇN}>m(Q t:WΎImŴV.3IZ#k]q;f| vjxAt`{ʚe=_M.|QտTb@DHH5`5?][4%?ߌlpvg vl"IHHH5 v\NqvKgs'bdFï} A$@$@; Rh،)5@Ŭ5X9,P츨0F-m۰e˖"ʗ]vڵeGvu.3n8rD$@$<4ߑ\N弗L޸7sQm|S.DtNN~ȼAǪwΞW~D(NeR/W޷^[ @QO޻aBϧvZ@$*ڳCNnubh.jJtVFK\M  %@N$ s|q-LIo1;M+vN;rO:'% 0W1MVjb>IkD7v+nnjOz@ů}]t}x.D$XC$@$ZPZ$89s`nsΘ0a>_&"hcjVK.šCկ~g}v֬Y_QO<MMM6mN:؟=aZODOSӪ5oH`e<#%wQ>)ͭ=WB-؉}DIR oG›wP5.ziʇ ⛆ `#iPGxr:*75ʃ-rC:ǃHH!@N$ s|q[svLN>qӾ>6qwNl#@)cչrvLl/vOэ]1SP@r"e8׭x oߋX{rI6 @+'@N+`gϞkQ^Yy{o6Ͳ' ^/2~ӟ7|\pA7Wȑ#}1Z v 촌'y$;j3+5t<یwy}X/oU{FBp`n^v7 v76\Fo23i*jȽS–HH`'9-Q9;&}cie8i_qb\;LZ'DDF1\9;&BZm|_'iƮu)Q(+i\cXf] ;~6̐ >촾g;:B`GW-yg_:'USSU>r!,-ޞIFJ\6(؉, T촌'sd;v=.G(qQ+|8kZy'GDӄ5w]a5z9#|;sc)`K$@$@(؉4>a/np|o􍝦}l;}}ĉmra3iFRILjs &_L+k}1S5cƧ`G= Lރ_򼶁}?x>uZ :t8]:7bg{R2' @ @Nxμ`Goo~{zz(Wr1Ǿ}O~2*#&/})șzߺu+{ ԩS`՞$+kٶm[=nݧO>8:mأz ^eSO 3jD^lقru՜ >hᔾWݷ_~^=~_ߋ^E͡=UM벟6x~%%%/}]vJZ}sghw-sۿ[/3>~}lԾwzkצ+jE NfgPh.כy.zU+mC~(Gu߸s4gN늳#*/~fŰڳgO5NJ9vIHFĎ`GeSq57X0c؄9{e>C\V)t耓OWaw1`Gn5Ⱦǫ=v8W`/_$@$@$GHLZ?߮c7vVwduOdkHscչrvLl/vOэ]1SPAN&nyN[Se#~wA vIH>v(=2^GE v4w?j"ZDEz#V07"ŋVS]]ʜdNw̘1H 1BUVEE\vevy٫NZT4v@\dbտ_hQ$0qӺG__}o.1nܸ@Tp}avZ-+r-}>@D(;V\wδ~&L!ϭK?'/])ĝu͒WԢ b=_FZ:erՂٳgPm5jQg_q |9ʻ1+kR viҥ9E5:;e{ߖ>ojR9 }0N$@촌'u4; :\cԢ:9Xa'MIkD7v+nnjOz@v^ș;-93q4tHHZ9 vZ';/+E^:J"&ADJA$$ %ˁ*p ,[)FE JQ)D^hA/"CAL`(;l췞2Ȝ]k${??9c^7żs"=B"F}E~>CˆeܹM ;Zʮ?cw_k3 bE㶎!}9s}"-Ul)`^-UXbﰇ(C霨̫ߥf|elgvN6o牽%KȢEqGmmA3t @?&@NklަdϖGdQkNPs,^35^޻U{6 ;ҙsk]̺dφ&{_>]f=ʝw+;q$@$@;&&̉'Ʃ1mԶO1QRbTѻY6)lF1Ryv3[Uj ;Ԏ4L{)6`G~#]!ə M9w~/?Wdy݅%HH`0`g02~ ر?&Pmp3 ^q^:uj&?' &2e :4͑uŊTB .놀7bHH1[Ǐ`x=lpK;DucES^\a BW%f#_?nm$z3x%8c]̡{x C_*3׳UGFG^xŹw}}4 .%"%Iz^׭[7" }ӱ xn-FAogBku.}gPccIV0 MZxq .+nF}+V]_,{Fl @&@Nk&d/]A`gIvXߠٶuj'e()M*,g6D#-eHbglWm.P;2n|n}ڦ`' +y>ߪO{z{z'HH vܖ6~ رc7@\"ɓ'(>(/"/A>nZ|j>;~h{ns|+,(^|\/s ^%pE@\_9fowc{*1RLu3X7/^Zʮ qgƿʯ=&({n;S׌.{y k_Ϥ(JgϞɋ_Oͳ sl3 uۚ0yqMoX[r^XAO;g3F WJKﷳiioX#ؑe٭'Wb8FnCn/Sާm v |zu<~p/ܩOВ)WL?gM(k  (ˇ ~ رB+? Qa yĎ` Ա`(VX7ȅB-j AI =tMV\[uz([l܋4OE"~vq#D͋xnAg玝-]7*;oH\{b}cic}Dn3|~#2þ6sTcI{Vb<-:Vg猝37tFg! BzerV&θqڞ񇱯:fit):m|s? K䋇v*ߠ;w]gm-'aa')k&șHlܰ]ΚP$tȢY;K&ȌYW?~ͦqi @Ib0'0? }vI>6 +S6)lF1Ryv3[Uj ;Ԏ4L{)ة|PmUt^dG فHH``g$ct=@/ *=Ubv &b moC!njW1ֆ+<]ouKͅH yV<c"ǭ".{SĮv] 2nܰ[ԯ *vRms v,/}6ԖA,y1C3qQbgMVNڳo3<TWg9ѿE> NlwuIۺ2$ mvS]!]daMr62jG$@$()41aNE86vǼ϶ծS>)FImRFg<ۧUR8jeHbglWm.P;2n|n}ڦ`Ae    vxH Bv-֯SWj+~sj1сc5D~{~I,Gg^$͉e xӹl 4'6/^[36w;>_Zˮ%rܺ?^h^j~ͱgӘXlX~vfasƸu`LǴF0ҩ`4fմ9׬Nc @`5v;A   MOBL^kc}l[:퓲} m&kn|ͳ} [(QvT^(}xc>/Sާm vjTv!  h`'"`?ti1QC=$ .ذap nA6n+B}p$@$@;&&̉'A1mԶO1QRbTѻY6)lF1Ryv3[Uj ;Ԏ4L{)ةqPمHHHAhqqO`o(C5\SZ ?fCxa-J+BL!HXAU}% ~0e]wp^SbdDŽc-*n@~jn>XDm6*L^MaW5TvP;^hgYX"?cϬ Bzb{_Ƕ o69tZ5WNs7<鸶7y9aE9Q5 .RU b0'"XǼ϶ծS>)FImRFg<ۧ!m);F*/>nfkJmsa1早UtsC~6;=O$@$@;xi-,ikkŋ!Q}-ci˝wޙ7V`L0?#G;V{kp=pS@ꫯ1z؍(Co=+,'9?cTHyxтcg>Zr켱Epye-(dU5kVc;f%:N5ZW}9l<ƸshmwM,=Se-b@4q?#9cB$@;>p$@$@;&&̉'A1mԶO1QRbTѻY6)lF1Ryv3[Uj ;Ԏ4L{)ةqPمHHHACΈ# ..\ #> E,_{ ~̾s׋-%-:䄞 ~_j.}WH8`_*n%cƌY{ n:Oc‘/&pʌ3[Shm9b1;oHhEFz.-zܩ{sJ۱м2=K}9l꙲kvo`#elVJa#ym75^CO*!i]HHH(I ']QW/ϜIHdҤc(#|-@̀>PUع>nCE3:%i,X'X7?>cMǖس~\<1Dzeˊ.^a9! s k׮-ZAOƫ+.YuyqqG%SLy,_/psry`&2yZR98i+<^9Uj~;gP}9l)FImRFg<ۧUR8jeHbglWm.P;2n|n}ڦ`Ae    vxH B`r`)rk>a2wzC8ɮ솈 %i6 ~tm.?D|!Ċ+nvPXGz,سyB9@J+=M&& +uA)>mIp5Z1"טK.)mF)^a۱Мfkћ3eI{݂H'VklN]Vv\ϟ_Bw̨sFm$@$ P;HNkWA$@$08PSibœߋpbm$>VNmlC%%Iel†hPl*ͅj|_Vэ OԲ? b )`Ǿ%80yd{c)2o7>mXΟ:g]HHHwJyq-9,4H6 `g|TPpCÛ\P þZ`cGvz `]m~U0> e V` ^XrLX֨5݇f bMرb_9I=oC! IIXgǞ$@$@$P;&&̉'Ɩٶuj'e()M*,g P G Pl*ͅj|_Vэ O8B$@$@$ @O VT%߉'4u@U!`g'  8(8{; ;%$@$@$P;&&̉'fٶuj'e()M*,g P G Pl*ͅj|_Vэ O8B$@$@$ @O ,kz^{%'NosU=Cmֽ#@N7 @;q63B͹HH; v 1ML{N#cgjשmch6XwlS*@)5cB1fƫ6vi~ZE7>7>mSS㠲 @;< $7`fϞ-Mwqi"JC46& @N H!S @7 v 1ML{N >}vI>6JJl5z7>>Q(;F*/>nfkJmsa1早UtsC~6;5* 4PÓ@}C v@mKWWǎ+xѣ7;K.+VСC#`M$@$@$@;F!|NM$@$0PSibœߋpbm)>VNmlC%%IelVJa#ym75^CO*!i]HHH(I !0;J-֐!Cz̘1HHHH`5HH`p`Ą918H>}vI>6JJl5z7>>Q(;F*/>nfkJmsa1早UtsC~6;%j-?pN.]WHHH```g`/u $NRHHHH` M[O)V:*v-682 (b0'"XǼ϶ծS>)FImRFg<ۧUR8jeHbglWm.P;2n|n}ڦ`Aɹ{2ܧ/4  (Gkipq$@$@$@$0( P^GӱF1<ϨxHHZ;&&̉' 1mԶO1QRbTѻY6)lF1Ryv3[Uj ;Ԏ4L{))yP89Cʲ24  M|% cǶڒz(t[& Cb0'"Xcgjשmch6XwlS*@)5b@IDATcB1fƫ6vi~ZE7>7>mSSRSHH vvaIHHHHHZ;-A\ "@N! sb~/‰q^|l[:퓲} m&kn|ͳ} [(QvT^(}xc>/Sާm vJT vJb "@Πn>, @`7# P()41aNE86΋ym]}RۤbލϲyOap0ʎ ż϶R\ءv̧ejܐM;Z9ll7c:y WbckONff}Ss-FSko[+?}]*Ջ1  h >p$@$@$@$@$@$@$ #@N! sb~/‰>}vI>6JJl5z7>>Q(;F*/>nfkJmsa1早UtsC~6;S)\Lk nS붕i{mźdOg_Z9z7>mS}jʴ݇8=;cG_EvH|]>~-sM5)Ptͧ_h'  >&@No'      C74IHH``Ą91A>VNmlC%%IelVJa#ym75^CO*!ilvdGٟ(Y-[w0}riv z)OOߘ '"k_o¬՗$'qsxcضGKvwysʺSB6ߙF˨F7yOŊHHZ;\ &@>= %@N! sb~/‰>}vI>6JJl5z7>>Q(;F*/>nfkJmsa1早UtsC~UXLtx Ï~̛Ή;H4^i,?2SF+ +jfitl9,K?]kS/ʶڌJHHZ;\ X/m&    B`رeN'@N #$@$@$ PSibœߋpbmlym]}RۤbލϲyOap0ʎ ż϶R\ءv̧ejܐM /{7A>j쫫p;%)Fk`̛;e]#Y庫?(3<]rO/d=r[ yV   hVp!$Ѓ;&McHHHH6%'|R(ٔˍMN9N"  ABL^kc|l[:퓲} m&kn|ͳ} [(QvT^(}xc>/Sާm vd,=d–+^\)۞V\s89wwe۹^s]厏o}$]Uo Qz|>$  &@NkW7x P3xON$@$@$@}E"s^ vz`HH6% v 1ML{N1mԶO1QRbTѻY6)lF1Ryv3[Uj ;Ԏ4L{)k;e/i^?맮b&POsʜvelM+3"sHHH P;I L0zIHHH6 v6*#SSsIHHw()41aNE866Ǽ϶ծS>)FImRFg<ۧUR8jeHbglWm.P;2n|n}ڦ`G^gJ_zM~8!}%~-wVNmlC%%IelVJa#ym75^CO*!ihc읽+{)|_5=Oogڬgf%eܿ=[V#o|V.qoBάi/=Z^ߵ.  tNI%PSSHHHH6* v6*ڃQS; @eb0'"X{cgjשmch6XwlS*@)5cB1fƫ6vi~ZE7>7>mzu_Аȶ=7s[?.=o\/ͱ2*l(wʼ|@a^t μtOh\#Ϯ\Ss]eǷGm=_]ܬHHHPZՐ`GI&   \(\P( lLb0'"Xcgjשmch6XwlS*@)5cB1fƫ6vi~ZE7>7>mSmPs֎Um;Wɲejjzze t%?ego_1Bױc#;uDzmM~VOOyMv|;j~#m瞒SvO͊HHZ;-!\ t`GHHHH`s`gsGN $@$@$)PSibœߋpbmlym]}RۤbލϲyOap0ʎ ż϶R\ءv̧ejܐMNA=SUYض%Kn=iKT.[sq9̛zGj7HHH5PU'@'6 &@Φ&\n| vqb l b0'"Xcgjשmch6XwlS*@)5cB1fƫ6vi~ZE7>7>mSc)I}+4;/eo'?46oɗ|VfXuPBNѣ}$՛2kW+ v[ha4IHHPz{Ps@$@$@$@$ P#|.zo^.O^   ()41aNE86xٶuj'e()M*,g P G Pl*ͅj|_Vэ O츃)'n)'}[ȯX#?Z"έ;eޒK}P4fl;d(w<n  h-~p5-D5_-?%ӧYGfHHHH21#is$@$@$PSibœߋpbm >VNmlC%%IelVJa#ym75^CO*!i]HHH(I ']W/ygN$n7;+G%MI矗˗S :T82efyy'r>v9y@l @`i)q@$  MHBL^kc|l[:퓲} m&kn|ͳ} [(QvT^(}xc>/Sާm vjTv!  h`'">)3v:T;`gܹjժ@0rHodܸqH`S /: D[y晼INJ5 = v~ vZc   ABL^k ٶuj'e()M*,g P G Pl*ͅj|_Vэ O8B$@$@$ @O D`gYpO#3ΐQF E$Ѓn{dݺur2Eɒ%KԭJ .aÆ~ |{ߓe˖ vsE$@}Mށ>p$@$@;&&̉'A1mԶO1QRbTѻY6)lF1Ryv3[Uj ;Ԏ4L{)ةqPمHHHAhqرcCu֮]H'?)yRI`PXt{9;N&NXnzW~SN޻;m$@;S>p$@$@;&&̉'A1mԶO1QRbTѻY6)lF1Ryv3[Uj ;Ԏ4L{)ةqPمHHHA v־!˟~F~y?>0in1iҤ# ;=7w… GyG"lzx-^τr'z~71̙3Q4!@N@ @`i)q@$  MHBL^kc|l[:퓲} m&kn|ͳ} [(QvT^(}xc>/Sާm vjTv!  h`'"`焯/ ޳D&bf (_Ib])^s%DGߛnO0!Տ:ڤ#g̘1[FwA1͇瞓7x#w ><=X?F]w51 cV-+VۅpoΨ7 ³._\V^/zYsb=NYwܸqQ>?m< nz_ z^Z_9rd =z-υ9u}G?wgg@k(3y5{hüX_3ֆ}kz|j}cAy0>o/ 65F浥*{Mћ3eו|'`GEX=306wVVʿ+56nL= }Ϫ' V#@Nk;\ @N! sb~/‰q|l[:퓲} m&kn|ͳ} ޖcB1fƫ6vi~ZE7>7>mSS$@$@$0 P37&`;Ft_}6h?\n؁x!%p΂ _~@Dq+h^Xh\1[㦟3f6"=餓 Qh-r̟?E~pqݴ ^v"Qѩԧ5~ؿ ?_{y _B vNEs̑v/B;O&_ 22o@t&lvnbA?  vZc'(i}*HH v 1ML{Ncgjשmch6XwlS*@)5cB1fƫ6vi~ZE7>7>mSS㠲 @;< $!ٹr?*o5D־7?J!~B Ԫ{+;.~C9ھBs|߮}}Ƕ=P0l;w@xӬ@񎖲XFs@d 8Eo{Ѹcb믿>(z}as9=ns\߆H B[7WK;!JP:'2wY=u7î꙲}];࿓ybjɒ%hѢ|xtG۩rsPn0$@$(i͡`5  ()41aNE86ym]}RۤbލϲyOap0ʎ ż϶R\ءv̧ejܐ=`;nm#.$@$@$@ÚE-9,4H`pf/feѓdfhR.qn8nR(ц7uIw/GSh1C}b)SСC[WXI+rn8p#ߟ[Dq~ ֋c|hs@XWh=V4֠/,xUn6EFx1_}ua0-nZy&C9$=lo{sëpϋsi.\KGEJ%[.go ­EX "cc߱XfɁb-[~;Wֈ\-Ϡ {?z@ +seVvY7e#.//Sާm v!dHHH< vʳb #`;G~Pn>>er$ny>jxtɬt=^'Vl9'L)46 2(7M-c FڰV}Rg뭺7`}p D!6ϊ'BxLBeo[x򰷐n^eaF_AU̥X^l-Xb-PgT#grcgy*-+5"&s3| /(i`5  (ء`ǜ('3CPԶP;2n|n}ڦ`Gw5 @eTF@΢L3)],"z`&ؙ:C}}`"pZ8윚uLtX ߞFQ4Ǯ׿*IsbuYq孂+t.͉oK1yWeygnjqΫ׬k 8;O}<_s:4fk;=`㶟s\1n=+*1mmo-+ot*.3Fz5-vN5Sج/$@$W(+=`'HHH`S`sRT AhE:;Ԏ4L{)ر_$@$@$@PS #ؑݜ3-|sNcr.G4^b7؛"nl<&x衇d…=v?6,un9;(V` x%S_lP]y +]v4?lGm׋W]8e/<ƫJ$+=WWR pF }8>2sr1S(}c6eqz=o;1= JͯcualsZm];:v\tz c[X!qYIH?`5vHH`p`sST AhE:;Ԏ4L{)ر_JvZ&dq ԗ$@$@$00P30O X(rdIsW/ygFnٰk҃'M9V˘`Qkx~̆[2Va煘BP ?>J`ˮἶ?*Ɏ ! ,[t?n+DV<z~* =SWSxX} ;$cZYV!O3?G^ױ-:au6{]vU O:M8?v:t|sNyXvΪgT`M$@;K;oI۪K mF-[Q   hyPciJ!(؁Eujvi~ZE7>7>mS;^XvxvkΣoʹ+Ί3HH v6q}A`'[QWӷ)5kzA94n|Qn2꛽촵 ~xE ¾Fֱ~;̛v|+ @0&ʑc+výϵ?{V)_ zWD=JFCd1b*$v*gԏ6 2 vZcw(i}*HH v(1'=%I쨈PԮS>CO*!i`SSSIH, vzKI9uC?J\~4:tYٲOY.M)|-@̀>PUc}<1ft/,KYNqm{nv?~|ƚp-g9x?<be˖]rB@;֮][+=WW!.c=] { 5JLX|y^dy!¹ùM(XefYY?rp&!2Vx.#Ls۫v|Ϡ fsxl͞îTNs1ŋ[m4, +cE_ /(i`5  (ء`ǜ('3CP"@QNmc>/Sާm vWMNXL% ([-傝ȭ8oyTf\ɣdSٯUN=CD0iҤN13θtCyLTLC"+Vm!-(Xg r ȁR=Ċ'Boo <_>蠃yy9Ǵ 1 nׁF;?c_Qc]r%żX(Ћ:l;l~C]}9zs];i[Ċ}ͩʎ W"uΨOHZ;C>p$@$@;옓bf v |Ѣvj|_Vэ Ok!/<52tPz&iB.%qc zdRڎٶJ:ɰQ;u|AzaɒZvФh_;m  \(\9@!б Y+ٿ /;u,qɓeȼy'zP2D;!GMVUb?C@_~bwY; }VHRtw}w~ӅOe?⣤E }FpsD7[7xWƶqa/*B"+8{!^?75qJGLPl5F8w^@5\}B,\rj$)v='c{/ JQy Xojf1|k.M1mѳ꿱c}žs]ozN؇*g/X7C$@UPS֦`oޒ] %r].9ar~:I$@$@;옃bfm%eHbglWm.P;2n|n}ڦ`p.,xQp/}4;r|3cM ,eߑ_wB!~[!glљu=E<"  >"@N$Є@+ v,u6'4u@L@G@H t`5Bo;͞[~$)iq  AB vQOrR13;*ԶP;2n|n}ڦ`2q_*21M|SE7wjLmD;+ i*F˜螹0g9jr>;fȏZ-tɢT;[PtiF죳&  K˛@YY`=Cţ^2q<^Y3-0f;{ `&@NkF~rݙmDjw|w? .|=Vkl8WA$@$Ч(ء`('3CP"@QNmc>/Sާm v z AIf|W䊏]2t7,gMvdúǚrtfA,.``oKɻfn9jo7%c)+  >#@N$$П;xٳgK{{{wtHHH`P`sST AhE:;Ԏ4L{)^EUa2y$=:8]ҶҙYF(e/Zvx62U@>cƚyye~ٽc_ksŎ`FYjkb;dGìIHH``g#$P@!,X ?tuumcǎ khH^:.]*+VCʑG؝HHH`0`5v>a9S~E[mC 2Pc|J!(Q ]}`1早UtsC~6;JvrOk>D_1ߴ;fYXZ/9&-Y/9fΣr߹le7fr.o4:HHH``gD$P@2dH^3F`  vZc?z+ع; w&!_doMU @`sRT AhE:;Ԏ4L{)xLa^wu_2%(Y6c\aF vf @`e `go%HHH  vZcz+عߗȴq-X^. :E$@$@;Ýbf vTD(jשmءv̧ejܐM}eu+e{ݠ`Gڲkm+gg = xlvSN. 7?h 2 vZywL|v    w?ko;{vθU_~LMxhJ+%I쨈PԮS>CO*!i+ؑt͍2a^gάѻMF`Cn vDvkp_c4W ~኿6tI$@$@;cbf vTD(jשmءv̧ejܐMv19eKCf#re7 #.O?䜓.eȓǿ],Yvxvr>r[:~&ǎ,x1\wv%dtU@y3k=: v 04HH  vVq$@$@$@$@$@$@@;\4v 2S䫋~,tA@H$@$@qPcNGJ!(Q ]}`1早UtsC~6;_ewe[9g,e7<$}}L8{ֱ21s汱Fڲ{Άy>NHH;}S @@;QGEd}eߐco߉M$@$@;cbf vTD(jשmءv̧ejܐM tAs7)Bϐ\} W wֻ7>7/m'T$Ǻ 9aFu9)|R㬞BWw͐g(7>mScIHH*`.& &%;tHH v(1G9%I쨈PԮS>CO*!iM$@$@$P;p1HK-".HHH7c`gl"HH`sXST AhE:;Ԏ4L{)ر_$@$@$@PS IZ;&MjUq9$@$@$@$? 7>mScIHH*`.&  vZlC   ~M> vZc   A vIOrR13;*ԶP;2n|n}ڦ`~h T"@N%\L&h1؆p9$@$@$@;}>p$@$@;옓bf vTD(jשmءv̧ejܐM &  DJL$b(i rHHH5 vZc(i}*HH v(1'=%I쨈PԮS>CO*!iM$@$@$P;p1HPb kQU PcNzJ!(Q ]}`1早UtsC~6;+@HHH v*b2 @`6!  (i`5  (ء`ǜ('3CP"@QNmc>/Sާm vW6 @%Td #@NmC$@$@$Я Pۧmݶ5U &G>2`p;?{v&a1_ʯc5) Qzm]}RJ*fͳ} [(QvT^(}xc>/Sާm vjTv!  h`'H?`?N$@$@$j(i`5  ()4^ԣ⚘_j$}vI>6 +S6)lF1Ryv3[Uj ;Ԏ4L{)ةqPمHHHA L{\; @`5vHH`p`Ą918H>}vI>6JJl5z7>>Q(;F*/>nfkJmsa1早UtsC~6;5* 4PÓ@|D~RȻDGazMo˯FvO); ;RXr$  hFBL^kc |l[:퓲} m&kn|ͳ} [(QvT^(}xc>/Sާm vjTv!  h`'Z@Oɗ Q~4{leɻdC?c<"g.?FE@IDATHǓr.Yɼ3'K6. "l{lH|͟~g?(/V̿L>e#s\YǠ`gp($9 <||ʡCG!C ٜK7s=#O|N;4r7;Dž@%@Nk;\ @N! sb~S϶ծS>)F:c%}>Q(;F*/>nfkJmsa1早UtsC~6;5* 4PÓ@M_)u"g/zAOi\$iD/fw\u"k;*"Ge۫v:>Gb3a@ fdB!+3ׯlCsʪU?lCaȑ#eqN MCϿv /Pn] }3-r (ANsF   EBL^kc|l[:퓲} m&kn|ͳ} [(QvT^(}xc>/Sާm vjFNYMPhV{v$  Kw\ \r+u-]I7@0[~*cz!X~2|O!5vkcckyخ=+_ >_i{lkvBٳgK{{{#F38CFKH #=#֭?^Wh"YdIo. d0/1ɲer; M MFMS%m~+DQFW:L& t()41aNE866Ǽ϶ծS>)FImRFg<ۧ!m);F*/>nfkJmsa1早UtsC~6;5OmDzeijs}S݇I_% cdĉ_yS{]yց|^{`;mTVeP:d)ܱ%.z@ȖCG!_z, _crd)yTO"!  |()41aNE866Ǽ϶ծS>)FImRFg<ۧ!m);F*/>nfkJmsa1早UtsC~6;5Otc8}05HHH vb`"V^{ y{wC瞔J? *.ycs̯#m]&#nj=?uk)Y_.;wxC^?mrzXA9/ق^~xvՎ^%*/÷߹X٫&:A8W&W"\D?O/z\0Rܞ;|xg…#]|ż6H`= gB9kN~5s|,~ P( '@gZgZ!#&炘#?: l6κWkdn'l;&GN?$@$@;&&̉'^ٶuj'e()M*,g P G Pl*ͅj|_Vэ O8;M,h+c7  |(|9S?#l3G:oBPL }O>]k4c}}-ESǯw>w43y\o-^j?sQIM;ͿL&w!nysC]7O?% _]!Y7e#.WwӪ1;6H vK.$:tME|„ ~&=k=enZ@X78]jYbE:#.[~uFyuz|9X N }ǍKcldz Ϭ*MuE<߿#G1a]G71+gy`-x.̩{k)h+DLb^el†hPl*ͅj|_Vэ O<]ӹ,&6+}gekHHZ;-A\^0ӄqՋdޙc]˔&\~ޑx]3dg'\9sE[O)@L. ;Ϳ24n 8s~@Ncd39Z^wt=Oۡ4YfY~! 9{)1v}6x0󝴂Xk)^hYbޙ3g>5gsc 6H6+ v6+d<'ל?&l{A 'g|'y'̒.Hc.|պd:ilnSӜ3HH;&&̉'ƾٶuj'e()M*,g P G Pl*ͅj|_Vэ O!/<52tPz&܍e;^_RFﲛwIǪ6a6Åˏ/_uf%쎣K?$@$@$ Ps~J`xyWv!ُ[寏[E.iKWkw}E>qz*Me̿`'9=z<^GkmտL:ff>Ra_ "2$d+ yJfb]3@1]dϦΕiW~C~dfuszzc *o0*,wCfk tNeR\{>o6]{3ezw'^XՒ%KdѢE裏S`:H;}}I{+؉`!7;Bsu,ysLYlױjLsg7=9)갾k?VdOʳ҆\?A*ETb*DXBEGPDƁ8oD e(8/"b $S5^ssY{<'{yg{sAG7I7|{^[|~c8ew29>ɍ5`ǯ'Ovknp}1 ~eZE@P J؉4.bK/I86.mR$'yO6lcm|0:|c"("&uM&{.5Ev?NMkKꡪ%Y}dV]Vg|ƣ;aÓ? ?!86|_b_ 2ntZswi}}UPE@P*J9p`b@Ůah꺨2`Ӌ¦O ~0mZ1p^ ~Yn CcBrZ a&HS0vUM[n=ˡK`:s*ƒŗl D9a uB'\=՜ @WX<aÌA0<[rNa?L*H Ff&뒐܁׿ȇM&H&)** ~(@8p\*@:W͉@x] c)F$"yC|$$z,,-TH@q^pҔ$W cW%af#Y(`6"# r{7A3-Qx`&ACcD5lOrl3WaV"IW`0#ƍiIIbNt^O:LAcF}91+p ]tQ@B_|xUo9i,~Kb ry,KTIߓ`o͛32^!^?Tb{F٭#>& '+Ēc=m+A@ ;w9#]U=GC.4[O|ow>%ȳKx#Y|ɏYK0{ i.MG^C uxuR2u~X2-zRxFj Jnw탮`GD6X"(J؉4.bKĔ8ᙑ~R$'yO6lcycHFHsK>|~6v6e[ۥ#85nM/uVNVR6\pƛ H;NŽڪ\m~l {_/ BM l}3vmŻkP<<9NPE@8(aC0ˀaDrU0W[ WػaaURWIN׈cIvF,^s[275d,3`ksdƹ%| PZ|#|o<x8A:R9{0/LIïHn2 H мю@aNbdǬ=@'F>pHgܠ&Gg͚P$}%by#$q@;]9Ƴ>eR 38o,rlhL3#sw#|7IsF~c|8bnLr(.nMI9$?A1N癢IyNe&$9X,9r/8G'33jMu"pP6%,ߐq~ kc׫ L6.%Ka׼:a߉ˉWdБ&na|!x‘Ӡsdhʧ!On4Af 4b!켪LP("B@ ;Eq鑘?DZIo1>Yڰ*>c"I#-qlRdRs_mmD6Q[ ;xj?1ԺobIM޷!9 ag/R2Mxq7 =ggr{ \fMacCY|N Yg/a`Uay4X$9 s b4О冰3uMന *8DADNCp^DI὾Q }Ȝ`{,"Fȱcǂ@tjpќ8Pp$ D(kۼ8q1l#X]g捱FA_,< r\m}D_W5ɽ >G~ vWAɥv'Yq]M >Di.V%Yt8:g8_EE@8(aΠل&M3Ȑq(vKUЮg7#‚o^G{HFx,G_aB92" ;!Cң#mհ^:TG9!Qg7?08Td2JZPEu#L"j6m<'K6>[1LH@ۇf:&=lkt&ҍ饎J1T =Ŧ#4qPP1pn\V$ DcxIA~s5j,$5E*("(- %촬ٴ iKi8 ~&+%an:c_gSn댰fI-|n1uk"th`0>ػ'7vbI?. Ү ;DzpH0^T[[DV >&fīlc) t8qD0'$#ptR:"7|ca$p?N./:CYxg7q' 3/aFA+י󝷳Mxڰ|υ=`X\g*_s}O"O5p\(kQsv K,o?>N5 dn0߃GlL8dD>TUgI'%ղ Ǵ&n ewTbΏV +1PrTf߰I/z pSnBmj~2\䤌m("Zi\^p\m<[&uMrdi6g ׸~<&)qlRdRs_mmD6Q[ ;%;Cp俟a17BǟBi:5@[.}kXc5!(a' J"( J9oJ'zE1WG0Э\] m<9v,"c4]EK#`r1Yj] ȟgJ\&H0;(t_D͌_e8ITHdwH:XbECzȇW^D>:~pp?%|\|p?ګxtm˸ǥei\lXb?>>6Oݵ|#~LL ^.Q 4ȹw2~O$"<>5#š}AfͰ/"(Vi\^p\m]ڤINR,m#lat׏D2P"E!n>?Mx;L\jґH7צ:j+a󃮄[>6?aQƣP?_‰gՙpJ؉("4vDbAf֯e2`)HiɅRvH)WM-Y]9ޙc[ ;*_~QFډMn}㶻kЧ[/̓AMc J+B%[ n0~yx%ƃ+{UҗA"A7·?(w*~mظqc!۶m0 f6f+lc_xm\7k֬( 1 ͩ}AwXUWxNB? xWx%'O+x> 8q ~\,6>.} fQ9r$\wuY~|%|l>/mt\{H}15l령:{vx$hͳ.ѵX6r̓>Vsv-4%.--T̓oLd6XPx L]#nsV!Mj76}7eϘ?4Mv^MlNNU&dfS-a&tC& ~ `~1V+vRPE @@ ;Eq% FMx$5҆m,>FqxL$%R$٤dϥ(.ǩt#}mzvv}!؛vv? +aLJE@Ps#.+v- 48suO6֔v+av`OpqВ ag*X1킩溭؁)SVLJDE]þUХDUͰc v)F@Ցr(dPWve`A~j)|$BWpHN6_+i9_O> >H}-Dn@X/RD$} =Wr_6ʶlWS+~ ۅ͇6oXٰD?DcdkYpo|clcpkAs9$?5τCMOH/33J}h(%[ifv 73NM; F„Q 8/HCO,ODYp+OaGc}m l .i?}Cy@?!avAE@P"L"j#&uMrdi6g ׸~<&4&uM&{.5Ev?NMkKïzĤ\N;vN:[y yNR~Gh2C=,zjSE@8_PS:ϳ@ J2yx<=2$a`!y@9vTŠë.}Gsl/1jCUW0Tez`]t&rkô"Ȑr& q`!ozRs;555HPn#UkaA6EG}AIhtFA9xl 7=mm_brx ~6$qʈ"9qmDCzDIZ~Vu/.袈@ƱaIqsfINAbfʵ3G}1݅quz{{}:SŊYĐ<ICH".qgZE#s8vJ$h%ryjJa%P(I55U0aKLﮅV溬溬N:`@q<)56}g c*QF״@JijE@P%Dd1ǥ$W6m<'K6>[1斸}l6l2s/ʶKGqj"H_^ꨭNw{ܭ/q܏k҉l=`ccnv/+"bB'ADc_:vFs?򀪒ZY.Wbع93z* ;7ȐlJqѕZi  ˔Ea} M#sxbT砹`&|X5HR7`FX;N[#tNjg!zMm\knH ڟ}(|$nRZZ 0{Ŋuee%5XlmlǶ6˂Ws^6:cIKu^s5(Fgl@<@` Ep+`t W4)#IOu?]|6$ѹX)l-?)|Z|gs=S|\n{nwA5+pH5]u(1s9m+A@ ;w9j ; w2>o%Qv)  t&v94KrS% |gq@) v?380|: * ,՝:-6?NqgI cl=*ɛB,WJؑj[PE v"2#1%:&9Ic|a U|6׏D2P"E!n>?Mx;L\jґH7צ:j+alUestdV(֙p;-Y:欄^ n}2f E@PE :!f_uSq> 6\}⦘1[zg*;!Ĭb̓&5ƶa3v3 ]& n< ٹaKThק#Ga>$ cLË-P"U‡yDq$_D>Hdyb,2^ک- ٥\f 3ZAY; .H樮qB;ЀpB'O+H.d% < w <ٓ$8]|Hs28f+G?*>gu,;'9~d<<7oڐM[O9y .l&nj{F1V"{sgp&;Pw` lt3 ]&oA[)|pj {cD1&S0SOFߥvIح=`vŌYG ؍axakCIU\ox ~Oce (,+S%jQE@#L"j#&uMrdi6g ׸~<&)qlRdRs_mmD6Q[ ;xP379f,Uy_驔# ;p_s_y|;l_c(7Ef9dt ś{)w6i:p }QE@P)J9-ag\2jčٞM/΃C: e۞[L6#H97K9:5Cw2.K.Wq ԰v)c*(6,/ IiJ+Cf8́33]Z6eQJ$gyk/'uR쳍ќ3'I~$W׮qX>}]Xn]eFHrFyʊ"pPιÞ|F;f~țpjPd<:u-OmOd6 _>5Ԧ#΁Ap[R%Pm嫠÷`b3C~rH%RXl r?^: &N؜ͤ<-K0TL"&cl;j_$IkE@PVv"2KgLڤINR,m#lat׏D2F[٤dϥ(.ǩt#}mzvRzj ,f |ŤJL~0m`cy@~kۚmiQE@PZ JiS:?hlcꠍ5^ ܗUk9t<50}={O͌hÇa6Ko;Wxl>|p^ C Xе[6J`^~ Ӆ4OecMb׋$}0sn\5' "S\a۱$BD I$Dɓ'իdB˽ޛIDp✭l>ϝ$\}Xٰue$Ns-q¶k/7Gu쳍)v{7ހwy'7sYXFz`EX9lr=دE@8(a )agxQ֩u55DL]PA-=pݵPV(~aA[P7jWdydyKJd9fMw%<а]WGCٕdĕ̀{`r@QN6PE@@@ ;Eq% FMx$5҆m,>FqxL$%R$٤dϥ(.ǩt#}mzvA?T&? /^+sf~G]|>Tľ ;m)"(g%iE9 } J fS~ݩaxHf:ڷoa6"9K۶mZer%`_28/$#!N9!VH,>?u 'P\ ׄ" 57 ?s}֯ώc!F]tO\ad]sߓtNqqr9ksqF(@vZIHFةvʘ{{ɐ/&M6X6L$6OO@tkp_2\$ ]š߃L7ꁏ, vp1V|KF݌&Vrc: t?B o>- ˆqQGQȐ}L~x16E@PZJ؉4.bK/I86)i:&9Ic|al㳅k\?D@ }l6l2s/ʶKGqj"H_^ꨭAmںz?u vN/-LeH>jt7s~"9FEoEڸԢ("cs7@W Pgt-zMy >{ܝ>KWgI<$̮3bkM%vr]eo,.<[Pqq7#PvacWE@P2@"N˘z0ƺ16`ˠs~LzMm4֟Symrw[Wc([1LH@ۇf:&=lkt&ҍ饎JIpP5DPE@PB'Ahؾ?#\|~7'5 =fp~p; ;x oWիWׅWVa *Z7%4?VE@0PNs6P?Mx;L\jґH7צ:j+a'AE@PE@PŽE@88 ;󡶶6|[ FqxL$%R$٤dϥ(.ǩt#}mzvT QE@P%IP󝰃ؿBccc Mibp˃ᄏi"("pa J؉5;W,:,:G*vQwVE@h(a'"Ӹ9.$xMx$5҆m,>FqxL$%R$٤dϥ(.ǩt#}mzvT QE@P%IP CW`i&@Y"("(g %-(aǏOcܰ6˯~a9 ܜ6]"(D@ ;Eq% FȥMx$5҆m,>FqxL$%R$٤dϥ(.ǩt#}mzvT QE@P%IP s>]PE@P. 2Q ;-ct"(%Dd1ǥ$WIo1>YڰG5d"DB>|~6v6e[ۥ#85nM/uVN!"(""= "p>#yt"("PN%촌}Y("i\^p\mי%$ME@PD(a'"Ӹ9.$ڸ/&uMrdi6g ׸~<&)qlRdRs_mmD6Q[ ; ("(@v$("("("(-%촜Й("\(a'"Ӹ9.$xMx$5҆m,>FqxL$%R$٤dϥ(.ǩt#}mzvT QE@P%IPE@PE@PE@PZJi9{3QE@PNDqs\zIqI6Ij㓥 X|d-_H&JH gIogɞK}Q]:SFRGm%$8("(!Jѓ("("("rBg("p#L"j!6m<'K6>[1LH@ۇf:&=lkt&ҍ饎JIpP5DPE@PB'APE@PE@PE@h9(aDPEG@ ;Eq% C$mR$'yO6lcm|0:|c"("&uM&{.5Ev?NMkKj"("(aGO"("("("rPN "(v"2KHڤINR,m#lat׏D2P"E!n>?Mx;L\jґH7צ:j+a'AE@PE@PŽE@PE@PE@PE :E@P %Dd1ǥ$WIo1>YڰG5d"DB>|~6v6e[ۥ#85nM/uVN!"(""= "on("(@+DknYKVN"(v"2KHڤINR,m#lat׏D2P"E!n>?Mx;L\jґH7צ:j+a'AC_79_bڧB("JPN+x]"X@N޽6U*"("p#k.Pι 5G?Sm;uNm=:E@PEPNDqs\zIqfi:&9Ic|al㳅k\?D]zS@IDAT@ }l6l2s/ʶKGqj"H_^ꨭՇA–1J(IX(":PNw]"PŽ*"(@@@ ;-cvj`!4)yBǎC&A.A_ w m2>zyRE@Pv"2K)mR$'yO6lcm|0:|c"("&uM&{.5Ev?NMkK࠶0Cag l ܟZ=\ "(2JعwWצ("\SE@PE@PND{} |& -lZb?5CyU ;vT("pPNDqs\zIqqOMx$5҆m,>FqxL$%R$٤dϥ(.ǩt#}mzvT %~[-c{5("pA! j;u1"LL5\PE@Pk2/ab⬢W#2ǘPr|fo`ַv|@MPEl#L"jJ6Ij㓥 X|d-_H&JH gIogɞK}Q]:SFRGm%$8("(!Jѓ(@%줱PIPE@PZJi{\`~l1Pw7n30Iqo» MjAQ kַvba"(g%Dd1ǥ$WMڤINR,m#lat׏D2P"E!n>?Mx;L\jґH7צ:j+aGػ3 f-{>ҹ{gUupA7|iy]i'"(v.յ)@(a'W_PE@P.$2v!e0N-u'aq,T{?O|;蘟zt7xax^/x _M4 4߯]wwN]EPE@ L"j#&uMrdi6g ׸~<&)qlRdRs_mmD6Q[ ;\Ç<}Hm0_ `ɠ3&Gێô~y1|bkNY2(`l3^,OW^=sK . _VCmkf`Me"󀁋vsYMf E@P%ϻsWӍvN7ڟ"("O(ae֙$ t-,p&p$4Oݥ_Ka(7Ja'zZ2n9RDuM}еCF7vn>ىQAPE v"2K`K6Ij㓥 X|d-_H&JH gIogɞK}Q]:SFRGm%j25w6'i o) w¤>'cZaU=St&f=3^BwefAAa(1WcZ" 6Y(PNlQPE@P.@26LvÔ?!ͥ>8$;]r \V+o2d󖄝ܿ;`ȓ[pM%vR**"PNDqs\zIqli:&9Ic|al㳅k\?D@ }l6l2s/ʶKGqj"H_^ꨭOE7pxtn G,P+;#8u}Bѩ_ ,5$aVƽp^Oo.u_ ;xV>{Q|* yh{n"\(aW]"$C@ ;p(E@PE@0PN3Op;Z &v g ||@R. ߱^/˃b-m\D(okn+?&D뛩_zNͬnZ |NUn[RV\v"0TPE@(a'"Ӹ9.$ڈIo1>YڰG5d"DB>|~6v6e[ۥ#85nM/uVŽ٠)6ݵ0035S}!eƛᙡכ(FZ0D?OCCȣOÕC Zc̯VFqPU&o1!7Xq S~00y}<0iQE"VӄUwhw WG_ZU7 _bNRp.Xw 'گTsm.EPE@PrF@ ;9CvFaz! (#0Nq ͏i~L[8_7_f\{}Cr+?_8LꗚKj{ 'z-L튀"(%Dd1ǥGbJLI?m<'K<]g1qxL$%R$٤dϥ(.ǩt#}mzv f!A|b>l o7^3p،w]qΘ 8rd# #8}d+}N.1f]ܫ (kkv.] G}YW"(yv sv8?cl<ڃ"p裏`Ϟ=yyypwB6 {;{(N^M PNcH2R$:sgo1?ͤ {̈́tG ASC k0")dߋSE@PE B@ ;Eq鑘?ZIo1>Yڰ*>c"("&uM&{.5Ev?NMkKGM+3f uYb; Nj%z>MskD[JaJ!a *uT> ͺӅ0IIrJ("p# xsuig=Kh30<zg&z<+suO6͂[F.Wq;.ì? 0fn=Fv agE.sg@aZ dB(Bi~ Xkp36Nyy9=z`H ر#vmгgf"+/Bq}c>$)Ng tF8PNذBYf2)ow,N# S+Cԯ_kσO7 MnM3S AN{u'WnY7zpE(KIJBJE@Pv"2KLڤINR,m#lat׏D2P"E!n>?Mx;L\jґH7צ:j+an; ?Ȟ?wħI+i!%#4B`t+#")aLJE@P΅Ǻ3yGiSqȮxx\{|ڈ 2janrZ45{RÑ2_O4OxpH uSnѶ#0ӟfFؙ?>f0:uLW"ZիԩSpŸٟ Eee%lݺ5KN m6͟9b韢(a]"A@ ;p΢v͕R_K])ž3fۏM]DYa 8oM?'095[kWE@h(a'"Ӹ9.$xMx$5҆m,>FqxL$#i%n>?Mx;L\jґH7צ:j+aj̕XWbY bȫot6s@g8%!larjBxj3pg/Av.7?^X=G hzK.dIV ;oFtxri 'ڕ#υE@h](au6)uo{H \|1 MpmK aps1]nb_~n&1\)nՙDx+spm*ьI]#E TTAٟ_ IǫwF'tW\ 6Wqa+Ot}Xլ"(Y 07Ð-;=3wȂ䝍7;3a>}f@D@ go;ur<̙3ŏvUP2N%0:f?%nn%dǤnСoҊߘ7 2fݎfo#2PWdj(2M E@PVv"2#1%x¤6Ij㓥 Xp~<&)qlRdRs_mmD6Q[ ;f;wyX0"!#um̅ßBL+BJO 8>s _ ;i@Zح=`v5N0IC ;1M]E@Pӂ=`RxXomk;`J0CBY!H( sճpĐ#75x"}a؂3|D(蘎&5[]O k'Bvx ~-P5wVξu3*l(6dzq V} CgO|h Gd؛v!0'?Id/,, !EMM y@,#~߾}pر@վ} kO=8>(1ݺu A ̵8p.Y:\Akݳg8q" 8e+Np۳gO'Y֏}y<3 )^d_wرc+??v Ǥ=rz@B WĄqqq9K,M9pƳ ƸZG~Kco)>/{vTgc;=/_ߘ1>zE@p#76glŽ㚨ƚzjJSKfӇv}PdFqU)]~wԜ{}7<]NcB8leeC`Byߋg\"((a'"Ӹ9.$xʤMx$5҆m,>FqxL$#i%n>?Mx;L\jґH7צ:j+aOP0C؂) g\3̺kXxZ4i|&'c‹3MÏ,sIڕ }QE"V3䐛RSf˃t6qppAډ #`roU)&˜AgX(j%S`}Oٷn!ʷ #nLBlDB㾝G3;Ao[ TB;fu;al;CհEpp(=,\m e@򂏰!e7ވDD=DV " >ט駴46@nqG֭H2//;mKf' $;6 j|K/E$Sm>gۿ]dyR… VAЁɓc$ D0]{=~„ Mcplnf߸9s,_I +l\mc k$̝;^sDR3'==t& $INAիgI5hw\я~a2cΜ93cxߘMFGP/Js֌%Ϳ/m8j-L1u,q4ه\)K E݁>UO۟ܒr,ms/_3 ;ɾ74Bc%0w}p SR^tZ+"(!J؉4.bK/I86-mR$'yO6lcm|0:|c"("&uM&{.5Ev?NMkK:G/+?=% |L*-vxFd9,up5Li #5mW_D ;MQ"(%}U@L/,'S`seט?~?Gt[d5u&as-ORsVè.im|Q *ϔއNjʌ5dE{ Wio3Spj!Q'MX7 _~Zlj> 5V'wLt}31YK.oWbe#`vUcĀ4J/X>irq48T#9IAT"9.2 /`%X~ጬ7|l+HB /q78.\Ľ=Ēm62uq齔͗g=3c= ߓ㸮L֭[2=s=|@>$9MP"B@ ;`:NI ;DT Kaӫir}i;XwO `EiIJj#.)c׌]~Ώn+̀{ I|6E@P8J؉4.bK/I86-mR$'yO6lcm|0:|c"("&uM&{.5Ev?NMkKjJۿmaJ#|~(O~s_OƘ+ ӦH9]zr]A-9CStטPr|50I0|KJo0G1Tte*i&a'"dDkCPE5 ְ˺$78 NkEGa3)+ 'RI!H5¨̿v-=33Ww!bdF BGU\YI+uf$:aWմ&,.ET{gF9a \!':vRjMLF0c2%Z+ttvq̤,\m$1`f. x|_\\]$mA>d" 9YW8Y+༑1֯_̛mD"?G=>G |B$e'MIrk0 ,xUf61{ f#,2 {yTpo1f4hPp==O$ \3$F,%nfޟ<(T=p߃ʿ ˆeC 8CV"("z%VL"HL3'uMrdi6l<&d$4gIogɞK}Q]:SFRGm%XNm}] 7^:~D榪9f&p}33ѬT4^v%oC&Qò`8dCҦ"(v.=a!L]g%Fę8O6A-# :_iEQHZh5WAԤ'&vP*RWGD8ɮ*xwyoHoX3ΰ*qi7s3a\[ך@ɾmwJ }:R"sWQp"IoA;/d7(YfEI>B_I>}z@^#I\1NW.O"A /i(S*>/s<ȫ]d0$={/}ҜڥgAp4"EQ\ܚ)sI~\c3EIߓlM8IrMsYr^pBO>f.gԆE ;JɎHD99Fu=.G~bXo3$"y:#zAkާ'37(" ;Eq鑘?DZIo1>Yڰ*>c"I#-qlRdRs_mmD6Q[ ;=k^< ~1Ϭ[=aF =#k"(- %촴 E\u9Cn*S]/9\K|ɢH[v>3 S +-6v2gb:;~, tlg럺bl5Is\Z fمirCؙ0(Nz:;I1A|lDI}Q }Ȝ`{,"Fȱc]',>~4'N:x=/.IG$9o,6/k\+0|l٢yc,ǵ} |B+W뽀}roUhMr/ȟѶ1H}1uzr]I~V\WS8<z'ɱyNcvFQEPrG@ ;cv&"wΙDYy7rdxOKeLyS.e,diCPEU"L"j㹒6m<'K6>[1LH@ۇf:&=lkt&ҍ饎JIpPOOH=T>? V8=;Iܛ;ig褽)zs,MG"*Ӫ[v}' v&Yj9s~h;<֙'nܯn: a~N`kcqdBXJc0\5N^ņU<PڕaH! _xUPmmm Y6UL12xMZ'NsB2 JW.Yo r7f AR ۸"Ήy3'.< yvqڭ8V0kır9y;ۄ k\sl. uB5$?+2 XaDXX7|L匢E@%ٙPN Tᵔka#×:쮬ϑ`OaZQ~NEPE5"L"j㱒6m<'K6>[1LH@ۇf:&=lkt&ҍ饎JIpP[m 3|ْt&X0ipvQL(v.=&Av îi4KG  qN(k/ 2x;sOaj0s%VӡFLAA=Pڥ›L_|$qqMNX. À^5?ާkl[>./[Mbi- ?Ak#y_lp nql$cIޓa5xC\N#wk^ tj>fU"(ae%ۇLaG#;`;WU+"Ri\^p\m[F7*.a& 졐yVz_w5d}]kOag8*P"U۰qƌICmWGa̺Cm@W2 ƾ |n֬YQcS ;Zٱū 8ƫJ$Ns%W|p 8: 8>?~Ym>|\̢4rHbJ}_,7>lcpkAsu~I4+g]kl 33J}h(!:SJٺr:/C_ W\90(ps~:SWE@Pֈv"2KJڤINR,m#lat׏D2P"E!n>?Mx;L\jґH7צ:j+a'AE@PE@PŽEEcnmFRGx8> BLX PuhIH}b'~gxum7L]k{]|(ͅ.+L R׸IHvͯ2d.jZ`jk.'*fiǶσ ̕X+\u ^&x4wT)LFun.a?\ qG?‡HAb /t '@qLD[ o%#[ndjpGV 'iϸ 륗^D|~6v6e[ۥ#85nM/uVNsO+"(%ץ{$R(b>mXAOz:Xc19`ܢJ?*L8a9݂1LA&ހ)YfoAGBI+{N,9Midɰ 5 A#L-YfFsU`Tz+2JѵS鯽ӊD CkAx 0WfMWkNvjjj***&"Ѓrv>ozp/}wڤ 3 @$w lHA\?ŕEAszݵ.rlj2=F$@18yiC͇Z/#._|\tEceÒ x҇vr-~̑cqbm nwam^>rǁTsc1$bA3:> ?CEE@%ٙPΙ@UTE@P(a'"Ӹ9.$Io1>YڰG5d$4gIogɞK}Q]:SFRGm%4j"("ЊPN+|]zx&t-峡ߵP`'ÌRXI ;&̞еhj4ƀqs;઼Spz GSYt Hdiطd)baX=u$ж>|uxfYohH'v.w <8Ÿ;vsx,=rA|C]f ҠMYF4?'FN a&3QUD|~=TGСLS\CzzMm\knH ڟ}(|$nRZZ Aw0{Ŋuee%5XlmlǶ6˂Ws^6:cIKu^s5(Fgl@<@` Ep+`t W4)#IOu?]|6$ѹX)l-?)|Z|gs=S|\n{nwA5+pH5]u(1s9m+@<33("PNDqs\z$~R$'yO6lcycH&JH gIogɞK}Q]:SFRGm%$8("(!Jѓx8JoD9+fOd2 6\}X}fAiCL;6OKeI{޴Ȑ_f6ixŃ rx&dFu>:}15lsEYwryOs@ xxN7od!9XQXs]ؠMbE@%ٙPΙ@UTE@P(a'"Ӹ9.$Io1>YڰG5d"DB>|~6v6e[ۥ#85nM/uVN!"(""= @'3Y&&͏}zi;/qUGCخu0otbpa0swE99oL(KO}r*G`D >¸@™7쁩]`G̯q!qMa9Kor*f{ AdӖA&~q2$"ͦ$c1(FFpllƳB0e Xd1N5rܭ)1z$Q[n[syλݠׯwJwO/w.'}:ӆ_"V2/ў?yie_^ny[NYVD1=eFv7PU}O$+W`bXA=|F:]G;|+E t=D'|/w x=輼<}_rcߊI:VPÇX|EϩG}꾷K_(g~^pM:;e_;Ƴi;&:۲"!_&ksyFumb;L=ζs|9~%Ռ>?E71!E?LaLᛏKOohy}贙BJ hGfInwh-6;4ܝܫgyUk~mݱLGZUz[]FѻHa߿$}ϷDO#_#Hoz'_nwyP%V@`q^ss=jժ.-`5x}kŝ.tʾckӽַkEr;XMvg- 2o}PB b݇w~wvjDZRV]wZ@cٮk}$+K{]j쫑4к֜\[<+}WBch7o|8}]r7+.-v8yXq:3vVvyqTOݎvnU   ~81MHkNȷuNǤvQl=bbX.>SdY99buIjFYkmq}Ȣ]Xh@S+ n}Wx˜5W&E;n\V`C̙3+c{L>8jj;v.[vw{^VD4QϞ!dYYau)]nsdO bSdY99buIjFYkmq}Ȣ]Xh@ShOf3柭ysO/f<%*&jISn#~݂֮]>Tg| ة{Nm ӄ9|tNǤvQl=bbX.>SdY99buIjFYkmq}Ȣ]Xh@S+NSחϹ_u?:V/۰NR&`Ǿ-?LjuWV3]`8>쌏A@j;q ة qb0'"o3ʞsַGLlegjq6 P\ :GΗ1W9_(kC1OYtk}qc -   v@@D`2 vU_% .(`ovQH#׾5P9@@&@S ة qb0'"o3ʞsַGLlegjq6 P\ :GΗ1W9_(kC1OYtk}qc Z/otﯮKp=  ;.t&`7Ao*@IDAT?<}{Wa566ޒC`|zzzW^Mnx@W_ @@n#v&$ ŵ'ۻs:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'B˴rR:_6q q1  ;.tA6mZq_6`'D'Z3xk33@@@ @4!aN(E8!1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9j] _ vZ9Zb@@v`v @= v&{/;Ow3h+iff<;|q$ӄ9|[tLleO9#&3N8(.H#V+ٜfק,ѵNZ_-t t RK;IIsL6L;HL$sM`k#5o}ʜP+u%gN#ӄ9|ftNǤvQl=bbX.>SdY99buIjFYkmq}Ȣ]Xh@ %V@@@2}Sk!ةۀ" qb0'"oW3ʞsַGLlegjq6 P\ :GΗ1W9_(kC1OYtk}qcPG/еܤjH"vfdpGnM4{:@/ݼI4iiRO!Wl>bŗܵ4- -h' h)I)yq      5JN v2?y%zŐ}F KϮ]a1:O/L7̳wyyoͳ̳[@@ @4!aN(E8! 1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;b^=EVm^:t(mZ$2Եrm=o޻ֽ״`scYCB$lw˴yNzH;}IZvgyx[:/_BҜIC2|ZXtz:j=gkGWEG S;SVBA@@@@@&;#)=S(]~z/mZ=Q[ʶvV#\.o~7O57o5Zsx-K' sBq- 蜎I<:g}{6\֝}grs|9~%Ռ>?E71![کysu}?vYK6EGm[B 5ը]=ys svN vzhVhBK2tsۨQ@@&3v&ù;%؉=OeZ<_\@#ݑ[;DyQ:hc]<ž/K v)  #ӄ9|{tNǤvQl=bbX.>SdY99buIjFYkmq}Ȣ]؅:p?[ů.gvS_fSr_%.J.|Z?|ykԬ3MJM9Cײ-iN}ז킳x7}%tgh_2œMŝt^:%QaG vt?t?|`i5CWnЦE!P  0 @3oN@@@@@@#xN=GS;bv}2)4w5ϾE#Wьc:|Eo;Bf:2;  !ӄ9|{tNǤvQl=bbX.>SdY99buIjFYkmq}Ȣ]+1!T(ӏe׳.!tzs7q?'rVh~ LiNJ_) X97i#̹n^}# vZs/;fvQf7!SLL`gS_>}1ޏ!>^z-oN=Z?ۃyN9H,fA;b$A@``ljiBœP SRA3ʞsַ=ɞ:l@#uX/cүdsQZb\2F:>;Fxe^mվJVeZ7ciqn:gu%;mGƑ=b`M7Up|[ӍʾsXiGJxt:Z\hu9E\4Kg4p6.i!NJ\@ゝ {N?h~@:R) 荞T.sBZQ   B' sBq- 6蜎I<:g}{6\֝}grs|9~%Ռ>?E71!ءls ­KV#9;=潹>! uHI\Ol|vmfSQ!)xi.qfFĴш%&"@@`J`gJf\$$!p;:<Ͻ/KEo&CnIHv,+ ky.n+{ELI' sBq- 蜎I<:g}{6\֝}grs|9~%Ռ>?E71!Df-/f )BSdY99buIjFYkmq}Ȣ]Рy%֜XmRhpNHM|\_?JsʝwJs y(yFumb;L=ζs|9~%Ռ>?E71!17FjTq>| }Eeky4m}ڛDk|v%)5e_UN|HL8v&)& 湫|caٽB|w?=FZ:v0$i ChEq'SJ~d!'Yѵӏ{ϐ^=M:ܾ6,6UA@@ /v&$ ŵ'[s:&}'frYwZ'{atLlW3ZkPSFZ_\؇`.Krr:omsl8IJ3mzG[7Njl23>;iQ  0q ؙ8 @@@@@@KjhmlXLY>t>4}:sm*v=K?2-?›B>gsK/>F vhԈDEExzEzn9\b&@@`b@4!aN(E8!(1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;c uR]5l[;.҉mj̫֙Wi%OXk_F# (%QJ&3|ZZ'|&@]`.o+. @@@@@@`Zcӏ#Wlvyz%9H/=2bg}y%j|W|cc.7/'(:pA@I' sBq- 霎I<:g}{6\֝}grs|9~%Ռ>?E71! uYChy4fa=bH ӱhA[gi/)5\2;F3o(NtmZ$ j;DCG6Q-I[ӍȾxﰙk仉9?Pݿ[^_#&*a1v@@@@@@&<{O?I,ͳ _K+٧|){Nm'?v<_Tt^?[jZs~Wg':uEHD፴pC#)&8   Pv&$ ŭ0%?{CtI<:g}{ I=f 0Rr:&J6e}~()#nt/cCY#Ã4<2J7oMk&͞)4 k7*qB7GG@@B A@@@@@@<♎S˯е~Xtݸ/h{ ֝Q|~fMyFK3qy`tiԜecloɥ'W/s=[ TO' sBq- 蜎I<:g}{6\֝}grs|9~%Ռ>?E71!ɱP'e4ϡ++O"d7r9*@@@N`NPw@q vҾ/YKϩN/);ˇK7g;tй@.81MHkNȷEtLleO9#&3N8(.H#V+ٜfק,ѵN:1-#t(*:߼kwy2$A@@N`N@T%Z7k9=30Y?Gi~X TI' sBq- 蜎I<:g}{6\֝}grs|9~%Ռ>?E71!ɱP   `+@@@@@@j􃗏]t6dnG(E@@ @4!aN(E8! 1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@"ktI:|nzϻH3_-Z;NM™ v&$ ŵ'۵s:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @FSVS;g  0@4!aN(E8!߮&1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@`v@@ @4!aN(E8!."1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9*Z@@@2`%@'K^&WҲy34}ҚhZI_81MHkNȷGtLleO9#&3N8(.H#V+ٜfק,ѵN;X       P;r v/Y鼹¾ԳcY.XIKB]FH@`ljiBœP\pB]<:cg;({bY1M,ugu,@qF:_NǤ_|5Ÿ>edэu}vr,Td J!K3r6XJOhۖD/k#5o5խ4tDB@@&5v&$ ŵ's:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @FwTb; ة3 qb0'"oo3ʞsַGLlegjq6 P\ :GΗ1W9_(kC1OYtk}qcPG/еܤjH"y ܤ鳛ht^{-yhӂ(FB?:LW^WߺQ, s]^#0|C+(un8޿p^m.M^F9`#v@@@@@@&+wRC ^3q"*,h,a1_|=oZAZ٫/s X  ;NLZm9>yFumb;L=f 0Rr:&J6e}~()#nt/cC#ճ]nVlCҦeM"3L]+gD-:iݛM:]N;5$TQ^ڴ1yul~h(gkI!Z3t㞓Wh9kѧVGK6jg_LbJ=WaT#@`~%@@@@@@`x;9_{ϗm7ۧkw猷U *lói\81MHkNȷwMtLleO9#&3N8(.H#V+ٜfק,ѵB=NF3Y3L*U|MCd+k"Lgsqy`;ROi3nxγ`È-+ Fw &*~ zl"b  uB:  0nNٳe?ޮԼ)+NJsh3ogxN!L4v&$ ŵ'[s:&}'frYwZ'{atLlW3ZkPSFZ_\؇`.ԁ?E71!17W}/.C=CPէ?v=*=zBzs7)d?Rj\q1LOk7LtK;J2d+rsG.ӺK״dwBiO/wќ[$m;K`G:A|UafN;RYv.=6+A6>zijXޚx%V>@@``gs\1@ Ζt끱3VyslhvkOy<׎C@@` qb0'"oo3ʞsַGLlegjq6 P\ :GΗ1W9_(kC1OYtk}qc0zܬBp{]8=t'v u"}ߜDk,\ i:Ӷ%3]{Sy8k{o  ;NLVsNǤvQl=yXNgrs|9~%Ռ>?E71!Df-/f .RώeTv̮;'RjtOO+›t Rs3󅃧]sx  S;S&A@@@@@& v|b}q&,<`nxo| &ӄ9|{tNǤvQl=bbX.>SdY99buIjFYkmq}Ȣ]vC[nRhpNHMz%H6AJMnI ^:@sohϡ'7KwϘAw5FzD W=faGu ڴȣb'vn] Δ͸HIB`܂SK(6 v<s}  p @4!aN(n))ٛtLleO9ÞgdOjq6 P\ :GΗ1W9_(kC1OYtk}qc#Z9+vd͊G'_*M,( }NU\qW!w*Ep;-|hCSLL; IydX8G qb0'"o3ʞsַGLlegjq6 P\ :GΗ1W9_(kC1OYtk}qcszoOǯXT|GOӦGIGi +IԔ}U)Bъ|*=ƿQ?=w+:.JVs_8p0Z_g1@`nn%.@@@@@@ԧ`x] ;NLZZ9>yFumb;L=f 0Rr:&J6e}~()#nt/cCcyY鼵ͱ$+iδo}nfo"Wϛf1NJMq⤏k$4}϶4._|b-d̄vdԣ4-KvZGk:N;gD!aA@`j`gjo\-@m'Nx]+g E' sBq- v蜎I<:g}{6\֝}grs|9~%Ռ>?E71![heqNl[6Vc^μJVbW'GzʹSdY99buIjFYkmq}Ȣ]숅:|,}U0\@$YID͔M{Ooϗ5l@hµ5Um=t v*g߱vZ᠙o ]EMM}g?9Ea0mO¿w-&2Jsۖ7{-L";n~(j2Ϻ7u H͛SC4tbĞC7'9te6-d>yFu:b9ٓZ'{atLlW3ZkPSFZ_\؇`dzPGixdn$~L=S)X<=yB} JDfܳ.Ǩ9 4mmk.5δ؃9LHK_2s_Ub}XT ؙ       0ILky8kOS:!ӄ9|RtNǤvQl=bbX.>SdY99buIjFYkmq}Ȣ]X2H\q%Lܱ'c  2ۘ*+1/v@@@@@@&z edэu}vr,ԉi>G_ <2BoEV ܞhg&/o~/=tt;>zg5ޞ  5L985)G"<מr  v&$ ŵ'{s:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'B:-tUad/ҒS @@D X       N`<~!@ v&$ ŵ'ۥs:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'Bj-zgзzy{ߣw߻~үXTy  S;SfRA@@@@@j;[%@@nv&$ ŵ's:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BE @Fν?v&$ ŵ'Es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'u ҵqfjׄ_:E_]K4Mؼ53p?: z掠VS93@;8H& qb0'"oח3ʞsַGLlegjq6 P\ :GΗ1W9_(kC1OYtk}qcą{`%5o?o[{-˴rRNF=7P- ߥEDzĦBj  P ةZ v׆啴l@Y~H?M?_M-ZP qb0'"o뜎I<:g}{6\֝}grs|9~%Ռ>?E71!I\]ySvNY%Z9k`.2LWN >?.Xg;Oe_7۱8I|'@ ةA@@@@@@6%ϟ .Rώep1ռyv@@`J`ljiBœP\pB]W:cg;({bY1M,ugu,@qF:_NǤ_|5Ÿ>edэu}v;*ءa::g%4-k}#g/xRۮ?% k5N@$v$m| 7ދ?WL9>6Jv|   S;NLZ*9>yFumb;L=f 0Rr:&J6e}~()#nt/cCPYNIrH9ӼۅsT줒B~.D,/>׵ghu=gN@@&v&$ ŵ'es:&}'frYwZ'{atLlW3ZkPSFZ_\؇`G-kn6Ќi&v }fC׸Ǝ>zkt|_ÌiҼoip\j}fk7|ݍ -oޤ6}>W_J/\5b fa@IDAT~560_y^}v>Ayhk Nҙ?YF7Gf75Rv#4pmnonlB~z5Z^>8x㾾U@@ F@@@@@@@PH=:c1֎f[_g/g73ad^|<6ϱ3t=x$>.Yyy?<H#ӄ9|{#tNǤvQl=bbX.>SdY99buIjFYkmq}Ȣ]츅:J?F7.Amg?uoAlD 1ry<_jZ>DGb5)ȵsЭ=Եrm-4t׏>>o0eG+=r˟̧ϘV9ii֎C1S='/RQ^ڴG% }ellz^ӎ%iyb{&zZ;ϖ c\ρ@@v*B@@@@@@\+Kg޲Yݫ=?a?2m;֔yNoiO=I ݿ2wW DY&TA' sBq+LI ]cg;({bY:{0v`gk1aD<یˇ7Ok[q'&@T4A]tұgC٫iBaK)֞3oK;T z ?+O9CԀ qb0'"o뜎I<:g}{6\֝}grs|9~%Ռ>?E71!17hij-}'{/(]}umbrGiLҲ(}ӿKkFzKh&BGvZG[3ΞdB~:iS|\`_%)agm48t?-=FґGW̵@cQ6-DPsk5t<{`\3Glwo  BJ;C`-.}<Ξ_ovK}נöFK@y^gtghYu( z<}ׅT&ӄ9|{tNǤvQl=bbX.>SdY99buIjFYkmq}Ȣ]QG?fD7Kvϙ׍S 6l.e8K+^dD-CFR+ȻR'z'iӏ6F=?9Rflr>z, =B3,-tܶyx6G%DXBg8G fէ+2yݼw|]cz _=o88]Zyu2b^Ѽjvoc߳rwB0@@@*81MH[aJ~:>yFu:b9ٓZ'{atLlW3ZkPSFZ_\؇`G ShnD5rN6v)3|ټi)iȼƉ):ZZ5BI۵찓mp'=ߣM;zKsvؑ"=F̲[YWA ݃ʅ22K{{ͫj#乼R5h)2)q*r vh`MތG>Bk[Nq [. ؙ       PS&Zc/Pm*;pyq@S ӮqUZꟕ `ljiBœP\pBs:&}'frYwZ'{atLlW3ZkPSFZ_\؇`nQ߲m^9J╒e|Xu sD{DO+*PКkKɼVt?z.N?N]Xqn686k:4$)K¢ҹsv9{_^{|MkU'`Gs5Zwo`'y!vҳ:^\ i:?vϠsoFW3=+o  `ljiBœP SR7D3ʞsַ=ɞ:l@#uX/cүdsQZb\2F:>;×hݬ噠c8?EsQ#gii3RR`+?zHR:' ROӻ)•ҜqIƼjcv5ۼ+ (Ů 9MM>s/B׻hQ:ev6_[\Ok']?Nr{;?.=_fM' sBq- v蜎I<:g}{6\֝}grs|9~%Ռ>?E71!ءAZ7Ig/(X^ ht4Q㼦@m5zKRs ڴȪpJHcT_^:Ķ%c9]z~ctt]shK˴c_>y-_Z{ܛ'BfyXq!#2; wgw}\  ةi+)%g*=ڰkE|ysXsjg{V^:X   P v&$ ŵ'ۡs:&}'frYwZ'{atLlW3ZkPSFZ_\؇`LjaV΢}"-oaڬ'kv_M`(>?VPd-h^X";N7,W.)Y/cI\siWm}n7s'LS~o^6f-fmAnvw}  BJ;C`܂}_KO oЎbaCR{-;yAYy|`@5 qb0'”u:&}'fa3tr'N8(.H#V+ٜfק,ѵA-Fqۈdxl_=(-\~G`;%k_3JD}ut6j2;{t:[3u7_]Ҕ葂~z]/:.JVN|\>LnvL%9JK-tkEq7_\z+ cKW?l^A%p=783Cٯ-]!)f  BJ;C`;x y4;簡L#.ةx`@4!aN(E8!1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;v̜L$c=Goof{Cvw:Zݾ-t.Z9DwsyXμnfu[?`l˖gi_Y?EoMO}hSJ#~i`X"jyxC}8?ݶœҜ~?;N?J] m+mi/]Z:?=ٌx|zo3756ׄ]sWzyZ7Q;B*N]_vH%N*)ԁ'p;;f:Gh}zT{ğpϠsϠ?+;   P%v&$ ŵ';s:&}'frYwZ'{atLlW3ZkPSFZ_\؇`gl~y,l۶S.vek\ښm7=M,={f5oԎ)ju].ϵTKW^{'A/n٦4g`]H/=2/לnC6ИUN; vL.t[H]e;#>䈁 @Dqy;fwG/^]>vE]Oa޹AzV~o@@N@4!aN(n))ٕtLleO9ÞgdOjq6 P\ :GΗ1W9_(kC1OYtk}qcP.?Mt=koZůӿk{ЊlЕ]hh4 ڪe,ڲo7}N _}SDfVڷ{'}eTa:qm'8z@}06'iu_iżܶ%cWx}i^zhe=[:/П-ZоӸͼ:-=O>(m["=DhXloӃi9?ϸ-|nB&  P;!       w@>N/i'm̳M>+0>6-9|\g׾̥v!ڴH>06H͛C4tdXyA֪۫  `ljiBœP SR7E3ʞsַ=ɞ:l@#uX/cүdsQZb\2F:>;,Q$#tyei#484L#7ft={vuO,::2L#tMj~i~~ (MHK u_}X-jQ݃7h4z\jLe8 ~j{hjj,k@=@3>~$K3'0sU |V>@@& v&$ ŵ'ەs:&}'frYwZ'{atLlW3ZkPSFZ_\؇`'BA:<7hCt`+Mf[ٖ7s%8Iyp      I;w Y@@`@4!aN(E8!*1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9j-\>nf ?Kݼ8C}FjǹLzL[ #SWCguq)  0I@4!aN(E8!߮1鳝g=1[o&˺:l@#uX/cүdsQZb\2F:>;9jm бGVц.:s+SdY99buIjFYkmq}Ȣ]Xr,| }=z~{-E4O  0 @3o!.@@@@@@LmNv#񬼎4.@j;NLZ 9>yFumb;L=f 0Rr:&J6e}~()#nt/cCc@@@ #Vvj^L@@;NLZ"9>yFumb;L=f 0Rr:&J6e}~()#nt/cCc@@@ #Vvj^L@@;NLZ"9>yFumb;L=f 0Rr:&J6e}~()#nt/cCc@@@ #Vvj^L@@;NLZ"9>yFumb;L=f 0Rr:&J6e}~()#nt/cCc@@@ #Vvj^L@@;NLZ"9>yFumb;L=f 0Rr:&J6e}~()#nt/cCc@@@ #Vvj^L@@;NLZ"9>yFumb;L=f 0Rr:&J6e}~()#nt/cCc@@@ #Vvj^L@@;NLZ"9>yFumb;L=f 0Rr:&J6e}~()#nt/cCc@@@ #Vvj^L@@;NLZ"9>yFumb;L=f 0Rr:&J6e}~()#nt/cCc@@@ #Vvj^L@@;NLZ"9>yFumb;L=f 0Rr:&J6e}~()#nt/U]JDt^$p@nr, Iњ3FL/LDs $BkʹI*{_ NN p]p+^xtc91' 'wZ{g[c73g/={m;| vR\B$@$@$`W T v\pOHH&? v'mϗc4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔON ]HHH"J       !@N '@9-"sv5l;'}6\;LZgѶ@ #:WΎ~9[+iZi|_L'iƮu)ؑjv{ CXvzمHH  vI! T- vpHH&! v'mϗWc4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔO8AcѶ%=3$@$@$@a0K$@$@$@$@$@$@$ \|/:}ǖ|+W-lăʏ z.7(T~+~wb-҉nID7v+nǔO8AsůuSXXf!.$@$@$p`~?      Dvtm-8Z}9>O?{Kr^) z?ѳi'GfqA̟f Wcu-^׶@h4HHH2h1O"/Oc4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔON ]HHH"J       !V3z0f.W}q)1-;܏?"Tʛcs (͍m>lo-6^5#m+hy}naϊzcyX7*܍[].{8Xcx@fIHH`P4>a/np|VHa$#T1/g|%Y+m$صS>; 5Qf7ɯFsb !PFø{s#37}@n^y?486>ɿ$@$@$n`ݤ&      R;8|&Fc;s{V/7y8P(kǪ8V5N+E^~Us4^5Z#cʽ׹􊆂=z/ Jm,;~;׏ ƪD3rZͩV/_zL+boAϩ5ΚMGMϡmћxfݼx ߌ}K $D~ڈG+i簢P rԵ ˶*-^K9tgi"f]@7*$@$@;ZLRy :;fNӚ}B~P쓴m%@сF1Bu3rWҚvO*э]1S#NPvKva}E[GaZ qqˀ=i Cw;HHHHHH\v6i:.e C<2^1?E~v,6VkE?9ތBȲqozq":}a/np|VHa$#T1/g|%Y+m$صS>;?'DӼ }Ǿ 0r4`Mob_OB,/*[1vrcpM5[  TY> @D `lBV90iWQfD~xtXSf|-(jއs_+8D.b1/?O#c̹5W|~gVD]hه羂Y+ڪ%8Tt-o ^ 8gܗ?'^xNX3:8q~aUL+ QC+PS P4>a/np| 3}ei>!I_n!M(>֙}(:H:FΕc_VJZV.SIZ%k]q;| v 2}%B[;pmx?ps'n_hq?a$  #@N %P`'O~k !*L1h.:z<įeK{0Ǫ,yy>k3h/E!4 ̹8uq(|`,-ry C{ƣGbم1[:ɿ*Urk_K&^vDH;ZL˳o+;Mk vNr mBwm%@сF1Bu3rWҚvO*э]1S#N6^wě9WzIO!~H%  #@N՝ &P`g]gbK&.BR/+uQ$9Br J)MbҌXg)XW+Լ=(''Ve= V*?v(Q WnIa7VX# ~Va/G'>uJ;  rh1O"/;gL_iZOȶsҗ[HlEϤufm+0s闳U֬T}VnZW܎)qLAMoɶf@U?^ғŌ- Aa/np|3}ei>!I_n!M(>֙}(:H:FΕc_VJZV.SIZ%k]q;| v ;~x'c)؉Y" Ps/- &F Eq,Q(7:<|P.iSX`]AbfS8TH%sѯ]Njbk Wg}R:+ZF yvnX|8T+0?~UO>8}UUlIH;ZLm+;Mk vNr mBwm%@сF1Bu3rWҚvO*э]1S#NPnمHH` `g |: @H#:$欉d48ƫZŊGCPNuP=0|]iÂMrOϵ#D\'^)(w`vFpx,lv5ӺO Pt־3r֣ hi=;Gp,dt'V:ZXM$ xP4>a/np|VHa$#T1/g|%Y+m$صS>;?)I]HH0 v硓 T4dnQqŘӅ49>RY"ViHYMxMQkDn~Z0Ly2wjl*d7';*Gѵv& ó=hU v V=xfXj_#Dohi*# v! Bh1OKaJY+;Mk vNrB9O:ERIչrv*_IkJb>ID7v+nǔO8Av vRe  )L)|y$@$@$@$@$@$@UG `GMXWNJl-~|:D;×cmQLqϐ)U1X7_5?0>|KVc? . $/>UZ;`K7Xs.Ms'.kWYiq!in ;숹PŽؿ1{uZ;pV47i[MS% '@9$_^v3}ei>!I_nr?}[(gIZgѶ@ #:WΎ~9[+iZi|_L'iƮu)'(NNB$@$0 P3O>HHHHHHIrթ^aW[hA2qvl9>]n_"iގS?`'w1|2u%[qF(]kTXG݃AYD\G۱k5|+7EÙ͊'H!HzA{sh}t.j+'5ۢWw0;ȋ~5_!u~cvW7q5HHP4>a/np|VHa$#T1/g|%Y+m$صS>;? v,^GPn fD[Wo IIo]HHH%@;_F$@$@$@$@$@$@A;rԑ+hZ`se|mqFR Xo MZіZQZLnls8X}@B-qrkM<7߂c'Xъsbѫ ;׵ObX T7o!-WDQSE$@M- s|q[`+;Mk vNr mBwm%@сF1Bu3rWҚvO*э]1S#NP=`:~L埻v\cw}!}u `l1&*(͖ EHH;Uu:3$@$@$@$@$@$@SHv!n+[FKa|cװnRߔ{z Gīf_*>>[ظn'vĊ@;qicIܵ#bn~ QC$$r8.B家ÝO^%3-7 M$U{dnal] Q[f3.qpt@pW/ol__/mwyB3^\- @5`Gi|_|y3}ei>!I_n!M(>֙}(:H:FΕc_VJZV.SIZ%k]q;| v ǮꚛE|.j?03/-חy  (ؙ'@$@$@$@$@$@$0imND>;:~[x뮘,t40>nb1"kGńLL_0onC9yB~qM`VC}C>s"/*b+,_x;gL_iZOȶsҗ[HlEϤufm+0s闳U֬T}VnZW܎)J vnw35i/?~x"?Ç[W5مHHG@$@$@$@$@$@$0yPS2{keZ׭~ǰoa*= I- s|q[%n;gL_iZOȶsҗ[HlEϤufm+0s闳U֬T}VnZW܎)J v/Ǣmѥ*/{/bU#_o. LQL&     JTi)hqhnn?ևO)eHH P4>a/np|VHa$#T1/g|%Y+m$صSø먙;nLqѲ LeLc'     6TGMS87/b譇D䳿59̯1$@$P(b0E8>_;gL_iZOȶsҗ[HlEϤufm+0s闳U֬T}VnZW܎)J vR\B$@$@$``ǀAHHHHHHe'_O$@$0P4>a/np|3}ei>!I_n!M(>֙}(:H:FΕc_VJZV.SIZ%k]q;| vR\B$@$@$`W T v\pOHH&? v'mϗc4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔON ]HHH"J       !@N '@9-"sv5l;'}6\;LZgѶ@ #:WΎ~9[+iZi|_L'iƮu)Iq @D^ $@$@$@$@$@$@$P=(ةs=! (b0E8>_^DvΎӴfm/&zGI>VHa$#T1/g|%Y+m$صS>;).Tv!  P+HHHHHH;s.'$@$@;ZLˋ1WvۄrQ3iGJ)c\9;fl5k}1UUcʧ`'Ņ.$@$@$@ vx% @`zHH``Gi|_|y9;fNӚ}BBbP.}&3h[ Pt tP+gLf]/ꓴJtc׺vL줸PمHHH "@      TϹ L~h1O"/@IDAT/";gL_iZOȶsҗ[HlEϤufm+0s闳U֬T}VnZW܎)* D(@$@$@$@$@$@$@C9  O- s|q[Ed+;Mk vNr mBwm%@сF1Bu3rWҚvO*э]1SBe   ;HHHHHHHzPS={B$@$0 P4>a/np|3}ei>!I_n!M(>֙}(:H:FΕc_VJZV.SIZ%k]q;| vR\B$@$@$`W T v\pOHH&? v'mϗc4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔOPC8Wx=š0-{v~37 T vdpWHHHHHH< v%@$@$@  v'mϗg1WvۄrQ3iGJ)c\9;fl5k}1UUcʧ`G˱h[omɍ=>Oc$ {M{M @r"y.^8uwx?-,V ,[؈0z 7=πr5ϨhGqoayK{+   $@9-t9;fNӚ}BBbP.}&3h[ Pt tP+gLf]/ꓴJtc׺vLxPdpae*yƸ$@$P](ة!     ޮ`Øz[b> dfˢlG6aqmر8Qg);xuMeKTg/c`bݏ Kh1O"/Oc4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔO8AsůuSXX_?ŵ^5]WI }LqIHHHHH&#عҵK`Wr|8CZphϥލ9kغ2N8 29mSg} ]  w;ZL3j+;Mk vNr mBwm%@сF1Bu3rWҚvO*э]1SBe   ;HHHHHHHz^9Klw_o\GCO;ꁖcs L3jJKX;sTʥwU򷈑;qB*ofK$@$@K- s|q[ sv5l;'}6\;LZgѶ@ #:WΎ~9[+iZi|_L'iƮu))\9 ⮸0:. |&ܭAMm-?bq[^|-5P/Q\{e WȠ޸oaĸܒ c*ƺLy7O=?:g:0ŭZ>+Mo޺   ILI|ryh$@$@$@$@$@$@tQ^>zw&j ~`5s28ʹNjY9L=}v?Mrn^ʎ7! s<|c}o oO}  P4>a/np|83}ei>!I_n!M(>֙}(:H:FΕc_VJZV.SIZ%k]q;| v 27&L)> kaˡv\1Vd{wɧZsj fJ{fyg͋\Уo׾%7D9\:'XtѨK3:/C=BX]l*Zϡc?cNhFV:2=  ;⾒ Lvi;Q;o}Us4j9V-:^7+fMKNn6<­;YY[Mb>1B5e{3/eǁ6b_[@o/{X@$@ v'ť0%+;Mk vNrB9O:ERIչrv*_IkJb>ID7v+nǔO8A.'inWzvmuM(<0.~ܒ+P7Wu)cfqk`ҠE?܂zn+#܇<Eh?,vלVm9x_4Z&#f͙\so/˝$@$p`Gi|_|yU9;fNӚ}BBbP.}&3h[ Pt tP+gLf]/ꓴJtc׺vLdh/$n-xȈY8;f|v@l= 2Wv᫏/Ĵ~)V}de 1WUKpuchˈ_gEoDz]œer5y<*L+招(~o;X~ #@;ID7v+nǔO8AvS9VFk{Ph'6`]A#-wj4ޖb)W`,-܄1q~*PYPxpKUlnԠΟQO㱥s ascĚ"TEX&uAqTJ/(% Euy$@$@$@$@$@$@Mr9<~Zm>;\y#ǜ7:WE ;Yj]AUͭGCȳUt=ϋ2/?;ԏ ?a< }kb^va^?~}ؽMȀ8߮3[ rh1O"/;gL_iZOȶsҗ[HlEϤufm+0s闳U֬T}VnZW܎)qh%Z^xgRIz3LCC}Ǯs;EK7Nn;}Eܤ,)\aG<̐~fG|w:lX܊DۧYy7 - Z(ؙZGK$@$@$@$@$@$P*쌢kLl%F)Fm,9D1W\˼ckÅ <|vJL`UxLra/.)I]gL_iZOȶsҗOʙ}֙}(:H:FΕc_VJZV.SIZ%k]q;| v 2ǢWȥg0kNѡn]d}u-5jv;1vTɉq>WQ{ii㿾_hnƢ %AcO)<~v+Qh*z(؉" Euy$@$@$@$@$@$@Mrx-mOoI,.}r4(i+G'ļu |vB!9E{l\j[bHx׳osut=>`O| #ŸW~*޸:^؏!|{ t(b0E8>_^3vΎӴfm/&zGI>VHa$#T1/g|%Y+m$صS>;?ڍKUp74.{]4]Yo9Trsyރśxc7nZ: WFw7O>/IH&+ v&q ܏v3KWQǟX>(Wr[Z0=X~f"<;pV1^k?8aZ16zmV9N7>uWVη01G$@5 v'mϗׇc4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔO8ANJpJ znW[wnoau ".]/8|BWlؒ{!1_z w>QUU \ؒ LfLc#      ~sD+hNBSsZGè[Md2 D|T;FH=}]Ja/np|P3}ei>!I_n!M(>֙}(:H:FΕc_VJZV.SIZ%k]q;| v Wp,o*(Pύ _¹Xfnﳭ vf BF(vnܨeP/> "ו4HH&1 v&塑 wvFpi̹cr1l]8oI<#VV))S t<1y,K,1]sn=:Mη;0C$@$p`Gi|_|yq9;fNӚ}BBbP.}&3h[ Pt tP+gLf]/ꓴJtc׺vLTp管v\%.fQYTݷ QK,317 ذG|bsǷi+cIAwgwcAv摭zDµ& (ؙgE$@$@$@$@$@$p?H%z~V- _:J:ks w&^565wǰxcҹP١~b? @kQ`};=+t.28X} ˷cqBx5;qa}:[Vͫ!η`C$@;ZLk1WvۄrQ3iGJ)c\9;fl5k}1UUcʧ`GG{v>صM[ؚ}-Ue|/aBfEqCP,3q.,X~v+lC죟{9(JvD697  N~y|$@$@$@$@$@$@1I:<t*~S-:3*O=ێ-^8vk~+o+΁;h˿XfmZ8u.OC.g/üӑx_v> Wk;`b5oA{sh}t.j+'5ۢ5ΘηO d(b0E8>_^1vΎӴfm/&zGI>VHa$#T1/g|%Y+m$صS>;? vQoq?KxwʢkmmgL@¸6Yue860'2ћ}pZR\9(޳ղ {tXtIH&% v&iA ܧR v-;/m8Vzώ7Nf:0WɑXeʎ.gdp߉WfEs߅~]k"̉x )иЁr]sA$@ v'mϗc4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔO8AAJEr @ws{v}q4KynU; _c Mm$i=;G(}nN @}p$@$@$@$@$@$@SI#'orLG^=8!͎ C#y?G܏b_yB>i{P_qo& {H- s|q[ٳsv5l;'}6\;LZgѶ@ #:WΎ~9[+iZi|_L'iƮu):fo8UNx33mH}Iﻌݳ؝è[p-}8ٶhFE++[]C$@$`̯       &JXb g(b0E8>_^vΎӴfm/&zGI>VHa$#T1/g|%Y+m$صSgk6.V2|wJ_A庠 :Kn]߱[2-VCu?cyHeci}TO  )E)uy$@$@$@$@$@$@UN*=Ao" G- s|q[ sv5l;'}6\;LZgѶ@ #:WΎ~9[+iZi|_L'iƮu`r,]g V5گJq%O.*:Ut<ǴX>Qa  IHIxRyH$@$@$@$@$@$@- vqz H`Gi|_|yj3}ei>!I_n!M(>֙}(:H:FΕc_VJZV.SIZ%k]q;)-gqQ3}7~"?{ E 0[h|7w>8Ms! (I]HHHHHHH`a9>Q$9 T v'mϗ1WvۄrQ3iGJ)c\9;fl5k}1UUcʟ҂( (1`$      w; ד L)h1O"/';gL_iZOȶsҗ[HlEϤufm+0s闳U֬T}VnZW܎)* D(@$@$@$@$@$@$@C9  O- s|q[Ed+;Mk vNr mBwm%@сF1Bu3rWҚvO*э]1SBe   ;HHHHHHHzPS={B$@$0 P4>a/np|3}ei>!I_n!M(>֙}(:H:FΕc_VJZV.SIZ%k]q;| vR\B$@$@$`W T v\pOHH&? v'mϗc4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔON ]HHH"J       !@N '@9-"sv5l;'}6\;LZgѶ@ #:WΎ~9[+iZi|_L'iƮu)Iq @D^ $@$@$@$@$@$@$P=(ةs=! (b0E8>_^DvΎӴfm/&zGI>VHa$#T1/g|%Y+m$صS>;).Tv!  P+HHHHHH;s.'$@$@;ZLˋ1WvۄrQ3iGJ)c\9;fl5k}1UUcʧ`'Ņ.$@$@$@ vx% @`zHH``Gi|_|y9;fNӚ}BBbP.}&3h[ Pt tP+gLf]/ꓴJtc׺vL줸PمHHH "@      TϹ L~h1O"//";gL_iZOȶsҗ[HlEϤufm+0s闳U֬T}VnZW܎)* D(@$@$@$@$@$@$@C9  O- s|q[Ed+;Mk vNr mBwm%@сF1Bu3rWҚvO*э]1S#/N^{χf*4LKqVCr@$@$;0HHHHHH#%$@$@$P @9-c4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔO8AcѶ%=3~,qq T&@T>ؒbxyy ?L0^z n|SW(O(ow~w5Ψx_ T@- s|q[ٰsv5l;'}6\;LZgѶ@ #:WΎ~9[+iZi|_L'iƮu)'Z!yp6Ž).wd9w$HH ) vb {oWsa[-0{aeѵ[z7,~Ď+h[k= !  &@9-9;fNӚ}BBbP.}&3h[ Pt tP+gLf]/ꓴJtc׺vL:oO?{Kr^cU 쮙ŪL&A[`VĂaozwGK?qy@6flM  ;ZL˓b+;Mk vNr mBwm%@сF1Bu3rWҚvO*э]1SBe   ;HHHHHHHz^9Kuw_o\ Sǟ~ivF-p?ۏu }u%xTeEmn M[>ݸut#؍"P󾋸gI$@$@;ZL˓g+;Mk vNr mBwm%@сF1Bu3rWҚvO*э]1SSPs]qk1a+:7:ќaFf;ѡkx0ޭA5n!rʫ]Ԡa1K)Oy!]^8ևj9F<"ȎͻwY _ixMTS[Ə.sӕ^͟DYS;]KihSm="z}3Lo3sKn L< v&)G$       vFqxLl덾uߙR]s~b.}֛b~b~< k(b0E8>_[;gL_iZOȶsҗ[HlEϤufm+0s闳U֬T}VnZW܎)qQ==7&ރ5?6#P`ޚF"27w^ķۖZ?stOlZD֌΋ж4; G~jūkh6G;paB;еPoi\zq?FCISq r z=(a<-9YAsh[&Y7/~C_6DA$@$pPsr\      @Nq[}YX5稱jcFǬ{ ]^+9X Nx ^y#4|v?fׂbܥ80 Ks#8F5^r+|+͙]XEJsXϘS,]0~DHH`" P4>a/.)Iˮc4'd9Mo >I>VHa$#T1/g|%Y+m$صS>;7i4nR\˾sgEOZ,"Ls 5D7biѶҢg.D6*~G4;A5Y͈KfpKx̸?6;Έn%2{ǵh낶/vQ$j?$@$@o;o ; H#ڀ-\ne*z}ϾهyD?nA*~u >EF=|\8wjl*ě_YSG`(oo4nXܜsN̙זΙooe!.ZEyyk|9(9' i L0 v'mϗg1WvۄrQ3iGJ)c\9;fl5k}1UUcʧ`G G{wZ];U,lkk U&+/7&Y\95,Y_TԷtcN9}hއ} ԅ9˶Dõ'"ы8r8ڙN|뿏3+lѯ>6+74w7a偢:}0NY רEǙU 7bÃ񥦘)R2_v%8}Urʏ7j]2"<1_V-ѫMr򫸑 Pg R v×M/[w(ˢWpbŕw2q7AoD'갮Ti|i%k/XMXӌ/`7sŇU[/Fp[_sCWqtᔬb$m8=BxХej쌿! $@9-9;fNӚ}BBbP.}&3h[ Pt tP+gLf]/ꓴJtc׺vLdh[9KvhB㟇kxfFKUl~ثΟQO㱥s aqNfNQ;\޹#EOabH[7o=o Ex_pM^NOΊՅXdw\X [ -)d]ؒ ;;HHHHHH*%P`g]gbK&1{Su)ˈo>9(減9wfY\+x }Ra/np|VHa$#T1/g|%Y+m$صS>;BtbXL9 VÑ/rw.>ii㿾_hnƢ %u}Y b!-ޙk,G5^D.=Yvfu+rtc¶^ѫZZUΠ^f5ߵj%#c ^!&Iw՞% ;b- [ vXh?\eX"V?Y\Q~|{h= f;=vM<&^٥:->-KNkp"v>_X]LJwXe^Me߳9.8?7nU{ϵUwV ֢$lID7v+nǔO8AbXL-ؑ85lr䇾yޣp}[.g{Y<0suaXs@_3EX%^R㛏x㘪,{6ɕS)؉" w; ד A `'4 Xj=X>(55U [ptc3(eQQryL<`j3@,^Յ>jr/nj}zx44g~}s$@$@ v'mϗ1WvۄrQ3iGJ)c\9;fl5k}1UUcʧ`GG{ vԿo\K/sr#ˣ\aƺLXdq X| 7:ZC񒟙]gWx_]7$(vlIHPSMgB$@$@$@$@$@$0  ~s"H$1[ŊGՊrwfDk'ۏu ̾~V~4>9gMADMID7v+nǔO8Ajkʏzf BFhi\a|m^K.(.)D}m)|ykU^n~0 '7q],k:wY7brxx_]7$쌃 T v$pHHHHHHHH `GLkQ 79>%oăgbK\ڿʢAN7: E s6Xeृ[a~8O[y03$@$@ v'mϗ'1WvۄrQ3iGJ)c\9;fl5k}1UUcʧ`GG{vDZaOػ_oc+fuc>sPq|obFsV.ڤ_e v&)Naq#qKHFaqW:B8t ]VuCBN$@$P](ة!     R vr`׏\ǂ×cn|Hgn]d\}a+o>O-_Fګ 66!mގwb"yy.̗O!q= ;tzX(z9>|v7fh#[OHJlzȟĚy$@ v'mϗ׃c4'd9-$ g:ERIչrv*_IkJb>ID7v+nǔO8AɯXLbWˍm؅6{2q.,X~vF"w[<7 r>Dd]ˑ.-TXUZy\}oLD|MWD I$@O?GC      C `GLHN"yW[hazTz[dSjױn<ID7v+nǔO8A􂝮uE;(7m!؉d6Yojvl` Od BsEjIW :;ʖm?*cfx֬S>B\ˡWmS4)V҃  'NIDAT(ع'X9( "Z#m f-)͝Ņ,N<;V3%xԘxZDg(G3`#V-V.%8oaxix|术x *^3n=ķe:> % v\<# j$@9-9;fNӚ}BBbP.}&3hzY@IDAT}Ty;!Idž%qD51}H3(|H @0DZ{!7#dqǠ"mh.(Iݒf:w޻v~}Nr~)rP+'c/e|9#նL}xsܲ1ߢKzMj!~6z~yz쳣iԨQ%۶7,onH+&M {pZ"Ѣ9Sԟ;j'-Ƕ%v,y;u\Bl&]u5-H>JSﴙ~2葉.:}s[r6KO/?Js27M"uԸlL:ӗ>Mm"lZ4xW\1/ӯe翬'͖;CqZb{'M.Pn.*e6nj5ؿ?턷|'޳> kG?}3)xi7[i =WhD=, Z^2Q6A?N:cm#@F:WNƸ_6rF^mшnd+.cƇ`h߆ӼuGԩjR+lWE+Ţ0yqpTo'hKy?k5k7[0A?o81@sc'% 0PAڽ-4|JPzyrλN,MVoJ݇JEp@@*;VLRٖ9㾱ӌ'd˜ۄrIw[{m(6ˆ#TK&_k}1S3эueCZshffR?J|G..hj sƺ;e>MYJdh/ѬݟҮdH OP]9@@ @Sg      0@Sg;{fgVї̢KvM.=m2'c7vlӾBbP.N^cxRs\9~)y]/fcF#H vzt굷f=4a|).Jlh;D{W{oAw7>yK(To   p] @s]c       PD"$KUj|{ezC~s4O>KŕwpD  0P رb0"Of=![洯&K:cm#@F:WNƸ_6rF^mшnd+.c҂(Z@@@v       p @sOv  0@c4>a/.E8>__O2'c7vlӾBbP.N^cxRs\9~)y]/fcF#*Z@@@J!N  @`NJi|_\p|dNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;).T$       C98'9|}ɜqiFeNz mB;y=6Hasdl/gvFֺ2f|vR\hH@+@@@@@@*;s.p$  O+ s|q)"9㾱ӌ'd˜ۄrIw[{m(6ˆ#TK&_k}1S3эue줸P   `WTv*\H@@;VLRE$s2}cyOȖ9-$ 5XPl ;GΕ1Mjb>f4Y˘!Iq@@@ !TιT?v'ťHdN3-s[Hl%klﱶ@ #vP+'c/e|9#նL}hD71CBE @B\       P9 ةs#~X1OKf=![洯&K:cm#@F:WNƸ_6rF^mшnd+.cƇ`'Ņ;@@@@@@@r@S9G  P رb0"/"1;{Bi_o!M(t'uFb)9Buqm匼V.31ZW\ƌN -    vp%@`r@@ @c4>a/.E8>__D2'c7vlӾBbP.N^cxRs\9~)y]/fcF#*Z@@@J!N  @`NJi|_\p|dNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;BͶӾ=~͛?Lq@@ vI[XTT!v'ťFdN3-s[Hl%klﱶ@ #vP+'c/e|9#նL}hD71CN-3i#Ki{+$   &N       p- `GW%#>F=h?ҭ7{^KRrĈOд?ɟͥ#6}w?z= y;E@@ رb0"˜qiFeNz mB;y=6Hasdl/gvFֺ2f|v :e켒d7'&|  C;CT.=&]~oRf5ϞFϷ9r}9LtērLd'9|}dNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;7;H]h4J@@`(`g(ugJ%p5Ӵ/Dk6ΤߺzӦC6a b&ڸ}!5_ MK2|%̮c4 J~vKY.u7iq9sgɐ!9|}dNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;).T$       C `5ΪG^2&qShQ4+/?BFkF ӅKws'i҂G=@N?߯eն1jX)i;u踣wB(;VLR9㾱ӌ'd˜ۄrIw[{m(6ˆ#TK&_k}1S3эue/uvtQbtHA?5?{:~}jjjhDx0bUYs'שC{tOV}R'{e_~v:C51M߿ ,v'f!NE[fG=N=j0ғ7MDvDsV9c4ax)~z\-m?'@<Zow?]߿W: őqWieP͈z'y^;AucJܣ-t|00/vO=>c@E`NJi|_\p|>2'c7vlӾBbP.N^cxRs\9~)y]/fcF#um[hxC-ݴb2@앜c;N3&w5֣yz/I)zM>{]6mS;p }js/&hf3]l]G HO       M `'wv7NX"jª9NƸo4# 2}m=6Hasdl/gvFֺ2f|v ʪ:-չD.Ӕf-P&WU77'(hc=ԨD8 "__~GNUB``gab*Ji;mS5?>^eߡձ1l]M&wzNZU:ur䛔 HAJސ~D.QlV5ws"v OEk}=ZէViV+@礹SWhD/g<>v4N+z@@@`@3P1%$JC vj"j&_&hTգ̢gS]MK Ve魑^CM}_^;+){͞TLI>JM@@; Ӄ@Ҿ>MhRn%a]f+<;թTI&:x~_W vǗT7m>VZ:iChZ_M{/f4Y˘!Q'haŽz>D#}ruݧZVsZVS }KxR+<ҷ  א;6v      %/Q+T+Qgӹֵ4hYӨmu>ڪmO4c%XTLF'hL"2:ueo3NV~>3~x.=Uϒ|= iloPX釋nq?zN/}}j&,  Pq رb0"Ϲf=![洯&K:cm#@F:WNƸ_6rF^mшnd+.cƇ`G a97sWB!iZD\F2>WWYhzV^Þ[ -Y8X   0 L@@@@@@#P`4e~^uXlڶY=Zˈuh 4m W:}V_F=k3]8BN~L4nI?q+|?[i=~Gj{L?{5t-z]3wpeza05#@`NJi|_\ Sb.dN3-sכ>N:cm#@F:WNƸ_6rF^mшnd+.cƇ`G‡-˻thhMEԾW}6eF_(?}v0@@@` @3%FӶm>5,ׂF:z0Mb\WuvҭުjjG3iA>sIFɛ]yoϛ\y>_R8`tގC`;GZQ]QsYTv'ť-s2}cyOȖ9-$ 5XPl ;GΕ1Mjb>f4Y˘!Q'j;Q3WѿmMZVS/Y';F;79T/4}ѸyP #E[`Y v'㣦E,ǩuT>k/=F$ǷVL.Z')8@ c ߇O#-3G#n3S_CnM[Nuj.)SWh^+,`Tv'ť.s2}cyOȖ9-$ 5XPl ;GΕ1Mjb>f4Y˘!Q'Xe vӟouGttgfbOv8/  7vnni;\laH'as v k|%|k+oWq}G/5);\j็?-Pmh4n;>[a'۶ݣf-m3V/ ZNlI’}BS  @@F#΍vp      L `G9feaִ~ql /Z*hqmϬSWҁ @s_*EZN[tӨYٞ;نѴ:x{GD4juԾ}_`%iZ(y0X.}yr͞4B2  F+ s|q)˜qiFeNz mB;y=6Hasdl/gvFֺ2f|v *|hO+F'|fݱfv=Ɂ V'7+H R'; 8܈ ع@@@@@@Z J'zth?~)}~#-EWHkyj>g?YO5 h7VMGQKNOb+肽zD9\mhđNG =3g=ݧ5jܬVYQX&|>`&C@O5=oӉAVE7qFZ͸ @*;VLRE s2}cyOȖ9-$ 5XPl ;GΕ1Mjb>f4Y˘!Q'=`G@gq快$-쳰NQ[Z8/W4 \ ._      H-;6m/ 6O~zN˵qmsǶvJ&"Vߚ-[0}}>{5W&f5[7ϖ$}8^S@@r@c4>a/.E8>_l1;{Bi_o!M(t'uFb)9Buqm匼V.31ZW\ƌ:Ai;jұߤKiFZQZ_XC4wzNZ/ LZBY-!5@vmLgJ~   p @s c       I;zv'iVr53_9ju}R+ͬL7{ϬMPI[揦ZQZ9:m X5{腧(9|?=~[w.*W1_[Տq~gn۷V[Y]ZU߮K͡yhVٜIv>DݹWt9%ȧh9_&+ָE4uDX1OKק]dN3-s[Hl%klﱶ@ #vP+'c/e|9#նL}hD71CNPC;Ѯ3Whn9z ^==T 4v$Kٮ.zGpȑ4΢D@@n$Hg       PZ7I|~}2]Q7iՏIhoϽNj.ӥnaKvSO]uut4>J]^:H|ro+^qHLvQ6K#'c4sԌhV .@E`NJi|_\p|>2'c7vlӾBbP.N^cxRs\9~)y]/fcF#}>OQI=7w;(Zے3qK֣@@;Ut2V@@@@@@nx%عAT- {ת]jj?[i!B8p8 hX1OK_dN3-s[Hl%klﱶ@ #vP+'c/e|9#նL}hD71iY YtQ:ׯ/麠s9_-bXW1zT)@S'4%/}ZK4>z筴kjOo{ O{IcX1OK'[dN3-s[Hl%klﱶ@ #vP+'c/e|9#նL}hD71i-3#ɥ/o9cR\h!D!tVA@'=b7|3r-Gl   0@Sgv?6n ,YE-MY  `@c4>a/.E8>_9㾱ӌ'd˜ۄrIw[{m(6ˆ#TK&_k}1S3эueCZӛSE5# ϗMq=@@;UA@}]|2;m-]#7tZswAwuW޾F݀u'u?%^mk.u4gO,=@@Z`NJi|_\p|>2'c7vlӾBbP.N^cxRs\9~)y]/fcF#H vR\h` a0`T=-~;/1b9n7|.]T}4|Fz8VHC4  @c4>a/.E8>_1;{Bi_o!M(t'uFb)9Buqm匼V.31ZW\ƌN -    vp% %Z;uuuя~t(μ`G w@`0@%`NJi|_\p|>2'c7vlӾBbP.N^cxRs\9~)y]/fcF#*Z@@@J*c`']v.\ZGeaj&N5]7J#9|}eNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;).T$   0T\xzzzhҤIC-W;).T$   0h믿Na/.E8>_L1;{Bi_o!M(t'uFb)9Buqm匼V.31ZW\ƌN -    vp% Zs>}PxU;F&L`j0&v رb0"Of=![洯&K:cm#@F:WNƸ_6rF^mшnd+.cƇ`'Ņ;@@-W1cFżl'{tx>RWO bDZ9CT[f{kGTI{G~HA#9|}dNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;).T$   0T`'q+#NV=J&tOnOSVrt0M, ;UyZ@@v   0 رb0”:㾱ӌ'd˜ӷr'XPl ;GΕ1Mjb>f4Y˘!_9uT).m@u`:+@1J޷&[W|pom~ٶma*D-/dv1B@@ndg  p`NJi|_\ SbKBf=![洯7}-=uFb)9Buqm匼V.31ZW\ƌ:Aٶm4ayrVLv15jTś#P ԰yvNi4m;:LYŢvH,vB-9{RݮY8͝>{f/Oo\~FЃsS]y'E@@;VLR9㾱ӌ'd˜ۄrIw[{m(6ˆ#TK&_k}1S3эueNZ  ; @@-mG5YԙeP}/+hj2tB+ͪK< ;@5HNmfغkNˏq[CqnH رb0"/1;{Bi_o!M(t'uFb)9Buqm匼V.31ZW\ƌ:A-   @@@`(^:اi]Խ?*G>΢sIƞ&`'AsDwucjzVzbnvk}<3]O=z>qv@@* `,=yzSjJ݇Cxqݫ)̓dcΨ@"ΝCqa @c4>a/.E8>__2'c7vlӾBbP.N^cxRs\9~)y]/fcF#u I_+Z@@@@`P p];h昹}jT+.Z1ſn0I5m/܂wjrtbiҢ-|ˊY8l\Cu#0PΧi_)~i;ٓ43mnH+&>O"9|}eNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;q+Ԝ:϶NuIiX\W'uzΑ4R4\Pݸ(u韽F4 #@% <v)f<S^e{gi.薑4-n?oҚ/_vr>K,;%W5LY@@*@vrwSDY;f,|*;[[սܽ9E=5wR}4g^[G?!NTC5#w3~~Te;kg;WT_7nf4Y˘!Q'ݼw?]>։-g^9ٝ苼ަ{hޯCK>Rܽh+]Lo{&[]S޲GKЯ T *^L  P!`fT}AG/)xcQqI3׬w'5ЙOg$5SSշ@s 1@zN=0VϻZLwbyD=oS|Q~9Wٓ[hjMg<μ}+fh)l*&9|}ȜqiFeNz mB;y=6Hasdl/gvFֺ2f|v *P|&>G,EY쬫9[Q   %@S  UAz v -WC/0Ҍ"Wөv%ٓn˓kL-;TVkKs[2j~j?>Etz)dƒsdTl O>wڨ)t;&:ESG^?(%&%y^A@``NJi|_\p|dNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;kvʟRoihޖ:E8ѯƦi\C-&׫GoG_4/ _3=-Շ'?RtbhMIiZ|Ip)O3F عMR;[.Ҋ|=K`>D-z~-qMZ+w78OA}ˬ?HkgɃP~/^\C W0(!v+&0W%}Ql+}/DA,h#(;F رb0”Bu2}cyOȖ9Mo xOlﱶ@ #vP+'c/e|9#նL}hD71CNPчEG4&nvcS NG?Zmm͓>]e(ZyDD7=ja%ε卽\#VsoC:SzċoP v v_Hdx6,LE-;ŧ@uH/” ?#8)!`4e+E-[E^~6j+(ӷgߴZf*6W*>|wj@IDATtwу*C9&?'}Ռ[xQX1FqkVe{x/;`  UJ+ s|q)ʑ9㾱ӌ'd˜ۄrIw[{m(6ˆ#TK&_k}1S3эue?k n/ R_l{HfVGM?A@@`0@3T1'@~JNz٭w .zqݦ'BSW}_~/JӦwհ>"v@ZNgiؤ~T2QgnH?_Fm6к#SWx?s=]g#>+vz\$⥋E˕nq?zN`޳/Q̈́x\*$9|}ȜqiFeNz mB;y=6Hasdl/gvFֺ2f|v )(Q{RakcG?LAT ,x 7'PN}TIll N=]%ќK`ˀ%!0@@ Gl>V36:S4GA|`%^@S\56P@()3W+\I>:UժEkN)~zֱW7^Kg#D!qMHT"J|?^Mf+@5`NJi|_\ SbGf=![洯7}-=uFb)9Buqm匼V.31ZW\ƌ:A %/B{?\|7 @@@`@3xl13@ގ1sD*~P!9jv,w$'~W1=Vؙ<(M[OLN@s  p-ܦ[Qk%<+([=:So ̛EŊ \b#8D kl \¡tӨy:F@@@c4>a/.E8>__12'c7vlӾBbP.N^cxRs\9~)y]/fcF#u !I-Z@@0v[!Dz vrZZ"Yy=a&{DƣmeK'WV3;_%(y]KvJF%ε卽\WS7&дq?|bNhrtlc4cyP ?#S_v}YŽ5+QP_h휱 v/ڰ@_ v#w/N E˶ж%Y2'yG],3=h|ᐊkу`gbj!P`'K["QNZ-EWv>BEOk83bA\aG}>O3U{=,64 v7WB4iiAu_  UC+ s|q)Z9㾱ӌ'd˜ۄrIw[{m(6ˆ#TK&_k}1S3эueTJ0*hRi>h^jj}/-   IHP( \owb|O0ekӟOMUrO O.ٗm?ߗEOҘO۲{˟m7lb7nn\Y93?+'\ǔq_!|'P`q55Dh4=n^Q+hbVa}ߓ4n^YJ;=y826=G+͝'w)K_Z:@@J @c4>a/.E8>__92'c7vlӾBbP.N^cxRs\9~)y]/fcF#u Hݢ@@``g|uB*ACKǶ,+)ŠYSr=Gz՗Q5/ӫ\<ܜ2ԝ~I|eb̬3?Z{]VG8pA@@NXVQӕP͜_\g{y߾1%٤M5Ђi莚wit$#ZV{o=dhhу㩦m:7hJBa Cm\>FoVo}o @`NJi|_\p|rdNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;>; ;goǍzL:?,{C5վK*@@@ -vҒCD2; iYfгVќz_ RN̟KkkVRœV=}$e]nXߜf<}mzeh8#ӋD>Z\+$0~[vg޳/Q̈́-TV^zZI~@-SխSe.3"|u~~=mnH+&5{NP>|q?{5WiɊ^Y@@z@c4>a/.E8>__,2'c7vlӾBbP.N^cxRs\9~)y]/fcF#urgwS턅Kv{k7-ɘ? UUKwO\lϷ5uпhk+lNO)oS`hп`Xw)Z4 ع+F; ^8Gx]tPMoи񟠱uOvRzTsHU=gvuQN=KM#1~g_* ,@@ `ܟV+vҖ)*:hbY:Zi3zErw{KUw+qGfy]X6N[fvmZ1o]'QGv-]znS"=|]u}w>_z.G>E J4)-txx~X1OKf=![洯&K:cm#@F:WNƸ_6rF^mшnd+.cƇ`'ŅJ.:}M}dw҄c`(م1@sc'%<ս P=x  0 `gfSCwǷ'&a\K]]o&]_W$7lnVGNs19jF^fyXz9|*%9|}ȜqiFeNz mB;y=6Hasdl/gvFֺ2f|vR\ٓ)#;3t e   P ة > ;@@\?- he^~j?~G~mnV—0#T&v'ť/s2}cyOȖ9-$ 5XPl ;GΕ1Mjb>f4Y˘!Iqњ7[~cw(`gbB $NN hh@34%/=wVmjy+Z^1l OP1T=v'ťkHdN3-s[Hl%klﱶ@ #vP+'c/e|9#նL}hD71CBE @B\  C@OO={>OmP`r\.G?i֥o}?/9vfM*:Ι3#YA@;VLR#s2}cyOȖ9-$ 5XPl ;GΕ1Mjb>f4Y˘!Iq@@@ !@>sooR}}PxU;::?97nY  I;mhj[;ut7>?>;}"A=x13T*v'ťS/s2}cyOȖ9-$ 5XPl ;GΕ1Mjb>f4Y˘!Iq@@@ !Bv>O\U'?I~e~Uf@@@@`G  G+ s|q)ɜqiFeNz mB;y=6Hasdl/gvFֺ2f|vR\hH@+@@`Wعۮ^ag̘14|xOx   #   0 رb0"Of=![洯&K:cm--vP+'c/e|9#նL}hD71CsW-A@@``g|ub}/I.]}c΍qhΛoI#F|#t-66^T;<X1OK']dN3-s[Hl%klﱶ@ #vP+'c/e|9#նL}hD71CBE @B\  C;C.\J-uuu% ^\Bze#Gѣ;c`j @@ @c4>a/.E8>_ 1;{Bi_o!M(t'uFb)9Buqm匼V.31ZW\ƌN -    vp% %|oWfE<: %^o&n8gذaunJ;f 4v(?v'ťk2'c7vlӾBbP.N^cxRs\9~)y]/fcF#*Z@@@Jj8{シxGF3XT5Hn#mk6   g@@R@c4>a/.E8>_j1;{Bi_o!M(t'uFb)9Buqm匼V.31ZW\ƌN -    vp% U>|e-vQYi[n! %   p @c4>a/.E8>_b1;{Bi_o!M(t'uFb)9Buqm匼V.31ZW\ƌN -    vp%@`r@@ @c4>a/.E8>__D2'c7vlӾBbP.N^cxRs\9~)y]/fcF#*Z@@@J!N  @`NJi|_\p|dNƸo4# 2}6\ҝk 0bչr2Rɗ3Zm|_njFt#k]q3>;).T$       C98'9|}ɜqiFeNz mB;y=6Hasdl/gvFֺ2f|vj:;jhd}ݙ<{hj~xo  e`L`(A$   @c4>a/.E8>_9㾱ӌ'd˜ۄrIw[{m(6ˆ#TK&_k}1S3эuemF/-i).*mɝSH-+;)ESl-(;3C d;NҞ>]<>gBs[x,ows~"CFOeRϚh  B+ s|q)<ɜqiFeNz mB;y=6Hasdl/gvFֺ2f|v `'_k>OHǻaz@@vƛp;tbi˟ƍtekr-h D@@@ @c4>a/.E8>__2'c7vlӾBbP.N^cxRs\9~)y]/fcF#*; Ν} BBC۾I~wLN+n! @sC64@Ђs\w@cff~kZQ/?|Lb.E5^,ZOk~:r3+9s-  7v'ŵ0%柾dqiFeNzB9[{m(6ˆ#TK&_k}1S3эueU+Iq@@@ swO>a\;Fs=&|js=ԛ$Rt#}W&_jT@!lFS뫉#TK&_k}1S3эueU']\ >v1          MUrl1b~k^Y[#TK&_k}1S3эueU!9pVz=4JC iA v8\f4Y˘B5-:o˽a!ֹб/  qxۚ}1sf\&:cm#@F:WNƸ_6rF^mшnd+.cƯN -               0 @S$nD\&:cm#@F:WNƸ_6rF^mшnd+.cƇ`'Ņ qxtK3[{m(6ˆ#TK&_k}1S3эue줸P              B"!p[nbp՛cxRs\9~)y]/fcF#*Z@@@@@@@@@@@@@@@Z@S$n-}_\zclﱶ@ #vP+'c/e|9#նL}hD71CBE T v8\f4Y˘!Iq@@@@@@@@@@@@@@;EB.>̥79>k 0bչr2Rɗ3Zm|_njFt#k]q3>;).T@`H3֧[4\&:cm#@F:WNƸ_6rF^mшnd+.cƇ`'Ņ qxtK3[{m(6ˆ#TK&_k}1S3эue줸P              B"!p[nbp՛cxRs\9~)y]/fcF#*Z@@@@@@@@@@@@@ڊ~zoC&%Ș3hf#h;ȫKׯ4$v(& FzZ(( J4oc2`#FlAQL眧SO]4x;w?_{Wկ6u9? vrB+?ݡ_өלmSlVJ`QV˅1뷲5NkkbZҪ&Øv*LT@ @ @ @z ;9!X۟/ԈkζuOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @NJgOwtj5g:'U*cralͷZo֧* kc0> . @ @ @ BNNc3;b:5bmj*@1b0fVimc~QLSZ݄xSNJ@ @ @ @@o!`''ıkE1z6ld Feu\~+[1()nX<`D  @ @ @ @@XNXlZgd P@#uX.YvZ[_VE7am,GSa @ @ @ [ qxt~QLF^sM}2[(Y:FY],Ƭ|;v/i}J6c#ة0Q@ @ @ @-8V,PH. coekz;Ŵ>UMX1Tt @ @ @ vrB+?ݡ_өלmSlVJ`QV˅1뷲5NkkbZҪ&Øv*LT@ @ @ @z ;9!X۟/ԈkζuOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @NJgOwtj5g:'U*cralͷZo֧* kc0> . @ @ @ BNNc3;b:5bmj*@1b0fVimc~QLSZ݄xSNJ@ @ @ @@o!`''ıkE1z6ld Feu\~+[1()nX<`D  @ @ @ @@XNXlZgd P@#uX.YvZ[_VE7am,GSa @ @ @ [ qxt~QLF^sM}2[(Y:FY],Ƭ|;v/i}J6c#ة0Q@ @ @ @-8V,PH. coekz;Ŵ>UMX1Tt @ @ @ vrB+?ݡ_өלmSlVJ`QV˅1뷲5NkkbZҪ&Øv*LT@ @ @ @z ;9!X۟/ԈkζuOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @NJgOwtj5g:'U*cralͷZo֧* kc0> . @ @ @ BNNc3;b:5bmj*@1b0fVimc~QLSZ݄xSNJ@ @ @ @@o!`''ıkE1z6ld Feu\~+[1()nX<`D  @ @ @ @@XNXlZgd P@#uX.YvZ[_VE7am,GSa @ @ @ [ qxt~QLF^sM}2[(Y:FY],Ƭ|;v/i}J6c#ة0Q@ @ @ @-8V,PH. coekz;Ŵ>UMX1Tt @ @ @ vrB+?ݡ_өלmSlVJ`QV˅1뷲5NkkbZҪ&Øv*LT@ @ @ @z ;9!X۟/ԈkζuOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @NJgOwtj5g:'U*cralͷZo֧* kc0> . @ @ @ BNNc3;b:5bmj*@1b0fVimc~QLSZ݄xSNJ@ @ @ @@o!`''ıkE1z6ld Feu\~+[1()nX<`D  @ @ @ @@XNXlZgd P@#uX.YvZ[_VE7am,GSa @ @ @ [ qxt~QLF^sM}2[(Y:FY],Ƭ|;v/i}J6c#ة0Q@ @ @ @-8V,PH. coekz;Ŵ>UMX1Tt @ @ @ vrB+?ݡ_өלmSlVJ`QV˅1뷲5NkkbZҪ&Øv*LT@ @ @ @z ;9!X۟/ԈkζuOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @NJgOwtj5g:'U*cralͷZo֧* kc0> . @ s#}>& @ CNNc3B(tcmj*@1b0fVimc~QLSZ݄xSNJ@ @xꔗ_xI^y'A!gpƗܛvo2a`'- 2צ=^A @*@X=/陈kζuOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @'е]V-9닢;:~/.I#F{F`sqq&_]sLqpڀk̡/񯩷EFԝ\wrF/?ݴB$N6IW@ @{NJgi5g:'U*cralͷZo֧* kc0> . @ pdYo4}LB[yy4_"@йa pEǍlK;$'77F5&o&6,!X &#"@ @8V,PH. coekz;Ŵ>UMX1Tt @ @?zJB˩o OmyactƵZT'YhGq4NYrP/u3&ٸb#.|y򷿗?;-Pp`'C@ @@X=/)XlZgd P@#uX.YvZ[_VE7am,GSa @D2+SL{"&kdKg69>DZRg9&ttigQv v @ @NJgOytz5g:'h1b0fVimc~QLSZ݄xSNOg-!@ @ e 32RYhKsr 7=.wMY)[!];G1OavӷK>N~«[oG9ЍZW%s߿;ޔ:H~9'X՞7Nw}#>6{ˆ_?#/m!ncrܟ']֭n^}#/@>:؍ VO;]e{PG _x{R y_;E17Z~?c#ˈO6O @ ^$`''ıkSE1z6ld Feu\~+[1()nX<`D  @ hdɹ7TV\2t'^~@wa=i{_Es2t]RXӹV#^}9?VyOYnRjڥ nar.muw)'}mW$3>?KtlrON=ܳNrRb &?ݴBƴXn'lmev󥂝=7{dMdBO,&=F-c2rweqTV@ @8 qx,~QLx^sM}2ۋFzJ. coekz;Ŵ>UMX1t @=ZV^_IOCR:)b'x/uA;G8MbtsO B`;}lijN4ˊ[li#*<!ZB>E;`v5㦸̎o͏)^s4L#g)pFqwѓ-Hӆi u-  @ w qx~QLO~^sM}2[(Y:FY],Ƭ|;v/i}J6c#ة0Q@ @'4&57+.*IaM7!HoY>\#!xKbUߐOk]$gfŔg̾>rSRMȭ!}do|,̪2] ޗk}~R{$V9kaJ!l~5x{㫛}͘-:"og%~"sOƈ'{ . @ pp 6v&[UUL8u/T]np\X*8#4s+^ TjTf yo=z6Wٌ<;nLBS۱7xTl_>{C j[z @ @=JNNc3g7b:bmj^4Weu\.@IDAT~+[1()nX<` @ '`0`g^Sºd§ϩ3 n75]| Q[Zh=BjLGHG6m\!j(LS;+ixof?5N<9ʷ&|Q7Y|o䌑Umj@ @8 qxԅ~QLOs^sM}2[(Y:FY],Ƭ|;v/i}J6c#ة0Q@ @#"=Iaُ7>"w]ri'uíFEjQy.W8F'is"J5d9á, Ux,θS6puf kbzvrI>3F9f}\" @ NJgO\$5g:'U*cralͷZo֧* kc0> . @ ptx$V;b3LaM6LB݆tyO7ʬb)R)L-!fyEJX^OEoT$q5qLmx @ NJg^L5g:'U*cralͷZo֧* kc0> . @ pxCy\QV{Lh}B(]QX ĩUB݆/?3:_92z1MJ(iS\,q+ 5w,= 01Z#]^ @ NJgOW5g:'h1b0fVimc~QLSZ݄xSNOg-!@ @ `E4O?]2v`= 7cI`{4ҢPf'妩6) B`)uoѵgōeƄh9n @ `''ıkE1=z6l/+uX.YvZ[_VE7am,GYK@ @8@!wb}={ftɯ؏ewrrIDH'Y uϞ> CN^>r{*9tzy\t9FH,1Tf'<[Wwg:bIO'$lhNz*YrPv姛1m,c;7=٭R!m~mx|9!Ƈ)zTg=_?O$Hν"*9N\~c(W8Y}[~jޅ_τCo|D)艋TmqM99G,g=_Or;"L~\+v?}u!V$=H&<4*ڧx63jt7[eirԮpɳo+=IG^,\Yn+4 @ @5vrB+?_)לmSlVJ`QV˅1뷲5NkkbZҪ&Øv*LT@ @Krs돒jugI$$yy7sœBCNhBM{$VRߐq˓^eE*E+lV9ɿƻvV\m v޵?(d2]:jQMQorS2Ɵ8ZsctydMr\+ D @ #`''ıkE1=z6ld Feu\~+[1()nX<`D  @ :%7|CX wdگyFKeY 6:ŒL|S! MO pMSGh|Z}r_](K@;k90f+ΐ j{"oIbO* 7ud۝)ZS:7,f)3Gʢ5?gQSQ܎\T+f+u7.>E6ԝ koˢ&FHq . @ 獭¦>wG *!B[Wwҿ|ٻu#]]"}t?מ7ӱG82 t?\8h@ @8V,PH. coekz;Ŵ>UMX1Tt @ @ @ vrB+?ݡ_өלmSlVJ`QV˅1뷲5NkkbZҪ&Øv*LT@ @ @ @z ;9!X۟/ԈkζuOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @NJgOwtj5g:'U*cralͷZo֧* kc0> . @ @ @ BNNc3;b:5bmj*@1b0fVimc~QLSZ݄xSNJ@ @ @ @@o!`''ıkE1z6ld Feu\~+[1()nX<`D  @ @ @ @@XNXlZgd P@#uX.YvZ[_VE7am,GSa @ @ @ [ qxt~QLF^sM}2[(Y:FY],Ƭ|;v/i}J6c#ة0Q@ @ @ @-8V,PH. coekz;Ŵ>UMX1Tt @ @ @ vrB+?ݡ_өלmSlVJ`QV˅1뷲5NkkbZҪ&Øv*LT@ @ @ @z ;9!X۟/ԈkζuOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @NJgOwtj5g:'U*cralͷZo֧* kc0> . @ @ @ BNNc3;b:5bmj*@1b0fVimc~QLSZ݄xSNJ@ @ @ @@o!`''ıkE1z6ld Feu\~+[1()nX<`D  @ @ @ @@XNXlZgd P@#uX.YvZ[_VE7am,GSa @ @ @ [ qxt~QLF^sM}2[(Y:FY],Ƭ|;v/i}J6c#ة0Q@ @ @ @-8V,PH. coekz;Ŵ>UMX1Tt @ @ @ vrB+?ݡ_өלmSlVJ`QV˅1뷲5NkkbZҪ&Øv*LT@ @ @ @z ;9!X۟/ԈkζuOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @NJgOwtj5g:'U*cralͷZo֧* kc0> . @ @ @ BNNc3;b:5bmj*@1b0fVimc~QLSZ݄xSNJ@ @ @ @@o!`''ıkE1z6ld Feu\~+[1()nX<?0@ @ @ @x8VV9ۦ>E#=}QV˅1뷲5NkkbZҪ&Ø/Kg?9F@ @ @ @;N}n;9!X۟/Ժ|h21:FY],Ƭ|;v/i}J6c;vXt  @ @ @ ##ةV I Øծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ @ @ @ & E8EG˜ծ>evmruOf% T0R(˜[ٚoގE1OiUtaL};&*] @ е[vW>mt'o# @#u;% @IN7M() sajWim2;y߿6ez:'U*cralͷZo֧* kc0> . pطKm}U+>A8@v!%InH߾Ad3ȝ7|W߻K>>i<vӨ7XN~vMYwՄ42vo'~Ő;E|/)?w@߹Sn^ 7\r2n^# o]!n1ʳG];ɫ;ެ1Ч>pTX-?Ё1 훷os~C]݅nTOf;7JOΝ{zInwʷȀf'|um_oa2p`We뿾.~/tȡ}gOMO&ѷ{7RmuqDcA3  OvnBNOC cWJkaU&){>,PH. coekz;Ŵ>UMX1Tt O`ȭ+^p7{堏&W^|k^deɿ:igOG ȽM/_||\{7a3?iEa.Ϲ^pXz2sLs>{l9U2t^կMtopwS;\~Ԃ5N{&ʚ6Knk7s _O~T:7.ig]/Ͻ~w[ݿP?Q8L.q71 6?%n<_2li2e`nyۆU3ҽ >VΘr(`J{uͿHA!-9ˑu}XrD0l)TeuwzWGLm+}߼U^x+=HNu8$o^Y_>Н/-ov*nZ% o{Lr.#DN^3[dL3olrsb zk5qO[% w|I\K8:؆O{UcgۻMV}/v߃~e)gȩF{_oCҹ]"3O96|dvY%g"War=Wi=;{mX\sS}>jǗM^"f޾ypIX߇o\>[7>|?,6 ?vn0%??Iº0f}O_~?^e9'l/+uX.YvZ[_VE7am,GYK@/{ݘEP+9ܬ&,kWK^NvN:+ݍH2 8gkTR:oפw ?tۜ;A;o͖[lG^$w4%K+gSy}eWd7V`wR꫋d˹&S .sSn?C.M;nӆYӷKKk'¹̈pt'*Q? tryCfaK2O xd({idQ <}2ڿ|yr}2ƞ6{ur9焳9⻖9r~p6vQWߗJ-p[@dSdkŸNsD hi&*̍ E7x\p<هV.WXr/O~2m{׏oۯ{w?&N@.uW>7/\0O)mvz?im5&w9rݗ^xPnc/'SF[;K9#y+dwe6t@=R׌7wg_#%M=|϶c,\4z=o2qE;,ts'v{?4/~hsrb!=R͙q,PH. coekz;Ŵ>UMX1Tt Oo;~ OƟsp7"χ>Wذlܕuɘ8rt| Yo&?y[,#ھI^|M}$,~#e2c,2[8c9w ; yȣWYTL5'cS4{Aa+/w9jW5y#ٯS>+[`qr:krKsiStjQ2cnk׮eck#=-WԷbC.At׮AW7\&Gqʺ^Z.}]y;gG7-ʤnvLqL~nwdsKvvsO=+]nMO}ZjVo # .? ?\Vl}see cx9?}}_Ƀ`W._,C;K\xu2#㯷z 8A3Y'݇yS~h|Ű3eqǿl_R6W_M_V=G,ɹrb58l#k JWQ#Nڶ?6?vЋ&_*s}t&DzmQ;wؐbV=2ŷw~,yKzlzEy]#=Xjokܿ`nD/GϯC]{m<8"A?,c>&{k{AZƹKidCrow .P_y;Wp~[>4T||̻YUZƺ}˛[~#!y>?fϗQT.+x+|lO1w׮>R=97Uӻy0 '_sVuyK] 7.wW]z3cxbHCON7эOnXƬv)Ügѫ,g>,PH. coekz;Ŵ>UMX1Tt O`d/mYn?/|hfj7G럞=M6: ez낝/+}]yuɏg3 ƸOdrݶcJloй쨹dյd@]|VVpZ;eju>lO`gbu߅]lTӵQ?\nx1Ҕ+]&[>~fwU4Vy}Q&=|2gusL vܼw.yntO~^pXck>S ߼GU9:9x#\=je_]43J5 ezvJ'Y {|nVmM =u?ۼJzrre ye}RcuKw&ztn`gK/^!"ϙ#I, 0G6+VigڿeJO[v%TnZyܶ J$N{l; m\ÇEfJЕlsSyECN3p\})^R5\'1R2/0&}/Ky/s^;ۚ'>OT8ҫYtMu'̽l;s/_<{luy3O}+axnƐɮ&? @=GN7M()sajWim2;y߿6ez:'U*cralͷZo֧* kc0> . 4?+`{Qt}FQg{,"tGhk>BcƮ}"AW]wm}Avq=XڻC^[ٶSoArȡT}vȦ}vߎdАOA+ݱM6vlK_LJ CԿ۽텍idh~[nE^>o'-[^zUܗtɖgUx2(F]c?9Cd?c>{;ܒ҇ Z\_m$mɐ3=gP؋3d#ϪW7>#ONGra#Cc_3dwwmncTlowɎ-[d*=儺Hdu~!2O s%_+W7~۪cpOɟ{g. -;ޖwe7}`\>Cfៈ33!CQGw;TJXEn9k|UO]-GEٹvrž)GXQ?uo[՝~?띲 N}?y<[^?v4K򺟰{M:'~%쮙kУG'ŠGYn "yY5AQZTdǜ8ڱt .^|.vBP)x.T,9s/8g {|-i5IL˛==ofӉsknGy$hݗq{]V󪯂VK_zBY2{/VRkbJW/x=yA/uѡN',}Cfڜf :2#:s|n^{z|=3heMtO[svUϕ*{o>}꛵ߟ0J `|dhT(yxSrbtxaJuajWim2;y߿~rOj*@1b0fVimc~QLSZ݄xSNJ@R>۷)(:#+'ع vju ;s]SE8 =7Y&hLd&Ӯo2z`(!-[2˳ -|e侠 nnYLZ[}9rpK/Y~S*#9_P`&l'sdK,xa75iF-X#^9?tw_-%Z|}9L'YyulCw#ן,=dZl`,9@^K˫kuC]%eꩣYEOwKqօ-ِ cy [Qۇ 86º?Q|Tz78UyHז'䚿'7$7(7Njo‚yRnyoȸ nh;;cĬpg]".锥3;"Wu5F6Y_͖munc}B l;GN=s~Xidw˨g[dޟ 64e|ka2oryn*^[o߉ƎEnj V=/n)dL#4vby N hS\nn.Iߑ68]H;{D}('O-J=uNL{CcaZ+=ozju#&@8;D7^?a]UZۧs/EZgd P@#uX.YvZ[_VE7am,'?kV:pޕ./峟va >XI٥mk䚛W;c*(c@ٹ~'^9Z4Ƚv[?ce7,v8<ĭVc_qKv_,͋w7x?|@1rEZf'4ˈu|wO) aɲwԣ]Vݲc>ޒVtihV'?g-ArGO0ͱbݼ>NY7ofgf]egvɴcҏ*Y {E`>Lw–03uͧV^#)4*v㏽&NeAkХ% ~ݔ]^&j^-TYwUm#2+#4>gi' həqf^0O%>-/W:(C?y :4&7 .vMjd~q>G%GMhtWhwuW*_^rn ״7v<7Z\7Mw-]#2KeR?;ɒs%sOyVmVQ[~޲^M6ffFwF|6JHۗ?u=rA~ҹKo}Aߺ ?S_<.'Ɏ!ڴA~_-J*}tAC#E;f~cv#Uz&yvn5| >u ^ UvnpuW=` ȴז^[sqM~x.9>^lͽxM^e&ѪfGMyEf53Ҽߐ#gz;D7^ a]UZۧs/EZgd P@#uX.YvZ[_VE7am,GSa?VZn}ri|}v>`ה4QhsK''ڇ^~ċ- }?ٴg_WqUƎ{8v x_la,G#)J.#kr|յȏ>9?}_"?{~g8:Ƹo~ʭ~_1gnyBvYfrPhe?GZ[d71JmK~b_'p~_Lwگ#+믒o{?U@KO,oG|rWk[r&A[VΓ!pd'}[^Z? ϐLD o;tv{]n=mY:O6=4KA>~{4ӟG3ի:BV7WNNxnTnF}An]8m_R:>ߗ׍`nN6J>&n7hU;c\)b|;e_߳yae7ҵh Ӿtaҧk_{W?ZD+t"ȕ յ2|/F2j0V2KO.@IDATmn \8q=+ϐ,Dafjo:o5}ڭ[|Y@n/LIυ.Y_*Sf9ϢWYI}2[(Y:FY],Ƭ|;v/i}J6c#ة0Q@@+-6kvϨ9>-ȉuN6jKWNVy4[ Tk h$xmq d 7Y=XQ;Jt3ܹmٹ2:r ܰQS{ɿ]Pimy;´w> O?=[1&o'0su2uԆ(j{Ӏ#:oW[r\'ʪWj_kIԶ_=zU ,9qs#o#gbEbNߑotbM1isd!{=%w{E'D)zu3 78s /sB陙!&^2/Ь7ͬNܸ'fn8p}A_j2b@0N չiw[~~pMXGƴػ,mo[Vs/C팴g{J@ykg)NԽs˻}nNcNYng5pw6h\p&\Y龬xNɿ't?x%r)Gz3ztG|SȇJ5dǦ$ +uøU<1y՛Ծ,~SM;r̹~q}e#oTuaQj?V:Δklפ:=V;s[ghz%n굱;#rf}og]S޳նk\BV?A:dig'we{چѿ7:#ٗnF2]8LO#OtK^d;[l$/m"o񦼾s~N "i6*ן&^Zv?Pr{V)9 ^΢HsHF 3!D$$ E6pxE+l@3W=\q%wÊ(,rOr/fXװg ,011Ht#əi駞z_UWBHQt₈NݴSshmu^ąߠ"imFG0~ple&.\^PB>Eء%В`V0O%H1nsC@OQCD{nGx.Sd`F3v|:5-${'JDZlQdO~kY ](؂.C ӕwv67uaiЧm~4X!/_S;AD˷kAԋx,;NĀsxvCXTM(/ uNڇFB>Iq 2 2<@[v#cݫ:h*h|͓c-SEKfҕȢ(V8F=)FŪDuZpa$Z]798輪צ"PqDy(NH|d[ERM*n7Dp1n.¶2-FV. 6Ȋf_Nr͓,nBt@(^Vv6Awp=vuO{3˿g?RFQVQ=h>3ۿ/u^VJWe ) Yl!}37vi&OK "f,#ietrqn";[V93:k^uP6N V%{-Sj]sr6$tIhK AEYF*7J(@sS9®ƍs(`Q;w -k5mo܈a }3|.=^ՃCa~T>._ u=+6lkmȝ&`pŢ屗rQ+(rFaIh (*6="!x'㵍1 [X[yʗοU'NH{%>ORk7O uSn*3cfb6Dj)QR)(_u7姙Iޜb[VX]5ǫN߽D˖Fe㗍H!d|ӱΏ)R=H>?=ZQMZ7`IH4N.'t`!Kbsˌ#0W.Ln1hTNשi)'٫>Q'b ۢTS;Ǒ%Q$Lˈ uj:,U[!td/I7m^4vtTvaF^p/9KVNzKƩ>91|ebM)%(}=XE_yx6ťδ"ZJqD36>ٚ pz:p_?&E(|5>t_;9^۽g{R~1T QS6-e-u`kWDwR^$Iboya)D LߋvD*ȴoTN2^4G Y;F{j}.Puwګ/ vHLP ^̿DR8n |V9rIb:~!Ą CAEUeT%B^y{΃pXZ81m/ÈY뜴}gB7&\o9 K!N %L6~bg2%Tx,˳#% `M)d=[RK[)*0Z0]:+UiVA㙚_qwl}3l2{ѽMu(\%0 0WQc="7Zr -AyV(4?>`o4K}.oɽ3455.-ezSgSom3b7-EgL*qsq\ϔd4>H<׽FON"=152quQTfꉫ {Ȟ h\37"7xEľ5D?zvsu(/%maF:yO5NmB$}`1N=?{vY= K_v 0IEI :v.'MYfWDȞ^n[zqU#0!#$he=Oשi)'٫>Q'b"D_S;Ǒ%Q$Lˈ uj:,U[!td/I7m^4vtTvaF^˴-"l*]4Otc1;ꋽ29]yukxW[3@Hw"X\<\xCTD? ܵ*Y6*8Jg1`eZO7^YY_. Tt-Oie?fFEieyfY8ĔF1Yj}Gk/A6y@(tØ3t2ՏGqOI1(+K,Hk~T!GKcM՗=LB&[Bh(Zh`Rjoē;.XxtiOKUuduL.XfPO158Bw6vԭ]F~ŧa]﨨j]ZQruq=\):\DKy,%m̞OIlZj B9kkJ,l/}Bˑn}헸lz};IOmO{2zU~])I$7)M3\k3)m].87EBQΊӋ\SRcM`4ꏾfl)lׁl1YxuՈuV&!Iچ*?Y$秶'=Tc F]C Kov`GMG]+~kZ3=ʜ{e%yJ6"+@M]/w·];0#i"F'ᄥEyNMK9^<[&*ک>, ("`ZF]PSdg 9(&*[ :fN.#0?:.~rSB߼YguLtpcvD}(l:+˪#O+'6aX2XˢI dÆԋVҤp/a CC{C/WVžR̢^Pᕿ{ t}{OpL/R%F|q>V ~N3 8cF?̧q{N٣u ;VMCaS Ñ^)x)Na^ʹJZ:Mmr^yQCF/~ުP{Iԗި_՗K8zh8^Xh/xB/MvA*;E䅃[~ɺÂ) Ӥa"CJ N PY6:DuPHW}Rڕ(od-B49T1 QI u XGtPs |MUw:a+ wCz5SN@Fl qD-9&d7H*!^E-æݩ$(o*}ԩ"QP-{WKr~q3a~{-ߤs1TW=Hww*O"buQa,wsrEY:l3r!:Oq?ZzjV”0'{JbgO)cc{h=y!=(:jкH5NrfF ]PƢ:j#4h2`6E2[E4MlܚZz Ihx>5Nd߅ՋZeI؈.Cmy(_>S^AޫbEAKF1S='{ K/q9%XB1OXE-jV~Z+y-;H掘جEE >#0&H7b_Nשi)'٫>Q'b ۢTS;Ǒ%Q$Lˈ uj:,U[!td/I7m^4vtTvaF^GИz/*j1j< y%\4S{c1;B}ZK{d9S{]>l=Mg.Q?{0֯O.ġ}85G~3 UUoOW|.܍S2s]X:?J ]AXdkW p ס?7,s^^ r®?mo`MS=籠b;V̛B0%bZa7ԵeHؼ4ӆ$"Wlp>A d q5>±([.ET&Dڠ.+iZ~{l& #ɤ c"DD)ЬهފZaߔaݮT;/%q0`6،2 R`=t 8qϲE>v9 ;%q=ƏXW0p=nu^o6> 58Zcy8v6@qt*^2P%ޣHQDz\fU,D0oJ>ȞZ1/KAdcGmXZwifa'{ddʅ#u~Q9QR|3z? hۥKȾmXbB! lLk6}=h8 CJ;wʪw r p=FCבJWcn7\(x=q$աg HIĔr"x)=L%UT݆6e)x>V Z`6UQ#)hUEQr 3pGGܦ+ª'Jn=2Ys[ %'vS 2/سAG쐣֩߇oC?Aݎ7SȚs˄RU]ym2ҿbmˈT/3m#O -U,O;Ng6ӧ>GhiڙBD􅈎pBz[SRr«bH5zL'Evڄ/y*~~I:MU_$nP$EsxrFR{jzl8MS., /)mkV +D}Fe1ˎ$kBBמĈz\w_AuL{|p푼/݃|ΡKy`:!Mz5/ "`Rbz`U)*L#7q.3N+QX⣻R]i ԯRrO哼n F`>o0aGI8ai<]dDzH-lg{MTGL72tN'8{VA07Kҍnu2̈́L{-3# +4E-i9HCoM7d5(<\hp0Yl y zZ&LYTYú© ܋%erKRw+jz5{"'@\6?)LJ;MVGr³KPw&:Pd Z~(e忌ڭ΃P +n8Zw(QͭD>@ "‚jz{8"xz՚aHQutrwyVcu`}*[&:B|zLi2}7*a=zUزj1i ܟA\quFAacz10]Ȓ:\rerΉ)=DXl+2Zp`׏U5uLtݞ[J9n"HS0hTT ?jh=QwJ&Tt?"pG2̎3x;pZ%d6j*yNB%-E=WJA9˳dMmƳ>1?_<=B"n ) S2!=cBf|ΦN.Ez짎X6uSX§?Lshi:h?Z?d6r `}d8B;]? JHݙALöMФƳG?&4ٞ|U?ɢ:u9DәMiәĐ +eqMG7nX9N+×вGogOOBxב&'t`AjxLYF`>0aGu;]dDzHM3lS}LTGG@0-#.(Oשt̏WmI{JuL3a'AGeF`G}^/᪫3$_%t437dG]s[֠ldJAPi* Ci Ŀt\]UWŦ6IALކNGG.eCtlC;>@ץ.Zm m#18ղmJMG% !!Hَn\eMÆM uwvR? ^&BA8wt]0Zo!)g|vH.eq:?kUKywtX0_K}ԃ~ rg9fE}ys*&X)^Uo7)p--;p0 ;qêK;./#wX\[;e{w*_ ǽwǺG*Dz@˻m^\s Eÿ %m~8}_Zb8 kIQůe/D߰jmoTRtZ{<pڳ0#B ;>ҍ AujZIO牴D=öD]{#~ɪi/c^t2G2Gz5'Q'b"D_S;Ǒ%Q$Lˈ uj:,U[!td/I7m^4vtTvaF`F`F`F`F`F`F`!F'ᄥzSRNW}d=OEʳv#KH et:Y٫BJ餽^nt i&$0#0#0#0#0#0#0#C ;>ҍN K h<]dDzH-lg{MTGG@0-#.(Oשt̏WmI{$Az]'LIQم`F`F`F`F`F`F`FHv|yNMK9^<[&*ک>, ("`ZF]PSdg 9(&{ImN #0#0#0#0#0#0#0#&H7: ',-tr%y"-(MTm5S}YPE(<]2?^rP:L'MtuL3a'AGeF`F`F`F`F`F`F`F LntNXZ@:5-${'JDZlQdQ'b"D_S;Ǒ%Q$Lˈ uj:,U[!td/I7m^4vtTvaF`F`F`F`F`F`F`!F'ᄥzSRNW}d=OEʳv#KH et:Y٫BJ餽^nt i&$0#0#0#0#0#0#0#C ;>ҍN K h<]dDzH-lg{MTGG@0-#.(Oשt̏WmI{$Az]'LIQم`F`F`F`F`F`F`FHv|yNMK9^<[&*ک>, ("`ZF]PSdg 9(&{ImN #0#0#0#0#0#0#0#&H7: ',-tr%y"-(MTm5S}YPE(<]2?^rP:L'MtuL3a'AGeF`F`F`F`F`F`F`F LntNXZ@:5-${'JDZlQdQ'b"D_S;Ǒ%Q$Lˈ uj:,U[!td/I7m^4vtTvaF`F`F`F`F`F`F`!F'ᄥzSRNW}d=OEʳv#KH et:Y٫BJ餽^nt i&$0#0##>@IDAT0#0#0#0#0#C ;>ҍN K h<]dDzH-lg{MTGG@0-#.(Oשt̏WmI{$Az]'LIQم`F`F`F`F`F`F`FHv|yNMK9^<[&*ک>, ("`ZF]PSdg 9(&{ImN #0#0#0#0#0#0#0#&H7: ',-tr%y"-(MTm5S}YPE(<]2?^rP:L'MtuL3a'AGeF`F`F`F`F`F`F`F LntNXZ@:5-${'JDZlQdde8s= NaPV>f!rQ>˶ +K1pЗ1|X._(#7_8 VV,+ͺJ]g][ W;]6j62@S?d_x7f3pd0.xۉs.1~Ѱr9lpX}=Wǩ_!V!]1 N:S !9|,RXB˻QMzg!{N1l3mTL3=?{J!v\l==-Pi7QzB)V- &SZ 6L& \2=?{BϴBXY3>a.`F &H7: ',-0tr%y"-(MTm5S}YPE(<]2?^rP:L'MtuL3a'AGeF`hZ=Mj׃vV-*f MuQqWǻe]s19^<|`6_&˰xz^Ӹq&>,ơ1J3ϼF}*Qo<oL{>7܀S=fI_F<~ `;OUoDCC6}K'ij+Q)?|{ OGO700bE$ko2JTQ?'6'ާq#0uW)4]*&in2,/vA? pg*r"pwGN$S2 cB h_!#t|5sj &:?8IIk/D~8V2}] eF/#5/@ujZIO牴D=ö*fDYpd1bR{ ^ '!y(Dq!<6~Za(?'auClo–RjMi͸s)+$+k&,qI`x 82˻p/5[94\N̍&hu&Ê"њ@w:_`yd4r&J~y_#2E;۹"#*}߿pDȺ}bLu*-!qdПPԁoU{Lo|QLOb_.`]g#ȞFnxP`Ln1h^Nשi)'٫>Q'b ۢTS;Ǒ%Q$Lˈ uj:,U[!td/I7m^4vtTvaFF̫V2”y1f{{{1bw&Ms1Ƕ[J),Yn8.t?_k0)HQ_rlJ]"U61LB1VЈ]]-G*O﹗10om`3Z]K0ONdSu?wp4.?k_<Ԩ"uq8TJNszJUc0lBi:?o{<7vEGj;Yw!rؽɍ[3=cPaS0g_ ^u'T- Y J/Xv@ `Cz&%h=:8#SQjdXU軀w|ɞeǫ/w/2jneEі[0^Wnm2f]h>yD""q:]5S}.ҍN K&tr%y"-(MTm5S}YPE(<]2?^rP:L'MtuL3a'AGeF`hܗXTc&h&ٸo}:Շj&;LVQ)*JE;e[ 6&;@o9GDzϽBd{.A. $嘵Ŕm!ׄ;sGN9q1q^n<;g E(߻¶+*fsSƈ{;Q6>$עis9],j5TugW7E ?_"/~w# bkޏ#30kmuaA4ᅝʁxJhKj_EΔ[)umME.nYRذ#EƼepj)K$w",{bɏM8Z*k%tdCCR$=nч-e{ֶ*^V!prZl~[;ݥzc+ϏQ$OꈀE[ʚs5] ;^8Q}>3=s|07bs ]Ir )k%Vך%2NGviFKFԗ嫎rq#0@r#$]ujZIO牴آ6QyNqdI@q 2tN'8{VA07Kҍnu2̈́]F`p ;/h25J颯+S_gS27dzO|\9yWq;{6o j"ffϏ,9H߀Qؑ촯d[v~I˛PQ6V|`4^m̉6IMq=E€l̵K}b7~-rG mvܐJuE뉟?R k0޲Hҍܣ m9# U*pH9F< $ZʈbO深s''j\‡?殳Z +&\n0o'"XwCCOm4>d &.w4f_C8!BaTk5z;)Ua~qV7ZϵYC.}V]_=~BRxmU{n۠#"tg61]"'M((YqQJALs{5yx"(z{^R{JZ>m#-gQb>3gnJ- 'TG4ys u7NXtv(S?tXk֤_q:}/h0Nw2Q=FQ7 @qDaoʊ0Z`fEB{F`+&H7b_nԴU(YizmQyȒ(eD:5Nqa:io6Hd ; :*0#D#`}4͓ $x+"%ŽFqK'_rˡ/WæWԖ(S蔘v{{+-Yyp<5k4 K&D(mHEҜFab#IT'~e,*-aS_w*rãg:RK>wVzqv-&-\7>VSYg)u _q?)Z1m,4XSĜF=] Dhk|p.hDTy%%9Nzǘ5wr@.IZعp֗a,{\*\]:yd-3{d?y/d}ofam[nQRokX8܀7.w%;ED9* v=@EYCwǗ4o%;Dx ,n=|Y'aߎ}&^KlVᓅyDPJMBUY@E]UL!UmLa'P]EK*wXѱ,QDGeC}Oi9Q'b ۢTS;Ǒ%Q$Lˈ uj:,U[!td/I7m^4vtTvaFF _aaG_>=ˣA1=a|^nXkRNXCd]jx( (Jeͣ w]X_%IJ3o/:H^ܔz#u{ӉW!h2aQf }&{_OKP;*KpibdUž+Ӻ`1Gt}Bzb5נUpK:x*kOЫ`!GiY@"BuK}]Z}"1(w}P1j?wX>FMwX הriN|"8O(112f8[hYOM n BHamaE)|RP{Fd(z9l**Ğ#m-RIfuPν"ZOv|˦u]0#itJz/(gOKqnĎSMKu:B\F]˰xjVL;֣dޕ嘬.Pr[/5Źo@dAGc Jɧхc'pE3A|mcFZE}Ɏchy(?gStгO}T|nZD(. z6l}ʪVѼ(?ڮ*vj)Յv\p)IŒD1I >A_QDcjxܤc{?G9u)%8mGp1^+ `cq:t}O@Cr2c za"nqFTGpiG.22˘so '@aF`Žt#)&E3vNMK9^<gک>, ("`ZF]PSdg 9(&{ImN #0@4I_dxKmǮ8߻'ר)/7hC8|S\N]x[j5/iG!+KuEM'o ]Ջ g~,ྕxvN(σ8k,Z|%|g}}Ut׃!=-xӳ's7|C$zOc|+WDǭY5XY$lXGf8u?;%QtD+jbw'cK8ƺ1,piLsFE< .y]ijKQ(Cl1jЉ-Ӳv)C6h Tb֣h}!m3!"_̉y %1*f9K%DK$k0u\~:.KM!{t骋tWV'{Pk4*1@jM請udiHbj;mnP83T}k_CIZ҆OWbKVgBP(^{I6!o@3L?%v۟Jh]?(.8ݗeQ[㗶`qq%O9GjiCaeZ!F6XזC~X %RKne8^;/B-v 22D$;6(5xAܷދxixj1F&TuN N+Ьj;܁--UK'{,:*W=z.B?KNDAn1zN]%`ƢceyA)H8fJt(="S/`KhJ|CkdeT3Go$˱*)ʗc Eaj>L&"Sd ;''Av|}1 ]OyKI.}SW6Kq:szx۴bjE p΃c}OA= _HCPI'㚌HuR[[|_g;=ԝ–GKԾ ON]8NS*hƜdFB`Žt#)&EvNMK9^<gک>, ("`ZF]PSdg 9(&{ImN #0@4I_dU%[Z]Vu=jhiU%pm}%OBrp6V_I;ק~&k^D٦VYٓђv󇞣[yeX.DAs~)bZ\&^(=Qgф\/}t-s/tǶ*qZ)^QEdLub5^H55MlٸWTx}+(:LrLbqSXiC:q- Q\|#}We =z#c檾L$)_Q^~SGKclegh2zm&&(& Q:AWeZkU'qT`")4IA=&r67V9DesH| H_ vׂ:a츩{<R샜xy% 榨 "jRQf33|BF_: wPjS} KJpt ~!Ս+%8 1]+ f_qZ^,=oW) r5<Tw%8NLz01 ?蘬cF2`Žt#)&EvNMK9^<gک>, ("`ZF]PSdg 9(&{ImN #0@4I_dR[)E~i.dW`*oԺVjYJϙz@j^ء b:1tH-_j.d`e8.vm5![ulC×K^^Ӳ/R˾XL 8g2YhGшYr(T~x+KQ,b"P%)_1{GFkkiG8 SfN z(u@=+qz=- X6%/2(3eH}s9Fh>Mo6dë' lJh)DWʨ}h݆}s&T SLM ;7BUK8K ÆdG]%R[: r׈#tkU0rԊ/5/sc,i %AKWN28xu0ҒNSK:-~[JZ>JT`?S/R_|٤ƍDD|Բ36AԖ+m"Hd2^e,~ X[z!x}CD-ba>,烴A$l4="-G h 6u֨I,4wk?RH3B+=+p u?{m^z&⇱|-B$BIl\[c={ 5 =t?&ܒCj܈@LQg)2^kiękom "KҩsOMFF}.gOZ7/X 4\;!)NuBQN@19!؛=#%+>nqc]}u2N)+柷k%q'M}(tg_yϵJ=oW)bnxܤc;N῝H~v~ow u#0&H7: ',-FujZIO牴آ6QyNqdI@q 2tN'8{VA07Kҍnu2̈́]F`H"Cz& IW3A/zp]GQY:?AGׇ?k}N ;`06JNvDVn\ڲrFcom#,q _.yK(2+FneNll@r5asp"P3w\RvS.Z)"2JRK\d,L6E\PPti|Cha$nZv9}:/BoA|4"6h^{nnƹV|GqЁߜ?=cܶ1+n^)&[T:ץqL|T\Xm];W%<_MrtR:tT[-]: ;cp#V\>Kе~\Kj%ӣv o:2XS6XD:}ߦVq-*Z!=)&㵧 D>O/4\V{8Eoaꪪؑ'"F5FK&"L |x/Fe*"|^"BK.m*.gR)ET/PyoP0 G.%2 yP:>v TKl,넑n^]T`/AIޒްml24;}=l2n9a=u#X:uDp1ڸcqS7{%+>¼ݜ orJ7xsmMxu9NߵA7]I$F7{OJ=>J y9ކǍ76:+t!Eg|ր%F`+&H7: ',-PujZIO牴آ6QyNqdI@q 2tN'8{VA07Kҍnu2̈́]F`H"d=j }M,Iw 7?>a|ӫV&}kC͇jywjKwA;1{xW]gPa+hU-gT5A|mr[JPr#|ܗB.Cp(Th-I;п̌AaL\yqcW{ LqSIF9.kBNZM4XbAKV!Oe4NhY*Z2KުEF9|n#OYbLЂJFyq"faǎ CT"<Bie?eN)QęS蓌m 8i."7@@긻-ZF˱<} wmC؉q~<8{GW|"F|R |bI>r*t RX@F@-d1e!ɈDML$|ksϻyAYwZ;ɻZQ*Vq|{E&9W"r1jߙ3=I 1t1Λh_{ڵwtD;E jLMYK+YGuF`/&H7: '.NަԺOآ6Qmij8$8iQvAmNge{RrP=L'MJImN֙` #0@4Il܏ߴ"׈O (>xRH޽n]i{1N'ΖὟ)jo_/_{މ#g~=^=c1J^V߆_.yǡ,.GC}9z ZPD uŘ; ӳ#k`.|k9'S'NQxgfQ<,=*d9%$_[Gɭ;zTH6S [(mXtڰЅV6bEāW`?*s.{s7:yc`}C=nE9 2r-yRbmJţCY8 zO(ΘHZ-uO%E oT'޹1'at+}R-j!0<$>tNڶ$u,ez@>쥨--ѿS4%ʓRވT|h*-tD`xS;Q܈=o>oxhg2Tkw\u:1fI" 8j(hxXUhW7E]v_i/ `L"z襞{Q~o-s+g[F}lkNcG:jTmۏ[˜=X#<-&bM9kҏS"\VoEYN5Չ=Bs*cLTaS/5SڨQ裾oBfύ)BpJ:o]םVC8~~?߃$ΖxXǝ{gZxy ڕ~k?ޥv=%y,Yܧvd3ܧ;Oe޶x{qzCQϛFONŽOH޿#Rz1Vz:Zz7?'~c]9ˌ#0LntNX]?Mשu)')U(YouEmloNqdI@q >tZ$8j+zNڛtu3a'DeF`hb}~+p:ea<=cD_ygڇCcYMcB[:jlC5ZBNw7:Eޚxkvb-NAn99L_0w!Q zkӴF9W㤄BZ"ɸ4d%>qGں:uZhvV)F X1۲ Ǽ] ?bE3UNY4їhza`V|kWB>]+:_?_JЁ-/cBi#r({=0 c/Wk?n6CP*^'ڑ?{"B)\IiEq,/,?pM1"sP'Kw5r<>W"_LѿT|JxZ|EojF` {>܇{=\p^|(-PP@%z$a?X g.ķz.K+ ᷕnT,-3~?p3 W)3K[nܮ=uHT2"9ezne,ѸE5QU{:=\4?k i)],VlAv2مGniA"71>m%o,GgGX Ck*$%V55PqR(׌T^#ɹ1}j~g 촥lz1槛~y VFJaOUH }ߛv7`c}!ˈdJr|G~ s'elEp]>sOq fK6i<`~u]+){ܧ)=µ0alWa{O6sMK)7O?xotV4Pt+#8E_g- B9 3)"2F`+&H7b_eNשu)')U(YouqmQmȒ(}D:IqJVA07)tߺm^:vLTvaFF Zxb}BE'b"l xc~Śgփ~em.aG]ˡſ=5_<Cr4A,. 3I)¢3E#*˥sGG>?3|bh.8{qoۄu /ڣ(FdT,BwqHuTC]G6S)_% \uY9wM]gvbĠ)X/j+w&\u୍0n\/ĮC>gvRWX O`C^̙ o,ݡzn(TY(/XC$z##xR^11j.UN-Hz_xěꉦ%Xf˨KjĔ/CK{nǨ_= [} ӟ0Dn@ :..aG *a=LO:F ج¥prsߐc>eN&,#3_X9pTM:0mqzYQK,-SŽYyR_I mY%3,?ct^‹hm$4yVDypb:)y E}hYG+D3+=cn;蹿D|)_q:PzQ;诘MEJM1Uo s(3Ԓ/v&|vDi|@IDAT( ?&_ tQ7Kpҥ9QUcRZ%!'y"&=>{yI2uʨwf+$=o^ u:wx'~p\]羳G>:5Qz_I _e}:I1l{߫>}|;E;hG+O?LBW[X (Z= g+0{;֖yzp_QFXE躰wtL=VREg{C0̟#03Ln18SRNR>Q&b ۢTS;Ǒi$ʹ(6]3ɲ=N 9&$Az]'Lvֲ?#0/2~f Z [{vv.Ÿ'@fȴ=aHԬ^f3CHiwC>i:񶓶K4Hu%^r&ޏQw||~Ǝ8- zTVn (|a8!OqA"\:s>ҋBZB,,;Rzg>Wҋ) ȴ(8o-f ٿ`'4**Cq Bp Hk("MQgB"4.M c p6?g;A ''ۏMes<1r݈>nxsV[woExMM^ sx!^p;KE!#[DKvaָ;iweX]NJMIFdE%Md?~aȁ$xbWnH3K?)4+q?ӯ57+kv0ٹtRNWNp;X쏮7[9+jv=V}{>OΐV̟C =R'B[fQT&^礡sJݯ~ 3X5q,y+7^9>>J4u!"[.?vUcuQ [^:R2iE #L\j u MOԻl![nQpktO/K0SD$Y')(~␰oɈ>j.x`;1 eS:7I!,[&G(Ur=Wr\ Wѓ)'>^^{=NVsN6|d(NY7{DHBD[sioLXf.$5*Cgq8Ya~\+dW}v"tfiȉo!3? KgB,y%?6=X:oN/$7h|vw:sftZ$8j+zNڛtu3a'DeF`hBPڵ/Ԕ1G0Uύ \iARI1jz[p/zjܵchMCg`FF ;>ҍN :.$%m.(MTmک>,H#n}D:IqJVA07)%F :YgNF`|x]Ne0#Mξhu_i DKɡK}:&挝S6Lk;Ape_nxm+}>gSww\9KI u[aZ#=_珞cLJhQ*CW^L0F~i;jG!Ey?]7(OF_su[W9`+sٌ{E+h-jP1)L;cwg_[dxUBLQZ+SO>DCJP} w^M'A(~d:ǫA;q=Rjwipfn/ Fd[Y]H օ5(s)S)xѨ-7E]GJ3n`u&xs=M/ U42.0B1y 4G ]uC5Pk1!eKu R^e, 84Ram>3d*9ZkMQK5nrei\46>M.F'>= aVd\ē J3^(g^TޙoŹg>.]›*E'GQY!R˞-'|ݧkv4J}@l|CR>VwgK>݊՛5<̹9GO/T+p3_:+tCiNYl9RHJuMR}R4:[q;y͵0ӯźQ"Z:zgo뭬{Mkm--tOE={~}.zʎuoGp} ceԅsM ࣏h'z܉F =蔴^@yZ4^цsmUnȹ*(b2 ƌQ9/GyL#0Ln1Puj]IJ'JD]lba[Tcj8 deԦz&Y)U[!tޤ6Hd ;ZgF!~ɫ5F_͊ Kw䘸hú4 b߂d#%w;{=\cjTc n| Ӈ\El,,[z-R-TF3%K:|{c#`ٻzxzD6lô(+f]8fRb~PoC[zk#h#ߧvu2O܂uZa\Άe1#p9"F'tZrR6Q[&?MTGG@0#.MשLlSB餽I)I7m^:vLTvaFFaFl}o;Õc_R;TS;xGZk5FjDR?ҩ̎( YW>c/W>\,܃[p /LKauaR^:;HO ^OQ`>Wi+je7Lb~M<*~\Eq#`Pejp00R!Kw5rR05`˿@rxrLĬJs}J=sW/ M{+0`2tf6mF`.#bq u;]֥T}dM&E>v#KH eԦz&Y)U[!tޤ6Hd ; &*0#0#0#D v;k~F)s 1OQ&b"DȒ(}D:IqJVA07)%F :YgN.#0#0#0#0#0#0#0@&#$Vouj]IJ'JD]lQd64S}YPE(6]3ɲ=N 9&$Az]'LI0Qم`F`F`F`F`F`F`FȄv|VmNK9IDz-lf{۟v#KH eԦz&Y)U[!tޤ6Hd ; &*0#0#0#0#0#0#0#0`ŽtpZMשu)')U(YouEmloNqdI@q >tZ$8j+zNڛtu3a'DeF`F`F`F`F`F`F`F LntNX]@:.$%m.(MTmک>, ("`G]PSdTmTI{Rnt u&$0#0#0#0#0#0#0#dB ;>ҍN h6]֥T}dMEjOS;Ǒ%Q$L juj=,㔪a:ioRJҍnu΄]F`F`F`F`F`F`F`L0aGI8auަԺOآ6Qmij8$8iQvAmNge{RrP=L'MJImN֙` #0#0#0#0#0#0#0# &H7: '.tZrR6Q[&?MTGG@0#.MשLlSB餽I)I7m^:vLTvaF`F`F`F`F`F`F`2!F'zSRNR>Q&b"DȒ(}D:IqJVA07)%F :YgN.#0#0#0#0#0#0#0@&#$Vouj]IJ'JD]lQd64S}YPE(6]3ɲ=N 9&$Az]'LI0Qم`F`F`F`F`F`F`FȄv|VmNK9IDz-lf{۟v#KH eԦz&Y)U[!tޤ6Hd ; &*0#0#0#0#0#0#0#0`ŽtpZMשu)')U(YouEmloNqdI@q >tZ$8j+zNڛtu3a'DeF`F`F`F`F`F`F`F LntNX]@:.$%m.(MTmک>, ("`G]PSdTmTI{Rnt u&$0#0#0#0#0#0#0#dB ;>ҍN h6]֥T}dMEjOS;Ǒ%Q$L juj=,㔪a:ioRJҍnu΄]F`F`F`F`F`F`F`L0aGI8auަԺOآ6Qmij8$8iQvAmNge{RrP=L'MJImN֙` #0#0#0#0#0#0#0# &H7: '.tZrR6Q[&?MTGG@0#.MשLlSB餽I)I7m^:vLTvaF`F`F`F`F`F`F`2!F'zSRNR>Q&b"DȒ(}D:IqJVA07)%F :YgN.#0#0#0#0#0#0#0@&#$Vouj]IJ'JD]lQd64S}YPE(6]3ɲ=N 9&$Az]'LI0Qم`F`F`F`F`F`F`FȄv|VmNK9IDz-lf{۟v#KH eԦz&Y)U[!tޤ6Hd ; &*0#0#0#0#0#0#0#0`ŽtpZMשu)')U(YouEmloNqdI@q >tZ$8j+zNڛtu3a'DeF`F@]=#^3xcF`.::::,>F`F`F`FD0aGI8auqF6]֥T}dMEjOS;Ǒ%Q$L juj=,㔪a:ioRJҍnu΄]F`F X~Z9w ,^lH8)p rWN+}"$ uI9^H/t>T."ssqFqzx]ss-lښz mXpGW94/={D^_E~y}}6(^Yu`gܿ{>ƽ~j />Nt Pz+m3{_='2[:yWݎç+u_}z]b?婼jO%<(F`FE ;>ҍN :.$%m.(MTmک>, ("`G]PSdTmTI{Rnt u&$0#\4xw"z)X8xԪ||jTlN[\Z]밽y6ʃ 9?jvwGnh_2CC:hykUmqLcvC⑒RT*j!!_٣it7gޓy-<xX:kIqXT:!nrgްS똹K0)%:>eo<0|2o{4М)\K̷?oEx| Sְݿ:;qDB{ lOk;1ͬ:gKzW_DZ-\|s&T؂ClnGwbưVz|3jkk(c/,B=(#~4?]]0-5 ս][0os9i^b.꽥$6.]1p#L}}* =[1cZNmNgގj(|2!:g ϟK{sQOYrr"WASJ^"vdwF`F@ ;>ҍ _uj]IJ'JD]lba[Tcj8$8iQvAmNge{RrP=L'MJImN֙` #0#p%#p "ռpQLh`:X#"M-[P5VZg~ѧ>̍z"mqAaQdi QK( -/1وQt1 J^ID&wt˥/[w/Gh8AiCI|nڏcuEU yie>\.ywZ_]-u*»nue_[y<eNM1:]2eH7_L]K$ GvT&;$…kB܅OV$uaVQn"l ;WxNE_{˹xxtgGDV y0?5p^B0w]voØ{iiu)c{VI7%0uͺIގ{)FӈuQ2s3ο[TK3KOc3tn"du|?Ͻs5|^ݧlE2+ٞ7g~;-4ԬÆZA=;]-g!Az غDA;{*W<[Pj&/ ׅa=r~עU^ s"cRd 5KtU͆vȥ;]'Q6"+,Y-ʵ9KYpW{0Wx/ޙ ){K8KjݮKz(axVT=dbNUm[SxpӸ@;[kw✢X>H7gr<3tOVKjЗ@H}2#0LntNX]HMשu)')U(YouEmloNqdI@q >tZ$8j+zNڛtu3a'DeF`FJFlQ m@1i}5o7Z6sT. .YFQVwh0xo[|_rh VZoPŋX\", ?W`2x EM%w෷5t|G&>>aDq lAsk7>=#lzӎ T\ uSܨ2wؿq<-h0pM*!+l÷pN'| 1ឰҧYs%pƽ7=\EJIw4K_H.,Ϧ6u nkRKڛq)|E덼;aˍO:Æ T%m wt_5KO@m ֕:qI4rדw`!4R:l Hþ!t_ aŒ-ĮCgo`F` `Žt#)&ºSRNR>Q&b ۢTS;Ǒ%Q$L juj=,㔪a:ioRJҍnu΄]F`+/{kfX^r03 bs^"}58rOQpqG?Iw%{?e^Cx.سa" ?, 81яdܭALH5nZ(ek"WvJ[NAr=\)Jl:(D"DY8?Y$ "G%:kP5k)xY_v<>Js/xͽ`kƚ+ٞ7e57*}բ:D|F.j0~dZSĜ}#t,XGUNm%DyZQ4~{j:eN!VOMM0ר4tZa4nxzN_1c! j[.t嵊nDkofi[5 ܣ+)U۾BY.ʾX 6 3Fٹ7REsIoE(n~SЎI#REOZ6{gM5e0y+\yOyS\clLN.L'vx%v}3LLk9sI5+6nHjm1>5-+fc  mEsW {in>?ޥ);ˈc1kW%HdWt(s)"tjQ'J93r^;Ϣڣ-ܿ 0f#n"-?mM`<_J>n>԰]&c7:)y\'}B?Yw<ڶԺp}W̕ؼ)p[<#eq`F`!FSL u;]֥T}dM&E>v#KH eԦz&Y)U[!tޤ6Hd ; &*0#0W2&_ jU)ӑ`34KO!k'XL)5=Nx| tقʕ8*9x`Q9ƨ)~ %wvHa9OqzDO.}c _O8s;É3AWw9toAVmv"5qxi;&)\s1jc͎V%1 ;2RŒ瞄.M0Wނф,\P:"쨋ϘW  h~>Dh bPZR4rI_㗼ώc[qwtp$Ap/0* \ixKݯ3!EomÎ{^pѹGGq׷ F^1YI}8ZN`-V'8)[100I8!o>C*|.i tXꥻ(zU$KxuBɱeD֑ |1{i^͢o]37SJ.(\(\Ŕl7-u0 Tat`ɯ]rq%soI/,aRNW[\|bIX'5ߑ=ekvX=ͫGEks06,gfPH%ۀ6+W;Vo9Mj-U0t-۱rlwg/~u \G6R}#D[(}ICs ]bV>6l]-LlG !V"M#sHuF`Fv|AL1/tZrR6Qgզک>, ("`G]PSdTmTI{Rnt u&$0#\|٫@_Oycԝ_Ck|=lNA(xp+MI8zd;*__1@Wnq~e7r _9=~(j5FQ[*KxB{R ǵOXNʟy'@ y Ow#yg,%b Cey;fthNZH/^ǚW_O=/)tRβT/>r<|y03og T- kroT`QX#>׾+ A{8JQ09?D=. ԅlY{ J%rMV ѯNN/y"+"IDBٶJW`q /`*}""҅kcAXRK 'WDL:"' "Ba'~N ?LhR1(^/bU miC};JCC^V=1™bjΘC]4/7?,jB3+G}xqw5]~|V8;\֤SwaJWcIԯ7.9DsIJ( %X8腍fOu SuzÍɣGj}8FJ[X_J= A|;vst\NMG*q9W5bHY~;>ܛo)ǥ+;(Ytʬ{dS&}8XB ^iM:O$u˸£2wxJ K .(pFKj$E͜5CTnlwhzttPj8:.۷Gc{n!'S pa =}g-'_=I:2wkw$R#ªX1μc}?ׅpa$(jUo߁G)0m['#0#dv|AL1/tZrR6Qgզک>, ("`G]PSdTmTI{Rnt u&$0#\|ϮzCm"Xߞ;ݸeߴnj%&r8.g46Jgx p3S#ܐwAurb_F+ǥ@IDATx:bPs˂8^B/)*P:fMo[{=%gtnlL"tӯzW֖OĪĪwbY”z~cv2_z<ۘsEݣO6Mĺ}OVMø%r>y-ʡչ&U;PubK<}5^lJ{3}R.fÊfISA,.{4ߴ SI68{ExGex\ivo&X/ Xrt7Ų PF<=Pvq 0cc*~r"GItB۷ &'!/>RɈ+)SVd7"&[Nʵm^2ӑ)FU>\F`F #bnԺO8ö6NqdI@q >tZ$8j+zNڛtu3a'DeF`FJFb|Kź7@~CHROfR#g/e!n,&SNgw,d,5K95jV꟢d-oU}+fs#幽DК&hQa #&5a+ ufVقcygejL} A}'oԹ=2^qߠmȗ-`=$`i[י1~4oQl1̲8L++ϾB)*Mi[Ԩ'-8k{?(pvFP/QQv/.$ZS~j{u9c4e}">AVThb xڧj(O:AGy޵VU%2oC[z-U1`[FO>O];Pf9*Q1ݳ),m"p0hR:q9< @&O.-+jizD390*)Wd4t0橆=4O>EN~;i'ڝ>f}OF`FE ;>ҍN 6]֥T}dMEjOS;Ǒ%Q$L juj=,㔪a:ioRJҍnu΄]F`+x_v|H>Dk'h |=mECɎӌըkM#K^ĺa2%aok7G~SRTc")+riVwg/ uQ("Eo|iku@mOT>M%4"AË1cDlAK<xIF>Gab(x3VU8^b7m΋X{S@s8 sO|^k `xFw%"DEa~L^VvFRITV#(J0"*lxH.1>5<:tgǟ|k3y^Y񜵐/t/Nd!kz@Ԑ FQvQc2|,uaԓs7-\NyR?򜵓g?*OEdWS|p_HDCiJ4"X.`R5:sӶhnO#^s\v2^sP:rMԚ(m,CWzqZ_Z*.~ &H=n/ub\baחfo5՗WQF9Obɴ"d~Awʹ[Bzo⎂;1okz'[7/W`|ss0|cא_gFl~_utux׷vx|'w)͠L8of1Yw- ~Gҩ=Khm$|kB۰LHa`F``ŽtpPMשu)')U(YouEmloNqdI@q >tZ$8j+zNڛtu3a'DeF`FJF޳o-oSܓC҈x+DG".?/} 8+RfɱYTep:y4m['A@N(//V/rADy,B$^)I{貙*s!㼩Q{ڵō$~Άvwe7ڀ#;_Y nZ?ھҙBμZ`]$)5DҭN96{\%GX4VQ籫X$uRmsR~eIъ 줦R8fγ0Tu KybR,U f b@K (l>gZנVtam<3mOIlaGI[h̽:i .½Rt7Egs: :ώ}N$.aK3?)!k$ўSdvB#Ѻӥz: #0#X0aGI8auަԺOآ6Qmij8$8iQvAmNge{RrP=L'MJImN֙` #0#p%#`e7Ҍ}9Rwq}8zݳnx'<;Q[8nw?ouI0%s,dtºe` R&'gTQlg8?9i*ZRX!Cpkw3~lE;w.)Ug!N\EibՍŤ3(FJFYu/Ϻi=,BzD_ɮ]iBT@{P~-"o:ELٸ+ֿiE[,{Sx|'O3xtZ$8j+zNڛtu3a'DeF`FJF xoK/o/Q7`,bx7`K]s<",'7Vm~r)i5kʉX&/??C'4Ki:BnF9x`Q9ƄI@ŚŠ*)t' ~c(W]x;Šv!0:o]r:0^D8mzj xhǝ{gZR@sKb3<6ܯ{/Dl7\Ҏ1s+^RaJwM2 b}" Oij1,ڏ܃>;f}PE_ /ij:b?%Ʀ,)$NWC&9h݇!UeUROsivJb;wU FhSMX)wu[;G{p#wdh$eJBYG@ǒh4""P+E7Ri br3;OnE鼬nJ)5ĉ3{րt8-=LRa鮣Z?c*)b܌:1hAv%VkF=OaS5eLԵ֗OOu1чھ|O#Sb?gaWyłCδ=a' Fي~k v[/זhzO- }OF{K3ϱwn%"XݧDlLrga^]߾R=珂1?f( cI9*))WǾ!mxفwcsij^0#0@LntNX]:.$%m.(MTmک>, ("`G]PSdTmTI{Rnt u&$0#\d?U?l  w/ǏlDU4)U\׹X6F$"FdE PRP ],\ )%1;AbHsǫyRԜyq ư $Ao/EO/c^"=Ggl%sgRz&1t p.wY?y}%Ro>xP[~?efdˇWggq kNAfCzԆUqP#OIJp~dalCDZF`FvbH7X"SRS>~&:Tv%K!έMשxlOTmVI ۺu3a'BeF`F@/{{[r|&W,E:<cxHB_o~)/".Ԡ a_qA>GGQ9Nt쎉N:_NnsT RbXvL::ڪocF+z"qY1Yru Œn4c2s2PjN&K:}jEF9] 9 LB #MXo > VpR6 6e G#;tIX##)b"DM0I\WQDkI>'FAs΢j\j @Dž>>tƢ9F,]DkQY9wCR׍ nţ݊ͳ"#'Zw*VE /z~XD,^N4"&N#v{z/yBQ'7DNf=HC>{g2zfw aW w;~uw2o.w|btw)u M]Gn'ܟ*sgUYbޫK" ;z +ڇ3M2Ot\ޏ`~r.%UF`FH&Đn1%nԺÔ8<6'cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`>єZhg"|z_sT-*w5( ǤG~Q(DZQ lS1nLL)E~ChҩWOr#- O6=-GYHd1nq~CzH?$l;q`W?BPbF'{=<&+k!"& S(]HeZ8^;kQQcL"W-}xA{xtҲK7 ~)_OiKGDy$u~d֭u{RM6 73 (Q%:C,ayf/΍IxcVJ97}v,6đf!ng=nr ^5"Ц;(>E3xv<J[IEdU%JdAAY>q Nf\L#tMRkBċGK/տr[0v#1=o,{t<\j,Y[o;Rk1l͢T3֡l[:xw.\PjMD2K*%|Wn݇% Ft٫N( ˔b,CI1k(v{O!eʎ/;L ayz$NT@MM?ƕ [FءkFpZ9#({ hI1|$|_1a~}r 5v{z bL򞘅~}>s'a(QNl3DJO -+3_r\s8{/d(+5p`|>'ZcRt٠w .}|;~>Iæ}?f:5_^݂ggڪsDXΒe<\g_G[0ܭeI`F`D ;1AL _ :.0'm.1OïM jX$XB>tZ'DJVnu/RJҍnu΄ ]F`3An>L):`5UUnMz We4N%Z%8e-mèVFd 6t@δNNNx32|m{uDQ&ymMJ|ƁjZwWt@)GC8ascϧ:3r|}AFJ΂4ˉ[K9A^%tm31lᓽ?uC`e4O)h_3}s/׭'rfChq3bӉkX1@\aF`P0a'tpoMשu))U?YouqmLo5cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`>8-/{=2u4MWDZ{Z3T:0VHDXlW6DZpx?Pc8˔4Lp' eE:+4?7c>q|`"btx|U &ʟCk}V%IE8dG J3h$z]88KWy}b֜h5;XNކK%aړ?s%y~^{]֭/Zk3Rp};ou}*Fe#̑eN#oGv`}qNwd Kee8K؏V.R[ՊYxmTۄœZY?Sט(T!FDڑV\%:9VAQ56b Sck9Ÿrl5TzuHp-)gmZt?Kn}c2/H9E7,7t׳?EE]CB⿿v^@X]tqzᅿDϊ9WޫJ_D5ʲVl~lZ1Ge^5&gc^34%lItY!Fh6!ӖŤ(n!?iI SX!6꾂cpCSyPY7w8xɜgBaAkE3އ٨m>9Bc\nҶwk'(g??䑱m Kꝟ<v ?ML]Ca,b¡қ ,e\2#0@CĔ SRS>~&:Tv%K!έMשxlOTmVI $nz]'L Pم`F<#`obːe^#i0t툈HEZ7dL*(m'_wZkG;w=gW!=:+xW;N /Cņe7+ヒn|tnDէO_'BIdY(qYZߠasnGs˯]ZC_;Gg Vx%gcqFJt[^Fܷ{>&>ӹ}!>0'd|[bF3S#}` !)&>)W{G'dRޏwۅZ. bIqv\׏$9:\e\@ssiso` ]48grӛ;.?.4wߋȇ$:iw5|~20wAq%8qBd5Y޻lC{W_՗ ]۲[Q5{)y8Q}Sʄ6pLtD韊#gHx{r R>wT~b'&YbnQQ9*a;mDM#uUF ):6#u aJ)6gCF j\A͔4zZ a`9JqjC'B+YyV_MI\ҭNL/b*F i::1i}ƺ5O3aukgbi-[N!?݌t3>oulAE~z) U qbq]ZcRna5-]l-#0#L؉!bJgNשu))U?Youqyz~mOP;ǒ%Rg֦z.#cZ2ryt]>.3'tZ'DJVnu/RJҍnu΄ ]F`F@矽܇&`4F`F`F#~z?wo3^ |#0#C ;1UEouj]aJOD]~d6| jX$XB>tZ'DJVnu/RJҍnu΄ ]F`F`F`F`F`F`F`x0a'tpZMשu))U?YouqmLo5cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`F`F`F`F`F`F`!ҍN h6]֥T}dMGk3נv%K!έMשxlOTmVI $nz]'L Pم`F`F`F`F`F`F`FvbH7: ǫ.tZrR6QƯ6_ک>, ("?;6]ɲ=R[K'탔tۺu3a'BeF`F`F`F`F`F`F`F L؉!$Vouj]aJOD]~d6| jX$XB>tZ'DJVnu/RJҍnu΄ ]F`F`F`F`F`F`F`x0a'tpZMשu))U?YouqmLo5cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`F`F`F`F`F`F`!ҍN h6]֥T}dMGk3נv%K!έMשxlOTmVI $nz]'L Pم`F`F`F`F`F`F`FvbH7: ǫ.tZrR6QƯ6_ک>, ("?;6]ɲ=R[K'탔tۺu3a'BeF`F`F`F`F`F`F`F L؉!$Vouj]aJOD]~d6| jX$XB>tZ'DJVnu/RJҍnu΄ ]F`F`F`F`F`F`F`x0a'tpZMשu))U?YouqmLo5cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`F`F`F`F`F`F`!ҍN h6]֥T}dMGk3נv%K!έMשxlOTmVI $nz]'L Pم`F`F`F`F`F`F`FvbH7: ǫ.tZrR6QƯ6_ک>, ("?;6]ɲ=R[K'탔tۺu3a'BeF`F`F`F`F`F`F`F L؉!$Vouj]aJOD]~d6| jX$XB>tZ'DJVnu/RJҍnu΄ ]F`F`F`F`F`F`F`x0a'tpZMשu))U?YouqmLo5cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`F`F`F`F`F`F`!ҍN h6]֥T}dMGk3נv%K!έMשxlOTmVI $nz]'L Pم`F`F`F`F`F`F`FvbH7: ǫ.tZrR6QƯ6_ک>, ("?;6]ɲ=R[K'탔tۺu3a'BeF`F`F`F`F`F`F`F L؉!$Vouj]aJOD]~d6| jX$XB>tZ'DJVnu/RJҍnu΄ ]F`F`F`F`F`F`F`x0a'tpZMשu))U?YouqmLo5cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`F`F`F`F`F`F`!ҍN h6]֥T}dMGk3נv%K!έMשxlOTmVI $nz]'L Pم`F`F`F`F`F`F`FvbH7: ǫ.tZrR6QƯ6_ک>, ("?;6]ɲ=R[K'탔tۺu3a'BeF`F`F`F`F`F`F`F L؉!$Vouj]aJOD]~d6| jX$XB>tZ'DJVnu/RJҍnu΄ ]F`F`F`F`F`F`F`x0a'tpZMשu))U?YouqmLo5cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`F`F`F`F`F`F`!ҍN h6]֥T}dMGk3נv%K!έMשxlOTmVI $nz]'L Pم`F#ݍ))|0#0_D﮻x|Ό#0#0#0#&ĐntW]`:.0'm.?_mS},YP,E!h~vnmNǓe{"j+dN)%Fu:YgN.#0nx{vifFp6| !;<C]w\f=ii$qz ;ѿ*&pZ&{mnl-㵗87#v#GO+}M<0u f;lC!rׯ2 NǀnΩz*+İۚӅ30km=QoEZtQ,xvl8܄)Ab߈jO'.7"HaxM#WN NBzSs^̞^""d>X<96$O`F` ҍN 6]֥T}dMGk3נv%K!έMשxlOTmVI $nz]'L Pم`FmoOww߁ϬFP>sAE+tq-NJ bFXW? 81wrG9j njjM9% gaQp=Z #wz[O?W'>!^W^u GmpFW:]S d|epצز"(rtf`\kaX%"xB!w*LKxKp:iXS8=;?xYP m9FkEYO,/z u7㥵[_xh)FDo>"+u<ONͯl؝g @IDATC6y?m] xzs[VZ =е=al@':bgl'1%u" b|Gq_}q{QT#MkX4wk(gsW!kaQ?Ɵm@01)fg\0Mqc_'78e,q6;/.ϫx'$i9ڋWc1| ̙4Os {nQV=2 ݙ?܅_gȸwF`/L؉!bJbyvNK9LzCkS}ک>,H#Askuj=,)U[!սt>H)I7^:v]0#nDΣW]5bM\lUW7l(+؀^O#F퍨\W:w8%q-mՓ*PvS',Aq(] tGUE5.Ŷ84Y3 S4?_nݎ#͝ccS.v7*Q} (~+5+%kʞHo7|n^[~n{x=7π}1xs0#$vbH7@^uj]aJOD]b^_NdI@!}ٹ:O퉔^:iMd ;!*0#05籹4ɔyX#C)#Ga`\9_tod8H3gj+I1v?ۋVWyO#_RvQǭTLz|6~oWvs jgeg<ܡjo|qCNM{\{ra?zTlփ 4އ[G@5?`ƪ )S\E:(gGu&a(_c,"TE˕b!ksvXQ4oFXS}*F ޤn9VEQX.0^iR'JC1:" g v=dXWk?{w\mB?mx&;گ _셏AmE|>[[;0([BQڌKL+ʁyջvL5Yúk0upqߎ奨W, 6 Ӎ!ܸMueNTMIrqy[qh!;]owKɣ5BOp?:KT%d,1yA䃁*„h1וCQ+A2;qm|隉 Mʢo;>#LG5v~&WZSt*o^n}F]`ƺo_΍+Qiaq~נs(N]Gx}~Ǐ}wS=hGLC|lM &{n܂&_[~n{zߢ}ޤ7r#0gvbH7: ǫ..ަԺÔ86~mNdI@!}ٹ:O퉔^:iMd ;!*0#05iFܚҸD| Atybܕ7txy(%D&ʙ9lBź]h,ñ-W(5ӆ>iy(Yؐ&NAIk^=`$ Ǔ4g@"i4ģT:bbT48VULUNMDDeb䵱 ;XRڱXzpyk0l8 N^{t}[{u^t sb-IuS:uv"]miv'_DI@D[p\ޏ~U#=fvԭ_k$u"oģ-T=c%Q \YJAav<7sc3Vr! 6+:隥,0+aWZNv 0irCa:"|0`!;L|]v %cNk9wJÅj^6L{p[4aj~^y ox=OtJ4 K4+ouA!bI K,re;Zg󚶼 S3\ bQEXūyS}lND^~BۋWW[qV`C !i&)kcv| C6"\j2\ܧ>w[oNkFYמneյ-l)fvT5zA҄6~4.}l ql !8fNDǃf䖤YUc1}cl':TP?D9; cql6a5O`{1<=̝goxAۅ8pzL{ӇcFc`nHۅ4GV/p|F<8c. 藊UwiGѽx0{H)I7^:vB,TvaF`nk}akAcرLUS˙4p@6%OcL;ޫ‹ Oi鮢]a;[B9:n7 ;^-09ff-QiJف>}|7lJ[Ua lE\Ah㰯O&̦B+ƀJ2XEalRiDdϏמՕMd${Y7{7o5}Ĭ`0b"N!u^H#^>if mQĜq#\AtڽX!VmDQ5Tokx x.3NjQ2'4$zZOV>fN<73Kz EѨwب<8 đh<NLh\1eW]_/ Yyn}do'j Sagi[xY)my J&he8{6k\S^Q-vUqCk'kJ1qZ ^;7?G%{dEj#wAY^<1Nk]1N7琽sk?&eQ5SRQB:Oe>\ >(~5,ə~c i&LoȀNZO,^.œ]a';Džun;g-1# j#T?2d- W v; Dqq¼'+tYgVaWC-h1||c91a]_m?ԛnm,y_C+?;Pe `'A[u8O0}nb~XJO qxX~F/Ruh5{,vъOnkL\laTxTy.:g"ۍvl2ccWF`F@E ;1AL _:.0'm.1OïM jX$XB>tZ'DJVnu/RJҍnu΄ ]F`@_R%"d= O=<2 (ݰ dCSc~qR2h)7퍵XW`d>ܢd38kuѯ*EC\??3VaiD6ܙ&FDQ/ߟ>M,\3+f :xv_d77YP4WϵpE;-x[$AQ֔G_vk7|rN0fś玠s77DuS\ިtkwLbQ8!iG!noaG%K`Y-WnWg'g X4ڱ} ,yUtcKؤ禺­'+:،|I0]^#Ys tiR.z; unn4h u*9>:O#J씢l:]{nuuv`Ÿ;q MX qRާ(:ՊDtOqןR;0+_İ󳯵RjS 5~ wU\e8S`o{:r7; G"Z[wNDv̧Q~5K΃(SOnF(lk>Ferjm"d3Tm'7&Hqw$#R (bӲ۾YћF3*fʔrno)EmZ4ڌ2FCA̩QӡtTSc,]ؿ+г}9 SRA!QsRJgX'ͣԵJ{)$a'ڗ)kPxHHn!&fߏ.\9ZpT-1gϏINeF`FD ;1AL _`:.0'm.1OïM jX$XB>tZ'DJVnu/RJҍnu΄ ]F`@_F(rF%-gdx|e`Dy =,z)HN/8 lj3Q*Ecȟ/jg|w:=밫ltN"jKr/Z!t3MvR$Eco2oTbqW,}obovӲ?5UKu zq;:hSTH4oo$>WL:VB+ST|&u$zUSڤrUD0$LG$sTaGZxk p6ѵ6wl@7}Zq^NLw'orid%oHc4h .FM>m/Md(;>J& /A)mO)t#zU'؛z'by9ZT.GNiH~Z]Ϛs3"K"t+qxwrGuJik_`_uLKM:q5l5b\| 2w9ѣ|ESqNc\p՘HHbAg(ؓT}^aI"ߡb}/nDCʝ;}iڞtR|wvW:",mB՜QkXj#:[Y} Eߛ;?ų`cA\ߐo{I1:PS-U-!﹀U2Ѱ/z$.%-ki,Ĥ,VEQMgUѷRrESvѼ i^c}x&V\kB("Qm+)+ʐ \cy8v(&qrt8ȶ3K1T>*cn=ow͌/7 4t̠5=8jm:Q3I1HvwgqOxcl5)r䢙3DZgW(BORڰ1I;=7h>DE׾xUʵM殟 "Ӽ뫿7wRgzυll:K wMǪ1oIp *wcq$lzܟmγf4 #0#"ҍ /ptZrR6QצS},YP,E!h~vnmNǓe{"j+dN)%Fu:YMG}/!.vGy Cq#0@_v=Qt~zXU9_ʧ9K[M$a).p!lt9~I&bڒe:Iqv4'נ`[b˯vng(+Ѩ@ G Y&dcf)&޵ a'{_vU%%Vz4 H$&gxgkO]ambkE1VvL@7CM%Z &k~JA-eh0KOI'ڛV˻w ">}EoFn>n%Pa$ un~oa b*=^TZsYQ2žޅ{zL}>7zh|,m BmK A3w1G +>;K/)bJELѩSMv\c/vO-5(-EnA(JEVUuAV,V>X w:& PP} pzz`1v~vakITZdKVZZ uDZy.u5܆9cMteAD 7&I 9Kה: _TYTY;"?ҌuR994jV)jyepļ^NE9@{x?rMĺż\?V2"{1aHHSbDKg/NOϺ9{M5bJj{70"aM gX6fEz+.~,\&? e:ħn|zSGӸrelkze+ұ/龧?]0Flh,Idé1B}kBgu(^?7oqy(J}5%&z!ڎ`SHn:?;(=DI^39zE"Wt\0#0@\CĔ SRS>~&:Tv%K!έMשxlOTmVI $nz]'L Pم`F_ mƖV$0_p[__[SLa]P4v/ϊqTL13sG9aί ҏ=O.Eޘ!d׋`O.<$Ohv+?0|֟zF'hgP;vQ[ #'FR "8B E<*˫͢hj2O9ʫn/]Q; g kOAG)ȢG<_MI+˶#/Ό5I8br7J cqt- x)QfGnkcN7FzVnM G֏o0&O0m{؍LI&"S-#`iFO8M[_6J𥉉(ITlLfZD=@ZLtM~7PfaI aǃiBEHr"D9Sͧ496dBR>](fh<=J4sNÌ;rGtу}\=U]"5q|ae˲|+#R免c0rmyYK,a5FQJ; 'كEn6<}z&⩤N, n72#0vbH7: ǫ.@tZrR6QƯ6_ک>, ("?;6]ɲ=R[K'탔tۺu3a'BeF`FF /l;_>D'(҅O>{.uoН$DzRj #X ImtdMo v~_W44ѯeGF귊~{hO5+t $>hqq_"%BG{c ՘ :Oh< zmllkmWzGLݘ4u%~AOg|>RJdH |@&xoW6iTtjͦa#64KQ>KmIQJZ(%P7\pQ)=޳37GZ/4rk+>c\ϙ\ok_4HM(۞J4V/C|y W;~fx^sT?esܡarƛc4lϊv-znT?w!Oj(Ȃa 6QQyQpbR3țh/(ڋ{tl!TӋ%( {9;rrYl5%òZD.SdAFdocב1}-ک6EN {{RgFJD QlӉbKt5F P%$NHirK$xJ;\Kn\ O?%m81A<4f4>+HuIr[),ó_WR>oznPF}?\r0rxܞ'}]jUr=@dwo `r3E~goq|qFfF`Fw&ĐntW] Mשu))U?YouqmLo5cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`nk~a}u;^B}SyRNF"_pwCEus੉ik+P#OªIdC!5.6;1(E1V1`SRYTX͛Sk}}ן鮌{W~?g#X_/Ըr 224 ݣ[\g;0%]V7]#5Yꫦbr"M'(rJ+\lH?(OV4ʏw5br_36䤅rϒ*\HvP[J'Sj?3Xmoy=R2JXE(< bǢ/Lj,^;g2>aչi}{!?kPkx$sn,و~)3gT7N{|xd)T.⍡'F w9n4yo˳,b: W Ӳ~x%sPieZznЇ\؆^J ʵ Ns+׏z_/:،|E6M w^<,C+M?&$lA$72#0&Đn1%nԺÔ8<6'cɒb)BAskuj=,)U[!սt>H)I7^:vB,TvaF`nk|aFAJ&?] urfl9$8I)z Ox+ x9j>-e`сtU4sw `>X)D)"ˆ[I'ēHҋ^*xѣTPt?)%Y&dc8h ^ tͅEIO":(5]z"riPCd(Sn37sWGJA׊Dh poԺ51\"HX>Jb v{?ch#')"`J#2hI!ȵ" \TVVC&c#9,R^%}#&EL8G:jQ*$GJ$pQP ౹6_ ff^&0Ek;ѣ}f9G1i`Y*S*d,A݈Lt[)gea=VZ3HdjF߉M9vM4Q܃GǾnC'il+$h#<(7T5N[w GM{V:4_CF%Ys.>Vt,f6.5H)a'`P.{M}R:LH H#.LyzEcNi=m \o =N2W^^Dگ_G%{Nz;p_ZC󘰤. ;QJ~൞X8 ʵs}[~σu'N@U=СzSҧ jDDT:/C/#0#&ĐntW]૷:.0'm.?_mS},YP,E!h~vnmNǓe{"j+dN)%Fu:YgN.#0m@/lϿQh"/qxs) N"TRԜye(MU67n _H6 U[(?eG0yC+adj2Wz5=BdIes]IlL&̵!`*(p)EBy߶||N"XO:q`fC6ŏŦ9s~zזH[c~ݵ sݴF[lE~,_BƬO" H<._5ZXb$ wE1Wp^+;%W!d)_mG%Ff&hCl7/pׁ҅sT%O,D/FƞM{֗GXb^~w#^(?qVk7y64Žr@]B$&\A&C/muV /&IK GqB-A̯6A//&T&l3t^+Gh͹yu~ ږ}M0sI1,VWn/+ړW!*AXvL::ڪocӆ&qZ&k(tkQRs6%Q۾Y&“rfrhQN6'X VX.s1UD+&"]!o}8P1|y?f̊eZH)vP*_gjt1egQ5Gy15ɦy#ϣJZ&Ȕcr,YFqQnC zDn{'p)i,밀/x&b3Jcˉe|L#j"V.xc(v]ʷ7&ʞzXB!RNOO.9+D%g= O=Lp{nv?XOɬ:~H8 !LkMz؄Hqu FsN_뛛14$6b'S$eWㄙ}8H(%hF<A#0##ҍ /tZrR6QצS},YF=g֦z#M289g'/{ h>n֯嬭u\>OL䲨_ '냉tۃ_9^om[TU/S*g&+ |, ۃw+j_?kzf Eqįh+)ҏI.R0Oa_NAo.'Y%pFpKذZ3iUStQoiYx{F 1#%ym<2S5ELġ؎ڊuѵU]_d>E$l qEdb1d #Nce)L .HC\ ऑ3)jONLǍ.CRW`&H-!!$`ξYw}|uZk}}{Z3JN[Uxu@]y<?Z0%r~'Squbp׷^tAǻA?oE"u<cmՅr7䲏\=]Gؽ __Opo~wMڹM\+{tDZas~DShY} o~l[g_?B&lϊE!hnY?FYoœwW sӼ{n#:[r'ȋ)[W_fM:wy>޽ B3ea)'3;B>+~l䢫;n\7/>)ճPFvxbt#1}V̿C _%G=˿VηVg>!k_{5kk7!%/<.wt\nư?Nԡ{O~ 7(\'rGǫ_gOw1v[zٻ;eǞL~n@]ɡ?LzYGAOOQnyQrz-LXy񿔛.H׫_#\źiCL\xí?;ٳ=]_A>G;}.;_  ;vwvyw3|Wy1]* rEgL?X.l9儷Ɂ/?/=~q|cv]=_.Klk8Zܗc:ϭ+ p:^UZuw$sWn64f~RV.!/^?+'  g%,@@;3C7a0|L){!.I|tz\wzm̮>CB~Ow:pv#_>MxZ^8Sl{ɭ_yr.Q;*m}~ֹwF.=U.rMojW0Žxߺ+_~ߵ%ܯ'W41[Z#.Pxv:'nNwnqir߻@IDATw0?H<.9p{w)\.\Qu&=uwLdSD6~F9ϟY}&m~c~5 ==&vi2}טLcld{ڳaͰw?..mrkVd] ~ ?]ZqE*|SOsc7`33w2f%/]Ťb<`~q]؟Iz8Fom7zн1mopߨrQgHWUkZ?9{w|[d)zԝ_+"[9[:1gT@6{{dJ<{|&ɡnɏ5_}<{\Խ~p~&SE13}Fa]8П}COޭ?I>xrZZVGOS>vr~n{ 'q*wQO]@PvfnN.~lqjϔb( ۔_kJ,4(|CYmoSy5T4g`F n2OwPP:O\|?{;nnc?N ߹_k.`wW"O*]~ܟ=K]~zl?ހf˟?Sm#<\I,}̿rpݻ\5uvl8ɟ=(OywW_k~M7 CO+W_H4Ƚ:y_u76-TL/?CAsVsj}v=Хuwnẽ{4;3Z?F/v|>v;aޙd6>*Ϭn-\K)Uݻ|xerfxccȵI){ʹiM>ڻS~> 6W}N[O^zS|7+uOO~jC9YyX} Wk9t)OvORm[hL'M>M?wۻ@R~7ʗn^|ꪏ+#іGo'pgw\9깏 [Se뗜-Yzko~,0o?<^6r~];|]'O]=5O~poȿ>י_gӳeG:roevrI-znNatx{Wo~=0}ly{[uؽת__B?7^W{WK&۽P eVWLt[<1}U.'~4z.0Xc{)owoߤ-O|~ҟ+&v[,ǟz94Y~)۟2ݻMyOi?lލqD7=h]7wto>+?6G~'Y'psÄZ]սʯJuWswʯ\E,eN Դ;/~ ^xR}^  P``gf& `}\՞)~/gQڳgjJ,4(|CYmoSy5T4g`F KOWgtr9OR]6m*u"|WoY>y8Gw7?9DڲEv}򶷿]}8V޲kc{vn= JwwO'훟_rFs}۷nYd{G܃e鲥&.>nX簥+`zmNaWOew?%~0_2lzp;X15݋e&=AoS3'(7t\>}>sx ,vfn`J_}fs[V{GxGiϞgbFvQ{R_jl>8j_ЍM}Msvv< };{NWWʹ^cgW<2{3*.,>UfM[<ػӲGw\wͳmw ^ vG>Q6hطFGɥÓwGވa;GN8*;-?j ;vqgW| ̦o#O~$>==& U?-|L){!ҰMiozzϞBCP{R_jl>8j_ЍM}MsvnT  _ l{Ywɓ'WOp;o=9pk.`x"S[s^>?% w쵨 0B`Stö>q~V(OrُpaǟT}|j3=8^yLE[}En`L>mQ^N*_{=c? `'N}ߞ\@+ML+lqjϔb3(3}Lu%kR{fXǬ7ĩvqt-_w8   {&: A5J}=_PcVTiͪC77U5iQ9 N?3%orfu^w?0   M[o /ɻ*#2D@;3C7~'5nYRBakm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`H?5kܲ3<{&: A5J}=_PcVTiͪC77U5iQ9       ;3C7~'Zk6׸egJ yxmJ{ӯ}Lu%kR{fXǬ7ĩ8j_ЍM}MsvnT        0$Ѝ5nYRBakm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`H?5kܲ3<{&: A5J}=_PcVTiͪC77U5iQ9       ;3C7~'Zk6׸egJ yxmJ{ӯ}Lu%kR{fXǬ7ĩ8j_ЍM}MsvnT        0$Ѝ5nYRBakm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`H?5kܲ3<{&: A5J}=_PcVTiͪC77U5iQ9       ;3C7~'Zk6׸egJ yxmJ{ӯ}Lu%kR{fXǬ7ĩ8j_ЍM}MsvnT        0$Ѝ5nYRBakm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`H?5kܲ3<{&: A5J}=_PcVTiͪC77U5iQ9       ;3C7~'Zk6׸egJ yxmJ{ӯ}Lu%kR{fXǬ7ĩ8j_ЍM}MsvnT        0$Ѝ5nYRBakm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`H?5kܲ3<{&: A5J}=_PcVTiͪC77U5iQ9       ;3C7~'Zk6׸egJ yxmJ{ӯ}Lu%kR{fXǬ7ĩ8j_ЍM}MsvnT        0$Ѝ5nYRBakm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`H?5kܲ3<{&: A5J}=_PcVTiͪC77U5iQ9       ;3C7~'Zk6׸egJ yxmJ{ӯ}Lu%kR{fXǬ7ĩ8j_ЍM}MsvnT        0$Ѝ5nYRBakm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`H?5kܲ3<{&: A5J}=_PcVTiͪC77U5iQ9       ;3C7~'Zk6׸egJ yxmJ{ӯ}Lu%kR{fXǬ7ĩlj>ru}O6lCF5J}=_PcVTiͪC77U5ة1iA@@@@@@@` 0ı36/s5URg>{&: A5J}=_PcVTiͪC77U5iQ9       b``7cgl^njzkuϮ}Lu%kR{fXǬ7ĩH]kJ,4(|CYmoSy5T4g`F       E qx}魑=31XhjQK͇b\MkV񽩺iNÍ@@@@@@@;A;8j_ЍM}MsvnT        X 繚~ݳkm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`0ı36/s55Rg>{&: A5J}=_PcVTiͪC77U5iQ9       b``7cgl^njzkuϮ}Lu%kR{fXǬ7ĩH]kJ,4(|CYmoSy5T4g`F       E qx}魑=31XhjQK͇b\MkV񽩺iNÍ@@@@@@@;A;8j_ЍM}MsvnT        X 繚~ݳkm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`0ı36/s55Rg>{&: A5J}=_PcVTiͪC77U5iQ9       b``7cgl^njzkuϮ}Lu%kR{fXǬ7ĩH]kJ,4(|CYmoSy5T4g`F       E qx}魑=31XhjQK͇b\MkV񽩺iNÍ@@@@@@@;A;8j_ЍM}MsvnT        X 繚~ݳkm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`0ı36/s55Rg>{&: A5J}=_PcVTiͪC77U5iQ9       b``7cgl^njzkuϮ}Lu%kR{fXǬ7ĩH]kJ,4(|CYmoSy5T4g`F       E qx}魑=31XhjQK͇b\MkV񽩺iNÍ@@@/ꫯ+"a9%<@@@x 0ı36s5Rg>{&: A5J}=_PcVTiͪC77U5iQ9   po./K0kMZ?ϼUW9C7MC@@@` 0ı36/s5Rg>{&: A5J}=_PcVTiͪC77U5iQ9   P͛{do~,[L7.g^~eٴi A#8B.]:ߌ "   0ı36s5}Sg>{&: A5J}=_PcVTiͪC77U5iQ9    NYbo]x?x |ߕ7Nv@@@;A;{ahg x   b``7cglnjzuϮ}Lu%kR{fXǬ7ĩ8j_ЍM}MsvnT   ,:O?tIG-mxy9c&ﴳ?<'   {B qy/S_ZgXPb!F/k6uj{Cs5Yu꾦9; 7*G@@@@y׿uAW'   ' zj2uϮ}Lu%kR{fXǬ7ĩ   R qx|˙=31XhjQK͇b\MkV񽩺iNÍ@@@#ήV |Cr@@@Svz8vxyt_ZgXPb!F/k6uj{Cs5Yu꾦9; 7*G@@@&(;;+elzQ<)ond|rq+/M)̖pH\y׏(+?Hvn(֝?TVBo1ydvvn <#]bQ]ac#߷e+7[d)N.u'O]/+C=;7<(W4~QUʗy%;    qk\M_TٵϞBCP{R_jl>8j_ЍM}MsvnT   ,E3aS$ >οC~޻lڍr66%' {&: A5J}=_PcVTiͪC77U5iQ9   pO9'Kvlz9cM[玛@mv>'Fcr1dǦ'O}B>rY1A;]#y*Yi [|@@@})No8D>L]kJ,4(|CYmoSy5T4g`F   X;߹Ot,xvt#. \)G57fyKd{ 9R}=?{ǟwܽ#P=t,'tu>r?kC쁔=%@@@;vz8vxuyq_ZgXPb!F/k6uj{Cs5Yu꾦9; 7*G@@@bY}rUϼ[O!뒓mrEɹfhdgrђdڶvvW|ݷ^->{| 5?2yz3Iv5@@@` 0ı36s5}5Sg>{&: A5J}=_PcVTiͪC77U5iQ9   pΚ[a0N/{=[;[d9K/m kߝ ;Sk!   q\M_TٵϞBCP{R_jl>8j_ЍM}MsvnT   ,};c?i<}ԩs3,v;JG̕A=hNέlVp];ilVrNXsȎʪ="HIPB@@Xt q\MTٵϞBCP{R_jl>8j_ЍM}MsvnT   ,};#k/zlM8w>%yϱgKP*ޑ瑭 ˡs;"~6|Vn67/}/Q,S鴎^yClGN:A xH~Kmxv9q#=qvyꑫg,;G=,NW;yĵsCϯ]uF/B7sn?c`grE@@@O qx|鋜=31XhjQK͇b\MkV񽩺iNÍ@@@#vDɝ&NN&pοFn7wsQ/>'^zA]-?nXve`}G'!W O߸S>Ź) aGicz=2ڵ#?/;;ӽw~RY-ޤ텕̷@@@ q\M_TٵϞBCP{R_jl>8j_ЍM}MsvnT   ,}?Y|ZnбrUrCn9S&Vhb6䬫u\V]q_.y`]rgJ9`n?t{[+D w?%}װSv .F@@o 繚~ݳkm=c@Ԟ|(1 q*մfաߛ 4ܨA@@X8΄k|O˿]{mG83[;\xǷdy[1=o6yEǼ[]#g|<911/?"|yhgCwOvt/~ٓG6Es[.^nނhk.A~W~QN?ξՐ^} g    ``7cgl^$juϮ}Lu%kR{fXǬ7ĩjlܰ-eGC("6mmG,eQH_vW @@@;A;{&: A5J}=_PcVTiͪC77U5iQ9   pؙVE:~wיb`g@@@` 0ı36/s5)Rg>{&: A5J}=_PcVTiͪC77U5iQ9   pv!O<؏z g副ɿߒ?٫>_n+K}]Vm&7㎓%K. @@@^ q\MoTٵϞBCP{R_jl>8j_ЍM}MsvnT   ,W_}Uz)y&+W\8O|O>m[dI7$;6oYz\v 6lc=VRx    8 繚(~ݳkm=c@Ԟ|(1 q*մfաߛ 4ܨA@@XXw%Ώ.'γݧ73ygy@@@ q\M_TٵϞBCP{R_jl>8j_ЍM}MsvnT   ,,ݻ|;ߙ;yD';G},]t<)   {K qx9|K=31C#F/k6uj{Cs5Yu꾦9;zr@@@`xWg^xA C;KO`-o~-oyx`'<@@@X q \MoTٵϞBCP{R_jl>8j_ЍM}MsvnT   ,<^zI}YٴiSVX!;l0<.uVٸqL˖-:J9    ;A;H]kJ,4(|CYmoSy5T4g`F       E qx}魑=31XhjQK͇b\MkV񽩺iNÍ@@@@@@@;A;8j_ЍM}MsvnT        X 繚~ݳkm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`0ı36/s55Rg>{&: A5J}=_PcVTiͪC77U5iQ9       b``7cgl^njzkuϮ}Lu%kR{fXǬ7ĩH]kJ,4(|CYmoSy5T4g`F       E qx}魑=31XhjQK͇b\MkV񽩺iNÍ@@@@@@@;A;8j_ЍM}MsvnT        X 繚~ݳkm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`0ı36/s55Rg>{&: A5J}=_PcVTiͪC77U5iQ9       b``7cgl^njzkuϮ}Lu%kR{fXǬ7ĩH]kJ,4(|CYmoSy5T4g`F       E qx}魑=31XhjQK͇b\MkV񽩺iNÍ@@@@@@@;A;8j_ЍM}MsvnT        X 繚~ݳkm=c@Ԟ|(1 q*մfաߛ 4ܨA@@@@@@@`0ı36/s55Rg>{&: A5J}=_PcVTiͪC77U5iQ9       b``7cgl^njzkuϮ}Lu%kR{fXǬ7ĩH]kJ,4(|CYmoSy5T4g`F       E qx}魑=31XhjQK͇b\MkV񽩺iNÍ@@@@@@@;A;ϭo~is/ @ BA?׷;u9|,=Un(#)=ɗEmD׷;u9|,=Un(#)=ɗEmD׷;u9|,=Un(#)=ɗEmD׷;u9|,=Un(#)=ɗEmD׷;u9|,=Un(#)=ɗEmD׷;u9|,=Un(#)=ɗEmD׷;u9|,=Un(#)=ɗEmD׷;u9|,=Un(#)=ɗEmD'|Rl"Or E{!{>|\^Ǐm|I#+d+e9"19lmrc->e=8A?WcRM\/jn˭%z湺N3IQ& @ @ @tPGv֬Y#z许sxdٕk?xB){N^+? +bRZG?g'.[k|Dyt뤼6S3>=u#9|,=U>g_RzF/׋rwޫy_m&zszԬf`߽.@ @ @ Y ?}|0:s!C;=`K*kW,K!}>u/`}be( OU'.,=gozzv"= A'mźNozs[P(=ɗEmDʫ۴R'g4r-~轚:%цn7Gjvߧ @ @ @۷o6:oי /~}t~ޗHJ\y7>3# ?<5?lξ/ ;#?n:޲z>*{v&_5_W\]$MYLZC @ @ "߬s/ꧏz]8最T.Gqv }k7%R >?3u;h} }">ܰZ&} ;SHo1{駴ڷo!. 6Rk]S}'m"/;#8~;!.D4*t]j4Dޜ5ؙ]~@ @ @ ^t0;Ž֦?>gn>9ljY>[O;3N'-rO^-"sNק ;ӟkg%}2gxvޭTJůX"OMʺP ަL s~Ф\s $JvXmN~[T5_['=MyiKn5RS3|^|ݖ[K^sufhC7ћӣf5;3k@ @ @ ЋYMr%/UOw_Sebuy'":K5'ڮ9V ;nٗdO?au'0!M/?3+UKd qy+ħLxrB.$|k ;mZ]3|^|ݖ[K^sufhC7ћӣf5;]P @ @ @F` ˗uy+x--}TN^S;[.}|2:# \Hv^WaoIvQ޼{/ 9bEUNuMݸC>cruryKW8M[0c}uɔκ'SN"Q7S۾üQ}ӔǞֺVS)=U>:'g4r-~轚:%цn7Gjv:ߦl @ @ @ f`g\Kpr /:fl[|W;Fvl[MHv^}jyÏaa 8= ZT)Kc/_! o5v]ȶvO_InkI{}=S;rxg`sm{)k{k/>qH၇E?f\<ǖHwC7w55+y$>icOk]zVS)=U>/:'g4r-~轚:%цn7Gjv:ߦl @ @ @ b`gWȾ_P])^>{igq6Eyۑ/;x|i~kg~k3t͆V7shMW nawG'ӷn;JÿqY֑Uݻ^ƓaG4;LOٕ֮+u/iϻc?VV5N˺&}Y<#'V8U>@M߰3vGZ2Id>j\~~,<\H?OjǸϡ䪗.]s_=9{}'r?k'omS/;>ͼU]kM\ja'Aa`Y?ROBU&_5_W\]$MYNMI @ @ @`n쁝{oG>bΐ[7]''0_\{ 9skv.m\&vZyΔOLrɫ4Uyɾ/Sz-ɾ׼N؎w򹏞/Gdo}U~/KJ8O ϛl}g}7=>E1rP|a~+e |q'ŗ6H{7l-ߊZ"zj`ퟞIG-?73-dg_#_okǞ);qT&y͏wg\v\w ՙsr-r1/C#aojQ9Wq8i×\*rn.qrگ7l7e./m0\{N}:84xS=y($8ĚBI.;dJV/{?),KhzQu[n.{5uK DoN ޜ @ @ @fعss?9x!y?;ś劗+<ȇ.8U\J6=7M}-\$LO73[Cʊr_\*/>~UkǪ)Z7H^vmL{yELN;Lg]wx.k{zOyb]w\P}ϻK0{wi`?[-~?FڢqqrϥW/Ml*V}|J9~03)̘|bťIOL7L\:s+7o)xbT|I$Vz6;?HTޞ3]g4r-~轚:%цn7Gjvfzײ @ @ @>sEع`8ު *7}wo|\d%JV?SZ=8᧷)r_S!_+e>"4<}?(Cq{ndzhpo/Kڳ)}ks\uξ25$5 345w1 'V˚C~i-+RH)/^-V￴|~?]K߈ _ع: |rpI?a{6Z1}>Mp7^ӛ{~`|WD[iئ7}BЏe_RM\/jn˭%z湺N3IQ@ @ @ 0vN9atf6_|L!mNN 7sʻo/]rOkzHf\nK?yFغNnK5yvO/^zsRN/<% )YrYɓ\駯d?}wZ9+W ~ K6Xt_<융\ƶO[k ;'I?ڜ5=Q[&1Q;G{?Ox.߰31C&|N ;ߵ_h[n.{5uK DoN 섛 @ @ @`ص;M[OQʺʖ͏ÛM>)y ;u;g\~\w ;<7s<]u3\L?50 ^ؘ~λEۼEV>[KZ*ם=5aWOb] _v7韏UsTPvrG=z︜7:ñ.{ubmy=; ; `_]j4Dޜ5w 9 @ @ @N#v^~kǟ+W]ronkJ`!}wMM٪4s^)#_0 We}uK7zW~>>رoY/iYSߙa߾3o%rchZ_r\nO_\s&ϯ7MJ:WNIA׷:&|SPE{>Xz?<~o ;vLʑRS6R =3|^|ݖ[K^sufhC7ћӣf5;=nT@ @ @ ̜'ip]}?EcT3;NNYVv!raW~Ry34sb\$wO./сS>O~l3G5{<(ߐ^S28~dCG⚜<}SYzi\ύ%zTen. |zze[!'O_<13Wӷ7 פ|/S5d9K=?vp>; Ev2*YYzt]j4Dޜ5ؙ]~@ @ @ ^vʊ#ϜֳǯjoBCɴƷɇ-igߩ9rukWtΑ|G$VsW#[EC2'N[<+Ob2$ΖǮ}gΞ۸+a%|<ҏ^I߸;,w yԽ'?sUgrpkܲ]^tN 줟zmvߥ3h]۶[dBaX>{[]Fv%vة5_W\]$MYNu@ @ @ L;{`Gdf-rOߕW+r\s{ׯxh?u&sʧ[:2[߰#ՏR^Ozw]rkֺJ7~]>ʿ}oN>Lȋq[KN^㾤l޼y/[J9E:sG[t ex͇:vz:ڗú&oozoOU-=x|eßJ;u˸Cc:7H9Mw!SOp3%&[|c%ˮ6غI7{QBr9ǖH0Nn8';kD)=u[M=TW+cRzF/׋rwޫy_m&zszԬf`M @ @ @`v_M?b8[O W6N>Yݫ> vQ{oX<ҁ͟KU39=u lSgM{\4@4妋+/txg_u?.[{}SG·qrѤk^"94s `oש|zWV¸;^kG_L# cSV=M 윕z _qsx޿6.ը<,J ?}ob`'PT``t |SfL]j4Dޜ5ر@ @ @ S 슁{SrϗӧZ/wZNX{l#-_253\y&yqoy˹/:FVzߗ ><!k{+\tr΋--ҿ>#luS?5B"e׿_~ه~M5!nNN/ Z;Ui`ǖp &Q;UmpsX]NJSz.{}׺9]˾-fz-1zHA}4\fy:||6U9;)qFM7m=wreP[o,FV\3nn~~_51~kd`\߆XiTۜ*=ɗEmD^tٗ.W;pn]9s(<-ObuVcS f8c=%G?LNq4ꪰ s[ fO!$^[˫/;yrX7i@hϩ7~w\^yϝO܆o0x뇩lI;v~k!.eTJ}ٳlT,=ɗEmDaW<ɞOt#c ~j5~ix}Qi>+߰(} ;C EvO*=ɗEmDicOk]zu*ܷU/)=ɗEmDXsޗ-rϐ[vY4IW [Hߪdq򟟷./1;#8~x[#D4*RUIJhzQu[n.{5uK DoN S@ @ @ 0ko.O>*J葔]j4Dޜ552fe  @ @ @ @`!``gd\Xiv[T TB&_5_W\]$MY7+[ @ @ @ @ ;#8~xVǺN"緞>mz$g4r-~轚:%цn7Gj Y@ @ @ @X3>׷:u9|,=Un(#)=ɗEmDmz$g4r-~轚:%цn7i Q@ @ @ @X(2;qXGJJ:EQ}ӔǞֺ:VS)=Un(#)=ɗEmDud&_5_W\]$6 \k냟JX @ @ @ HN@N[?Z߆6{,<4l-=Un(#)=ɗEmDq @ @ @ 032tg|ou4-r~X{P*GRzF/׋rwޫy'a1&e  @ @ @ @`!~ x{,XG7)[ @ @ @ @ ;#8~xǺN#緞>ud&_5_W\]ߢ2<7|cx  .'cA @ @ @ xL |/-?o:X|>'m{1ykD)=u׭S{\AfJhzQu[n.{5uZ?7|c^kyMs}0 @ @ @ @NرEmy^|my4屧apor*=ɗEmDy9Os]mX'=MyiˮwXmygr*=ɗEmDyiMD)=uѿM=,=U 3]g4r-~轚.zm&OYqUzuhd&_5_)^|(èim6S_Ԭ4Q,@ @ @ @"$ הj:|^]#U̓]6屧.{ joS;K}~OLWM\/jn˭%漪yaԴ]1aA @ @ @! 4>RlP\Խ^ڢ?ô.{Z=RS:2UzF/׋rw9j^:FexO\ $,@ @ @ @b$P0%GN;Ctbj과O{Z=RS: 2UzF/׋r~tor\kcx >"  @ @ @ ED`j`{%oI7>}47k`G5hcOk]joS;K}~OLWM\/jn˭'=j-*Cۢys-KRgA @ @ @!03=r99ޫ}3~oS{Z[M=TtmQ_z/r:֨]e`gȃ @ @ @ EIf`GntofujK/=_.{Z^sj=>ud&_5_֯})=aX'퍵qUzX @ @ @ @ٜ^ X ^s{4D׹\5]z=MyiVzY{\@fJhzQu[nӺN^hC8Gk]Zz16lC5 @ @ @ @X<oW{gz4 P\MkӻF=;sj]m{Z?F6Tt1k^y 7rygFv`A @ @ @!;:\5c껐Uӕ^ѧ.;wXmygrFIM\/jn˭oQ_赘R}66Ϭw@ @ @ @X2;RMOfw zŜfg^S{Z MiWޗm%,KhzQu[n}R,E dbM\/jns}rkMyi~빼{냁͂ @ @ @ ,B;y`GXl]rb1uu :82UzF/׋\ߴ\ϕ6扚UוGmY13@ @ @ @#v&?,cߌ5Ou]q5:ݼy)=uu ѿM=,=UnC$#)=ɗE]r6E?J5c6~iL7qUzXJ@ @ @ @XdmǢGSO4ϳ~ܴ{yiKn5RS:(2UzF/׋m4bN^6t._fuSvٰaGȂ @ @ @ @`H;^4C8tXm=5<>iַ,/SGֺ칇ߦw*J葔\/z]ym֦Yn1z|Qm6}ZzCa@ @ @ @Xl2;q GJr걧Ǣ-XGWcZIT.=V]}M[RS:2UzF/׋r}-֛IԽ~]ݦ[)r̟>'}Y @ @ @ ED`j`{%oI 44GS.C9ާ=]h?G @ @ @ @X4Ӌ3=6 heGS/zj}|4ֳK^uvF{MZI<sz(ס3|^|s~_[>zk:} ZO~XWE @ @ @ @X<ޝX:@4ӽ}i[vl_[QztElp{z9i~ *=ɗE>u4W_fўl5Ya44Ȃ @ @ @ @`H;^ckz0Nn G5]:\nL1Z[n}jimhz$g4rZ/49\/1-uFl\Wǚo?#@ @ @ @ @ JקXz$j=]džTYQjo>\O5[毫U*ᐙ3|^bmŜVS|zWrrgy}oᆷ/|p @ @ @ @ W/viJtSzc'uz95kj9zsS>3c~y\omؤ,=ɗE-zYY,̟^<֋}wuG=wy+ @ @ @ @ >^"L$ur{4ikKbSK˯Xk/ٞyln&fXzF/izI^ܢ7iE~ߕ^XzЈ hnf{t5igy>ks4f{z;s*=ɗ4V[no>Z}L5fg[GΫM??=R,@ @ @ @ ?OO7tֹ;* 4@=̫kӬQڢs~y9gMgx_6AlP,=ɗ4$[nfMycuܣ4|. 9jcw/>QG}!bŊX @ @ @ >ƶ>?n{[?a5:4bf=K}ڟ&-+jV=MZI<s~cn&fM\/xrvVEzr:-~um GX:Oz{^ܣ窦˞#֦g:9MǖUcny괙; @ @ @\0B9o|s}gc&_Zn^1j?jZf}Yϴ\]=3s6v ؘGCuj$!;'jV[>ӛb[c4k-Y@ @ @ @؝!r_yksZcS/zGu5k}s6ͭsգ˼j]4}菚ͯ._kˢM80:}ϰ=>Foi6(=ץm- )j/y4߯z єxrx=Z>ghޓMzJ'ykE{clGgןC@ @ @ @`ghhhM\ku^bynl`ƞS6hkϭcW]bn,hg[K{\-{J}~O AM.=(clE^ks^bk/VhZKܼyy;7@IDATtئ皺2~-/s1}\xz}|~OAM.Zcž^yImgE5e<ߟyu=u-gٲ}4ֳG{5զhgط۰-\<9zu7վgEoT:³%=O[{|6 JhuE-/r9z}nި{OgZz ij sruܟTk=k>=KbΣxǼN{z]lC @ @ @C*{ru-sZm3$by;h6}y#]kkj9zs=o=}u鹽>>gc_,Q_||sz-Zg^im>]rմ淞}}-4 Oչ=?c^!@ @ @ Dk):Q-.{ިǺ 6(=ėD=- $<^v+~6u__=GֽnYߢ[4j}n>k<'~_.ϝA @ @ @Xh,4u_{Mf}i51m6=9Mץ͞+^oQuKb㵘9jqٵF=֥oPưA%'jMYnQ_%1jc}6 nL1ꙺJΎ޸m}.uX{_iWwG?5 @ @ @ Jmk}|}Mci^îabԽQӺ̺YܮVk[}Ӛb絘9jqٵF=֥oP )d7_Ӧ~./ѢjMy[[Tt6LcZhOsJzM>߯ub^}s/c+U=?.Ϣ @ @ @ 3dޜD3EcbE-zQ3_{Uƾgu9MgsX{|&CvH%M}-uZn1jQZch鵹P>|Xr-5Z1z_i+z4\k]vְ[7]5=O)8!@ @ @ <3FtV]/{tV[,̯q7>L7w\hm=oNz̵*yzsc$(=ėioE}[iO{l Ř%5~ڹq]Q4(YNRU BM5/98\5q6j=q噑uU3b @ @ @V}gj7x'=y>}z^G=^G]g󞉣[ro~\3׫]gvfx`wN_s:>lıvukxg?zxc_٫g̵Y<X\>O3|fv @ @ @ @' 쾰j=ۈcrQksO7:#u/܌3g=}Ev=\x5L3; ';}]N.tN.z5j\nvnޝlGjjkYޜqݏ2fY~6g䯜YS#@ @ @ @O,?,r-ǫZŚ{#GOR߹/ ͞#?wGwqb3k\r#XYQn_>_|3vf=5Zıq5n͹}̊Z^WWgZk}rǜ7r\x\GϿ/*].jL5 @ @ @/,t\ZG=L.z5FfU>fǔgZbQOu\Lk9Z]sߨl.fG5'x; mFrUOx֑8.8zst'rџ_}Wr(zƫQW7j3;^683wUϵ2|ısk7r#kkן9rGk}&>zX⨯֮s#W#7jzT?V @ @ @+/0zyqݹuk9E+W~'GnwꙻZb"kG.9Y.G<̣g;3gzx%ęGzxX\>ql훝ݗxg.7-:qǕ9~T?ju>:՟_%'gtr ]-rvQuԸ'yq糣V[sOcx'bOk']>rkܸro:c֨\qYb @ @ @G/2w5X\ǹuώZG./s5sr\{}̣\kbWfݰu_W\]oExvwU.jƳ:3_Ϲg}].jyqɹzΏx\>}4OwL=  @ @ @ / p^9ū\}wȍ/5>왺s9<##^]-jqߑ˭j8׭W}:zIa]ѝrqVsuU.jݚsC\9}b3"_ݾzΞ @ @ @]`^s}b]5Zȭ^ϸǜ=w?=GqWkW˹dzܸ~h纵wx\j5:tqv֮'jfs]m'{ ȏ5ǵܳkmg^g꙼qlQ;zıY\L.ggr= :{]y97oVrܪ?j/\qt~|G[r^㱟VZ̍5Lb_Q_ԝU.j(\r;] @ @ @~VN.Dp_Q[]-jgȯ^=պ|]=rzw.n]G]=1 8:7|3qYGoןsysOīZ7+uj֬9>Sgk=,k؏+?2gYѕ3O!@ @ @ @,?TZ}ı]~"Zs-q99LzԻZݏ\ısʚƫQ+?u=_=q 8:7|_skN.zQojo=|3=s#W<'1gWr͓͌Ygﭟ @ @ @Wzpg֪\cp&ޱ8ݸΛ#_|r]u'[cvEkr6ڟsWzf{žG>"Gf_s#@[Foq3bF|؟Y9q]km=>_^9㝞ܿ @ @ @xe;V=93q5ywG_yٹg}^k-sg"GkGj~g?zz{T#s;^$:ܬ^}Q?U\kLnw.7jXźqEn\,ʏڸ<29S8I @ @ @}^F(95w[s(z^s<>R8j97;Gu&WQ3kݍk_qcߣ'>N"Gf_s(͹_wgb֪6ܗVsgk}יּg7}~q3?vgzדT  @ @ @ μȰj5Q՚k9}7#fjrL?X="wfͽq#7xGO<2W}%35f{8r;kדs#Z\̨"?;7qXs=r;kٍk_F~\$U @ @ @n3/2zZͭvG}gzrnu?Q=zwVZ̚{WqGϸQsx ꌣsz?G͹|E|΋Zn=*n?rg{{T#sl< @ @ @~5Q#׭GQ=5w=#o$XܨuQ5#wfͽqqų||=<'Ssyw5yG~\ۍk_qُ̟v "@ @ @ @ѹYW\;6"֣\׸\Zܟk9ylg}q#W|ı晑kkoƣo\1{rQ_DѺs @ @ @J^F(3kՎr~G=w\qǽV~=V\qXWkl΍x\q\tY깏ywHpusoıgktY.W񨝭wg"w4+3qyFx'x\q~qYLw?-K @ @ @/5Z}7]泹ړk#jG3\W[zs_Gk~v}wf+?{W实w 9r,Q몖{s\όZWn,Wό\ϵ7j㪹=Wqf\1;˯|Vs>D @ @ @ pEUsj:ќFqc{W:;{rE><#r쨍+gq8XήrQ5'r3g/9|"jFWrYrOg}'+k>|뾞n\;k5r㪹=v]uW5|.k3وc|]sߨbrn?k\y;Wξ @ @ @ @@xe:f]>>Xs=rZ5r3rnG|yD-x|]/o{>3wHrOW|& q3V[׸_wY.k|e?+?g~'z;gK @ @ @w<2ByYWz~VgX6xufԎs:3kכs#Wܳ;WXW9ZqNj_kg#""kQXss.h_\#u|ϹU|TW~Ggt̟ @ @ @ o\yauj=u|{#W>ǬռZr-#5E"7qEo]sm~}^WܷqK_1cv.s9ywȝYsoyrnu۷:G><#ru9Wskۏ\\lǺU|nj|5 @ @ @S/,Cf޼mwqgkr}zrOG*>1u=nԌ;^6}vr'3#5#k>w}^sm=5|>X=Y.kzF.lQ=_9'@ @ @ @S/#hjnr>⺎5r5r*ε,9_Lƾ[r>xw3ћ|??5 j>5f{⨟Yޣ\_?ٹzά]oʾ;3r? @ @ @f]soıo*+kw(xU>ӝ[墶ݸGϸ?vߝgw윝._sy]Xȭ֮s99W9?g9qyV䮬Lcȍ+s\kͥ?rި׹_Wά @ @ @ @ \yaufV595r+kwfYr}y{r~.^֭].!j\Lj#;G}u6ٽWg3vz|ͭū\ά]oōZ}.^vegvgU~.ݵw̸vg @ @ @ O4cV5Qglx|csu{j_bsܕ;sֺ,ʏZ\cf}yWft;XVıv5dz3]O1{sOı+kw&j|eߝqg{wWΞU @ @ @ny)arٳ._sg7XG5q(7;=ݼsqݹ3㪽GC>ύ33ľ[wz+g|3]N8z:Pkn\=9;w3ʚƫQW8+&@ @ @ @O/:QLϬ+|f'F?rӓWqu3ћx֜;9پpxh ;g\q;zwk/n=bfڸfݸu3ћl9YS#@ @ @ @oΙg'W{Luc͍}'їs9>wݙź:=5׮L?ϗsG3G3{BÕvt=Wsܙ8zc<]O3]_:;_~\(q(q&xwNOw.  @ @ @ SxagƬܙ}:c͍8Zwk]Qw.bFƵoǕg>2菵zlWlpeΙUO=.\]Wܟ{wq7\Ώx\q;wgQJ_>ϴ{N @ @ @K%/)^u"u|crur.,]n=Ɯد\q7cǕkWCʵwz7+gt}Wf|rQYW=]j].[ύ[ŵgU6P @ @ @ pN Yjk]|6y\Z9]˭v=G\qrF-_|n_>wL>Ư|)3._su?`r(\v֫=sWke69}wfv6y˵Y|l< @ @ @~vz|͝ވc[ıvUSWV복9ѳv=9ȍ+s\kͥ۩ZիF @ @ @ pK ;sV=]m'{r<ǚs9ꑻƙG컵휋Xc>:8kƵoǕgvNOqfC&s֭}>9 @ @ @xN Yj=ﻸ ֝Ww)<c?|ov'zuݹe/)1{gƬw9^r_r;k͟ewfw&r3sWݙ+7ry=_=g  @ @ @ @ cEUܙ}8Yxl3g=wWj9F-Lk[w枣xvs+_JkќUrOfQ5\Zʙ:#ݬ.zxgr>wϞ߹ @ @ @g_p=?;ړ]s{}]ZvWfr_>>ȏ>LfDm6/wYwN^]wzv]_x`>}ިEmL7(5<#?dޘӭ9]QYGR'@ @ @ @; "Y.c}kou\ǚ3ݜYn'{FW|j>΍#w㞯~;3V=]m'W{jܝ\ ZZߍW}6Lz^{W6 @ @ @W%/)L8k;u\\%⺮j73gkJ\t3;Y}3WH=vίzf._syGz53ru}'q'vUߨ+d|;:5>W  @ @ @ @/Cw.7\kŻ/ruSk_tq7/k\|j#;ϯݞ_sվK|Y{휟޼xr;Us931ʳvsG3׺l7C @ @ @ξ?׾~{"uu*Xպ\*>S<=xvՙӵ|I{휟~{⮾]J5fw?vsG3vgC @ @ @g_9?׾~{⮾]Z5fw?,w%?U[ʗUϬknߩ垣uXr׾}3r|]բk'zW]sVP#@ @ @ @o녇9Yܙ}=w]n&"_׮v%8s#n~.#7zgSi/!<{Y~\zynn1d2'juY{_5ʳ8C @ @ @xՋ gzg.>׺ˍ-ޕ;؏+nE~u&b_5_]:j]soǗXsnw].g3;lLƙzvګ> @ @ @_BݽQ߬wr'j|tJL~ܛv]ȍ>#sߪ wkgΪgV5wf{gkޣz{7+!͜n=Z9^^$@ @ @ @w>G=\ޜߍkξqgd>jѵ9{Ų]/-Yj]s+㫳w΍}wTn?rxdg9G_^ @ @ @ p$/r<4>ǫZźӛ{3ξq|]>3rQ{9WpvfzԺޚ[wkk|qyqj]S컞YnUNϟ'Yr @ @ @ p^;V=]m'W{V\C,jx6#ϙ|~g}]բo'z;gM/,u9YjOjwg:_kc?z3QOVsj @ @ @ /p初sv5x(8?{l*n&7qwӟٻ|NDܝq7wA\syڛk_>GΌܸg~d>jѵyz. @ @ @ @u/H[j]9y]9jmǕr~d3>Dޙq3ws9Ͻ9gƫ{ڕ}wfƕQ}gƟ?w;?E @ @ @ p՗$v]ٯ]vfվ3Fm\5W]lcfE-֝蝭w̘͞;;㨯wYu_{j}߭}G_͍qg|^wzr9  @ @ @ @3/K=Wsjk9y?wՙZ:ֻȍkow};cS2]ޙs3յW?՞ܪw=ٹgw:'@ @ @ @yYboV;ڳQcVZǙqgxd~wל~$/,u9G}n뫹jz~&7#ם_gy @ @ @ pNٗ%v]~'W{W_]dgz׬Q=z 3sVawQߪպ@:ܟA}};vJO=;;'G @ @ @#p%sG=] ._sgu?MlN}苵'ElݽS(~]ϱ;oUպ| wvgngNϳ:;?Qy=_}텅;쬣U}V;zwr;=swlf8El;[q)G @ @ @xŊ3zWY˿:wv3WN}k g7s=۾*C/LNO3y{m%켣ٹ;gf̨j;'֝yѻZ @ @ @/pKgzԺ3]nw.73?fkvG}͋y] w?y;G=vGÙUmsYGϱ:F @ @ @?Oٗ0vWj3]ˍojVbfΌ{gg^~+vάz:?~{~/cݝϬ_ygY @ @ @W0q^VYw33=fGO]3yySOx)g2oQϪ>KWj;ݞ3Dz3K @ @ @ Y;G=,?gY~u&3=Ys힟2+̝3G=ݵjfvzƙ_=kO @ @ @?W2]]{%fw{F_{wWܽaOyyeUߪ6UjZ=S}Wܳ< @ @ @M^zs;}wɿՌܗ+g.~>s?饄W=ٹG}WgƏhuv?ģ98׭w @ @ @ @YG}Wg7:S_ќl;Z_5辧?W>ٻ;}Um|ѫvtv3u;G=_yv͏`jY>{~x+3Ϝ9}=c8w|L|K @ @ @ +w{=GϚ{#r&W̜O||eA<[ќl3<1 @ @ @.;^8;c&bFb}yr&_ůO}}e3;w1cvďLo9{|j|99 @ @ @X |WwN=wΜ>s&WhK?ŅW?ۙuW1̪?+g9+fZ  @ @ @ @ ƕYgvg|;;WM>vOus;};=ㇱӷӓdg:眍yO @ @ @6ΥIDATsz=ӿ3wvzvg7;3cz.ίW^oxiᕟvwvzglo(̏3y{ @ @ @ @%+Μn_ ?{.ί֫ϴeī?3Ϝ}E+fƏ83gcƕ{Y!@ @ @ @(]/a|+O%3=sgϞ;?~PW.~r @ @ @ ~WΟ=s?իgw֯γ<[_~}rʙz8S޵sC @ @ @x/zsrfгcU9z%lwꌯ>?Usg'@ @ @ @KU/~<;>-Wg_ug緿 {=3Ənj+wʳ:C @ @ @ |׋wu6g3vׯ3/՟=;S_ @ @ @x}9ϳP_}_y?]cλXnx|5 @ @ @ ^2epYQ;zڋ_y?;g̳;Нbj奵wYwͩ?WͭyY3 @ @ @ ~ Gzܻ_ٝvqg_}>sޝV?ԯ @ @ @ @@Fϝl]=_uW}w{ @ @ @ @ | +foUs~!uߣziK}wҳ#@ @ @ @oI/sY_5Usw~{y{irvq~}= @ @ @ @EW畳w3K~ǫj~뾟O "@ @ @ @BɫG})~z}ݞJY @ @ @W=WgxgX=ߗּ 1~~دgt:  @ @ @ @ ė?~;P{+n?9y @ @ @ ;~ &|o'g< t@? @ @ @-_ '<߿ox/'g} \ڝ @ @ @x/i{I?Y_m=1ՋO?>w%@ @ @ @?J৾S~^ָ?FM$@ @ @ @ ]~? 87bQ}u @ @ @ @[~ۋ-%?;K xM @ @ @ @E_x_{g_ @ @ @ @3*g7s(Nr>#}+ @ @ @ )OՑu/C za|[ @ @ @ Ox=f7~/^JF־E @ @ @EK:0>? "jU @ @ @ @GK:o]{) |O  @ @ @ xIeyA8$3Zz  @ @ @ @~þ'0 @ @ @ @ xA^_} % @ @ @ CC3饎3Z?C @ @ @~s~w'}p @ @ @/|;ҋmw @ @ @ @~[=1>O @ @ @^ل6Fڝ @ @ @ @?/|%Xox4 @ @ @~c~a  @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @VŇv{UIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/0000755000175000017500000000000013752535025021034 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/link_editor.png0000644000175000017500000014274513502206677024063 0ustar noahfxnoahfxPNG  IHDRAxO iCCPICC ProfileHTSޛ^hH RH%bCdqlHS]i  ,,oEAY 6Ta;/|d@- 2D! 6fsB@j>EQ2W)rybP0q\1'hE 9]/+C8PV 6 S=qS, D= -J21#ơ0Pp'u$(oCyNjjB8/q3NNZ&E⋅)voHC%Q cA is''9Q>g4s^) 9Ò`M(-D'fhf.Irl^K3;1,r3 Y0)$!E>5.>K柑'[#{&78J- e> Y|aJ̟+3Cec3636X?Ili^XKgVdL,3MROH`7` 8s֖VL&>y [#5cj>zв#eN0/, y Ԁz9pnh h p@"H"V m`(U`?Q Nsn; ` /Ÿ?m5Cr %M Qh@t$ĕĭvuqH2"9HIRR"1L%;r)2D1xRP$-ZJJ5Qc-zyS'9K+NBEky2lc l  ')*S(* JyJ+͓ơm] (㔍YIʅʇ{GTTDPP9"#tC:BJ?JK2Ks,ެMfݜQu*O@Y5ZvV'uSEY{/V43`5` SU55F54}5e5ZnZIZ;h iӴ];jd0)RƈDZGgLH7\7WYISSoD_[jFDAn.F [ TXFFFƮ5ƷMp&Ld=&7LaS[D flY995sS3,ύ}n),Y)Y[Z[6XWX߶جiy3loymi l7v~5 Wc*3EXu>;9f8u)٩ip|uRK>+۵ w$C=,=D'<>z:zB| zý˽$4mdqX{5(ME  v,x``akbzl"ܢEX .m5Qq$3B>bID}HHiܨ5Qעգm11Zww|+'2^m}W_O\vF{37]oumkw~%gs_.@s/O x+᫱?|mnvD [N]y;GG~H0ڧ]_"Z[SDžl{@j4O˓jIjIѾ &DY`Y6S(h4>N|;Dc{ǿZtOR8>5??u9_z V>`iTXtXML:com.adobe.xmp 730 577 YE>IDATx |u&}vin/-S$&e4nnqҲ]&I& nM8UaR`2(ABSPaeBʠI2K#4bH: %y{,p>ܹ3g=^AD?Rę  `:.4.:<;wΜ9C/  RhhZm_~e  Ū;^, ApT*9v τ~q.Al.vs~ ApRf^x?{#/^jJxq.Al.;fI]_[%oM( 5MJR}MRľզНw3-}MCSh?{3Rh/|jzHߵIhz| 6b@73v>}V>}/}T~o"?6OIZ=Ia}zg%oMt;'g^GЛ5$oMtۋ+](oC)c{6ҥK:x%W'rZ>G_=b<=~~{X:=4MO#yPPig A윮oԖGJ!]!z3M7o1ؕ;uO=;v ^lA[l1ؕ;BFoxA񛷉}q ඣ?YB{ |ڂ>Oǥ[o6L{X㡓NiO/#_b|+WnbYn<|Oo}Ju~ؖm>vy ؍1ؕһ'ٙߥߘ7(~b[#])=Ԅv#~Ph4Ɵ\|?Hxǭd~c?x+ ljG;(7-?E;A7G}sgqM<(z9{N|(3QkGǞU7 nw(o7lӓ-Ycı+կ~Ԧ>!'ű+VcPyC`(1<ۼO#]~Av,VH{n>O'yO?Ls||Do?sr'ߍ_s5AAr=Ԏ+ZRz+'یn$/8 _ۜ7h ݩ}'SRA_{/?ǿ<}/HO}o8_v~R"yc$o_'NkGSֱ_" tҺIy-ATt-//S;CGı+w:"])o8tDRz n?:"BA^zxUY<7NgkNn}:}i\;A|F׭'=@7a w3m}t>N}O>h]>O<p|{A]|=x08v): )])?wǮۏv}zW.Ihc%]-ݜ"EY~bA`Q=}~F/}\L~ ZtЍ\}2bmӓmשv%cOW>miL *|O?e1ؕ;qℜv1ؕKLjcWJG!r?=/9^V]L'DӬ#` 4o5zǦ.>Jvu}[OOo=~{Tcֺ-cM=|e/k~}}c{e9ztN𵳏k_;@AEēS ֈ}N!"",iOiz_z!G5b8@^4Wb[ॗ^rڱ =t9,}.h߷=~aqɿ;i{ؿdv ̄Ծ{d:|emu=3V^g.O|ݹ8cM`t4| u¥C(=Q[l:MǤ鹞oӽ_8H~+7A&6=M-CGXD Jh @#Ĺ^OZ?B; ]\@3Mm.H$$ۅ؅6AlEA,qḼ(t/E Z\\$AAחǏ  KWs   uYAAAp}          Bh   6  Bh#eZ^֨`V) 496 ZLghﺕ@so-fhA+of'iQ~$.8zM(>FKˤ(^Wvv,nmwmm{\re k`ZFHkPڎB,=9AsK|eturӓ49m9MU4l,֖՗6;\Wgh`>+i^hGg;gcMu޶?vuͦxr+kl{ir wlղsW_:,ns[4ߙ%rQhVFzO^߲B{F!N}bRl JtXB#Rբ._!.pC3Fş_k*I{ U9; }s}<_s^6?mƨ"Zn{ڿՅ㯈KJM/|.͎ʿG3իkm{ 5x踫gr=C=+|D]ŏMn4?`՝.q3j%XeiEevZ{ /c oA Of?nXЛ<^= c=?0@>LhvpW_&Ks5>1u'&_w/ ofbXCZ^b:L=@3A_>\ ==fӬHCKӐU4:eoȯfK+=O#sV%VyӲ<ȴ7xl{GqY{?@n(- |?&{ YyX&mO{\\K|7(.㼮y5 Tz&kEW0ey (7-=J4m䃯k uFU,mǖe|x.Qmz/) H? * 밽ޓ}6 Ȭ2˱}dcFӺ}lUoڶxƭdguUclzgF8åopڅܘO: 퓣]K6rF3MS8sګy^j=<ؠ}pdzz[ h0KbPomЄV9wҶNn;U}y&^~jJ}yf|uRmmYly72ri~vZ<1~G' 'GWAy'V(kvjք0u ;zAꄶ@16~~4gFdN]=, lwb!&cX/ΒO4R(kʻ=D+/DW]c* ɕFg$7=crzXYh{M #[~h[e%zY`mr+2ee8iedM /yA3[u%/LFvPEA!7krqKcMM[_]/KOYGy354[KϐxYc7 3_(8 mmxqd?qoҬ4C3dy ClxDC4.XEѦ.>}(h,gqg)][?*fF{ey,Չ}U}Y c]GEw{Cwo1UfǬrBM~!/4f<>i4G{\߃,w B`rz u^Vd+ߟiupSlRmz]Ј{g-?0zԦ7YoS3f֨"nT>&t%R[-;%uri:PMژ̓_^Qs4cdF-\{]FL}f`ej-7)hG~~IqbxSY/~ZЕ[|m'Bkڽ #0W|5HpR3:su8ġq]Xy^b},jA 1jFxBTV;[GB[M4b]VK+mXrv -c?E]ҰN>棑IZX.|5>ifg "z]Fe#o R4(3;Ic jǦz囙'<懩Ct.Al :e3ekyMڳjU8$#@7cu%*;i{_E1ëQhӣWԫņz5rq~Nj->Y/qs ]C|Ne&ۨ-:"6aQAh607ғ--R__6QfB{1u7F.F25y/-ڄvp~һ`ywl PXqdH ib&&&i#e~>,FivB^Ռ+ ZHО\jړ n/~%Zèq򩮮ᆆRݣ/\|϶ls#V\S}p̻4ƶ `hNl}(ؠIq~1)f9eXFV3zuQ'i&G[ mgrtnǫk@sQ.4#>۶ަ^3nL6*m?&욄veZ,|SRQG?VNQ5iB1i̱wT&ؖ;W0znit+vptogsٴzVg[\ʪ, 4↵:;SpQ1E"x9u)Bf2ub=2zgʺM^;m`%e't%~1u(]2o[52_@P|^Q8 NY]Uu5r,U*kڿӲUݨU, xy~Uug;Vlpv3tF,wo5(0͞X*i6{͌!oM]clE<8]Fxo1ױJCG>uUX&pƙ0{T<39d n' &NfoÂlLW_fk]"۬/sm-lSeU?Z ;:e1?f&S3抅6r古"-Ω1a-NCD##Һaizohîz\y-5˘k65^p?shl.|A]>3_kP9er1KtZ4 /sq5rQ*D5AfqsczXtiαڀ>g@1u.'.mp3k=3,}5J{rK6Fz#.Kȍ"oݗUlaxcKwQhm|]<aMB mޮLk&<=Mk z:i2R݋ʳ=l52pzu&rޓx30B#bR:<՗c6iָs9ǡ}l7mcʥsj^Yi8یx,~=kvZfyXuO8=U xj)8#/txC-?WNh8-k{ڊx ^eو2z׾t V,N/ԅ .:WRyki5oeU@c5ZZZj9PVUNǵSm[auvzǩ-Y:u%\Piˆ0ٵړcok6=1~o+8vlF꼔"d+ Y4Se0^*\ A7b8K,@܄,вhÖr   n~-fhAۀ]$OI3wՖ-%mF5ʾË3t׆r۵g幦%6fCI^TBJ)̘VS9?+{ jthorjm.O:9B=\3L+n|0PzqoЮq'^n{rz&疮yIZjHSA^%  I'T.XgrW8]a]3,_ˏZVXNSW*_\}r9}jǫ ]óWnfw~Wٜzfm찈U3i7w1/hF_7{|gC,3ݞs-l<ߕ SF{Fw}*2餼ŘZzjʣki u ɕf;yJqg}Z;pkr"\NxC>=b.~ۚW^\6j33QSi/s\Hkm-Z6e, !nFQpML,e>0`G˂4c0H9o3]$~ljad6'hFvsIg2:pzm1\c˴_=VeW(oY@ʅvow\, cvy}g]J׹|hx˜+\O|b^n13d^Me.V?2~8<#gO^RU54\`msg1}[Ey%<s:-jcuŌ.^ ՌSy9}9\Ee48L>kjmi񜰽>[%xNvnjA;ċz׭g߷56[w <rIjnk1gQ5[Λy{EgDXgzm5oWblGv|%X/VO{&<+!~oy}8 fbϤc[jkxWn Lx˅V8UѪ-rj[lP'-\@p# 킱Ǖ"O8 %|]ݼ2ز+ʆI.Gn,#¢\B\e/]H71&W i[TeYazk+C;x)5)ų! eo 8_V:\Q{E4nxš>8Va^}8]@~c*qZ^4`5|Fgx~EM]_&Nئ68Gܘ +9|R,j~G"ϗ8nU%c#sYw A c fgU-,/߬- IJg&4ZZ=b^-p}KA-23纥ʯ ΌZBkAgPӂݣ+ByoUOꘙnKRJCkt9,H녋siٗ1i;dL8W/7jF]5pn' ,F;-W=5?{+PŪjAuq snzwGOHgq/>4!5wCA 4#cLYPUn}49ۉWx&R8l/ϮX.4f؉nfԷEǵ.6 WsOsH7Ж om[7VwSgЊZ28eK#4O"iVq+#Xזk\ UD1†FivAi.:85"xZ/Ԩڒoh,Jn}m5GBk塅164=mn/{<پu7.x Uijmq*)PGZ]HaXKO4x|vٺ[C'J.Seu-ɴg4],ql<|CJߧ,Z0R_dMh]lNuDSjᖡ#|ݣ¸eN,oU̼byߠ8yIׯ֢^--kc:bPqûnEJ\J&ۆ mvRyrEu5ե<-mol$ 9?=(Q{b@ds8ޣsyFmf3-ョ\leQm}HafrȚ͠|{}&:yV ligƄ1]4E5Ām1+:֬W2t`:-&ҪPGu҂j0$;#FrEss8"/=VPMr{hWƍwhYC1."Dû8Xqz[} N8Yh Z؏!wT3{8e(-70DrWpۍ g1-6]X#B_mwuݐY hۍ%p5fd'CG?llQ v)lks^6-js\k.0cPs),pb x]CxYs9Hkbsx h$>fۨ*6"*c1(G rYõݵxvJ,ώp#`ek@ܨ qn۠Xٛp\}]sGjzLpLwܖ.{M}e|]{4r|]ʢ[&ut u*9(8 sXY9 m= q &XءK]̈mP%ns`1 jS.53ǧ2Cfӱ5͞}).j^,-@m ߡY=;h;Lwf?v.K~h.}yzlkKeۻE>衙]]ݨmَm*YavǺn4~c%׭=F{G:z&RQ.Wn8SΞ8mqv}4O{pz.fO, 9_AoV aJ/y _ԎtTUy 3]V'|*+=q^+?Żr kN巺w[Wձy,K]YZ{j1֢j-qGezuBr.23-cWَKhٞ(i,3} vgI >{WKmV!3PU ˯Ӻ6GNdmo/tn6?X <;U~ynflם KXWxxh.N      6  Bh   6          nOAA77~G 6mІ !B !mmmmBB BBІmІ !B !mmmmBB BBІmƓ !mmmm ˗/׾5;馛n6VÇ^:z[B}{鮻6o~~ t/~[[H$(ɴ(0m`%|n6{Qp^|EَٟEɟ^z  ߿jFP@+\UlA1pG~GP@#`"_ҳ>,?y$6`ǁ?f7q$6`ǹs,~2 Bh[VنVd+_7 4::JT(P/xV-oڿKrnmw]z[Bv["(ɟISZ!n;o~sG3cnӆ"[0 >9 __i%oo|# ZVd8qBZ̏ݿŶgy抯msD7[_d_kEn ! ! M$!ІȆ6^It?@hCh@dBmmlnml2y*:es:U?9JcJRVeu:-GYJղR`&#TUx8HZL6GŊq~Z9yMEI.GzZH+ٷ9_c5>N(R7 Ȇ*MP^gnrB$Q@8B_XVǿ}SyC™B ^ `a)WQy|b!\,ۏcoB8(vϢUyLHDTjq}F~'zBܻ.&wΗI˥LJTtm \!&cTs礀v#-k0]Ӭ eJVq)FH7k)UBXQq}/e5rKq+1xdb6r>'3e>we*6p T^Yrzky6 mE"#CSR_ztɭbR~NHoǕ[x~L!l]8%R4V^mw(SA7K'EFJEiHlڙ|C7{^ 7E3 {+ V02')ݕ ml..(, G!TbaP>OM{9BdkmpBp8LPBdkmz]G~)U7Nj5qn P E(_a%@ґZ{8&>v}kB   &ѽ!68DE"N:"cgn^N]Ps"ROi6BS]:vܾ(l ixŁŤ^BBK!GMQMQd {-]ZxnI$a =6 -DЇ頊mXeVv52gaH²LqSv봈9.w F͞JBJ劚c'1+B>ĵ-v0-IR2e܇.?dV镆Y_%?@h*VE^}!FAFLInow~9v Omn;0K> tC}Ѭܕx/"Q =yyKM{2!ƍ~9-簅@'q.C=*Q妤Uu~(SLLJȾ5w6?Me⥀OEӪ ;nZbRAQk0ވ}k _XȖ!$ÿB}>T+`Mx{KW-L&7X>P"!l2յ9z/ۍ)jk^r[ ,U90(y5 z2L ]׾rnnyX '~ujUؒx ly!(L,Σ(e @hChے:n5FQ)4\)ˣet8j |9O($s--j؄[lDC@B3  u3 6^O\TX25K)D%#?A椁tkD4h` @ܫfDxQKO$[+L<Ph>Wի$c7Wv[?3 ּ&( Qtv+Dfyv[mj^3i<ʴpyxuyC+Ssj5A5>[ڱ-i}ת4T^rOBhChCh ռBGePF(o`ϵ'gk5![]DTG[zSGxRЎdrQvV0AhSAX\֌̓6C==+_zpl) Z\D!dުʫe(JȸCTl?<=ƽ{W򕎏[H$7.ƥK$?Ov:111uM]5.C{a#\7NJ浞J.fc?ER ã-zBQJe6_JhC!+g앬k_2m5IJ ;}vݡ-q+Ul[dcu!!!lh]qQ*7%D[z(q}FJJt~\d]籕(7n}lJvyyB[}u0x=F}!녶[60B9uP"bP+H%8^+ܥ] =5"dbBİBhIhUH5b#/t0ܦh8F{nD%[,܄sh{`Ԯez녶4s:^zpz8}B[t+M!$seS2 0[]X.>${(/j&._l!b6,2JEhEW*lݲkYtڢAeH4ux=U%! }5Sk QS}8FS ,ǔ'BG녭:b Aym#e91!^ HEMZNBܗ {8Ff{{s !۾F-is+"bX lOhj]wUwSjЊ[6v[lsֈk(A-29,9t5ֆSrK-CG8p0J_tjQW[mwHxϩb%dLd<`Q0uх6C [n0$2ܾV;ks*ғkkdIKAفLqJH+aLoRUtJvCSV5 vݽؾZcyJ6a]ػbZ8|EB>r(#tf9Mnʔτ|L[hJIlTn Ej4'X\,#=/,ID,A2YΏTEuYd;iz)do". OU-:M OW(mۦ_l's* 卢(&Ls*YGLAhCh@l[|±w [%k.w& Gp-Ly&X e%Cn2 GzuPjG=X{քr_/e|ʙ0y|AsH&)K奚%/ɖm  " g acy)y#5aO~-DTJMXK6󇢔mD(VB;t[ d%"|^dY(l,`0/>G{{a:*sR`w0UwK6׏?8s=twl+n Cu6`9b2KqDMf#ޟrY{+b"!JXGY߫ĸ/BZhi/{ӥ]MŘPԫg2jd SxX*)N'HY!MOq$k x #!$tmBb{KkAlhfm[jpxElt'܌M<ɂƒP_]f8EmO1"V͂"%tKIhƌ+ma/WOIhg665BIJ]])S ![av׾>I6U++V7aIT9.=AA B؆H]7ijQh2RhW(3Ez([Ă5 k ! [\BBbBbBB`kmk !ؾt$56pbwwѿyÿŶ#G@\@h==oe՝Jߴb/_>kmSSo)/[ډ_Vkm`%+_YQd[b{ǯ׳ :&{d_>x 56zϼ~B}M~BhDT]6Ο?옙ϯYh96@h6DQzG^fWߏd2o5 |Oҳ>r$[ma 6 7ٍ]G~ 6 bZ3bB?^?_b5E@h+'~Sw; J9o tk?OWѫ^-)~m~>4 BX-._Ldvo6mmBB BBІmІ !B !mmmmBB BBІmІ !B !mmmІ !B Gh  f   Bh   6  Bh   6          Bh   6  Bh           6  Bh   6 ۗZZX sY+l<t $-prf'(\!AA uq\ruΗ0MCC47f=<۰o=tӬ>nyC3x@mAכK4d~hZB[WBIh@_ynD,6}ho|oV>w9=IsKW!O4 kSgmr[ 93n706uQzPr7 y3<%}4-zԾ~`k:=]| \K6|bxxhOhx2S'G8tdizG-D[nuuڛΘc|h Qfc:7 άzFQ7Q7AsaB ЁB}"--MVS^fI6ɏ~A\. Z=4:ԯ4АO M/t,G ѿ,Ŵ!httdC1ΓK3KFRdO`ܷR(  6TaCBFVe~QZ_5O[b2Kب1M/^ ~+p;>+bg44 vۺitf DDu>RhnoMbkyddبc4qU-=,Er!=j9БVB{ښ^_ZmbfakLG ###=F i6 푙9+exfI~9|`2ˋ4eܓоi1-o/$:cH$D[ |23F#]|7Z4͕|le0K#CF3⼠ ciz\uac~YP%{l`~&4DCYE%MQ3j9V"{hIhM+]fzޣF i#76Pvz@ ://iWdK'G-,,c>vQlAuUO׬KTEbBF{BZ6FL7w:atOMdlFռnn#Y^e -8EM…]x* bR־m_scj b18n[Uq2ʰ/ώrq#@.*qn[c<7uuA"]jbN+Z "n5 vtEg  6  Bh           6  Bh   6     ń3gsÇ-#GX=?y+߶OT/}uuZO Nh CfرcT*غ#jf[ +{Bs߀ؾDDݼVuGJ )ڪM't8oRo/;lgKc߃DDݼVuBAg}VvQ[WU|gg+򷹽u?B߇DDݼVuB{cG9y[EhiXmx@mXTu ήW !Q7Q7UHB{m-&/:zCxy\g-{nCun *e+ZJƦZ.Q!v)[!= 0e='cxsynekonfƛ'}.^"穋u|(Ve-CɌnEy~w,ܾ䲲>о\Rh}r:Q?2o"%sv׷}׿ Ԟ}H?7f?ʇ{6>b9O# Q'C}VCeo}W](~g %Z̆1a**]o2:xyꏦ a_EL٪/`s|2lܔЫuX|ӶVJ4sBuIheXh.5Զv:}_]Q,?Q;z~xG{OGC~G>>t?fcD4DS~~e#J$F2:pfIrb~x1F0%=HGj}-vg0Zh{ylt*qG6VW7bϲȞd);b2h|"{1|\3emJs?"5n|t[XL7}tI/Yǽ?UhB{Mqj"&'lyٰ|EEa[ZuuH呬3)YB[WwˤQm{* vu?/@q?S>N7e}"a5uYh@O4t0Ex)~+p˭t #Kg[[Eynݷ]O(|m8u>{\U=۷/]f/Ѣ x[|],o}뇨Kb=jybn3 7Jwn;kcg҇nnw{ٺsn7ı9߻Y:KGチv=B]뉑B۞Vע6*żm%=}CߪҲ.) I]=J!!cyza5uYhG'x$Xe*f9e||r{D8c'FQ=zb2~|ixZwm ah B{Tr #gͰ>?B7%v4:qr*Fխ ˛)kB>'ec^)WԺ6EAUy Z삫5՝l:P,_/p k&_$+3h dȉ 6YؙwwI{ы׼u; 7^檛Vo&]-woYW=T:VylBZ~ӥ&-H|u!3nuͣ]Lp[º}ݗyj!,|=Uh_$nҽFB{&YnJ[oѻHDu螮,n>gI{[t-/]evK|VpV;I0˧};X xy~c&m#1ӫ?J]]Bi\xh};{̻Uτx:.lx!EZ7C178ٻSO$MIuhe̢N1O͘+C;6[aT>#Ŗd#:P]qa7ozt=A#6ϘE+ƴe~[sFᅯ*섅BKع~E ^T/1KƱ,!+7Cl'#*Px=z %Z7J|#<2$?D|˔.4LsEzݜ~6D-FM7! 7XоusU#ŃgБ1nAhWyՌ 7Aho< #-c>xf5f*aO}> ٻcpK+ ]7Nq7~/[f;(i}^Mͦ{ЬuAabU9<[qv< `71ZC7Ty3kԻm^J HoYemi7sn1;]hU"l 1~'%Wk3敦\`=TwjБ9# @w)sL4`圌jo*gX6V0f i0[B;]iEݶ3QJ~٦?ba Y?Me5nUK Է"\Bpϧ.AW_^ _I-7[!7ެuӜu ]%>kR[ "44Di>m{]?/@7;tuuZMm QM A<ܨWn^3](̶%Z) 4~A2ŋnl' =o9_hiwC>h<mAB[:n"o9+~RkNKR2k Jj* ||_} T)RwmAr+Cb>N@ĬXyl BhU[ jdCuՕ*-!=|/N%(R+F 1W1!WhuXT SЭe$Aʓߧ*Ƕy  va+ٖh*5<} 2DE%O>CiS(qGI^=ëeZ}~J}},2ary#je[@b'Duφ34MH|Gq_^$֌j. !Xo&Jټic_ZpJWKmlbTT ?*gMƃQN۳3%I.(a,U|{<> ^>"@OG83وWNKKR$l *I/b,R"^4yozYy! Þhm7%3PӲ j\R2:U8.>bƣ>&و{qIr#k-ʰ?0U9%ŕDU!A~3,=~N]m'VWm6H1)?xBIC21!}ɄRL MXdB^2Tr|t/ ZJyl BhPҾZdi>%Bd F(2C( ,XgMc_OOZ޷QZ4M^Xjvu#Ggn9cd] _7,9j=ii?DD݄^a6hä9/,K76HHcbq6 ӂO' 4+ 0#0N 3iwQ=yKRphf~h'x?[X#su&iY9?=.L,S=ClcasDDܜ"lY ezK>'SW LV,)=O ^2$3qy1OHgm[]s:. ΨxQ}. tohvYv WlLSQ6K+=mƢ. {5^y(2k NM޿e6]ʘC=]C-, )^aAuus Toe!1/v5OpE Ls:^S.U k^F޾fJ&-7ag޼\!؈LK# pAƉvˮݳ&qn1u6xpkx91cܭibPį4K):fh` \SmX+iS3V$}Qڕ)}},V?{#ױl]4S#Pj [y*Z||AؙeƿrQ^GJ V죩\Y ^r7W{n '*CxxW r`UO1a4֯X1k `,Qnnn%!)r50%*i)) d=n#ENh bFc BhAh],v#}.k@V G}`RZ\/[xvڥlL :a|^/keri&BSd0cܫ9.eĢj5ʰVf.F|o*g>h́a=6 n}rz@eJ6 }-@h9u>)Wl_hiyy?gN$u/8Q 3t@tǎ s+I\Ƀ{A/vPsƽbx"?;L 'Xeb^3|:GCҰ:ݷ:gP3qJ޷tG:{:ϝ< S@ T{px =t6&&6[Ahp2:FcwP >4\3!FrJeGKHRKEs7MʘnǬvr7Jyҁ 6lԄ4ٱqSڛ0)Qq>U;swnYd\lς4EvºNο)<pg]\;vgS[;Kr $˕9{3՚j߾:޳=y6&&6ڛ8t,,]$ Ȕwf|bgO?eΚߣ:ʘ;Fgl|W c~nҨ]<3/ } ?zjƼ k;4?QoN%xo.,<# v35^g{29z=v3iˣtқ#&yctdAvC%jq><GJ9.L'ChoMMm 7`s;Sv^PH; d wbo5|`}^}QFv/w^#e;JY `}{a62N}A?ϩc; iیy9*6|%h޴#M=-,,9"y M3""Ľ{  !*0f<1*J\D={ @h@hChUh /ǷL{ @h@h_.kLfimʔT]b/S&"}X,QM&z^z)mZ-1y*W Bg[^=]w&6ڛQhW2r`<# [9ݔY5/ S@%i'#Q+-w~\oޓZȄaHKY>4ԭ\Ee:j-Q4e)B\6. B{uB-/WߛDY c,=]; ڦjH ƭ'g3,n؈&4鑚b&,Ac^LK8$ gʖ2`#틳q C_Ke'ԫ)yk-0_H!N(]jkDA2|oH .SAXqx@ZBuuB+ll,^FyX6d=D0U%bHF̐#јWxiy S'l5i#Z3U-!'3-y9#DcH9Y."Ah_'챮+`Q(սSFg }pn юmA.uB d"ADR4充u2Ť*v{w XR<`pJ j}AVε!rleɆZvw@b]_Rtz1/"Yd<{4yGT^]37mt3!>X+d#n,BuuB2d\5fRσszS O^bW0UKQj*y`H#TgCڦOIQ+T4 {MwFڨ@hChofh(~ LӘ!?@`]nzrBE[Bj%~`H'6ڨ0>xІ76vRxUمF݄ІH  !.k k;@hBvb&ᐏ)q/ m+bd6ƨLN1v2E:vwbl688&+Ύ\.G[|Ľ{BusMmmy\:i<ÚV+x 9- RɩkK9Q+|j1-]=sJ~BgBB!AmmІ! 7оi2}/=Dn'!! ƒc'{B%K/(}J~_v͢3w 1 }X j=k^X{6)O]B#4=406v }wG_7=.we3~㸷u zdtNhБ^>%}. /]4;iE*Y Sc`++~T}2e_dDٙeVڛOhwzI қK8P"LCINpzm^+dvA#鶞_CگjsB`K m!k!ec%)dvIzWae) |nz"?럤 e/T)&6d2}')%||qJ?< DJқ~ vNzomNlxy32]Y@DChoV-pmt _?BVoc}{/poz@56BEÇp`|aT^r)(OvJz|Ooҳ2O%nshmm&B;?)S̜tvwL(li/7xB<3o{R4qMeo'wqy6{sjk }^ Bv!LѤ=t{0+$/OM AG [Z<Ѷ yfws~:Ń&2naOnS5~ڵ~[pu@mm#2n[k~>M̼HIcys}nߵ2ּ')/J9Mc,8g5yQyڝ %ȸmm~q h׮]qz9mcm@ƚw}PƄS>GYؿ8Qh]׮Oyڝ 6gܕc"N.\bmbN!pyB?/g+yaF|dΙ9mC4'0sѮEۏuAT_xnc-xcʝ 96g$R~L~}[P,\Y,? xpS셞ܲK|tAlpiF9?{_c,‚_ĆJn͇iCX^]*[r@DChle8_Ṣ5P~m'z Pm%z*dys Bm"ԤZ/ֱT!t|~%51[Α훤WkANׁ ֔zC=WσǍ[m>,BM^S/f[c7o2KpxۃO}5) I@DChlQT{o15@l <7yz <Ç[ 9LwtSAh@h 6`! !AmmBB$`%@h_ W=BBІmAh 6 &]L2EzQղNyeNRfF݄mڕZF4Ec;moi^8#jKR2[l{~.HLaQ 6ڨB+өT,R.jEix}Ά=v躦jN DƳ3AM(PIjyX(L"F:!F"(E!p%VR%$[[+\(BɌfyLc^E]M=c! d Bh+Q7Q7!mi*yàꦉj>"y@36ic̋)1nyD<֍޹d%fw0jVľr04UyڵCw(mQ9OJCiw,F;8A|~%;v'(ün5 iNs{C{R]Côg~%N}@iީsP8ju<}V˸HuLCKﳶӆWڨ/('|N6i m=blvĎ `/lP ޴&vp7$lp>ڹE aw#:w@X8$ ہP9C|Nѥs$U.V=|S atPt tbA __u:qW+N:J{^?O)OX/ q6mB91>p%r rHg(+LNU Unno9AzO8Ȗ ^<tmйy6vtFKگf9%{N%JT4nN{dJp~Cho,y56 B[fn6:+Emaϰb{?J.ÛVoKCf9{g1?% N )># aL'-]˯;`cG vHo_XsuN:;??OGѱvi4_!ڷGu?qB\FD݄AB{ U0ŻW c.yTĈH`Ϸ4֥ύc;e I?Oƛjcc.Xp 5)@v;OfsP@ty^sh.!΅6&z m77M܂B;LsNz O~#"ۿ6phm a\٘K١; 6;AQ;s-\g6c.p׌޳6kAkV6>óxNByJƘr>>Ob;kf~#j6& mm`ŋo0NrB)._Fy`ĖWڜVe-` ֯H b ˳1lHmOJ@hn [ F;R.JkqН\3aOq|fam癕^9i̋IrKnxܶQkVF_yZH7Յ61ڨ6nWZb:yH$R!4r" *Q !#yB{;hnBh nN]d_I{zi6f9&J e  @h6.LӨDY!mBׅphk~YXQx'@mB uMj):\+ iFk^7DY!mB7Ю΅_΄!- 6 Bh&ڼlK@@N+GւVxPn_#a|v]X1גaHQB AmBB[q\Ihץo[Vϫ:E,#YmB!mp+ mwl0e "y j^pJl'B;]}F) g;DW6 @h6q6{zy;3ˍoۣo&#Rh7y9:RZB;D2ID3 Bh 6 nbM Y)[ȿLLDsBd mH*GE=K!oБԴTQB"=V*:Bh 6 n -A~1Q12E=B[WHC[{,](  6 Bh _|QzuM꺾%iGq 6 Bh  @h BhCh6 Q6vBk/A n #-xPAAAngN`L  Fl ϤN(lA!AAAAAAmAAAAABAA!AAABAAAmAAAAAmAAAAABAA!AAABAAAmAAAAAmAA!AAABAA!AAAAAAmAAAAA /& ^~AM(o F3gsӑ#G$>eiޣ_qE9[:G;'/U }酶&6 )e6cǎQTqE9X9zC?[ }Vq7Y?x|}_Ǧe7.!AN=~8$AZrѻ$Ħ[uYc/n`Bh > H(O{16Jފ4ƫln]( LmAp3NCa ce;^>5M>MDylj], LmApСCRe*KTT@.yW~]Qk-˷B7һlK{ux3ڏ'5VjWǦ/ҺctkW3_/Pr~:5s4IǮJ!Aιu \.]ˡ逛+(? b/G_{8zR}wFVt.'r^C^:uqkg7%ZNQ?϶=]4|gi^C:zaM$^4B!A ˙UbnR|5 gt;?HD~s>C{+?6 vn2erB(ǞhN|BLmҜLUKMkT)B> F>9OYW(*osߵEO6o_/^R"߽Rߢ<6~3?%g^>B}gwKtV9|?yާgXLoc=Ȣ/G o?w |'@H崺1e:s~g;F/ߠ {^>"3W}u~? \}N>q's)T:;;oe B42VY:BAB[,)9h4¢EpF m>/hU)REY@)\TDV">)9Ahۡ1.5<΂zPg(M\_*P4Ӥ>Y Б^'g/tL{I)H˖,; Ar/Gyܙԃ~>n>FX% S )RW^=tЋG>++J3i}wJQXn"K '?ijNny/Jas AAmsQRg: i}%IՄ襚`(xXh'&H$8Y#Ah\{ץڶup8 (q-.{?Ezy߶{屩w6\;EGW7u/gҧ黅 xx<,fo)M]He90w3͟,{'}iN4}v[ߥg})oӟ]?LX4}|٘B[l׬N;I 󵼤}~uγNP߅A`b*dz*ZeH@0&ܪUBn']v5|IEDZTqb&1 :?v^?{Zl[{XPq/on%أ{N~Z&ϟwd1_қy@m[}Hȡ$Gy738Pֱ_ݟUۊJ=Q:}4X~:꾑z+}d?/efqٓSyZ9_{>>ͺZ(]Aϫ5w5HR0nU?γߪKOfkBto|3 v}L>n( m_BA.B[ \bjB^NZJlgאU?3#_-5?ݗϿs]S>EyljOOWȗ*9_b:|΋-2"|(]d^2( ;y [W"///Lrݿ[r_m{ v.. A7;u -+Y2eO*ſہ}'I-cS }{5wI[^xAvx6 љdd@Dy,*x'v]5oUu,/ӯV!A\.GPb;.Qk-?GnL? ƿO(6屩vۻc0 %;xctWo)LzxsM/{XOt73ro;u.mh6`h `h t4KE@#E@Fj:Wt'6vR4or@'Ybd{6LvlԛrE)IENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/metadata.png0000644000175000017500000007276213502206677023341 0ustar noahfxnoahfxPNG  IHDRoco iCCPICC ProfileHTSޛ^hH RH%bCdqlHS]i  ,,oEAY 6Ta;/|d@- 2D! 6fsB@j>EQ2W)rybP0q\1'hE 9]/+C8PV 6 S=qS, D= -J21#ơ0Pp'u$(oCyNjjB8/q3NNZ&E⋅)voHC%Q cA is''9Q>g4s^) 9Ò`M(-D'fhf.Irl^K3;1,r3 Y0)$!E>5.>K柑'[#{&78J- e> Y|aJ̟+3Cec3636X?Ili^XKgVdL,3MROH`7` 8s֖VL&>y [#5cj>zв#eN0/, y Ԁz9pnh h p@"H"V m`(U`?Q Nsn; ` /Ÿ?m5Cr %M Qh@t$ĕĭvuqH2"9HIRR"1L%;r)2D1xRP$-ZJJ5Qc-zyS'9K+NBEky2lc l  ')*S(* JyJ+͓ơm] (㔍YIʅʇ{GTTDPP9"#tC:BJ?JK2Ks,ެMfݜQu*O@Y5ZvV'uSEY{/V43`5` SU55F54}5e5ZnZIZ;h iӴ];jd0)RƈDZGgLH7\7WYISSoD_[jFDAn.F [ TXFFFƮ5ƷMp&Ld=&7LaS[D flY995sS3,ύ}n),Y)Y[Z[6XWX߶جiy3loymi l7v~5 Wc*3EXu>;9f8u)٩ip|uRK>+۵ w$C=,=D'<>z:zB| zý˽$4mdqX{5(ME  v,x``akbzl"ܢEX .m5Qq$3B>bID}HHiܨ5Qעգm11Zww|+'2^m}W_O\vF{37]oumkw~%gs_.@s/O x+᫱?|mnvD [N]y;GG~H0ڧ]_"Z[SDžl{@j4O˓jIjIѾ &DY`Y6S(h4>N|;Dc{ǿZtOR8>5??u9_z V>`iTXtXML:com.adobe.xmp 367 355 hyiKIDATxsG.n/".~ ".~qo7GqG D,OnxھX--) Ѳf!u_s[$, m6 ˀ钭e"!)b8 #'3oeI&'#A2+2O>eatj(( H$ҁrqf. _5lmmH$逈qq|gg*D"mHh\TTvqX.KuH"U64WW@ڟYus΃OPס /9}I$ҽq~lnnL_O;(ofX\Wy/O᫙ ^/o7kO_?|o7cUKx6}{JHma/)@_{ Ͷ8x~+W;_?plci\xO?Nb6t_7so,%-w; ̾; r}-GKe ޖ ax rXZ$ ^!Y^o}>5ɹwKَ!/隅مO1.ϊdfg@2|\tfg 7ɍnc=»]?0lo߆~ù>pB=bqciXڽX>m,aiU?? uMXKY.r?=>x퍰o?N> 7nmγ>n%^_hus+\{H{,ߺ'X҃>*Lx>WWi('!*\?fw5-4th_ê9˅@>ٜG\y<_M˯>}u ^T㣆^߇>_]y~g3.+윺H{˶չ n][oT83ԟsZe.8+?n] UU p+nޯi1^6癇O{ ~ k\cc"͏s{ۗiO_\"OLyK>]a.e>+j瓆_4?x|yOƒ['?um,v,>5s. :iǼ^qgem:W_ v-/ٹXO;?6Q7xN7+7RТ^n޷,-QW54Y+qZۋ @1 KW~; OB8Ʊ4,RjUo7Wxos9?(4y}QxW^S߀''OS+pK^ߏ˟|/N}^Zog|v]'1ɋ &cK< 0?$$)$?[;V}},y;|/r=3|<'q0x/qgyg^8zeuy/m}y^::eUy>zJ3X0|Vgc>~SuG7m}}:i+{ =ϯKyܚ_Q8]<۷9ϛKWקW\ڶnBau {/>ONMϕi'W *P_żWpόsY~C=Vcrd~ -1kYX6|~iBX pu\xN=ig^޴{WEjWMqG>,~)Ls .,| ;Fs 2\Q 9æ,͏Vs~XeVm4)L2.ak+_ǿk@T_.oy*E>Է}OxFЮތoނ1K |P_epGx:~wx|+^ ߏyv/`jD#>F@o_~3 yUEn[X6l6Hfy/]X{,Yڽ;\,^u+3J-vfio nsBxc2 ?Çcg@ ۶Rg`rgLfa9/!qWg~W3"W~2n5=Vcyr ⲑ&3m9Ct_.ƽZq%2_*\ xl^y_ྏS.';c5LjF'!)_O?Q99ļ&F벏:Ĵr; dEO?xqC 緤ƿ/~'>d^-~h>~}ϳ}7x=7m+(;A5!yNzGZigY>Z={/.,Mekw}HL\.{iYO]ۗtXKM^.fw2+=Ž󹾍9KJV*ĿJ$fX\/|W Lo7җǩzL2l! m`fo?gޟX/U~Nq%}sҐ/駟|^a<|^a^Gҧ~e,GC,HoxS(j(>>ȶ. KuH"l@"H%"HK8Ӭ{D"HLmooD"$D&H$D"HoD"xH$M"H$7D"I$D&H$D"$D"xH$M"H$7D"I$D&H$D"$D"xH$M"HoD"IN%Xd(0HCie.Bbfo~cRB ݻ$> sD>6;Nyg CqCv+:`Jknh=\ V 2:1 Fﱞ>HejTwiG`x^km[WaC{"U?{b`,p[Vފ4 ݊x+ꃁ!UKmjnD^ X֬>V`xʀkzfL3tr${`'ҦF3c806/5alYWQ60 c=0:/`51ylXޞA ؅?<3Sjv_VS,0t>!\1C7c%5.6!ϕ,fidS}@Vtxz`G+֯ꊒ£y 8\w C)7cX7 zFH 9qO8`lk7~@) 4n`BybDte% #t"PP&p#18 q \AGׇ 1GE2?9DrlQX7ej2}jO2?/'Á>{r}`ήE`û(XMN5»ǀ7>ASxp+~\2`&f|b&P6assc6@&53;l\+ⴖP"?qXن5>yS$[[@@s^RDlszP,>O@`K)gn0[ vaԺөqf8{B6=Kk>D"037'f4S#\%,x8lƥU9m15['DṧƎ՟kH[托O(Jbjn{(k@{ƄD"xxooqvMw) s_% c}M`K7wx }1=!,qEys,0CSRsJQ(ez)Y/Cn&IMq>9_.Ŭl1MdRᮑF}y޹9ϏTT{% <>\kpYÍTMb`[Xwۮp{ކA cq|^mfF8Û8'n)2k^4.{C=@O]9pbкwjyY1 o1Z@-\1zNTRs313B"xj)M_A[RNq Q=AK^sEe4=GVŃ0DJ H$7D"$D"xH$D"HoD"I$M"H$7>4HY{_ɵN{T^WS@NI{~D>ԯ4t\ $\)o=ܯI9.n.o,Y M YOIkھ+oz=__ǽc{+Im}% oƔ 2Hsc{}EESߟO=̯X|aAHŰzFg W2* XX\,(6K:ZlA/\K ǏcyJ}V4b)y8V V-»Ǻ0<0YcQӢR(@t-6_aW `Fdղ`lXk4,ZO kuPV_ekKkWۚ%ѸLvIAM;u:v=_h*Ƨq|I_S?Wȼ?b%(+KγϠMA[n]TfLHbyQF,[lmnyɈCl,.$Guu}k'__I}];0+VZ|Mn bA4kz99:u:>-nap•30%q|D>$790>>ZQSY]-&2;LU_T?`pX3>MR)~_6'FK162.<1 zB&^q/wBZK-[PSEF8(&xg]Lu92$!3}!G{ۧk>f砟SIXRhMLvc ޹_ ^]g6֦SX y/Ltv>oqg%ٷ?˕B s&S-frt} G`rʲ4~qBW=ĊmVҙd ̨ m2a| v_e[JկwƾPүi +-A)u!55:F>Q7kA|bSu~ttyl=)h&xG 'xw>{,]6)ePɘ{Wy׸.ûl`X~.]=\y^_ ؜:H&AgK&,V"(7ɛfRY۞c PoD"I$M"H$7D"$D&H$D"HoD"xSEH$D"HoD"I$mg_ .tD"H b|d'prADF(37I;x#^{N"m'gu4CP?wM@Dk|?<{M"x3(7 oHYWE8׏~| f.qK{]n#`tk$_]@< o}iq]!z]G V{R*K/!E.^ezI o[o?螎h3;w^~q e p$@tY;<a>m`Ѱw2j\[x_])޳z_K?D"x: }J >7{ fgga,\ s`.]?Ww Kh+}mvyMj2߮yTW_-w3o-kOFgy݄sbna_'%iw26½5YҥY~= އ{Rrׅ:d:wxY@4O ³&u#8K:(| n߸>@oaO Ψ.Mx]_R} ZXC-]q?P>S"E:4ۄD>S Rm/s WA`dh|`UmShm]Y49TT-~ k/l#lz:Fl2jFe G~TgzsG%](U<1Ov~LIŲAK:m/#G Kf%$DaDMH$}gH$ґw(Pp o (P xFx׫P)WZ CLtDX N 8_ܰw ,r[| _ jUu` BGެ~wo>va]}bW(:I@&xj 8| x3h!~lOtN3 Y C6L&z̎ۤަV%iZրw18q~fz) mlnُq@\EM$URC3FX<8WTAqbUlV"xI^k twS>PX}B4Kn owIoDW_*XA`35CKfy&Wաo:VP>SvY,k̝4(׃ZNס9;f(6,ކ 㶡 иmp: orŚO  s݆!.>r7) @DnlF͆uwM"x NHV."hM]˶m3HroC" $D&xH$7M 7 o (P xPB\j툕 ?\TF& \89 Mt6jJMmlJ5aNíe4,=x`W95"!CC p/WdE3U-#PvTtdXʲ, 9*IQL[̋wG@p,ofFx BK9⠏)uK|A*b[@^fKIȖ.K0 jzF)P x?hx;Iqoam7AJhY;rC& '5`4_RZecAP N$K[i;&|(@;g` x >A`ģݐ<䖷v Y(5.0<6`YѭI NN'o0c?28DOy׫e(*U>#;ez6)P xfEU!k2eٰiRGMyWea.)w8H@"Ho7D" {ig Pܬ66vG" %CMKLwaLPJ p˔ >\Hbü{psbSĥKYEz ΟpCp:ϻ7E S' eG" ]{@]gĀ.m0.6IMS+w3'0M2Y <-(Rb4^[0; . ެ_E Woh//82wD&xwmys0p#RLrЇWZ?PRbI #o N Ӊ$Hצ G뾯!mcÇn eJ`ϤKjFS=$7+x=Nr{"8-VR;jg 톼5 B$bLZ?$dl}=~špqrkZ+Н' ]`.̿C r׀V OŒiK> \-w!5? c'%anxD] w[ %ew1 t8zIowր9s@0hg H% t=4WTnXViλYZg~ msv%je(m cg;D"xom \Dx _5~ig˵=W,M~"ޔw1%HZXʪ-"ʕ?+ JzARgA㳦q.mJ+h :q- }X<⧶,#[n{§KBs9dĤu Em:e &t@;őHoSikr8uh U1e8|T_D&xH$M"HoZϛ 7 o (P x^JՖ_WA.ZJuq( " icN&RˀӍċtE6WP60o'ֲD=E8]0VBӰ$3}u Wi8 .V1X]*WdE_4Vh_42*π $P,e7h1oyQAQPʢ1kR2ywS owז7'ZAȅr0AS,*FUkvl@m]F+Ca*s`ÇWkpX/@uNq( ].'9Ľ"jvB*e-;JCz/q) \I182ZMprkZ+)7Kx#4̕/D 24 %a'3Q$#]g km-,L2cIdjgnN*NDXVp9%]RxƲН(P xfz` He30;t8 f dzDbIXaʻ^-CQZ-٨.;^A?y`W, gV r؃q0@@M$|p-|n!ڼq ,U |$ޔwUV}`;%N"xm rQ ^mbm>(sbŮ R[ަlJXNõ\3=kIȅ8'!ަD&xwlCqs (udK?ޭl*"H4gJbд/>7e+xθt<{2kvJpۄD&xT!"XjyV7ypPe0bwaڝF#i(E=(ׂMI^<T*짌%b'a' ޤ;lmWmG" $D&xH$@M@7  J˞Y(V:_ǩv"J J6U|V{䀆:~>M>"f2ϭvC4S1qp۬|UXC×l6vdpιd!U *?PN,{gN܆U-fe BIor7 >ι"ÈӴbǮyŔ:kwc _i#='a VT|Xƀ\V-9P= `d!ϴi8+qZޟ. އ>VjP!srf 6o#caÀ U+ˆ ," (A+1|\*l p+5ql9B{ <(G[2Jxc,e`u{I/n)cnpø|;&jQp@ -ZJYԙwO#+pumUU \z˕o~Y9IJڕ٣߲)'}Mrm< Z>[a@w*xBZ&A[M@E#ː[?` @! ށG ZmfuwjoiT̶EWŃiwhX/k Uޓ;LЃrE.}e=hk0 ˝]ccq}\>,oXo5.UG"d?~L%(<9,,.=ǞC,q{MM ך+*;㎖oӜJl\.?b!+P!6ΥSo@-Ӈ~2Tjul(FVan:[.^(yA8VkՐ?Z*2h^+/2]2t-Նa0 RV1yMEoWQ{^n[n#/dotj6fimS JY[IYv9'{@DT?@7_*s›Вtc/ƍ>-3 t+X5`h%^ZV-q$aQVzͳFcG:fwxPC:XݣQXf7/*ybaָyYY=qSUqTaV"dm3%SYˋ>l'V502C3`C5Uۤ!7$c!4.jO8y<|P?1tyECr\,' Dk-W+cj|{xg=tNL1vHз͡' 0PJJ!1RnoL;n6sWB>35ץn/Gx=bp wne?kVqqѿ7IS5iཫM|q6QW?1{XS1?õZnN^&HAҊS^T*N} `YYAu }XŞt0*ʚ!* u` :SB'OWAnaV cywU4FŠ XfSrq4_/KxZ8 [_m S cVO]X t.Íoj>U1vͱNKN锘 @i+,E޸Y NmLice=q@@]xkqu^/CЦqustn4UaL -a״{q-Bqg}u64:L7hL/ F:8g-MYy!0pS۽S{r^xUp]T?nvtv`z8W{4YǁH/[du#s_VS{q &?6܏󮱗tNkD k~xݿ*#xS3)Xab.I5ۆ9 m>`A|EױnK*}D>Bކq;cJ>%viR%ȭ߆X!SnUxRrPLqN@څq8>+*ބ&,tDn37,ۅSXcB8B몴. .[ź)bf øT[1b n{l<^zNrϜ=[Z$[Q 0v ok1@5Y+Ї:et*,~c4L %x bdoO.x%;CQ P@^1%v{eg^F$$6$_(heP:[ktGoރJqfsC 坻h߁$w;`54_j7j"xèX3ލV(6^=[¬!^gNtx놻]"^ ick$m^=-z6cm}mF/>of{]}-- >nxk+Pn5W{{S/`SKXⶰWjnc++q~mNMBxzOͶ$M iNN6ww#$կux3L+DJ6|p߯'r{Ϻ,'Kn܂Hay~T9q|!H\j ]" Vr\P枏?hfA<[ѭ2S& }KD> $,o Kάk0,S&mfaY{'bP|Կ.lAY'lA^ԙ'Ǎ)m0~fr ;+Bz;kJk'q>Xʏ7E#M"T *0Psx*R7[X ^9+8S7[&JTJʊ(quUbk,Lr,u5tvX."xG%q灋ڿ݋Qw6}ZΊCׂDI$M&HoZϛ p xS@M CJJPksB!Jj˺$l4SQBMK†E'n_Ҋ&sdLˆ=|q9! V_]!<"V6t(8LZ00ӓLMPaARz 1goe죮 $C^j:-ȸ27Y T`$vzFNZׅ/0:4c^,+㔤/LMPˇ9D+V h}gPގ ͂8S*~^?`\3UEu<|(Ǎz*Qz) gpQ pKR*"Ũ ( 98(].:?(.ƭ ~Kf-l$}p%z) >n^-ǹ{#?%lrdgq.írox-,Z]Lo ou3FK+u.sKӯB}#8sh IOt"ЇdoWHURyc=k-)^o( ZR x)n&%a 2ֵXEiq+5NRWaVEk>wH <_F:wn#oQ%6ez) *8U0PR @dlsHh5bjǼlfFݸPɄMSWM.'*\GZyGbduS x 2^ *z{V^T) (P@&xS@&H$D" $D>  cn?>\X/Aanu_?wܾ?j 6$~qwHmxoaߺ$ԕm+MKŽ_X1o2KqBx0kkڇi,%KMC?HޛtsFن/I NA*.$&TWvuPΜqsϚ0@㔕0]Wbz+ζgܐ$0]iSbIXi,_l*Pn0ՇǏ˫4+1𶟖YXt^0jٷ0x;#4Rr{yt׿oK{cvs7?~]4f`aj~Pq c]mp j#?zJ{3TsنyJHN[IOd0v k*R%O{C,"{ecs[N_=Js|nAacpɥp50iX3hi?GTd6"8FR]b`=acYd냈Sғa^f 3!x+Ywqz+:6˳7ƴ15n=˭]fϝFW$h:̅K{LknkFyJ|͆Fc &x&Wgw[V@D}H(s07Y  nGXךo]d;=Ac0rpA,mY?̪Ԁ)6ƁgyS D {ml#'Eĸ`wVZ`J)&9[Ә]C: NRmޝS{'5Ou7適t]+{[[QZ"kҨ||SO̚SayN^bL"8a fyYxOPR1]՛6@M)$xg2fq._aӰ%;DZyg=uzIgK:윔Aq{gg87D"@xkgV6s0}z@|~G+{Be(r(фw5>uh" l|k@ B4BYomBs9-j.rj!΅ijZ: Ֆk=^Z*/V>4aPr+K(Y$P xȢW!KfJ ٸ?Y( Zsh)²+<|ؼ ͠EYb&n,ˈ2X1$]܊Uor[rT-ytT3~ 3xxm!/2S#P x(xan).ZX%wxk@' @Wjuh10;2Lb A'8z- X{X>h\sZDUw^ėf ]:lC:tWYU0_,?`Jԡ@>Hk (7[mճ&@0X%s$pB W,^A%l)77n[m*NQ|+r8ww|a#njF:AaEBqJ) ̪[* b . Y%/q_n{WI(lZ|n=pߑh;xc1>WƋWYxC}&` fJ+x8G!NNB>L6FvVys8,n]q/›4qPVup7 om wqNN/x{^Y]'\9_uAgTNLW65˻]BbL#p(e&|٘p3q;»륓( תUuua7e崹ubF(_k]֪ }E;IzgJ@ i7O~$AZ|?_>qW4xYxψ <7^s&q`ng0Ii6xܭ_nyu5+0 Gpjkx.?"LJPaG) ߘKF35*Դ$l(Yq_%h*F< 0<E epb:rcffuAXkFF|<={ :&d+5%auk/b@2\ˆtZ'n I eaZwH|PU`Ud ͏hq/փ+㔤ILMD>!X)F;[v`l\ ę.TZV B^KXѪEU捨5`dn M-nV!7LM.? FJ[ Z )$,FulH@Y,Amvr,`FIrѹ֐+ Pvh4h&J_}?%a-rQS1PF\=˭M_e332ƥTռ]5b`J< |r2Jui$FV77~-{g剗*Ue o7 ( (P@I$>@"Ho7D"C*aCsSw35ǍDVup/F"x~뒰SWL6- ;~aŀGS zN<.u =gxo(0~y6WnC{`?-70taԲoq G6 }rߌ9$n9ϝfuZ< q/pmfNysض@:ސaS:t0܆0Yn-?Kts~[/#AK7_XoMt Y#u*I#x-4̆Hc+l?kzi6{[v[?65\b-{֩3)ވʵIȚջ%sk2JsO{ chi{.)P0M“{gn e6w|oUuIlmccxC8 qOJczX4r$7[joX@R"^ NDp0r+x۬$SejlJ0ϻlۉ-ڪ_tOE %,n=7 &ODVP*@Y0=5[6؝ZNyFZ 쪰M1 f"D6nIH1& ZAuPoXJ" ލ‡:pO"64HZpsM [LlT}覉LNwMucr+c*Pd&jekrOM Bi w`72jGG\ui@"xx#* vO/^Ǟs^HD{Iam=,M&H$7D"޴7 h=o7 ( (P@> Tĵ8jkyԫy7)8KF3Ag[W}*p}Cn>._(qlTyĮ:jysVr(Tmrٲޝ\@PϺ/if:4ҙPzJHC}p]v [AVF%atW (2d>,V\Qb×ͫ"= Ze(fbfWTk%U0jFl1[aˬR GQTy+mW _yqp`c*Pz*WU:y#˖Yxi"'6'XxcqJB^ T5޵|[+Pn&d.0K~\iZ-.nWt hUM4e) x޺O:FZe/w- ^-5j28^Fp=G_r" x`$5;N!JZ~x(8WVtxk2+ x7^޵<\nx*VS3y ~\Āُ-]/ XOV…i"`6/La.Vt /VG󆯘 kk|ʚE@լ}M8t,r6c]H!eUs{Qye{i(T NtZ!Ii>֧ Z6ZOlfka8I 67%r@gkX8@t:xXpE f64Mߤ@}_a/wлZ8ͮ^pbQ IMS@M7M"H7D"oDD"HVHx~b$Dj/Ͼ($ԅ/ 5u H$d}7D"$D&H$D"HoD"xH$M"H$7D"I$D&H$D"$D:$=UD"*;JA"HJQ[RqSEH$ hQD:eܶ~x|$t|܌ό641$IENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/reprojection.jpg0000644000175000017500000026007213502206677024251 0ustar noahfxnoahfxJFIFHHC     C     @9=;cΧղ9qנѥ<_?c}_>Vޫ>>9G[Ăwε1}@:wr~gl@0"&- H]sf.GXymq\N o7ÙߗA?zA9y|;w=Zuq'֐er; *"o>oGe=YmC<cpgu}Op^nt@ ŧ7go!6'\:{g}6Q`놯Lzn[>crVvTǧl4[iZCPΔr@9Cu{;.G*\;J(Ҍ O羟;@}>+gvk8L#&Sd7s fb0"p0 SpL 2e8A,2dcћ03 Ƀ  LfOaϡr4]:no7Vm;,hEթdV)(kQW.*H[+Xdޓ;sm:*mJ\LS_9kBtl(Yr'9Mcz6E#_ܶ2{4.|OaץÔ۝iuu[3Tcf,EVtQU&bRݳ&5g8gUNG-|3eҝms$fH%IDL!!.ڳ[Ec"iyqFl]0Ū>y|0oDbHZ_k:/lNse(/w5)5)T$Vk:·^ZE,ZIZMۢï׈n fr(" JUȪbaf3_mo"c##fnj*f{,s깧'|WS`(З. Dŋ]bkbl t4Sm V(KbTV6B6J %|ݢ֓_43f_c-65 #E0W+4&1 DrUe%&]bLⲌnYvty| ET<.,y̟i:+,R1"]c b3[fubszصn$B0ҲLވr15i++QSH%HYF"s)Ls;=mE/ rRUG) l羟;@8K3r59JbRZHLhJcXH$"[s3bjVʆUyU%Ub2}vоt)IfS"#,E #6V3I9HS;NzfU5YjҬ~yOaC--1hl[6`fjȴ-HVV# 0Yj-b!ʵ2:[43qJ鰽Ӎ6JW^+ieta9+T#FL"2d˭6uMglwi,3G`= JaNIVc-)*+)2&0%ffa ɓ/6Ӽ.[5 a4Y Jh"DB"K^Zw-(HUdAtW̊#i$!TBWfgkhK4^3 3I%Eu ;l羟;@8):#}x&e 2bwNJW-ݡ6΋ U_*"S1,^6[vE "JL+u*1ee[0NճSfS8zUYΣ l羟;@8_C6gI !C-/iBQe:*~Vv6mENѷ11#RrDf̪+$/nNy:95/)DF唬*EE&1$dn%Dd\&*N~{l羟;@8_C 佻cg%V˚!*jLm,ͦ(}ưI5jfJIH',ƨL6ƫ7e[9&xB*^eRkCm~M՘:!$4_v}>+gvq¾P電ߪu򌤴dzBVf#S%V'#{XJ)J2V-jd4Y)⯚RץuiSg)>4/tVCК|.b|عXhFp]+gvqÝ'Zs teгQXѬ`ysDЪY5)ooO+<sk4._LތZ3+8G;;tn_)`UaU5TDTg*ƳENRL26u^"V|W'"Y1[Z_ie9NW2X,]YfO-\MP3WUOUt_BzrٕW%& ԄR!V`iUUrdJ-2aп{2Ӯ¨W`=9_G1,2Ƶ˭.^[zj6:ͯRL4fkmmðȭma1K)Eb51:V*4rv ee9YTԊ b*M|aqTUroSLeDWTB|y|0kaw޻ڲVWͳa~#3u׺)y໩~e6}}r޶]7o-}3u&3 W)HLeUW6ֳXјFbLtZSaG|sV̜{f^8Fi-%6em{l+NoЯ'{_;?340kVޫ KXַ6!gdbvdEfְI\ҋ[M60oyΩUWlyKSBc$Juo^ޣ_̫xѦX[;KAS ZVs*R2g,MVUJib,Tm=K򦧯^sMS[6QNS?F|y|P >8[25U)ouo{VW]+[Mw M/kj템m*n[[\19k:j-[\eBwb kY ڮ1feMBhVÛr.JE+goʭ2[(©1|W 糀a4lE|RxŗcaEgkֻ°6] -{V-㤡g-m6qZQ0mhRBiDkZ2NOBr8L(D"*1Tbhc]{$ш.:T4YMuOʔ+N/TJܽ$IEb{ݹ]mhrtN 7Uy,g7u5}^Za kiij7XaL E,i e^3V̜ЄX+hQ t*q9Ny|I|+M,ZvɶmY";õZŭl賤6Iɕ(u&ZLƪѴږI>UUɽYWzʾ5^7<=:NgRȘbΎgΊ*e˴&cUUW%|O(*"5nӡ6%dK~pX}>+gvݥ]xa;gx6}-׵G^dfq$tgU㵞,M-UZpe:dd .կ]j֚U襂0*4/UdeD*HV![Z5-FMS9|W.Juo;>!t֖ul-2M5լMl㶪)[k[o@ZJ%ViquhD#)M3XrPō"WDUaHjM`QLJ_g|W=.G^0^˚.}\=l%[z/|3 &'[tE[-]]16ڝxan6KUjβ"(wFzzy0'1(+gvq~Ƌzi74z[kæ̭[BԔ#1fu[`+7ѽM,teͭ±0qjRZBӮ46m+B蹖44k~_6&+gr' !q,iWE‘^D Z+gvq~"%hmEіٲm{+m::>е鄡_Z&aj֔J3Ѽo IUiϑ7ZbN:gκhT^*E*dJtdcкīhӭS@ds[|W}>VZ W6v.jNۭmo~Ot^*Z+XcSlYMUfzU_41z1"ViĻ *s\vWI6n/ƕ)ZL&1ejk_9ehH\I2V*w`=|m0ݍ;H{ym;r hMᕊI;_14)MMl7u>kҍfme"!i&JˏQ[SigempZٱ֚J-Gok3J8HL1YtZKGL&L9UPL)5imnVtFfőIZQlܵ`=D%}?#a'39m[wN%Y}/:pbtAnFb]klk+ՖM|nsfY,5(IZ8Qf;juŮ.sWY>$LAB5*5q]J B3 (g65rVIUa:mW;y]VOapRҙ?Hq4G.Iت31箳K6gEbi1'u\e%?7\'hjtfJъ)(!IE!1ҪA !YiFq)V2U;#Y+RߢgefыEiJF5̔6dVBƅR]YҸ1I*tISȚ0_Z+!T!vו0M#&l\E+gvV9PE#XmH086  t%FlpdH=ʞjzA+@CЎzz 羟;@}>+gvyǍ=D2ɺ4fӛכ-yyFHCFY-c6!1$2 "0456A#3P`%@B&tjc+?vYx^Bk2KŔ!5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CP5 CPQ&c;-ۥNMs[yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy0J@#*f9RH0Y".7Tr-N:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d:HY#d%cxզjjjjjjjj۫jjjjjjjjW Xn 0i xXd/#Ӟ7c1L1Pq9[z#`)bCoqb̓p&MĀOAٶ:+_TX1`q5Ӓd-7qq #+3beNb#.&qif5qO%»{ƨ7a:L?0Wn.3fbK+e^GLd..........................2అtۉȋLnG y[!ʸ/-.18N]fpKax)LB,j%9'qċQe丙kC69iÙWd]1F2!d_ [ԛiW*O1pEr%F!8b?...1L~b[6Ļ<\c,~f̖n..........................2ŤF܏<\br"\g̿xϵUd#2!= C/gy3vyf`' fy' [^ye`Gx fyf~_<x?/gy42~_1fo?y3p7ϧRN݊Q7ùk^.cVjEv1v°NxTGK8vMM冣 Q˺.8T*p~*AW*;jh Ĩݻ+SvZb+'/2ϳ?ԓd*5EQEiBbefÎ~UEMJyeIJ$߸VA(1|ow!*nT=+KG7m+9QDSF+J6Ϛ;1tu}4>Ϗ#Oⲍ.呞~y @SaDAPGM.qQGnvAjW-(9h5782-AwڎhA{-T]8& Čn7_,}Uw&` I2ϳ㛑b"5 2tRDۂN(WuQ ]ׅE+v +h7t;1-}jRqVA)VV;(RbThh9l9Mԩ5o+ ,>sIpwp^ TAݑJadJg)Q ZnczNA\/M "vCUm7ow Q9IiJ:Rke߆WY}ϧRN8/^(*(7AJYQi FBQXQT8PTtY1X^)57q)(oZ!(RAxCUBݑRbq},>ϧRN7~ wF|G3LQe %Hh,4H9n*PASw4A$1pUAP•(8\M7?i"0m%e߆WY}sI;"pۍ-PNEn+AvJ\$UA1aVVU-.QslۥNg"_Q2V(XPTt:(6U6od߃F6FaCF_eeg-]MF4V жN*QA‚!DnHw/̎+GC V,˚碕5*Cpl= 96KSe]Ndb!8Nk((V>_eegWjv!̰Z2dFӂDh٭LHB@e(B=ת;jR*&¥HK9(C;Eb5^ !AɲwwwVTs( Qtj*T^ v׍x8U&_eeg58%al5n+h+x85n$4TsCUvTDe L> nܕ FD=ʬAjT1X5rn?UNÔT; vBW"+'jTGJK ,3JMBǡq[v!Z+E|KTVrt5D`݇%HL6ut:2+hȯ{-*q8nr.Q¥GW4J ^b]F8G *9W8uee߆WY})o g|AAM)DYdTt$h䠪1.8-G>VAUEU] FAѱD]2V9gA|[uTG>MF;xoB EJ Pw QTԩM;' 5DSKrǥI> g ,y&Q mM2.mSNd!PM2<*հԅ`U!@,|5(j;G嘃Hzkt8[: UHGCٰ;t|2#NX51ÐVDQ[Et= *UHL&_eegC]҃{¶jXԆ`aZ`ՆBHb6v!wj 4T:Wj "%HU5X+6V 5V\* *앥 UAG5јvJ;n ʈr\TU_K ,3dy '19)^RDvz8- 14AC0 V7HQFA$?;Fj:*:.֋u-NJB$(+xV`63luD"K ,1VMLyI?ԓa8 V-/!EpFe' E6]Z,Ycb"EUWP]l)SBj-RZHJҥPGUUS{s6^t"^i]"Pb? ,34l65?ԑ6XGKoAbjJRhjMLĄIBbúnz SRV-[rjE]*5j͕h&X=Q݅QҊ(8FN® ^eĿ>σ0K=p!~)?ԛ] ѭ-ބ&AM7atqUFբ)v2&ʨb+B,Hބ&Uˆz'C2bDWճ ntE{jnM%]څ*VYwK/ f_eeg%[7aÜRM(JĪCEYt`X-a©5)ϠjcTWzU%x+ڐٻ܈ϕgVF}TzTsKt|F$xwmZ]J6$w#>fMX]A=7w/9s]:ܶJ*#2o+ ,> ۱8|:6byz4 ܴAAvjAYR -f"eHHUy ۯo%D*#DmEenbU\vunXri iIƋXY(A=8PQJc:]+…8L'}ϧRDҔ)F!kjJ,2T4q DMDAZ(+prHL ѢLZ9D+ƒQEBӰT.!Q飼QG{*T(wOO/2ϳ?Ԙ/ l֌hƐaԆ4M FԲeDn5`w /jDA89ʭVr^b DWE޼DSa97rVZB8w)5o+ ,>sIdqFmP1j7{vjpVQ7G8skjJ"ѓZ[t]R"wa(!҃ΠJ),ȯAQJpGPGRqPJ&ؗ~_YgM_ɺl5x0EQZÈ((PUHv*n9ݍQ*Cq !舭UR.5#]Ƣ8tM?gYH"`ZG|*kqG { R6_GB(Wjbg߆WY}!&n="\]wh%vږT~Aҕ&߆WY}ϧR m!G#AT D;ћ⢨gQXgI:5FQd5"Cv!9+ QB\DUQAñ ňmD`WTUA} Ը-J"{G4jЄ!(D>ϣ;ǐLfu%A凸hDZ!ZkquJ\96I؛‡yHL^4sϣ ag<|||ZqUUE!1UVPG܅Zmk1nJCu J,"GG ? mQ &TT4T(5q^VxT^DKxVvEDRj]Za8.3WY}<5Qx1FLӨT,J,8jE|dGnV Z a{ƹ#W56eHs7ˆT1X\暐-YN4/}iWQ** ׉ UJ^-WEYv%d߆WY}<8Kj47V-DmEm6DUFkƨ؅Mc3zyyR*\Cp x_ EXnJe֜;;q/AW¼j\Tժu-QZ(WuZI}O9E41#7c5e戃D.D9X9RDa֣G ~ QZC*5wcRQ"ܻQ7\wZn.vDR+A o+ ,>sJ lZѠIdZ*" Mե : \Kmp9)QG 4hňZj1;Å!]۰궗 FQK]Q]aEUE |Õm.q6j(㰼I~_YgP"a} TTAP\"ARqejZDEjj`l$AG8jkmOb,BJ˄J6j 5" WTx/eQHp(}W8v.2~_YgP*PhPĢ+Xb"pTwV+VsQEk` U# >#c̶E*Vo{ R-.;(N㰪wEڼ(#(kP1o+ ,>sJVF c40FLjHq4cJXW堨k9[W%XƢ%gA:aB닚5=4UAAAʂ)r HHsK#^u*G B^\:AʕJ1j ?^Ȧ$,/2ϳB6Ls)`qp\fc70000000000000000n6 -.NNNNNNNNNNNNNNNNLW-eX9o,LC7aAqDʓ1eL700000r^֣+ ,?s4&Fo`卍aRi?YCJ ;AŒSI>,-mY<=#~WY2ϳ/9ʼn JNzf!15-??bnűLRcfloiLl'`L,'=` >_<)op,%\(ۺm9+œYf-V׷^XݱKk7Z3ej"K±+4nĸـaGZؐVy|dlml]>t2fcp_͝2WLi&3_edO#^7qej]@_Vu2r+NvRb{36{kv۹!'vϴ^ZNC +ovY"t۷͵} Çg +yqqAԳvkYh?q~ ;B{cfd;MALwomϸjxƗjʲsVnlw6Lh,v+eVⰵ]lM>mj6 b57ް ٳqWod-gi{hݗ s,[rllmLꯟ]]Ʉ}?W>>>>, B)R B) T!J)I B B h) B) B)R!HR!HR!H[AHR!HR!H[AHRR!HR!HR)l8.}U)~PE'(Lә&PS3D&3l` dHU>0%p .9Јׄ h%J/4sA3^Z &D] 66`$TlNd'`eQ 8S*rv pvrQCkM(( B耤m7Qc{5֡ 98!Fv pvt׌8~rҖN V8ȫQ29pe 蛳pDp\v%*)Zчdq5"):9.H/9>Zp83<5X#)us?q!-( 𴦜ETeB VkteIж@8dJ-IB \3iôced pv$#$ feQкk D8jcMb|}.}U? QHS(b9 H8P#"Dbͯ\A|+? ݂5]~ 0SLžTuB1T sVp2BM4 @E΍ϸjxǍ lupM(3:j\@C%E2a/$G%Ms>F' Ӟ0~ϸjxNj05ƕFR I"'%lOiQLYisI:p~P~TǍNlM?6X.}U))DPLRPS j(FU[ʔY#~hh*ѵp1(B5&d&6KQO5]~ΐ#T/)Q4mBu h!5º`X:'*i SFeQ&曒5]~Ǖ&aʐjQ)ȕXMlepNs GE!gJkA+Q TcգX6 pv۟pv۟pv۟pv۟pv۟pv۟pv۟pvۮV!6̻b33vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]vOUڽ=Wj]X4< !1$04CQRbr2A"@#`aq3P?'y V%gu-]v᫭ӋS-/VR;H4&d.I;9Ŵ0ۅ\$Ԏ dx>8F:U[lLx7]pދ>gdP߃z%7~V&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗre}{o_g&^ɗZYY'v^G鸩֔' YK>?o_/B#qu#qR_%RzrROauŰ8,Rŗͼ:˥^jwhZ$^K *PZUQМEk,M]#6-l^^zQzNI7-]!JTwkFQmgT^.LT%$?}M>?o_/BzN2z֋_ٹ"z(exYMBUj N%!:8b&Da )t$JXU;%8֌{qRVvutZe]x(қ]VkI/)+yZtvqNpغDm]ݶzǻRy⥖. X],qX]m/%h5FsE,,-Uu'k;XԶ%UxvN\nkW9x$dcFgy_M6d2}V lQ“uՏkgk?7_A IYK4{ӌ{4.wu&NRS!e(3 |"z |"z |"z |"zK_qY]Yw%dq}I%dq}Iw\.}>$.}Iweeqeeq]Yweeqeeq]Iw%dq]I\.}>㋟qϸqs8%dq]We}We}Vee_qYeeqYY_q]Iw_q]M.sE.&DDbQ=XIQB%!A DDkcC1ˮ4)("H8:'e_Ш- %I#p̈́^e>+~R Z8Oкg12B(Ƅc q bUG˜SAണ#5=?p[/ݗttVJ´…J4TuԠx&-Pb\ƚ¤RSSŨdz'e]*],V"c5"*Tk&TS T$$13 |"zJZI^డB䨊jŽqm)Xz B"! cB}n~v^ҭ%VMbA*21tdX)AI<Ɗ >?o_/Bt2L˃h/BdXBM2)EƆ  |"z0zL#@+3#feF\ DkF BXPxWB h1-Gfm-Wz'e_E4V4…0s39+I* bXLM!"ZC~ U2ѬZ*&GX$4eL4TCFR ,j7MxS須Pl-sE.1%1(PHJ#5QĠk(8R,hchv_D *$SԷ▇ |"zhGT… 2"Zz aR!<$hcB‚6`:5E0Cƺ(FU+fbZ'e].WRa!`Z8,ɅY% "5j5(E=َb XIʏƘ=ETڕ-e |"zBGꃭt2 5#DjhLh82CADƍ쎡h8xWBbduCcdz'e_H`P$(^ƃ*%S)gBvgBΆGM*Y/;38h%bbIGA+N<35)8OкTl($ET˥J(E#. L(PFh;R32qȼq%AJFQ2PI,($"x2C(oTی}n~v^ҡЮX!HZ.2: %cfjҩyX)UBP B`eU4px4Ƣu٦_p[/ݗt*TBme A"AF""VQTf!5[f_g=Z7+jlۂ,9IjxPk hєAJbLR}n~v^!#8&EpT̓d:i\% K(RRLJ%0*`$=bT11UZ VI/C~ E339RE".DF52HP6669 Td`!m+]q[E*$͜aHqO R2q(4P -X5jefUsaQSnqJ}n~v^ӧ¥D*"G ,\d(DK=QHh(e41డ%WcI*z2^ |"z^"/P#dF7LõUJqG%h؊hq"$hc%a]i2J%sE/SAB"E U,lc)9|w'gK3.S0P. R*f*,,D7MDZ XI*`zt)N%sE.bPA"E$0BLE6 *8#^j-$Bsk$F/PBIʈWqm U"+DY_,јlZZ36k(4TRРƴף/p[/ݗE"H(YQE51!R$12R kDFDLQgu(~ z鄓[02I,6z - l>?o_/BւB#XB"A FiQ:JD02h6T,u((`'kMdPBQC*Q`<A/C~  Y*Eh?o_/Bc($be6a" 5'$ʒUS5nTђ̗ 6f*gʒcQa\*&TxcZ 'B2-}n~v^ҭ)EFP!+(禲Tr,Ft%,*6EJ*- Z t(PEq1B _z'e]2NVuD5z Bh&T<$Y2ɤʋTEph+NN +bCO Yp[/ݗt¦ѡCB FBeFȲp9P8z.v1rХu+.DuPKBT \6RXTO >?o_/BPBZS(FB#Q:W썅%6TR3U*W2Պ*2*R̪35HbaB_<(SNfĦ ~C~ u*&,(!EJS?y9wzM*,(SE eDCxW SNԋ֝C~ T(dD Dx$PJCcccu¥pl¸!Cbż^tD,^ |"zKU viZf5Ċ)EQU VcQ$ǂQhTMʍNJgsE. (ћAa7$42(ZHx,#)TbB)$- w lQ$t^z'e]*J(,[<3 FQ@VbC)ed̨E*q'قE0 T,Q<zůC`?C~ mH2515fḂBmC&: #P)w·$6C0TrEPBm`QʣQT6<+C2*UCxRQRqy8OпxOпxOпxOпxOпxOпxOпxOпxA/~]-35RKUas>yϸ}09qyϸ}09qyϸ}09qyϸ}09qyϸ}09qyϸ}09qyϸ}09qyϸ}09qyϸ}09qyϸ}09qyϸ}09qy͸ګ{WhO !1 024"ACQart5@q3B#PR$`bsdScD?24MF$t?o[CeT-EhZ-EhZ-EhZ-EhZ-EhZ-EhZ-EhZ-EhZ-EhZ-EhZ-E] MJA~Zf#BZm mjQ-DʏK?71"Yu@UpSs%Pku]15tƺUc]WLku]15tƺUc]WLku]15tƺUc]WLku]15tƺUc]WLku]15tƺUc]WLku]15tƺUc]WLku]15tƺUc]WLku]15tƺUc]WLku]15tƺUc]WLku]15tƺUc]WLku]15tƺK̡KmVD0caitK&Ҽ&J-1iLkNtU90Ŧ-1iZI'YŦ-1iLZbŦ-0 @^MtLZbŦ-1iLZbŦ-1iLZbTbŦ-1iLZb,e%J*-1iLZbŦ-1iLZbŦ-1iLZbŦ-1iLZbŦ-1 }F0lsS4S4 _! mB\_&3/(AQJ['Ḧ"!m- i= wAT;ĞUVlzQCh)gC( Me[Nɗ5( {I2YΊ,,LU蕆| tԙ|\KDM6GInˍ:HeKO꫈S-)2`W6:یA8JOESy-dɕGF%J==`8U3dԾ_\$R)/5:MkI&WMuZiHIeXS/)?2%V}2,Aчm>$G+%Nyw#9wiFf+U" ?UyC3$@`$MF2mL5n-KYZs?\CF$TgI|%d´ޒJJ"-:l8,RIJk*EJ9wí0k:UTJb<bS,: :- 3`.1f=m@P7'c|HŃJ?ܫH)x"p9CJN$i0ɸLgPɚdS5qRTI$:m2 nH||髆e^qCɵR)A%iY`֪\rQMd ^Є)L4DѠ;L#)|a RI  9#4V|8IQSQ) sظ&E!G (a̫teXg`b(N8(T9FI'-cT *,jF&F9RIE [RIe]CDi/nCiwïp^63O??6#¹؉TV(a.I+ BtIuJJ׋\9U*8g*TvHKE8hrF4Vi:8o{1km&|z,G%R;M3ud;(` 9ϊ\5hǚT]R }F0msϗ"*Y$$JOE].1IS>s͞4IEC-JDVth *fqJb%+s2$s)LQQHvb*VNeިkX}pͽɺ%#Mp?v  ۂS(UU"s+9gdF/=m:F*r#B KRbsuv]# )ꡗZa4V_))Tꤊ ~C|,-WnugީCHI<3z{Q%\h 0D t]A(""D5BI4D~zz$g0es $v̳ y3:DdK=. Md;O@T]R }F0msϐ,G-9)h_BᴙHyfW}ʬG3Re3#Me0aa.I+,IRs IMFBPA4eg͚eWs IIL5,pc v>L/\zf=!Td5V(]O8fSdTKHT5,pc v>L/\z>b f\rNdz-HR9kX}_.$'LY WQUVuO5t@8o3OJY,P]R }F0msϐ-f,]9eB53Yz*uL/\TU#! MԻI3#)tI4S1#z66C uK7]϶- ZSh84)sqp/B3> *| <*ZdM\`R5ZyQH Gn^9KkX}Mp2dm#,2>5Pz!tRroRI&0ZNu&%EE3w6;t\;O8A&MR4_E)d1Z yh.Ka~E1,eVBJ=>|XA- (K)lR5w8o{1km&|fҝ|ޱPFaz[5pahrY nC]R }F0msB,)J'"35JgU~-5rϐ,f4T?XX%!1^139wilֲ+Pw8o{1kl؂eq,*2-DIL+qIaGΉ'ϝ+ YJCxT*ē%:'u׋SW`&ӨMtx%ǘ\t9|XtD9 uK7]϶lKL5~ʲ ~ i#&5WUd坅+ ^4MgpD*%8Dxe2OIUyMN|:ۜ2* J Q$F)GUw]R }F0ms횓o7eiu:iQDA>XET!r|**I/\zX,dC= H"QZ|J"3"UF\q2'Bth& )UϞ`kX}\C!Ҥ:n9TTJ!C)}膚iSyFQ>bϖg$Q,J*fzk3:4+UU^yN^YȹIGDtxKy')f'=cK͈a4,EK#tUkPFYĚ+,DfSdTHכVmE^)ظ8beTT,̖y'άӜZE*A~T{Q%\f ZU rUD|gWIe;*+)=z; W>^mȥ9U׈IMy#9bW휇U+I^.mXtI<]R }F0msaht^ *%D`踨jqL&JVUr4gVv|Gft+;3*s3# BfYi:']:bxts8o{1klײ-C$TJ$L$Cy48fW%Lf4' m8Ƥf BD0d 5#3YHUaHdZ*)6? C^jm.ZY nbx;sKye<ҝ3L#*z=$%]QKqHzv_S%dl9}%@USQϐ-)Ȫ*IRV*4%V>DQf3(8JˉXꆺa.I+ ZW-W GbJ29pH: E.~1Ej>`du iĒ5 WVkT{Q%\i0es売΍1IV'DKAQq3`kX}_ TJ#I*e*F2#:q>kX}_IĎQ&M%4en1$gYtYuL5,pc v>L/\ҙ RSDBډJ2'-65*%JL<諜f~T5,pc v>L/\Z$HR3w&VcDgQg!+]3 $DzVO>z8o{1km&|qȳ5>n#\ZNfzK>g?0T{Q%\i0es~їYY:Jb4VLʮc,gQn~uZ0T{Q%\i0es p5/eE1>2Nt'\Ӫj$ȴ5,pc v>&d+Nr)cABG0*8EO Ydʐ]WyMU.>mk)429H"r2> cKiŲ"mg/#iF>r*)"'9aX \Zad#YH'KhK*E'2#E:OʭsC$Z$Ka~9)4G2XE'D m45fS r[sPSQi¢&KJSNE*ʉu 5,pc v>L/\2u S.c|QZAN(gz8o{1km&|{3)Wdꆺa.I+ fwЗ8:6ix@-DqE͉*DQ35~+gin{PT{Q%\i0es2ͫbʅ)+6s2IU!`kX}_ h+9g&*YƇ*I9 k5W١*,ΨkX}_ XƱwb5kx2ť5k.]XƱwb5k sQ^i bhςFtUHZ@ HZB5ZB5k!<'g b5k.i Ei EhBHZ-hZ-!i EdӔacK8퓬TTXd=Hz"D!?C DA$=H)p\mRIE& 2mxhX {/FTdP5ถЅ:ZR$=Hz"D!?C DA$=Hz"D!?C DAQ0`[iHNIgRfқ΂TJc DA$=Hz"D!?C DA$=Hz"D!?C wSƚASQ(60ktHB |} DA$=Hz"D!?C DA$=Hz"D p Ki$Dig$r14n%ǚZSE)5\3 iL먝$ f[0B৐MjldhD!?C DA$ ))TEa.Ֆ!kc"c^2RQ9}6܌(xeo|F!;["d$GDλmaW"WA8ФU\\A|T~^]&t9%2:A7q0%J!&';*2Z GI%aa.d>PYhZ8GI.OJg)@-k`Zm,r%үXZQpTfPDC2 4k5R.0דJгtNr>;[jj:#/ ߈&*iipH= 2t"\2"91MʘOI :Bi>%}B'1qkzpGT RItZ2EB-M.:Ȳ+'WIUd(rCeH!Z5)G }F0ms]BKࠓ#{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Qu(ߺߔmo6{[Fo~~Q }F0͹\Q؞qVc*epuN3T}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}tP\ dεYa8igU+!1AQaq@ 0`p?!sc3:hYf4[K-f  _&J\uM7!xn+\JW/|_%r+\JW/|_%r+\JW/|_%r+\JW/|_%r+\JW/|_%r+\JW/|_%r+\JW/|_%r+\JW/|_%r+\JW/|_%r+\JW/|_%r+\JW/'eрK瑧^>+{..羚#S׽_?p5G|q L5#z_]HNhP&iiiiiiiiiiiiiiiiiiiiim H|2cD =ciW1~#wG?l"Sd?l~c?l~{VYT́Bs?l~c?l~c튌²?l~c 1L8?l~c?l~c?l~c?l~cvq\RnE0:iVg=i;xrGWv?|;wFu2:r{o(&O~YtG%"ՀibV#Qpd>xâb@k8|Y/R/a+0ݿø YT u";ЛoCd>&O4#߷^4ED\M4'Ruwg.iKJFZ+z2 Oh`"z H*e]ñ ex[%ûWf.Kc52oSla5ءi¨(_(4/Ìi}Is.h2iB(9|㞟?y<7V ̖p綞])z&ᇐY:zJ񺩦j0Ɏ)8>ҐV]l*q&WĐx#e+r҉iYpMnP2_OFla gmWA+xibZdi`ʸ%)1:se \kKKGGRn?? :룆?4p٦qk7W;DZFtuwVdVڿٳr%SM9T|U/duld59x0jYhS;uAf4adA<#Q7&so7-vVۻ274FigaR":-gqNSA$nSOlL,>l2Vg]"A! XMp%%w'En,?#pYv1D$D'M9C"YYrhJ3m?ɠ&Д\fdlDIaIzLKC+e.sq$F=f0` A5Xh'iv]5'$k"!iElhnbNcDo REwz/"LfydZ5-&dlt^O `3K SH QUHB6J.3ULIU0bB$B ;Yv=oiVkuQS+/CnkWT9ץܐL~EpD1KbxOw] dy,- !!l0G\tLENJR pgU#ul#B J+ZjEo7 {rN{p!{\齧3EC7AXkʊ)(΃Aa,9-0U{%J@f W m(5M ]*u2VoP֢L XMbB__؋̛6FB)sIvNmov\?"n%Enw+]5vh0Q)wye&DəzeUD9[v FQ WHs, fУ]8);qfpXI~aA 5/u^I2ۍ31,ʷtF"ڙh)$6j;jh(rD3'BWEb1eL͉rǮu?GM9|Xy"-S[DpmHUeKwDGLr!3A!o 1R?CEQTѳMڄ]; FMQ'v#hL]aWA,kЮ֗o%˜6+Ɋt%RBqĸˏn8tfr~?sx]2N#o ,f&3RC/"`M566B g!lF3`K4 nRyWU7C섨TZ `hh LUGa~\j+b%Qr%iY"Bwp&j4pk&F!ˏn'&0jWMw R0p'.Ox B e< BJC( vY6lZ7ɑ wUWE592h!EBtsM(wudbH::9PSM9F&فe]=fTQhtҼatv8ئYDVs3ixĹ t[@6`_PURFZ֣ƛet+) Wz_~e7M!khx^ZP9,eB4&5 FƳC7fCKUҐ:qKnIhз$0e :, W"\Gq Iv/pa;Lc]$AK5ICuU.=¹,v*V}1ƢzǥFA7ŧx5lSCkH JaOo3q #z>` 5u ,QIL .s9yЀsW"jPUQqtj8#ƕ7˓<]qDLR 8*ډEF K5f)"]lV 2-D[#H[UInsw)n"aiiرF=ucȭQgKаjU c,\tC_5/}̗c1tz趡m"Y3&eY5 !,cq -۠5TYhtmF$I %E]p~bޅkM-niKrnkQ1K]ЅΈ)=DihQ& b!+?Ѡx%5"|V5Zdb2l[tdݒpCQ-jȩUvGS\齧1p lX5L3BИ^49_b ,Β="cX$2zFc1h)fX;BAF"cj*FN0 !,[ NPG{ưվ T&I\齧1<k=Ew5A l#! CɔXtp:y)f_ FT,rEˑ=M!C>J 88GKLR蘋'Ӟja*V\85RfDͱZ;`r5`u6Y IN۫\齧1M] qZ;{IsbNP[Y8kM A !4%/G|<, %= E4!F} tɬB1)r0FX@ A߀/6;b}Ph[q j\ \HjM3P*G-8EMA+ر1Lt = 1Z"*[چHbkGH3RL>F)mR2H5C{Bzb=oit'?O+ѧ3+m1RPJG%z$`0-+ZMJ f!hS ٧!uŨř0ʾ]='zkV G]¨o a^XEV*)W!ōAГ&llB-Cx&&IPё4/eL'\MLRn]bZV̦TӈY$fx ؤɂS :8@Uhj\JeU ϒ| bnVԚ29veD5u>Gx[8N҇"e&E19*nhZ b&rԉ]i 9=z8<2f?ٴoK%͢Ɩ?D+e$ߖ waMNG[w'uEbS1lijWrͲu3 JLjA < GR$ɉ(Zq瑌Qrd qHo Rظ&cEpiƘA t0cط5]Xӹ)y#JLxP2[hmuIX5VY:奟]7GY(>NTeWJh/a$E䅞KaBi1 Ь kdW\LXg(o!^T''`dZ%j. ;%q/fxCyJ knȅ44X3y4 v* *w#D$-!I D4'澉RKR[k !{NfFumSu6`C+-&\(Ry3Vb$| j2%X% ܣF[DFn]#jzDf J˔f %AHY6ucy(,GUAL\Z"@5A""JeGM9$X )M*dpk"Q?(cR*`-t:CI -aN \1<x rO24M定4hIEq Ax*M[ (rM&. c՛ˏe.s̪CObasgNnRԢH!W!pШLO#.Pfh"W@ 1r-QNYw%)5B'ɐ#le#b K -ҷfQ8sȇNQU*A8Yoj4d%y'W(\齧3g#~F!$Ěy*q*s 0BغRQ(ߤIVL! Y hZ(wE"H-?$预h7<7kLnT6<ΆЏ4:\,Rbh2&wm)5dGhX~C {NfSՏDph4ZO[*$,)qx0o؎o(Mxy3^Eב 4щ5vUJ5ѓ,l<`H8h\ "D ttGcOQԺAn;CQS5p/#aLzYH"2F9\fpЪmWr|+"ZL[ %@`Omy%0`EW?4ifH?_@4W3sɲGm$>J4$R*# D|KGU-I)pIl֏aJAFQ=Rֶg{G{G m`LOm* T*:iXz7xG{G{G{G{к>IO*fصq=o=o=o=o=l%*#jBmW.o=o=o=o=uV09m ̈GRz8wٰ":TxbHQ=Oo=o~^ UYrqev=Fב)7wh`F4v7fvq2nn(DX/? dv.SJ1_Rv[ukD'Wcȑ켪uד!P{tü#-#Dr}aߖEwG+k}iZO0LՃOF%-M *5({`5#p?I rI$I$I$I$I$I$I$I$I$I$I$HܒI$I$I$I$I$I$I$I$I$I$I$7$I$ [ oo׮SO$H#2@ٻ eBıD?# ^B1#rI$Hl6 <-0PRSnnHܒI$N&̼3Y z *'p37$I$bx!8h?0[TeK č$I$a% nJu[:g$ACx%F\"_#rI$IbS׍KZmoxH)0 HܒI$f8% Q#e:gsȱ"bH@_q7$I$-TVGs ZU鞇č$I ,HlPH|ԯtέ cq#rI$Ix*~Xh i{^юHܒI$~oG/ '^?czQQ.7$I$0: e{2kWTJ$}5>$I'"Q`SJcR r#rI$HHP$ޥlg邽vg(Hܒ $n'ZZ66:l;s.bZ7$I$tУ"8pw<4$I'I$B7%jPZG-`VM@A#rI$HTf ^&Ic􈑽NxHܒI$qY]N ȹmiE=m%~v/ԇ7$I$ HwS-9- I'*B1~/2vX7J>֦#rHI}$P4Z7rs׵2iU HܒI$D{[ uj:= lؤn7$I$ɞwe5@uO?HxLič$I"OCr:WƄ7{ύa#rI$Iye~\wPOF<Q37$I$d"f\t>)4Wm6Gg-r"}D$I# s,M&D|8?DL¯#rI$yk{xj yT@#' -zHܒI$=#*GK<u(?.Vs7$I$a5>e Q3cD.UM$I%"e'XXYC8-[Y :a#rI$HI5{UÊVoe-RCJ22@HܒI$]dYr%<-.kZW?]ߡw*rT0J ϘV[0ǎIYu.Vw0!wg&JnuIeE'Yl+fRhv6b`,B^*2@1;Xt54?s$ZkI n|yގ!ǻ-=ruUp(˛Xl_uR:w}xFN# -8c-ՁxvO׫HN 5A~{ 8aA_F^@fiU2K2%erAgV\0E:E\1d6:iwS6ɰVP)n0?ez -jHl݄']dMrRI,ȷ=|yމ'c)eoJ޿Gօi|:Bt7Ǽ-ʠ ]胹ڝ£iDYN}UEvkֿMFPe].^ Zд])?ҫ\(/3;>Y{~G ^Z[L xbmi47Lp=%@aG+[K[G,eI3SI]`腰W3>Gq'4U*CQ.1d v"JW.3 >Y{~)MD8F"bĻjvlĴ8MWy{mx;Q@&b# =61*/Үd1"1Nb&啩Ps6UX\mKb"#n~-ԛR\o71AV3*򹔕S_#g.-:ELNa)bb8jxc1TꌭܫǠ] 2 WV`MDg30G;Âd&-ϹP5EzUzp+sb,eu:qq8KjYPP<:E{JBe)|yߪ f!Ȇ J1]B338\5JfM<Ʈ v왬jSqBbPPF" 4 >? /Hs \sY*U&=E% X@c2\{1gU)K 9 ~||* P$ʉn-6z X=3j>a^ \q3*2XX=J ܤĪ%"bak2'NgL)I}c7Dˉg*0nJ&3وʖLUHM35(<,nڗ1RJXW `c,=UĢoəa,ܶ.* nb+c 9\UcBDSLe\Tik'8?ExkG7]!G=w`^M3 Sүם#A0ߦN= nbV}%Yq)~yXpTZf[q SRQļܵm,B~#gb*KܳNUu ĶOLw% 9&uLne/3m^Zrny&Qq6 <ĸHOe`7D8ܾ,_?Ժl.s5Seca^XpLަ e S"g^ckG"f*󹕹]6sqzCqI5qµ6A 5-JJH90e+mm1QCYpfE" 2Y]MXes ̤[en^"Us ab V%^ Vocb!c t=3waLG?.e_fQ/Tp- b^kgFf=+PUJf)F(V׬|*)\YG7 ʨMsܱDhNbaq,Į5.^c1&lb&!9haU AajV|P$EX*b`m ܿ(Iz nZ4ayrĂ ܰ?7`J/2qqЮfue\ӿ XX6jTG1:sj hޘgPΧx5.ۂ"{KY17(._u4goʝ?-O<bQYWHtOx"/>RL IB! Î%2u0;D71Kɩ!WN*];ΓV]ƺKJeT2Z_Z:H&-/Qkן~#gGu7M^7.kngy&*.2A+^>lk5ysʃn؅M'WQBf9k zPX\J1jNg SUFKk.zyLefbw 0|]s\D!PCY\Jˀgf!(PC:B oTi(1UWo!y&ۂL%, 6Ƙ-YqJ œLޠ^aUJ%~wk,1Gs|J"DaU2 J48ry׬U[-@Β)u7SC,=Uqqs+Ql J:ẕu#[ ^Fs~+,AMBcUĠ&(YqJ`S[BU)V\\LW{{qq!L$myT+()Y\T˖d*fe#UW2 .TTeܾv%[S G|̽P{M7Znk{#gꫣBm/DB &ˀqPUd/lԩU*kq]6`_Lzn"՞yf喰&8Teآb6b\ʊ7ʢnygUmWgFc^sg ]ן"/L0Bv3}[eP`nY0w+0j$B{ |KVƠ\n"5"<"7\: 7e5sW .6)단P@."rQ5K੹\ʙU;A`DTse<&=0~#ggCVEn Ǟ}- R .pߝbum^&9Әt1bfIJK,2ϸHNX2e@2q+?hQU_7 rRܟyfg~<1!T[bTqF*7e5 {BS0)U)U^g2\` }3cXb-9+U3AMBdtN!ܗ)j% nn% X8 `SL+n\aDX\s9_~}\SMnUC1^~^ʶO%m1: v)S2=!$*g]"F =  M+0q "*M82wyq0GDl6U/4jvf 0"0NކYT3[ V,1UYPL1ЋR9k|zZ)Bs@QgN)җ7s5Lz!zz-sZhd76RTP!L5F+ w0ErML e| RbjRQ=Ao]y%TFDbAF57cI0ǜ|̡j%gb.)3aW,' ܤc Jg^y?g83b5URyfE11B}zg gCY{~):D4s3ĤˉBF.-Pr_\ZcC=w{ţ, JX*0V?PNlA((aÈAAK/9/Evy">*35zPQ]-YyYrsu G2Ȭu89j(3,u>G 1u>G3DK&LحͲW[:TD@L3Mif@Wh-U;Ks Zycض ;l(#-w2Qn +llt; Q9 -sbe̚+QgoAh0«09)j鄿MGh 6#/my*7 e P;w|y߫RMFsLye޼r(2j8]yTbbmqZP.5X(yB M|y߫ase<}Ebzk2C~yןE<éuyRԮ[:C b_2ۨ2A}fYg" "^]aKQs#VEJb;^X+o GY{~GJ?}A9?G?5+>+pfqo<_|W&y">?+>ЁM<3wO_D}~Aua;>B9yϮ>~sG}}lKÐ`Ǐy1]\>hOb0߰Zܫ=UHjj9;oy&4{0wg~i?&?`<׏oP{?v4 |y2 ЩK?_N$`r>znQ=Tz}f q뉀{%˿U￾k3=v o@]G}69uw<6]i-.2kKw<`xx?a~?#j{???-c'zD){- d1fE~=wɗB?OO[_ |-?_+777??7?7?'?'_˚_3k_3|?_3|_3|?7777776|||a6~|?_;|򯅾U*~/|-򯅾P?w/%~ۜlCN[EzD0ov&Kc \N6gRetD1ɄKdɓ6Ӿp6i;.rL F0N L}$Bv|9{Yae.܇u:[ocgv]>a/q&Xy$rHi>ŇACi;%GۉϹdOgYmYdi vbi.鰹/~ۼ2hr#̻eINZ:T&^669l2{Em m$,O.,y6Lu{2rHi.Ʋ@C-}݌!?w\Ðm׋E&ZB27YvU6vO/-ȴI`]g\ͥͭ햶,rlvlmyq-8y|?nMtcϡ. [7lIz7!HVlL[8al2fYѵLd{Ctvp=3#`QfT7]!F9/bFoa,O?q r^ˣld=<2k>LlqGg!2)]&kcS'Zv ÷VY$˩;/>JXYH?qn?MPX_CI(9T0bL$o)Lk''Dɒ\u8. ̏oDvf' [7,-⾏~چjQ{Er;!.,/hP2`- s I8I{ѻ9qiiBP{e_hí~`6$JUxny8Cg͍\?,}r2d&GNF`% PmB흯 "KvƄ{7s K ZB1vy.HBg!} j7?ӀBAA4d+A>i-tB$( 7'Ia7 #.0Ky 2Hamՙar[Ko-(|?qqiB {_RpIeod)!6$>{2"b#ȓ1fŅǓb[Dr`dŽc;jF 'm%Ms= ;{ɟXݾR/!aܐ" īŗ>,@l=0=KB@;=3.nI9ygѱ'3t ?@Yٰ/~ۇc׷_V莡HLyZYdrG4{Wvoio AV icM],y"'SO`dYxafc=Gcxrg9]$<}TMAu5$W۴^' K6}tȏXn\<ܹm8!cN%HYT6~̩uwαI ̷r#P:z;}V|eg~ABǦV6㷃fne [ruc;ls >H=cr-ua2]r]6IvvU3\=V ~GB<Kv>} ,v7CLi r ' B]} ހܰK;j{ LC$\b+Z[BH!u&vEr@ܐݶ[o~&Ԍx;'63yȒdwR$srljyl2xk۫ 1Z:X2 $ؾsfc!a6Ժza6> "~$|y k.Y7&njrjO׽Zӑ=a#'- Ć8w^_Ɋ5_o<w!$_ {-'Fr1'ZK3H!m![!d2 2vNے[f?w{zf!ĕ6͌Htjh?Kx|$ q"3zvTH- | +c2化hf=[H?[eR^sI ~Lx\.!;lK{}Lhl7۬3l<-&:] ޣ9CqC-餖|}~Ϝ+cXfr塊[P*{7OЗMyۖ@߿vbok@fK$[=loNC|BBm-Mb#nzv<${A`z{ rԶYc WmSm=y(ll lY<cbxD)oQi7>O[n heyc&zo]=ֶ_o8^#шOSLOd+L}xz˼ESgjNi=Cِ݄ w#FE||e i{/6:v1xv|O6KÞvM?wa='X[hINZKX/ge#3TBlE[o17/hn:%|ݧwo&۲!g!%}$ '$^yf6}loK'ر̄j:=؋{ 1̗rGWŇv)LJoydӃ&O6+ 9u`2vK-HLt$&M'-NC }PAlU|?wB~w=ndjrJ=VNKtrYy;t$3d&:(ŋk/ z_GH)ѐKK?IN|fĢ̌W[{װ\6+?wse>F dSoz$c$e۸9&;u۫ڏ)t7Y^2; Y BQ4-e $`߁clm|ې,Kߵlľ&i`6@܏9hc,o>ĻܱŃߥŇWV^ԲR0o#,e{Abr ̉!{C<;K _oa9Ka-0vCJ'y.ێb Ns7ͧ$-;jrq6  |DAnv".7|$l&7.\Ԏ{uYH!)פvNBn ;ȉO rx|O^{i: 杁<SV?w콡cˎ9"A:asz1fd4dh[9$2m}׋RS 8XOӗ9gaiNjljO˭,iNB$νidpX3" ~Y@O!26ω)ll(Í]92Lōce~]^{eHp/bgo-KY͐ݔNvWL<!g<˳t3Qh}'sl-awdr-3ڻ6Z$/(ոn,r|'[[C샶ua /2xc%H۶G60@˚Ϗ@g8<7˄xyimϘ1;`x9ۄcrG,/;a,y'Ac۰2].3գa nYѭ/u.YvdiُM|M:L$ewvL`\y21 2 K;7ЄiAY_0PLn.9ppe=ݖ&z8'=Cۯ7=dyWw?wvif_Ui0mmmmmmmmmmm'?9ߧ>/+!1AQqa 0`@P?N[pĪ]dd8D4զOA}~?xsLl~63~?xO'~?xO'~?xO'~?xO'~?xO'~?xO'~?xO'~?xO'~?xO'~?xO'~p~B/ 眷G|߬Spl\d{$9mO9uVa4-n7sy9q8O~'?|>q8O~'?|>q8O~'?|>q8O~'?|>q8O~'?|>q8O~'?|>q8O~'?|>q8O~'?|>q8O~'?|>q8O~'?|>q8O~'?JC':beZyl~֋ &-&$~?m#I?mg?l~?mى~QA,67֪.g~?mgl~?mgl~M١l~?mgؠ 6e[Sl~?mgl~?mgl~?mgl~?mgƧ|x2<rD`1~E bUaAjrBDjVs<[7~$K"<U%!xT,'b/jjZP  L(ɼ, 򂯻 ^USt+(i2ӕ p0s(EPjE5m&JPa)c"kRd;I%'}a!U*l{ fx3yO)_̩L"0bϔԓsQ9A4U"@T)2ijL+RO(8zTJ 2RR\ :,/SyEyO(ut܊DeEX /t%0^VYA*i-g9KL Ž( 6.9Aj$R=E&-[y1cn廂uR9gK )^>[ޡ'= b'S3W`w=-ܷrw-ܷrw-ܷrw-ܷrw-ܷrw-ܷrw-ܷr5^<5`%]7e)™Team<.P]R˵FT<Ԫ730k@ [ k*`GVQ3\ClE7J5~=?)fEO*ۢDRmUFËMB:'sA)`+L`SlcM[(I4C rڬ6cSe2vgכͰJ ]]Xf̲%(vxu.TvӆVV/8xp[#4UVn"9W/`lF\P)1A!4#otuL]'*Uc)AgC`-OD`@H.ݎ[]Z Y]8Af)蚆X yR ̮QZ1 /U{2֪,Q)8.ŝ@؝ܮ% ^h?HVV/YOeV](AH8+?n`,ZL =?)(b-_ 5})I7f/*T oH?q9F"ʣPR+bq9fO̽R͜#"@Zļ 9Y&(7 uKak üBHcרUy繨5 "ͺ\=铌KLP[Kƶ m\T,:C@meeKV=S=P-]g^E-Ёaxz *\(rS,plRmܯlx.^c;D"&X \GFW&(8f!RaFAnP\X-mUdq"hlqo[hZc֢Wl\88MTgz#hӪ7 TĹ~uQ-g_aB(GHHԭqVbm`[itԱnʸ57cQwy%[xysgĢۂ.#s0Ԩ- \lm@Fӈ lwvN*0_zb(ӯ"ܦY}9I_uk_~pq3ՃMi.͒(h(8KcV˜+7Uڸ&S.([v\\YU"sRyFtn&Kkbku*GYS"1PYK)7?W;p`tymA)S[95pUS۩X]VmFK፟#OzJ+Yh~%:r(هrvfRQ]2fE7F0FG_団`\k.KTEL1PK5[ߧXR;mw-qw%A8FdL>y _*k.+)je /˂آm5~8>CР.p>ЋG!{ ""[2nY l.cK07G]}ʆivLSAc˵#5,do2&vΆ`^lt5mB[޵ߴsUȜEZ)LjAq<l=1Mrܺ<-@}#+Ō>PA及܁0-еh>4,i[e+>e V #u͔֢ژ#vR娈;4-)[+Uul.#3X&j[R7xSki=?)fVTl eLQs ,Syf*2n ]EoucF7*!BMOȄVN2eSdx'5sQ`8#ZLJ@[٬Q yW()sѹl^+faw:GB`1']46Uqȕ XwC-Ʃ!*{XT1B^lXn#'#q r@\XDS7Z=2o`Z0],Z^-&\M^iX5|Lql; u O1N6o 1)Kj ^1ap{.m@lੵi`xkjV _q;/tqe kԱyj& =XA21 !_6zT*Ia[Q |̈Tʤ1g*P+^w)W{V⡥ 3NYG酥`w,%~[f5SN75,dkUD0Qv*ʱ{73RhԬ=Ǿs-dc/K5/@f<96 gG x!<=2C Lě% s+~;+hG ׵h":!,`oYX"Ăp~(hyҊl-@ wGۈZXVm  ]{KXuOb]b\½b0: SV-` ۀCVBmT6=xKy>Q "/$:z%ʳ^8yO8p \D ,qJ-#.@lԩkfi%p]PjXmh=;pԼ8w=u.LtXC ϙ1N7hoY%c-)n&Io1GP['9n;):(M]!]b ýTA,&o7Ǥ66\e@H"rK_+rє0CS qgJcpo"%7Ap fY܍@UcP0 w+wsʈSYb%%Pf W dtO^A{'0*(Z4Y4x(pr/Bj! Vk1J^1l'&- wWEܹ@qiG%jX#X9=֝NѮ:kky) 2ap6!t]rmCBL ; W +asO1NlpnD +/ m!DmJL=.KHgph+W\U&hY-6e0$j2Q.ZWX9X%afE@B+KfX񷘷&z*Sa1@V5YD^s0hקzy^4[E-jQYR*^ ,"acZpMikWQBsQ,O/Rn*ٷX~s)ܨxvKE-Ĭn8Rz$ X|E[:70}sBy"ʍxMXc[ܪZ]}˜ { 0S@K@r.y*o,oEIFY`mY-d}> ǮPW"*7ƿ?)f0-/*Z9o7hr {WI I-8\-H3iuVC ) LÉQqs0eCN~aȫ.l(~`s8YC nԊ3m4ų'lmcvH%YxX%'*Pio_yMEC;id3y0roD@+S.M@C^<OZ!Ez1Rui<r Bc_"5+EVw#YxɸU :b ]r7A_laCCx`<}z ~S bsZ]y&g6j 4Ke"M´}"Aw"n[Ie%,x -32,f CH_IEz !WTe? CELA/70ŗ#֪5p\j}"TP ˊ =)9"QT+6 UG$J@w)U̩mP5ј#~"Zs`W(޲#øT揤+ &kI,R}MH!us `|=8^6H++n1)NŧZ\h~=g m7Ol@VSJpUE>&_[Y'k4)r7tU8E͵&`Y\>YR;Yw~ mud9 z( W\/Ex&G$bf { \r!V! j RBb32 ϣn)p86^0 l jfJcP7R*.Dg؂u O$_yiq̵|TR.E֨օ4fGHsg,OBQy^(zlٗt[nTg35PYs K"w(Yk tK\ A9tj+S}Ah7{\EkU"pH\*+cOi_iJ>b]aMחF݀]sg_%ڥH/!sh4K4(+mX ZGb\=۶5튫FVj]MJAnBSs?챊'i5jU Ax +0iT#W nUY̾ˑľ`T<,d fQ̧PK1SaZ07XeK q  7^H{0VZf\+Ykct» BЃW WdYdPHXX-z?VP}%Z[c6oƥ󊪚6:qO!Q`?P-PJՇ(aw U<=Px/6\-\KWcDi)b +0ӔU,YQ>eEiƾ}'_ƀ4#1r(}U6ͺuL9feKHrț^~|;BP۪u oi\ˏqAOPfSX~ zmWuCDy9j-+Z*WOH+ TP zc4A!ܰ5iKu*{h|!E0Fa\"l]cu.LTy ")/K^*\]ˈ1΂TӘ0A@lO!K> afd[lnb^B9r/1<ø6s#\="92;mº-- p^n`E7G,K_f*E3z ~SוrlRn 3u1]DQYz%m3H)}!_CRUNO4y&TG)P'#eRj6/;b1uP-Z gp\F7=DǠQMz]mNSo)/jޡMki7ڜAN}"9HaSz ~SxP `@D\?Q0T@^*4k3Ý԰m zeg]:cRTj=IHgp JzNA0nHǭOw_cvi?1ld+gmP~M!}E"/<!4n s` N*^dʻMDVɎ(0na%5V^ի'%1+RBV.:eVL#ܲHZ5͝@ZQGMə F4C!lbTm=-tvRV%sWG`$E/f- E)rm&J=NitB+s9#u.,Y)nz ~Srl&x1QJ#%jt#kE@6JYxm+U0,*v&GE3r4ycSYSޥz} lu0w E5?\Qjε3Q3¯Tg.c,n)BZlYTߣ2si_fψ4o07cs-s8wPLХrO"kF-טPj9Cu#e%P9Q"+qcט0—50(/PU3(,zo{TŗKԹ+:ՊL֜8k%5×VELߴOнFWy~^O yGg- [n73C)YR"'_o" ZQk6T ­u xbByZƣA..yc\v׋9`K`W_=,4@ mWzgqhQ eMS B˟Hp2 \*TL;U2)ohɾb؞dؚwJAJDaiF )Sq_er%x 3K [1TVoR)EtAzr[n5LRdʏ}gP^=`74#~ZBb{]01i QJeKx>0ŗĠ*A K ,F0ʱ.״P$5ӗ]D;zFi=?)KvoYUT__PV(}1~"ѢM6*ӹq-.( d\0` -a+X1#(>uQYo|t. G 9AtZSzKU(1YPmbq+a'vf7' "-6U tZs[.Fp5zƨ5M-ߘl.lR_OQ,+"a/:X`i2q/UnP,1['YF[w7|u ]pg>Zp D+) 8LD1Q<n˃ik_x.;86D[ Sq}p̬q Zv4k4L#Q˅`UXQw1+j])9W7uiwZ6"Ň2A)ԲC=a/@^}aƞKKУRG2bBT^}߫ A]oSj1FE^27u-M}jC4<8su׺FXh?ihf_3/7!BjX ,bjr*f6hv!:U)>QW%GabUCX?OjXqy*39## n9vT9Aew)f UAUYoJkAկ-%xH>H`2^=XJ4=>" jGVVW6ˏhP ƏaW1p۰IZc G2.WcQF,v@8﫸 zk0n3*09kx [)l{Fl 汈"Q7>g:,41փ-+e¹b`YA4툛WePL!;m! Us{?> RϤҷz yP&xv\qK)ZwH&OX^&P"@jĸO9 7s:,m닄y>+B71Y,kwy֮{dxޣK_*sk2}[,K^v40د+E0?='M\*˛Zw/z% z&6nFXcOZ%yy>ˋeW cg:,: YKo90Yn}@i3[n+u h\ƪ>߉D2MH.BRzDN&-1% U5\{UkJUn)J?xB4`Pswv`,(vJG0-^TeesH[O{&yUbPhY}Nvdsx &khlv-,ɓ7xl, AX)-CW?=c{9] CO8:xG'_vR; (l^;69ré`s/@EÕ1L ;Jr0-pn`QgУ+s,8xU}fGA9%y ~VRg8qDpNLtV%<}fof_D׬lLHNe0|) s[` `1C@f$0Yc(.5V c="/1n{Oq %!X3R~HD_~Z3. 9nQl* O `)E:Lw*ψwdt)5-3 iis-ax]T ֜Jb ۊ KhUhq>.4|] Sz ~Sͱf&2*_c}ʀ%TKepEK=%PV'#JSzς meoBxjZ}i*p㣻H=8`aTu-mz@0s'FcGA1"L%BSZb._RU-M@_@n&G@$Ka&9'2KȐ5Wʢ7yMפSTĥSz ~Sͥ7E66{JI"3;zBg%혐V|K ClZ>9R phR#6 2.h*d oPt|t1mZlSn5-r u҃ys3<ܤ!-_:S)x!p+l޶=!Ө^egQ ncOiH˝A=LF!Tk ")teesvΣo=K-k״ d1UXx(cyz 59&9n/h~",r<}Kze1uL* *Q3coz,(Ɗ 46C-W lq1]KhsiXp,rk9_gΠ3[,b-RU눩0׼P{srzٸntӏcu^%tЖ`R-[^9066:0c`625 ֬Ǥ`&Y`-sxN1JeICD#eA&"M{JWaLby,*w!VRl]K_UDz%y u5./\!jWh8$Vo2=eT>2U.ZZ8@)uX A3cmn0@א37RGn.u\K *Z /I_B:l8Ƣt9cx=&+rSw|VV_0 V2ÑEZPOdYj`ft,y]kB2 r[ЇT-er& s*1ɨ9hC-9nOh*hP`ג۝/1/s)DD{do OO2G=nkb(ںZaV4n2c(f5fgPXfLQ1bjQP"+k]vꙏۨ雄ҰH`U uaG@Ӗ̧9<ʍtcdUb+1RT\5) zuv:fZA4HYMM$5W?H< >&;"d Qx ~̠C ՜تd)í&% XD6ӧE9%eyN'z zORU Voh/5(g 09`(*G92Z 6fWwyW|ʆ1xW1M>@Ҫbq qPNK>;mm.CYAG 3^Z-,rd4ū 9Y %K3`Uk~@0cNyJ˨W05rGXawq*8q,Z_H~i@t9-K]wԵ2 %1TbDQy89+ !YfL7ldW\f[+LU2B%odڐ3؉Sz ~SX[jǢo?qs)o8Gf .McɐŜ(u%^/ruJN%o41/ -?ܲ,|'L^i̢stQi`"d[%=yAٗ4J:KxWՊ(gkiDb&Ȥuk+!}JX#levihF"ވ(y^#l~S{u76-! ס-xvtHvaV;UQbV?dU:صEQ2W)rybP0q\1'hE 9]/+C8PV 6 S=qS, D= -J21#ơ0Pp'u$(oCyNjjB8/q3NNZ&E⋅)voHC%Q cA is''9Q>g4s^) 9Ò`M(-D'fhf.Irl^K3;1,r3 Y0)$!E>5.>K柑'[#{&78J- e> Y|aJ̟+3Cec3636X?Ili^XKgVdL,3MROH`7` 8s֖VL&>y [#5cj>zв#eN0/, y Ԁz9pnh h p@"H"V m`(U`?Q Nsn; ` /Ÿ?m5Cr %M Qh@t$ĕĭvuqH2"9HIRR"1L%;r)2D1xRP$-ZJJ5Qc-zyS'9K+NBEky2lc l  ')*S(* JyJ+͓ơm] (㔍YIʅʇ{GTTDPP9"#tC:BJ?JK2Ks,ެMfݜQu*O@Y5ZvV'uSEY{/V43`5` SU55F54}5e5ZnZIZ;h iӴ];jd0)RƈDZGgLH7\7WYISSoD_[jFDAn.F [ TXFFFƮ5ƷMp&Ld=&7LaS[D flY995sS3,ύ}n),Y)Y[Z[6XWX߶جiy3loymi l7v~5 Wc*3EXu>;9f8u)٩ip|uRK>+۵ w$C=,=D'<>z:zB| zý˽$4mdqX{5(ME  v,x``akbzl"ܢEX .m5Qq$3B>bID}HHiܨ5Qעգm11Zww|+'2^m}W_O\vF{37]oumkw~%gs_.@s/O x+᫱?|mnvD [N]y;GG~H0ڧ]_"Z[SDžl{@j4O˓jIjIѾ &DY`Y6S(h4>N|;Dc{ǿZtOR8>5??u9_z V>`iTXtXML:com.adobe.xmp 420 494 ^F<IDATx t#u&"kf%Ykn߉ssg%vۿ8;IhqʶKڎABP̑ю@Y j2( h Rd5MAl]ՀPB$'M-N:9ϩ ʙ3gJeqAp!fBp O:Eo8q^u8o\#8Gp IJNql&!s%]lIHGr5iDH+G "DZch%h3+LW^A?>'!>qLYM^/2R)I@KmOiVq6{G3 -c"j/秭oZ?˿ddW' hiiI'x"Iߎ>{-4:BNK_`5U6$)qڕ;r$gy8@U$ >qLiWﹼ" w0O~xހ}H#ҮX_`[Cʏ~|˟}"Z$~n:x$os<G<(=O/W(3o W_N.iՓ HIiW^OxL"j8&҈+7/( ?=g@l$҈+lo#FHrY ,m3O|CwY=O7]i+h+B3ɨ0HIiW'922u&LKIiWHלIF&)4"5 рEH$I$-//s|g(Ku"FgƇiǟяEn<.N2}3ߢ_7rg^ƽv$bAG9gdZFǕ:zmVO,^0$$a 4iWO,^EEADϺu3,%>&҈+(|Q#~mvŹytl{ڝOֶ+y3_E3}OD t^}(}$ {a嵎У}yOnglq鄣?[5!+裏Dڕ?ǫ&$v 9O~Nqqns/]_59t,y7銯Eso^A߼+#?DIsg#W߼Yy}#БCIWzdsWy]-;8qUDڕ{WiWϦXN])?`{|H^Ud}ReyHcOhcጾcOx.:<=9 >Gl~O~NC:ғ >{_i/yXSY`guՋDڕ3vfQHR~=u5+lo aZb!4{LBjs"L?xbn\^I4s2S =AStHL\~"OC1Y]{/e|ɴ$>A׊|v?S8x_ 5#tB.#_ʊ˾Ev^|EJ˾EvSՊ˾Ev A*3$|v?˺[e'~LBj z*i%>Oӧli|^~qc?z˴׊c;oGc-QNy{6?{AhGk5?z<{ H8~x1I8^D% GNc1f=nI8^Dl__j$D|U!Ecz_Ƴ<@N5Ѯ{hJ3G7}RKwƱC}LH3VON{ēkn=Po93{ H8^z%Z ܿ ӑxA.`mO[m>&LKhffF.^ۦeGzO/Ķ'5?`{$$cԊPs7,/ 5eW/!x6Igq G6%)0 4P<䲳V˓ { ovzءpy)hVg1~@?`D=v0sssqIp?%;~a8h1b\zϫT+s̪[*)鹺nvr[BRS4 D:IMjT0OI/垢.7+UW8 ^|z&S6߽}`[R]LB]kT): kpfkTP9Jv]CmV\Aik4e|15a`ݪpE=ۉ4e;sMhʄ4D7-!RTs (˰hh\P&\?Kc 5sӴN7<c46`܃^j4M!v[mB޽27BsƓ>Ү Z+5=ljV;-=K=)F{\z?g~}gJ{S&{~;2=׺w_]j3[,?7yeܴC74 Z9gyLuX?yJ, krYB+A. )1֤ep"(MQJ5٥++,V:M5Bӓ &yF2Tu5H [3#2isZ,i<_8" u,+A60vOO8ONtRI!5gX{4I;5קnΦs'uw8O4dSrBHMN.!SI496Ln}O ]:z]'hBxSxerD{Fȿ]2n_6ocӜ >%TWΆ{2@H?E~R#۠^5}A>܎AizvWs !$'dCeEuE.mLc֦k~k _a,k C(mz1/r Ub^5􁖎:9o,jMл SA(0BK a Q5?xnBZӴp5[mL@0>6QoQ6ДTv Ҿ UK7yNen*VbNKc7laSIbS1ծϴ|2wc0.ڏmaO­8]Kk;x0` &yٷ˾ۘL5\v6B.!CagU;O-E2U[]jL2ZBAOϵ\hNT nZHud>5wx;8-&˘螛w'L Ԩ[7Ca~61岕-E6M׷M/MM!|Ipa爫2n_}c{%sr񞘓ʳRwӹe!˲6ykQ*6ip#ڠ^XeS=lZpy)koT1].|i\tuXl신LZhf@H}1v֗'5ZQVUUkވؿ/oj<Ǡ~FЮ<}:MkFm6+wMm){{[.C)͘ڬ7hl:VS6mSھm*zjBe^ڂ哫:~ ! ! ! !klj'رc/,=sEo> ,;G/_>~CeBZddT*lOWc5|M7=oϑ ^)etAldՓU I_mKicoA/vdUUu>G:'9+/N}zuN1!m*މ*}L2bscպ i`޽aTITee5^Z&%$җ5ʩ_JYUZDbiyQXR TJm~"-.zf!Ry&% 8k$Ir:t~KT)SFV&Yr+1KPr+>[|-e'c lUTpڧ&BNk+T0K~g-O ];j-$t_ݻHyoWs{x"-/e_֏-Z/B)P,;yDH AcVaT2!rVpedyHF26X{'tKIct Q g@'G5R JԑJ&[yTK%%2іz/H-+x@"]n{I%+ݧ |{UA"$%)@wXΝӴJ!lސ S8)sR~-qQzF2!K}^d_O>WlYx xsϕ-yR:T\ΰn-~/Y1_$-v:"\S}^V'JXMJq,g@\Ĥ&ke5*ɢ"Ϙjh![5,7.cGJMwgZOj"X#h}u\tm(wʺfJ^-kĦ[3&J4!ܚ}9!UdnSY&k\7!lRVNPEi 6JhI_d۪]Kz幢7b}f߿Bږ*7Z7P|飤Vm&$wʩO'dM߳UT[Qϝ0c']Hkى<ԖGG'm5qd*-";ڧk ҰaY:mF>V6ϔ}GV\9XmwXVn- %1o[j2}=,6ܟY? -ƴ@H HH<#7dj^(+kI_f<zݻ-8z:kҕA=Bںtqi1_y`+Bэ@s1ޡ@H @H @H @HiuPyU*9RfmBjaͥghzf\zv`n{`Ku4=@m]?_9$M[ 4ES+J#&BFqN7.&< (רSt%I)]`B$̶intNj?0}ʤ=Her uQ`jUyޝۤ^ ƥP)ݣ$9iNXqԼFy%*(A E)7tB`%?Nyu6?@f!1@@4JG3sLs3H*V{-<[]4B/1pنSMsUc=*Sò:qh4:ʻ`k2OWoM)&}ܠIb .Vݛ>Vԣz?וƘlYQ~QzusJ^'}Cy\~yoM=4R;'܂5\+aYw != t7:WK+KH#<8zkJFQhxlxа|R좩Y<2/,~bbE>ퟜ7\W.V9 23]Oye,.dW++Z5sˮڑ* n]-W<;=#~xDoA4͔~/j%e@!O3QyPKZVjE$oRWmi!?Ŋf5":7k-23I d9D>$jS\vϬtu$<|))ٷ7c1؞.;iO'fez MsH35| 5԰)$[CˮjMHPy&4W vR0,^v>76mlxXhqg&hpMwZG0X<35Ӡt꫻"dԭ PM*.v}@ vE伔K@ w14VZýBJ<@S i}Nyc-hOi~en2g e'A- $6.z>FmkEHdάʨP܃ꪫt=i 5}^|V>cxP jD473s Vk\!Mxî9հ9C3${cnz0fSQf`%tw}˾buI09yQ۠MFm馨քxRkYY1_0ټT=J*;b6\OVp˘#128oe@^ǶIM9[,j.+._mсn\C3mlW{KSs!>4@Wb6rک 5VZ=خ vuVdΗ 2dΘNs#!mٶoƃ}aX˶`I[?˘>ר,vcFZ]Cv>Y Y\=xV|T?OvA[˞3S0:#QEk*ڧu_X;h5q}#>ЮϬ 緝;6c=e;c頭vzZZ5zB:GfBBBBҶƉ'رc/,=sEo> ,;G/_>~t ikAQ&PT"}?9G_5goW?G+z BB1!m(WOVj&'u|Ŷ-~?GC=B:ؿtA 'W=Tձ2z6Z4&8q>{AA:ƨ@x'k2Ɉ}:YЏU~ |sЃ {WT*\Y]#\ր@Γ|*] Wޯߕ^%],c# $B*ZULm8M(A+R$rsJTKY3ԚުD3eh)%#u"U_ὋL7[?hAViR1!}(JB(*;LIJCA) 5TnAF>ANr2*X)t/X-2]q%Ǘt6UAHۋ*YA@nJ@M')[cNO"lGްR˖T|UOIXв} GM$T׏ڬ3$ZJ%”cwb`!A.]UƢ"U|e _rtn4EAvs٩p:VtrוYR ꄔ {9whF3!,Fa"j҉~R8cU(Wqە)!A.-q߹h\sd;M $6C ?Q3,/%B*g mvVZn9yx(< SL6#0>tӕˮ: $c:~ Do/֎1KF% 4B!m3Bjv*tkg"ޜabʄ ڦdڲA Jb}}~*]-*qJ$Vy+I|Nb#// msF쿣m,A!Iґ4]W4yD"b|CI)RHBv2lU9 2aH*&lY's*WJ9DVbw-Gqv\|\7iZi۹ z}{1c/*s7T[m J4w09DDj>&:cƨ>9iXP+c)Va7ґ?mز\pf$I!wJ"* *|X^`Rl.+6 uXZ%~(@#s~Bڦ@Ke=ضꖒ{ ieͿ_!mOA -%3Lm@lۿدi AQ醃@ Oq2Ftn&o62oK -l6+B 'v=~ܾؾho> =BZcN( 6_ UL_ؾЃ OUUڷo=裴gzآؽ{G/5S{Y7衽'AH[?.-&Mآct#\w=BҖ/Aw;E?(:tQh$u@BhOE:tQ Lu#~eS\0 Ѝ NHx<0@eo6={Ν;jU¾O1AH $9ooR&_8p:u B!A GH'Oz|I d~iy B$ +4Ih dd8B!mOB: Mu%raCwM(t@ye/َi.Y_""r^ I8B!m[Bs zRY<9G=Ko-+h y%F2!)!] 5f9ĸ'IՙK=]wk==K}E*{>#GV\0Nv{~猢wx^,X8yuC?GOw#tDZ$h{葽O˘x%d;c}QQs 1z|-=<^;@@H $RK8M2gg4+uVā`^{V 9JK孷)-})*uN42R9;@Μ}|t. _3o+LG/ۜ ?LaRy3:rm>_,ENGM I:$9wzϙs V~')a?i $-!ϑ^b9NS/žxN,+ۧu+c-ze~c*)w:BGE2%4kX?~G҉9͇徍R%+6 F 1I9FMBn% $GȶGbJɈ_o"! AHl\"WJϗo+[d'vigL}; RQ5-֏" Vc AHD17DNZ1<}>yR ";&H! 8O$(b>6}(t>{wiYO9lQ̰Qu!D ckB0s%1d6U$-Ij՜<MSRd' ~]*U*Ew‚R"*R=!QE!7e(H \BEo [X $*%iYt-Pj<&p"^L~)wS $ݱa'>L wٵ"$7 B!eʁNXǮ(}SMK"$VPtMUrA$xE,q'Ѫyub PhR&(C >[G'Kt߃gN1.%:ÊN3ibi#=?~Ȩ[2f"%xB3! ʟXM ]ťexSuY$Wdy['WjW銫hBa;t=|^StNK ီџakrLqxR<+F ϞpǷ wg5LX,gXYKE]W?>v(E8nDA2 =~DU% *&*OFVGL^=N^eޮRVdyEˤwACԵz ,Cqn㼽9UsP $p Orght>vˑ@}wR"z,QVK4m@D_'u]OiCHEAjdBǤaw]s 4 ǚjKH/9kط/bEEWoIކI˳M[#$QSh'7PcX:z҅ B!.Qݻ*aȔk=JTF)DH:9)uM"LF!ڟӁ^=֙tBkgaD#u " Mj W!ʉ)JL$7ui vm%] ȯ`c|AAGQi1Uۘ!R i2^Xx [7!e"?:36M {\YMAvC]lUZ}_J}\sڻ{KXq^5G'_/e#8jy[ucw-..]Yg^d>N)aw٭|A& 6総N.;I:D[2ˋWeDݟܢ[qJ]vh$hBי&݆Nv^?M!@H $B]g]nr}ZF^ T(5b=^[Q}$ns~s錜2,d w!{MiOK♸)CZ^|pFH/?YK{ճTsHfJ-6\ εm+^!I7eFHϿmlǾK2!m $OˑE~Rv|bu:ʶt~Hd=j 9WxO4.c⚃S]aճu_8(.چp_A@H'xԿsW#:\{J{h@H $ $BB!B@H  @@H @@H KL"˾EV@@H $`S IZ_@  TBVA˜qDZq!M%$Uۑؾ34DZyĶq*ղF9|.Siʙ_II$I@ v!͐W=Gݭ$!'IM)SxV9 ?yQ ]-~oVyK%hfmheqP(S! KH>~G҉9͵*bH_<XɄH_)tIY5%]Y{W+p#$A6\+LuRq x#d)ϕ-xNk!XiÜ'$)kzC *ey/"qU\|~çsPg+5K鰎Ų-#Vmb_G>i< JLN 5) $$(b>6}(t>{wi#ȾMcg1 2JJLX*6_$MJDHv5&+WRLPi%~<&6'fĖ~>{"8|/c_LORNaRLvN&UZ^[y<ee|lӹ#$"{mBn gk(9z/UNL0Bb>9:kt?*!&>?R3pJHSX~аr8Y +O'%J4/H$VڟzI"r,tmZM(vPzھuUʆrn XYֈ^ray%^m!>m$5hYty,i!za#$ZB@H $`\v-"S(}SMK!P!H.E]1;J$|<.@sHtFHble]$8 ò$oY }y M %H!ko&$#u(y'8կ@H ޾R i2^Xx _]j(:M 7+-H @Ϯ)+:J$q8}U-eיּ I\[K[Q{"Y] '+ew^5+@"'˓K,Ϛ CyI$݆M.œ`qbԣu5!):V֗;$=ke!bGvww]wUQk y16#XXѳC>Ún+q>?FZH2ym BDZ7QTM>)|hպY9@!J2k][#֖;_27!|B!Cٻ˴LA=7ߔfp=_Cshtħud1JE#A Bd -"7*#@sP"N혏p>B1ki=v'gȩ,',(Co!Zd'LѫH$iV9NNΈ2r;#XjA/C"*W8(Y[{5EFNك/?[mx $iԭ]|^*wNWci Է~B Շ6ÏtSD(W(f5 )F 2M0"%WGI@V"$Q me(˱H>g}Qj_E aiv*h#bw! $t~:HNҳ%#䫕r}(Ֆ *V+jmM&@O1.lu* k-R<[ַ~ BU7㪕ldK>ŧ^%wIZ T՗ꠕA( @uH\bH% @HB!A B!A  @@H @@H $%r7!@HmB @@H $R[itP0f|:@H bj;2wWF>II$IۄVTj:Q !mfH8-揠 BXsw"Nb -#f6qr:_NJ!e@@H $ Qt|>|s'$@dW7飢L+|nrA $pFksE[2͗o+o0@_Z//VN/yF#_ڮpsOSJ(Xi )aCFl,hū K,3һ),RK|Y5Q@@H $R B?H|>:MLK4dsMK"P<}1%޻D!V"4yIs2#>j\mcV$"`v0*z("܃ HlI]k+Q`1p"*R" 9Lke*kiJݟ,i:k 4-B!u">?R3pJHS8|7 *4Z!ɩ1?YpFܵ‰YTɈO'-%/Wq((E"aO뤤YR-p멦RBHLB*'@HzB2* __׭`[RvBE JB KBҚJ&":~O<+O@CHT-!c $ҎY}gôpb/,{o</.nIm pų$$dc~˶وPAq<:eWL|1$v9eV2܃Uv9R$$&@ p|r"F\DXO|UvB k!L&Ӗ1iG]g]nr}ZF^D$xXWL:N!nj꤈ѥIKLs(q!|Є]ar41/$Br [}ξ[飤>B:y$=SMOc $>tDZ)0UtLZ+ &y^YV+{ɫ9VyW5r.dcdNoZAH $|\uK05-)A!FT{v  '    B@ $B@@H $ @H !@HX}}C  /nc! t:]s3NF?@@H $RK\;Csǫr{cWm/eUuLS-kSWʔjKj:AOC@Hi 07CR\in7TO%$>>SAvHOG>JryUT_w{-eȬ=ny?"! 2?K'7׾}>If` HH"DJQ Lj:V Ro4_;L}l?^#WMz aAp! yVP"DHL7U&Mo‚W8丛24y8y(䗡E~xnTDg&px "iXcM\/)Ql/g8ܺ`a)r~Z~a.'i/?@{@H <b>6}(t>{wi#Ⱦ1 狤Y)YjdS틐VaE$HJm+E'pZ^jZ?oBHHjL/_/k }Yy *q8 Ek8I&Alq>PKk%*5_+ ViY&I OT -n9o$ fX򒍐d<& >_tm !IeZd E8Ը?Y\2/Z4AlTdNI P\v_l(̊A,UҲ9XBB6a̯zL?V*|0WBS;?+TN+GIi`%Jd~H|U|TQlR !@H-wpxgD}׿! +"=yԥm&Q,xc 8,z# $I' hmHߕzKam dZO ZX*9 yV؅P"DHz7U&M En=7ջPȯ{qR9[W`,+?}/hqmdz$r!-D*"2 BBO,ߦNg.2Mrt|"|4G^-s #.@_b>p8 -4œ{Jό6yYd楦&ꊄ3QvTqTĔQB stVi5k[`Pd'_%%QW9ǎr[!@H!IY|ʝU-Gm, f=yFȄf_P6􅥲-D[o0C7B'pU)$(P8' CZ0-#4kWJ6u/J1׍0A B\v-"S(}SMK#@eRNHUYXq- k \cBJ)@HU=3Ly({Ԧ\M̄5˄Er? $,@H $ҥR i2^Xx ۠.ϊ% ֹ*4n*ptY ^wyC|c]ђ!F^lLDs$ݚTcN'X#$N#sW4k[jVnx>n鲫"t~Uo'DB!]z/~dz!zu\u5ְc-  @r~\AWʐY[ Q ]mQCUKNI>+<:ByjyE΋ܺ%9NӚK>m;l4kW.emxʶAբ:9B!A頲®*gXDZE +Qe .4ET~xZxm,JedT-W* $ W=Wq/n e 5y !!!@H@HB!A B!A  @@HR_ @@H $R[/B  V?.'i#@ $?ڎUn)k9R \ !mfH8-揂 BPsUUpjroڕ Jd.LԴ:Gp#6xы $i$wpxgD}׿7BH">DVRHd Yћ_?ϒ z(L({Dl&T$MRODF@HFksE[2͗o+o8@mKWW)j ѝdH iixTV8TeiL[Zz_(a|ȵ\k0ax)hR >*^';A$ BZgEzieZ\ZI oJXD@e--HDH .+u'$VP ΧPZDX5 .qV}HDN_y2Hz̢LHfdŽC@H $`'y]|^*wNWci ԷB4!qZr.1'iC@ $?ڎU)k9ԉeJ+:?7q8ns!HI)WǪ+ B!͐W=pZmqTW#i Ȫ[_r?KI_1_I41ݟ!@H;} esk_tB⯀ %13ACSd*1Pm_L7ȻHCs@H 5Z+wޒix[yXƃp }4;S `!J9s穅@!ZMQf׈V[n7+}Y xy46,"2$/J"Eݭ| ySVxH!9Y;Ȑ@"1`O8a=_K< eEײ#ي#&rBLK?IdFm/Ѓ D8b\@NPBB'=r*cҫr]Ƅ2>D$!Uh:JF}2jh =ܞ ~! -IH?|Rbrght>vˑ@}%$Sz4Pd8p U$[ EfMBeb@/,S$au+KS(PXX"3"ZmMVy 9f\-trC((gb -r1&#ۊΆr}r>j?ueW-ɶ@naiU$C".#| $]v-"S(}SMKC*SG@H"nIӭe,(Sɔm',kKHyTY;Jz>>J421  !mc? sM_kXeB@Ht *xW9Q:+){UZxyyJT/L9O{n|/hRV^,mo&OztݔI2kV\v $^QU|ﶨ6S/Dh$bfJx9>B@H?(RRHE^.*! $!@H@HB!  B@ $^ @H $ @@H $`3>.'iC@ $?ڎU6 jEͲFk/`t=m,Dn˟S $ig07CR\in7PTҦ0/'فT13Io5+SH\k)D\ϟ!@H;} esk_4Br<#ggp•j߭5>/t~' 29 qrn FksE[2͗o+oJl,`}3 zt8JG^JKd!enG_ zq+ؕ\.?"ϕe᧞WAH5d Jq_cn͈~"PO}a*p B7pBQV^a:bZ~a.Fi!AH $ a?H|>:MLK4dsM aUT*rb8BB2RŖVG { (+Q~]VxEz*PLJTL@ &7 Wi{%BREu")2Ƒw"' *q Et4Sӌ=Fu&?W[T'|FKT˯J1HP8#aT*P $iԟwyZ8]r)Pz )+H#Ze떄đVnl>-i*p,>yVhv-X*vDn זqgG [._D'ϩcr ݊2HHVx2X٪֢7H$hB!Ed5zi~iaP2O:!z -#Om!d™R]O2,MbU&ZH!]sw[M%[#F"[iH8 Jv\=! -R i2^Xx ]]F[Nɨ*Wˮ--LP(l);H2RVF}VJK!ȑX2KU%ÅE9휸f9GA&O$ۜIZ2L421ݵjAHAeW1:A&˒!5&,C;*\(Q-*ؚB!@H؏:C8Dpbla/dK-搜 a9 c'E>֊W[V•4krZaaY!E ErZ,Z4u7)!B Te6VoQO'݌9 889k 5|4@H $|:hEZV/$hJi+G}~oH>J%sVy!k PҪ8<˹R0$3ɢtۗuV)W߶BU7"eK7O*rC݄/4tsz@H $E*E*KpA@H $!A7@HB!  B@ $B@@H $}C  ./B  $j܌OA  FWmGFcULv^-kؿ{3WV)ƾkZ%-#|B!!-Uvz!mjr?*0!äMd-~|%Jd~OV/%.gB!v!piB{)#B(70 tt#颒x*:&,lCi/K`FG@H<05M h{_رc:~ 6y`4k4=/BY(u!m,:~ 6y`+㵣.GG_}}Ux^8ަQ.WW^hj/iK'NtD2Azy9wQOs+)g/o>j>eG_?Jsu^o}>zG_|G~Gj/iKR&`">==>MNw[!Ƶӯ4;8}N*㯘_. >;ߗѵϺbz|^y|\No~.m{ -90x㍶x]]Wkz~b z∾7^ ]e=^5zWWuY>2uy]u&* )q_D*=_ooU.k(+=0wn㵧d? <_ye/hsf?uDȾ!Xj~ /Η}je]>AHn`AH;n` ht>59N}ߗ~ӭwQ\ i4Kʳ>'* Fk.$$=Ư:zT%>1FH7[yſZЗvg"y:N߿\͏Mzp_C}V}u?ѹ3~T9.NjtBY7lcy:8̶oʻӱqiݴ8K&%P;9F2|=[ir6;߿|ݾɤtݲ O& t,C4[KqKE].tuߚǬuMS5/i;r W-qq;Y1Bf~1MK[V[Vww#g;ZM՗rJ^wqZ^=LHVWLϏP_GϼfevK:_KccV-߮#';_noGe<"׵k_ҶO^'_?NGyO8ɎyOsRwrUX=NʗNQON~/::h>W_4=/B: h{_ ߣvT*:~ 6y`9sX0&L`@BG@H @H @H @H @H'JE#mAvn ;0Li)̎Ie)EapSu>MoͩMkLOde)ɳh ҠPj i’a64> 7֜Tnzgϯ`O+W`!F E-6?-Yu?)?NyMXT>V*͌bUNn䣩Y5RSÖ%ϐPsh0C1_7 DZJ5"ZL'&9ĵƚfM gcRC.rvvmt RSQﵞBJa qe^#i^(99d\ V,L35լe [ItD^{P}`:b~l[Biy O-KB)w &]7T޶u%;h;iue!X!O3QY~åu-mDc#~l'78LE0?.IHvov[.9B*4;g~)''i||&e5BjưlsC:)סFq Y TSyH&455MJ&|GBq?+e=)%TRխ~ϰݢLcsXDX4Hw6K ,-GymO248=C]<2\d:767--yHE6% V+.iӭpUJN b~c|Vk`Bfm2h)oM>}`MV@kBjv)R!ω'ݣM C|5[i\LCĢIc˴vN[ \(s٩[=7u\iQC4|'jFf PޚiV\)#B#$ϖtw'$n˵ĊW[%Z(DajDb7VtvB.*1[HD^\Bd6k'L4KQI>f"*ڗA'9嵺דme׮tɅ9$T_-h*΍/򤼦bm4iiw3ik}'lrSP8m&.oʰ|:@H @H @H @H‰'رc/,=sEo> ,;G/_>~\BZddT*lOWc5|M7=oϑ ^)etAldՓU I_mKicoA/vdUUu>G:'9+/N}zuN1!m*މ*}L2bscպ i`޽-k9RGesT#O5KzN\T(R:b?P+ҋDźv|lBBHRЅ RV J.wBMdSQ/oJiߟlpDk]Qp8(1!UT)lLJdשz4mf!սVU7.2/\oآ[AH mIHnJRq,DzT*rbyE&r3x)SٜJ%!JEHGZ"` X)}&wVsοTa!x@"]n{I%+ݧ |{UA!\85VOHxmBx,-kGsQ[T[oD*9 y~7E '|l ,٨/+yȖB w]>yWhHU&D~|2[$+'yzCJ-~I*u^DuJ;$ N*$@<ǖEa+V]w0ɕ-g[MU<#ȕx>IiY q U'r&FX>ţ4ʚ8TzƘx[K% e'2$ڡKYw.Z;Ylz ,j)GX"0HHHlD*@8D(deJ-u,)?w˹;\#L-'ɪl#Ք&@-R%3Թ'N$ԯkXR'j$y$.]%O }{v_2.YAi$ )HPػ2!3a鞊$%ky<. IV F,̄;B[p3\GH+ojk(!K&Ӕ3..=z[sB  /Gn__%=GmY!mGBrR\eWW$?1Tfڍ'٥JseBjVZ%[,jHKKOZI$ %آItN Tƅz)2!I)V FwU rzD{%T-{pӲtRw&y@.;.ߵDY+}p> ~qA-gkAH mHHަ%Bk*95' 4W%VB>/l(xRf$>*%Cڊ7Ú<~, LZyQC5؇Ù15J 9km 引Y)4Da A ۖL_-˅ gOBKo1i RThU^y̽yW_Rn|UuBEn{KU䑍XuXZ%~(@#s~BatJҼ!˰K4 A.1b[J5ѷ7~zOA -oۿs}so9_BBPxI d;;nZwȖM߄-YXm|feX׮gPo çAH[+bpۉ0B#F3JiC+"3wzu JG}C> [wfww9KW=t iǥ$ քbCg@*)9*mu&'SuH_PHUѱ"l~9fBSv/h.PSsʦΥijjF3;=I3sv#$M`sCTbST3\.:trOwrp9T)U܏i 5$}(|m{i4ESo?i2:I󅵟75_o|s1P_M(ܸ>]q1$f\1! e#^o43꓍>aց:M6.Ss Lw3Jekn~n/2M r_ܢl[~~fi`mablNkk=W=ӟ^ ן7R6-5?{Ylyo1ISF|~=c(M$# bC;Dּ?J398ۼJCu34I)굝G{D?0 voϰAN]>rh|Y)61۠s?Qxp6$!(UhXg;C\iM&АeYu[)nKJPWWu;Ѯ-f=ljV;\3=K=)>=.n|O}'pw NO\*B:d?V8<.iD[ihHci6ե[ }g릱I6u5lvcMMGyLch`|lN7(h=ozK7߃~|w͸x<B[c~4nʶRS#6Rý44o"yiik9Cm}M[c5(1 篢o BJ1ԤUI /nK5 !:vavLgIc?u;Ua;K ,aqA*sl6kYl DS,EmveiyTB Inv酢fs<;L3`tf5G> ڭ1yeVa~vVf"  2K0Y5n:NZoTjjX5*<9Zu,I9=xy_sfE]35 v~Ѧ&AǢ=Flo5JdR>%U? |nzXE+l^V*[#z-y(9Xm>=Y6O+m~FЌZ$+:1l!.k<45ܫKmUo FAIi\w5dW~QJ5 i9^l־lY&>!Kǵkpˣq={ 9 r;/4)Ԭ#>\\]>լ5;!O@0o|zGaLBY6́$47@#Q|s e*S#|l ijEH/ׯ]zufXv,!gZ?%6ܘ{Jknqˈ;8DT?48H#ןD, wYaTZztY9GҪ~%UG+ckD(,Bkx·Iج4,5۽\b)luH bXՑA۪_Z0zIq}6VFZ]1Pntq| Nc7ƾ~Ohl`%ڏ|kcрJ{{6[W-܎֪l YZiEHm2\S))3tx' E}D/*p٥-6tIiV4㭟F2>9I495E3*-%$O<Ā5:x+Wzb%$f&kք4,;\9$!Km+j{Z6`.jRWUma^VxE.-H4eC7*Vij?E=xT2";Լh?[^je(j6E<UALfeGlju'6~a}ɧmДT>v 4=YS:گ i}Z aekbWlyZy{~S?)5Zs݆[SBaFy^n,v[Vg5V6FB~MqV dԇ).v!$}Bm~xNmKyè\)(]HrW[M0pyw5/:mnIoWtb"N_W~Vlo^s(]S4Ts!o-> %bNx֍k>o5G)g?xWmbjMHbOY v\,j-0`sfyĊaxœa-wd) k*W'd]5e3Rk~4ګ!unOt/QXꞷw$rWuk)wMX!/ R:mu:K3u48ݼe^G><7eW.k~HSiׇ5}whFFd<ϯXsQ5m `*1q֡vZ奮nR}6-u߫a|:hw]mWk}q~+Wu4_J5XZB@HB@HB@HB@HB@Hh $tq :v4}{9ڷoij>caQW[_hgj WSwOBSW p&PTKQDEE;X7W3$uzJ3~|!n!=ۖCE_(i]Q=; u(=i BD{-?{9!nUwNQ^/NT?X?4&=ۖyY1נ; fLDAH{Pp:>3f/B`ԦB6QMx1K$~'heJe[_[o5f&&W>wػDS5meVAH\\B*+aIBXW-H)`=H_\B߾Whm"kE"@6qk8ge=[`!pQ BQ[Edb2D >JiZO ey1|~$/q/&&j8}!U y[s>y5E&6m~2-"|bn-Sya>?L-BHz"\?f1X4r1D_0F 'rU'Ecdd|9E 8ܪy_Od:!vڮ~- -;3Q!M4i껰bYVI_G颾x$d-9 B ^dBm-ZnVs7eDϗhX,SٶL_l-9__bJ+cن%\tӨLbJ-^PV*V;:@:;τ[^fo\PF]EOY:/Æzd#,}w]r-ļɻ^C~AHlOȧ~鷇wnan;f=fEAHH7d;Xo[oW߼7.[흂wܴ\WGX׷b,@6QS!$Azw_2 Dv ; :_6X7DTa %IDޢ616$EQ2W)rybP0q\1'hE 9]/+C8PV 6 S=qS, D= -J21#ơ0Pp'u$(oCyNjjB8/q3NNZ&E⋅)voHC%Q cA is''9Q>g4s^) 9Ò`M(-D'fhf.Irl^K3;1,r3 Y0)$!E>5.>K柑'[#{&78J- e> Y|aJ̟+3Cec3636X?Ili^XKgVdL,3MROH`7` 8s֖VL&>y [#5cj>zв#eN0/, y Ԁz9pnh h p@"H"V m`(U`?Q Nsn; ` /Ÿ?m5Cr %M Qh@t$ĕĭvuqH2"9HIRR"1L%;r)2D1xRP$-ZJJ5Qc-zyS'9K+NBEky2lc l  ')*S(* JyJ+͓ơm] (㔍YIʅʇ{GTTDPP9"#tC:BJ?JK2Ks,ެMfݜQu*O@Y5ZvV'uSEY{/V43`5` SU55F54}5e5ZnZIZ;h iӴ];jd0)RƈDZGgLH7\7WYISSoD_[jFDAn.F [ TXFFFƮ5ƷMp&Ld=&7LaS[D flY995sS3,ύ}n),Y)Y[Z[6XWX߶جiy3loymi l7v~5 Wc*3EXu>;9f8u)٩ip|uRK>+۵ w$C=,=D'<>z:zB| zý˽$4mdqX{5(ME  v,x``akbzl"ܢEX .m5Qq$3B>bID}HHiܨ5Qעգm11Zww|+'2^m}W_O\vF{37]oumkw~%gs_.@s/O x+᫱?|mnvD [N]y;GG~H0ڧ]_"Z[SDžl{@j4O˓jIjIѾ &DY`Y6S(h4>N|;Dc{ǿZtOR8>5??u9_z V>`iTXtXML:com.adobe.xmp 478 393 P>dIDATxTW~/9/gO6df&gsl~~9$AMBcֺ,[smO֭[tMAIB[ +TO?X, 2Qh\p;\˜LA^!+Eh4J V)TիTb=LܹCxbtn +ĽHCWBs]7nܠ!WI[rH$B>9q-_>r^m??Wk/)~sZk]0(@6}W_M% ˵tM[it굕*N_뚚lG?xhD~9qM/K.Qڡ!:{-ΉkM衿}?qO m9qM?pmQ nI~~jڶm]Cx5ڻw2v1Sx:h$[8oPɷ:J}xDNG%mFYIq̒ K4r$ xi1cGu|N\nw)~!;wF,z8' 7m6NCRX&Y 9 |ͷhۆh owk>Sxۿ͛7o?-y~7h>%W?ӶAX_}xI]oY?0}xY?wCz8&GZ@ Lɉ7od΄ 7m6Μ9#{B` M&ZStCNJp#f\{7Lo*~S}݋^[ !%ڐ޷º_Az"]lqe3ByqfsbDHcjx/'S_F吲5 =_F_QOR6Dip#f/,EUQx;;;_н{v?#җwZsWkNOZ^y^^k ӻ}C^s_2t)l~vynqj>e>AgnJN<W-dkp5ܜ9ʟgvŽ\o~DFeڂ=J06"y9BW&h Ak_nGA4o$?G2<_ڼԳ?C1:V3c|n80t8 xz8='ro\CCT |Nb"=2i=R9m!r#<_r^6Yxl~/gn=tyTvŽ=}i1yx)ztV~au>~N\zz=uyȘ=i,(pQ~y|p+=HN_ŋg[tAaʽDXcsNGq=7x@υڭj ;Cxhp/=L.>_C/h`EQzuT?+G+44J>ut$Qvߨ#տ|\IGGuϋ+8>hVq*L 6S t8z0=ms:Ͽx35?|crzf NY4 s7x.)v45& a3! 7LV;pڢaJNx}oI?حCͩ,TNwׄyT8 v9oy*v t-Rl aXÿ+W߹BMsc~xNtI-ĘIj~0ߛ챓ݴk{HܛB猝]M\"}ΥGjt$wi 닮 !N "*sZ4z~_.p>F@FJPĵ| C,B3 hܥBܻv~^+Ν_{ݞCZ '\40 #4l^!+IGn 4Ap%   ]`< +COV8pA%bκfffA!A!  AA ^A!  4޺uKnx ;U:uN< b5鿼7w%op>'Ͼy QnϦ`-㫝q* W;7Y~K9;Sݑ>P+`-wS9w?M79P@xApe QkkeqʾXL9Mkc?{s^\ ,C5'm|fMk/9&#s9q0R,6{_|<.{0>=x%ᝤVi j~*ueYRWTFVk.@ZZe[q롑ẋd~4'sZrUƱb;䫰$w:q&- ϗyGsF_ jY"8NZ3xϓT]" έ~=w\7~7 ܡ{>=_kwL?y הr1 v7[TX孤9ޫQ,y0k(rsRŨqwp8(C|=F*2zp~!Ȗ uUpy)kW=_G^cx\TP.%E,%!]sW8P[Mك(icAPs;qƝ>1zh,w)+iBn[)'dmExC%8Vj̝w⾠QXJ i2P}@驦@adkYwZ* U8KLz&̆BWmygacYٛLK+zԙ5bAjl3MU/hQn1k$#-g<-Cxzd/:CsbI,9eFdLFK.R#4;$W/^3Xߡ8l_g?#;dV߿xu\kPE5aV*.`#TΕ^hTÿ+BER\[GǨ#PD/fmUQ(I"_Y*B&i"#{g_9Yx{9.S8/iO*.|m5qWBcf8k`RO]j,6"!}j{dgYŰ\"<w1/2{FhX;ҍ.#@c0S/FI@-/s-J6)8-u+;J?[ ^p o^XC~57QCC seV'nCECwU졆N9`RCS51k]rx9,҄6f%^K" ͽX U-m<9s|evDUCJ؍p>!/j5;.q/|rl wYj})s ᝗ixU uvˮ4;8&G6DH~7NxlwsG^u3ֳ^/?'*ф*i^woVnڻ Skea1 釻OxvIa m{jW=2_Ҝ_g1 ht-JZWSh\FĽ ^5)9W#b8 o yJݴ=9.K2}U xX'eZԒɎ[ҨRuOr,]IdhDTw%>'?W25n 7Vܣ.'y,Χ@x!:#/ҢrO;"EymaYxyU ;bKyT~>iI ,)I$G8/hÔȫs,)"- hа3aאg֎qiGĻ.;9Cu=V 1']e-tf)29IVF5ϟELsk,@n7/eޣdFGP@xApu̫ +_GJ/_Ll@ +P($ /ZƯlG=nűpg֯{oU^\!өSPskssl~ABr"' 2b{([_%59p+l {pdzSO {[^WgSae~>WE<2u3k]iͫø0o XÝ 6L5"bi"~+MTqa,ɀVJUoXKtJarU$x X^ U$,N)"!y8I8DEEVOD(/-\]J33:*ӯ?# R (920s9Lc잠`k%Q,haYS%:LpAT> SϮϣh2OUaYi%?_ӒQs#Jc 2̶ʧ^~aPGċʍ~y\"ej j):ߴrQ彰Ga>g)BxE%Y@pJ(Katՙ_"J}Xrcvn qf)m6(meIezJ.O3E43lÉ8u=PD REIﺽ )#N:9pd/$JV6{m5Usr6jވɜaw2ӾR]Bl/냚p4YeUJWf +y Epȧg/UG/ 3`!2[J"n"nԫ, PKQ:NtYl-ɡc1yC#\HAI 5rA6+?3bZ;i8uPLÉTSYIY3#kl'AcM`/RkxX3=&3!a5"ċik_Wx=/B.ؐhI/+PeLOZ+4?YxmϭRsmSU6Fz>尩Vᐟ|wCeW2RSe>+!k+WJ+&®Y9ݰϣZ5 )%M%ѤMY)q2;)]>܊.O["X^Hq*Iił gjVfFL4̋}2,uB51m~αʭuTDwUb~Ue+ǘbՀH}W5,H(lqO]ƫjd#"C!9W})lZyIޤwz;3:$Şj8r›!R++y(bON #ջmSq,LeqU[^W(#y|"Gxw?ci=^~~esAe nwM| 5N[˙"FmBjf˯ms\7wN_A| m2ḙņ2/aEtE*hJ܄Gas;;B;+խY,%lN+kt)6das&}Zz3nAug CQjXCH}cɆ3*=dw®Q`tC=O'ci4.j13VY [iҖ܍(C6ݳ?/yOUfKKK%y$$;yG:?Bkv4 \ʯmk|6qgS'8/~7pL@\.LB}Ny\']5-{qaCm4)OXJUl^(/ri s<љW>ҥAo`M]I[r=^{730[ExH}ƲSn-:69.ː膽azuN/p*w0 i>f&dg~›csc C-ym~fNQd"6pL0_6|YRnOeUuwM&v}o,pfS^{7sI5u">xxMK۟qr2n`Ѥ|ޕԼu.WK,m730*b!F)y!f[#ل7v{d?ո{n>, gD,Nհ'T{ba ;ww G9a{ֽy fqۼNnanuc Aʯms\7ugS'8/~7pL@Unc<rs\t>ެ ޺ڊBs+@isa/k hSƔbN}dC/dծ#A&'$ l޸s7htiJ xuM5LpϳyI3n~MR7; S.׶ EAػ.|s7Ʉӆ'suGJ]CKOjf2Pe4K9@NZm.r[e wx%j[\氱yarlw37PSp҄_Nc 'x 7Tno=0-1EC1Lo@R_C!MӠ܍o~{fζNX1}GC6L,d3<9'ӯd>ٮIК+rmեa>˯\r(ËʃoRqMFZvKݺ 4c3s3!đ`^"x^se5#`2r!-|Gl< S@s1k@xAA\+ ;ǶƆ KbKe31Ih!wPT[a)M'2.ca w7?֝M:پWiyx2aoP^ ,'S ߘHW]QuM3lkfq#+ l\,o#'ӌXa۸e7d~)M@dc&e8"Za;ӝ~?O+r'ko(7,f9oH7-,o[,&,VrzOeN'*=k(N1bE&DMsx,"@ Bxm>,OkkZ,zQCMMlWgM r2^J;CחeFmwoȐ!89&I8yF_@bef7Y( ǧtI㍤7HucKryOeL'*=S!_Fp4(/Ҕ,Om ɝJk6+7jOQQ䒶kaiX:!VChPVjWl*t1Y2j?-NY&3yk.!ng\l!l'DԦl›)McӉ$O Fu;v'~K}ӉdWr{b67qHUOz' ʫnIFmZni˲m"ωr6*mc:&})  ?vl~釖մ۬aNyϵ:K\6z'Os3jp ὏nsܺ A!  BxAA ^A!  AA ^A  A/߿XC&\~ 7WNMMj^*?y ]ji\‹Y_'/E\W޸q%W"WKxB7˂j^*?y /\! g@0ofEW"WKxo޼x ng6}:suEqлw}J7_ Nct_x><;/4~W('ϑv]\ل֭[JE?t>B>חվ妾x.Я"W `].r-I$!> o j^FFVF nS.]gwy8qKЀNKZΟo|}q5:9pFSuKg(?y].*}W[N_{.M\{5\MxgggxY`Xdh{%"OaRe @ ósE~!! ձ^[TW:74:N ҵe4mlZ]tM:?=*D%hi] +?JO_aY9@QN8v ifYxs){s9v.;B|ޗrqeO>ā\ɛ1!ǔT[wSH/Q]a!~nZw_LŵoSaa]Jy!so?Iŏk:>}}tezu]tX>,=i::}]?Mo?YL=8bz{vq: |Mkq=RԎwQ3fe$]n`{n%=x1U=$d7 񬗋C{WޠO#T~5\ :ΈL/a7N!?9E_sRW\Ǥؽx 7g4zj';*0{{*>##+4cr_Ο3l?C4 g|{UǨ8Ɏ3tC.85}{?LOoqb/vm>VNl?M\X]\]1ү˧4<Ԫç/Ѵv d_H~~޾tmZT0 J4ƕCFE".!_^w'0p^KVlqѸBr}bgq:0z{h\ѝ6G DE .;0ً~MߗKTSww?]ûޝCyӺ;e% Q Qt,<\֦jL鬉gB{v0HS|- /p;'"~M^BĽ *JcP.OQǁ:CG4!)~&>~qH=vT@N>y-!{,O#SZ^5|#!4὞"rf^y}r: _v)\,(4 R08Ѯ<4Yf}P0v[]{XǗUs_ĄذtgHFfw䵩f*t79SzKĵWF:tyV5)z͐k3w)fy#}͆ax7i0Kg}]1~qH=vTreoYC2KAO ?Q ySya4}Qǩ0Ey:{_C|-z;)ȟ7޺N<k#hgY}{'tp/u^cʿ(;~$su餱hr~AzuvGe}P8%9 ~)*SorsUn(pArGGh' (:+``8ܦH/=">]hM^DU}@;SEi᷆u/}S Q5lzp$ݭ%nUxEQ:7iݫԮӛ}Si9v7;obsq-ٛf&gVDo_pMG9=z:*+pp[)hpEexsMq[o›s)OG-빤.kJx/W~PYx?%?OA0oBxA/)B!y̛Yx8\‹Y_'/@0oBxA/)Z(Jj,h  ^ A$/Bnӫ?AAІB+f.JxM?AA0Gf_GDA)s+ΆG  hCC/v.~ A  AA/Tr A  { Ao;3H].z,TncȌ^ΏG8?\Vaz AWU`,![.2!3npL"|] GV_>ܞ<A»YmL-GG1yzbFzVӯMێm ܺɼAdpV@nMo i4}X#u3x{Mr'q tV3= Р h m\caݏ$"e?fVGҨ>= m mڠmxbo#ArE="E )Μƍ[茨chqf+޸f"ԺITbQYyDh4#G{aQxe>lA;vЎmwL[3O m3\Xb34ql書MS)Ÿ!4!9ۜǶmd1&R[Xt7ˆWV 0шjB~pjӎS[:{{':'8,dߵrQ#+[aAa rOD;+J74ڱ!1ԬR{ڶm!Qsjro{3b1j(*hdz29nڲmJ[l|MQޛaÏwne[3J۶C69:ĖmtpphBe' L`ũU&\1yf=A;:'h[ lgdeTÎ M7$ p?}{=:rEPc[6$=I^wƲyL>ribW ]Y0oz,IxOm_'vp= 1?!/֍s{yH'wCWǽXL#G4B#[p9Vb7<P|k Wˆ>lM#[!3glLMxʯSwCӖVV 'ߎA0*! BxmWPs#GktAyVM\c}m.ro}Nt{To9>:&q7u}h'l3g{c5[鷛"j*ÏSX²GcI=^)OXšXT&t/ޜ(viX#gb4o<-?c܋)yz`&<{ݶA/,WZ.HyxDXĐ ^A"qAApń  pلt@x ^:|lF5/s4l~//qfug!FB4G[MU8uT<,D}7YX PFu#5^!ӦkI*c7}GZ٪yT4'»zh_z)>_$Lʨ6`ԳXsHDe.H%kDx穑TLc^rxM\6h Օ_]DGxiRIyo٦b'C/y*eG-1+HyUTM!98O]%TRa)j eøJWI}"ˌPcԇpxP"6?٧TLΉV國#s2]t-܄HW5UPYzFm{ז(w ]ɤ{&uAl3Dʼ!3O< )0T2j|9_Gs;ɞDYu4]IeʈxW΢BQ5oJ?;s|ti5ӮHODy0qQL" ڴTN,7 »؁ڨ%4j)ᗰ:`4ihW!*vYلet5-ȕO>QpyKaPH@+"kA/ D=EҬo?;o\Z<.ƕa樯{NxUp;B$_㊵+ΞYE^Me=R`geCF }*MJ(XȧWAʸ1%1qiU#m7`ݵG)gm`yR/:j 7ڨ"8y0+"95x8UQ׸L7%s7æq zHv2k-+n2_NyGp̸F<}<Xs#^ QlvΦr`K+v xK(O? LV.}K}Yq̏$[=Z").+#o LV9uOmyq{MD,𥬨#"K è,P[{O&C}o)ej|LA=l|JDȊ]HjE^EŨV\"CJt2V>ZjIũ7r/12==`{)d,OR /D[Rۢ`8) .M4DCV%%G88?T:>B{ K$#ExGV`F= Q^A( VŒOhyغzzz/@f^r^gw."ѓO:5Wū",rƩZ E4&!>k8RE޼&ϻǒP='{><:o)FE'|\S*rը*P$}C V h|zH{!'{)7JXԘS})i䆝!(~2PP6,iuY׫p(n>^$ #<1*՛Ar K1,PפN.P<_$AůDUU#%,_|ȼʡ"1kefR`-jШ]V`9 V5;sZrh\%6=}+z)[`C</{Y&<-PMEϡ/I'}%Q*<_]A!arS3MOސޥW'T P|qXį\&A(PC{W&c\sUfubk.ƀW9$%?A1nwpER *Y1o7"Z`mIԍ+xB14phEt[d?lwHX7M_x %WXS2"OkE~DusE^i6M9MxmˎVp|-X6?K.Py^g|\=,b[rW <^`U̒~bZĥVq7fM~ (7+»]y۝x~>y_:bOQI,)CH务xs󶋢YJ˜. ItwYC L:<7.1k]oYD O\'΋Pgbs/;˂%Ni>D&S5r,u9@xX5Q,@x /@x ^ ^ ^Bx ^ /   ^ ^ ^/@x!/ /  @x ^/@x /@x /@x!/^ ^Bx ^Bx ^/@x ^/@x /@x ^ ^ ^Bx ^ /   ^/@x /@x!/@x!/  ^Bx ^,@x ^/@x /@x /@x!/^ ^Bx ^Bx ^/@x   ^ ^ ^/@x!/ /   ^/@x /@x!/@x!ovAA03Dxw6=AA0;v.XxO_ yPh炅_ޢ `f &9 / KG/ BxAA  A!  BxAoݺESSSt%SNАɓ'-8xtaVKGB_yg_s Bo^R;wfggEEE:(>훧sڴs{.zW;wmgg2=  鍎º?CHLTww(R9,ҡO  ٳg*@2DzdJ}sz]fwTa9^Xn9J{y/B ȔNTƂ:ʾM#s"=  ҩV|fc47_q&)2ȔN9=7N_wwy0wzYF\Ez@xA%Zr\&Kjh%j :'ȔN7{>fOR.5ukq~FhO~ܼ|gK / yvSb]T$7b/ 8m|NoxJtwo;4-N|v)ܴݡXxwcqm.=O?lEA> Sa2)5"= Ex稱E9]28K2 EB[-jH~LgwkП63t7svb YJx{r`jCQABxA^H9I &3GK=}}-Kل)?iD%QKO GH6/w_ez*zWz7qn 1O}ӹWSο_67{2ܧ/m8CdW^*ׇ55ޫW|"kP}Κɞlt+_sb!Po={)3[~u@hvvTq߹G_ゝJx|]yE}Lg! sω *ZH4U jgo)g^_u¿ "Y[sEXo2 ]f`Ɇ̕ߣ])9e= ҿ GU0GXf3S-<;-EUj=fƹ.}-wwR) xwNxotK{I+fJ|! d:_=Wu_T4~_I .PH$AGtߊӯ_={WK࿬Gӷ`@ApASNAu'9"=2Wv~[6 7͊E2=  Ԕ^E/xt)>ѿxYa_xd: .bH$BCCCt!ڿ?uttH۷o҈Hvi-zO7sKQ ~ƍgPӴuKk"?XIDATx \TG}}۾iIKjI$MLӆ4cȮ; 0  0 *&QϽ2" <;{ιa0DeudW w%'j~!@px㿚0USR5 {%H$iA}J%@ॷ^7x=$r|Ttl ]ղ_\_B" 3[ݡ߲_?oy?@" $RQSHK!Ag*&sǸV&fSI_.ȳٻH'@mmmf'y|DuwwXә񡡡3$ҝ =2|7rㇾg<  udX%?,X+ Px ad lFg@1DR)K6 U@j3|1faO.]2lN zw۱$=BkT7Hbbպ3Yuw s$BwAn!~.g?|2t,E+K 3 g`!JZcaj?.K"-!=n#)SlO`f /bw޺⾭ɽ9/DO o[/ZwMkGM7 :]ۚA(5ӻ2+DZ;3#'Z銐f 8͏#wd@ v,tKѡLJ!Wvg觎X!2o\M]4=>naCح2t3nV I%,ۧޟmew=%9K[y[H : EuܑEO;K([#,%#[j4)["AbXZ@WDpH"m\'Z~F~բ43CҔ ϭ|}pqhSJn@2GBu": N5mJz3i Ì8 de7H$R桧ޗm3a 0^TUQ$ZkӔMD2qG=KS{$zxGDS8@ѵX?/ %}ՔPʧUuh5o+h+BZn"R"xs Cwi033!wpHpx74?%MY0CP JftIHJ]3C.n8R 2tFUMLKY.0ӱ%M/䬝i) H%\Z2,ȶn_PMA^&S5%%RX2##] ģ\JS6J?sߟv{kxxKe;]Ҳ!"2Lٻ ?3t[b|\Ow6gLJ ȥr< 3Ruww]:3oFyndM:f&+  9R;!vuP@$Kaa飰92I d@$'RdaWD%n![s@"-!R$ݢ3SV?I)ucyřg}k}c#x}n58U>R>7FXJAC+Ҹ!iIsCwx\?6#$jf}0π|[XB0؀!H$iA5[8xϱ$3Wy8{Wk9>~,N"H$2-!4^8!pcwwX4J]4/#$.H$ukpH"hPΐH:H$EpH"H$NjD"I$D)D"He,CD"t:s-C?pD"H$r!582qD"n$DZ>2ñe6rtt6WڪY9<m|?xdddv׻(tu݁JB nXDDLTRJ JbGS,]Oؾod?=f?x﮳_f"Wo䄅nɃϼa0u[_ O< 4jݺ,j``dyMEs"SsKD|>!HO7ùAgsGwE3յv6w1kl׶w,@--N&o`h#zc04x癘ꁡ3gδi=9]}Zu:Tl\Zﯯ[>盙DL6S 5VuGv'cO'^7?;Ȳx/ܣ?~<`N Ôg\{ҧLgoEVrqw?o|Na$jӗJNgOϧCm+jK?POSNథE|8<"rD"Ҵ).8=^ig~6`ci#)V,6GrKp/+hnNt>rv-}/pRN %'S<{RPggעF8<LCEmCxhE*tӑ*4/]s;.PU&=>6I+Vd6uBWܞ3jߪ}} xsFFFUp,ScVRt )uuu-p8xG|C&o>|,hq+{m[Z9<}Ş<B ,GX7>I_^X2{i~ |/#vۏ~O`q;6MZW.Tbgg/ޘOp{|ZdBzYuj'?@&۽w+ADkts8KSߌBDHװ{yܱ"=?QXvl牊8'gWHodgXq1; {&6w"~#7ӫva/))MJN+Y SGGVO21U}O{OnQH:NGfrrr3 dik33KRW)sLi/_9@{(+FZk)Ix~{zzjÇ74VVV(ɕfI3SRI 11Qa!!A~>>dlJ.ZPgϞݷ"X_ w?nfYEcnQgb?u1w5#B߷ZgákE#?R|M CL~j#/&lfw|lG0BHoR!sU{ǂWtoE;TnhrN}}ڴOzu/}SoooO/:9:1GbujyC@*:74[ؘEdoѤ<,frUzC0ٝכp%LLshU6#xakmqRryy~-o@TtewS=5ՉIi 1CKڥ `̬%#Gh*NM ןowP# =ѸܢmQbGLii,$;Rjk=yVP#+& n[UMu*EYyEaQI Y4(1==Dql\|TTt;cǎUiwqvv^ba`ɩRWIhDq`:[򊏏_/M ]k>#|キZfǠ;x[{Eև?~aO;߹ӌ`lYVV`dx$1Q}rkj6?a黿܃/E /{Զoysۋ%NJ^;fڲy'?H֞eg;E z&'Tڙ߽~Dts8qyFĸEDIU.=>".{i{=sn?XzO[! þTDޤ{?}rs %EɁ!>|чmh28 .^µ6M׿cv}'?}uc$mg}U|FCQx#ɫg >H,<aa[äMd_<϶}!D">@!YYE>iqy*ugcK&2Px%E"R([x{[gpODcII 6`%N)bj`VCly]dO_תUr  Nϕ)]ܻ۶̐ӭӭUqc`5;Ṅ7R/McN {zf؆WLHQSk"JK,^*T/\a݇ s %9 aȐ?o>(aN٫wݽgAʔH$vqHx[q< Cx5ad6ij)W {1xݑ0cŕlYw> ]vmqkpػh*gy zlk V8NemmJh¯m~l¡p<<ٿ'2~䍷ͯmtWkKZ;d -.mP{}[l~>IUBu2Dkq>Ni=5Zh!| <Ҏ&Nݳ?5'!D"ns N3 ò} b@0lXVY*g'|RNx'$%&%fل_Ee⦚(kFl'$/ZyEPmg[xPHmОapXTZ{3J[+;ti^lrcS% mh}ƙ-I‡wݺ+EW?;=V9VV^4+M>跬M1jY q"] /)mC8UdX֏Z*GɻnnˊA='A٪Z´0簹EYPWȊy's32:49 rs;w^(aNsC= P>/BVᡡS9^sI6"[/M g:EA8rЗ>9-++-MxGE|ᐏ6a43PwJ_`ࣻbNf&!,W_  [ĠOx0g#|ϐf(Lahvp׆cљe9^P_=4ƽB!o:ZxTiW8xj읈m9iwU:~n3y{<~6Kl?otiPLno QvPiZ^jH]T Q\=$p/Ma6]xfebU-uDuhX®xI4d0(aXP;<L#Iޞw^B MxZYTe{0!7*rA՞<)}K k{K8 Sh{=m3=,::}1fگ;ͥ3;G:z[:#bzƺ{ǚǪ;G 5n%9.#V #J#1,Į]CXW?qxt Pjj2꼏˪3Fԣ>^ C0~1n\Ï&UT*؆ix * d֓cp֭-}ؖc#jG,KB\QyeKtJkX36h&E٥U7/!ApMGvU ¿:SVWS!x%C2DoR Ð0327%,҉;hw@h,hwފ9D 1G%O޺Lv ^|w{'Xxo=zkKG>rlf߻>/: O.J22 _|z/''WW<7ߜ={A,-.2><C w\XYYaMC@&usF{?$ Jj[TsG _7շ%{&CU}cC'4.IocV}Ϟ9Ef Yu2yVWNZLn|bLTMnǢc}a[$Z!gx0S@>rX2iFOH$^794uK3 } sKZk['* Ҳxii\nIlNqLvQ02K)8 K=%, 91YNǤ]!E\mu^)}WSSfKJeEy's%%%'$B҈(6f~ Qa&H' E[! 6͝SՒ,N@"U7!ĩ^/M h!?8O:>;ȧӍ?3ﯷw38^hnCy/_b+qqE2YZ]{"/dv[lKo ??K8~|o n輲[\QIKf0xK5(Z׿ru/kUfu M~=EqEpyWoa6GW\ځ&93=sEMݛfQI_CS;z䧱U;pPWK$Z6\4KmmÉ/l.Z+׺tݘRg#-ξ-dKlyOj܏ts8qg\4H00Y])7TS0xx>1zf2޹sH' c[7sY B uM]Kxwp>FGG/puc/~χr/߫V>/84royS>\{UR/%/oۧCrr=|ɻ*7?U54fgfnݺo}~x^9JS; {7vb(H\XZܢ i Ԝ5L4;k:Kgt=C$EMOnt痯U3I~aAB]\uJ3 K,r\)rRv\M`D\ p%D4i{W߷#")Me{&vIj_;k,ZilS&RT]}+DjƆ$f湪.ts8q&>t$)11gn 9X,,Ǩ t4Ca6!6b0(a9*ξ' 0I[^VVTP0!>3(V8ljkKlmeG+=#oMmmcl@ OC)[bnuc˭l8枚UQ3r{hs`o_Qvvh:흝KN>31U)0Ac=p=Z9R³e$iĽqPu Y#)@8Ņy}bs:(c$}$u$Y=LLi8h<h͹͒ͩyIy f?IwVfffff۴Cͧ 5q,k)lP&h\Vu|xPjnMԟ=:4-.kxe}C"acBhK* 0fǭmd zxx *4(aSUUr[B  ,fW]m7< >„%;o]v/=A0؀nv5-lff|-"v.b;,x(s? RF/beEuJ$ɨ>^WQqKdӜGPd:,4%H˭o>RV6{=6x}#QEyS翾[j0IJ a.`SoS`B]YW;7 Rl:V6'Ɩ߷5:?l1dgVm jC !+Dnݓc==ʽW V Ǐe%H%!'Yꛒv||HNLH>y$:)2!<.4Ppu`ugPB0̈́Ty7m|ݎNT+--9U] $W4n𰡡266.H SVֆp(),ϯ..[e=qA9i Ct[AH|zrŭVw4_2ɻ.:Nz &wOdGDд~oĭݸob864ϳ!O8<p;qc[/Lc?ʙ8\ږ,d Jc&cb1,`C`O0xզx̦ 2ZVfF8Kqu$ip8xszq)rKfrdWdh$F2D8\p.H<UREDA3vX]<.Sg6& T:Z44'& aa#$2͔:"-Hi5*v8Xnim?j-o6v )8 ) .yg,s ?=UIpzֻ UٕDƩ\ ܮ^=X{'n2;­7[P@XK#u(eH< iFV:yׇ.Ҟn7v ׆' eZbTCaC C GYl_3dW5rhP{UUuaa179a(54>N9*oVUs;V҆aZ}Rc:˜Ċ#~2KiT{W /[ƀ 7 :J[ ySp7du~yVvAQexa{pdFjhlu+5'GŸVaRVTHZ+=*1̦NKHIŜ7iTЛpuusB;}Տ^0ǣ!v.6N>w }1jC}U/V>{$OJ̖.H! w߾9Rۏ>7ĕ52y`M u[FsZᡤ{r> ~Zu"BIn}m}}aw믄m0K&*v49ȴCCYt``3s ?lÿp,lá~nj"reZ!XD"-E@v/ cl{( wȐҲN$gHrB" g%,* ~-9#͙#Iʨ!E@iHqrIeT"3 aZpY=ZZ;\ã]=664Zm5ɥ*ʨ5a՜P[^&iLT 4 +uʻp{xKRk;| B#uGjVU!S;5l*i<:LoL(ˋI Qk܈>Xo*ͳ%MMM)i.^V )6썩9Po7xE'qq Nee]X,Vw=\mZpsGhtUr$zޅ5X -,!B ?'NZ!MA;C]{JJJY !4̯nkT` 8Gs_aW;܍>~x !Eѓ'=ʇ|=|c\=#l8|f >myu1a/m:xb&s_I]Y9 [{SKKZaEP  +k|eKv-Mk+N3N+!yuf+av]<Xb|rmœyɹLp/O!SMFlq< &I$p CaɎ)NY;{U*ݽB/@ȒbInaFvAxL 8fP¢21a^BδTY>GFGT CCվU'beEEDbRj1ԢSV`/M[[k-mHu]6x6$8s%J3-ml{FǯSb}KeSmr+ 664K2c|BRvf4K-iT;S+8q a @9Y7Z3[9pog:Ҷz e y ihUxꪶ8P9`STgKu! ڃz-:1Y <w!:8~6#uԋ98 8쀀4/?vb6 O=bҧ18 7/AAJӽ\HU*G=%ҨRӵ~~#Yחrp(m[M%WMS1rE~q܏'?gPB\qD"fPfB0k'򚣞!Y9YDjF^xt!ZT&v ȓ28tsdTTzᰨrakoQʼjѹŹeU5 y)nɐZ8虐TZEX&-kdU3>6>-UszQNͬLNߪQptl38'4ځÊ[v,e"44$48404 ?7';+xHGHңnAGJ::<[R^QdNt_2gUZm+28WqXJu<@Geyڿ?իa0$;q,1վ} %BBBRZ!s7YΞg- Ka"ـKrxij-)W 8(/g^G: up =giK^~ou18,ÄⅭMRn?C葧`97LO`*Tٵ s(,r?#*Uӟ%Wܯeݟ{%羥Dts8q:dBtn] 8{Ɋ\sː03/9=7<&~X}~q2[H470P]̐2f&l@~$ܦ2? r7;Y7H/8SLFcSBaY',:gdJյEQ{8\ 0MesE['Qn!`)x?~~E}UUen~Dkki_`P/M P5'm8uV{ ~O? z;znj;t ܚ'=O|eϔ:zrps#SU]Á qTQWIWOk(>-ޞO+ίo{^|/፲5?+ MG U$tp8م{0;虓Wz<B%妤B(IBrVpD¾þ,(lڒJ%lC58,7'^Դ4'8[X_'U[ J˳+`-<`1WוZ͝ghh oʆƹNnχ;Q_SwLw϶oWyֲ-`%'^ a"+, U[2~>8k U|$ybr>2%l>A4B*8~f’0`8e guu]n`}6@k .p8'74$h^ -9;:[ZPzaMCqzr7g?2~G?g܇’SGrEn9E*~FP6OkZὄ?L> W8e)pH"Hwns<`=qh=8;w:ța}wq[taЋlϭy9ٰv{sS>E"B+3=VHs[JKCJ R&:D)Z?yݴX \ sO Zɝzn) 333w%KNfc2-޹UT5,E)_x79~}7ܿ 6H$ݧܠ*,OegJr KgJ"yM OII1wLr䐭 LIMp /)SOW.RD`5 3Om7_uhuSUw_E2=pw?p^Jv%9UuVkkk!`ނfXpa=D"HF7ûh+>>s ՊRyLݴxRW wcԻ챕(Uֳ3fE' H!8TH$lts8$&H$֑H$lts8$&H$֒H$lts8$&H$֐H$lts8$&H$fH$lts8$&H$^%HLgHED"HG$Dp8#7D"HD" I$DO]$DpHpH"H$z"8$H$CCD"S!D"8$H$I$҄ӧOwuu555ʮwyh^QnE*ص4 W/\ep I$D"8$HZhnnniiN|y"w--K[$x 8!D"H$P0V4k.qfHvIo{Vk WD"H$CDph(J~~I`_oLzjB_,/޽UZ'8$H$D"= >Ha_}]_=}'8$H$D"J.߻'.{`bm{n+NpH"H$!D"84TEE"|V7hM4YMn- P0n }\KCډEfO$D"H$9EZȻzL¬wQ)c/,Ɖ9?^z#ԍoCĥ}7mX~r{3{`c\k,yxVY cE5D"H$CDp8w84Fσ)g 1..XQޚ:v2G)gooqn_JlV\g׺ @蕏9O\tq\:}Pu{ {H$DpH"5rosũp̓MN+/\^6fB"vVI)|7o?!)aZZ>%+/_rY_ޕi|gӶxєa!o3裫7pwхB.[p}3oo+m<-!D"H$pXTT48\#2֖"g^lZc&i&Ld ׈Dy%/D43ԗTx/LjMِo 72o`=y%H~9Jd__ n=3+n6m^)_D;C. )wDhË)k׺ 8U#.\쫎LxlVVVww0!D"H$P' 奫n0V>1F7rxiB[$Z#z[ )E;WF4эGov<,6is~o. =xYի__W'ƮxyC/Ǯ޷98p _Yc84$ s+*_ݸٞs-](ĺ||nv îY!!D"H$iɓ'g Zd؊5;7?˥<WTW>\jm!D"H$VdX`p <$9K}nǁVC0$LJa8LV:. Ts=#%D$0E9K^9m` \ՔNIyʗ 9 X,u\]"=`ucn .]Bq6K.! !xŹ\}CD"I$!'>5U.qpȭN1 0i Um 7NckkkS׬J1KE5͗szC߸S~z3థ`JWΏ{î 󾢡aZ^f5"DTXxN +#x 8trApyneʫs=H$Dp8mGKK{KKK_^Ȩ i u⬖4^PՂ>p m\^{MMM---_|o~Wz"DK?w$DNՀ˗//డ|asoNyE0]תw8=ls~%effRAo9())iŊ"O>̜t>ػwcD"Z~BV{=g77k>ޛsJUk WԩSqqqp礘ը'ZQQQisPdd$ۭ*<<:7ᅟ_&8\@8ܺu+ x䰿}ih=$VHehݣCe|A`!|Ua1aOLL0U1f+ 2\kwFhBH$N ~ +++}Y|P۷߸xp8@Z"8݂WtBc Wx=~|ןz^*y"?@k0i+uLxk^xm^0}>uzG~X'ɟ2*l[zamX/cN:nn^gxa U4lz/,]ЧyLlʠdTa?cmFسXNj/ުw|AH{A^77~G#$C5 |_F^BC1 ?"J1ZÏ]W 0}G/̿~y6 ! ?Ywymu@ڮœz9k^xF0Oхa/ؼ@/^x8Bo1aKޜ*DYmv-(_팇=b@l 9kaK~c=@ 1<7 rveŖB}7.ÑûٗDpHO'$CCC!=DpHpHpH"8g_CCCC!=0)Rr?~:!> \/<T酉Vz!bN{X;gz'Y 0þm6N? <[5+Bba2 FBa?9b!BsŒ1= ̘%32-WZ:[{%Ff\Ta%%( 3!OTBMًoD45fY[`qr~0Z/`i*̯061#V9g~Az1[h,mZ3[p4 \dPFK0uk     I)7lyTPa&Zih8y]/lpx!u%Aq.4qe-kJ_S}\AU F悼'fF eB tXF0@Jc-H8D*)PO\XXJ}Md*E"8\N V4ZeWe&R!h!h5"sE/)a!SɊ EL@rR{ݮCe"q1Yi<ſWjh"NhRs 9!>0F%bC r0c&\ >#K08D(2!lrlI P%X5K<ˍ^rY!U؉`abV'<%9`xF(D Ʀ V~ ALQNdLWx'M*auQ[qoav0*6!!á,P+ RP4da0kʥb_@ 7&jM^c(G-fVj,2Jc dkP #.,D##J#i$"3nTЅkVUZxp ?W,C!Y}z Lm6[fjcSc=NE mDqL pp Kp5*q1WnvڜB#k8t!9k?%zD@4¡J*1cTj Y%?R{S)k~DZ ""-ˆRc Ţ)4(?"1|F+5d*\ip^BMw JYثxXv(XBbی^2V!~rK)!"0"+cE,ug_ ל4p>d`μMpHpHpv)s  E\V 2)F2!٫J *+,L Xn)9Ms05X+SjL,\\ #qQmcQAe0P$½bRs}?HzJխ b0a!C |I=b3rk3r,898'.* HCp9">y8qe Kģۡp;F& G`B?L:r .T'0Q3l # ñ4PVe[/lU'B*,D Q{̭TGSKB&9.0KoF, 5Ll̪n#3(}YN j8 8|xg "*+=h5,*+du/W`y^GNSZ JroiY:_3/!!p8Q\q6.C8\RI\(*{ok?Ɠ/!! 'G6nL^UFRkHKXplp:{ E(/7B8|C92a wl4D/Y| l7U`o WaFy }G1R [NwaLv%d*5 aOx:  {#D[rV0ERCګ-06(Qs^(Hh3bV+5])rP\hiЪg8U VWA3]D8ԄV{h)mVТ󥅽 )TAڄsЕi8KUf%ׁrhDHѠ&8\pXbp#` M`$҃ a5qpї>Kݼ4 c`Xƺ4SJckV4Bc 0Ń=r$j:]syUP1O}\Fyyjs2z 3s(?6 !6BAKJ2!%3  coȢ~- 6Yu4vaSzN-lrpc*ÛT!en\Q+3LKtmS\a`jPG[:Ԫsz^:8q~ΡW_ wX9?IpHpHpHpHpHpH? CCCO> n_}դ^ApxO#GOO̅C"b6c} qF*Pna<2Cip)*Efj Shup/G r8J-,eh1mn˧6̓A?p˕JYέFU}][4ŝiܬgusmDSŁ)xʄ--C{qV89Rb9wzf  p<}%pqB|7pD>.V/B6؉k-b{ZX~]S+|X`~O,3bđĘ+Vø{g^U z.[Z,3XOl',;!UĘBL"sV7mZS̖y @Z@u5TcCMpx! .ÉJ6w>5=9:GpB jbjQ]\xPcdfi1s`n8P8H Ծ|(e=rZVd"2F .9Fm/s1r`|OVW!  _f; k]Er(6ϰsm S0> +Nm!F:8uX߁|ȡ!B~(^ׇ!8$8$8$8$8$8_<饗^%E|'g!㸷42 \õ`ƛvnkj49ڮ7kJk푆b VθQPV;9݁ RK^QeK]ю]nB7T;cXcKD fd؀M4pgS%dExgŞSe:` W0k˯_`kM<7s2}`fB09µ+Tr %iDnYD8â#ceی#g2]nxWC&s/LnVA.V ҃å)DCwDpHpHpHpHpHpH?7VwB ٮ#8\px:!e򋯧_ o3GpHpxϪW      %8\T8eOnTHLJ$8$8$]BcOB,4L,.%>#) S =Ncs-(.˜,@` 1Ňh!mCCfs}]0$=Fȁq z[):$:,Ec2(tI0A, G~U4UQoKY,VBFkx0&/+f`?^2 +BeB7/6;LvK5"WWFJp WVMn2§FZJf1 (h9 B5x;NH;SF3y+$8\p&ZE|4Scc3sSn2Hd9e1C3r!%Gdbmb[O!v|)pֵCWG/"/ח𰩃\;eyaamzc3K JȩNWv^%am6oxb&=>#Da6V>#!00G|DJ BBDLq 0'⎩Bd'\!z} >PgfM`a[!6gzPO,J :# Kck`܅DzDlz28Dtpenx"%ڡ^3X]PgbG$)ݧ3^ V2^S }@.0 Pח;sNdjbj昝vKå ZZɹ`qv~ `&d.i JjᖇR4{aUJV)喌R*`WШf='BpD􌋌! *̌eBbeBJX~iSrFA2V]bi$ H1aKk" . 0>QyLaڒ 5W@GDpHpHpHpHpHpH"805$`\LQ *Ogbi"c/NaȚOӲC\F+.'%%.&"t%kyxp6Aj="[ŧk()!t-\eYb| |B5imJž.@&\S;G-FzH#TMpH"8$8$8$8$8$8${px*2v+^UvcS{8RKNqܐxy #88..N&*zsaT.Ŝs5L,$,v@a0gl!x>lB\|05X!2GGc8TYpAbq)!v S=>^30.l! ct'Sfaɸm># 2C@`(' ɢb#Xਸ਼"Q`CH`Or ˉL`M:é}I{"\,#p8L Zù*xl[4k-F pȷ4zR88]3 BO  6r~bc"\\ww0SCCCCCC%8$8$8cή&gOpI¡1lc M[H.lPƈ}qW⢋bjanljR)kY~P#0֕`biEtj y^k9z8 i"z4 V`7JRE*T[] T%8E4שB?c((EX#(D>0mxW/+#N!6+f""%)1?ޘ](c'-܅xc4NG:"GJcB> Yb}vWϢx a# /0*,/A#"ݱD(H Aw܈Zv4|A_mCCC[<>je;c_ЂBJL^ jg(a=%hsh3MAؔ{lCCCCCCC%8$8$8f!C[:F6o=KpHpHZ"8$8\ BoL!6X2腉^׃[x]:1p ;"> =ZAX2 4a( #b ˯CG@CB(J*X$a ٹ ̀0(& !2t¶n2!t.|6fXr[Ђ7.2b;;t~)&bG^ɖDN 9î2^߯0 jHBS 60bďTVJpHpHSpS Ǐv-l4'8$8$p?{ǟ}FH~ӟ7Mkk+f~'a/o85kְҜ~S !!!!!!s.c -L| !!ógFFFO</l<Ӹ/<==CCC͠իWCWU~~> xy9O ֖@_Ddn008 [= `9b;,TpM潉|P?b)a$_s+EC^BCR0!27΄{4j4H=.,ŒA߳ Z5r`%Z̮]`X>+Vru oۀቀa!# ڦ~^j^ $NAq^x]28`0N_mCC3$Mo:ۮ%8$8$.Ҩ( 1t\r;7xv  ѣy!      IM=n6'8$8$pOVJs #`x ".ZȞa3guȄz On 珱)g8 1c//hlA46LB~׊ ǒGCZDV{`{{0Sr'~|'z!ˏ1Ta w " "\C0r(/`XN!1CRBZK+puC88%83 - Z=5DV |$8wpnTXԹ/n1kӱbC;" Z#FHyןKtŏ_m,OF ,2#8{~! 4*/|.G{U/aœBwy,0)ީS šA0܅ ul -ѣ``E |`%c7>H XPb< kFb{1⁈SZ1*0*[P bR|8K A㚲Ýbsq&!!^‰V]•0AwCG266ކC7Ӊs8vTP8!T*Y3/}mopq]J_;O/akk0BAIɃpIḻqC;"Xu!8Kzc1/Q5Bې  ~D?ADdm9b膺E/ OΫ%,7_HR~c@)G`K 2(BOKt.cD2j8c VY0gȍ`eΟH`x bF BR0 I   VH,)3;ϔ(+"⁘y .(Y^N1o#/ K()ބx/h6 픜<^p6XaCX-*oำ+!,)G R.mH)_G޾F/m&8$8$8$8$8$8_C-422@9qn+R w8LpHp8K)$bRP޴7.NLOL\\W]xB.tisU~o v=KpHp, "n1JIꅘ)1, C>,@/|ǧyVv^Gy!+)2r [?. 99Bt,:0` c*D2.9 0UD$gDǠ5GEaeH(" !<\h:30 s+:3g(C7(U0Xb!i ( .,WDc΢kjAwANa#=zau%C#hQ@{~ለ;Kpp8zlMÜ 8\Rp Whpꒈ8Y/qs.drیcy }[$?ʻz+᛿&\z+_y 7W\6 l?IpHpHpHpHpHpH?"UV};߁+|YqnJw;sM;78ݺLnCcQDR䥚he+Eֺ"z{{npx!~hJ9~ +ᔾK y.]tBK}Tu\Kܥ˰\ro]"8$8$8$8$8$8_åKppsCFb]AcKSLd* O[l*",,̌9cs%_q)+Ȍ#w+DfpAft60}i%q26F Ư\f>y?#fn kfT{/Fp0ך!>A ^ƪzʉ l߹ffይ.o.]Z}~K+_swyC%y%>WaYLHLą q/|g`0ɖU`SM1΋Cbk "> i&"/qTxږp! vebb!2=:gGYiK\VlQup85@ѣDs}i:7RC8V7~ m$>""j "fIvBnEJtE(ٹbh[`Oz:81D/pl7]}e~]b7|e>e  .2Ø^ǢG ("<x~0\ iJXt;F0("#FtEkOHc:mP X6(ѐ:,))aPS0#g {`.#X=@\  a"1"酻czU5wႊ]n*BLx!6&8$8$N8T`h jllJsi/m>s/65 \B`>1Ks+˽xcNI;p PnBxA( w\MS_y.ÿ+K#fACåxARSwT&!1srz!.B?ƁV x腈$rp@y`AC_ӤX<W+=$nahXL0af#[@V69l8'56#Q cۺ1Lص(5,b*)/Fӭ3Bx&U0f`qw1VDD$@ĹzrüpyzŖ4D3X 2WP;?61"WtprSv͝- G-v.984N]&ss(9~Zk4@̹5&Z6>/mҸ9i!fnk-[b(PckD"@Tܼfm 7}9~@nx+}d9tJ+bZ!n. VI#neqU؆a%8$8$8$8$8$8_CCCq=wå3JRlβ9!ũN@U(.^. 7*Wfv^|URKgپ~~    0>i٦y#g>e .+ݑÍ¿vW !7h$ĩlfpz![?|0*DȖ8}q6^r/q 2?d?1(bW`#WLJB@83'-ԉtEu%% !"tD0da""DG0T|U/l^_W~kC%WypWBD?ҍz!"3=@tUE|K/2`!HНKڿ /#ThTaOzT!jc1E#Fm0$`'&KJl$W כ66|Mqroo0IC7W0[qtB2@C84Lv䭷ѐLu&B0+?D^^W/^z!1"Y^-Ky BAkGmƜEb!L16<H_mCCm v ph|* L;$8$8djhhP(7ǫ椳 G~Qg!HW#8$8$8$8$8$8_CCC% g5㞾#~~n!ij)**壣9\gf8t\}ߒG  б >37Q亿bfpqL//M,R^FH)Yvh&*\  V/DDMЧpE0 +F/G/CI+yfx ݱ[?b# a. 2*@Bҭz.j.T*؀=7/IpHp8QU7ł!$C!!!!!ݫo}[6`6Ņ&8\p89a['*k]N$Iw4))/"H"8$8wul. I$;&D86CD5sy-ftlD1$8 ٔ6LD`Q7q=\p/-h8CQ ""40CƁ8!<[\3Od`8d²20.Jp fNM %ʐP!``B#K,8X VxEKGb:酼n^x!1)غ 1fi^*e^8iwa% QwH,..ޟ& EWpaB`|2d͚57=<^KK{K^0^CCb[PDpHpH"8$޽KOOp~ɑ7= Cصzjꫯ~_ދp8{Ԭ_sKo>S^EpHpH"83'H^JiAAQ=/x8E0,4U+ցl`N[ᓑfX9+EÕ3")#<+⺅MDЁ,0DP0A]5aتT#-~sDnD dة+W^sμiHVz'eL([0#¤^T:"UfdG7 0 Z?"ά[/L|-&xOWp1]\tVwMݡ R8ܶLlm3'  Iw@!!!Vl?/z'    IBAxRW7h>p0۹/ ^Apxȧ[&lorlCCClNH(\Ð=#60p:>#`~$>cl{)h~ r 턋+b!RklZgRgpaC?0?ed0' Eu  T>ѰIV/DDDvenBQw EU2Ur^_X, ׂ!v;^zኅlEDtm!aQZ3U t!׸1åosxW/8t?Q$C*++ƷopG|T*g_&*kG?X@8&8\2p8zOi67wҁetBB[zB>!V/\}/"aT HdCCҰ["oGwqCwMĭtD5G+h~r`ΟU-. i?b`W70l*3 eUQ0! JB^ KO 㑺#^`xE !B/-L8P"@a΢T/ҳ;G諌pȢbE*tU5 MpjnnA[ xO?Ϫ;[}z8pxwǻz ^$eKppjݵwn y' "BKlK)- ^Rp裏_^=x.7?s++/|߮^^鄭 "n*\Lq8#>sׇ~1X&0Շ!1CDDfc~~"2x-p $"b0DD8 Kt>\! `5A`X2d+n ?"L2\Dž 2.BQ"0P ݎ]_b ey"UgΟ’Mp슸nB,k ,Jq Bhj.frív)ÿc)q-O'.81324EGV=Z@8^R{\Fqsj -n+q|CrVkB@9@ {H$8$8$8$8$8$ C#?FpOΝp^˜؄}=5]< ?rp <(?>1YC8\gT0|x2N,gc6^sۯϩy38QlBM'P/ /xmE{tvvB">d>mC6cD dPdRxL6sWDDJٯXI2w =?fT++4>1'^:8(c 1?&nMB~ C,9ԕ0` AGlCatC`lz!5E&L+Wh#`2xpz!2^r3K pZ~} 9|0<)TŸ(n*DD<£(WͦQqcNKND"A4 tq-Y[+,96ǻ^ۮ<ѥH| p ~S|5QlU3Xj>Y?Yp8C .E.#Ss ų[!ڨ[hތ5.=(b RQ5ge=|JK v-C1_C.e;5ИW?sh"Ҟ힇CK S! o,x s13N4N 2@A.Cq>+%4K>^hV?F =_[йsC-բit9uVYg{G@CNDCaѡwDXNUM Ьjbj$ݹkXYXh鳢gKGoHk]{awABi<_޳h$ްwDB oآqKSSSaY^XaKOO'8\Vz:9? F2d@9o!>38ďH lmC˲PWC/S9V.MQQ`=hx :v%# !1"ȱP$*" %hO'r7Z32^^b^ἼBO"BgQo^zz#AX|`/& /;02:<]x1‹5L`MtKƒ `{*uȱ=kyÓ꘏W+գ£10Y׾5p8C \]Ǧ$eruj]koy38V Yw>UyorCjVޝrXGE;zBSڶt9\O'$T9>3HN gp1=:4b@$>>Dą+T=`}'3 dpBDDVD@4$@h!bx8r#"a$h`x:,"\w4\^qz!xbȍlBt(@~#.|;^X2( cNAʮ2&*@w!1bl7Š.p&8\X T^^#.]} %{ 3!v\9K8庈w3VNl^o??|W$r+'?;S˝~o] 9=F>ә{+OvV;(in[i:||v- ̽5X;ncȺmݸy3x>z ŝ\'I9{OBg 1 |T޶{[{VmZPպ׸Ĉ,"6a% a ۰% $L&LaK7.EƄߟu$#YXF޷_B +B屹*****vWp[&No FQPbڒE'%MOQ50KbOacCEqfoIp5*pKY-{Mhe \u>}tU': xK|qǴ7pn3^j/[l\0-a:zهWESuk\Qdkf@&-sK{0s㹲՞p}<G}U!aW5 *Chpp8%{ 8Dʊc!k/H0kcK pأO KPQ=v,g}9ͭ]񪰁dt Ԃê?f4^c+.*4z8I a>CJA!g6< LʬzM}bBC@$+[Ovś;"D(c[tE-K:Hf;Ÿ :*A;K2' ry!Ó2&'!{!"DvhidS3-M2,ɓ->I;1$]AD1l7%BIb܈ Nw6t'ޭ J~H^妘Q:ANV85Ru9Uy}TÔY %0fh7zoŪ­ MkK/C{k;ֶanns5:5pn۶ {馛;vpoQPPPPPPp`3dJp%p2 Z>P^B wc޻$uP\b O֓[6Nj8j V E9MBAqe ,L~H=}: 1ELM-ȮC$ -/{4.] S%ƥ,8٤]-ZZ%b$9P1y%  !2m8ak1$-Kܩ="dĚ',qwdQو=T$F}k#^{m5\_R0dM,>qK u0P}>j VE[v}uJ5u|xM#LP0C=<kow)oYApx¡¡¡¡a]x9|1~̙~;x}݇ ! '>R$Æ8ij++sF&N5u|x Y)zfu?6u_u)||~x[ѬTGuvΝ;1v7 #F{W&!EpN}sm]FQ~aCUa#+i`zމa͊ ifO-0tK L1#] TѤp(HS,iG{(Nd6'E!v2{Y( yd%S5 EhP$Z.]PH cfZHI K$Fž Er-.&IۗJ ;[! hqI^KKj aYn>-х” J\GL9y<@c ?I}kC?ݻ1xb";;a¡gcTvAdh4&NyMU[mQ8T8T)֩֯_0Ҧ:C|E;o߮pppppp"^&?iڴi*DŽaa~c&{O4:~)S?Sc¡¡Jj7}o~`<2虅HDE'!2[bEĬpHKklF -$K0FXBÀu1KļBR3ڎ2f)S|1b g-ŸDJڅpE2) " P!LPhBRq6A(SCi9{ZJ,R4HŚKb *$ʵ`Q$#(tKIkO3;+iɞQdϲH|В=Zi)g飭pXڰaL1(}Ö-[.b|)1][oŷ|FHE?qZivيYg^a#xnT8T8T)֝8pC =NCCCCCa\^x  M &_ᐡÑpY}{Wy]?<|¡¡JraNխpp(,Ag_KL[$i*ȬVeZ6r-:^E2ic)I!hbRf9n'cG1ѭYfj%#Ј(ۊ'y0 c#`oD~h5²  M:2X)J2kĂpIwsHG-UʙF6g-''r׺X._C B>} ࡸdDÆ +>_ݱS}zG Þ} .T8T8T)֝n2m݆ЈTpppppR8 6Hix*aYZ͵ RܫijgV©S-p@I& wߍٰU-aҔiþ f(*N)!&ЭDJ_t$(*****6"RLC'$>XiNڙ ݧ{uz*ppR8;anZ 7ER1~Phb* ~Aڂ2ؚ2)JòWor]lTv4(%zD$S-ȍ9$DضdBd$RVa?TX'fJ.}u/mSi8%{6BITu$I%mGil,-~`4T9HSܩP(>aG[Ƶ~yCؼys kU T$`;s6S|sW[cq+V)6R8 %58DBsNþMpppppR8 ~;Ç7ѷO/go>zYAcpǮ}/\pHO?gرcCW0%?qq3<E6)L(,AZb|*AAMCG# f8cd*Zi+J6'TCi+4#28 PidJV*"Q>J@':|Knfi)8mt%іdc!꣭pXq&U8"8|L9c朷ފn'gar/ ̜ppw8}Lm۶ĉMp޽!;Q)**Cpxp]V~~<؁aív]v8t])u?i¡¡~#g_|edd * e"--M0.$ `g5;D5% AO%z"@B+D% |>]اew}m*}G,@atA82&(ehZʒļ$#ʆcQ3ƨn*Qj8M4tSd h[S.S&)DLiNɰ4bJ&K3 -ZD6 Rڅ瞶kM Jw2OwruT V8T8T|z6kl pdC[^#(*1fhΒa M̙'x>iM *CCSt;v8񉓿Ʀr!av F%poc¡¡~0(r'M#h@CCCC¡¡JѾC(pX;ݱc牓}AȬMRUC#ա¡¡pey1EpR"`G?y|"pHhKuD7ͻőCg֥!LC=8dys/ JKJeSy>幡@g@I}Ս<327*0LUcrwbpSdwwc.9]+p¡¡¡JFtrP8l p]>{//27?JcNӒW #FWQaɶ/vʶ)J6Ni9 _۔mҜڶ%V צ$o|v u.c4wrVR6av^czNQmҖUXW &^$-;] XBm%n0tA.6㺬d밦T3+Ch{NT*Ð/Y'' $Z Ȣ4K ;@DĽrRH;% p2Ȁ[K@23QH(\p8{wf;ODSX1nCKH#[}пkeq.#]8ʒ(BAGn_Pν-(jOkw}Z[bpLy<SڸZ.8坯s( *C0?y\sҥKNf… !)s 8xЏ>r%pطt{*a^\s)\xMabڵ5Gz!avڨeA\$*@Y).,mvSLK]tځ /JN-hpI7eC9F9 9Cm(#uƯqssi %8,C8o.6Ǚ#E2tg0+V !AüF4vw=X[e8`kU$pUUGٍ!A% )%( +i)6Ғ }Q¡yvDz-'k9~X6#@{I hk٠ZR8މJPPPpX>&nF<˜ؿm۴mz필9^tG^ 8+xO u@yϳLa?k+BK9j8#m3iT3)m\9qԮOҜh1mWQIm^֛m/l_823Osk4+92MRpJl#m1-r漼 M5̦Q9re&.0aUr\J *Cŋc'ÇWR8 58,>q`\~7&̬+5x-_ NCM6!G1y9,3KM}%)-sk5{'֨.[P˜. (l`a7{WcCS mްMcˢ mWv َQi^5 w:vU0&4>mCeڦ:f|Gs@Yp8;'/䜒 !v>ƦyFtwNFGVOwl/KÐRP`RW'K!)CPd6Eԑ-3!a KH@KdbY@5ㅒ H |M8\(I:+~`.%l09sX 5+gېF_4Owk$tCr=DNپ ʢ TwU5JW:.p(cHё`Epeph,Qfy֖?+5E†e}w9t {̑㨥:rUcH<ښrgUh)&2K`I;A"20iMˋF,10)#2! (K$0)GY=l8 Qheӧl2eq(S;t@a4Tb'E%禖liMش˵)IcXbA"ډp)U%.C@c]ּ{L(K1%%=%S5AN4h?GĨa '^QpjpxހڶMX!hVV)G^)****)@KÑq!b5~Qk! ?].?ӟ*h'V BCI~Hc]XQL8.4j*T%Vm&LfAA|"J={ x(5!qYȍdz#Y?5i2 sҬk0͔Pȥ(2%ͷfa^qx<9/pW"C-Zp-iLr&Sy%B ɊK09p)ZأX*/y5 "o Ma% @%޴# V8Y!xFC!&pdnC]{\!!%ISۿsб`8qў/UuC8Un8;; *C~ rkQaas\^á:w}fmT¬ *ZsK/?QPPPPPpH'Fq75k4꾯ׄQcp0#ؔn۱ͷުOBCCa ԫ pS8 ){-H``Z"^rv~$&Q d#'d_:dQ. Y#:1Dg$cG:JЕCd8(gYR!dxRZt;p,!(Ć|e|۶(tZejO-1(!JW"ipHy$+4ȫoZUzˁZ٦ggW`R 2K`~RAP=fVQ8T8 URWk!2cp&U!Qݑ0;i7(iT0I JccQpA,4q)O؏֤4Eْ8eԩ(FӒP BSՔ(W_eӺ5 UƔD˓"~ϑ/c֤Gi '̣h7Ixh+֬O~ʫ:ٳ{馛;vgYxyr-o6UU8w8,{`|ᒥ+R-Y8|7&fK3+٠ppR8#?>pppppR8 7ߌԠBÐiA(&1؛璒@و=$?!{bhGYTCJYBMGXZث7 I?ӗe~(9c0|Z҂L(e͈($L1XSRB㑿nCТMf-hM&nNbɟ\K6«F(K"%fdem@$vxrp.hbˋ&8MK/l1~qh{G[f_>{b<%WDv뮻.0G;wGA׀Cg$ !5 {;wM ÷=ppR8]=]a./'LCCCCC¡]\śF֭Qy0(?aY.lٲ%;x5huNĿ=<{yG^}_(55l+2Znx 8<~/U +iɱ!¡¡¡¡¡JЮpl{qgb/buv8|ǐAbh;bk3a=a^>ap}<9f]Q}7f欹p%pVCCa?Mpd8Q(d(.j^D H.jt w&(/¡8.raߣΞmD\G'y{0!ʢ޽QB4 =ƎCY3) E|”iiDaJño2];!G +9d>IoPNIxõzlL9![%^e%!Lr~&saVO3+ӈ|# G8ck;u+p#kTw=4> 5%gf͚s=wmO>pRp0gmM)XuO[~3X9RFd4CmI3EBƄp.2%^|Bbrvzޚk3չӰ?$S8T8T8T8T8T8lB{62==p:p0ɳRu.q8Lz`):d65f'ESrs2)ކڒY۪@VJRFr?->*"T#}f߈Ϭse%\t ĔL5S8T8T8T8T8T8lB *|衇C`&!8 M.;k XpXppd6t]\ȰIxT~0Grpp)68ȟg ~=ap({NaM\ +rPҽp,QrRRX8k(t(L .߿֐SPO{NNSgjٖf犄K'qD$Zd#4((O pnn1sy1˻ !*[#KyY +H[7n=k&%wxc0v 5M¡{%)."%*&)+#Ȕ`MLDT'pޟa.inKFf2b̕_2ӜrQIp?76 yb"r I JNJ2<ʙ{)gܴfa19Y].cc\YY@gse-xRP. 7ەdC\t^cS| _¡¡¡¡¡aCBy9rbFCЁ >{sRk;uR-a^(6f8tTc%F5Ntޜ4`N| ,,:=ǝ-@zlRSى#`KºIؠ',,2;`J9eFi=f|9aa Y^eTXX$47@++F'fx޴|;l;Y=_rfxܙrBfU$,zqdlGLtdDX(t*bRʕ^F^Ƒ`{Ux|fכsbLֈhcs 9IǓQ8l8]KtJ{^L٭Uaq:\W9]dLR)MYb@EZd OB6y Jy,fBa#Y$NDaUa( K%6)E8mfJ6#H54Sr"D_6wJ2-L4((Ss_Hkbkjbi'L즳]W9(#Y[uwUej*g)}kJHvNCzt0K0Dĩc 0!׬ͪm8#~νwE|æLa5~uLG*nRr0Q39x#@9,fTʄ }py r {)%i/aF1Vt96q;~Fhh{ij~_d;#nw\QSYpap'l|tЅÆNPPPPP!  T*)8~g͙چ “qG_{-Zi`8LSwutN$R1J*ufYYY.Wdˍ0*wlX#44V5*(AcU |2Nκ(fćspX~&<;Yv8pg[,Uia>h zpp)ֻ~nU(vzv\\y҂@-< |D*"Z0ֈ`!ك zh~)|5ҧpI(IQĬ^)fJ9 ) Y,ƪaI nS'K|\CXskdQ90=ÓX5ǂKV꣭pXSzJ)́a';~Kb*:2-9X㋖*6R8'yT"bT+!jW]a$k@'@NeFn+ˇpg3pd8A\^,ˆ|.ÝuU#w @#@ 2Ӗ:]ھ_} Ø)6Z8bAa'3"eB06h^s h͊) 602#;JH ֪aWGt1)4UuƆpTi9g ڦ+G:_fmų(bexYVjJ(5ޤ貵d9k\ gVNT *CCH[رs有aYZKd٪񽣣[>RPvOp؈Pq}EfzwuX0l7a_@cPSY+aʕpp B> 'ēs5=do4v9k%vnh$ 5q)zMD;KtS,2EwLK[Mm6岴L}fpH@:3ʉx:=,cˋnt/dKdSKRnwt)Nh׎ 펎rk-'½*DGD9\FĢ;MN(⍪¡¡`1+&.\eڽy2W V^X6G[cHGIPPPPPPpppx$ ɠ׮T7p{0 2\PRRR3r+jDuIS%v  94&բ#t(/~2H}[ihG9q6 v]h~)/iȽĹfXHbki)"S-gdC!Z&GǛ"ߒ[" &KZ#q%{vJҋ&,IfhJ^PLZJ V#>./bHB{SI7K[2e> bczٶcO!R&NRGAB CCa҃>~E5{{o馻kǎ\oT8T8T8T8T8T8T);" rH9N]{6бچCepLb^^ *&BN WLh\L@{Q8c%m=r Ƣn\*]v{ B;R{6\G B. N;EHV ]`iuZJ75BS,4hK<;~{K%#MɖI4kːZA ]y>V8T8T)6AS#oĀa!?d MpX8}x)"[r֛8I>;JpKBQPiFiý3yw"R-5EC a:?)XjJHHN*%:}eLMrSd\ٖHvOE?9K* ܾr_ڍA 8$$ 92E,Ko~+/=)&>ڵ¡a S ݳuիb| qqJ7F,KU Qַ^{mERV^ vpp*p8~ G3oAaZ\[UPPpt[9s?!iaq A)K؏xC>dzwNO"D7%q-3%!m u}n*4.%/1\KdU$ădKN*rC\]qJZexfj,KsL}jJ)+՞bO&%wLT*,ˉJybZt%6`PD.IU=&,ׂ]טZkܵ'"EMS^3.B-M. "F}kuP0)f}sCc8JEK¡¡Jᰉ*~wm=LeRTO/KNP0y, f]P%Niˎ pcoS8T8T)kKE,aL89l8G[DD6y {,IsG#-'uħhiI0cŭM3%f䋱(D8,|jkXA3ndVJ[ڝrz%t]Zf4 :ZښYjS+,r%ijmy$Uݸ%=E Kr鉈h+6U.ӟR8lpXc 7QݐaŋҖ^\RPPpR8T8T8T8T8T8l4]O?4"*6L8Dk^pjISawyU *CCCCCFAp0n~mӡ^1lf׳qCC¡Nk^mh]p♠ ፕ>tM%TÎ> D8cAA&\\h6Ŕٴ 8BiKM!pNtƓ0*wDigO{ !EoI jG_O0H1Y"a%nOgXb(Z"n0cqHTIx'y:іHCV $*룭p@Tu:_i.[BC`W.ۇu 3g˱{UzK >I9~GU ;Q8T8T8T8T8T8ldpXE:x`bpdPúcOy/Xe ^~ӅuippR8T)֊h']p;.CMFXb=Ľb$ ŌDTЅc?-]9["] Ko$Ld@S I>-s1xDD;ɇm-iT)񔝖ˋV^̓DM6(dV B%$!& + yThg{(+/> D0+"~MG?wC# Ņ'Ѻͬb|oI1fϹ"pG[ppR8T)*****6>UΗRÆ E3M^ݱso!pic%^8}¡¡JPpX+"6qIQ~9 NްsPK//aXNcȖe' D"ejA^HSc 2+fm #;^pĉSFC *C(*****>_ЙoUp;v8<ֺg]C~@hC(******j fҩǞ@>G nj-A ^27">Xp"pd<*gwńE*V~p.;#YrX,<*$%UF]X9`)>!1ML0ϙa57L˩j ;QU!g bAl|*IAFxFDt\v)(,ǝƕ g%Y%n!+.D/;bvSH>% joRW 3Ebds)Z&_7MI C m3SD>zc.bx}L(KdY)+tٶ%h+*jgqN=`]caNn^. &%d'+1%ΆO,Ua5\ؔLW.:#Úff;܀qHE%df2^2GEDDeD/ ev"S܌p+[8؅¡NT *C.KxzC!%ᱷQ8 8$9Ãᰢ.(q8t9#J!e&_| \9K̶/ -!3<"ZWyˌovUDlB / C흨* SoZ#BP`v4.%BHBR % yߖ%kH5\K `?^h”$%I H!(i"*i4lp~d6  T‡#j $3[)6\,o\LIYA_{3eD1H%Naj)ΰDr&I3-UBrI$e%ۍ鱖j_(V8T8T]!""mǽ;N9;_*aÀǕM{ؔGb|DԌhg'E$e;,؈lb@n+=)YXLz++33;s|)ёeƨi_3TyDf$v[tZ/=Y ىQ8AWeAB1Fx0;z3q %780i9¦ 5q[BVջ******}1Jʕg}5UM8D1O?3u w8\f.at>W8lLpwEEbSrXt9 Ȅ"R.T2sN`Utv28izR78#Z&VfUs͌ǠbΔۈlt 22] ~+EuNVP0tE 7,H.{@?$^H!n Ҟ"2($!x>dCLWa%Y#G*%Xf=hi)i얐.Nw1xA-lCzP26gYޒ HM' A2[ؒD&cQ($6Y2;rKvJ1rոMn&BI|4.0QmCƫ[Q+ /N v&tu<v׻Pᰱ' )DØ Z1~a 8(?/lǎ]l8mVi3vS~ f̛x+Jh.ox"#"c+r!Ry$NQ8l,pbeu@sP +`r]Y"7bߎyW3ekc͐60\qCY.9[73-ծt㻔Iaf05h N"kKgVr;*G]68t,<>CCCCCC¡a}ܹsiI'F9 ḏcu Nj-3Kz/:DWpQπDc.wg&~G<ƀÁ.8 Kp=Y]ep _q0|×h3FtËp<$9f"hxZ_ە]k1O[>ba ORtXA:'pp"g GP7SܠޤaP`RbP|5chefJ®6IK:"Cj9’==@Ks9٦Ht2\gi)z孲tţYi@W9Fe9žݯbqIj+Dn NKfIEN6Ys,1)גCP.RNs8iy҅EƧجH}!2yYIܴr$ņ3ME91&+1Fv}Փ/3m10G&7+QvoKhE mb}G\4MNt9#˭k@]TdU D~Ն᧟ٓ0tNggO;8\i8iZqI3>~0[zN@N t dͶ#55Ѐ~nOܜ\75.fdCWD/+}b&b;JDP yv/AQr R%.Ib@%)q-.%-w-"څ pajKLm5Ɣ9$" ZKr`jdiJJ,$7J s#dȠpvUpȳj)sDe9Z6 鋔;*BlFңJt%;=M癊,o*}K:}U -)91z 8fM!"1{#n[Æ(=z0W|8F/+ثjI *Cú̜9~7oMH{*p.py觞] 784 }g^vq pts}pR8/:hȰ1bh? B3Ed"Iڙ"*y`bVJ"BU[&XJ6 thv(ġ^ESZ6nAfbNIa0 Eb d&vs_.?yļ֎b$D;b@1HeJ0h O\kAIh+ð4G.%{IT{|> V fBBV[}'&O8\*sÏ|-; @ŬM3rR^X蝹5U*&rzӦ_mG\ڃCoqc'L/pElPpػOFeXh7=߬ppR8މJPPPPP1q/h7qR'<*\IPPpU 4(>'d%bw/Z-l/XM]T*FtE$">cJB'Nh['6`<R=8n4ŗ3D_ K vuD^x Egq JH7,@|lBz?L*֨Ϧ+H =cBY]X? *CÆeM1xHSdسa!rZPfD o.0U7)6>5+'7;iICUN V ONw dY_;QQ9~njTDDH3dbϟdLdfg&a{Le$BNiLդCC!J,H]mӾm,=gӜ% G7M >o H`WMqSo[zx[^|/p]1+Glkƥ RR+?bHu$3dRBX̿GVB,DU!m9ɧ_e" 1 ( \[bTgȢH$oii'7# ŬK05hI+Kr#mɈH#UpK<meZ"Ē ޽2飭pp8|5'秞˫q8<~}M!pk3?R0m:AU_kow!O*>[1?56ڑbrGR21,î2TLb&*WB93bc32%E rxKL`]5ЛywlX Iaͣ|:F\U>6yr.;#h`3YXHhMf$Mv[t&;s -[Y)y暂O̕G| IV6 q: ­l([laTUl' Jɍ{L +[-1 D?b$IbCm*lA6[Rػ2ڊ}#b"n002vOd%"aO>l< [PE]bӗD\hI %C&tZb32&NKlސ,oWIppp@rsKf^#[;e `ϝ9S#p#;(sACB)hӶb&̭~B :C`H2٦ţ ͌4 #4ż-6%I5 1࢜9(Y_$OYNur8ПV4|NcЗn N2FScsaYrQ v/ߛj#Τ\!E'p:&Șň'lTZ&qCxXyDQ+##33 aδ̌ C{ұ&)$-,(S`v|4r2RI J1p1@Ƴᖣ-Ѵ~R: ZCVX24ѤYfK$#1HK\\g7„"D]$Ze4OY9+,1RSiT!n$kG[bqZs03oB~wOh+6ٳ{馛;vg뭷w $pQMzaڽ0foT~Ҍ 6mn­ٰ)Ř!(|p`bW 0N2ʙreeffw:i14=ʀ0iv{*y|)1Ȉp" Ot]ʶ40l1hDFbyZÓn9r0-!\*6-.6@0!c֥Dg힝q;cbc ,DL¡¡¡¡¡¡JբE b#s}|=O|7 0gSb;vZߥ[˯`ĩ+V[h_p?vbѡ<Æ ]~28f:"0kҏϝ#V'~wf%W"L'%7P5|EXg99nn 7b(ѬYiuf'+)bH#5Fvph,cˤlFph k?Ěf{ve64Z!zP U8 ]wus\ܹs_=.裏Epx:xYO[\}c[sjg v,}vL {-v)6@8Ġ<|106ғ< \3O i%\`V*@hni|f)i"Ob\B}h+rOњ&QSWEěY0R"6g%He$ߐ1v8D"RPi9fRN_=-1((ΐ ;?qQ 2[;.J[n{a# ÅK(b ƘyFˬ57Dߊ>~ݹn[eAMn,_a0ݞuvz}۞a܁ U8<8[wYџ SJУGjpx|'~k̙3s-/S2`otl$9{ipx(-;z:Nك5 2D<؁Cm;܅pӶKQ8T8T]0pppppp_/in#GA}AMx<1za\W# rd>m,p2Lth|縂ORW8T8T)jDppppp4HdxjCV05T"HOweV'.)Jg/4e}c8>,L3lT56h3fapy^kdh ppR8މJPPPPP髝%gg̤޺xr( a ÉQ{G`gEF9< a{~)"˄ R/4yZ׮fϚ4E㋗p(ЏМ^\ΠT T-C*וm$v.{՟d*\YI 4Iap9>#kxӐ71~p3Elr¡NTVPLB3`K6%qwD!gI)t)tM}4T6bwJ&$JL1F nuCt QdA}i~P*힇'ZbT;IRb*ewt2$Թ,}nd(;G- ϋ(3eqSj[L2Ro׎W\a;t?裭ppt*3xfwD D뒫"~bܝvn|07VzW pD,8p0+7+e0"ÚG'dƆ!1ŌýIY9H;xuLڅ-egI$,n"g*,LxJs"!-םt܌DfItdy *Cp*U.))a%KTs#gϞŋXWXX~❢C#$)O<UJe čgܷ"&%<8d .^8Fv"3<' f́qiQNaCa]}X1<"wvZo'pfYpUX̎ 3ϗb 68̈o,c#ZP{'j@"";g9#Rr&iy7K4$uDd*N80)qMi+MiV,Ѡ4(ҞbKR0KՔ4yK$4!j%SBV[MzKLMDSʞA1 &ySFX{B9ݦH$ 5i>ݦĊmx$$ "7jJpg/(..٘ x q¡aCDcئ.kSx K$0"ⅆе{;(k"hy'&{|vj#CF-.]ls/wテ&cb9i+ai"&ve%ń7Ireeff2/ŀ:_v%O&p{቙ņG a/RRΥ4::ѫc#Ā8ǝ,,ʠN$jq=,bA~g8f]CN-:B@nb|¡¡¡¡¡¡JP&՞=Ip|9|lы-Mʔ1xo5*,W+e+7OUegcCl߹E:o2<=; >LP8lpd|\?,ˆ . ytbחKCn*N-:1ņ#(nt6 c]Bʮxe $E9L3g *C=!CO!83[fìۖ]p|FJ,Ӓt{ >a04wH{?J!+1<[$d cIQ`'#Fp2Lifa&L9j.W9HFxtWj4V|{3RJIpմ0 ;%K 5 D_ŴYxt0R-!~Bjrpxv 7 N>ӬgaD('ZDr=tgt z5o&bU x?Nxl_6j8ܓ2k&~:eh/۷aq)[P;_ᰞПYuUXfGnoHj B)P*p+LCaŧ ] Q=ȜOjә(2V>¡ 2/AxSz"rA xsrܜnɲ v2sph,4s@n/CL@Tx󐼉anc$30#i&O k'P3< C)($0 $){d]TWKD+mc%gICb[6;"ҫP EN%*Y %R(ci Luڑ%8(D=6Zħclj%sv/AqJ ~<aQq%g)zTz-Ixt%n>¢( iF1Is]م7i-l <@V0X1:7+WBy|+tԍܾKU6d@/¡ajO-͵Il,bGb:Ҡtie:i L*!L7,@ޱeog#UBDDؚ-H%RBSd0[QDNv$ȭDF">Zue)Fg$&4E-Ny]C-a0"/IbʘD8T"ge[)3"r:ϒ=ZPșlۙTl_ 0e/'^_}Q/\HN^`ǶO0k^Z')ޒ&('O;/Znq׮Ro-ba}HPPPPPPppX:wgC---UoD̿o>dE C2-ĩӋ}Q3 aHת >\APPpUUbJJYBd2YqGX" %%#Xh %!Qצh,?čФP%%'ܓ(5A%4+eXgLDcQFjd"SqZ2ڒx ǝ6%6$@BfKdx.?˒23Kll͸2qP.iHd֤k+BKB;Y"!2?pe❬¡a}tJa!<-Cxu@pG?CSf68D3wmf6 q)J Ȼz¡¡JP{'*CCC¡}BJ}ϖ{@;phDF o1<,.E R\L>\qr**'|nQ 4L׿[у?****** ;cEŰ-{v09n|-cC `p欹{?[0.s߉)HR{!CCaChϝ;itp|AL bZbCa'Ȋ䥠Н{fWrG$zI9rN*ptuDV$$)C= Ydў)YyH+TN.Ȕ u{-1s Q|&1 H1cLu-2CO)^;cxőKH@69m ĵ8--ܖ[2t\`gŦxkʋt%nSmC1cKW!?E nx7oCouYwi>7=8D;u f-:V봧Yf)*ЭL\s5ʻ >Ll<U ϡt#GN#p߱xl]ޅ6Ny쎞WߟTwz|ݛ|2iXdgC?:gs`V}@U B/"R"yڷm|g@Ak\ɀG>{CwDE2$ɪ%rOnyi\*K“`P4% }90F@m]^L\#DcB?VJR.)C|ZT^8BlaM*O&'D ) rTxAoʋhM-B9+ h[&XiӬ Fr4.d7R.c7ͅzh?Jz` MpD wx$-֙ǧM@ (He1z̸+82{ v2g C˜޾ÿx[Q8T)֏˿{N< 8w#O?t¾4YPj~k o/III_̙5߻jTz`2'z8npDPpXw܁3+u9Usώ5c,O&= Q,NӅ֔E1OT?Dc&xW).JNG 7G[ƱS(**օjxn_nO[aK[nZNT *:{/8:fǎAsa+Sqqq_ 'O;uvQcpgY,qrG/6|9f a]ҕ+O.\&⬐C]{Ut;fN7'LS{g4 wi n_+]q5U ;QՀ6ـ8'Pn=YE*ɍ&t9,ѵB{["eI.C䓘L=!.%a?baތ̮OgJLcO2IÔxK4&*v~bvmXY<~ SqAIS3q#ax$@+{]!qPǻB6*VU)]gX"ő{vcq(.<$}Zh?ooĞ0ZRR^.ypx,u<1Zqߚ5mP_~O_qr¯@.֓#U-,OYrolh~)wR;1Fi({VOW^dp8K3,I,YcJr1)IH̲Hzln*G h:vcTQa0FgU0gSK];e%F@U&g69-ZI}6 .Wy%nG+A4N1$7%c;-SMa$X.)F<}0u7OXbP~>cxWp6VG]3FbD IG } 1=4/#7Mt-&"񏇥R>#6X=DL1&H2,|7fIN;^,~ ,Kk{:/nj9>3TNm܄l KYܷgҍ3 HVd䄇J~ [򏇧Y/?}O~f| Kc-,.ni.O{uH(=z.y_ vO[v-_O5%)-큥/x3~dCOsG-|o5L?%gG-knx Ll9y-'Eп=pR8މJPPPPA! GwHiX*!^qAIDddl-;G&O[黢{:O=wđ#k7]ųf}UZ{ݺ'Nt͟Vc,1oNڑ2jnFtgmmةpii?Yvmy;m;1{F'KJN:ş:[ΕW]C9R\Ǽg?v`{O8P^c}ؐn̜S7!c5,l/TO1=5,Z=Cǩc}v+lṅ#㻻|p|yyEG^l]zoYPpՕ"$ b8"5¤(@bɐĢMI'YQ"+iw*tM"ʴ5mi;jkHՎ$bh,j()EJyʴ/%nPKc >$ni0Of LJaǖH ȡg B?Zv3%;X^K)%KLq|H$@;"Bd,TKtZvEodB#awL 7%Pc&gˋI^)3,=6RJVWO'/ǏxЅ~)Ev_GD^}UTU2<zVPW2xK>=:sc V$ **6!{ڴS"M>ۖg ěgLq-糦4K:-j%OKOV-4^)7]d%H%k q܌҃hK<-yuK/2%3/\5w(.Ev_xܾ\zn)Sr^vd$G2݈%c4h׬/az"󁁑7Bzٯ4Of={f_S5g6ۻg؊mywplll zfg O{>U?ݛc27֡F#ƜKvakCQroC&%NT*JU/KXf=~uz opqŽڡ`쓹^Hg#~G0M8Ēqc~Xg ohV^=;cLZQx1,a#k;HyCUb18IPI)??]|k׮{Vy:P5 ǵ)F֭CSkl/wExKXPP)Bwp} eA+ΙpxR8|h[`0~ ?hǓleͱÎ3i)o$'wpp<_&mnpv6|mtpv=CCݩ^Ps80|hX6rHO1 d}ǟGN$)JQs,EgϞ]xYU5f͚a8BruUW!Ɖh7ļvj޼9i;|va 8o(Ww'0i :d[vɂarƿŋA~ycf`e,]GOyd<_I0`U>087vSU8T8TՀjv\}@Pm7Mv.Wַ!IPRaZ<ȞʢCbR˴MF@1]m%dc#UD؏o硻LյqCCe ݦ͛7k;\B| !僤;S]t+¡JR-x^w_yRs~lyѳ>_/¡JRTץpRB U o7d'馛p }Hy'Ə_WXg}+BӡY Q _m.dA??_nNgϞ{Mw]wر޴!X4TPR)d't9c!Hq̝;s_,㏏=bMM\}P= Ǎ'O?4K+r-X wܱx␅ÊkvaZhy=TZpV2 9U8Uum݆DfvځG"9UȻڪ_Y}@>Vzvկ"7`e˖W]wh}#Y_P {wyg*[= *tS~i˖-9\{yʐj>"cHKK {.g'wY/#|UZ8>2*ֶ0 pBjˬTC[+/*͛72!RKųv; UW]O9Z~Nak|_XnVz%'X¡¡ap5OqG8i0; /رcQxjUQ"MwXxJ,t:ϛi!BS~x #ѡ?m}}AuWZ8>2*֪0T2ܴiSеp\pΝUY_vC[+/KB2]+#&8CN_ ֤s޻w/ >\k|'XS¡¡¡JRTu!Z>6!\.*{<%_Y_v\Vʯ]+ @89sN?)99Vw+<U*JR8TT*CCCJRU*jHPRT*CJpXR5Z(T*JpRӪF+CJRU*+U¡JRT *JUpX\\VZMhmCJRU*aQQ_|VgBkU*JR8Tj^__W4cY lu,¡J1yܹU/ga9_JPRzx5kq<'N4R>\BZ֋}¡J:+j޼/~֭[{^{Q&pأGoR7!FuWR8TɬYfWXFU *U? (ùd~/k W{~WˡR)_߳gO9y=2۶mOk2/rAAAE8Mu]|gNNl ]bD%|p"vd3gwݒLM qPխ+¡J':t(?fpVw܁JڼyQ{]P?q0ϝ;_j]tyפrڶmݍ7د_?A|͟[,X džŷnwޑ۷/s̘1Uo~0oѢN}> 1M6x1鍤R8T5au76N4 /O>|8abB5THgee}l׮֭[1u8ɺ__xE cPO_۷oooJP$5a8[nbb@`yaYf!x' 8L0`^C¡¡JUph9{",j-[A%~9$ړ1( ' S9]ӧ# b8, TpqūI *Uɓ%[C8iT *U] RQR'\dPR8TIV AsdrJpRT *CJRU*JPpRT*¡JR)U*JR8TT*C¡JRT *JpR8TT*JPRU *JR)T*¡JPRT*CJR8T)T*JpRT *CJRU*JPpRT*¡JR)U*JjpJRT!#CJR. GT*J ]U*J: C*JR. *J JRBFChJRpJRT!KáJRBGApGRTѥPZRT 8TT**tti8TVT*U(T*J2-[\U*J: -*JRIUJRBGApRT*UdRYJRBGǷ`u*JRƏ2ϡJRBG L ~RT*UhK*CT**t> WT*J4K*FT**t>T*Jդ5/s$RThsTRTd> *J A>T*JD5Ԕ}>*J A> U*Jjb*h>*J A>ZhѢEK+-tUfJ*J A>ZhѢEK+_!4XtUpRT*UӓV1RT*UF/9pRT(xT*JR)T*J?jW@RT*G҇ېKIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/scatter_density.png0000644000175000017500000033224113502206677024754 0ustar noahfxnoahfxPNG  IHDR_nbKGD pHYs  tIME9,kA.IDATxxWg. HH[,ٲ,WY{/.K,K{$N.?ga4=je9s?Ltȑ#G]7ح_[G9rCG9rIl79rȑ#G9~_w83c򙄘7#_FA}f7~tfѿAݯ}Ovґ#G9._׮~[?[?{> d'?""}dK;WMߠ.;^h w׿w1 d'?‘#Gk'N8yцȑKL7޷`Oow>8ߎ w['w[s>[h[N~p< `{:]k8(;wQ jF.Г{`׆I/]s?=йp󉠳NCS'\ZH#GZnx/ 1}´xf9{f L_@M:k.`k3m8]{Ɔ9r(So s_Hǹ޿{;9`'.O^(}^_57/:xۑ#G-D70WMкS4h…ֵ:k08 ܹQ MO#G>؃ŧk醌SW3j!9٧}gNx~_VWupm 7$|Gc[WνΏ] ;]9m W9ޯtOȑ& S۶5.\X?q@sOz٣ºGV<.85G}֌Cʣ {a]wS;CY7=^jI igV2L;]9΋qbʶsdͰ9rDtx:UU 0'N1h]Uy:"S 0'Na'?E>x8tڑ#GJGݹsy(t;ND剃p#GNFWl95yj&e?y2 :q`é0W+5)hpߦ! dIG9r躤^uȑ#G]t G9rO7wȑ#G]t1G9rO7T;rȑ#Gxw}_u5\s5׮X;v5\s͵ ;rȑ#G0 ؿwoK*vݭ>$;rȑ;GQo~<쏶$?WqSȑ#G9r5HWqSȑ#G9:P\ny*n9r:GQӡKEn9r:N_)=l8mضgQ(/~ܣw^ܡ7m{ܪ^[tnʁwQfɑ#G_56;hVۖlUqwVelqpF^Ao{wÆ[o_}6wc5_}WMH~79玼ynV:D݂#ue S9r蚠P/МM' FQw'd'|ZnM:~m:dΨL\`>h_*}_ $FсR ?uwvnЉ-4'䴞{キo`yɷ}܆V,ZZs W:P7`1$z͚ܹ\/,wHuL%K[Yl;i#oq旎9Ey8vmRN7{^=y h_hwMy{_|h[R?^uكϮ{_xW&ǁ.s_ChҚk,hй-1!g֗/wӾLmE?w= ݻ›y]>snkjN[;w'.mG 12g}&O޷^AݳAYؼZTgsn }OG>ۭ/,,)і-[yr͛;r䨅)`pAI_uvgqSߕVZ/-Gn?~YؿXesQ S~߾ϷwY`͆=&w8vq˜EF asfj9#yϿ릇pGzx)[rҭ|Ez/,g_GJ|nDО(}gבk7otwޓX,i[k?>J5moxU4zx)H=vvoMɟ)?o,f5펎0mTW^czʵle 7ߩst=K?{Ṍ3jv}=xscY/=w?nRWa==1G 9=w=\<\Ώs9zr{.r~\~>ǼùMoV;_?nYmw7P݈uv7m6ݢ/UƍEd^t-qO~ '/ Gڳ\goM O0oEJe?o''wUV;9jI o7[к8>vu[lO DXɏpG{O|"}'&Ǚ[TU d6#?.*ܳqukJgόt0{|طܢ] [[_Ѡsɉ,ѧ Ю}cO9/~6TUy?mKú6Z27lܬnycץo߸}~ܺuG [_Y;oh]G+?ntcMgμ٦c<13[WҋkWuo*sU+}Q0,[V}ǎfР5NS{ot\=kɑ>2c9o7z[pF ׍W'NS汻>򃛊w9;YyW.'V#GZB[hGo!asێZd ݲzv\~MϵO݇#aj1ڼ|yn_C^rǏs˯>oϞ0}f4=H[~mڱcG"w,Z__izρ'Zw՚GG_xtoۘ}S+6&xo̡MX ؠ-;^7Pݺ'A=?< l3dG>ز~dqWnݡxsCV>Yۇ볻n߽اOƺzږxG{!9dp{lYv֐r_YԚ_9{y\sU{^Q8u]rQ~++ ľxD={/b>W/}F~ˈ& #.YvȼݠϷo&кpWӟiqAuN;gͪyz酙)ݷU-?q׮^x fFۃhJQ]G Gqpܓ:.&cXgwƘ񛏟w|ޮÉ_pz[^{kk׿mv#fx.ƹW{{m}`[6| UOyjTT[n%{qnޮ{_PXCu5S㶺.;G_ko;զ9q3vsԝ}L3i ;;z|zG|hqg֎%Ӵ :l?$9dLװGWL,X49s(YTswwwWCD{Yoڡ|æCo8re(`p^ O ed?˘D?|&>61>;]}3!_j7Vnz^kV:o_nIO+yZQ҉sROmR߶8;Ё[6mLN|nnҷqxWasa؏7wȅ=Gäe嶒j\HwwU[YȜ 9e|UC1le閉jh{&n?w~oy==H~j-|͘{欹//{nʕ.aG+[=t?6 ;tkDSՋdjy M;dˎ2GLh;w=Ug?_sVXُz)hߵ_ tvCe:2[ζ^vHIbKNJ_ڸm;|=b |s\~̊]ߝ8kam`nn9m_}g q\3g _?mWӶoڿuÇkf/%}[FĖ iM <5ÆYESPdJiPu;v7n[$~sΘ\k;Xw6uNaή5at]޺T hMkի7}ˊ|pw[Q6{Wk=*O];/Ѹ偾[ `)49pۯ'/Uiӏz Kې#GZBPSaqH?l h!wvb_x/_JWS-@zƆ{׎Ղ}gkkj O8!Z_uꝵs(ZZWURi ةr)O`SVg1+_ɛPyn[p *X9 ԅ]痷Qⴂ:$W8҈|K=P2l#qۍZ]y#r&}]7h=qgC'Z-uOlom+~`7ynjOꑺx1;_IyXn5BvQ^i@/:{㊳Z5O[yĸӧ,ĴW>d p\|ب=ԘC rϴ]\S1vʸE&8qn?Dfz=$?uE `o#GZBP$>RFBz<./|^n/?h->ْ`ϙ9k'ƒ>:χiϰ?nMumt-`WYeSܢ߳j{g&nCm"a閃 1b w-Щf}bȜGfh_0{~u? ؍ ~W?5f'Gn_x.o}ަ?V*q]Qjlq,Ϯ8ex 9s+O+;Qh DuC -<==7ڳ_Y}fx &d칠f֐<܂^)s͍WR/䌝ȑcsh os>?L^us%-w|X9j N㠯dvw) ',6x173ޓ$uO;3c.r'E.\"n۵/]>f ZXR&"cS1cg7?wИ-{=^PVPի*:av~;w=̶lĬ:ttCFW7q69rE|l{~scu?Xw.2{v&^".,&CBY/ǕW\gbK[dL1u,}c;>ؾ?hWzO lMI>/pG-۾rm_r=im/|<] ؟?)#GZBěLK<[Ox踩Akd 3KE_Ja)Z2=>S4F^J]cK|vvnʘjZ5Dyz#qd⤜~M[rM %[nuuG߿o sqKիAT֯?|ȓmwԵZ&#~Hg?`7Z{mϞ[iP;vxdj|₍{f>?OL>CsQQ>zt4 ؄fk_>zĄC9#`?`=kS6,`9vdأwac?&[g?Xv *v}Դz;u,*hc4}S_|]Ps [HzzρQǢ%KHnj#EPGk~o;W{;},8zMSiitpVvG31~EĞ%+W:ȑ}8p5[~OYTJ+ 5 󇍝3dLs2zN/ 3IXf=NG0P͹׭x{Jn`dU,۵ ,`nj۠sOw=o8wO_AZk8(C/8| iݺ rb?iSD(דXon[iܳe?۵_܌1 %K.=:th CS%x;rJ}Q3ngff-wݽ6?xi[+|lƛapK"+v>߹ymড?4wL `7 {Ҋ(]@zq )ëjV5(ǓnU[ڮŮePy=7{C5}zU?X"<⩾E?{/ֶmjHs'v {9ȑ+BF-Y1ZmJ~esKmչhKn=z{ݶq/&J<Б?7ڻ2yA(]@ZQKzbsa}6?xUҾ}iSƺZc}@Z͟;o>^9r}#:tF~5ikQvTRgpi]c/D?,vȑkB~GQKEn9r:QtM#G]:j.vȑBGQӊKEn9r:QT}M#G]9rȑ#G0 9rȑ#GW9vȑ#G`;rȑ#G`;rȑ#G9rȑ#G9rȑ#G9rȑ#؎9rȑ#؎9rȑ#؎9rȑlG9rȑlG9rlG9r#G9r#G9rۑ#G9rۑ#G9rۑ#G9rȑ#G9rȑ#G9rȑ#G9vȑ#G9vȑ#G`;rȑ#G`_w={l#G9rD0X8c#G9HLNzM~__ = yGW...^r徼˗oZU7}znjj^l' uZ97!1}ڬvrNlY .445$wQϞ]&v.]ضݼW^׵kA}G۷z8fy.8+k˯i*WڲQ޽9ҦΝgϞ]=n|EZt.] FEEEG<۷oܸC.܅VRUU|nڴiG^fСCeeeb;1bر=ڿ?˥ׯ_O?S=b'Oyw]SSSUoZ<3@k ؗx< oO۹31e7_מyĂuo瞯XaGjٳhIJ>}rtٺu+' 9Xo~ ƿ#$7nAZڬ*6 Xn{wiΕ-ZԽ`i7LV=8kIϞ1ݺUb2O[ءcn%=zK2sK׊ꞩÆc;Б6'>aq߾/}3F).]$Z+rcc7Ϝ2!`ȑ'Nޭ\?ujG ^z6yΫ= A׮\e*Ue~(0!!d{ᛲ4!ghl^x!;G Dvx%ga: :xp#_w.XPW$uLU5@UYY l{Z&Oߣ'KoHF-5 XeHc&$i3vX:0f̖YwO>|8*2711k7Fȝ2<106B)ğl )j?+ 2!ꮜp] 3fX˓=|0ˁI@;JoVmِ# A%ݻWhA.Bk׮S1(T YN.- >uPA/C7h.^|ԣc%KFEN[G5#F7X8gΚIh̲%]afRz'l@hMVҖ%%Etu+g֍\kzh?aЀW?gH\B؉^:tM2ãy=S:s9v9.i69sQf/Yjy dž+-Bl30F(wA2 8))|#[/Ʀz?y{<k@iOÏ*7_c}29̳^zNPd @q ?gWq!5%n1z ~;.rI" 1caX>$c2e r!`Mc9`Bwi*%$ ֺ 8b@fcZx̧i<i14F@d?XV@cl=S<9w:ʀxLe͐lndକ2SzА9&Gir"שּׁW/L0+_d_u֋܏>0/!;Vpe^r4qވWͭ<0aBaa!= :̙3G46p"?@r#`z%KɆL"#4BOVg0pm؀HZ(Hb=-t~a7$23 z yZNtр>u=66FYyAppz> `Yd;isrg(͂6!"K=3dz63 i@y 0Gzl4À dPp%y޼t,iii-o.]8V 6H[F[lu2 ڐdvJ</y^PC3 т idu6O$Z $gy9P 5xn G ~![k @[= W \.]PK0W2CXE.Oga.{'KjΜC.mHUM+H#\"_KeU8Q.Zǜ /~«uK8?si#JX̆N#55]%+AΕlMn׭΢#ڿb !u6D&N_$+,-z^B"gUv>wޖٳ@ >|lt  {32Ȋ ZۉfqfeіVN#O&$# +Rz+S{ןel}b6GvY74Y_̝߳xxP c@G[#2ӥ0ii=a;I9 Z$htNg* D'h&%1lX '(̑,=!љ[]qŽ{XcvMiW FdzqRG43#{Xy\J6Wb!BC4Jr'a_}ҩ-dbY hۢcX͝+,  `-cƍ [JJ%.ZWk0WжPAc=v -.f3M(WϞ#Lo8 g=Zi.eIݗt)l߱nI8Ѫ?`MƠp824wiBb1c/ 0E# fհaHǎ1Qxfscb'ydY2Oqw3"+G3ٶ9Wefrl$u44P%^߹_a6@W=Bۯ?5uN_Pc`,X&r3kVK E2@"2 ZPA`|>FFi9#c:[=` FkGõ"._FnaРFـ+"&]{ZY.zG+ `E(i0 MÐ!C0+A P ߫gfP4I풭tȐf4`JIufc:m2&'3'*̄kgЇ[_IHBIBcJ,)$4ţM9Y!Գx1k6 o2< l32LBa`^ׅUrJ+4@Ǚ+Wh:B Z@͂۩W-}HtHE:;\>;id;RJZH=LA!:x?_: ՟t%zoߺ>8v}pBBd>M9 R2jCX?Mlƌ+1 'U;I[=+KM[EEf9o`rՀK蝎7ΝuK"T 7 !0Zh}m[BV{L aIAsivhfa^ ١`HBSa `_9$~Fwj*B8b45?! AY٤yX[@C3䆣۝SaC#c`$׫y+cܪ_ JӼ%?6n o),R=>{L#MrH~TRB2S*Pv x q[`;vRr#o6UB fQ@Z'@EVe㥥_:EV 5q$(ꕆJZRIX% Q^61|^s!!)UBՂq;7%g!\CkӗuO.ONk3UIz BliL,qKOWn4|qő^~VK}nidkWm#G2$%4V-Gl,0.Jl@:YC@ AWӐMJNq8o68MɓnR!aMa+}0!*_`~(Q“ڌi L:LGLr=1N?uj^uJ55gr"k-p.*p!b@kSZ'a*n-"đk?6`hK6_Vsh LjkrhQ" ḵX_"CVdq2y/ iFW ' cV`*h uȚ;=QFբ6mqFMrPM$ $e3UII$N4fǍ39HYlzGD,_˴WZk_1*&{H P[њFmEY}Vx+[`c6Q[E)~Z…~P:Xc &,nc {m3&QyZo`fҸNʪ1\LڍNqp`h|yaij/`:9o6FSOFQ >Kf  o ?R1bl4-W~u8*V<=fӅ7VINMNv@s΃_&f +'pRpP4bUKX_L0Lr @EK&"7yR/@{#5Mr8}U~bgt(j]@Nݺ.fEB"I2|8 r bQ.)| .'"#'v^)si.]3YiAqP<ذ93& b$T.! \n<I\)e^ UbT+`G'/a~[ [~N)qbWe鹴O_ن _3-%\"vt(7-dnLٹss WC3%~62/" ]E]jR؍h]o`ҁYBt]c`` IĆ5[ Jlj]Fn g|Ï\ 8-rB$b _| A".ҀsT@,X1U+hx52 O[teቝQӔK=&cINъ+S Ϙ z7+0:\8č?#$rY¢td_`Q3,-ت̀T f* Y->1C̛F79Frs#j.`$ B-[*/?ĵ[@4 j ʒʡ2\ ` ɯUNd`X^ Y[NGLd`;vlW-egg35qq]H'ҴC=կ~+_ ͵M jJ}٫6{Ґ=TV8KV:RaС4 ШM ē @jNl2.$EM¿`IY[kv~`wR VU4LN0TX^K8f$$*P0wAiK<'5΀=2VɄ@b \$3A]Li ꂐxyi4޴^JsE0U4"X+5D;"" 9BԹcBu4,a2#[8B& @! pUJſ|c2wi/8Jc rݵجeO=-"~Gde w_|3,lEY׿֛*lT#Ϣ6mh]a_Wr(MU "/ʱ|G|1/2h cc #͙HHk2 A֔Čd b(ةzȠff%&k7 Eb^g%ٹAk*`izD#U{ ~tՀ$0c9d&LylX q߸OK5Uɔg تAu hk0T$]`k*,kg5<@zkxb_] 9\IiaGM 7N}C- fL2Ko!C!a)Rjy*1(6.B+8?gΜ[l^', jGbc mE}،Z + 0hID@5 qi kFVnHM%K{|+M[9(!I=.mRjlEQkedpi<}Lh\/+r-`DgJH <&k`x< }y9:Z";5k&k0Rڜ3V=fޟ$NjNo)GbD` ԯ_?!f=뮻hm_=ӦM6񄄄~رcH a[XF&Ncex{A_+iոx<T'Z HyJ=E)^$DG  M<.M8LT ˄GB&^ky|ByTv9DThH¹L--.SL׈*4Ԥ!F*"bAzfO.&X`#8X"( 'F5\)1>$[I\pg+3L` gke1Jq0[c`z%d L.Ah 75qۗ>5+W5TD QVq%pFqkFOǁ2VOMg? L!oV>|gԧ>UJ|Сmڴ⒊`m>cz$fQ_b:+2k\|Mc9N-!!zRS7 .X Hf4i-R w3JS|6Grz`NNGYLlil'Qڠ/m3<Ȍ2FׄhkaAj=MÏ_6M5 3NPaR.C8eI&*MUIA~0qqIQ6Kl`^w)1sE*MV!+)#[Q\;k%ѼYn}A_2VuIRVfOV HE,yL{QB,y}BV]F@R(]"ENU;Ifgotv ~,vofG>4N8vl#`s`rk^C Ap(˻vN/A&JrwX1f#G`PB;&N`B0DgyŶ 30+O~sG\4Mee7lS#7R39KN&Ѓ}C&l n\E Zۄ)=`T@n;-\JJ  %0V۶9])U)5A_NaRL3j0LYk/!JǘbѕӤ9 =!͍MQJlٸKؘi Xx(4vJx< 8!7^i㹶&+IM ]ixc30͕x fMܨoƚcePw-٘8FsM@ZfS,Я6l}-67Qܾrl׷ǣny2I2iTo#$@FVF9Kxy,okRlW)T"B<-T޸MvE⪭P*D$ ڲ:ycP dy<#=:0v"rUܑ[zjݺ,?&,(4xaCȚ+o)V^'ڪeN g'5ZF[umb gdSPNc= ъ;w&V.00JKD!V޵)Cއ ke]( H?ٿie)=0iL;wQ 61$@W ƀ]U04fT^<z(f=R 0 . lx{yY\])wd툫*ooƃpߠ CVf&Q(d!ƭN¾%l&} @z9 MӮr `UhjNغ@Vފ5nEeeʑ D!cBk漊^HjܹD$Fb~אlIwE+zқfSM4eJem`:rk7XL DޔT v 6nޞ[EG@e [kՀ(TS/m6YռX2$# WgkSL1iGq&/,4cV6@/Y[6ܒ{cϧƉd#hx p8?`ܒ.]z6^TPyּm PV^T j/Љ?kU!|Fߟv7pQf@I4>`;vRL\FFі "M|!eF~ dPo^/4b+*=Κ%8KB!%%&18T: }g1ENmڒ%a~ʉl]šj]'BWgt]ӯ)ylߟhl?;t#@.Umo`"ʼl%W~ȋZi[ "-+]U֚0U)=h⛤W+,2' 4*KWӆdsf_u#ysIϾw3xSC.mf%mr#dQĊ}V.~-ਆ~R@MKܩލ(?Iw«nݼO8z'ֳ܀w GPU@)g$-k7+RP%RhnS_@A5&'Hb"\ `]* U*q-G_X(`Lg#:l 7M@kDgS yAz~`/2.]mTtaW1PER!"CD^)EDf:g0Ư0!g/4cx>G+hJƀ=W/ E-~G'al՞Gۜ1y.Rɋ<5 \)< L兄o+1$?}HIkyï(k4^+fӣOȨ+?er˥y-G ̣sMPJ%&WB`_)XQ|#$ 4[/U|:/ CiU ZԦ TKJ`i"o+bE.Zfb+ b^lG665[ܺu>ᩩ7S k@[h$*ec [yJAq=bRT:'ͫ1{(HJGZdK%^h5HcT30,ȸ,;*W)(S22L.]sc;"Ϗ qʌFF6?Ұ )/:Vו,W{ 1^*qE*yBˍF-Y(slT` v4FaTߔ(x57bÅkGɂ^ q/fe~{X MJVBB6X\iQC5%oK+p/=~j(9lIV8'GHZ #G6Fd5vkEE 6Ea%TQ E tRG 29@Iˬ!(X5jۉQ2LC͎1c/ILL+E&/; ]naT@\I{ ߙN܀98( J,`*l'_Y[PXu3QadeB.貎6ʙ|l()`idىm$)++ukk`H*` 2~+Dkv+@fu6e+q: ȆfQƟk*c . (5i+:wHJ镦1VC;W^m#F gT-(~a،DN,>}%ac.x4)&8F]ia6  Q ;=||pEL3 Äظ0 IJt]k0l=2%ބicgqSzRH :`ĭw╖sr0Z : `<@*/||!;v*P#?Wfa!?*7<;guO`82G$SK Q A.SKW8Qrz8R{%'iA'6 @h[(ESq6WhθwA;"[!Sʚ>}i$H!, !)ܤc6u8I44U2QXvN..i\Ŝ)>VD6-Gi%ܛ:6EP'sC3 !,WwqqcYaBcym*=w+Pߩ?l'aGbEG4 itӦћ7Kdd R$l6R; *SS=$(h˕K~ n29(m6ǣ64. Հ6fg;[11jP6Zc l>ܸynZ@luϞK=-jx8\/Ih]dnP %))=OFY67EzpS$>,>jH%.-483 y[ĸ$ьE!Ӡf0gONar`n0 q؞3y4z}!3iw?1.<>ْ!ygt cDSbw+oHu:{An8vğ.W1mB͢Hn/T^Z*r؉*T'H`KO% }܃k1^$cX;s& 0Vp*Bke ,޽ sh&yEk=zJ.M/mm= TF82`ޖ2uipύ4瘽qD V +c< ݪU>#9xI׮JR)Lgh^ޕj|HBc;hM2#OM[nH3=G-6"2q $kq<]!yUl)l+V?YUs# 8\tԃȶnl8#GCʪ+Dd0`3*s0el`;vYQsLp!9FG*,I}nJ)>V,$L^N.-Wk8Jn• JMfo\QW O %0Q)OљfQa^fw)9`#5n'aJM3UWJhHPb/HKMh˹(&v<Ʊk7ZgK@+MpOz/[j$jضIBg SnS\eq1YÙj PR=l5̽)U-H[*4uަ )Xl(j+ۄ5kKZ(o>:2ZDsllхϧW4@v蕃t}vȔvKgw:׮zѤGD~AE: XDD72$T. |[e NR"'F \uO>`5>۞EVҒ+ (€IS+rAudOefljy;ƿF5q"J7zcbsz.͸} A3A@8%ܹ,3R/ ; zFt&(DMB ,awMtY{A–!~a;'L`$ǖ,1ݣG` sM0-M&HQh57%vBWS-; T6@= J%e59Ń($Y(4mp޷7/]oZ_PL%s^h;W`_[C_ }6#RdHYfK+K  >-%(A R.wD+Jf!r$EjoOT.5ٓjT)7`vpZ!xuRee9} yX'N4IQƍ_ُ`.Zhaa5<Г{1c$/3Tq1xqL'C : 6 %F^-=٘qscP1,Ttð&4{2:tAA‚nS`jk.u,PAk_x-`_*|w5{GGb7Y,@>a%Ԡ*JIx-& ޲GqھŹRW4Ja.Wq Wȑhw~1E |XIDBtt`!W'04zpNB&m'܋+SR0SzjZT:;*Aƍjm=9.ي"=850 ^}&T tG P1H)=m;!Xtj3f"^>#b<K,萰o4l $BZ8k҇]^ӦV.0oD7bJƤp U? |19\K)VTbhC4pWYhb) N%WG3©ѯk5`{GeVާB5Ei;F6v˙R63bbF"<6uS$=1n9J@UdI]ִ(s.9shЋQrN֍ȑ49ýa* z!}af*Ս9Sȣv=0ʒЇWyW.-F$rLt٘0rι41'`LK6Qp8iIi FHEҸy] Ð(\8IVf=ɣ*wIƊ =_,`L!YiNMAFI Re!Jq4h"28Uy8XNeȀ=:p" >⸑mCfUnpxo "@T,x#gt%t|αsdڞl|1뜂ǖ:QVl(a]](kf n"J9ig^?+G` =0K,*'LȔ QGY| [.,AXk$4Zg(4t̆r Q`;vl`_yݠW-˨lFE '310bשbYl?Mқݚ x)js"Kɀ:k6oT!i،eu<`FixG &` Xȇee8xfTѨbɑ,.@au಑3!K3$,#^~f$l/)X4zCF{`;-*eGs2$ ani^ҡ#=tҹ3 &T%;$qwR3Ndc$m 8S f(U@9ۜ#ī(qb-[ÕrP<\K?&0 G!+ cRܤ05 g*|@Aؘfl`gCz2E9O1ϟ M ;+Tdx#?/=D6x7S5_VYH4EL<%^åtRS텶(L ǘZ#h`R%5]H˖JK`xS~C [ G7׳p&}ڽfq[V UH@Ԟ ذq+cr`FEMsed;a8):Wy^ P_&xu5iv|g7hjR{!tVоtܱv!YۼN"ۏD6;7+ ),[! Pa\$ܤ[3rtfOhJ{`Xܔd.'YyT=$%z u+uCP@(6#A#%Bq l@l[LBegS26 njjqiQ)bl`;vM%޼i^КXxЪQfl^N1˖!)P``'Ι3_-+H`}̘1dJ J8ΐ:Ɣ>,L0I&̙k./K!\FK<#1]/ yU:\ȸt%%QGxF&f% ~霜e!@.+>,hq`!sxIlcZf]B'8 h&RKvJ.Q =Ltg1lVSχIN c[Xˣ .5&NQ<"5j!_."U/oJp1 (JA|#`z{_QO*r:&$[t]p ¡ R*BH 6\NyYpQ(Uf)'Wz 8F) om<-%k{6.~= JV_JI#8 k̦ǺȗS=B JFOi+u*i k52&hi8+F.G3!jtOf) zxX^\ ]23OeZo*Pk /''Eetp ʜ/\K1c`X Q9~+p $ЮY 1g zL규L&.`Лdybzc%K SqiWʁ}W@8k_nhM/vגM':$Ù VFlA7lr¨/귨}@,B6saQ! Ea99b}4nb:J^~p,-e|(ܟSŪ8G22H\9q鿡Kyi=R@*7a0L"+Y vQ]\.`&z[n`?#&lw}_|u{vߧkÌڲʀB߮U$/k ࠑLpe% !$!"n)Z͈Ϊ*I e&}Jbfel`(ɺnfV@3h sCO)QCw@[1kWlpGk+wnQ_p_m{~x*\T!4"YA1<}o`$aUwƣ?/Ubeuޛ 3Hg@œ$(-e0t#tÁO_Ʃ\5XIDpݳ|)ur 77<]!'d]Uu3"CПgP̙3z+/֯O_g?piX=}ue+s=|QSF]\w¾ IjT 㢈Fm;[2/x,Maν4J̤"ч4tv0R^iF@buK⒈g*jҒpr\? A}hX &#F@NOs1@Ȼ%|BK2Q*pC~J+J̀0hPe 'xQr kXUd mDER\ @2lǥ*z iikzhG$1~H8sw#I~ u{.S^3r 쿣Q,qul<$*y|;P ,:vl%4<(}l^ߏgϞe$7ɪ|ʐ!Cn0] z-W53lմWabd2Wo٢:*^ȋ9d Ut2@UԆo)Ϩ~9j|ұ7VLM?21j))F?+xW]q~@&N\񦡦h]g b + ~d#GЉQϙCRC&lb=eܦ&SW%>t6mڄ)))KW98d>I>/:א囆juP,=x~iaR2d)#bclST 's/$ٓ@`SDj/*BBGZNao"W)$+Iҷk ^·)/LqцǓpBNfPPx&ڋR1"_͓\U@iVڇi"pB~E`4TདԷlavb1QH2W/nPɓ6 қ\o)-DNO5-Z(~P3'ZKM:Eur0fc9h{@'\ sEOca5 Zn>ٌ)jk ˽D p7r@QJNR .7p6k+XIj]/6se>r;Hwx&^c0Vիn'\bpv}Iرc?)略Y>n6)EDHַſ?]ݠw4d9hr-I☭`!%f9*Br$z`T[[`=[( g_LFEE]^4FhY] IތyXRV:6xW\Еt2ɕVO<7/KSܔ"9@"׺$.=B6ҭW,:.E "ʫFvUUEj/ 4K{D%Pi/ԝiH3F`|g̤qQjL5#G^̰ᦀ 5@({N 9*!_84[L:$ɔ"kNE. Wf4 e満wM~͒W 9c.geKdw}PWoqe+ loTtT~Zɋ,'w) ٣b6Ai7ϯ(Ι:\/s;YY.N&MZ%Naȇ2<I/ Lt$<r׫F!+_K),[)__wx5 `%@?K:9Esb+E6eY 6)E-JX {sF YNUTn:JMS)m!/C4pNEŲx)e)Ǻo%pҞMۻ1d`, G$ lxxʀ6mPE  nep˧hɀ6͑yF8HU D_+aKM+Wmd#-5JQWT`^Hic%pLjF虜zҥ-ӧ4.ti Dgzь Aչ.Fn"_kˈlnQ/#uJŨV)ls; `Nr" 9xFSoy6n3%[ͮ4V BFHFku;R^kUF߅2hT{>$-vbdX^AJV%9@;Vf#7P2>Ҝ *(l9D4=^/l0 I!Ay5q\hGXvpG2u9+,N0mOm`oʔrx "' 1Q+pwI+ń|/6ltۉ&[㱩3~|,yy-mMϜHZ$($9`H3:P%|t%.⮚ʫ%71 ىMjpQ ^%)Hi+7BY(Zz7}8䕃MG;y4/Y~KQ|N `;vl/`44ⅎf0Kh@hQJՌ4#@c0hl8!|3 WQ@ 1pKt&1 1leMlGF[5D 6/ZJ_60$l@eCnFQ\#2hD~ ׺)`T'I$d&lnCMݲ>8NO/23Tjm<p(f!x4\ 0s`>GEmWNew`H҅S!Tw 9eT ժ^Vs-vx-i=52hѬTklXARw%X)EXlS g={#'/6`5 ]N"l1%b.>\6׷kmXdd\yBNTbd`9 W1# >Uq}v^Dd|Ŧ4Ӯ,jvJfב H) 8+VQ ^QPA uN[,p6`ts2oA'~ O ^ҵ+ϦLjf]cJ^| H?6U,KS[ic @`)Nǩ,`Vה)Sp_i w3z3n9-^|`\>FIۚA3~'Ll.4w'Q1c4t{pL ވLؔ&qsXFaiTi˟EN[Ҡ rR?W; NՍˤ;\,l{n%(K8T4:@4He%ȔXzi 4 8em@>- 8dsm*.-Fw*?IM8H5jjd,q\Wu0)Mi8;&]ܭN ٸg MQՄfR [ *q'S7䦆y 4|IR*B-Wsʲ*t,Ш"ddHyH8&q]j`G x8+JF'$+ N?R8w<kL 9& =]=NNEko Ax |$ fL^ 험_Vj^T[Q7:JKDhgZo%~yz~2+P `icGB ZIӁi]r |6%rr@΂+^fP`)X.Q~G\(ao6zA &Vz.])EON3b3f˩A6P IL M"h x|^ګMx99Ҵ&'mc"2Yaw{@LMuTOs)Eq<-fa5KTaM#DZqb!@hQPgnt执7r*3bR:e]yr[=9B HWTʘzOϴ3CKtEN#; [hyj!$iH|ȑ"Qm?W! K -z')£ *r0y?ŦK1:gT0 Rq')epA&z¶A}M]^52hs@5 i ^l$? M9iؒAb'MDƱ1$cFGN٣t̀I{y-NN&l.#=6xppƌ3gGC@dyBaBa!٠5MrD ${^^v+2HMZ+GX5TZ=\+VwL~P6Di UtDiomR kVxݑM7އMn DF"+\9Ye"E uˬ6@ii0I4K-+R~077WWpc$ K|Ǥ3 _-Iݼ`gKw2e.")7I.X (U<57a2_Vnҿ5/6F^ɥi/;]a5'I RS9 FeL N<)@Ě@9d Z ݺM^p4%?>,6i%G{NYAuf#.ٺpKl"THqVҿכsxoT`[S>mGB/EO[E7FȑڝzL̍l`;v8F3k ϵLD?zN zNg,RU-i:B^4d_C᪊[ɐHh`5nب1@;P\U0&xY-Vnj`%'$`oM3HF # :&Oo )dpEvqC0&a զuC2DB+ :6#:b ޓkɫU\SזU2۠ȰIT3LU6ǠP- K`NTsS$7vwbxwt%8rVUlaP-wߵl=!]cv(ZVf qV%>J`._̼@h~>í)k: ;$}jR&2>H!õ x\V6& Hz!YY+eY#Y&$Rm SI_t87#) 1I1+W1Rex|gyy+Sj1qQ^(l=찖xBctʕ&Zv624peMg.[8&N&4D$-*F;w gs||"Έ[O7n(aU Z}@t:~m5KW*vJפvRsvBLfs?nnc Jxg>r9z:-#)LV+>+>Hw( ?3|Pi\{pޟ 0*\ȵ\%.U\5K[tNB Ŕd,>~=zW^sSF1WܤF6?2MX}@ YK ktUJUvN 6Q{PfhkݓjBCd\ 9;…VKJqQj]jg]F;>$uQT/ G"EX_33 _PZđ.d߯-!ejIG=np@=ZONa MQ7k+߻-YTe{,sw2'w_# ; U.ʷpKSQf 0|c\TR3QdJYA۰@Fn3b_:7q_^8$G8ڍs5p%D/.58?Ar1"k\'PjZjU=zrEV^|;'G`=ٰq_&vF~eQ1h;.ȯ&()El$fӘ uM 8ttbx.X,)V+ trj_i9\徘II39IVJ+o!RB;wرͣFq/`9l ܲӏtB+Mht ' 6AG̉^ X;cM*6.DyŶmbQDG&`q @gE=QUB#6ҀXH+u++@FT*ppB"T3wKl笡p 4:y ҭ)iYIR^,2 a!ad!/2 9N&r˟jRwZ0]$Q+UzrƀJe"Bb6S0 iXN\$½+["|ʸw FoTU)͡IitӦUwO#K?k6OJ ~c[Q U[PUH #p}H5$ <rGLKA *+j[ިNB{$C u8r˔]LM)W-!]6ĽԊr'wl`;n2`]ų[lQ# K.ŋ >k0b Ws^1p&PcJGBl ))3;u1[u4;w"+WJHMkA}̔j\ r=W^1D܉ X嘶$>3Ը(OX1r+/vdf-2y<8`_`5p#)\¶#i ]]ѡЮi9VFSU#[c^13+W7B ¸T{fbLFuTz+op/3\,%S p²W_yiPԀ; Hɬڠ/ӻ>yQU%\Wi g}N `_v>(g>W5:&A7Uȗ6Fwqi,* w%k~^X_XiҺ#q33~^෺Y, wH7,Y1Kԩؤ+??l)M#t 2P-t63f窫Aܠ`MqkCOM؈2h%=5լeR[Mŝ0fYԮ(*'ތ# 87:C s]LrEB6cLWfDчkGVիB~a y[/H!<`EuPjœxR YD"}ȯ/<,H i[IJ.qܺDP[oF]c k= 0|bJ3* _bfEhpqn)TZFKbr5Y,BRO쌘N 5"Ri)3 OQK*$lΜ9@) 3b+/@zT͜^{ R%xF\UVsR"3m'R/ t4 w{ð" Jc؄6崫IMD1HpV@5=iHϧNyiflJzY3<eH W>a'2Sy/ Sټ,Lg =GJ~5X+ӏTX+Ylxȷ#}*Oo?p~; ^H:Q b̼0KKo'r~;Ut  rC Wβ̗@LR}``|fYpNԤؘB-4DX dmect^Q4h r/GtL).]Q@I.i6ϸJ_ALËF)?w.k:"1HN6 :k%c/-i6Qf3N4 tq0=2qGYb)8_sV6UhylpdAjSL(GL:<ʏYzZd'QNWfB <[OxK2ڎY [.,=#嫑\ɐ#o /~w-8rrUb+E7b *rG^B:@ snS,0ٰbWn-M3_I_ 2~R#vX5X!ZV "#*n΃O;|XsV,, n 6uPJKHDHXMRqb, Fn2q$ 3`'?MιsK?634ʤ0%0hۖ})Ʊ8| zrÆ}3P^Uq0&}ѐ85R`eH#\lKʈWLAnlK. y5oכnC2i(! 68Iy/-YYlk%U;mI$ j"o'\xi%dKEߥ2ŔXh T.۶<5kh/&O"d=aޕ 5XcZhї;6$B,7sГq!S:wVIT|K~+dRR4`Y^c,lZ.hc&yV%[FMŎYqƿʾ~`bWTijR$/C6%7Fbp/`G}!~d(Px)9s1*-"U'WwK*$IƤO# 閉YRjuFÍ^a|d]C%(f&CX ; 5!f#qdτo>ƤBHJR6XRL$̧Re9KX.[!FXo cTX- KT o#>Rs8iBN{HB@?`qrXP^ziQp}%v8;d%W5<ɺ*qFU l(.Vˢ>BV2t邈JbAʯ.JKt4 ԑJ$UmEҤP/ 0ç+oL͕\LK=jFL7z#W˅FQ&wԅ:.mGFxjLMwD!+bcd|;6fx$AStvuxD\0)>K-A6ɍݼy,6ҿ j3zm~ڌ4NE{)?je`.Λc /k7rsEOͬ [S;(T[{i$I_QRRkƔʒ2Lii#@N=2Nhd!q:楉Ql`;v؍S^]htV=pZFGf^j*mj.@ X"`$pZV _EI|@Q,:KթD^ 0K׉' Va`4զ=Zշ/^%Mu)k#F/Tmq^F584y8'( u>F:&vq1d<-&V,M̭DE[k!sH>8'*  j cZ l6噙\eޣ_?!ռܗԞ4>y O⪭27R_amqO; >UY Kqz2uKcm\yF%«|" 5_s`8'I>KdCci@or@GF8vpҙ4صMPX!7Q EL|{{GXAG 9ZpޞKbbɠX1$`e_7q! {pi\h .\jvRz5dku R'O@C?l8K0 04ϼWbT4eGtyšc^ʮY/+jJ_u.uQ~f헰#ۯw`YU]|' eΗ-'5Q|z#]i_8-7ImVTqXwd2qn;܇.tZʏ MIIܖ u#Ghr1a/ؑ* ߳8/lKʉDQH5@MVNG9-]D 3uZg,R&uV^AOQnf*K@Y~E q$eAx׹0.)%x1csbbrUw"22lv&in${D^R`?`%UYl-+A%[Tofem(e%B\u݇f.5Cl_nόWycb92B||8#^#G" XO`-ߦ)b"F5Y՟|wB% >JD7$HW^6,͒Mj3B 2hó;Ly҃ ];)9shl r]{Wrd N$\ ui\">h,s+ڢ>}PHi4 YֹSnŎðPs6O˒HFw '{*8?KW8h2.U_j7zlKd]vKOfæut)\OM@\LUF7]o5cW]|^K>Vx) @Xզkq`_v~x& -ͨ!`6ˉWyQ%u&!UiQI  n.akzmf,W)(eIW nQ1%J{wj~b - tDb!msqhM#7 0pl2[ [wۅj^BFȖXm,UƯ$"XYrvvE؏,Nil+&@λ5 BLYZ+͘nj)Ӈ䶴QIDچ糊nPc[:YF/8'&:?^5Fp"UlFk+2-7 RMqkMfQV̅pRu7ݜ$F.nIc!˲2Ģ -9 Y^B1c2 *h[k*+@Oՙsŕ[)hJZ},/Ujd5=w go\HNK = lR2rɖ ha$ =.`f0_&БcB_bM%KZn_z;@hMJm 7cS\iʔ(!7uY׉jZqTK?`qjoWt†޹wOVcS*ʜg2]6wMb|xⴑ"oK U|^vDHޘoUmeLՏ"ܘ~i •+(^rBo dDFZ 7<4*'Q3- 4&^2,k Fi%7%ZHB/MqFrwUpEbfBdcm^k\a Ұ1$\`I͟3D xx |ِ.͌-yYәd'!1LQcn]H}Z @А ygJh+?N)f3'ݯr)Rٌh7`;vlGX| \U ΙT> vfT-晔$LN^?t(gG!Qky90>Tv9s%}jvEs۔d`X ?.0ƴ6$XPV=,& x4Ʃ%(9;4~Q=5zoқ(Tz% \<=z!|,pa gˈ+Fx)Q`ׅj!>w8n5h貋.~4+Ϳ]ylZ+m̦-4ZJn 20腴T) ~h|TPDin AtU1bPYd/dqT~.<rh I($xA,sκfqrj_\G ٪MEEߦO9`"@) 'Aբ6mrM.bcBƌ)ՋX (8 um,.чe7yDZ5[r ~ʚd(L=8 Fi4u'.$ F( y V4ojA+|%K:ƨZ%/?3 Asڷ!^Q <mur^CV.O- ` 4o"`#'.3T@ǎ, NFp9W4j&;D*eҜ_XBsQn4yuz:i \TX2aq\7O[q&BGW.3y0!LŹ)m ;B>5.$wzQ3Lp`̘c_…4Ȕ)Ҥ i4*=YlQRZQS͟5x_^K/Z|.677&mYp`}!fs}=*%7}=6v>}\3@6hF1 C!R#Y(&NlOBmJ ap"`e_oV@~" br:c2AM6ԩA[;r$ZhTa/ܙ⚠,!:+ {Mu S O I1HHL$PlZ}uٿ0)F.U:PVQ "F3QH4dy'mf0IZ΍35BBL(0(*9 1~¡qh'&OC:%cǎbu6e m;4rSJHq 7qH (SM!'ǤYݾd$dwA،"0:x$`II ݬMr)b,#Bo5МK rwTJr~ (CYo0N8UDE"9vl`_+rxohNfjT%; ȼxE'?wu=sD#P6T̗@K+ޙhe^hAÔ|-t xUkYCҒzjzY}cy=[؏<'t};/g>g޽{7b~/W /m47)"O3C(X;?楒&M'LML bX鍜$x)b'I0U1Mb6<\r#bNBU RM p mɎ20C\ F#&4uc230bv1l?nSa_-S"{_=P)ŹMVp<uV'kU4HiyŤAZ BoEHk D#>bvãAo%MpqFe`UL|֟gP̙3z+O+?g?M#B?9;"ad8 .]6];ƵxzPF 9,T#(^‹ֳSRVM@lCLn*_yd«mdAE4ۈR}[sIR;h.S3yT<5^tiG 7l`ٚ*Ỏp sAYqk6P"=T-r1lL 3lRc3fr"\QɭQsiiA)l6Љon+7|- U[Rxw4憐6J(U!89 G?*Ml>{vF>_?eC`_ъu yO1Q^~AJO}5vn(qihh4,2x4!tN|NݓeqaMn{EU.iD-33 !jЉBci`@6´t0' p0EwF3XWp c&kzxc͸C8_Jp(ŋumT,[p)4b->sKȼ%M\@`)ܦL J^} [k جVhS0G׾5{(ܾ"III999lD!CajwѾ'arDqw*o Ɯתժ:ƬX%Jlbh;w1(SREFIRxRN.d b;8)I:OY͆O(95[Ъ0-Ffv@STnbvy!s _qSQd}>q" lf@뾶ReN!0y# Xs$i4yEB!zY<+@8&œAs2wCZznݠhlC翳yC_s#c H$ObGPR_l5؈G7tο˿'Aڭ}#?ST*Cio~Sp?J!)[/۔(u仵)sQ0QӐV.BAƯ|.BQA*,sGjil$ l'6YD6o&'F9x'T14dc`s\iT|˖EFʴ 'C(e3ʽ=5IZJ µ1s!ňaƩW77yawWyg9Wg0N6O ]nfo[L~?``n!ӀwxN+J`}/qG7|3ްџ+ z:{G~;eO a7cìZ7TӅ;ؚ peر d{0˪? G\>U11:ro4C\H# 4MC7=S<44t"%rE#z5[\ϩSUZ߳zNϞz&N^@s(t;6vMyXW0&,}-57t$~; L]|EV[c_ڶM}{ -PMXHqes$R2EiҥK=aspjthg#;z-% ~c߾f9س2<r 21a[CYv\ d7&g&DA9|8KjX?TbA)H^9͊k@Vslxxc`16@B)u?WO?4ihuO>+o~`;v즕܉ߦG>yn&l⬏. 8 D* -b3!m 0FOp2?\}]ox+@ ìĕW9 aN/հO\אrDMs"EP/j<:C$9~XN+WUf5 :lԕ VEnJXZq=+SJA y( 5z9 Q1pa8XXP]ЉP-q E@m0?:ݸNQI Y[lU}.V,PHO+׭eLUg'sKby׻RsTdT ~.n΃|4w E; HFqBH0@,iTYʻfyPQ =¿l XbVb$b Q 1c酲x ˯KDv `@_H&MVfS:%tMڶ|YCo)V8s"S2ڎ={͉ ;W/Zq 1*cqe< ^<iNXZn .JUؓ]MúةiD))&C޿# ƥ곁1lGY~Mc +A zg 4 C%:5T ̦~L}۞=lA߆yMuW{^n Zb 8 i7: v3TMF\+0Z.]6hik"1b֝sSѻ6Xh9 q}޷SݬWILQ0Fy &c ŨWw 1H"= <ð5k=2f7l]T߾Xj,o0M%ԱE"N1[)67߬2M7aRn '*\-2l{ G`O/(Ź}`X0|1 y>w.Yrjo<>U.kW=l89ndfw,cM P}iA$pmw;(])S׎O)} /V9=S0#ՄKqHm&PkK6V6!q0R}(BZݵ-9xPwP=Gx`xZfSIߦp 8AUg6Zi4uS\(*QsJEcW$xv#^W'5Zv4v=ͩw*۠0\H00 ؈sPi.,conGxՌu}b/ @}5Ĥ Qc[! 6f+[2K[P'NhFB)S+p2ZmJlĿ~񭂲nR8٩Ǵu/'8H]@;UBwut)}VG }9rNΠwm?6w.VMpzʕ,%JG!7n*fo?몰-cǣ+VdAٽG== S%p].֯7onVV]cd)SkGc\'?b+D9XI>Y};Ϫ0VZF >ZLXGۋ9`Q)ՕOZ6ῗ&7ĭy䜤1)`*e/ 4L:>j\Eb*X]ɯG((Nr0L^e\/~aTe`0pVSB#*RW`g bٚxGQ)Tvm)SQԭM 7Ξ8ZTcP hz-fP ?T(&p;"9ucU@,w793.9ّIE ?+KCp=Mv R߾+UC'& Ů,Kr2̩" ]ymڴi4直Gv?BYl!`0U ,&sYX-^=d]u/IZӅwd}>.FB F,ɗ.A 2(f14l[=\7)oX<+Fb}R;H# ?r:,TR/qV}) x߄͉ @#cfr@PBlgĉ hh6kW =y`/{JPt+Nͅs<,;ӂ6='hY6 L,kw3N y4OT';;+Yɷ=0nǑK@g IF 2)]De_J#/mJ4)DK"5a?,"捽p͵ l_QkNy:q cY_9#I$뮾?6Ī|r,XZ9q+Y]I6@rhҀy8}+tȶv{Ǎw9PdVW#=f0 q&iR7zκR|c~߁'Do[*b>Ry8f{_p.R󪃺/q Q[*ިU3d؝'n]*F@/lެW V,fl{C AޅiJIM!eagqP+wZR'5iCNxX̝3Qc[ԉD`F^c.j58@!8vL)BZ GYR2l=bϜTŦ  n3ᚳ`kF1#OuFy=pUgZdpmNX^s=׃?رc;v/?h>wy>>F.s,aa^` &?p!>ax֫B6n[V0xC VbWX ^?Y\{m$`pF?|]+?\o}ѣ(h?U\%h 'ȇc\yd Ÿae M>p3X ̚=NL&@[lqKaEs9w^+.%qjdbm28Zl{52Aĝ.m@+'<~8;~y&ٙJt$ 킽BG?Q}>]Y|ĉȉJMtw 뇫Y ~0d?2H >s%i_b>ᇀf%-y@/Ʉ4#VEB8$ ՚;du#dG)k`0m&^@_q1q:;_xA/^ZmPʸ:4:|€t=ʤVlt"K.96aegGL8k+X ﹰNx.j)Hmu!yN)PSϥ {ݯ+ۣ`^xž XgK_s JG/;8s&|]8F~mǹSo=]wfDdHo6r-DS-:V+H)`|TOT]f2x5 cꩠf~akA&p t(8U-жlQ5U`P?:eɿ*]p '|{aiAlL=W=eegr0rЉ2p] ߆"_̜Ó2>#i1`6 R}+gMBJ*wMؔ\:L?l:NoĞ{,V Ht W.&a(AulL( zץkWuٝS5pK6w1Q#F̚5K s)*m+7q)eT8ūC4ꯜ֦иņ^ӻVt޽[!粀lHBaBu@ol TYCׁM5*e@u[$z+n~B)~PeݧB>-3c:%Ew*"d]yD} 'N7+KT1lXELj{=f 5`񉊷 (|(54AuMǠ;64R{Oo Pͯ )ulŪxEx"~lM޸'F0L,[bUo5 51.sq-W_#?ar LAyJ\`56BeZ B,MϬzńly&Zwfj]oUK>X~=^(d=+y^6e5bs#nczС2f̘. M#>/f 9sko qUr}-!=V/521x ҋL(^ˆ(NYA]0bV7[%kmGvjr@ g;#g0P@w܀Xuu m}dtRe1^F;:d(a-Bj>=QʘeQڌb\ FF΁5txM`_yNjmAy $]uuwV* ΞG <].!CVb$ H- -\ℙlf.r"+w @.XDl+b7h".R[ND7mY pogs"04ulQ|Y+Z}0*o(eMx`k}w 6PT%w ;(?yݒUV~u3ޮ . fmCT-6zZ4ÌA#F(5v-k9(K3!qAx+Y[ qgjG1o0+se6pY^aLg vA$^ԯiN_cC#0`Ӧ\d;;#!AP\TB>2V,ZlUm&Wj qH8{kփ? `xH$SC(V2z,g~7MG~NM:)ja=f.n-q {?Xa. AbAcSLv\NabyZLaE}$ 2 F:h5K2'CԼAJLm[E[[YŭgF9y>`VdF;R8,A`|vr<1Q)L.t.d̫U&y+P۞Ė+&0p(ia->P!iI^ vI8_tRL'-RRi20@10 hM 01=7\ucmTW * g&|jG&wPK1mU;ljb3T,P)}2^`"(W?^?\19SV,0 3pSs V*͑=@*4UT^m/CU'Z(܀CPި fZ<0.|\Z;N}Mm?{VC\6bIw;5o87xEG;W4 `c|]xϟAh/xteuĘf o)X[) x#8so8CKbq +2,v3u̕W`\iu|+_'Ecac~cP%oRψ@;ʳH<%W#>}kѷ~Tw)a tG'8~pئ?T*T.ٹl&e@5q 7nYoR1q;017>x0W[N0u9A:0T,M @E `;vl8Ù@hh.C@0 Fiu֥o؝,-#0 9[= "+ġ Z.WJ0€UҙhΰyEv-t(D1hW6A8j+KCV$DĘ 9֓Ōs0PSGȔGv9]@3Ui/"-Q DI6tn ^](4VX[csb隳F˚I6EJY/.'ba4gκ~ t]۰Ԛ[IsV@K7'SSШdn3!,K$+EL$A-vP51K;~%_/SwG՜e1lkivhd:.&@%X xؤ@pt(gaHD}~X$J~t38~RϜP@WFYh(:GZ_0svpwy*~nWtaR~5k'\GEmǯi|يa+]π^`="`fzu4W>tת}/Zi._PG0#',G!75e#hqGcT47_:nՋ+11c&b D3RөV:HȾwd pj YDysʢQ;w1cfo>&}A~HX\x1wϞV4>kTo9 \O3U>HJg<_ydC^|kor}cg|pL6s.@*pbc{wlᑰM9Lx[D> ?7fpZԧ" EU''ʲeu^VP|هg(ȁqo,XI N+5} ]^>ꫯG?,֧Ow;v:l&/R}WFu 9n+Jx g Ҭ#l5ZG&!MD5Wx_K+c-!^E_HcQg@WnjfҹA<}:ᣗXyRFZ$l䲽SI{`MH\bų - W}6Nue%L4+f R Q⠱v JM,YMG~ WFu/|Ck -SOKGt27|zs:l^';:=|lxX׌)RM>@ fZD+ 44[ٯGC"3f,\_M7DH-Z$6|6#li]G+ H焴+|lCI?|СU ƓihQ#XGsIAG`;ZӦ)z3tYlu11ఝ%FJ^4|UsyՑ3'ʼnY{~Nt{-l eb XS0GiJ}=~ 2n6NP-1W!llE=d@S?l`9;޳ڎb=`!g{Nʛ j Ӵ#4uÆ3+4[m1qn궲;Ћ7gUGbU}]j\1P`htqϼwO {ȑ'NM,Y F>o9@[9:{; W p"k[;)Ts1Pe:iJ5Nj"ʐMtJnqslRSaYٙU%9ϊ{.uew]Հ@TchNx9)ٳgsi,|K.y{˔[s.~kk+ yK/2d/~"w:gWWdTH6ًZh^-ypC)@= KOBŸU$QV1S+54r D^N 窢mr}\=yHN+&s! |JU ur3)Ɗ̈?YWG Uv ~IR{ӛ$֯y ɰW/6p;z9ӑWާvzrNΖ{N9uvEakfh f6 &ttU~[#`URx?2Q \v#\Պf6c\ls}>$a:SrSMcXb0tt%`UE0N `(+s:l02D:Z`bE(u8,dUb%š,ZUֳsI(s;ێSzZ椧-tn˺z࡬-lGęR _>,8lZhUկ~W_frLɫco<ٕnOv|{dtY7tWD_ |-]nՔkp, ~pum fl[o:[E"e5,ooBCGU^f|薭R/Xcc@]r;kG&K\;ⰱCkqû [(iՕcc#1p.:r9\ .Euľ/,GL(`A%9%*Gup)rwuE ֓t#AA+r=q_ZL4 ZJ>0o\~%5߯x+>Oc|jHT< &VDIΚ)12]I9'5)k8K^?lJ~XX bvo7Bs`BnO/0fҠ&F kԤGt WrhrW[HHfo)IYįBZZD送ĒNwLwrvqjnCz*0CA;ٶxG.A\QJOč&A˕uR4]RU;du8;=F ̦e]sիW@kX͋X6&J"ڧsǏWD\IPb.,HTsIBYhA~_R+98-4qa$P&j-vqX  ` Y;qwQC0X5kXҷJ0Aw4OdОp؜:\GhM^.bڌĥvZ ڃXHKQZQEwo5Q:o N$IWUKQPθĒs,7+aW4<%ær Ї,.K -o5jKהt3+6mZq_GVYa6TϜ冷ߓ+,`fÈQyVDӁklcie WX49\ `OyXlCf #G&j k;`61j-/4¾N@n"0wdz9=#e!+RddXo!ߺ%CrTjQQs29NN]&cJ8zZ*Ahá7i5DhM5,y,@k4$zJeB3U *+b+VFk)Y闪3`W 4! wO>+o~4 *=q5DԤek<;cw TH@+,SYZ@d fȫaFGWGfc &q8V@/+mL96@,cs7U6B+6'b;"w7dc۱U )*.:Ay9rgAND/*a3g X;tʄjhgff3'<ֲ$!uc628K̩KS4ZR+ -`ԩؙIgݥONdb=VQV0MC\l81oOh\+L_|Sl@*UV`^7~׺YTM+DL~lL͏޾~t̚h.2 -| >*$#vf 2̓mr[#fU9}ؼy3 {vͿ6| XXؐq✂|QE( .v>p[eTcPBe"ksю+jH',TToOu򐤚B9Tcya &uvvm=Lm6PnmM~b&p<n@LI`y=)VV6#o3a` fy2g+\WOTPwԝD=,FeqJfp Wm/Ė5L7pUr "/r=>|ѽĹ&j?j>Xh@TH+ οM}k))`/D&`QrN*S-5Q^"9MKGq6'I%fVu n"*V!ۀjr'<@$H#7B`0HϚE4._'`g {"#XDfL>\k]?a&܆ F$ NhQb5g4;eiGgKd($W |We޺ dew*+cеnu5r'qb a:npưgYsop\4_O~\q*f֛pb ڥH}MT*Clfkֿ(XѠ ͉̀]wu/zы~7>\{?O`;vuؤg)Ƅ?ßɟ, gƽxUbu@Rynԓ@/7 vhTsJ=@ҥ΋l+QM|h0Mת}WE"^RRWE}b3E=jØF~ˋl2Tu2sS@jq#r 7:mlDNiǁ3;];cm [ZX t:)~WL+=g408eE CRB6&&ad¬/Hba܉jl >5q'{/ÒK2E$TPRx'Sk/!:>jUU ry&z}jbXEf8&#eaˌ @^lЕȱ,`b8{.TgI%T~afm)S[`k"#ceޒM ڬ\_)G:QρY [%: ,nH3Ko0䇗bA][{q5T`ƀV" *weGכ۱7p73 TMl5Tn]}o\_N]']z[/~(>_z@SlET@J!WUT(Ʒ|ƲơV~vǶ3,fM)o  C2 .~ƆbE!,LlGcXUKq 0fX0Wk w$F -QQ)\u) DH8Yumu$?p]lgrYHNIo V( :jG։,>=]kejL\):X؝+I:!GNLYSz4ŋk4.l\`h}Ç?~rtK4=l0&ƌ([W-]Lr[1L[p{T,hˮ6pˆ V,C}A>l ?#,t%e/z`@F!,_E@!HV2-Q@YYej=r3%3*X19cPz.\|,,qH=H~4h&Sff|Bjۥ!,[^bfm3ں{4VW8ws`"SK+Q{]} 72j=t*ТQKLu,uB?Ͷ<6i<ՠ5i^YHqjok9`$',b p G.bg)jeBzD_Aʂ].2|z_plL!Vp6XN$߆sTV*P$^Ioq%a*߲]Y S`;vlR~/MN1w*5reb?(%kE·TLClH1 j]akʋ% 8g8AP%o3?aG癕U J{ 6-NL;W08N]_bJ) } M`f& s:b$Է렟@ǚI@0U ?GMt.R:5jt/6i`"90}/5Y))Es!0fb]{/vRp+?S߉5I+3t؞kYF~Y #m6g9V&b5(KJڵk)ͺ`aV j@QpJf#|bo:. ͼuUhL+G9njLpsÈ`D:~+6Z/Yt_m^E"&Aqgvt iCJ %=Yb뼁lHnuK5}-X.eS-#nGo? l"e}~|8l_X;kW\Lp2Xk,8 O} -?gރ^ sHf da[`*vժyT|ZZ [+'V$ T]3fd^Y|رo{?1YYf=Y7Q\P΃8mHc7x#KNOLCJr2TyBB'%$F22{03?Y.X,YM%:u?o&% YϩS_%?W ŷE_雘϶H)%OLO7eG^;gflr'i 1:t)CۡT$J3%m& G@o'\cS͚H6DL'xv1 gp&& $3 f8_~xG O"GL0&VrqneZUԢj) J-0,G)SbҮcǺurR o?>ZTslQҶQ $T߈&_?~Μ9DUq[GƚRpDYO X Hps1Mk#F:@ =\^E͹MFN,3յ̜M8]\:d+,rk']5}&crPI;tK}{_~|OO?O9`3Q2a8}[0v[Q[L&/QPO ǐ]Zt'Z& ǀ#y=#[A0j1>akLm: ̛O"64 4y\-HuuD~ΑζngV#,SE7n'_-&^@$39[n% 1Ee rtUKMqD&83IljvybYJFZÑde`+IP ^N\֝(LMvK/miiJ~W^NlHއ7!U- ˏ+ƸH4Vt%BL,HҶmoPtE*#1%V܄e 1+ued`Ll̚-BL|p]5٬#c-ז#hAm5\wۆ ae nPgIaLG9`0Q=Holoוwod\(WZ޾scM@/ӷH mF|-cSL*_@qomԟ y+=_ f_r%W]uկF|lP]da[UHŁa-W[L c3,aO#Z+ rb5<n#ԼIH0ЉLo>:+f,֦H}fhgKp/cƲSv eNQ>ps8I(èU9CBrw0_o٨ e$> 69c{9}EF?)Rʂ/ + lد2L۰UDžcH;8_|,lʸ ʼ_ИxvmaPqL֬Z5riAn]R8+^jrVX 궩" -ϑp*xÍ1xbh!D0fqEeEu"/'GUTW\SIusY=̨Efr"Zٯ;`a/%/ &tk}Pn=CsRol-|l+NF,ׂ0<~۲c>lDI>Ģ @\U]kmj5'Oھ+b{K\sCL2qk6չݷ8/?oǧNLO]5  8̶FmݣG;؃皔w6˚7 D\՗K*dquq#| 7v)xdrJQ˔95#t#`s򲡜4ӝުNErԮ]8 3d YEbiV]#G X>Ѳ٠`[/je&d~g<. 䆌eL |ge'BR`,د03;7N%.q H6:3 N8w7I{p%֢ȴ=u4A툑(LR +?gy\c5vr5TY#86WHظo[YL9$fUÝ;v&a9O/!&'Rs 7+x3O-2ɉUa2PJWB`9s8xi mzq 6h+n5HQɱc3,g vwI昕+03b׶qT<3:8sHǥ@M5eclSl* Z⃕Sz?E,ML:]y 8: ?)]RVIde,60IYΤi'#q9󠿄)|gs߉f}C:~^v?{b`v!X( `%O3wU߾1R̒ ض*G+@:MV$K lRN5llM\U8@bt%2zq03[Q×ॲer401Լ>Z|9"v5ϥ4|խ<`@73`<<3StwY6فYITUޘ?%ns[6Y] a`2UБRe$c.#ܛGB%e ZP>eʨ;?E)ߞRo\88c S:%p:tΆuP~ig7mP[soG=Ll|l\.,oSsgyGn.|y#gY)z:UGj1Q< c@a"h߻Whleljܹe3W3v}'OH$XNZo0r/A>fz#fQK_g*H=pMVo6XZ" Z<gz;D*:+2+>AmJ6XRj&o6=+eH܉0AR@ՃReǶm)`y" +.>(ϩ#~LױH@0x'>ypP` HƠdPv^~qLUs_=NWDJDV+q ޷DTls5:n"Cbˤ[U뚄.ZOP9ƚvSRaȑ^SFol u,,KW. es1}p"`T+HZ16V"$@$v# =){نzӱAbLV2;v>$-oyKSމ"*t\{e(ܻN"PA)~-،pH)wǎO TYzClYL⟉TMFS@$2 ;1!Td8Zt%M :ʦue]3t(0Lm|-}ˍ ʮl$42Bp_0vբJLO b~<*)uɓ14q3c5sJ 4lYUf4؆G#;`$nvڣFP!"0vfd"s8oD-;Ɖ)vP=fvy$doӇ.h;3hScV1`c>gΜ+KQ _Pö~Iu\jGγ*xhԡ򈽛zq"ϳgu"9Thw@x .lNlw ^gqUiȊW ߞ>#~.PL@$ @{&f"~͡LvFa`A3Vu5hy V\tKX# ϊ;;X>5a(f`XcŠŹo %W`f ؆ (E!YYɫ5d]ssku4g q@k)EQU<-rSwpjYsL2Ó[7Cr583|tYݼx;D8/˪ݺ~i6kb5_img]b<$Yւ ;Of̘Wbʔ) ᕯ|NԜ*śŤh]?ʫzag8kD2F(^tL[0u36#U&ܛ"A *䓋L *lKv˭% C-VsxeN؛6݇tnY:M:"g%ɀd ׁ.뮾Fk  W\QΛ<*vzSP5-6.JR'B݇psd*ReY9ϩ/La$O^p,  )]tYw"W2J5s {A+s &vrp+Iʁ:?տ&>eU^):I(G䍑;z%;iҳ`aSҰI#rOfo6c(Y?]9/O IY2ؔPp0Ho;`(!hlWr'@n8YD}&<vs^~ Sq]"XR8a.0z*o?awgߡQ[k0̓A}k$dMDquۄ|ӓ%ߖ=7wq`A_v#z뇗-c\~ O/Lnҿ1T"~Q(~NXz6` $SJiqF\q]_ݣ?MOY-PDMaךּpu,`_p@HBqI>яMo`ChJr,1:?iHփŴ"w['OvSZgƦܑ:~3tCK*Y`*d1]cf\MvHE" n>5պ`@귭@-X ζ14tG:fLGcSG*:ԏȄ#73= qHu 6ov5e]lS\>'(THPFmCat#e]pdY.@ӉdWo29Xn+JtJ~%M< In+Ly/a >w^`L>+p~_>_̫}/~RTF n0GRN/r>}DZf~T 0.q[jS Y%`6]AJ 3)3 J*CrZJ|D︓jɵfq d#Uk|.\jۥ\6͢" f1VJw_|,f˂]y߾h00Z"ZEcRDb.(Y.w|vL3ff=Vz-ߍ 1W]0ЪlX%~'*GJuW:!c&=I,.dҤI#3E͋g۾+I~SQ[|O-6E30:{βc:{[JJ50|xѢo}<RWNتU\Oܩ^V>l a,WF[~8QgQviJ)fn܊#H 7c[K+ݩ!'cfsFkRfw`HȒX'̥6YZ2:CX fH0߅eISs<^n $xybb)@BC@2 ꓍9*=؀^Vx^ E!w[Z,.U=!~I8p˜7(|w+H)Wضod<1ح xܩftBu{őwy1|uի?)j`W]魵צRNPu9hmNZLCjwGۮ x|~&ցK ]iV m' m Y9o̼r-J]vqrxG0 0 f讦 J. d=`3l6W,h-뺄0ڍO3,868~lEb5c@AKNؒ%>%=\(qsNVMa _ұK7?qL_(A̭sh|+DWV"fsA,{}3CIg̎7"-Cyp7KD6>Ye]\ Ԗg?+R+atMYnlrra% s%H\32ǬL@[LP @%Y|gdMLo9M'ya &a 6DL>}wq$voٻfseJNHdϣ+;qddwqǩ= +[ Dc 9jRY8EY<97XB97BpZ&'-ĥ8)s`]1lO Tq|HV9f7n\v_*!:# t,\Uk!-.W}y,MHqe:@oN짟~44ʺw?+2J$ڊ[G_Y;vN $}C 2"̳LOzCoNtl1pH؀=fvRٚ߹L'֍28 > qkJ9rEg .$͑0K a5*U#<{CKp#rZt>Ϝ Hv<}JyP6ݦ#Gʾ{Y4?M"El* JS+&ѷĺ$޳? [`;1Ls4@E,RjةF V-k~[ O8BW *RdKѵlcPY[E)Y|841!1.R,{o~W|ÑlGub:skOiHÿl9~Pw~y+렺++"+Q)_wp)')f!s\6X@:σZݔf˦ص :ԴSCn8ןOa,Y{y}MH0^ عϼŜuʅ&g|\0B)f:5bLCSD/;O8hLu]E\<2ެybX8 ܢЈ-3]MԃAHZR6T^Uh3\ YW!DVp5\i,Ywl `@Nc] 0V\W BݽQ.w+ovL(EywGCwNamrQs?Ȗ`gĢAPQvrz CD!ur'ML14WP) Zf35ȷL_*%S=sj:S=zL4Hr5-cf"vQL2v=">pa${ۯE6>tH]ng_!"1lP)#03WsR<L:gXo%FT[_?֥V0/h@dM OF 1[k323{&a/>?Kʭk.R\s(KC<-Fh#! hY$ZOu`;vtC%(M+9NWL'+Wv>3`T\AiB/1%H ~bfdMa_q[&[M9ҚDVgwUN@,S6[3RԓU+Z9_Q6 9C^M\\:%P G;?TPDX+W9Y4e#jaOJՂWK! T?,PnŘ$U,ȥq (Vnj屄2va 9v5i~b$Ʉ|P-|o*&<ꥴ$s\Pzp7(d?*ͷMcueI@rjbY|&uHid"dž,i'| &MY.@e2ΘۘZȑd\-c;k409Gً&nq>v-IX(( "23fҖӄjɮ- } Z1_IA=gݲjx$m>Rc T|񧐧9\Bnl%ƨKzĎʚx=祒}Q݇%JN[۱ %B{wϰ)g2!5p)n',+~*oe; dj8O8`c1z'`lsXx"<4asulV֧_ tC%̼gKeʱ,E߂ČC@eUãT2mۢ,fL8NyذSHv4 v;"A G#mMڗT }]Y<\cesкGgG[Җڢzm '3_Ml/6NN8|y~ilbzW/Oéq6uaK䂉Ũ } J 쮿DfFQٿt1چ ;~  T+!r4bԌxIo Qs+Fv4 c=e+9l`Յu|v6w-[i-sTܥVx5/GK9ֶ$wMЎ[mTF&>x$rB r@D]ږ+^Z9~62Z+JmVzUM΍sUPg14?I%8L^X";|[Ts[ܺdfGzVT4ɟto:Bx'JXYӕoNMep 3uƒv~=I΢Z-[frg양g[Y\%dW<OOȦ@_fLeIg.jjllk8Cy5Ë*Yic6*j)l|X>j;^ΔU۱>dω+g|?sp JCTPSG7VBe7HnN GD4<2[AA$fXs $ؠN#D)/E^£; h;܉2F465HBõv6܉`*! gc$sH$':NK` 8 Ai.\d"R.mT t/ocV)/v#fI+[dKK C61S9nɧE\SWjb)ƭ:pGtG#ݭ>Ҙ{%߯\ܢUMwZJ.)vݖ2EaKT~޼W4c.Rc<5Ŏ kXT]K`Nyz`TOd`3pad4ބAK sRS*#FA/޵Js%獓&3.u2KQ `9jȅ`2ÁF~NV>{|ZKAc`6e2p.숣8b d vFd# zicLVgR{V.bvL@Ԣ"glrӉIA"=c㤄Rc%.椸9=X'N0DjIpG7rc_V0+1YHu7+JDM#Hb>`;%[jW>JEַGJLs6]z:JxvJQogN<=kqw Dw)5"gRs1lhhy39V&MʙdrՔR'vbl@ U7#ߎt *hVnOɌkL4`mʑfm ȕ&hwD{hv9lǎ`aN뷭D*;JSB_$.Ly"1Ywç.ԮI=QIggX3v 6ĖE6kf燬"wgj|9ULti]樫sWbn_G{dP= g|=~8W\yh{&Li_A.v^ ˱5bС;b]8H^^t<LXsJyj d!5cPg.6)g^lj44 ;e#֠&"K,H( \$Es|̨luWUr}b9O(Ж@'I0Dk%ShS1|r5f%!~~ +3^ cDjwVq|ra%Kt<ߤRji=0W'&JHr>ҺÞO_E5HA74qV \mL3gZF̭Uc(os$XacZtHg.ƌV 5c8gI◘tJ^?at6ZNҧUUj*O:.o ?Cf|0[-PGoF1ܪ:lu26 xcSI fD*ѴClAiB)?Zy#>%W>6VW_ jbԡ93@*)R~Roz)o `]?/)W`EBV!8[v-"d\_ii $3r޳.v fYF'kZR{eK._!}tl_Ѿ)|66jNaCTqO٢aQ:kC =_lVQi/(7f,;-,K_pIbz^LLcQGvX]SsX1h?1"ϟFEsq> ͉ۋ}v,;9l6Ei|:|D{5wu*5ڬ`=(hhRلdC@mO_)wOj-Z`$h}soVelk~Rc-10'Ne12F-ܲjXx0]x BѝbeqqN1k 111HV]'i+ QIO8}{D.jR qָ/uvJAlD^UsCڹKl/a֭"Fh5^2|wʲܭ5Η+ǀuFD_ϡxv*w)#NW8QrRRd?qcѹp<8.]G"'ZlI|Fq_}SUYPiz`;v ,q~s=^6I"g0f{|d&5BlfR5GbVDBu }Ídqr+6UVYZ"Qnn˷._*5A㖗ߞq-gM1SհRo.ӿ2)*PͬaSqlơp$X8*90jf@:ɓ7&tc%|qbw:6#6P#w(2\H#RSJ*wQ@̲*K&~KHu,7APP5۱CY26K6s^??&E+;| K!-ꞃ&O2Ru#GnWuu 껠O5ij]6F ;u]،Pj2G,<6=Yu)V\5#^ji!K7ѓ(/T$Bhۤ߈ޫF_^ ѫi9GTBt.jf^e X Wtkw:)I'@g&$}a TXo&pmO=ViΘK cM>v%>MZe]LLq^? SƦQ3cZg l?|X4gL SwNXl_Z)85(;鄍yܲGiŖ3)ػMF6oN P,-E,v1,wM\ˍ7ZB=PW[H\/"-N^Pzy9A=9&9c!gLŐ2өk>^fykH,Ɂ 1סARV6F?'(+-B[З`Sj^dIŽk #Gl^'/pjbO X |nki]7|8C\5l9;8u?0pfl' J3(xUS.7ȭDtфoy5%5IwrKr,/h2D-#]125Hzʠ(mW(Dc3Ձ[Bh鹳4bYjUIRd0R2V)ya#cHoS㩀z/lq8*&˪,w$udD">p r%iC%Z2dn<7w}NfF, ⹤HS_`;vNd:XE:DX?38CLncg6TpB`!G* ry2t}h/!ga԰av/kqè7V&;H-w0馲גG:-W{ds&•.U\?>VT? 9ybkr5$۾٧]p;P4KKlS7dBKE;~ZJRT+<^Yes*ߒ,1\TeQ[T>rd OCA݌K$S]@ \AʉA >O]?q;'y?[?r2SBG.U〹w5L;bI5vڧ]5x!:bVD0BzReXc̺4*P=5 ȝ Ē]Zk+֪BTQLP8U^퓁3rE5c8eRWMШ@e5 fe΂JZܼPw?re<}F-1ư.(͍+ʴMG!r5ʮ泳`7 `34Al:jCXTeW͟Opy= |mqa3˺u bp6oUc+Vg9%ڏm(qjbCi6i)pr"GW-mEYxiӑ YE!J#ZĿ1bϞ(H|$Ig2O1ZMSb?Omm l H MۤuZrz UeՌ]`7WAVUyJ\!H52ǯA,{ $3+jAt yd|U 6dHې0@^$mܸjgٰ6D53&ef ߢ﫫ao^jPxGf1cT_E1&dMfa?Bxº_oX-}pxLA lc {3v H=q?͊ RRh,=˳^xpJYƜ<ȟr2A<#;k3vOT𘒷P7{h`y10d,*bLU5j7=f jDEFqrc'u0{k$8a@+'J1,kj- MLR-_fZQOQ0ZҖ[1:'YR/I9*YX+%֖ԞytAk"S x\8+jr#SfzykbzlsScYfkkA 8HCϸ|_':xN_:M)[Ɗ/b ^ÌqK4TT.^;v`;ĻO;e[>deGOnuvУF0j^ #_/̣@nL4piK-+Hr-z#ZPkJ.£iHʠ~o"}AfU[k*SI]bRI17dn*ǻԞ/&4ꒊ'QM bozj gʱx/o4"H19T[ h$7s<< :p L+@5̹YuԴJ2)it:o:k9Ht5Ę *41l懟:eƁ!lc=m#fnwr4 hp taC3s׊KF{10ʕ.'VˤkO\ѥ:o,6 2%+zDc>T[2!>ʒ'WK3R{1= wOFӳ:25Y3ESI]Wsˑ:k^#]o*+稔h\+֣%jTMZwAW{{ѩM;ye]Fv ̒C6 \{qLI7B.7ӢLL({  z`|~6r$aʨ%taCBĪ{-6!S((92IEcݭzr]\3cӷ]ILt[D,1N*jf먑jl!PgNDMMsmBc N\;șb Yv4'\g'"|#]BbEHRkѦ&91Cqӫ|'JɱO"IٲŅ7`Iz)'ؙ2{l.dޫ|\r{ޤe.~kkЇ.w;['Mm1c&fodoeRڅ1ZyC$Ɩbc&m6m@a&ViWN) 8&_M- WDHNhJB,-s^Jtjh9l%Ѐs~*nDv[MAAtD2>ӂ[@^e)7r,z r}XMZdg\*XAlb=xțs+4fZa2Ke`&c{x}h< 8]S&G>8/J?W?E]ԦGU*4uʍLjژڰ!/s/b')/-@9"fw`6$b:އ<fg{-é_Ųw'^7?9S?s1YG'ɚ!`$JkNǮXj('rUl\u~7p͹^ˣ 6$\*n"(2@3r LǕ Rl# Ն@).:bFt1Fդd ΓD7J=i.`VYO"v/~ Pgyc[ի^_^x׾Y(qٕ f/K̀ ܱ'V;Z9YgxMԆ=M68>v| RX&٬@\l%dAkjʤvC<Ӵ#lP-ͼ8OܺQ-wXS$~w0#6ȭ-,&v~l|:%Y|=L=nlضE+csqw_*QZicꢺm&jfbUq#|D\ŲؓJZgcgQ>N uyB4i f~nQ\~˖-+hH|B{|]^} _Ι cϘW;Dg&H:x{i P~5njn@8cLc3ƴR@j6pbS`dL;0c˓;j)*u{4 ~bǩɜ4 4RLdD 0!(sLg O 5R0 *|LX>'C 9s9_& hRQpqՃ,8%^j+=Il^闿屗$g͚;J`A׽H;׿$(;V%>gΜ^zv؜ ?BPʵzj"֧oy =,ZL8Ͱ/.$ho=#G͚Mr8Oo{>cw`w5?zrNBΊm p&ӊ곉J0nygr'yMچq $`Z 7zpՉu+,v;xB'Mjb czU鲌I6ɀ_vɽ1fpHX?D6.,&@;y'ud)ȹ60c2Edԩ'&ccwϕf6ļyG9Ҋڵ .(;Ok{ʕμnm3M6@5ɷK-=؋j.^fq,T_ew l35;n)L3@QFo)RmV]ջF:ʪ\O5yVQ,crd5ƃKO^-6&xjLbɆvޞXXs%GT7~ O?4ihuO>+7r7կ~_b>L2+DKg4۱bSg9C7c1$)G;}1afey2Ĕ䍫],L巬 u4Sph&H L~l1UesгJnXo10ҪYyEC5)\gӌ_%rM^\ y T$w2#IIB$r'tp6IR-Ԇ= jjIT]:U*Tm L]5"̿ج2O&2ZpTi4&/M3l*! ' ίJW`-/>ȭ3a|;wilycf@-SR&RhN}oU#p#i/fsD'ljsT#yU!)j\Ir\2Y_:xVahfDN+YBu7"¸NY׋cb9OTlvvO~_0 V'@)4kp=g A37 ) alɦF $f!nbq`| ʿ-&~=NoGAGŤ.faUZ?~ ޚ֒FS&ة}]5uPu:&ˣǬuk(eQv$b}&aHR{Ủ"M$[5]һmhtTrSQ vHm2 KkpŽ0jH{bpҕTw]S;v\^3]t$ò_fw_&Gl޺fM͛fD= 'gMrIaVc. Q,b?i[2QЌ, %u3XAl±OX bm5xrDկΥ5VPcc +˺aMTInfl?$\0+Ǜd֌CxL锥_M7Sk>HK-Kn!na끧@{;[},HXbizLpZڑu`;v즄|n{%f3v^芾RYI"v2}78 L.{[$Ky 6Kf>9g.[FOB?>{;f6%SVX+/ݜrJND~PA~FRciEkLŘTj$"?Hxy@xEvOޔR_:婞T@xU񣰦w]3B>U{+ޮ@Mt2ƀM)f<ҥ}rf_兩k,gF/X8 {?q]dvʻjBX u"Q-q H s0 H@k|{d,YE3HAJb~ {O#B)eYYm:WeEN,e4EHB몞OIU۝6XZh}x[moūmk#Eܮa_n;vx@%9F.NoR\۾Hl?ҼO̚}7ҠPY+?MwcKheuÌ8VaYd4:>S1ĉQЋ~jbyO$t>nܮCv k,5'Ffp"LC;Հup 15fnj)ɫɹon,a`ɓAJc2Q9cr)y|1Ss[YYk2xmjT*Z`U,5ac'V){.]N(vsvLG0w@51T) ±aoYmv NrN7NJW#MgeQ?6cӦ|#&3\V hƑ+V5n2 ˀgayjK3Td!0[wj[q>Q);,9DŽ󫊭PNE䥌,hcl|9SQ*iTŗ#Z- qK|҇}%;mu3y`aב%:N*?ۑo3Ly_J$>2mpјyzx8Qa3c،E~r,u?`606|>}7뉋K@ ج 6o2rO0-HsY,/o֎lЛ kOÑJZqRC+Ŭ^s|YROnoo%CEwO۠2%&nX$x#M#D304sOGTZMر[n}mjs W.L_Xytܘ3m]ǧNcMyX5c[A`>w.29sNPQסk8s|jac|Wln%6Ɯ1l'I+(bԝ7FoYjy0R]+`jvANe9y];v`]Ɏ|Z¾ @%[^2%/߰X;Gr$2r:cŔN iڌ8{S/ WH'o4y Zv9Ӂۊ58k&2U]LX`5m+l.gQn8>B|$"v5S?/[_)ύ]ػ{;U^jO/{-8+2\gR^Ҿ#Uy ;j] u %@25H#I69h%,#f1~KV}ǕϸX˕ɻ;vΞ,!\Ó&1p>CcJ&@M[xΏ-L@Is>jM ڵ1>u;WADPMAwN-W ͙ouX7NXsNbv+zyUo $="@N{^& ͕]'ઢv 9w-v6txTM ={N=oߝ;xpdb MK_Z=+)KoYa{ ]خ+ѣjIyؑii6`fu"al6!^e{&9:Ϡi^D_u(x}F>Wf;}Tm#?TZs)fgiBoY(OFޣ}>8ڲ*y;dS퓧=f)_s;SOGMc+:ӜI=vvk| W|we(ǥަ׾ rh?PzSJɓy :Oڥ4z`;a ?0w.\`S"s=#G2vK9V;qRTh"T?~(8l0FmvSl_Y IoJhy"L 9Ҿ;G9 p(4ȉ>9W_Y){|q:o ,q,qB$HQ`S&*8aM<Pʘ&ĞI;n %VEishiuW?[ >mi (秳~4F8 TSsbLQ!9JLYmU<}/vz^Eu됡;G`YatXɶֽSRO{ǎ`M5u.mپV0\$  ?z qֹ_^q֍Ŭ#~p#~6ψ]e ]tCJ};572SzTvji]eocVOP^r.24FA]},F;v6DA9b &f@ +:|4Ҹ?6Gc꯴# _|fq\7m>;''OQ-rg|wl-2b̊ʺk5lSP(hʸ>}F= Or rcU=$}`I:+$[*M'9T%:](g#Ҳ*vW> }Z gRcEPi2-{С׀1a橬J-.Fg7Ɗ@/ 1 7ˁa#&N?6rcu= Z.ǘ^C pN9ԛLxGEYSJWJ[Q;N}N[H3J&"JsߠF&mY93Uq =`3T^T1K+~waeğ[$eL 3=g֚︃fMZz=n}Y#&N@OY թ m$5{)5 /iq?)"8'IItLA.[mvG =`n)Հ!$>l nAʸ뮻As+5T5樛5:}V>n;cPf E{Mؕc샠g`' I@JT7<(]D\:kQ9"7IGO9ߖpe`YgY2r:e׬tfu+V7,}[Errʲ,WsTJ-bRUݥNoFx?mYku~}\nS٬n|}j/fE䓏ҲCSϴ|z`;v Hеk׮-0+ ɾVI"њ*mddu)|:֢gZI#u_u0|pzJW8uT\o8Ƀ3P[JJK6Ԓ `'[lV9I9{ea@ bv'o 2*ZZ]{y"H! ~j@kљO?et%QVd`Kkm ĭK}4RcoKR+NAUhCt|I7l0Q#o< SG;| ,l#W-.nU?$*`w ҕy n1m?q҉HfrA2+ũ We\sxN4gYM \;ҵ+.UONYHa@VզXFٞ툜G|¾\? /_9>%)JNxc^/;v솟R} +Gڄa!]lu\2r}?wD [R'Cirͭ}#^HY~4M$(5]9?o!H>"5kx^C &d9ϲ+R`7ϼi)dXU1 A/9vR\d-O&z+c3>͆>2YȑRl68ٙqȳW0aT9I2BR׊[w]H%BQcɠGC!guxM8f_ +՘bڒ#/ybvηUoi5٦Ә/1]Q̟- Z33:^]Qukӂ"Ulg]#a]|<1Q8/rٸiۈq9g YʉMd9 ;;ٜ:+jbׁɝ;VTܰ޽f!/ZDl_DFFjQWO 寙:kzRgϼsS]Q5dD/[h=:O=Z= 4R"”f[{SV̚+5^ZZK>eY/xq4i b~ncc/e˖Q-Z+oy[R5ˊz:+Hl}rJ.JfT6|,P< S F|fX9oJeO:Kceۯݸ"e=D蛓 E/` N{mXW/HU`#|pcI}饗 y{R ܞ`#[|T5k+n8۷ˌ^O>&pSc@uoxIVu+*Cal"J~酾,tRG5"qf/>v>fZd5v1մaD9FeXAEDA+zT狻79B#9^oذ .(3gN^"..4.rX*,Hg&}8Z^3k'7eZ@솦 Lıbe: cLagܚ4bnΨm\ٛZ1U+F}r=sxEu~aꓣcнK)昖;Sda.,&@  +VU`w(DWoo6qj^';'l7+*d>KͭBW2)#vk6RV+ Ozq<⩈W rMT/[n TkuwV;`infdzBOFY׻n+d(7ˑnjժ??_2v`if,@U]e[.4!X;M+M̏5#K:dy쬃1lBE~ʬYQER-W/m&x&~vzqVYZAr}bFK B$9gSbTV}:ZVgDa>` { ߳GB3+1sPEEnl,p`E6[OgE29IG+m߲~ lWˤu'*.yEbzSͲpu]( @̷Zd?O`D#Q˿.8$u ׼!"Xe2~mnlfg[*լ94myv rtm="{.#[V(x`U/%3~dIįRdb)h1۳Tđ #a/t#BDY DYΤZ=T=Y; v (daLu5$?@dƠCea;3먳o:Xe)KQhf3lUjT?>Q'[QFq\\e/S-HA0/m" G impg&υ_JplWBVJ~ߩ\x.ۈLٷoh/ޛHO41F}`3f7dG^EvDGGfE*0[fP)-VRiUE^gM)0 f-vvgQZ 劏YYANz{M`;v`Rkȏ`/3Fh~q~8)zsLOXW#o"pNZeeq,7J-4/~b]O\ͱY&h@` K (;?\=JG~#xby $6Nf;D"?+\r|ݩfu)^U>`GW?!Ȧ r301锘.UHes2ߓkJ!(.kGVWS.L: qFnSMLO%Om{ڑ"(4TI0HH#]xMkzUsҿ ?[~`׋'?N 1 NA~{|6G-rԩc"j5Ǯs)uRϥ&t['Ov#`l$]*TIat۵kV;O쬩Q\$! ^ uN̮ 0Xfѝ$H ArۀSzN6l8pds`E'N6l8p=d`#l8pN6l8p|C/~1k, 'N6l8p $H% $HA $HA $H )O~/wwpl_=/{Ə#<\rĈO~&>oۗ_~??+B7͹|˗/?3nKjӦM|;wp^oǏo]b dI_9r_z^{msdQ07B}_?4A ˽袋,׿>:9 GM4o^zW0nV'~̙3Za„ nsd27ׄu{w_K'_њLs/yK~ӟ6"---GKvlēEG?7>V' `Cz}즒Ç;H4~@g沗/~:9`wdMoonًP'+wäFؽ+l9߯f=xի^UokNV>_\;v؅^h;,uN= sUW:tu59Y3U?/]onEtrKlgy5y ̷<_|>y$ F=4ʝdyַN<U ''I&^CvMN֤wQ`76^놻e˖O]tWFu<Z'ϘbC Z\z>Y_pn&r[gL.~2glM_WLh*v7(t>c7n[zsg+@'KC|#w;`wW ٘ :tɒ%Y?y1?|nw}z?~~w3+S)OvwybGm֓տD?)vBDxE*9mARVgxN<RW &[~_ GO8Ҭ@>}7e o5"#ćo|XwV5gxNoG?Q=OP+3fpS+V̜9Sq.QE@tVπ]_9/7Ϳu{8~ p8;#yT`Of׶mۨ'm߾Ok_%ڻwoslCvMNVmѳ5G $H A $H A$H A$H A$H A` $H A` $H A` ҘBi쭷ޚagc۷/M v˔SS  ҝS>qC6]!d#A`IۗA Kӧ[%sc`NH#۷RF_}^O}*PxlkI.%\96̾b74)-{VOWA%`ݿ^" MEl8}@oy[+[2. _җX=tlR _h^xƍ'[ZZA:8_.8񜇧+H At`/[, _}մ4p>ۿvlz  KXNb8nڴ)sYMn'$v Aj KLscāOt儙w'6mZ%nk?~Kkw o馛QlkSr/7mMBAgܹq ;H $.&(,\Kf?3ΰ3b%uLޤF zy~rL `s,ﰘO>W^9*F0bזt(1yqSկ~2v8#ŝ? .w؁{ڵ/_tF&Zt)_=d| ;H #FKXɲ.^^]tI ÇKzO|JQܗ pVҧce]wy'(ȷD[##կ~ujYW*`#XDUX`06qr@,ˁ[a#¤I.Owt $HZvu!UH2`_wu ;HP~'P{$v W A?⠆ //3 ;HA^I6\ A` $H A` $H  $H  $H $HA $HA $H A $H A $H A {l A $o A $ $H Aԙ7 $H u&) A $HI `5H A RgA $Hz~2H A Rg"~ ؏ $H AL~,H A RgZ3q?$H A'Yf ha?$H AYIJ A $H}ȊHþ7H A RrG$>$H An=>$H AVY9%}8H A }rk$)10#0bYqFaF2ǘ S? A $H7 ﲟ=?J'LIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/profile_icon.png0000644000175000017500000000254113502206677024215 0ustar noahfxnoahfxPNG  IHDRcsRGB pHYs.#.#x?viTXtXML:com.adobe.xmp Adobe ImageReady 1 ).=/IDATH UNAav%(oEL/O&Exл_>//&Wc3 ,nP+kN@h@l'򊹑LAjHg"++cYC@;ּlH*ņ2M*'п{96AK A&5FUg@MDJk #G;0_CH zwRwa:"MWRp )Dάx,o2b{OK8Ɓ [ixookFQEzB E\ˋloMr%N+"g|eNl@Y{4}>(͖X;.N@a<8H#bϑb;Y=nA'ќPaV]ssa$5ںִL[49Z(,}̥ӣ:NbeE*{:~^0?]*ųdZz2U1ScoSB-9/ѵj^j3IBbJA[N.-\6r•gvй,t .4-@`x@5n;;*g>#I6W.:sgL =P~}KwlBMn$_hX?A?jẍ́߾&0 iGv;O ҕ:KIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/arithmetic_button.png0000644000175000017500000001532613502206677025276 0ustar noahfxnoahfxPNG  IHDR0<n,qk iCCPICC ProfileHTSޛ^hH RH%bCdqlHS]i  ,,oEAY 6Ta;/|d@- 2D! 6fsB@j>EQ2W)rybP0q\1'hE 9]/+C8PV 6 S=qS, D= -J21#ơ0Pp'u$(oCyNjjB8/q3NNZ&E⋅)voHC%Q cA is''9Q>g4s^) 9Ò`M(-D'fhf.Irl^K3;1,r3 Y0)$!E>5.>K柑'[#{&78J- e> Y|aJ̟+3Cec3636X?Ili^XKgVdL,3MROH`7` 8s֖VL&>y [#5cj>zв#eN0/, y Ԁz9pnh h p@"H"V m`(U`?Q Nsn; ` /Ÿ?m5Cr %M Qh@t$ĕĭvuqH2"9HIRR"1L%;r)2D1xRP$-ZJJ5Qc-zyS'9K+NBEky2lc l  ')*S(* JyJ+͓ơm] (㔍YIʅʇ{GTTDPP9"#tC:BJ?JK2Ks,ެMfݜQu*O@Y5ZvV'uSEY{/V43`5` SU55F54}5e5ZnZIZ;h iӴ];jd0)RƈDZGgLH7\7WYISSoD_[jFDAn.F [ TXFFFƮ5ƷMp&Ld=&7LaS[D flY995sS3,ύ}n),Y)Y[Z[6XWX߶جiy3loymi l7v~5 Wc*3EXu>;9f8u)٩ip|uRK>+۵ w$C=,=D'<>z:zB| zý˽$4mdqX{5(ME  v,x``akbzl"ܢEX .m5Qq$3B>bID}HHiܨ5Qעգm11Zww|+'2^m}W_O\vF{37]oumkw~%gs_.@s/O x+᫱?|mnvD [N]y;GG~H0ڧ]_"Z[SDžl{@j4O˓jIjIѾ &DY`Y6S(h4>N|;Dc{ǿZtOR8>5??u9_z V>`iTXtXML:com.adobe.xmp 304 60 x Z0IDATxOwՇ5jGTV+,@@thH".`B¥CsB.Bą`.6`8ぱ/f6L $u==kk5F)l'h^>8,꜁E]iA&HAT}HaYl3w?r^ vlsQp:Oꝫg4v?:% kE 4$}vmF(9 )~=9:u?l3IJJ/G9 4)Ham :i(@N3ԽB| 웣bfvRv7՞2 a~RR瞂Ցh_KKn/tϐeف,a5_zN=hj~EsnC>Y]=/.Ξup,+ċ R 4$y`ZOe5ǦN5gn3QsVᅁi wAC?~x~f[8'Ѕ/ q#3A,!3EAb2 H@ H@ H@ H@!3#3 Ab 1A9y y[ |w<̂țAHL)ݻw?#B'|r}L.@7Yfvw!</Gf2pdO&`,zv |PXt 5M^Tk~;@7"/ ;=\42*?|r^^^4&ҤrkNz݌k YH0TڥU?55E'{HR%w +)iKYJ5S KZ3X=yʗܯOZɓ?1eoo~{Ec,Zvg|nE5_'Ƭ="'?wd}XJH+: aSc8.%}lJZпwJhoO.p!3hr9-QK[J†LJ짹&u?%K)7q99xޘo߆KOO/*n}}}aaZ\ViXr` ”,yZ?x I1~cr~-y%$1cCt/2HJZA%dr~Zp,ݐ.!)*COg䄥uh)9|;<,]eK61@;_S6mvbIbyRAEBQRm6WCӆ?#Zj)xU%vR7/kQ7cU}F .)XCkzU"|B2z!]uFjԷZ6B*UQ|jyϚo@MՔnmd}Tʮ1QNwM = h22º, Fdu Ie*mwHZ+ j>FH5Zc BTd Z QXF Y_?LyژZa76ؙ&BEx tRmd؀ob+'$Wc!3-nj` [NVT-I u9]*fLQsZ7P5p :,v)$.K4iٵD+2LoR.+Rq)#հOoDF266oQ1RUePONXX- jk巄it7Ŋ%~J ׻mnn<~P U.,>w{ LvH,"U;T===ǎG|A?Dɖ^^`Õ=}:Hpא4Bsxsy /$N΀ߛrj'$4/XL0 pQAnu,,KqT@''`:y/sN.ԻYwV. .\jNQ˴b!N]tn$*K_I/;w  HJ~ SFY\[`ezp/@ם nh`&ㅯ,mՖmZe}cږ`RkcY[\km7 a<@;NDd4vuu=qx,>GW ;hw0JZWȲSOw`,&);sEùGݟYkRS\C-'qU-blnKSϝv$PD"X2Ds=; y/ʛN58$T,ƠTsz9_X׻N$qYR?t.'`||6(e RR91rMB6wZLע}ZVHI+_pu~jzNc8z銂js,g(x6`N\wML ējoOmѸK+Ts0JHfVgKO~J5\AB`gQAVʊo!'#÷6M <|&J,^! sBgAnAVjC02 TG,j6J1RE^?fnuc76Ɉsn7bIⲤ~u}ui,;[ھ'EX~j8[T蒔Vf:`"ޮeѥ(mHɌ5!rHιe5GknRP=k<i}j $)-'$IxZvttJ3act鑺Ty]^Nyդv7N]%F?uV+Qz7}zK9ElUElzlw7}]xBZӔ<_yɔ>|.b9b1%v0Sz'lbIⲤ~?Unp'Tx⼔lZ|d-7*/+i~bC² {bP`[T(]a'=P|3L T2Ě?} GC=zK/l 92R [>_S^"wgےi6`gq)\[W_$zS_=VS 046ޣy{+Vj͊O_'=d͚O>'pxJE;pwu*wS :7](^J^>uRmNã>mǟ735= /X虓د+.]?}tis٧hH㶽t3Ӣ3Px"M@i:Rr9 չ0 hoW=~"I'g̗< m7m _uqvѸ'm; 7pq+(PwW/tJW)ϝ`z*Hu3ڦ―u[>׎hтs,`GO=,Z4wn΀V zΈ>-.Xvΐ'sWN} /_0}oΈGqТ}1ן{/S|"6F<ŭ]ވGOe  v)hY+o6U?q@s͟rYR;m3#U?qeT4>;_F;#:ncO/;ι[[֓͟㜥yb 149luG'UzkV}\A؍^l굛[iMWn|KJ/,}n^i94@jw'֧n~ctL+oϿ'WUxtн :o8FTXMjީO=vgWiL>|o((3]pwMߔަ)+65d?/VSS~w eMssZ÷=ftn~7hxu;]`{Z(PjR:0a :.-EVWѽz?͟ϟws'k֬w)MVln:I@ćZ׷kN~~~olm{ƍ⺏iWWy1m> =}׸e,9lJwߺ^:& OY={%g<leKǟd#?(PZJO)W^w5 'e;e-S:^͟7jڕm]+z{7lrA_yWºz;~uآП8S<~utgm:4'ch{'̻jͺ/0wɺy+>\Ýi͚C_Yt6 n-<駽߾;襺pm͘zj{_e˖%͕[iƻF/qi+}wJvK &lW9jt^^^]5l)'<’맞s+}+bVɥ='&ڑOw7~]@R:][}:Ey_K7>opwrOn}&j}}?L k뙮9n=aǔ>wI-:}nAod5PS^fٯǰqkkFL6svN-dҙ׎:?M.nա؜p/*ӖǾvS W=EjΌ ¢ic|=θ=tq}M9uARaiwJ^j80<䯧~k^أh񻵺 ywu;x٬grjɏ&nܷ>:o`-iS{{P6P@)>e8瑮7t;5g±S˹t/ųx?g:so۟| *5I3GB%˟<{o͙6e ^}Nvj~Q;աiٳ w;ZVٓ%+׶~kAg?=I+l>}{I?ȿ||ʨaKZ^ֹOoIi_XzuFuVozx6-XiҲҷJG{s{τf|jy;|ݷFkdFkxW uժ՗_=`ڊM=[OwXX:tп9_mP&=F>Ѿ-=;mGɩ =Ӟ3uy5޹mbӈťo/}"]G2N]&Bz&=}Ҏݻ p<[5o[}k{q5wM@RP:}?~ 5kW|=UԷ|3w~@RP:]zyZ$^sz3oN9w9c'e=/us}Nnݯoz'5kJ>b;K챟s3bXK[~G̚5۫z(/NB ST۩O<ѭqiZKߍp:e;c^lqob7\7mW?ׂrjpW|XϢҗv(,m_z;Zzgq9;nxCV?dƦmk=j,.Ȭ;k7M^2_M>߿iw: ؙ' wo⟏v]?_yķZ(PwWU@&m^xſ<.zϿu[羷tzƧ{СuOuo\hyU@ӆtG}ٴs~c;agժmS\\j{ӧN}땗?gi0}9ho2z]dySӳg%m3M.}.\o_Iis|2',{wG-\hWp/~՚+ް/2n1p%5ޙ\iȢW\ '{]8ӊoR4f 61͞wW3W/(7Ɯ˲K/^t}}d6/_yvw.P/>5Вyqϙ+Jp7fzu^i5|)tyˎG\kٲ {'Po ^˧|꽝O﹓[<{ݝNqu-uӓݑ׶=M9 w߫kto;Ͻry:]x~LdѢ9ƿ~]Ow6k%3{Ĭ/z^)wߟd؅K_RﯷUG Y yH{.-)|VɩKR=9zO8~3Ϝ6Poy'/W 轨ҧpSz{nճJosߓ=N [˷Ws]^Jn>b/|s{6NumtO>'sOyvї/~e6.j^:%TOo;>񶧎\kYӇA_K )-((eoa'jcxlNW j_xXﶏ/,9}#nΫ>x_/[*;=I @BUӾ砸S=䰁O-Z=oeV}|zOG}3u;ϽWD͚.:ǺʾkǷe n@Q:]Q}5~9 `6g<3ttc.xqf~+fXHq{QwqCvϗrݝL `x(lr! >c^;)$kb -fgK,)vu|qK=iL.Mlwky Woަ+66uԝw_?qϖm}+޷}W.iz?_rpu/xɒW٠/oBSN,p_==~/wɫG.tXί9^%KSңzyOyqL<77/(P2J˒pwY 5OzO4;ϗO1/=K?߇COOO@W> +y99yi[^ۺ˖9}.dupw؄I6;=5~qw>3~Ҕ&OMھ|3oL25Uki0dqm:,o9w/hڹm+zV/:7?֯wk0S C^Gċ8% [0z^rn;FfݩoMw=)slGM/=}J' r[K+wH,M<8vnCnj>,=ZUӱ%?];n N[Ʊ΍w_YtLA> ScVμБ9gSɾ{5}? JVZ~ޡg=kY n p{-O"׊;k~S{?z~+ox?^Kb vnrʖyϻ,9tog=<`C>-/]ݱs`T'O+S%M_*!쀡N͆S.Z¦N~oEtov?geZIK~UrؒxUoT$?;+]QK]*7Ҷ}IWhyyvf5I%Pr)ȝ@eOiDד/{~N~.v[}??\sڏCgi3.wڿz`][a'<܃8%i@Q:]R}5}]${𡿿hcΆ_zm+= >yÝmqwI}Rw[-zæ}Q?jlN0pun>|vTRdY3^];Oit9~G>atgG]tWOKNc `T'M\jῺ=󥹿y~޹m6ks{z}ӋL_J6:IXw1% w[K%5wզn|%J" :n?N)9nb/JG|gD<ֲ'XOt{d7=1Mzt|!kEuɼ3g~ޜ[A7]`|_n w `%$P@(.>w~Z,> 6\#}~/uйK3|sR?~Kn-9}ח훎wV[#dl9q7|ۆ?a QPYW=3 _p_-ߨG_jϹhֲʓy5xfL˜Ź Eܚ*njҳaR#Pĵ7 nv૮|u/nпA7t5z͠ :٠tՃ|ՠӮttMwE<ײ'#F{2bYw9uxna> w{r Z(PwU]a?;7y<̋]$';Wo큧\b{c'pUpw9~UXv;g} woz5aS2[h+>c&dxz=əz/>?>LF=yz~~9˚OZS^V95XjgeMX iۃ(?7idjwErJ/.=8ռ?Un-?xΜyDT5"ϔ|[o A:o|{/Y , (P JE5OE)%qE>ܫ]ݑt|vOw{}|4zMrӦMٙʢE ?/7MliE#Oxw wa3!'qD/t&Ҏf_p_O~G?=GK7v/IΕtG),=ա#ORXB3rQrQsE|YSKNTrTVAcc:n BM >X*Dd1z(P@).HF<-HAܟBQ=v5 =/+Ͼxnmzko$?i.o%mTMw.YP} wrjѺ /\qg[n۞ qwO;!pA/ڧo&MܽGOV:oZ驓Oa#ln+{ux?`ۻϟg۶{jT v-?3m;JkD͞a!s:+OKӓξ⤳.Ùxe'q_SqiՎzNo)Ovͺ;xƓiazc>pw]컽 ˰ դ* ubylҬ Gͼ"+7)_3&%Cf͙[/@;<-<x~ft$XQʾpڬY? !~`'O2gΜ;JjDs _z{G_;_82%Iӫl;ww^MW\'niOOڙ w{Si}А ͪ& 5׷>RA=1),̝ Ms5J*({ϙVz2C\ޑQ;қo}7 (Pt[UUW]ſ5na򌙣&LʄGOlaOY "2QFfa ( 1U:P@5݂@.0ԁ Ԩ)ʘ* C(P@@Ә]Ea QS: 1U:P@5 (P:t[(P@S (P@ (P@ (P@ (P@w (P@w (P (P@ (P@ (n@ (n@ p7P@ p7P@ (P@ (P@ (P@ (P@w (P@w (P (P (P@ (P@ (n#u֕ (P=ޙ6y(P@hbPIZ]ryo8ͯuu!=l+kPbvWM^py+?.Y?xQ7|>Yypɪ?YUcx%k.k?:fSUk7,bG/|vtoL[.oGek-Y3p%۰D/vhM*ywgz^Oniv|_8v+zlЈym~ q%M(pUo0ƿ|:9mNK>@I%p⒙Ui~PQUd:10l֒50ix㈇li[[ 쟿B}7F╀摑UkϣsaއWs^/xz7A/st?ep{щKtckwY>fK>csb'^w~1׫LLw Tx$۠zմ 8ͯuu7q.mJꝟQ _ac:9mN˿@x{Nlܲ_׫ Uny:\1 8a9laӎ&qwo*}gYnyooʊt6}Ŗn+5r'0e=G޷[G|pN=07A/st?e컟F'.mӍm}v#j0;9~e6Պ0S!-p|`h>k.E'VnWXǃL!Wʷ€7Ƭ ӎjt ?ڿ0#`:S7oZ;l1GO\^纷)KPˊ 4:^ۡ/jGM&NYKm04 j($ ɠĄq?5U@p)ƅ8^b'w 9iOo?ͲtSuTtXtf}7zR@>+bC\YI7nq۞S'$e/ց~r% v%֌6i@Pn3.A# "%-J\&̃Ői༲y/: =gnx.~kw-$c8[`ǚm^!!uzGt,O/0P] eShrmt+tx{oL4@p7nC_ġ=wmH'ҕ;03;2 t࠿e6Y%=<5j6(JV*P ÅÖoɂ] 52L.O,ZC_p3Ԍܗ %jNSdY+&K94K;,RœHǜַPK1&1 `$S %6O.Ҥ\] ``<5jN\$Y<>tAPE*a*Cz DVF"{'kt;:^doW0Ķ@<#]B ч[|G|8gZբ֐-(p7n݀w ǁežgfkWX` LYv|v2g'0 f6x2Ǧ94+Ë0hAQreڻhP\)U_vrlK  s:gair% Z>%t<+ bYACōQ؛=6saAPTӲgޞ+=6'rlQ^uS 2CW51x-_܁RfQeW 51I ,X~[/jn-ʃyvaz`Y d80;B{b;꒶[V).2ȼzdi+]lH۷>?? ^|ċg7UcƑ n#wF\(&j:`L5 /h$) #9L(Qasb܎00씉_ К)>h]Ax#.!g܈lu#B?'؞|es7ݖ p{lr1Թ Z4W6f&muF=\< |ewɯ0f3}3bBxɵrE*/iM ǰ0Xj0}`~~©Hn>j1ʷ9H`; X|etyi|Mh Ò:qS m^t )4P*8}r{GG'AQ 9\|xUlI$pqEތI{ O^Pѐ> f"y`%yrowtɆy 363MT΍r98^srj\s8$\GҎJMʹcю`#|{s]g.QJ|Լr@fU "tXu٬xjEC9iUTYC!BZ5O-[t8|2ıC10Zw$,9X mW |Xgp@vJWsT0r) QKWC0'rJ[@\u?鹌^\08 [ LbH9J'*d{)dtIp+*}tkz)!Yjp7n݀w pw3+읩?A qk&'HkO5 %ԭli6M0N2B /6Y:&]4sjUF1xki0fcf| aat\a@5h8a3/(tUJ=5-!9QsۚC]<:e2!(Ze-SX 3NXS1bO a||L+ 8`w^,g.U?A*Bap2d{a]`VTRtt&?`M{Ftm.;t; ӊdƭ2{Ta^f6O2}89.!AtG+Qyno[x?g2nw:ݳ)I'H#cdyyJx $Bo|MYeIbQ3%aEt ;A^K~_()P^|03+73V\oXMdT ,f'kaT}B[re1Ry/7kO+Dg"2i_N m:Ws,xSG`!$B=ЖpG]Ɗr2s}4oE*aTP7SmL8u\T0,5{ΰ?ܚ^NIQh(?7iYLZ9|G#?jʍ/KK>'w볌T**6.f8ŗgZgv>aNhS~;w@Jy/ i#sܖ-2"=3h1pщŗxyH4˵kR-!K rRaGB(17%+- `^RW/+Rr.^u5:(E]x!b${52Keg#QX!I1;>Ų6`Z^YKگ"gTAZ{ [Uã![RkvdS̟yŖp݀җ9eY7+dUF;.fׇ!E6Ld0@rhlc>dv *0kgZ-2 N$w*E ۂ1 puG! {d@Q6#`: D zDv+ԋ hJO@d +/Hnx30ɾce2Ȼއcv%Wq#@n\Fhr,zg<-griP K;HZĠ``"dGYdG P$5gLj Flʭ}1헓]{ #VuĥoxVP5CrRm˕`!]Rw$1N_]VK^z`KK twl88,h-B6t5^Uj>4:ĭI,twʧ z3`H*SqY_ pԩ>CdUB[&ߨT%E4 r[YX'<;e[^*E(JcrN! ;Gm+f8JuƓJ1 l] G@O;,Jlv*]:T~g[Xv01 a_l·4VB\ɾq >k9vQq0_u $u+ WB8\.i=Xq Hj0K@w p7n݀{}w_ 5DmRoJgˌY>TmX*)IDjR^tt3W2  є%P h??4% `u4PtJ-)5 *U>@5 &1*  hMpXM`SeBsG fk~촣_N _MJ: f&ŝX(@z E?r-5RmxXR@9(w[H`]:L畮$,+lSN (˳06cEms :XA~32WXИ>ɭܥ+<PaD[%%AU -AU*<s=xE>r+ riX qWjpu{wZT(`|m˜ӭF}Dߘ`vQ eKOVi .x}݀w p7nݽ2WSGw%M Y[n8苉\IMYiV~eTAp JaS [a! MpT*.~Gd4,YTa+otG!y>[hDhЫgV*HP*W> ˞[fX\_pWnXu$,,4Kid`%0:iY`VH]QB.aVǕ*rj0@Ai buEѤ= Es+C_6ǎ? kMŵp_ˀ \i#>Y)Ggɡ!O)oiY^'8͹FXp7nq8=9-Q,jHS^ /Ar: ɎK;~ r4+T)5mq rY:WK,[:xX)@ESQLl/˭Y$QhV,XbH `l];̴AyKW> .V1tj!T0D$w7JQ % 8Ned\ewrlW"EEJ) +Kyͽ6aGr;C"q4qwMZ7 s=h+0[u^);U@YD+ٔAVfLVO\WLR vb4 <8IVT{DY];-~ȵdiyH=V3Z?`WE6+,`5ϑ$Nⵌ  ^0., !Zt2$nmjǣ+61^׹(Cl5_ 81Փh}f݀w p7n@[]ߔX"| lDs+JK1;d!4riSD dsź Y + x S|P[%?v*E#r9CLϥs#|%,a?zi鮁gVICoȧ\1,.=zeFOr BD:g)؁@nD`NT-h0rw]fu}cqD>WqjG:mQfǵ)K 1an D*>*,*Kwp(.Αr32}LȚ:Z Kb0Se  (VOE^T\F$l%YLG˅@,<@ ;0% x-\y 4̅deEf QAw`0 X ` o@lee򥫲2@2XrKYT9O]΍dY]^,ҍs<ҕ1Zܰa-PNfƙǪuCNG!wusls]}PTSR%& =I\ąUW*%r4nTZf݀j0My>\WJ5%:'0K dɧR~{UmkM84$h㙞4'0Ji&_rٕAW ,I קa:eq'm": N 5Ñ'"licG̍Vjd%jJa ߖ7p0,<גt@L:Ɯ Z(z٣AS 8wB RM?,\E>lHqi:zREi\q PX7'WH'ߗe_n6}qFFQ$Ō>-y9y!Uw)V8IF|}Xlױ0?dq5YR6$f~SԻp7n݀w p1nH\'X՘桱ZԜbaBVm12I?L*X=cB i MFr0P*3X^z+d~e.Mq&bzAJy>"bݕC/@>$%F_0 ΀܂l\He*}llYT*(4k Fa4a!JK2xpal0sy"Ej21~m;?si4,S,Qc:&V 9Vlo6$t:joR D)PYB+vswFLQ/-ToV*;(*. pa. <TEdJJ9E"`a9/h`+4AY x2ho Rr3L$( /.!3*KT;a@,K@J|Y"a3גGeRBV.$nf3 5g-φ.+A ne*tnvxVִ_Nˈ(U')ۗ3#0A7o1Fo9':p(iGcnq&192JNi݊}x^pkͮւ.g. p:z.%8"1ΒŽłD@M5%-WGm n=[Wv}(| `TK&16Kpjk7z0%spkj4uHc4[bB)w\H|d*纂4NEvL\PxF*devhM `~5%G&ݥRrU.*}{)/B]ƐN f%0;hZ$J4$V-K,1g$^f%~FGsi BKH/'}&@G 5<,(Z NOh(Up۲%<[dr-]TsBk(G1/+5yj5:'ϻi.|y*"~dyEԷhT<w p7nCm۶e+bŊSN9#</Сñh{.\x/~1t:ƨ1E &bgfה! xPbE EUcUfMaJ( &>G᳦V%׵@lN. @.L̹FhD=%)@Zk:&†c0ѕO;Q:Ev;͡:Qa* tAV,7@i?(nBed:tE ̵,3λ'e4x؃}5H~-hw+H3+T%q2'>iy4"L׌wQr%7v( xki1nb o5ˮՙ*kU^ .k-I5I3eѶJ!n]=wΝ{r!3!FU-p&Rj[U0-g.k[cZpSΣJLW-9YAP<ܭaj-HVUZҀ0>4˺&-:3g0v͂/nOL2)Q" ?8)|6 oպ/%jR./cs҆QiJu'(܎_XZŭl ZCw?`םWNzoȫ7x+ץPáUo4UMy[3N3 5!GqA*'Gbf%ՊaA5_a3S #ވ*qE/-5o ܒtI.!@c%f ES+'m{KnAG2O,R1 Sq8E>|O3&-ˍXZnHNDչs"-h% T-q$sZ@0tU r9e HmJb]? &EzR,SҚ}Γ fձbw.c$1]+Z@/vSP)-eieޥ L&%T.FVD m3| r n]R˖-vʆp裏_Q5Ϙ1#{K/m_^_}I]֭[p7n݀wuV6sp '$ݵk~;wq4ժUK.$6m35X=s`PlLߓ `pcۉ0shQu (Ig2r0L1 C^V nX%(dfCɌeW<JKJ-%#L-+$H)G,e3|RI)%UgTOWspVĐsɁ9c]̀NW:6QF*$uShԹiZ *@B9W|#/1`F)d%s-JS%x)NWa. _aΒ]Vide?{dTUZYyq<(y:@Zbp8ܦ`DӪ ?~+6HQ'9X8̽4QqYQW`,jq:4LaLUӨ83à$ʘ]+ [gJUc 8NZeI6/|q,07o D{sE|A9>),!̗GqۖWwAw-N:$cy>i$۳f<5ĝ;w+U{XK?]FWjOeKf4oTw-_#'|L˿Q`INpR@a*;Ⱦh yu \Qsr/4^HM⪇?򄜇@6zت%]U:~͏ܗўr:Ԓ_+i(VR) /'2FYY)gf?W܂|] 3ky{ wU.\AO-75>7ˆi*|-?1qEexIpޮLj÷:BITT~Ic.sy1e9O:!_UMpwٲexZGt/ZH^psɇvFo& 8p7n݀wm(\݅ j"kGDvtlz,1Jk$w!T,F ?l99^.U(Q($̺Ag#H90AC Z+(ȴk94TaP[yvr'P,fqdeAe?P/Yـ=+/1BgD [RaWrb\pWe^TnGFqqI_~V?Ze lvf"W?ũEo`=GSzIة^pHo*^YE},үyg!pEMV*|qX̄ep!Ho'U8*hR6/$oɦaWf6PZ͸n˖[ 6H ʉjʷ -"|ȋFM&*%0có‹-6m>Xe9LWznI9%/(;Xk;JAcc]osD>Y@Hg@-i "?Po`*ߟj AWc֖Iږ=C= ٸ)j֮0 ;Y̮ՒJQ]r1suD=;jf֥'n@ZgUN?g5t7RbQSΖ2ty HVYg~3bRf@ҵpOl( ET.V<xA+eT _埌c38\* L2|9fU2"YZ|%JO3m*T j*=K³vӒDiBե;08 stZsɻl(]Vd^I?“x' eS*=9._Thq.)dPd_=*,g,y=*dwR\x_Bxq?Ӵ/%PC݀w p7nݽFuGqM(\ ?#pKu19+o:b:`v4vc5&jk}# O`) Fk#NdZSX -64j \Tr;B 6+Fi u/*km|+Ue"VU)"##7.\' ڶDb wU@3}z ?f 1 :Xє܁R"[dvYiq'R K,wDpFx+^>/@4Ŗِ2J[Gtym _ːx,糭!4Yo3K8#yXعT.xNfUxX^ ׁeɧNrz Vt2e\=T5f0Wc,O=z^.~LiJǻt jLK5!@phG  #;$UM' og_hL]%*χȾLp8vv5[mՍלHYqBkASeI9/yW(Ð,\LU|zy-`-!Kfj6usզ οzNQ]ʡJM*VÜRe] )bf_ ĥB%|c$j`Qǰ-=wގO3_IJIzS3Y$wvx6vnw_ϪR=7i.w p7n݀wS r(*)D"TÊ4l hhR})/ٝ)&X !@bs C4ະ'm-N%9H`Lpv +}8;HaJZL0R2=%bb%޶/.ůЎ: ZIŘ*5XmNj4^S|ܬd_~RqM%y2J%d*]/&Juv@LbN䦡ĚVg%o,Og٪/k6njҳS[_dK(_m\MrG7hM>k$ ׳;vmwsMnKu/Ȉs]Uz w6X`+O='zn٢eVϑ|U4(O:CZ9e]05Uq.u3%ʚ2 w9#p.e9#H En-YCX(P6(eWEМuzi 6 as9eyU**h`g[݇Y>'k68.38dv_nDlLzqyy{-қ7qVKLVJ!] Mğ%?d.Uюy)y ks0QG”eU*J/e#DXت%1v 1GH^W94ybgvBx_'礁 w9w)3[:<%G?T0R:.xJ3[Hb8'Jl,x ,]P<=b/t@ x/YA\N;;⚘#1-5LJߨl%–NFTb!;?(\jи(~4:!3/SO*vR]ᝯgɗGp{IFQh!`6ZQmn\~,1)gݯ2vMK`K'MJTRњy@E e:+3xJM`mp.ӟ* 9\EXsiS P3bk*U97:ZS~>AEh }avЊ,q)0NKƹ/:)4wjutWX <4<_;!znnqbձ&@JXe,q}ImBhL+mtn0-Qk1=sEea xNE?;L+o6?Ɠ Z-/K}m[ v, M\˻'_?Xhp7ȻAmFBrYxj? cL Ț[u<6BSXysQTeZ4(bjĴ(LeZ ]y$2"r?v8W}nA;ZH2r =T@D~;+ܗ1p"{̝؛ǟVP'aLp1̽b c t HV9-qp+*\<<,|&-tʖ/g7 XlEd>TNU>-NS)Á/1FNLwQm lShG@юE~H/^T8}QzNJmVQ1.9lދjHQ5Z^B.QnkUқv}sL=>wX[tS-VA8 llI }s}z<ɠΩ I02V)VA3wu= zO`MFR)蟥%g@ei,=Xb */QQ+ K~#4jHbZ?+ʭqin_2ڗwާ>OiY$Andw"dgag~ JDeTw"Nu5W;<9(",;0oIԱZ:;'&KLRjtbnwvMPe%㜍ij~ϨT1j, yM?վZ'W\txsFR쨬i7k톨6>.]y>3LЖ7nO~hǢb:S\8=7Z0n݀w p7P39%ƭI6D'0>)|VRH~>i7aycvb^,zeGԃWU. H%,2*ԞS4fI)/`re WMt* $ (nSL˨5 Xi_FhNꖵ`ô Dw:tgI$;Ào0"/UK_xCeNF9)0 ˱qfF/ s.xZDy3x Mk߾H܋aq:v <7z{稸)KFF ]襍+R\~^a+I p nܨ ^n$fo.&8f6#/LYLJI& 4VWp{c*#(l) 6ojbOe)x֠s5?9037Qd@rZF눧{\(?F|:X]mMvo'hiL fw#U.R_*)#yopuRk]:mAn4A8/&*Nw(J+?Ds/F&qi,/U7F7O 씯\.DQ1fcI46|k_-N 񥀙XE4 -%~qjЭ? ']EGQxbvKWt#b4QbA"s 0;H;{jgt@/`rQ,LK VfG Kk9K1?J=ĥ/zrnH! Vp "XւUdb,2GлhY*MTRVkGρOZ-,bE5\6u0cKk dT/p;sfwkNE%+K9I|4C% 󒫇lKt1acڀ$¹~^8|q/!(]K#%j_;1L|HLmWԡ:n݀w p7}^zLBteNöezz#'Z1gڹ`LϿrL625hC0 iE͈&V3R-ց0A\]ذT2J̟+Lۍ.:Z&1Am2r:h!T[Mi?,;{N2*+#ɬ]| kw#tLᶊ)-D`Lq{œKH#|Z>>\hagiUuˈVS/͇%\)( ġ8sRUzzbܔ9]wZϊ) ^`3ONcIZ/ZI1t8%æ h%%6\> xʸIq!wfFU:{a xg?e&=伤.WP" h, 9&1'XZO!@ǘ(J,$L4VӞIղbgSىI a'p-H$E`ӔgY>Y _0ج (6Kn9҂{a?sܾ܋%z͹ .,U1GqUu{ԀЈHLPT*n k 3iʯYU_Ld!bӾͨb&'9K=%G9C9?jrࣳ.Y Y^ lͱ85-s7>YJ:5eź{!tw(6|Uun< de-T\̖I\hZ%f ɔ hcnǀ =%`IhtD/-hA 2Ѭ`Ibz(#JnV .P{x *Jvvw[` G1_nUn h-}6B_H1mJXCș D!oQ*Mo)!CTܛ`n1f$/ˁ%"rEUcAÝ?7IʎmE[k %ؑk Ie]oq},T Y+T0yRRxNuQ1OXyRQXi ձdkW47Fhp?O:p7n݀w pwgGyW-uf}T~PT,2MڿhX2EVAuJQ|.@yB&;.qqkR/7QM^MI%˶?Ôhhd~KWTLyJIN;iSPHJ ڡvv/ݓ4XX!ݦ_RMN .y7=Q(K[6- p]U4,qVZYQk ϯoGu 2idQ?q1D,f1m3}e{V |x.$5)(qBaܨy|1]{֕ goQ^ӌ>*l~>tX?PmCi-1V*J&FM?ebk+n`sg't/x0IDZ>QlL?KXZw^O#E`\DȃuR2/\%>=Ѵ Y9w~lJE/+ د刘[ ,'&E2+']eưZ/iIr)K܇o+_u*q@V^q#+2!+#4a6G+e%ƥBKZ&Km`UԫHYap%N9RQP!)"V{x@Q@O­K<Ƨe0& 6XX_Feh0EmQ#z_]UDh,Kx@~ p4g,?Gn5IL֋[⼮+TfC)/DtFKVJBA7nb_斗܉".\ɬo>j sdb ;A JPļO{7&?#MxJJhܑhMB63TvF0Z+ZZGINMI,(V DwڹUR7-n'Ŋ-ɫ.H҉/Mj߈'ix!yIq!b.)̤PDȐ ˓iK퓣Y#mJh4 Y&9ߚҨB,tL$pAIOW 23;c7eN}Y|6!nݚnu$Fo4őViflɵ˘|;%1q}Ĵrqվ_vL$֨|nEdE€4 dq /i; G9rwp⬦up!RK 7+-VcGGnV~dUg p|3ls.jQr.'҂KhJܽĽTT5GݻCzq_S( aͷbZIC4a:vaΪg 8T#i0<;PQY@LΆrIؔ@>58-mz}=Kiᮒi &Zm :GBnjcj'z+nݣNdg.OD GԏA֗sa \gz9_샿 t(^9Ÿ<6I3 8-@WO16I,7;iSߔD!c:IjQb̋dM<8aa'[Mo2A Fehbbugu#:b8WSx#nدe8,Wm glJX cE63PKankmժ-,U p7n݀w[FWFP1 "ǹQr.vh[tLI4G+*5<-T-rj&Ǵ(D)QtJ^+kTvrR+?]{eU >5uk-lVp/D0,ʹLR)B gfϗˤK,r[ lrQn"J9^Ψ£6}Bӎ 0c˽(@mbKt{~qi?16mIA9'>SAbdX^]H/aJ11,kx 4_ET%x/"`?+[FCOzF,[Ѭ!NR~jܗ][%(7ݞxkw [ U{֪d5VPApfv#08^(b)VOȦ,1^Brdb&!3Y" $2LK)BV0pHrL|nl;Q1J!)fs'?Jj*ԁ̴엁x;k`ovbgH5sufg5Dy㘎aS M6ICi;J &;E?/j x; Wg1krV y_Mʲ$kV0l91`-7$-*zvϧq}&tY Y+E1z;XK@d;d; '2XeՑӓ*9‹)+7%HZLs]`j@*Y|R8Lл;%W̿\] rmeZ׵@>H Ӿd?{ -Ka@帐Y!..J*=|PΘp煉dMd wQiꢭ٤*vN|9(P\o펻הtb3uSEod 8@i35Oun|#TK[UD4$%M:B/g=p7n݀w pwwcdm(iLXP9.R 2bGB)\?fR{z'yc9b.Vxfecy*DrVZrO3(w!=wCL7P}c:M ;vyPǤ!rÒ!NR]dQd7 ?y3L Rjte'V~`PT07f9?*GDWS 4t.x5{vzu1B$|4P2G?mOz)7w5uuAÚu51&: weޙ_eckJU&@I$bJ ֬w Z\(bc" [su%g0Ҫ<s98n jeyMx.47`[(?>΅{{@DLBfJ|4hBbݬ@/= Kqa`R7ʹΰ$aU >:MI#.l D&eCl`"(SJ$D;)^ 'DTЋ6U?UVHǟrߕOVH q Y;`RRG!S$RG.N+̻trT*#iV8pQE+O3)r7S$TX%t.;0{`IIlp~ѳ$Uĕ:jsu*u:HT5'%)D9'Rjom" âY,&/RdI?\ ZdS^VwH*}#/\srp3ի>SmXPP =9n݀wmuVuժUz('|߾}jիW'=Fz+EYQ;n"L5Pm]tPJwj4ŃNL22i b97tf>bJU NTW]T(*^@.F)fvyM*5g:a+)EZ>Ha3f?/Ft%ݯTR˛FH繊u ctVG>;3K7_ap;D9%BHJ _9)o*K Vw!R "Sre=sJI87i#7m:S{Z=?s"W4}3ԲpbA5Fb1}fwk1.rT5.w˖-ӊ8"\-Z_/cɇvӢ.]o˿蓣?$mD_2QrҘ+Dž\{IcT[NQkŧr ]&;,O^`WK0`9/[Y{3"wC䭪P-:xHʊ'>FPa k m\`'ȮL"f-!X}C87~[ 5h`6& N/ヤ+ަ6] pQmͳ_6)v]p/Y"U?SI`^6NDi# e,yZ9¢'uJ2"C xl}P $)lCDPSxNJ+>erw68 p7n݀w6Vܭ AOb5^Zr5Wl.YNΡ'T4&lZKijD?եQtv@J?ٕsb̯քʷ%d?ݓ`fa~˸F܆+S%EGfj@9rZk0}XKiLJʈ҈t < `!t]\|ë ϭ1H$%YEҺQGEhzcfwd7&I ]cr`nbj,6 g-:QqD^-I!̈́Kƾ{ݽww[lͤ(i|}Ee)7>E@HFM"ʇy/S@g ^8ܚ$bg`CD WeFV bdD 0xc JBV,ܵ976TXg9|$_>~TTK0J}s+t,CdܲLSIMq"ϛl&,r;/|J*3\^aPtEZ|8{|am@Y4hgzE?Yq0FG%݌(sp"}*Qe@Fq#>d-xc2vܵhJW5j܉vL hI}-}^%G3QDH`J*v9nw@Mǻ̟kאL_DER±ee+k\@V)Ya -/x2 |&\\wL.Vֵ) )3riOk Vh,srIe'2O%d=GRJiu/|K1eYONN}'6Y f5("KaFJXbo uEz8N2"^Zʮ[\gݧMJX+%̛_uy<őjGێ\0 U8&^ݣp 5JWI=p "x)E$.B|!|E3)+pH®R \~d_ vpUeyHZKm("_u-Kmu+HIr0D57Hǃ.U(qff !1rH]W  *a}b:azPӾLW`PyJe%oF*q7Jr ٙ 6oENUѷ0\Wߒbrb8re'd^N^Hv\KLh|9Y9I87aVUƑ\"d`|I/6:Tq'>@Ŕ-0Ȼw p7n݀ F\Ohm=D}Lfk[Q֤ &xځ+@W,UayI6ҁJ6fR[&ɓYX\W%؈jiFMr4+Kg+!kI7ce1ʀ;a4KcAi Qd0AVXD]E>>ƥ,&n!vatf0{> sqtπI Vze#&ʈaI>UQIHhSu+1&G]ל+51-8EUIm +ᝡe)xlKԼ2+7F'D,=9]߱bmWjԦzUz }Ƭe #~_('v$Hj xhw9HRuTWN4P;VȇpWzXZ#D.D`Ћ[ǀXlnS>P\m2-R)t;4JfOXYmuG;h|,NTiDΨ.,0ؽ/$O,WfW(GՖnwWD\b5=~8tᮼbNys)T;*)TӸ+DX$=W=[BT)>rG}!aeN.TA2D{T!2vʹ"]fYkw p7n݀ &pf^!)lG$r&1m֋ʎ$qTW;~PϠR%-3'oU Wd[gZ^KX`iBz`,v P'^y& Emn_$nݯ-& (,8hA<2ǂkR9֙G ܯۂ“.w\Qa$+FsuIJW{2Q۪2"ݾ捠gp7n݀wܭf_Ϻ3o23GwR_{>'~*TX*nypѥC.f{WֶlM {a~*qV_MZҡ{ehY ݓfBшܩ*VOʗ X>\Q:#^xIAكSeJF5Kؖ3[=e󸮦(s\a0Y\YGmX T+Ar4Sdx˾d*DսKb{pZ5pB3պ7aGJrKa b*oJm ՟WF;JXRXTu8G#z |${2M&~zF^/ǣD  g:Fg9ʱ/d+QK֝&S2~o>YrYSi Y܂uh9иO:;)L9qm)hNx 3.+̽Dύ4W8btUN!~7w3mhu%MELs|)VZVАIu~ 2 TrVEw )56+"fmةTPvbY*$rp/&ZITRԽ-[ON+ sU&:C7Ezx,{2z ٨{IHksxFR|0XZ/-/URR4XVRmBpPǧ4)z{I_ݴ`HzQ[bbxmT!ZLW[1|eup_k䬬x3h*`݀w p7nV!JUZU4D@vբ*S#Q:h1lR'sK, 402Z=m|Sӄ^^(s (̚&B}M~\NH,өEӍLwJ6i00 S[giJr.ePƢ%'~؁L> К(K Q<2gq9ؚĴ=ǕW8-b@9(DxcZ=0LXc82~w/:R9>qd-M7JwU YWsL93Q/OzLYl%ª©5AtD7xdL`Ńv!aUM&w:Fy>ҍTd.er3OXՊTUae$rRWw-1Gld3%ָZ.8$t 11\)eNݾvqK߄`v8L8ʵ8Lp Ӛ"H:7@Ë5$!vT}%/ NFą] y5-[a#[3Rh͈8cp 6pS9 PژE 1Qb V_eֲmiri+\T tBuQdؖm%TO̍*g5LXůACTuR\Ћ4CeИĉ:jLVc<Q> h,^Bc?GI)ЎNVFXrR̭nv^He1 iM7S 5LxGjMtT6P%zߥ?7ޓ0XPo,#s(D_?aJ4L)9X sNl"TX~ PDA^az 5HnDDsi{TJn (.ܕ,xP -AMO>s/L+"'~=KSHG9@BoK>̒mT _l;NKk=q:,]4A*8 -;LtJthH ~KJVc]2`s2]NHH`~%~ 9ǵ,lj ot#[ϫX9aL/ZOUH $e21G íWwbڳPTu`{8(e^R<0I>V w19W y+L׭3s1rKeDc0{$$ )o˱pQnrs8 a-SV(݇ELm:sGI />t|_+pSXY2Lǣ9K6$DO|M rOEZ缮}JW%~Y)ҤNuQ躴Hg~QhI}zp;??bĈ85XVwa\ 7 ',Z؀V؀2$qMabFb mRLB UUR8 ddU|K7܀ e_ ?%ՎPP&(T \YB,1sr>%UM.W ~Q*+lHcM2E/'k8@9<Ҥ]dJSsls:d=KۚXCV:''Kzpt'-**p7n݀wܭ{ַ%+p7xN`L3 3Z6,4-؃ `2{hZ 2J˧@.D@i,jelj*Ti@O@`J5\5UҥVWfpFW A&L?plދ>pbe}ffnMƐ*w{ Q.+l oC #3iNq; JaC+5l;)zE][yQa6Pń8)eG3/x$咦lP|we5}7:ܭsnZ^2bt^jŌ/A:lw0Yp B~b:3<, ٵ&V^.ɟK~ײʏIᶕ~ دvC0ǫ,dݯjP=W~d2W'HG]PcLubn,ǭԧ[K s8kAy4QIrIe27?W{,LI7WD ъK,JwkB! O|p39kܹ>' w9Y۶mx\P#{u p7n݀{W > ޺u+jժC=۷o3Bz'ۈ̙3M6[(JI kk6v%h%2emRNa08d4P #+RJRE2.pD̺NK 'ȶ8]>3HΟ}?duws q*Kn]!r޴ r>(|^)c6;bl5$qT~WZDsm$ r'jhM[] 76[ltejd$G'}H'J[jٲe׮]"+_Q5Ϙ1#{K//C|wJZn]}n4m*l&!퍔FY0H8Hb*ѣ'GjrG1ih0ldV]sõ@ ɗ*T鏬0nZM1l1Kf%X&d |~.򽒜r1^/H1aOY~M>P{'LPQYCL̈́VHs{475*3BIUuk`KH$~Rc@<$IfJnFܜȵQ=cN8!)];7{-nGl6m3EIC@!¹4EU+iyNnO2<8[*bh4z $*v&Iq<ئ%T~EN <}IS 慩RJ6Z^̆eTsu)}˻HcY&h^ Qtr4S]9=:\m]fQsaU%* +I!se"r4_UzH-|c-4"|zIsZIׇpԶzq OF';Ӝɭo~ݻ7gUgn׮o~F8(rg( p7n݀L|׷n:k3<].~UtII;~I}ƘIj&Ůlǻܐ2s%#ceEW++$,F7 d,tT[?+=%xXkFtZ1]4RDӎ8+bP7=Sl!SUTh B\x]) >5dB?dgԶ|kd +kDUۏԌ2q15Ĕ :~R9XMJ>.)2@gwS OvVw-[Lh"znj'O>ä ֦n@Z2U8m KfnA6H` UHW%YaA&$TEl5ke]N@ɚ2 w)oZ%b0P>Md[q\XٕMaVWn>fv /j_^|itP$9<Y8]{1A#Yی2928 GŞH/ɷV4E E:x*'?9vxƒveͧ9)̼.:^Bx{?qI@ToJ9Mz:ǮNmgdGp6 <(oe:pt0T+%141F3>A7+z"ݯ@J6Ud\ 1K5OZI0 J6-,ǀ%\韜>,P87 eyjߝ5uOmtޛk!(AcHŗXspi$MNL*9c=o$PIɸҽZ!MƤ[`- #yjXzb p7n@w SK6>w3IY^ݦu"Va˓nF[(Uf-RKPJǓ4,İSn`ϡ្WW(bjhȖ30+,G޺ҚlH# @Ta)*F!٘eve -&btp#o9 )1φ,\]նI3QET,O{F zL?qU,n˛Qju4>.!ia5 r<q40+@'#0h=aNx2Jڦ@C:*X9Ā"2Uxaq<@ZuFkq7^8̼QlGIUYm1J MIu}Fv|C~Zxt_t:BczkN9e4ӈYXe|MuՇn'-;l;6vãIQ3z]P9ގˣW g~|B` :<@dp^HesP, y.%뿜L+ H ŏ=0:[J<J9)dhuץqoO&JVM$_a'j%@+ZHn\nH UtPMqÌ%T@ {4` %>=lɫ0T d"*hGi"R!*nlu4)G'ER|-*ƉSGPfEO+b LI+-OHnRry{{mTk\fQ UЅ;f-t̮PTߍ:$$E`RD]&/ɨL'LjSs]W[oo a}#k'#h2**A+=mqJA%6eV5x_dOꋉVWRpYCs/Z2]C|1MJ7v">Pa~Rͳ;Ee~RnM0b? e:P]z;ٷɼy+aWO*|Uџ7 HTS,^ _+/ q_=)U)q 2O~‰)ޑa0b*irܭ9\,{>y-*_)k5UKOR{*Fܺp2c-'A&:)哎tV,WceŻKs$##:tkд.8״B7,F*FEr~NhSWD;Z&5@+= %\kH)R:4Jh c~}8nT0 eW.Yu5GЋ? [7W_}ݿ/ p7n݀uO?ئ;rFJ={S+ԬUHRkZnTs*! %ˎk2_Vb%! oE[`XɻNF_?@TCPǃ`K.3 r5SN[וQY&[ùV$p2P1,:؏[nz40y}y4я=0Wm2bcqP诉}%&Mf5pB&ca}1Ppf͚ZT2TI)T,6d:Wq/0+aƀMIn7ңm)lùL\N0tu][[>]Zzb-C*jo2]4?Y"REʇˊ),WBl,7ftt<β1 ,ȪRܾO|RbXntZ:TR*i&Eb9i TT`\T$>w p7n@͈g\ҍ@+<`?tS3T"rdTp p-0T#{tO<0ݬ.gg<&5kq"V.4Rz\m| -0c>Ps<+ jl_bX69L}x/֪BC p.9`V˰^H҇0T\Ja{#;V34Q5 8+K2%[Q%SIIsq'DۛoE%u]T:B:RA,gcnRV7#JOM%[f2I o!/aKuG[@pEn;Yp[Ӑ ˦kn*i g.'si6},@JIU=Z+p-筃HFCd85U"?9kI>1O,KN?N< .09KK`Uoچ$>Ὼ Y #SMoҠ^x67J,qPtڤw$q OȻttM+V[OgS-z "7'} yt1Ypthp˂x=Wb _(ֶ 7<%6U`kAXYS2ҧK-V@ɬkK,G-a6"M.7Y3?`fU2Uz&rj*_ϲJ\b:U.zMC(Z[%0 Q|VQ)<.?uR5^3e>q;CP6O*N䪫}?KW虛7oX,n݀wmۈC& >.#}Eg@\M7l-b7OrfJT (.K0p$F.\Up}X9E.HXj*:F ԩL_S̏xK 9y@Wk >x/]"X,,/nuND\`.FӼ昑IQ0> MZ_*Mi5_E!jc:Rv\WX1 㓱ޜbT!p=s&MtoMRP@5]qa(&OBl`. a7O7[{V v ٯ"Pz[2dG\0)yjsL7Ŭ{`[dw3K~'󑊻w% xw-m;F ,iV~.2*)%2yw7\y{h2D1[ktrҟ|L>!"ӣt5U4vԸwb#^޴Uk'EJ)L iYJ'X`*!; S$T>`RaN-_Ll[dSܗLY􁂻bmAؘ7RPN`.%7=s݀w p3,..sv6RKܗV9>쏩)Rz]Gbat5d=f9**#yUl3S+O\b7n*14-Mgj`bmYd]m٣u~*k(oܑUNr[&I_Ѣ@ٞi4U5WOwY ZVјB! ǯ*dBç?NjlRb > Wh{DOP|PjHhp)1o^.Eyz%  YMfT`4{IrG[ȎHe+EsY*G$0)ՎR[ `V *xzbW 0np/3T0L_Y2K\ӷvsSY"|%o|vrwOֹI5WjK)HO׽6 ix7[Z2S.wԠV<w !yp79-N6mA%>Sz^Anpi! Q oc޿^ȯى+Z$ZR X9(?.hJd'`љ,X04ע<_)܋@B;,Mn[v"zBZ1l^*s8~P-U9fiԕ0Cs2J皜@;]'^WS0#VN% /';xIvOÀdx;l=wA􍝉$=zƀw [DիWWG}aq97|0kUWaO8"v,9TYa8.ɳ~(z obssZn`VזrEr u-*upԪ&p\NP'cv?Μ(-[:%r\ xb۔mUA5Ίb|hY>VZvhĜ՘>X hDaEN$sp~[Q݂X W|@z$I_lmtFAb/9oIer5<@,VXjJ}y㮰\%ܕq,vY/&4WM ܬʉI> v\6i] PpT| .*F X{!.M!m ܠϴ68qp;OP ˈ.QUD2^gq<)w[)]h,Z%AnA߽ w/^L3p7 .TqT3!5Т8"p,*5rTL K/m>ƆJze,#l f+#a PZ|d DFވR`.‚|J!7=W}`2S:ݵ/$yZݐ.v'ObniӬ"V"֫oQe:u.c[6k09?fDGd _PxNs+]V3A6SkSbF{V^we p7n݀w=]tE_|~}oɒ%fΜgnYvJ3;P0t m`p E*ӎEһr:jw>s(<6%z1;0L.V,^e@OsYp"5 -Vu 0m3qaNMUt ӟܐܚ0)#sѕJ*!eMZhZcOlgФ~l26Ln/@՚C'& VFYx L*'8Z8|e%b•_>MIi$4T䀮S2[I"隝vpԹD}9Y>Y*r &VjX>Zƃl;odW2js֙TSJ!1 ֝V?p)ߖjpL5GvF  ,I-F`iꊴ c0^II5Cߊj%~#8bԩl\gN* t~H0,)l@7L> _$`twp6OӢ[^a8,R ~6B ru*˕ZxT뽴_srar/*9Tour*bfZ+h`UqtY3okLFOk4 >ɺ/"+AbYSkS^:\h%5\0c%%I${XgcNk!s`+g kʆ25z JPAp(0?5_.[ݛPppY"R"42T n۶ႂ7o@o _#\% whTefT7Gb!PVbab*_׼`||R@Z@RtvvEIXRo{1VE 48Oa$]\ɬ\K2%%Fr-K6Edի~X1?/Q.N-Y|piݯ39E!'*|b)il m8uހa.*-9&=瘪T)xl-\hao3Xv?Z=e"J!p{0\WZE^pO>}v8-t~7ڲҥK},'Bw4iqaw&8/)#|r uM? `HPEJK^۔_y'hJ2(:gPP+xzY)εZMZ&*~%g#h#$H͟HV{]A兂jɸum5?ܵTe> E.ǘ\b0ABRsSH!xp1K_ϊ{#E%BwvIT)V⑪o.v~ONw{篁їД01=Ru\HoHg0nM?%uDȤ۲eˮ]!E~%~ERΛ(QzZGRFuq[jXL)jina)eJ_cEA9 \a]MtbstxeXK dy(]ooF ϟ?F7& ?Z?;6=a0o /qG '$m,0qh*<^p $HQhD?( 24ʜ^r:aM8Ipz7yݛU۵kw.CMJDdI|]d?X\em\Mq6 d&g*]XU929Hz ;]^809J\\a+&2BȧVx'SL{7Wk4pm.j@WV&~|Uf7PD3BzՐD}@BE:`4X4K(rp:7 IJ$6])5K0ZBYgy9Ɂk"`ޗ:<> Z`[7V/#ZkQdF-#Ey, h~~`GI=蛸[(6RO+gk_wߵk:?O~r kժU$/::ݔwo\$+ǧv 9GTTjFZb0hD s q2n"f$h*6/4_HH.M;}AA1[sn0G,j:%ܥ%t44fI7+P8'r`i"kis4td'e$pf%/Q */ӈa<=7_rLKY@NXAK )zƢ}OUac)ot*]zePgۙ.TڽH'/޴Z(]]M3ׄ6QSq }5}uLI vb[{.s}enQ`!dObK|RY2~b rg`X{3sm |M\n&纷[C'Rޫ,wj:q'M຃ x4L^$ _E j X[HuRKj>Y*J]/}C%7o^~ۘ%vz,9Pl60+`.0@ioƋ~RȻl(.wm ]Yb*˥q)ӲBwT#BsBqH{DWA8bU$eta5' <9[?jNRY!BG~'#S=0 I挜l,/םQrMDipfU@;^aU"4RJM6L`Ηb/w7Ңgơ ,yԻロggnnnnnn=(ٹ_IMbUjeU̴ }6WK*P ]/ ifXoRqQ.:+SHdT fY ԗJWVg1xzinD8e8zw|*ڏMC0F^Ln?3a?˥firG VP Wc8;ז^:Î; pqrIo +@lGONARN'>\sMꫯ~h_ 2:wEQ >JXP# )LqE7] Xi`089@&T Y7}5V,GBjA˴`+t[Ъ+~yoxB,`<B_wn}ul7e\c/M2g^iaĹL(R1,ANc4Cvm,`^;;~_Tw w5j9۩a#=ZKEm>0tR}QN\D_F.Q?#J\nilGn)vqX,D&"qb;ԇsM4njJ}7tpchNGc|PA rAX S:9{Nԕyh)tK˜gAgkbyæ偻3Z_}8_iH1i%5q A3"H!EDWC>EOKk 5QBCqM[ՒNW[/]~̥<@0JݗPhT$_}.?%jnŵe y,/¥8Zt շ5 Ҹ pԓW&/sR9:.j9NX +Hf5d>0#H䲨ƪex]RK#f,s=ʚ05z?^\hGM_E<_% lKXI+ mڨq B`Ia~'bqҴ j8"kl+ h+V% 97rKPYXI.E[Ð ռGLwzy9F77ӒD4.;tOs Y2) M|i ˜9*o“=9m8 }Qu (ǁAЫGY!)[m</_^iEd2̫;bF} d\L%PGˏt{VS=ق%YN+wɕ#];(%DQ X_(.U:axY*C*S-e:=r S x36Z0UtpZ,# 1j:9>X_ì0"S&soU6.ΙfR|7S \V c{l *pS]k[&K<,c* [6~I6wpvݫ˄̑ĸZ/oҨB=ɀw($GvίަF^r<gK?Ч??}%"Y?Wfb*Aٰkr(q { Ȥpibuf*q 2Xi~+~DC~Pssu?A|$ͅ/:J4 4GX8<[ ZiT[y>.L\x# ?Kk@["FR=*nY!k,p-S tIx/&ەiu;׿O}SwwwwwwwG$nQ3X$us!M\1z`PQLJd^Ep$~q20jᨅ jfUNEр@Լ 6ZKYb6 g^UKV WKa ji~R_a uմ^G=oUMW}qNo:)ADej+Z`1wBKu}A%IHf&^qZidKba!MQy]׮=OO|sƍpc?N,Ǔy~!`Jf V h v;NV>FPezǑ/pۍ43%dF;:QJL!<ijW}9ҥ1k߭g ]@S/9:źv[9r~ 8R~,ڞ&j%1Ogy"͐ڛV`puIȧĘ0?*痹2ʼ:p&ݰW9f_C wbŀ"> '󌜙cVR\e+ǬSX|g7՟i1Pv\jna!W&SKDk'`8|A;T#UCT~#shYV¢1?Jߟ(qOwwwwwww矞g/~~faLF@#O2z}b!g gem@#F pAFQҀ@~QղWdd z69sSK8F3`?.b)W3S{Y"B0 O@ p\l3bژ92qA;wP'A+ p/ZP^+{0V0mP!ֿ2`u̷%Y 悸jd%]wݵ{|lvmx2/nH^)4~ K"y]S\~0JVU=YJ7`dyd52);Oc'&]aK2oH1lU"k70wy %ký .c/4˽n8\zf]sPs0X빸q5dY4,4m͚H;$-̐WdaI.VkQx1*%0P2_( 3-q_y߻zoG+mnwo\YAqDz{ ^G`%BxKN+\Hy3Ym-|m KJѐX%`XNPcH~\~@4.#'rbp"8*\)k{Zܱknm;IWՋ\3x(0LrjH熸fuyLY-CSǞK Bh "YU/ex\4Ƈ r:ܱͻpwb0{ q\oMi?Nwۡo._CR]_OO%/پ} y% Geخc?c{l^֟OO"^h8IaES4p C{uIh/B& ٰz~R40[|R;q7q7q7q7q7q7qwr"~)qHa՝F+hV2ǣs?X"`y!8h;7̽b:vEv%c|Xqu]UP; P*uyMͰB-vfq.zP.?UpKa4{{_qS<ݐ`&L ~b§5ɌUezho˅WqfF:>eБ}A(2k?cW u0@> CkJ݇=a7|sS!&:ˏk*'2 3σ.J;q Ds%^ iFl/ ky5UʈOg1mʋ8X[ݲ~k1 Or_$Nu@[~aU-?:J~ȴe6pcT{sdu(#p,BOc .VkC.[M-_Oܡ@ױ9 h`q:I_#^V}wE^Y> .(wy?S?qwFW&杀Wq&x蒿)J4oe+VưS ]j L Y9KZܨOBWi |D3="~F[SAN7MZCc|i(?x -ƛK$ gAR U C$E%m05M-iIbġuoAN!RJȁALg(xc*?#[.2Hlz?8=Z{ 7d>wwwwwww6o>!yȓkb/xMR\*Lf )=X3[F$"`U/̜KYwWMJ`B&WeP!yC<"h]]^}5b"KXJcCtRx07bPk>ÿZ=0ʫkXSf$sbT%kWEW, ρ+.oW8sQNU6M)DtuY7Ϊ.t:Ӗؕd;R1wU{?&Ywhx-_vvY߻M:`\@mAvMPx( XZ3N0.s=clF'%&&&&&&aʥmzSBsL\bWr֙WD[QDuFL`\ _@=2 i1<=L6JlTWkksa{hƨ!5iE}M̜[6B4MM.xq|X~RG*}Ǧh&J3Nr^uvy/+/1'mY RF% 2n3g' 1HWYfц` kgR2c-5-W^wۉmnjࢍR5ٲ"J-\FYhȬe5Mn_p@ӯJ1!R(w6 E^*VmPg@kLABV&*ˊʬQ!)SZo$D4@l#j(&Ve'b97L< G _=[T ʼQ_Dw4뿩  |уvL G28S: ~_6:`2rx)ߖ1 4?09F6 C5?3"pwժUO&6q%oLyH .`FGcq4je݀CpQWU gZ3hRʲc )Z8!|#?%H9QQҥ%*9T>{7h.ClҒ|)_l/1B]K#ԼCqd|<ǐgMdrb6=HJG?шo}[zԣںu+zM7w)۶mvbrO6r>tI}kovFd.>Pc2ˆǧ?N"/iH3z$O }Y_3_qdD}I# 8 .U &ՀAomT&ze! /Si"mre`)Ns 0؜ 'qM WNi@aj^S:ԽmpnȦ &Nj(-TSqڟ%i QYU0>:L~RU#PMA*Q0Z%NzaHzF&Ti_DŽ*m'\wtZߠ7 yk:C=X6]W+Q7^~h\~=P ߟ[o(-G$;An_`PV!Ƣh<~2y=!oͤ_m#41aۼ.&Px% iWu?0'3݃mYj"|󙝸{-Po 4uQ{;CS)q7q7q7q7q7qw o>'=q{ִn=?@i̇~~Mow]= i1M;30_gszw $ |rѡ.VJ% բ1?QkF-/FW/qkr5f5P3vA, i,OQN&[֗VܾV>StRqpRE:~S",V&KEdZ3Gn]B 0\Y4D~G9j/i1;3b@~je -gJƋa$c%j, 1pGt6Gs&W< 5~Bw̹c^dʀ8dMtIy?WaQ' ~Ƽz91 hV$+5K|{/UY_"cq `+[#Z9v9% woiE>svPr zM/߅|%s9})j$t -PΚ8hH ?ʑ@,e脁:&Ӱ4 ."Fy-,Xx6Y%%Nlu8%򑩵&p'$ewO4)~{C)ؠ?}v6|ϊ9 w'$ &]J'o:,t&3cT%G_'48$YyMNT\ $*Wjk\^ݟB&"_2aˁ(X~n#RڙLH&c1;߷e %&&&&&&MJ^ ;yIHxJx ֹc|+"NiWqd ZDz PjqVںtHY܍ u7jsY԰A9iXryTf[Gr'8Mk2Y)$rx,O y`ԥӍHШR)Fj4Z"`3W1pE}jc0?]ݲQ"4mP3Y&ۼt2{?>4^evn Tڠ.PvU&5& o*gqCj@y0N0xS,Sc[ԢQ>413ENWF"$e<3ϔ&v"+Xyf"<4_MCAJc žq0B_}VFXeYN͗QyK+y\p-Mg6Y9樚RZ)Z(,/V!&ze:kkxK̥yܓS:)qwL qw<?Swjnk 鱢^XBV~EdZy~5͒D2DT?l;٣ʑZTD 2E.l 4w#Db|cW3d" mϘ*)Mӱv"q kqpTBgۈl-d3WW3 `㙋aX5w+iG.!ƴ wQp$piTE eT l\mqGUj{Tчe< *і:jd!J * V^/8p؇uS|0L Ji˥(h,ڿ'sAWbM4vC΢ NQ/>uC%UQ%rq]^5RkKc?Jy2T2Oj4̰([i)R?؋F8:W|WfݱM40lS~ Yi Z86xhZ.,8E;:Q՜3U9>p1Vk+*7s@U3Vd GHĿȬ脬4.́@4 ڼGC OAѹd EE5~/aUdOPpUrCTrVٯieH["i`ܮ[(W\6 ft2ĝud<=q'ywzi?.I/g{xI*X*7t GXJ]&zF|Fu4,ap kB',Ed>TxyѶЀXI=*pY" Ǭ(sˉf=˶e:6ݠ,\C落(¥_H%J %ϹDHMIcwQC|?j<ҒO}*H툴8*\f.);ܾTh&6/^4GD|a] yq8(3dx* JPSp.Gę!:R?ayk1BM Cfiv<]۰BI@i1D_7]0q7q7q7q7q7q7q7q7qwdTD,.VY/~.CۺwfAg;Cl#Ɨaőq.ab$B AarGՉx9UXFK\Y,o&79#it 'FgLH[N6Iz;\?r2kS>lG憉Y[m0.H{Tb#Ե*Z7:7$:=#As;ПoR`SmZ7H4xj^[pHQ'Զ$$UHsS#zr,ٜ{?8V`U5ǜçߊyoᄬ,&D<;2=ڶK}j"* '.Oܕ"ܥÝx,N NybzԍeIŔ^?o@&aV?0 /АtWaje?(It~Ҧd洵u.*?5\/x<\rgL1-ѸJLj:wYSj,r r^Ɗ> ܈pCwWL;J4hxjQ: >=ʗl̋Asf%_]H\^w[Wbz%{&(R&J+˂s3*'gΕqUƈDoE\#B7 tM|0ZU xem] 1$}P(]|9< LGul8=tLa1->mj]7v茊$.g%M,p]?]8JY|EdTMȸ6̿#BO|=ˁR(⊆Ȋ&[wa(~YᄥU$ b 5<̪ ֚X]UgM rL> k=|@(fy-p94Pt kOZ\'5B)=IŻnSꐑc4f TPI`d?9 4[*cAph]82 (5T`W~BsS Cmg;2pբvgOINa߾Cp(rG:*LEE'&&&&&&&ܝ02,g((؉VO8Z]:R@t{wigEgGk7kw]b Qn>!އJ%Ҳ~XZ*%]n[5!U;vWPcerzQ\FGזLTD.v~Y N+C햇~)s3? :nwQDoG3R>xaJG:mFZ jS|dqjTUJWWevƈEi29W*h>r=pwydH1wԦҗmm™6 sy#&|;Q\$ۮd m ҃ &Xc]vx2`@ U_#5iȝy=R44=is2zK"TĞnJ$;\yLIq/:ſߠ( (MnN:KKyU;jIV2T$ pzMrSHdN1@3Lቋ)S]k"qmdwʗODX*Jrh=7bcъY>y|!Rp>`Dz2?'q7q7q7q7q7q7q7q7qwŝ9uלүkw6QՆ MmƤ:Z'F^ K[K.3-ӍuX4Iڿ%[GN\~$mTO5#˜̣͞FÑmv\]cNK+oUxkQr.@}#2ƶ-4T܈#DYb95L5ZyL;eX:]ݍ BhjG3s3ri +OryNzh,u>̼22'=Ӯ5ʼNXth 1 j ԮjjǺzws Unnnnnnny%Kz.N8m cԙ!uŘ]ap8¦*x,3ظ~Uz86]1bk52 9'kƾ[NuzΜh9\nǢ}vXc cq{@W}6Q)g^QYP, R\e8A4((NہUr?x feglq`0v+;1 q?jaP-U=Vz; r`88 @Ùc]W^z( нsz({x%+K>I[#6XXVEn#UfNWe5 nAW(昋il`)*:bB#HXLc coDVh=S5qҰ - q$1Ѫ-jtz8^!, U:Ɨw{7!g ?ZsOjI  @[^MV92X #T0/qe~e۶O؉-ƚHT Ec:?ő,56u5x_=S{Ys!'0hL!]՞Cnnnnnnnnݕ@Q%M<C\XVBc|mD1q.+A>@&kbژ ̶?K*po,ӮV*\UyD]m( 1 w:bhG ySS]isYQb3Ʀ[<54Z~4ҸIL7v01hPn k; . 3 L$ 7H| ~/dBL6 KF{22j1:gV|F8cRnεX%*zȯ;ۊ)p;_D}c)g <,b3#L9@ٚuG*;߽|4m~XXX cɠS-pT /6 0l{gB5H,4jʎEe&}_#h$+ @ZLDX/R`}(*EGGDm !7GVH@_d<B36 U-fN`;W)ƫ2OâLȞw4j´T 9w^^"t;mNү u+_Ú^Ҁ̘RTEaK"uˆFC('u0lU\/2φ;:䬸Wj_*VVj8EzD37gW͓wBaH"h ew'R\e>,u9r34Ƥb;z ʗdUQ.=WF'5!x)$E0Ŷ:e\8ϜVû g) a`.`T,4pÔ@fOx\p 걧7KخJ)opZO_!~1xÿ Ƒ*医vcn]U^{=Yכ#8'鑏|!T~7|w}}IMMMMMM~/zы'/x ?x6N9>#c=J\|guVBPRGW)pjZc)! E4Qw1b wMbf[*XRz}< b [@e0u}4*s^?h7f DQ?i#nXjUH;@)sU['XB|vPjPdY`^e(B2oUKA~7 ˃jv $S{u423 GE~^PV˥2,aLF`u;iIPZ*`20NNxu/w.4m=]nn\o=ܽ;Gm۶^߷?E%@f $Lv䬵"spa+=Ԝ+[),DKбFu\AzWX^nqv5a/"uvg&|{6e{"0%ZiO_Q5Y]t뭷.0.ݵSJamέR}AG9NS=0ʦ uP\0l@Y]98Qtg]vYFX]ju2S6Ā6:3QX9eʯw!vZ\*̥#^37\NF!uyY,vdܲ4 0bh@cĤ",pXLԠ}+jT? (*ߩquff<d7>q'4t33-2B7 '|]JAʻF^z)Zew{8|f'r-yq|Mݕ8I=q۳v '{Դz?|k\yObpTih&%x.VcP>7z ,bc.jj㋈Aڍ ayL\XoUﴪmǷ?;&dX,ڷ9_'] #> $IYT4)`!c2lo|t蓇uj#vFQ[Tӱ=ZY4=!h3x-v>N(2SyQ|?Cw"]6L<9 OyS;Nz%Kn,g R)UV}*ƹ0LVI!޸d3b.K+8)N1UEVs6L4̥S:*+zj?6q}Ŧzr>S!qwfo~O+k_wߵk>d!> OxcЇ>7 -W}8ϥ.a3ŏg"VUNNJtiEEVViYPϠ[FT/}YZ"*n (5qԨW!zikhWL`e0u[18`uV%SJƼZ;5R2@{> ՟hA |^6 !&p;QH=*43Eq2.Wðo6&q Pg6?%mVtPٛW]oڿm cڬϼ܉\pz劵?{ 沅ޕxNAEBfA\u'F;q4KxƘ$W,r➋Ҙ-3s:y%:BP/̬*и~ǍN%uV"eܩ♇4caɥ544t[~vm1lw1֧;:sѱsW|ea>Gv`0]+C;zϹ7H|Nx*NFͤU۾^߽*[ԉU$9&|kAq{C K0yo-qwʻ+B1U3UV׫jy1sJbq5 E5cAE(\%;? H:cf-1G*gF,֯DykgqBTytbԐNU4fد\@q)Jdl.*|MA %qߙu&͔ZMtV4^Pw}l- +g61He< 60A& kOqO"!7S58R׺TMeVQ/YC'd ( !0g}ʧi_칽.q7q7q7q7q7q7q7q7q7]j1]l\&`K iHpY(- *s5[&m@<_8S؆7׉zG؋@_3;-aO4%oI+L6)w_rN+VwwwwwwwwwwǦN#HBV wO+P1Lx4"hAG?v7SjVOډ0oF+tҎ 0/>fue8qd#-ekPzGhkox}Fe Qq0OxUZZW2t5a>FFV72&kN^{k9JoZgNdžX9jʽ+U,5޵|n""s_tL,jlP3,4`xdY` Ƹ4[uE sa syR.9^gE͐T8ώԑbq C[ֺ54pO~Ӵ'vJmyW"$NqM*rN&UHP44?R,u?ƹ;fFNLbnnnnnnnn^IbmRԯe& /kC ₎4 R[{`YWIS<]Md &|˽`cXL!aխL-yHޯ,CG y^αҳK~NhQ e;nur<֎SWオ r0W(t0<ɜVcGE+-l4us:GzV<}LԂN.h?d۩om9~rZerbw-Fk6b+l+Sr1 ¥z܆+v.&.|Wy0nbDH dJ'5a Z#G}O}MMk[%&&&&&&&&&NQ¶Yz<ƘZkB|فf]UOa2 LC W&o<h^"V2E@e:U5tg)4QkaTY@u0Rɳ02n S.$$#{)EiNOa)#F%/lW`X(ܴu=obک<5l?z^b8'J4XCu߸y 6f6[&i8p'︀N{/"HiGO Y[/6ZHYKC&q{YxQ@!6#q#vٲl_꜁^M:\0 mrʬ .$Wzc,)9ꅎ;%;#t֨u fp2pʛrT6`0NEeSKMMMMMMMMM]% 4Äp>BL ZؐȮǰ t鋄":\]eERsO*x4+՘ckb%3nSYaJsI.W*~8aD?GNq؞0(>Ch5>pi1ВS7}|,^d/΢=51H J~3&H*,a%U+&|mLJvR eq<\8*#ZZ|\˩z D*K#a#nK W`N^NѼ'r?^5Ra\ ұi,oms,4k ;j_Sd jS:}`)TҔ~c(nPf0©S3kwnʥLJB%Z}{s;1K-L-Mb5&NsOcbGe8j!DIPW"(ƚY1&hBg`wkyM;Λm n`toF8}1@UpnU)6!Z |9b\ n6qUHu47l_rv" >:65Lܝ;0hݺ^zֳ7<#~yGr!_j́m۶EBWVBb|wqX;.k?.!k3P,6cU:Js2hk.bHa֭*F9LRtz <͒8.anv0g1IǴ>x>S\ASd(EB.O{Þ:W(AOSl Lyގۏ~;,{N`:oA?qU5ui_^'=I ^?SN9}x=+`nzwwl߾~;O)uf*Q(ʴ5H\xc{?E\T${})fNknDf5cBTtJbɜhl0#++O[=iF3; Qo$Wi 9y :IHA6z֘5CyGaS󫓧IpϹS_䶔?((y9:v->D-;M|nmݺp8sOp;|ԣ >;O<餓y~׼/~wwwwwwwYzDz!">iO_Q5Y>蠃 C5<?Izb%o|;{AԆ$5N.&R*["Fx4)Cp=U0`T|9>墨l !0a)G`aJ^uu9bU+`ݱcpcz ]A!^5fc,~b*sY:M#1}Tw{gJpw2K/2rm{wg>wo喇?_wߵks򓟬v:ςpϔQ84/%] i.?({a-V<&unSeͧcVi$ )|;d:e3%LR32P|%eMܗX5l;n=m&dr7jQ Z;++rk-@:2QN2:LhZ7b^DIG}Uڌ6ٟpgUWj|toڟRţO+w#eތSnL4am תp5,f]}˝ V`xXx ~At|)w6-mYͱ/ЦIQ;M4\"rb^PCCs8?1>:ط5 LwѰ F,^T1\LCNT%1)&2GJ~k'54h2kmZHUb &S.Z]f*޳,1k [65bi,5p1v;: o0pkюlq7^.NC)>^^,ZnW9ūm2U4[h;*l>xSMaeh5hY+!Sz.J*pmw r@rlw:1Fnic}4a;2b':s^:}ζ1aoZ"Ljq-O⩘\X/^u~gG'ܫ\ o%.][.fsd6~=k0LqDP}Q$!%\`DŔFU"lcɠ<<*r 3+r״oPaWr5LAA#N@c4+PCW5Y!~uJ Bfj# /DN%Y/,*a:<'O v9dΪr' L9 azɻ\A?TpJ_}T4<0fYY 2Pl"ƦZv9j[Bh"+ۍl_K7;b#wm뎽yҩ-R_5i|2_ʠPodQE"{ _ܭ?Kp:˷o: 47摥{T:F}5; w[QnRnnnb '&&d`>/W(G4(i12m]OQ׏Z.Nk,+E誖OQ e(`V.Ւѱ˝is&DW+<~ŠԙuVkC1΀W9,'/[fmÞZ`ruf?&q\I0^BA_/lQ;L kjIuFg\ 2~룸}i oYb !C`J3ُ_b|{DGLMZ+M[f{'fO?w|*R(+s΋#Ip`bdh:`qqbrX1!oT_ʱspsL)>1mT1G;eהwwwwwwwwwfaPHc'4 hbLiyq:iɵepi_WJ OÚ[+R14\P[IvnZTpǎe9c<¦ WOtj1T/|qq˳$1`|g~2 %*\sh,f ĵھ4us&\dTwLy)9fl9Ǐ]XwwZL:(˲'JL*'V6a7MY`ݘZqeHaYw,UW!={ r,@tGBff=J:?UŀIszMif9G%՝,>6g L,@ҡB܃'6_F5#y1H7OTrS9*j<1LK7L%n#ޚIp4|Zvّ-qwYL,n52 ,T.x4"Gd 9uʥZ IzY8%(uN"dv<< cNiSMwM[_+٠9}褽G$AR.~'E F>;U&MgEGk)s"%?,}m%OYͧ*+u}45$_}7A3r:w-wLޅ%$Qoxb#jcJ}F!>Y$(RnnRnnnnnR%*(DX_IuC>>ilx{i0 (BǫZ(RNF*|͘`Mj<*8ƹ =P `S&SN[iXR˼(&3﹀*= aIp:H,pAt* U:e%u2a%LKCҊhijd ̻^w\0l'u£'+pgm2dZHVac:i JFa-OMM)PNCUlcbF5| (Hqn5#"ԇ3 $Ј5;Ӭ]SJ #gYutiy ~@ﮫa}[HJ(9F:8!s_XɧO yzctڰ*ǿPyLCܡ,%ӣm% SPʬP>2q_ $(X;ʏ,N|ڲrWtӯ:ّUwwW4PERMZl3Mi1p;*ukY )$E\Y,deE *}J۸; NX>( .uK[XiP<{0?v:;2T04αGU [QRjcI¦\wnxt@j+d7UGx N.D_Kˍ*GƤU/m|U}tm,=G=·+q7q7i'|+^BmQ[60xYW3S_%~ w\b,|qM$Qq3OŠ6:^8H1p@4¤\;(Bi++dzV]^;i9ʻú:x m8]=~OɊ]ǒn} ٽE`g߯=GdN%/Mj#amHe2a:4&vܒ]iq fXsK0{Xv<KGh!rioV ! `h'^}0$tR˕}Dݲf)%4n:S*a3΄kggTϬ<ꄁciU|}X0m_ݲcƐ:٩<`Μ!S<\z?J"sGth \&F)X=RpZ8oI+6o+[]Nߞv9W;ȅÃu7]8ǜO筟g{ϼG~omsnμf\|w_qY4.ҿ:mwO[6ow6]FW?8CW]D{x/R\y%}]Mgٍu ֹMzG{5wv7_U"Ɠ}o?;o;mm]#T}Ac­ݳ{NkeˮG5yۉgdFy+cbo-g o;VqpbtϑU]z.^7|1g+W>W>W>גx.wR|||||%F9wJM%uwZϕ4/OqƎ_7 oxC~n򧗿s=㹾/|S/~񋙟E\{O==cg}_?.|͉IMZv{~z?q{߾}^xly_/|xbz;/7xye=qi WIOz.z.p_/&&5կ~_~7k1E__afE{ƛcg}Ӱxpwt)oye>"y.i,mNi}m 5kְ<ߗs{ozԣϗ5_ϥ>ns=JMMBf~c2Z_:SO=5&S?SU3BwBͣ>zټ]$?p ??_矏G>[k+7Mp3.bCEyye*wWѢ[n_?O=Yrg?_:ۣRA{lkhInIIII GkkD)^-Äiߟ[]j~O?y%Pt??%<|,kFieS\y{^Y1oo9'~'8Lƕ9TDyA8YEGBݤ&"|-q" 2nO`@ע1Od%?- )זKߺꪫNO??w/z5=d?5'>9WO"_UOqַF6p*DvtIaG  qA:#>`N8ᄜ]IIII j*Bp җD:q~*?"qros9\wއ>z}+]h"v #Ր۸;h{BU~\O|"%J9sv%%&%%u_Cľ kD -.hGS YSݫ#)?QnQ+_G?Z!2Gm\ܝcr?b6ٕ4c>0 wQ1B@&4'< wrO(ݯ}k fZaD&qpZP8JJMJJ]қ憞yG B~`ͰomիWgF.Ox<~E|A~w))_TTc#1{ϻNIIII.~U`BTߤ:~UpvAŝ dxEP>d.׋.O}~ӷI7.(_z Hx3WƩ glNٰaNd=g?c\ߔ~U8[q>:ꨜ]IIII׼w!` 0Cmmc=p:T;(BP;M_eۈ#? +.Rxo=1#]S JPW)jƉ=\C-ipa}s9w:pwvY#fA6rK_|III;skP #wyƓwwV:>0voo%ȯʯ"_wRnnRR.5sdwwwwwwwwwwww>))))))i^woOJJJJJJW[pߒpƤy>ܽ>))))))i^wpڤy%k_Z~-))))))i^ xW}wHJJJJJJWtiz˓VZ莴sRRRRRRXSlwNJJJJJJ]Sg=+))))))i55ٲe˖-[5ٲe˖-[Y/vn7q7))))))iքyV -qIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/export_data_button.png0000644000175000017500000001336613502206677025461 0ustar noahfxnoahfxPNG  IHDR,Jq1 iCCPICC ProfileHTSޛ^hH RH%bCdqlHS]i  ,,oEAY 6Ta;/|d@- 2D! 6fsB@j>EQ2W)rybP0q\1'hE 9]/+C8PV 6 S=qS, D= -J21#ơ0Pp'u$(oCyNjjB8/q3NNZ&E⋅)voHC%Q cA is''9Q>g4s^) 9Ò`M(-D'fhf.Irl^K3;1,r3 Y0)$!E>5.>K柑'[#{&78J- e> Y|aJ̟+3Cec3636X?Ili^XKgVdL,3MROH`7` 8s֖VL&>y [#5cj>zв#eN0/, y Ԁz9pnh h p@"H"V m`(U`?Q Nsn; ` /Ÿ?m5Cr %M Qh@t$ĕĭvuqH2"9HIRR"1L%;r)2D1xRP$-ZJJ5Qc-zyS'9K+NBEky2lc l  ')*S(* JyJ+͓ơm] (㔍YIʅʇ{GTTDPP9"#tC:BJ?JK2Ks,ެMfݜQu*O@Y5ZvV'uSEY{/V43`5` SU55F54}5e5ZnZIZ;h iӴ];jd0)RƈDZGgLH7\7WYISSoD_[jFDAn.F [ TXFFFƮ5ƷMp&Ld=&7LaS[D flY995sS3,ύ}n),Y)Y[Z[6XWX߶جiy3loymi l7v~5 Wc*3EXu>;9f8u)٩ip|uRK>+۵ w$C=,=D'<>z:zB| zý˽$4mdqX{5(ME  v,x``akbzl"ܢEX .m5Qq$3B>bID}HHiܨ5Qעգm11Zww|+'2^m}W_O\vF{37]oumkw~%gs_.@s/O x+᫱?|mnvD [N]y;GG~H0ڧ]_"Z[SDžl{@j4O˓jIjIѾ &DY`Y6S(h4>N|;Dc{ǿZtOR8>5??u9_z V>`iTXtXML:com.adobe.xmp 176 44 = PIDATx훋SSWbZ>v2lmkӹSZ,< o򲠨hT%2

    &\;? UmF#35Kxz*{?sޭeQF~\*٧:VV+ _·i6+6GU=z?7E*bNglJŵW .E^Pg Ϭ\=v]H<*{(mvxC8S=ܐm{ֺpر /t@;%|OշO,z?AI{P{ 6* M+PgKN(He3KpkؽL<\lcznӘ:@5D PӰ+"&tg[T@g /ŗuK-xNxSD')e 8Cnu>%8IEpp9؁ 9N컵hw A~ڏڦuD(+=QR+mm];"ߖL4+^;7[!>˽Xqz[妜[JSZڤ]e ~CJex;ZZqEhVF9jh[%JdY 鿨Q(ա(hh_ >ޫY1B/ٳZE,9n= s%BsBLŧYeJÒCEf<)ZYEK19:ZmXP̃k_}~a?JյfJ. >^xO/yHݛ؜W"3_=X7ts-81ʊ+m(dI7[/`κYA"Kwo+m#V71FcݷfK甦-<C]zg0nLgʀʱ[}bP`b-NH.Oy'x Poȏݦ@^KA!m\D 5~ϊ>vJOۯ*@unqeChK\ 7h,5ycbɐAzR+Sm~?MصxzΘN)AoMum(*s/ZY<@g04>*Ԛ*4򂚾1VJũ^wi βou|Zԩ7bތ뎸3/'1nVuv}--c[کٮ6 ';9"V%Uuk]gUܒUB7ެ:yO!8Ef֎̡( [9&px۔ìnmQs >'&l?;U%[B2Tv8|*iDӘv2qRY;0J1}_*Re4>&21e̸35WijцkXA;*4K/ڵ_76!Cu_esV`O?äs舶x㓸QEG\PXhŘnėW ̄ϥ8O.DDt=Ĝ^8B/p;_;q@ *Oܒ 2%_XvF_zC է ټ3V,T7?7͡_,kZ%Y Ow%mޤ/1?X*Iou/V"%| 4 _$=ж5?lso(x;A_}ztQc>?0F HQAN H"$  D H &,7))))Zv.jfIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/profile_single_pixel.png0000644000175000017500000022001013502206677025740 0ustar noahfxnoahfxPNG  IHDRlbKGD pHYs  tIME;'IDATxxֆ{Z^ .QT@ 7@ {$@$""+1N!4Bg?yN̙gΙάvAAAh B|AAA\QDAAA\QDAAAEAϝ;wVA:WR_ BG.J r5C~  tAQ BǴDi" xBA:$_;I#333KrݽVA&Zb"AI=WͣGfgg瓞P/M^ P'2"Cr+Ξ(O9ulܢR/pEE +S~h끉c~1zAc pqҁ.?*5/"Ć|6iҸ5?g &AfJH)TkHoLZ}V;⬺v4bGJ?w3 *.3=ʪj]hv'v%z7+*+SX݄_~~qWB^п4:Z/eW=gED^п4ުy> OľLjW*QAhgOH=5K&Ahߕ.곂p}Q2̭kN_AI2JJ3BMndw"i]JEEYQ WV yhةӺK6Y]]\H9pEE3HjO:/GVFzV=JkYAH1YUt*cGײ"vS:׎Vj^Nk.viS3t4lWqT.W.}J}8bGb |89((%E,;CS%Kys?^WPJ%KyJ]^n/(^(Ho >ukBInhRBBViR5S6am[S%tPO:/$j% P5LtV8ZyFV(a k 5'?s)*8U]BV'NYԮTE=uLuYU"I7v=Q},#E:( "&~8nbpRÚRa+)%%[4Õ>K)~| ,ޢ B@7tZspAmVGYp |0Gs(A[BsDStCX^b E|>!E耈" BT5 k)Ҹ.]<(J!Ou ŧN.&]$C]2G"jUN73a=LD PCW?C2ñCk8Y!4Õ>KijF)4%BypRr|X\aҧ5%YyE3F:Η^I?)}syykKSR9(wBN70L**>*2cyXCaGaѡC#n47-//yDNJGtz~^Z|'-o Q%SM+ȮNiUhnFFJFnzj?f*4׌>IԊ:Ѥk{)rDQE ~5py Ns_p?_|/RD9˟+]Y\q]Vk"M)E+^:M`jkE::!:u^2e AhR ~f4z1iecZԄ*"j=Vq)LͣBC4z)WbJˬ8ɤZ (b(fXኟ YXJ]t54C L}NrsҲj&-YYYi5 j}(ד5$sٍe)Y\i50fN{h} ޽4[: ﷇe+Ja~rPǕMZ}Vmt*ੰN.)? ="ӂ Tptx.*璘SSXgI+N/%񖧢ѣoqNݚ!v[򊣛݂3 oSTi2=f/#JO>qTK O}A UĎ#T/ҼrKWS)9\y::ڥ*j[ӾwZjSd4} ]Zh8:x.)i9m}7:y[/[!eJ}Cc}kO}Ԝc-^e6دO*vX6q`;cSx"&G*E{pz(̵K8 E{'>Еʄ2_r×<0J3y,|'knF~O9O.dGiT0mc1kSɡp۠PiҼ)1o9Fg^m?]I.yx?;=.?;f=\woc66<%))l"Ug_WX?vL8LZ5;FsrrWYkaiբuێ,,wEVv}eGvPv;fG-ݒ[cd2FEE/5p %-6&no3y?jڛ[4mipNߎ;~_ݻvo%\=wzy۱gzxx* cK7nz[+*;}dʁq%'(:Yx)MΏ{c(r B|gΖ5i=Ǐ3cMk6^e`rk޲dYz֢U3ϘiOw1bG^MKhiESg[~z}uX~ ~=<Y"Q˭37>*"ېkupAVU~Y{.iN9~_h1鄕^KwOfoU%1PuRiuHq[~{6{,JW!!$oh&쳵x@ wf\UstdBQg|yU&U3VJSOZZORRRs-pΰwܕ^},YT9SgC8pP>=E%#}ʣwOs-sժU7prnJz͛Gr)S:Xv(gON}1%#-֔A;\;p4rg?E,kI.MfgӶɱ19i!{f;Y^چYÒ3y{Ħ.߱k>gMZmꝑXxǩrN[To︡4.%MCVNmK߰:/įGMM6qM.Ah{4Tao<@&-1ym814z[Xg޷LyTh -c;7?و-]ޝY=6}tJrLd?\K16͸@%~QT7)9Rs5%*we[M2V7@rT#֘S@_*'-ŇJy{-oNzhfg|Ј %!>6X'ٖY&jQY#jEO[;{!()irkO4`uSHZXCoknq8JMکl:3%MKhОP7Y~V\I~jk0aਲgɓ _z@(bZ^v;n/ :319=wR3b•޲]f̙ -Phb%?Lnr>ˋ ^ 謷ڪY[r)JhRF3`2爄Ek.94$',bʕ 'sOdO0*u[_cu0f1%?&yl)xŽmibHq4--VZRXC81:6 PEl}]k}$}Y!1~aI_.cю w_[?ӹ?lfaElQ?R5ϼ:Ӯ>vaq!Aƻ&u|[N}޴K__~Ǯ oOl,I>|_蘸7o[{'3 띻L 7o[Cߝ+/X<9M>~¤#GF_/{'-BKh*(([">[e˖sʰ~_M׮]iw})^^1:x.^ {4e{QK( ugxz=FE%+ջ?f⃂ * UF@%ju=vMڴ/<.38:'$z ][-|`bzkjz?El99'>LW>? /ˣ]ۨt3TkQVWW|<+7C Kc J"B s֛Yv~wIuv.Q׈I_{-wIMY/+"yKj=U!=ߤ'ZkWlx->}ӷz-YpQE>K?h[ܪԪyIU?%cўҊP/8: Wj2Ȩ(zOluˠ㡻SLveJ./N)N8.#O|ӷZ7|_?EKh+[[ۯn?(K7i_y啫OC4}BR"zHL܃xlu cO% ?/7bOwWgG[[G?=s7V-@FBXc{\llVf&ɓ?3=H<~z%4ב0G7Js$Ћqt*ӓ * UF@Sakz,5qoHtZ@x_hœܲofVm|ZqMƬح-dƑޟ 1fS/'i'Ο?hnNv|l#KtzX"~L.>+Sh}k̦Q^Epo7bBØͱYc2F&.5鮇#+v?9֍[И{19zTdWmJd}Ɋ^(ʝ &MWM"/M/߿0)bl|N.]YPm[+j}zՒUӼ 7")@ڂEKZ6 E5E-75hbT|DtA ,_]yhJ\"ѯguMl]} -\<}mp&[ErĐ}-N'o /VA J3lpKȹ5&e-`^?2(ac'K+~,.?_v$̒ťI+֬ S?ƅ:5)ovDDdvNEtDѱc^yǜsf:f1lQq˚\b.FF!EEA6ʣEsK} J |b j遧i5{_`n`L-+/ ) 7OYӅۿXsNǙglyw{'XMZo1a5cVbeFC1{Ӄ#"Gxx|>4Ovewg8ʡrv…ӧO()MOM ۽kc%n;ogC[5{STJ&ФtVDb*=M _겋uW_ ηMw=cUje?FV]lתRK|cI#EloFnnSO=wŶ6lܼr%Kϝp&OH]|}޽˯s60w{7[ox}[5O\O](G,}hBVfwbiO>cdD1x~n^a!!tK 05sfLKcb\\p`mgdV7 ˴̥BIMQ4ûsīZob=o0*x_jWё%x:ȉCG /.xŗf?xQ[e"6bF}ZZWPeaDV>vv҉NgŗlhՔKKP:ʯG9XV̬ZGYj'T/~~$ElY8eqfB(a΄oKak 6%6L!aABu &|&UoiEsX^Mifӣ*M2k lDNUo%y[k_[4n-^I-&xe5u[Xdfk,ԕˣ]'|r#4"zV_hnڸGE!׿g^}kt:\Tnnn]t<E|;BCC|(Ԓ{֙ Ekz El7Pu!їpRLb{>/}5~m t#X܄{z8,a-C\7Y L_Tw' B~:a_`oՒt"Z88RĽVī]OT2+w:"3+Ͱ5,i]".ZW]PQ|77bOn^z8eg] n1K2s݌jf+ yi☲A UF[6}h{;ZfٮwыzF豀ld3 ޞ)^ⅱ˞! :E%4g}a/F|9f}=Sc_jγc -|q3ӣ##}<=O~<Ύ'ޘKYi _v~ZK|PtG>ShYXt6b`=`EզV|W!#n3\aS\7o F˒+'U|!gЦO[$-R茦F(|t=Gɮ2TT51*OާVZmj*$1)AYTȬ m]RrJ|*uW+|ȯ?ޕzVޢloo3+y2ʅɕT+)Ĵ_tP`̘OF}umZ_Jei|*(/ ]n|X;-=UސB#0!+E-޳ @#X.Xn '5o~tA7xGo嗊rR|`ٳ0+bpppWAO\>=}: iDDiLN8@QĢ0W(| m7Ք->(֋oPp+wrӚKv/1Th E-@};"R/l/' #N8f{4yOB(b|JjzR]FҴ윝n{+=ӹ%"joq|2vu^1s,JόKˎ+N/>`og' r}GաMjgHSiQ;bkjAJŪ#z GLš'yYoJY`nQ9F_ڗt #N25HQe(bnqC4f+ "&(r1Cqa~v5]ͣAh4Ta$xB6Hk*n:kox{7]ꤕ]-v'-zx%?"6۬]3g<ْ]gkRi/>K,Υso/vN~/0SSs+Ս~trX7ew> ]2s]rj?fحv#6dh{efC '\2rQäLq v=r6yVBw"+;7!Nk5jn\rp}|V^veUSj<'rw(R&EPeՙJG '=ߘ:^fTQvʣqqouw'B`cTu4b>VV{uS|o;XizG<4R~J/ɞF97ng@`aWsc|ih 7xRESgһJGovžW]ȭReGB6fXYt\fdff>c5)jXg`bz̛6=vȐa|ݻwn>w^x{wILIi 3sϹw~ﶷǔSQ"6q4|) 3b>QA(b\Oп&Lh7Pu.W`OAz`HeVsM&7,)kn03ص`ג-Th -u@fFFaV r;Nhe:!8zSϘĤ;2a]gE􏩵=^!yXQtZUk\b2R2)O[|n6@J%`:'N}׮ <㉓qLMkAY,O\o|p{ꑍG$Ypda\9)bsMz@H:gd噔C5F\Ju@n 9 5h$wc#ܚfE 2 s]Y)z4[z^ǠC*in ;Jus6uB~8s=?|k%?|qso#ćE%3{U_j ls.uݯ1 i9K>"zRߧƭ4*9 c[?QʝxM|oī]F;:?盖=:68R[E6~m;lń>S"g7bOãNvu Nw\f_g•Ԛ?u,PĈ_$5~c)[6lwW58GD/ϤM\!N./stR VlbCԨv3*~HjWM%w=r; Ⱥ :`65};iO42)w}A6o;|5識LN笓Nv_]Ea7o%䋁̲ xGn72ذi媵K.3w?Lݘ1PIQ=}^pO>w}z78~fĉwݿn:L3f̆"~x~~~M\  p~GںM)ѮznR&PvUG < \n mfg|NK*n^ANn7/mDžPВl4"=y,huEHVq ͲٹytӾdX5ؼ%2#=)= 3/13&=:921"}DoМK3 O^)b|6o\c[Q)G%YxdI{LC=aiA/bjnPePBKKyhB(71\T3eGVAQ=TnU{)1OVMh+7E]eDʪ|į^.Rӝ |ؚ쯛Wg> 6o7aTt4x孎Q.h:=%G6O{h} HOOG6nYzR=y}?s֔okR 5ۯ_~GSO=C?rbAZBO`nTp=H] ٿM\ >h c>{ZGv(^?~|Wzlhej˚tTmfWllaBh-ZB~KBO i 23v;~,h}EEEHAq11N- Y9 MQ|kg75Yw eK<-߶PᐖfnQn^;|kJAR>L،I:JdzDv) ?D{y(1sMÊ ^_,5[SКj)`6VACe i"6jҺ͖TVm4_[ovh"_RĄR?|s~V4TEv2fYilJߴ"jߢ^KJwMqIkBbro_)p"m3㡇t藖ՈUF貙<+ܧQ#vփnVİԦ0]_ZҷI_GU~Qnh勁UQ{Ҳ꜃Չ%a4Z}vն̪iU+B; {Ƃa1U> z7'+˫Ri$%'Xm^ x3CcwՌIx˾}}edtzk-!pԹ.4e0ŕ^[E>-4eఔX>kC^u;ֹk,[rO4b|Հ/ǟ4|?|=~?oF HKhzU}7 ' 1wqz9(bvttlZ;[~Vv^|M Ӿ}b Y+;eʔvUGe{!jHemIB|C}ý|Cܽ\=,:/2حUuZ fk9a#{EX,'EW1,&yמ//*>)=Y[`?gN9abmox7oK+8xu*blfvS1'wquz)k˗'/+[>#ɞf?L&3-f_ KCfc]PEl}4AP(髓WQy썾~1pϩe 4Yr+bV7\sI+-8>:ӷ/hۯ%!53qk@@~ד@)ť;%fgҝ/,|;z%'~S}S3L4NdmG[Ӟ0L9pڐ1+ڐvAXvڴkGۄ4}{\e ޑ5^ 8}+Q&H;Pڿ%:ʈWVKNIQXV>Lv]oO]El!O[v߇;d'><Ӂfg6隘|k_sd-Et9J/3X"k]SuG͗15:/_pYM6cɣFfȰJO?7~}woi MdU-@(KPDH &.]ƶ:{35#XEoͶx-{R䇞~6΋Zhe-ےl0K[VTI2?5;&[JIMs63K;lrxsW^wxnчG<䐞+ 4*b# 1z7:u5e;m Zh:Zޜ--5[Uįm/tTDF733gF_>W[S٦8]nObV <ꝐII%VM\I.c&2| ΃;s߱ν:}磝?u#7G8>\4qKkK{7ok۔"޹&mܿǦ(b1V|痧zZ3BRϘ9inj:t ;K뮻(E I-HWiR޷Sm۶5q-/")bg]/L/x #@QĜأsV [ddem!17@ ݼ]j:"9Y;.\g?EܹoWK;jCSr PĢeIf aKE̅"D47zSDSи]Efv{L} ^âs52qv)5ظﳵ ޴;8N8xAbOW1GPEl}"R'/<4(OgCG=(pJ)3W<7тm-ǟ(1)b1b#Ġo|>w4+z;W$2}saJzFs-?>p<«~p=֡#G}o踐#q^^H^zDY7ZnUkBBCs!1U_F<{EudrINΈINrL :4R_YFFbyy1'kۅ5R/XGbYݞ^AWHNNHg|?k.è ׃{/OF} 7_^կ:ǔ>L}Š8-[ĵ"> vj~LAn(bV](d]>w/ie񪭾[Lܼtfg7?w?JTm~ڝ<]v!u.~ciU5Mɺz d;o;pe1E 5]O~QdnЃD XcBJd̢C+*NʈKhddd ݑ\)lmBEe˦GMqhqP Bۣ؈4pÚ}|c4!uMf+ L52">(bK1bģ9rd#@nϞ;1L}>tyPD[OfĔg]UD]EJV^sW>w>m~2goT>XR*Q(b3wnZoBt;'E16iߤ z X"x|I#힛onTII?iO1bԠC߳g>^g:wwmS ZBu> YVq㚸"RDO$R>sUG3[޼( Wl^k{5whzjrk_smMmoVϒTu6'5% ;fDkW':H_y~_NvvTd}@XDf˰5`JkXC\$ؔ:q\i|e}xBlZzW(dKVZshnܵ;0SPEl YD|OʝMyoҵVm0_ՌTX2s]K4k)bgj0hXu*+=t(їqWK8.WkU,8RxLxNyPơ?޸5n`H:a϶5v[X&$$%a4EψBkR<_JIGnSѩ6nZr5E$JKCtŊ-3dU.}D?~r3 ZwO}Go /tR?燷QoeXV:?կv=o6qE PVaÆסn(bF vB b  wqq!?e.{͝\-[[Zrq7g?,>#bĻatvo}:Oy?BVYSF-[ǩ%ݻ~#mVpԚ|HW#E̫QDhB>6&Pg8%4[x`r#'%%>&=0{&2ijhwD͸:=C Vص84*>~_aHKj,>M腭K=7;s߫gi W!}ΝKfhhسg>)w?,Ye|Vw6;:8F;;;%%$F5rIMM[#xmEupZ︄ ELAh{4Ty,=}VGm܍=8z]8  뤈~_O5qVX\g8v^ױ͝][Hc_aoH -.WEJ%&5="%Βv}Kra#ޣ.*ǧVZxٻwS3K!x{%&gGok;NyA)dttYplvt1MA: U6hk7j(%FE{4xܾ悞avU通ЌFEZjS4`/ORĆ/U-4ELACCQdZ¿" B(2-B j(U-4ELACCQdZ¯"& B(2-B ZHU GG=APEi- B(2- tDAQĿQD)A ( "" (  ((   (   (   (   (   (   (   (   (   (   (   (   (   (   (   (   (   (   (-[LHIIחzZWX!pU$&&\Rj_f(  " WƍH=\֭?)p_z*-"p,^[oEAQD᪱ 333F`bbrM7I=\;wJk۶mE;gyyyeee Њ/ҏ(b;ǧSNRWTZ#pwwzZ\\\ 7 J'AA?¢m'OƦ5 :IIIiBJkVVVRoWTZ7ᥟ_EmlQ䎾О8zM9QRBwGX-B !)nʍRB~E!L@*ęz!!t_Qfnc" n Uz)*sOU0 P0PLKJO j(Dl3V BR@[Sk.|U3|Fb!]?h[oQۻ7ODۀUWX_lvv"[+.RS_i࣬pZŧQP'\EN6zfp2qjaðSXf3QPCWy:EEQDA~~ELC*4U:G'P0TeÁS0^a dmNsbK,'bi`,zݩЬ|lp(V7QU m11 lط 0a*fl D9lI)]a 9i$L3b'yG=jSX+sbNZm_S6X=vb~>}(QAQD""""""JEDAQ(B@! $`nph48y~ؚaeVeBtlbf)98{&v SWa]Ɗ <h:Tlnñ1>K8 ,RP90"VD,[;a9S2=::SQ/XHŧj!kO'NMFhSᔖ(QAQD""""""JEDAQ(B$P7$mJ8D@s \vtm "]A'`t,Ѫ%6ՉKk;^Bృzݩ 5@A%knu`k{_m Ve،u [ }j7#qxȫCej6]ϫӆꈽ6/e ǜE6 " (QQDQDQDQDQDi( "JEEEEEE6 " (Q;E6ϊ~wa qmN ̣ Gt2B ᐽQMgF,Qdg[PTR>Ow.Ӈ e(f~:z]`:ǎ@#|۬]  |j.TfX^8"8ʼamAU sj)fX)1 (\mh.W;Kf#CPdd&qe2JYuRV@lEYsi >@uV5b{~V,yVڢFQDAE6 (((((mAEQDiMwALFgA(Ȍ whg l5X>rp^ձiUuO+@U<rRgV,DH1q 4RnJmH6`bÐB=; cMšFjz[5 eIۃʅice:Aa*kVh(fX,<V$_mQDi( "JEEEEEE6 " (QQDQDQDQDQDi( "JEh:h6a ,0Z9Dk~,좇 >-A:k l ;#UY}kZH 9mIl 8NQMNqA,;bFDrglVZ:1jSUr,v#9ii!\vDBk:)j!%s?VG mIQպ4;|E" (mAQQQQQ( (FvCbbED *Z-jG95V3I[0=(%TݯUl>L,rK.D`#Q3aaDx o6Sk-aK:ZQST,W5*#F XYmNL6wA;$2eՂ} HIӌ Bx)>(j"JEDAQ((((((QAQD"4ݏvNrJ4:׆Px([Fj юe@sf^;\TGA:ډձ*p)uذzQ?v,P'T'SWʄ"rv휐O;N<* UO.(a~\[vLv혙>XXq匹 DJ:Wjh-(mAEQDi%+VEDAQQQQQQQW/zGEQDAEEE@fj ЎHh;X8VNn mO>DȄVXkR9yv?%Ң1F-ۗ %vˑk&\("xvdZK'TGS^j齉IYa7vseBu%_mQϿ{xDQDAEEEEEEEE,XȈ^ԩ$U*墈B;V܋/Jp$''dA#\Eg@4١R,< nPVm,NDSdm'@(2̙<<-ޝ#gthFa1iG]@kt9_ .$X Ksjg(j&p\6-[<` `8XAU,PDj"^w :Q__SmDvqB% 'OjDEEEEEEE;"ׯS馛z! QhljELHJ?x )ǥZQDQDQk T hӳ 6w q&F<'$G'˄_t!u2`~"[4XgfU@gXԥD)#G.Ӈ `6OT <>$KT" ,3 4&M«v(b?uIӹGXR{9j9ip߁AcZEq~4?Xv5&_mQ(tpEӲ2QDAQQQQQQQQED;v000ظqcppT ("""""" BGTDz~Am۶m߾ѣRQ( z3ѵ?u:(OkA}TRiNV!dykiW^xKgGtuÞrw;l3v{i( Po'O^ *+BX p!N:W9vF9GN,'SlV2_E5u ۀiEwYOUgD̩ӱ3#&𙦽e0@sW[Q(p)"?(tQOO?dH AQEEEEEEDZ+++)/Y=ېe/4'D"4G0j ;+ZhC tԦ'%cX'&BtRksU`w5="u*febu<'\BgD̃q/a  . 6c:8' ʑ(QzWD@HI bbII͛ )x)(( 9EΝk"! MOOe˖yxxDrHvAQ(B}3Z^s%0+AvO-1@uЈg >MBh`=kϲQ%&F3lN)6nBy湳fm9ʒ /^ ca@#F`p<2jMCSL~O`#]x}:Lj+֙\ S o}K lֺyNj"JE:"7EGW\y QDA޽/ꫯ~G4g~mrb6goP]w§~K[z ShN}}}QDQDQDQDQDQDAQEEDN>_ݺu#...}wϜ9uV3335uեKof``(bk67Ih8XB XqmBц CV(m^=ӆ;v݂Dm4TA1KйKgTE?gɀza( v]mN3mh%W֬Ih2㘟#-r ̏ErH7O*Q>7XPϘc~ a,vpŀm(QAQQE6Pa:MLŋ?~+gϞ+)"h޵k4stt.Ţ( (("^< /[kJ酧'=c{eFLNNNxGO#["""" p*"}֬YCyh(1ED 㤈,z-('O"??2!!|}}uF(b96 cBCЕ7HEg  R??h՗w+Iک;a?PQHgZUd6 ̃sx6ND]P١`OaӂTLML]JZs:hثbରIeZ=Tcg.SF.TB89SюjU[2vcs=XN,ۀf'E6 Mq UģϸsYe0]%H z:" FDEEQDi5Ύ: N'|89Wn|ɿ]uS\}(#" FDEEEE"mZ鲘R"R?T4Eϟ(i6QDAQ(Ձ6: N'U&<3pJm^JH` aX)*ǡqQh ~.3Kf3 T0l%KM3tW P :+(`i6 UÐ7q<'/ſT2b쇉-@c*KfRv& Gǚ Se[63 ;QH^2 j"JE:"jLnnn%("JEEEED"mW_b*b" FCT"b"+$MCDαg#Tau:i*a :2apϩ8)o bSFh BI U r~* x # Z5lGoR;Ĭ: uBSiQ9Za L.VaN#NT,DZq1"Lı`MD6ǚo HxDmc!Ω ;'!ǢSDj"JEڦ"R|RĊջMq( ("""""" B;WDzACa* 'ں|rrH64¶( (mQ*F?L6}T ts">MoC@0'Gb"-ƊGB䇃!xkvm`;UBU8"BB⍌:;S(RѪLe=3VZ1䓳ݠ@hT gEHz \ ᣦm3fxC,yK剐=e86–-,&`QE6 YEԢUFWDW}}ӧO" FEEEEEE6 t\EK_ %(QQDQDQDAQ(Ю)" F=xL9`/A/Uphu_ ncn:i-lܕQ۳XZZcᴖH,^ܳNkkd૒P_!( fv*ᡡh%"/LLPQ1րEqC@H|jBm0w >Ŋ?W[Q( X"zyyQ* (EE6 ((("JE:"R tE""JE68H"767C~2RΆpAL {HQ,Yg!sXr\6J+ojm`#*!Y!Q *T(PAKR,xyQ S K!J+RTU%b#T0"n IκLe&.bDU8$gv"Ql ) 9D*#0NҠAP6y|V ԉ(QzW?:O\LŋqK藧.r\QDi(Q+")sQvԩusڱ K,7EVQE6PH0C ]썰Ab^#|t  by!M|{#!a Ҋ"Q ?N*qc*TG̜T0T(T9pRW(BXcT( 8(pkmzqH]ÊI^[3M9s*G A "BW8:G n1'Vѧx,((mAh3$~&L,.W=._EDQDQDQDQDQDQDAQh{I)/\8zųg/)oǗ" ((((((QDZ$zz6 @QED[d67RPʣ {侈 QMeKd; AG6cr6:汉R8pPs U*? ~]S vrQDi@('/ S`ÐP*H|o5%#& 9rU'ʹ1"]RPr%G!O%G֞" "|MO+ д6HyQaHʃ+Z)` T;j Q9>Ue̐+rEdǫcєPhǽG#TUpsAڢF]D躦" FDEEQDQDA6xܹ22'$" m/z 761*FDbLhFDr"Z?6-p#C: гjF+"K Y!xɐ"@"J.g)@XQ*݂pʁnWP/-*JY f(uZ#Aañ ajQI=NӰRHɣ](Qm*o9${U[NEDEEEEEEEEE5X\EQDQDQDQDQDQDQDQDQDQDA("u ]S֬YCWa9X((̰ "J{)o! X860IMYK QY !~!0 rz1^}Ϊ4N/( W*rR||m*`~N qꯂHvOE;"" 0gՎZEbix bD|X:9VCykHY;NΨx {xGpVW[Q(О̼mᜮIIIrQDQDAQQQEt_~W?#;EDus1碢ųg]j<" B ?VD~!0FbN$qk^8Nިˇ}_0zhFx>lZ"s*@%MA? j!`XE5E+E*Pʟ85L[a BfhCL97):4['9(TDWjNp9-' bu: lGڢ׶1ASCf뢈(b"qq ~EDQDQDQDQDQDQ6 fא^P3<ovuum7EDWxM詩( ("|36$LݍVA)Z*El7d)PcACc VE{i⥀y ۠ӆ(ڜQ]B2NTf{- :X\z1TYAX@,(48+5$* lgfsBvm2e+Fj"2ff&{)k ,>s9eE/*NEͥtNw" ((((((b9|0|g }Uo/~׷D}}NE#8@kU.X͢$&" ¥6ʍFIN渔*G2rpS!)2yDLBHe̙TH1|3hoD߯:?|;y2Q~tQ9cU(۶l9Qvn߮-֖TlmlPKoYYXX\.qq/PѱTŕ9{挶RUEEw9V:?g8~Jف(-mEٲi7SAR5[)zS140ikp9zzT( TOJgcQpVQ|R*,2e$|pTQRp`DS]־`\*w(2/_NePQ>ݼ7QR'''t}Q8Msz&ŜjSDz2"|EQDQfVlkk<,e_h˼nQ'T|%ʚz@*k׫7_}EeUT L}X*e(IOFeI(hL* 8̟Os@r s^C%`ϊ\ 7$M%‚JI%r=TE9d)s11 31TNSڃsH(J/S!!TnC 345q")TPc\ƌ32ՎOm>ؙ~ Cj˺b*ÆSqnPYGO*+}z@*Gw*|aGw׼Zj7o^Qٵku>_y.,ʟ~6;***9ܹ-FCՈm>󢈂(((((((((b-(i%¿4bMԷ~^"e 8uah"^'r>9EQElӊXTTVjhˊ(^7|P{SA BL7J=D!z AxjMEe~"" >dB*kXQ )\buWڹ GKiFJؘ "K]eh|0'(x 6(ATR>xɓT) 4);G Oc?Tp|=`9s(A| 4@k.,,^m"M:ZXXSu*hk{hw>s5 h/U' ãctxt>!RTjR8!+BB :(F(d)D!KjM"|T*P>֩v(o*QRrBt<,VBY$jVl~ UPjaqy <z ũ Nt;@  3U`X>ЉQڢL׮]i (" N/vIϞ=E}a)/$Pt8x ,7..~783MWDrO T.X` 9ZaCL)lB*"Io\!`* x|4FкTB3u _ U*)-Q8\8KCL)KT t/KM*EX|mPBm 7CֹA,iѪX&Ӣj`9)6Oڢݣ/ /@o[nBQ$t].%ڞԩ?^NQhŠ""""" "ͥ zWDEhG緽)" K/K_nE[UtHZ3H ~;pDÏ^Drx> rLUjLx#)rdLVăxN|`8]b :6BqV*_mQV " FJMj3P%Н\9Dۧ"R~hG v6KDEEEEQīn.DQx|TY)9 SMLEEEEEQī"SE>CQEz{MtQDQ\JPjnDQ<p{ )M|tn BKU]EQs :xDȤ$[=б} ]aYZcW~*0@_ZctWҿH!pR1lO A øAVԊVgERTjLġA}T&/00HLo'vCVyDH(}E[O>ED_E=囒""EkNf"uMEEEEED}( x*"哣($n""vEvM7DH/nfj"v(*pxW*F<'ԺBNϻ6Dh"SkB +T6+@TLtq6T!ip0tQnY`PG("ew9-XH"NIF˹RddeM&a}ؼb{X-k֚̓`k*DuA! Ǩ@FSE0*Eqb-xG;{lA(W[SW_ED?-7}ף"R("I#]7% V2Ro믿N/QLi(bs}GiQDAݱrf*gCھ"u1ިSiPDzM6QA Qv"d&>Sx~ Av?Zh#@G̃Y;U Z Bh Pi/VVr4K%о>QEElPY'"mL$)igyߦ1fxDEEEEEQk#+"-)\:R?.ǸS9QDmO? /)[nԢZ}D[ QA^S4!:׎nRڤT޲C-~"@GQ*0=X֏*'/@Ŀ)τ:\|U`kha:*q >%@e_f̉rRw)i#{y"M1^ Ȋsx B9#.BuB+`:DZ*8+A*X|E""6 t]zW:ECmhռ^+@j.SSB+=ü ~뭷DEEEEE#Μ9C=)`QDQvtmjheu>)IQEl hCJ,1[ZZ|Т&_sBj.SW^xߥXSat08+"6;hs P`DM*CRWqW>ANxAh RVюŧ{D OR"+Si_s)B=YkebniX{-v*jd0*q| G)@&TsUѧHyP\Ұ6CnS "ǚ0ĿqVD$= :څFx 查X#_PD\)ۥ"Mx;馛> {m=/VmzYQDAT.`$у\8KO( Yt3CEO:FQQQQ(qEԹZ{…o~1v&((jx?DkQDA1P4ׯS=Ź=MD"""""E@&[D/(%s=':MP*KjPl)+-Q( x])w^9] EmEyAstB/FBD߳1*4k`>քƠ">W@D, ʽ6(`<(b zE:Px#!(;[䌦- i观c>{PDYt4Sȡ3aJ@`EڱCj (1N uZ[UЯGgCq[q5aH4ʇp}, +Z,]gK欼0j"J_z{闇^<t[[oElHBF1j(b)<|#GTDzgU&("6j^3j~ ʊH]) zoҸᢈݻwp}zI/Dہ"4D4k2"^[EUEMDQĿ?o߾C1OA(sg-uTAׇj#E &Reo >~yL[JMG'*h$ωQA,han%q ֮%݂!QAP,=3J>*,ŧTLBUߛ8~4QƗQ[V:WgJұudV6)\M*@,Y1'q|m-E ʥܫW;_o^EHW\I2zqIׂ]|׵"3gvԙ_" x嗑\}+ 5Ψ-((((("6poEQ]kE|JPkWQDAaУhG9E;hs86Д0F8*hk۪a >p!lU0bP9#颂QE)`+FΆPX`Gmbն66>+WRclLv٥*p6s" /B.c!Tvj^bګ^N ? [QEO'h@䆩`sL6CZY@S/_mQVD4( _BǏwdEݧJۗ&k#H#aPӪm۫-'lX8{qq"VW" 5<0W^y3 JAEEEEEEQīshaJFJ5EN hRJKcEǧB++]VV]}R(bBH|UgP1h#P|`hB$:V`b"VU .`Ђ9*\a "Ktʣ" Jr=caU<.ٞ=dP5@Sl96R&aanNlp4?cN(V n*i!1?&BL);ꭀ3?!f8] cT:0)_mQVP먍"hB2 y|Υ+HЯ:Ku͡}(n{L[Z) ~HZA:SD"""" "(b@Ϝ9Ct͢UxUŋi)ΖFAl'R/^SD Gz:dA/)AD;8HHÃ^ &!/gC wxeT]݂`H&h "$E'ttL"HW,5n*#ŖBɰ%vdӦ}fS 6VA-mg e)*1fE3&(JmFO 2̮OA ASV De!8SŠY}q2vDڢΝ߿3L&>S:^B &"$w 'cJ!fE1lj 1* :VS;dwm HꢰK_ʺի\ Ol *1"byKPcP/NubDaVSkp\S8ɩXE~~FxN2RH;&-)ڱM,X|E['NhgЛoyfeED3" I"HNAn /O VpVAd)@&H#bdBAVunl;{ $5D6-$UR6X޺תG A*A&qL5WೋUwTA .j'#j|c-<C/㎢P?OO>;#(BHs̤ۦ+M䅇}" 1>c+H/8p뭷6BH#w񶦈בD4Y+ٗ( x(CQQQQQE*z-#Gз"Yɓ'Q)t`!={d"?FZT霤Ӎ zN$R}EQR'xBCWmE1C&TQ:qW7! ˟ fEDR t iN{T xG;Ċ<8)`?!Y`4f͜i*zSA ͅ5*3pC,5ϴ vU}F.u Yeb8K*ނa/%o31,6O эZD=Bh{E_$2-m/zA>-Я(bzͻfY&rC/묂@tAw[T=ڪu(/V쵹phҧD x)QQDQDQDA報+ U[nljDLϗ+"]F?<.7t*++xWH?E7 4 *oyջv(bee[VċΝ>NJf8Eّ3gp3>DQk$@DS Q(!X 9p0a PFZB}T0=ĿX6m)[ 懬"ְ l&Hlynjh|TKZP:3GXs2!]HꠂjD=axc*A8(cPVn] S @S\ C歅 ]@!T'53#\wW[Ψ&"qEl:Υ_BEFtk T">Y %&?_~{*]'&ko\ۼ\C/k+ݽ/,2QR DM:=EEEEED ,ŀR G޼H"^GH?tk/~wVM?: oeo)];9تB72gRD'~SD:tI@eץ"{tX;+ء?!A%|rRp$9 fxpV0dWA^٩t\E1L3ɓ~=teAaL¯@T[r.T& ҅Yh3"s,9] A,01|ѪpG`碢-DE۹"ҨM%%%ZEۿ/QDQDQDQDQDAx嗑{=N/(tQ*bٝiVESAR_/"8uwBC1XnLmAq D`hsҸ z!"J̰D%Q)dT+uomɁ۰i+d졟!2.7oT()# TAq:Ib9whĜK=tMXDiw>bZo!-l9b( *CoRs-SES52|E[: H/(uG?Ijf(((bR=\_~EElHЦ"R>=6oQDQ~w}M7Q?]v=S/@R]Cpl$"-9'9&  }+#XJ(AB B T)V8PNbPT`4vD0r)E:_j5L믩L:; jco[yDx lS5BcKV9ޱ""[±4@_ET1PFrX8d8@> ݲ"bwxEܒqܧj"JE耊HGHwW`"^AmAOImH8R[z:S?"oTD@-rjTQ# PmAQQQElhzHާO_|^ׯ-4{*QDQzNBЂ^Џ$e=δ5ӧIUh(/EEl:3QĶ13 ,I@,T >A dA*1 пR"' | x* h OĿP/VD+4a#LTlf#a~75`"J/i (:6_0(µx"5mW(1`TaoĊP94Ű4Iꮂc oD"? LWC);?nW[ՠK']Lo^{2{Q@+B_Q 7/r22CQDQDQR[z&(((((ؾ~h,DN4"""O(S~/I@j"((x+b]DEEEEE۷"(t6^>Fd!DE[M駕'1"϶kE<ׅ'tMN7Eq׍"L RP·7 k`LBT[PD.~T9Q<Wjz(D -DqW@!i6 Je{:P!Tp*W&| zͪ dDO-EƊj3QĶ 6a |C!*^~ 7D.*EPhJ̰H2QA O1X)YAbl=m_R.PX~{aéL0LqBT)\OQH" l7XcSa8 vw@c<%R`Ngl'1Rۜڠ?W J#jPj$`eMEcj"E 3D7KoQ"wg@pB;SDk;t(FrsQ3+Ο?Ooeeeѵqo(5VV""""""oEN( Wq>~J} mt@חvmT;"y?[E^#(?mZ"""" Di$H((bPk72~+(خ@b*k{n澷 ("T3!{#ުT8* d#EE ǣBH` : rK 8 ,h^m K%28lo'OX)LETA=!EgBvFbN~tP)6gqW$k10gbI$#Eu-UY`uPgLt;dUY {saﰧ||E"?8Х:6""6%gθ"ϯ((((((""^HI"6 4Fb9EH8jPʮDREQQQ-xQA_P(NQAl$!OJ9*4<_1Rfĸ1 I <"V y`Q E ݂lWAP߸1cVB%)BI23Baz\Ne*p]l$<,tqmA>N؟bzc=f@RA"y m)bY>QoKV I"bx2asmP-8d8E6("ARrlEZElv''WT4HH$RRl]R);)tÂEEؽ{_~W_裏M)..~y晷~=c(bS100uܹsRK-=B\B#ЀO>>>rNNt:Q)s""vtE0B[z:֭5{ KYדU "6;Z#X_"7đR,-<bB z$"gi6y)we=fhpWQH El̩3zv$@ޅWpUT~-TT $ʹO)i A3VA )$ hK5RUK!H 愹BPcHՎBGM`Tr!܉c]X/j"ҏ0 7J)((qF\QQQr {pw^h|'j#((((xMQ 8"61T+'(((("vDEA}ΝI j}?"O[A)RD  QhVASǵC($0]!:C b2UrLT Vpt2Bq_mt(O@X G#C)S9Ʃ`uadNJFƂ2ڬ > QDP>-XvMTޙIU뿁fOnr{ܘ,oЀ و1*J\A $bP "" 0dtQ HؙY}>ӳozNWש}"&TQ1huwn į+s%tOu%}-)=Z[[4D[:QU.8Π1ABD!QkIH7l}{[nE/04@Xk0T"D" BZADE& BD!"޹~rKQq9KWQ6T5r9RX+ .G1B1Tbg /T Đ6dC4T.H7H C_Mǜ`DKZֈc)bL6wy֯OļqSB>̟gT1J""]CɄV;F":b29fb 9_YPYkL!Rl77އP k1ݓp lڑ@x$ B#^']HC"FBD!QWxh1Dgh 9 7ǥ(mtA eHBD!Q(DlȈQ""l#"g7G.IB,iρ~@r4"Fiz'@@f̝999؉'N ^F xDĺ∀@BD!l!Q(D" e4^D+и8}t֭ZuS۶ںϟ+."V9". &AD8RϞ={BD(R"zZ:a- "A$S򫞱"rLp)R~HYBUじiAc~Ev _̃)J4ѭwo0@ f!P#)6&,EYL$=1y , =Ȝ_~SZ:33R&twwW -A[[(#", "Bj@wEBĪEDbͼypyc-2qxqW.!"b\S{0D+% eHBD!Q(D(F `jzD<{ǩ„>rWw;! /)t|$ADigH0p/}j?$"2~=-_u eHBD!Q(DҐVvIxC3zCt 5"=zSG9q~_j޲e.]!l|v0.%(g͹`fX¹dČF"(>]ϖ4E.ȐZ >DnF5F3 Fm +Χ3 t#!#-~ sJԻPSoCAb;"xΊZPVf?ΆN31;7Wsr6AD@;Ub7 ABD(Rq]D??[lIm̚g\Sw1%ikّ2)'||P7bΐ$ 'x$0|eyZYrw51)ۡc z<ۍMdF"ڢ$%ɯ,=u˄\8+_ ~GgFXlĝl(AS`w;9e,5Ϻ n9k]D!lƃeJX݈xbJ)D, aSAĄ›?PI"擮g!lI(D"JBD(>6ɧ=IXoRD^(..Oi!O1)DDf"D"F(Mm[ʏ@=&c Hel̙4"Sr}CR W\kс9א@n%V %,4_dWFϻ2l7v&Yj,%ĒY~hMB[{YHwE9wB(kǗѝi)Ƴ1fsx$`v ȝFw= Ӱ`dBKLxʬ{c,Q-5N>%!bGDDvZ0"RBD(Q(DQKދ`A1w@Ē6|dQX+[۞DM6;DDuPOW)>uV&u>|8ɯ`.ƃv$DWL(Vxthڻa =*Lp,xq&egXhsEE"-g>S^(DjnɉUf T. ;ݽ{n6RVgg ,@ q۷Yiee{hſk  +txt#=VAPЁ " BD!Q"3D\bx "= (a& f?t%8)jھ>:1ڀNzs'Ӟz)K/D`}oӝi  x+j`X2."^:u+lfӯG؊Vd늪$"3."z+ޜwYGF= MTwĉW}߿?gq0feefFx_$ɓx B>a H^'<ѹSaDDn@D9`D#"R!DЫ=sPxDD1WDʜ|" *hjӜ6 {.̘|I5}qaάj\D"vLHq'Xx<_nM˓Uը@,>cn[orIkX'_ʓKn ®ʌЪ]CNJF5PDS'J.hg'1CήF QZ "纵 #(X-xYS \3 YL}d5mGzX]겳~zܭ."+ ->.6̆;*ak!L7."b? ٵ0+qP/,tD<? 9X]WTe59K"zֻ2脉h_yXz p bIBDBtUDi^$<+ع@ 4C<p#Z=8DDRi( L= `j(x"+]DDij":Lod`'Ŀ[nuje{ؐx<3Hi=(g)JJ ̍\ C"0 x`'x<PGP(Z,!"]chi\Dg KuhӦ G".eh> ğ?Pg# dB `†n {8:aâL9! - UuMغN72X,\ $8+dHF*""_͑0,c>nv{ _}=S3V2dv`y7'ot4Xnm!bCED.H>-F4e`) `բEwA?$",3DQ?֡%@zܒ(BPEW{|S$"k-~k!"/;4+EVS) *"փ }4eʔ"Vų Ÿx0^ii@\Х `:`ooO茊`<2Yd c07<`O3xX#z1d!*C@D з%D" BD!$DHF AaTe%@q@ !%$P|cc02 W Cg?&w/;(:B93,$"r9z-@*AD,ӓyyR(Dny1i0-0,ߗ-i7,T'."^ gny>g~ xFQ? cZٕ+W2,\'^.H x*YGːCx+ <&7V(D" BDIXcT0az "p!ͱ6=;VG a}YNJG2a(Hp:Űd"b4}ls'O9pEC +,r86x^.DLE@SNPnx쐂`]7L" 8p([hdZA>3fY.ؔ?-bԞYZ~6]s$qXZ;)4h$zף:,gH馛]qۺvݑ7l@BUfl њdxOi{ T?%_YbWpq \3S4g@la@.7u!^N$m7Ia#"J?Wb.N3ԅuuD<K`bZ!  ˋpl.~ꩢ5k֮;SEO OK=~Bz 4L:F7@~&jnQKIؠ{|uk i AY)CDdIcybECDYN: uv[ܥtÕ$*ZVbO@_Zkc N@ }H<E4Dd#eU3B'?"X] hnm!lƉ'2^ĚGF¼( "M!a<㫶"`ltz*.B2GD=sBD!$D" BD!l!b "&bM?^.dW'NX'XvJc te@{t4_k<V<5B]v r໾^ Tqk |זXHKw47X+ղ@?x&ϝh1S6nm!l!brD("P<Γ<{wޡ/(Zu@Bĺ,$S=Q(D" QXΝ9`$!bix̳G"~>. gϑQb6BD!bc|JIQ4k.| ĝ\LN\ރp=? H>$6dzRН\럜`@GD%ƍ%nO1`zH,}gds)eQ-%XCD ]F!E.Yec:>@og^͗-L?wzŗdh iȄ>BF̓ik1bΌ>jq4"sW& !/ 7/~9ƁlU:preB[i]2<;>hoe_opHP|'{܀bT (r!D 0@.n '˖@t#.ԭ-D"@,c.AHa;pQK{jE: U(<{"ޔ09 ,-%/s`;Yl<~BD!Q(DW` y70F*-Tij^ã㤇 VUZA 7FwlfØjѪUT?>ybgn=ØIsAԍ|3!~I $%@B:u@ޛv8*[%!FwFKI>k.}bǎ=O_lDDI@LO+3:t[ <#t}<5y?qaCh/&x=ާ1%U_eIT~ep8,-ȫձsA+- X6 *&@խ-D"V"bT::n\Z~w%KK3'ћN|EiX6}>}ql !"5k}tcB*W},*":#{>pVG 4 qU'GDdъl""|ܿW(E" QXioM=21XTDwYcxC@OF(*(N' # !DD82`Z<"z1x%E 'PqIXY 3*O,^\!""F8ΞZDDLƢWDAx ;K""b15F" BD!Q" k Ɋ5^n;w"%n oJlsAS@,؝1I3ֲJc86DDâ{GD3Qggxc31-D!D,*QkQ;/ od7W""wV.." !w7$~e|`@N8<y3"-~H4ҠE$1g#"_S'@ӅO[%"b̻{byWwͩqV0xw o+ybIX;N 6DD1"v'@]\1sc`!bΝC_"yy9k| qOU!"bl]Kf0"b ^ՁͿ"FJD1_J׽<9I5t+!h/h1,*|JIAʮ VfHI-_ɯtk DDg`B+l "`"0N%FD0e+) f"Ǵiͷp]kn<z%a.64NBDE{FJ~:/ "؋~mT&"_r,?Xxx4) "09^SU`9x>0({J[!b-ӑg6D"QaD&Wr\Dp&BD:WG-ܵDx`㪡."‰ʞ3܉` ؼQ}l#"~8KM""Z(?N;K FgMF0sgBG! OD 4 X"qr)H?@̟:K@L*)aGDA F?9ch]#[XIYKN9NȄHMD\Ce0hdΩ/* ?;Z",m3xˀꈶ& xymhM<8M]m)[C^؟5EɓGk}ƳLݓVL0=K1s3`dTl}iF!aOa}"H&6 d_=4Q 4,f!1';c3gEXfW] "fbV3n$ ̼E̦!"nd1Y.D4ڴie""`5qFD$\!BD!Q(D5^x8x,Gb""AAؑ0hI;Ax"bB bSOyq5G{0ONCDy˾.,".Xt}܆sf-L[!LDM m @q˃A0"Nځ{0-&"BU "rӤ!b䈈*x)qsBa~[NR{;\BD(RD2DuN1ۍ#9%_Uj"/1i(([g"OfĊqMI !2xOi/_={[>}sby[d 7-VLIv5'l_,-@l"­Ee; DoR:K\bR3ߖ@dz|+peWح8l ֎uO-_2m}Hnm!bM?O'q4M>hup&:p2IDD70RT"1*k'GD0`21K X;%D"w:1ϢEp/]Dd_ht.<Ʒne*GDEo_y>cGM L~qfznM,7^Q$!Q( .R Ws%$ܹBL^."V #k? iH8~pDڋ0BD!bcqoSE={$1"n`QMSMo!yYʋw׶hJȷ";91;)w/6tc Zc40v- r<ɍLfB0 X&3Mo@nWO">1ga !袓-zK@Ŀ;}΄a{nʔqï۟{FJ:ZXZVm'q@l1:d"*zeCwӼXIN65'7Gsjq 2!OЂ@b'L/4D%:L~BDjLBC&A}V kH5oy睷~ĒPtWbn!Q(D" %}{zq}QlCt[YĖoTEyUf*ah RD}Se~Ƃp{\V kaxDe␉)s.=t(~UWgְBRǎ3228v^s50(Coķ"VƊ1ܨ!I DwP . -'>jDJ8źN%8Jӿ⁺'Svug]{-6:/z>ЋSp 8zWA F(@t4ub37xu .,ϖ$7ndc҉}zl7.%7a YqW,]*,1U" e_-**ް'\ %I(D" BD!Qj( Q(DC_ q2 xl!Tsm 3nfWvyZJ#7i¤nw ȍWd9sRv ^<~"֢Ξ= 5k`-UUGQu*\I&P;WM6E4C VPf0SPvC !TNĘQ;T@GW;WH(I$IBD)Uxšj +i= :ڡO?ڡB[L"J$IQ$Iꄄ$IQXk7Ѽy;wnqqW\q6mڼkL>K.7tiii>m;}ti;xpk M=X ;1[2iѢ DK_:27Tll^ /}3~7ndK/.N zӟ_|ń{x6G\i-SS!$I$D"֦ #D+=w>#`/~ ݻxb,?w|{m۶=Xi;6"Mmذˆ?>bĈ'Ϋϛ^xH]tѪU-"_~;wf;g_:!?$F4aԵTXԯ_oѭOr'V޽; oY\wA%=/SY˜tvdnxfp :xڣ?!.kw[F%Hu&!b]L?nWO<7%|衇~zi;7o|w6G'N5rb4<:aYvC3 B5F< hL;< p'Dt+7ko|3ge,C?q#ŷ[ݧB:좢"|(,,g?ˡӧj̙38AvY.#bu!Z~+# 0 OО={uuP?<>}8pGw[F%Hu&!b3}o6Cر#nr~]|ſ7K.d.YG?QvhU;:vkzw7o5n<]8v^s508!|;߱?O=Th^z>o+L]OuSԧ>K 1"++{M+0uxj$OL}g nE"H}g(ɯR""ů~N<#5p7x<VVO+Рo߾1~g#l$֭[暞f@,_\h1!"™P!1"b>o-Sn lwp=?cTcY{C#Ib;kx@=L#[pܪ ó^WB/` =Dk׮<#5puH'?}СhؚLD> _x~~_~P[UC+յ޺&}ӟ9h&J2~ kE0g'i}^2DEe` 4 DLޟڭE^^i:RY漱U/~!oudM6};b^l."Vi"ֲ `?au$?ܵkfܸqlTM " `1[+W6k2!n~Ȥ&جLKG8ާjںOuMix_)w֢ R͚5 &JHxCMb0\ou쎘rrr:Vi"ֲuq  C11N0& $;I}2[,O**ŀ@S /?t: JmP@W:b`w=z>-W}*DSB9 ][`xر\;EOS]V#2#>H}g omx믿QD8q)^ybs0*yF4 %I$IX.*@/v-u2T)SY/zROL}g]CĄ#+t!7hѢ^ٳg.|9sNj-~ gOQ$I$I$D$I$!$I$ %I$I(I$IBDI$I"J$IQ$I$I$D$I$!$I$ %I$I(I$IBDI$I"JTzJQr /^8y#١CI}%I(IRi~׺uμ<_}<< =X GY$UJ7xcWf;vmo}ݓ'OV׿?HzpBkR_I"JTؤI_y-?L "JBD!$U "WV^˵^[*nMt:$I(IZO|}ٞ'N|S"m۶ ;i:utxDݍg>O|o^}UjÆ ?>mӦ͑#GC$}11-'?yq&I(IRxW؟={?o7o}袋_mݺqݺu؏Oӗ^z Nq?~ܹ?ժU}ܹ?uTn7r?@K._җeˬlxk~c=z}3^'߭ /yApcǎ-6l0_qx0B$Q{/i𮁇oƀiV~_#"/8t%{؉Gw5G~+_ ݾ{~[ڶm?I'H"JTDܵkׅ^xDDg$^l:tx;\rSO=ojEEExi=/'|u̙۷fS@tٸqٳٳ?O5kPT!JB1_+G?馛 Q1cƼ+C iڴΝ;K߭/:}կk7? \d2w„ $!$Ixϖ-[^wuЯ_?%{א 3v؁Lo>%FkKe}CErؑ$pGa}8nܸ/| ?qQD~b ɔe:^Ȗ1U낥 ?ʤcǎ]v?|H܏0[_P>9D'߲h1d Wxgo^$D$ɄR}&k׮_{#tb… 1T辞.￿\_CՑi$Dzc WN@2ui8W=|7DDxK9?*?xH C:t׿|0q1#?hРw{w_ve @bn7w鶌$ %I}i愒:ńa2M1-GqED0'&.=gI(IRwy'Lx}_!&M "72Exms*`67>:&t4 b|h*D$%7ߌt_ S6ڵkOobo[:1gcj@SEѣG@x^y啈cR!uR$!$IADI3sg&"t㵄^q֬YĞ{/ 1x `oo3g| [!m/Nؽ{7bլ\<,<fҥx&Gw .F9n!$D$)NCw'b ^[xbRC<"lы_xaF*h4xK1fh| 1o$I(IRE\˛oAܢED|S8Ύp xa>X /;u֘hOF/,:\|9G]OPHOBDI(IRV@6$ %I^͜9. ױ$I(IR]i޶j?úzG9H6ܓvd{/gmQvj۾kMY`X~VėTf o&AW?ezY#/gbAAWM. ӞK~=hHYC4o{GNm[a;u}sr,amkE۵K "+Fv4lE\5ElzOslXիy -!Jgi-tc {=V kids^&]8;1w3n[=5G3km:CYNP .:rnfmm-(us]wRFpN~;u~yӷmXSW'  :^A{K; &O)LjGѳH+!8WNt3ԍ'ՖBX(I$IFx-[lDL@۶j1=}tN!1cm߾h`d?֭I]vZ5OY=h]+$qQe0'>ݚ}/~fD.Ï;F" d쒙W`<}ڼDy,X~M"f\*ex)j YHuhZ6cY 'wx7?o;pv~A.f'#SXڎ5C`@?= e-= Vd{V9;Y^4ǚf`naOS]һ<]5@Cr rgT+'19_sr~~v[5e ul0VY8^[6/+/I]qg9AMk;8+w{n8|V삼elLNSyEӛDd-(tZ9Z~4o|`˟<6+{AVm_iEKxgiLMӽܲGwi70{ͫZжMezl״Pd>IHFw46;JpNn׺yeh.M"=ԏ'Ւk*$IFev4M@yj5`[.qIdoDޞ>5pNFύ*- -#c%뽔Kjz>iȠ%v j6¼#kş?GItXW>y,X>M"}ྺ |1Ci!d0Zgu*c !990?^9I$#7H75k<"cNnޜ>86oU*pKSs~AƜa!˞ '˟b(}iy.MҺy+?1{/WXb䊼UvO {Y1ޞň zoKA#4ï\sz7[⫑ Zmٴ<7y{۲i{qi׭.h9~}LVLt Gs?k G sOˏM͛l*'Y1E5{#W_ET$+$[vUƁD㛼ChhYh6㗮[1k4Ԯ_"ir`wvf8-ypr[%,unu:MýEJ]:MC a|+Oܕ_`̋ґ=?s\2A~w>776*JZwÇO_.=;P\ $I5>:1cвE͛sԩ͛s{6d҂.cbזi-[4mi`&ixY74M;F#Hi#WDd %[;9Sڰ$1˨u ,XΌMzYCEQ2W)rybP0q\1'hE 9]/+C8PV 6 S=qS, D= -J21#ơ0Pp'u$(oCyNjjB8/q3NNZ&E⋅)voHC%Q cA is''9Q>g4s^) 9Ò`M(-D'fhf.Irl^K3;1,r3 Y0)$!E>5.>K柑'[#{&78J- e> Y|aJ̟+3Cec3636X?Ili^XKgVdL,3MROH`7` 8s֖VL&>y [#5cj>zв#eN0/, y Ԁz9pnh h p@"H"V m`(U`?Q Nsn; ` /Ÿ?m5Cr %M Qh@t$ĕĭvuqH2"9HIRR"1L%;r)2D1xRP$-ZJJ5Qc-zyS'9K+NBEky2lc l  ')*S(* JyJ+͓ơm] (㔍YIʅʇ{GTTDPP9"#tC:BJ?JK2Ks,ެMfݜQu*O@Y5ZvV'uSEY{/V43`5` SU55F54}5e5ZnZIZ;h iӴ];jd0)RƈDZGgLH7\7WYISSoD_[jFDAn.F [ TXFFFƮ5ƷMp&Ld=&7LaS[D flY995sS3,ύ}n),Y)Y[Z[6XWX߶جiy3loymi l7v~5 Wc*3EXu>;9f8u)٩ip|uRK>+۵ w$C=,=D'<>z:zB| zý˽$4mdqX{5(ME  v,x``akbzl"ܢEX .m5Qq$3B>bID}HHiܨ5Qעգm11Zww|+'2^m}W_O\vF{37]oumkw~%gs_.@s/O x+᫱?|mnvD [N]y;GG~H0ڧ]_"Z[SDžl{@j4O˓jIjIѾ &DY`Y6S(h4>N|;Dc{ǿZtOR8>5??u9_z V>`iTXtXML:com.adobe.xmp 534 328 (da]IDATx}[}%Ԥdk3L6;Ԧ 2Ul2Zdfw=$$ab{O,XA۲i!!YaQ%PR["$ `hY# ɂ[ @T7yNש{}x< "ǁzbc ` ͐Bh CT {х wAAJvBh ).xR  ^*#WժkhN(gA\!Sh ŋ$ [oѺoG6K.QѠJ*Ekq-8m ;{X$GHOIZAMhzzr޽[Rıҽw1¶ߔ>ql˛xnzuH6aRyxA\1s9E}r >ʧn#?>qL'/3gԔcǎIb8&ߎ }bϟ>{n>qL7?pSwXo폥=az RTw|\˖pYڷia'_G%㩣v%Kz ߠ$_ EGuJph$2(^>qL N+R8tq*rCD)kOiD^+HK~~@yAc"H۳O?,=|7"%,>:q_W=s̿￟r~pyUs9KzW_}izG3 ^ yqJ4OŅH#7ސwB@H#opTF%,QQC#kC\4"mA7a3hŇ~HuߖyĔxty%.Ago65a#rDg}ZfͧhO~IN]3W%+m :hm{]J%(7ьnL+)PA:x0ϢӉ.Նr;L~˪vޛ?L{4:hoxi$Lkc#[#w+6}zp][Ե.޷mR7z7:>rgFݷThMO xzDpmTnHm:bї?K}F_z*gA!:MGv3);O׺ujӾ{Cim=_ᓢrur<ƮCK:C8Tv6ϝ/:g?BycOq9h%ozKQ~۽ҫzp; ͩ75}ۿC!"mnW~ʠxwۋ`ʖ7m+ѭJ/δ_ozO;z>n*҈;y\O"H+܉)2qSF2(āX>v֭=^[qXNdMN8۽qHyo tO/ҙiHn+kl瞠>IC#?'Թd>yzNGXt }b my>knQ䧮ӹmu|cCI.O@;<'a=P :Aò.)#=8 8٣n-{2Gyݳr2qN㳴u=-=2mnM6VeqgU9Mi8[I~T8ۏ^Ms-ڝ/ɒKeI*w{W<=b|/-YHO^BD@8& olm,qL7?p\yS &,λlY or8_!4f(OU^3}qlMw[g^xO+3SGB:|ү9m]xZtg&e7k*Wt'x^;:6Fӹ:x/eۦB'{mbSg-tʧ)gyFz;RNҔ5'I32199)'i f$cԽ߻UN>ql˛C!,$,`m8k'@O*Iڽ zf( v)/mTËON{cǏt\u'sr!n}_E玫kM%!u*ӽ K a)J+0d2݆Bb1r`}RH ѯw7ޠϱPܸג]3?Gn?cpdՋ m_|m_cJc򳩏6 .3LsyJM|igJb.h-G^OfZhJ +.c ߏXqa"'Yl:KՕ 9>3~h(1_Q)$Oyy){TDaydϲ|m ?rdK7/ձK=aBXHX P~bQһ:NveҧC&c1r5K["aQ|#c"2DK~5͓&RyQ:ğ@JE.<W:E}FJXcCidA1 i].ǩ".J13ޠBG( jt]gVu% *RB=}/[>iwoa.'a&uGĠ0*7thlxuzx;|6L3A$#43OS"FHBT7:6Фvh$9я(vGfn ݲ׸/U7aQvbn;Z7L:7R4r2aGs9AU@7 z x;%!y]/?LH8'@rBy>Zрh$wqMTe['*ۨrS:&(&2Q-O0# ڨ!nb2& c,TBȆ}*SR$'j0/9_OcWKdĨJ} e ͹_u3( S~<)Z?#O0+>?%ݤBՃ42/4VA œ.:!bu`#,j΍z iCKNTv|w!BNR4yF~q]UwPF^-ࡥw]( zr{w51*y,7!iܿj):sghBJ K"Y*1lQeY <{ިP+ڭ`E 'D3(#¢:|kǢ([.K!UyXuZ ·?tYKoj[CX2녰sIJnif{(c; qSs2#"֏sxGɡumBbBTw%2JO$(=Ɲwga!ƃiG)yܹR9sӯ cpN9Z9P1Nq$TN2ojKiv"P.d)BR0N%]ɥɀG *N/aᣜ1C m(QS/)P0S-,ˡt8@ь4X゗`חcL_!,,,=rz,̌ ('DOSXs tJLppPEHz~=0Dc466FgrZ Mu:ǢmrQ%S!L6KL9pEᛑ e'(k|ࡉIu~ 7 \E*¢ĉ9`  ٰGYH~=IXtIץ)!|1rZۨ^| a=鑒%D2/Y<#LŞ"U UV) OWdeG/ߋ*_2\N‚$yCc!7" #ckקǥPaNNj"є!uF TR-'&P.f4CFmET^YbO *9\?El˜[XeQ^6!DJJDIBiNՒmސ#MY_<ɴC~{ef~s)BNlq+om3= 7]3Yc< *q0Sգ},*;1 UH!&oN_$ܛ)7څώ:4SjMS([ &vԊF~brv~ 2֝8 <09蹸U^ϫ",59y4;u?hycEnuRm~]D)|ytqDMryN8m?nZ5g&r+boάjkk(X K,r"}p=Vų~m&^ M\˼~E"C;s~aEa_I+줶<($Y[aÿeexS [ҨW ~@ Kz/( /~x7X (, ra%X}#Sb[^y `MR9 X{Y{mln-g?asa>+JOGp4~&S=!,@p1xxÇӮ]huV\ܶmEi>O=x~ @ \ ;rMYϻBX  a       BX  a  a   78{AA&B\LAAP*,:A#  hRh"aftdžC  ޤ4",,uAW O~飏>F!'  v?X,Ԕ>AApaߧCс,r9zW1 A"R>aq/kzw7ACnB؄gXXx5zǘKa ,,L tǀs/}YKΝ)9y!KX2ܗ襗^}ё 7gk|! =B㎔/qqv']Ӊ[Y<@sC&_%P V:tlhF";nu#$,c,&0`M1%2APXE55+ל BXAA  AAApEG  ,+@Xaq 2U7(D}䚥7T/x)[]Yb<޸:axO-a܄E$:BF0CdS\*'x"UtrT\by N:Z!ގkY _ Ƙ/OJVJ8'Q!,yc@u%>a?CAy+ oڤ ` e0Ǣs"mD02:)@A#2G/ꔏs:_\vEK2*+)>8i9dj*AN5dAX 8Ņ@EWZotX4 C,>Y_ZG8%2܆H)FGRrXgaM',|{*F 9 F%,e2dE QJy@C,FBHh  haQ+q)@X,n"1QQC B<&,9~kAtX9RNb5ʊ'RIdGXDHqZJ9I'rzQpFk]e1RbE0'VqP/O 8&o!Q9TELڄ/87R4]$ ,VY< <ݳrFC9V , ,Z>zwbyMiSY[%>kTt<$aqc ^*a*/%֩ʇU_xX8#U,ӌRŊ"&!E]vbh\/Zz/{$R!GeȯQR_7Q2V R|0"cY"$ ZIӬ9(p"a.V e ²=5W Zy:EQydGqο^sr)OD6uG30ҥ\4XTs[>O :ɕIkl)d?l*Ҷ)3ێHO*@X,E]\ʻLFZ>;5'ʭotPzvy~C vN;Qk%zh g@^bPB)!-~#ӅOKyJU-'3uJ|,  MTc_4(g6¢ B|~k3Ӥ}:RZu l _be1,ˣuN(rl#~Ez"Www?EXĚ]Nj ׹[ a":YGp O /Jʓ-Ӳ blŨr= ri+ϊSXrF zR7^ O@$nUqh*,bmBuMpEupQd8DR9& ,tOyx)9_Wn*r&$>E1ސRCn] 9k8B-_j^c(DJ]پ{ Z^f4Uqk+),j(*, ]u.!XT#TPrYǢhXT~ۢf\Iz;Z,%˃Tn4ۛ;l/`QmْiuAEBZ܇vuM5! NRj^Pyilԛ6M_4[fGO3LgB*"ҕ՝C!XXZ:l˖MXk촵2E?QRTd'H-H,׳Q9Agbmԛ6M_4[rugm:嚡Q sy&oyicIub;`HB XIQ ,  ,&Glf`"5@HXt<v^Kæ;awJi Sbb߾`>cʐevJ\SƬU5}ac%zWQ07VtYߢ,FlpgڢlVR~rjC AX ,K0wLv;TlX9+lIi 墣K]9Ϣ|f]kiYBD+d{ ~ -%? ]ZE4EҤU,UY\Fh_`@C7be ^6鍆wCtAmD)T2R4q{<@L<ߧ"=nnP_2sz4,#!ErnC(z iѨURP  ,X¢FlEXQөiZ>gc(V6CŊI^xJzZ>‹FX"XQXe'鲲Tco>JhrŲ5J&FZu-Xɒ ز\]-;JJgST ";t*o/@9cۥ{Q t`'N+iN.aqabLWl!UJ1kh7NRx^RUI+t1ajPS庲A)!]Z%DN;%iͲiH0i.Ѓ|}a.\z ,=jghA+ӨVJM¢z{Va FY|y!U%/W-jGD.,C5!,گ/:.,bm"XUXtXw,N+r%,H[Ȧ(R&1\ =%ѸLa>Vϓ".EG:ŵ*'I3^ 79uaEn Tr<Ø/! \T.cإI[¢f PɰB:@X\;-;zŃl,iavu e1^TgYd՚m vBXbưFsnV:@X\?YK)E9O Ӗ8V佺mczgapXW,5O@Xaa@X,5@ق5 }JA咾$W-uAE!&y۷ЗJWK$m7[WKYnE ,np7ޱ fli|<(G<¢Xi3h: x/cr]QFrA Xuv#Q\ ba˶<)),jM6u:[mvqP\ɴn/%<=/Ud'gqoi' f@X\¢-:yupiĒUx3.|m(Vlͼ:/=" zAR> f.KES87G)ݬյ"^2I1@X< ҏ;a *"6&(DʚYz٥  jY6Nv=RKZ7Hu¢I5ŐNx`^pٌcbo7  ;ag!>B@au.Xк$f 9laU-\dEX8lMvADu\lY'V׎uQ5JahNuk7+rTQd8DB ,n:a ]Ο0CF'jNL % P=ygHC@XE06ɱ;됷uz獄 K0%!kmnNWmzTh$:֩P(P0+ev8Z.Z[ZGbjZ6fNR¢ClK0wʂH,E^)Y]a/pxs-WCyeb!BF /˟,qaRlU\Rm㱐eD qN1rKq% ,R" #9qiY(aaQX% O-\XM`4LlVڡg|A*EgalUt!V1-ys-W$_Gx>9R/'xR# v> S8U·p8t=-}^kx44T116[H9|MFfC ,:+6߀Ű$o+W?syk:-k{ۦB ,Xjzq_XQZ"j)KʇwT0L+&MB)an vSRR.avȣa/]zӵwN!2zBVDH-*c : ^stw3]'0Jh@ !P5.ꔍ郱fFiO=bP,裨!,ܮFcNҝ5"H bɅe@Z0/?QR Q0}U*5|6]Ƣ[қGz[8!*̶)'y5*xeݯ]Xfk18K-,-s,|ь=hT$d8 =y;EWh@zXR8Lex7BhvE{CUn-,Xȹ2Wbr"-,22Q"l3Ev/Q\ VKt߼>s>2a!fuz K\yYF,PiP4 (u sr\ qJ T I S/'ETqFL1r?["o5RBBXפzJ0ݮ6y& êUrP_Rbm V_L<(i." 꼫Ťiq$ONV~϶#! %5~\ Ӣ<ݮ7UNM9uz4nnaz.<Ŏ\ɎmȫjQ~ʛaa@X@XJAo[ U>IKI[{!S*u:4aa@ReG\"uTNb5I'JupDze^Sh۪w.;0hfxPfhʒ=⥡$ɨ\Xm%ER,"2dPڳ%Mb Sdl wԚ}/6O$ꮿPE\tmEd 5isn?Ty쬻تvZ<wh|U,M+Jq$> 儴\wb4ҨR!m)>Kqt}-'\F @Xs,jeSq DZEwsS牎?HSVXyxZLXE)rhvgnu]lj^y('AA^)jM gʴ ꂅ13Ȣ!-ѼN*Li- .,*<OKрLKь0\m[a{dQ)P$bݭH[!), .¢z¢^J xȄaІ8nC!Ai;_lW#'HٲNz10a#/QWa}^a"\K?lU0٘\B[IWTdCXZX GYxʮۍz~b YA˺=7EʋڦXSe $E5`Y/%l 鈝_c xr7d.b MU뎴27wlR& BmpZC{{ԨTF^¢{4+ rNBo:@l-4]jS~  ,na"\؍;J洤]z{OK!HxmOMv 13wUXan{ۮ-Vr^m/RzŊ,[]A[rOn܅+aVEH&BXa.# ƼvNaTw곢 -,Z%Th@X¢Exh5eOrP/J%a^-ɧ;Ih{$[&15(tja&m8]yeb(Ύ~kΉPf5[}<aqLts,-jiYW(hOd[uk${xšb7޶&,|>ܛq+"jj()mu5/Ũ[VEqVm1@cvrv_W6}w]m[ӿ5ٕwNKݚ , b(+\<,: `3+ ,  ,7mzMlHhNzLX XP'|#ئW)ΒGkL3*KY @X,M|ģyGBqn,,15W×k^+9lɽM52P i=쇢VdWRN.{WU˺,qMƊMr!KI%Bv4!3i9&,]GJw\ڡ'(: M_ G" 1]Y`So ¢V[d$ 2,ۡs9s7X*[Ĝ-,8-A9DS!=O%:Q.21Rox4xx'LT "b%ۦ빸mZs ģɠ;l[5Y(Yfj# pLln62 ,lyRm-ӻ^Rns;t+au,+6 ,  , j)Kb@Ũɷjή>btdK+W|5+6f=R6b9TR:ebBQ/;Y+qyˠϰ#%Rۥ2'N sA>DeU/%ch6;tLs#!kOgݖ\fd/mCCQTKszlf7P/+.Ff6R#Ѽ4;lHޜ&|G<6 ks/<@a1n%ouMԯåO|hn9! ]~]m2.KW=C441h:swd|g-y&hY'S=xrf+,}.i a'Cgν㼕E苴a둫>o-[ٞDXs433K1$a05u'$`bSxNS7 ѿGGg.pB+8AgϾEo,B\:㼕E}:MOl?zH躊OGiѳ=-Po11HCh}n]OƑ;yF ,\<Vњܷnr48j:#=EkWWuCkh|CfhH 9c(k48ع3pK7|רrG"@a|Q́묶֎otDyФ&ڲ@sXaО筢Ma8=V0n:[c8^.C+cë[i/~Ҏrx=Gyyzmb:9m{ [&h:9iݰG\Gʹib^.bzǥB֫/ȞyyHYZL>!\?I#}^Qt&2FZ5эyJ7Ҍz|dws:NrOB`m@Wt~HSmS4iب,Svt^oz\tkeyH̤-?mX:= md'AhRtDܹձ=kTyl_#$;u8Q*Ô:Ο5sG+V\wx0ZѴs\?#ҹ_  O69gxaN)B׀gXp;XB}ކ|׌{*\E*˚ўղg4fZvܓl4d8BiAI q1Zh~;Ҝ>gnW6H:ZFN.տIck GTǾ^ ՃZh4ߡbҦD|a^nCSd{IN[g`i4SVD}w}\-)ngsx/-Sm^F|K/,9:P 4{焼+߼}ݻCtG6s2w81}N<6A|i|ӾG/"z{EFk+kf(k.w 2 ;v2{GywxCwrTET]R$lc%ab_]û'(C}]˨x>]^ix.ddG{E:aNB KXL)a1i.a:G} 8ދ`H@w}w}\-ߩύyL_hdL ʈH4wuG_e INx(ee5]0>3aEP!kFYeѥϜ0,C*:CFC.C!FRC!S|Mcj]9'{dMSA pŐƼѱu#,zۣD2Y34e$,jBVgȻ3y{spH[Uŝn'}L?'9}kEq%q̸S o<>[O[|} 4~|=?xʻ?&ӟϑ'w[m/Pypz<:S~l;")ݵ}TNim^a2.~W9gКk;Ns,Z7]3 2hib=ao*|2& NZ"Y?wNӕ?~r|ޜLgO*֎?S<ѣ",ނv7vx̰=[~O' y*ۀ5ٳMWֱ1W`s iVgg`!$ᚁNjh9DD7,;Mέla7Cf;?퟉541e!Or#PȠCX߁dQ43cktsq̀m_DSC԰ߛ>o2cnD<~3N} qwsIoB&ٲOiLaIqk9ŦΉ/ [C3qmtN=_3C1 a1jj q+M|>%2LncH:vu=Y'wzЊ< m{e ,zwaR0ϓ8l;aBC9SNڎ"?*B"by:nVXg)9ǩ"3+p,nZ;玀 x{\WW9s   aBX .sBX   x peAN A!,@AAAAAAA A!,@AAAA;… t9:s MOOӑ#GÒ KޢD{{?>_?zC?TozvA;Rtb?NjnFzvQTzZxCcGiLwύ7/>A(O8Avѩ'sU"~΢ cǎ?@D{tj?cl# ؠ57":ql6ՠ?{a 3CQ8 ڣS;Q;Tj[ol4ia +7jTTVo\wAPˤծ=:|DzAvS+~᧸R]gxìh A}RԢtt<E$Ms*h>pگ(ڣS;ރџniП$[l4Mm't߻Dsw߼eIYl@X K#,jtW)'Et݄E!H~ſnn}Aks{(Qq~s;~|VD?fa16ƯD?.^O-r9:S b .SQHio%#(GʙyhF J:/S0"D %dfhYo0Fq5-K3/_ u})RBi%6Y~e),L]%.SkE{@X K ,jz(Z}JD_d#TDMׇ3NҾZ_o,\KX?tk bNgLt8 b%,5/BrM)!v 'K 5Op^ GRsZX%/_|`1{}1s3JXGWd܉KRE&AHX$|" QlYβLB~gFf)$U[(},,2W*>Jdd8E!K-,~we/+>zYm?z޷ߙG>oJcV?]>wqaYoG]E{@X K2yC9 /<3B[FQZ¢QNHD\2ćȋ%9"f\RHu {.opC}TbDx℘cr }O*aS*We)O= >nZL5?n %H*uPixtt*7DYe1aM"e:0vkׯUB7Wk*_/AY8x% dBu~5T*TPU^/_ը܊zT]E~ ,~a0&×oe>uyɮ/= ,@7܄e\7*rK}!,@\%o-{ź+V4_7zInAX(@D{tj\'o^oTW,#W A;T*ё#G*#=:gWG3:غ}Ѩާ(+$Alsd_؅ߔB[_hN>>ۯ<_"g_g$VIh A.\ Môk.ھ};m۶Mr֭7,:zvV/@z=/{ 7xwI]oX:)MPN  ^WBX  a       ^gaq  ")4 Z  ^+r?hABSx #rœ AS!JGFIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.13/arithmetic_equation.png0000644000175000017500000010361313502206677025605 0ustar noahfxnoahfxPNG  IHDRa55p` iCCPICC ProfileHTSޛ^hH RH%bCdqlHS]i  ,,oEAY 6Ta;/|d@- 2D! 6fsB@j>EQ2W)rybP0q\1'hE 9]/+C8PV 6 S=qS, D= -J21#ơ0Pp'u$(oCyNjjB8/q3NNZ&E⋅)voHC%Q cA is''9Q>g4s^) 9Ò`M(-D'fhf.Irl^K3;1,r3 Y0)$!E>5.>K柑'[#{&78J- e> Y|aJ̟+3Cec3636X?Ili^XKgVdL,3MROH`7` 8s֖VL&>y [#5cj>zв#eN0/, y Ԁz9pnh h p@"H"V m`(U`?Q Nsn; ` /Ÿ?m5Cr %M Qh@t$ĕĭvuqH2"9HIRR"1L%;r)2D1xRP$-ZJJ5Qc-zyS'9K+NBEky2lc l  ')*S(* JyJ+͓ơm] (㔍YIʅʇ{GTTDPP9"#tC:BJ?JK2Ks,ެMfݜQu*O@Y5ZvV'uSEY{/V43`5` SU55F54}5e5ZnZIZ;h iӴ];jd0)RƈDZGgLH7\7WYISSoD_[jFDAn.F [ TXFFFƮ5ƷMp&Ld=&7LaS[D flY995sS3,ύ}n),Y)Y[Z[6XWX߶جiy3loymi l7v~5 Wc*3EXu>;9f8u)٩ip|uRK>+۵ w$C=,=D'<>z:zB| zý˽$4mdqX{5(ME  v,x``akbzl"ܢEX .m5Qq$3B>bID}HHiܨ5Qעգm11Zww|+'2^m}W_O\vF{37]oumkw~%gs_.@s/O x+᫱?|mnvD [N]y;GG~H0ڧ]_"Z[SDžl{@j4O˓jIjIѾ &DY`Y6S(h4>N|;Dc{ǿZtOR8>5??u9_z V>`iTXtXML:com.adobe.xmp 477 353 VzIDATx}uٓ9lqlr6^$tIËx: ApP ^', %ZՆl6F3fG!8lP*Y& HzDJ;iͰI޽PU@s~F}zU}яG?:vqAl OO~<nW^}^~eW +?<+6 |$ܼy3#FAŽCvo#7nܸ y y()?+-oߦuZ9q/WĽCvo#w饗^E2wd[J0j}_cFڷN~KJI1qn6>x Ɨt[Kv2?!3y0a|‹x`F_N`67Eot-o"VB-sqv5MI.\K.I1qN\3nzٙ_[s@KoqL׌!<|M*IL? I6Rdܹ#߹gy!4 ҟSK!);+o]rE{=4I{aI,}xb=;w~_]=9.o^͋oki}o|;o?!~}x5Q}ߐ Wl!Ήkĵ^ڬC/] W\#D~%Ayi*!yp]ow,>k;q݋/LOZ#"# E?*Mɷ"^qvTzgϞfd[õM6lW\#aiF g/&^qvdy}~U8_ҭVWݻ4#|3~Z3/fo~3?}f?H|켼>Vsk3zuN^ғ~uLɋе{\nzKN#jpwӛ mΏQ屏Se螧sx7})+w5ED,Z /s+?E!xjDzbM6 V`vTzb>;pxF\;3;^{M"輸w8L}_}*=ti}I'v||#o\/ȹ%*SgkRQ>Nm:>|n~N.9Ʒ.Д wNq>~EHDIBh tL8y0ؤ+ޗIW\;*扟tŵ?>\{v9]"輸w8?xMsҗ =?7= }3|swەxKOqЖ;{|o!> /瞧/Jϋ<>-uw\| liZ J]9Cq⬕678y|{qy~'G zs(}?Riys=Gs-D6g<"_ȋ˿`0ȱdO<@. ]H ]O"֭O`1t! H@ H҅@ ]@H.Hw;e/:W ݝ%ݵ)Dh+?P$„CiZ^SǗǏiu# LZ[*[R2HtĺԼCkixdbthJebZЖG4ux<9d-Cˇ?IHMRjB8:K4as4M9ԍ'&˴"DGMPay|Z pPHw8!;Y͠Uf3 L.8:I| i:֧Rs$8\#eIc|1Z`b^~(i* g q2߻F 28FB7S^9ljl*"(:vlnIife˱J2Oˢvha:EŐ>L7imE@t}4~#sNnl NLI&UAFO{gw4Z&k|ϔ>r:äk1fѯfbU<15f~)O>+CJi%k :T)X,:(2ѡ3/Km-KڤC i2%&SLj0}Tkv;+dOI3ւIW'hiu]vx,XReyL;Jߛ<|H$EhPòYؿlMyE1(/tyB|3\1hnJŜ{A^}Hm*L| u!Iwԡ!|s]8ts,&W9)U]^qWY{Q\uh79IpLcX#jX"e8yÊtʱXeny5$ك;eW4XMΖP7E bڢ C eh}r}TT>w\,' v|:r tNa9wZ^5^ |xⰛ{rӇu.Ǻo MтK#Ԡzt>qeͩ}0WC[2+¤DR#Y< HSzVVx*N @ Hp H׋Ӽ_wN S ]@t?m'/~8 @ Ȁ~B ҅@  ҅@  @ H@ nK=?sobv_σߡ+];2^^bsW|0_g^xC[B KQ]sSW ^>rf[PHj/ڨK_M>եZמkny'#]SLcR55X{9yOy/ɜPKguST^~]޿(eUĬ`ĹU9E}* fu9y8 Vݡs_W噛/\n_gx͛4mgYTyXryɋ2m՗iSGzQ٪#\lp>`wϽlyNlEZ|<^.;WҔ(sಈ6?eՖM˝ccdr:XN)פS܁j%wJ3gc)M]g.Nɫ_5ȩ"ݰ{rK fpS~*wk4{ E.1q"EkqX4e1d^y"=,Tu_'_^;Lr@,;,/ATnT[B 64mk18_xe~꤬5աԶ)\tkר|*<ִ>y9^٩tְ B!{p7g4);cG8Fk lyh;=ĤH́%c<"Yh̝ i&) XҚ`&]0̜FKknB9icDoM1E,dd.1ĚjC}͟ ~NY -.]'WLM֋=ޥWVpYnzvf֘m @@BH;u"i&^H RCRޢc첣tUyni& $Nxt_%]Hη?0!{̏^5X =ry"c4- G,.J鞥%+}1E_AG-*MxHWש{Ўq͟DTW,cK©xs48q4K.AJę4P$37`i5{ԬW[ʯM^%dm֖s+uB;ҹUgiM3s+IC7o 7x>6i}QN'!M=s/:.s4 Lt,5x6f|Mi |xͳpu$~e޺uQ} bi}y[C5^yLR-rfl~WiWܧ]W :ǚ$м]s_As?7WHV̕VҿED]mMGh)Rc^|̔I|5Q<ߢ=l6j&c5(7ek`4h6i+0vv,i鵁6wݷv-!Hw=RWK"6q1_Iw־=f\Y-\cŖ(t [y=l#[Iw%2}c7_Ge@ .H@ .@ ]@@ ]@@tAt!B B ҅@  ҅@  @ H@ .H@ .@ ].@ ]@@tAt!t!B ҅@  ҅@  @ H @ H@ .@ ].@ ]@@ ]@@t!t!B B ҅@  @ H @ H@ .H@ .@ ]@@ ]@@tAt!B B ҅@  ҅@  @ H@ .H@ .@ ].@ ]@@ttƶ.[H@ ] ]@}_N׮]VE/^g} .8WNӷ信_!5;Pb?sכ(((((+HċH/_7o‰*r@Oݢw̹x>׃-w[zb}CDDDEYA FpW\Apy{u$oz|q[E_QFQV.%\tIL xr]-J2~[oxsEȏ>DQ l 7@9rb#L-zo[}"zOH3wduw2Uj:žթR*S]/FYl@Z7"Ν:Kts#Sϭ[o-?ݦw>qvy/~pk!#NvEEÍFV5QH7n=9uO>|~4Ɔ٢B&Cj9֪()nFes #<0VRP%sIl| [I0-mǫ|E X]ȡYY.&hf~ =-bɯ}6Ϯ_t.ѷ_[@t(k:Mp?toӴŁ(뮓np:؆dM=Ap Jty2Hώ} ?fcפ(g}c腑u% BL tז% O3i{58L $F=ŕZ1 Z:ay㣷-[M?4]zg;q{i;Mh[T)jijYQN3ET=f9o*QT*IJƨ43"%\<]R 1O5_kU\uqWuNKtz~4JTV4֊)ĒlNNYm4q]4EMcT~LTCPtJ>峢eZbR%AO*O-KjeJ"=bC U-|0&MrSMQ=m O׌tĮI-;17IIJMsGgɴXzui[96MG'8i:d;td1MGSĴ>L.8S}~_yxqmZg=ݽK/wmr}*'&R4P}ɡY/}f}@_XfWtؤav5#+mBL.RTըR$XMQ{$1&@]zZJ^bJX*GzRm*1 Sz 3W%k T&GI|!4OWcq9_I7ܗj %6lrW̟*3]՚Tʈ0\~$S4{5gi3R4ʶ>6Zi3|Č$E7oڊ.?ITYյ0 Wfx0w~J3'inh>C;?@LߑǏ0.o>%iҴ/jS] {| &Ch} ]'FNLަv͚T+PU#ѢjDb'6tGJJra4,UVrDܪjPTB!Ox!=M5 kre^߶Ia߸\=kQEaXf9:4??OhA[+ż)ݝ:,?qM%#8D}tCsJ fҝiJtWm\o3w~ 3ou8g;@7OYO3USUZH3WK5-Be5,ͷkijӳ8fID4tIz{[xAOÙNP5"%x6U|WC'rUҵ %#ҵR&rC-T,G/~-_[T(kJҵIuجQfˬ56U,^b,DHzMŤzת2K?Ǭ֗xZS)5yB"]qa,NKs[?b2u!cd7iiAK3Oܡ/u=y.#ϋ[k;Lz4Jܒ:(aka5!UMktm3W\I옘2B+"+2&`&6 k=6E ^PԻ1Thrl\3EbL&b| >gA N,67{I{,4lBRYh­e5h;*OIjNYLY يZf=?olY[/w&쇜g#sk/ZfVG< Q,J"݉E*D~oݕ_Koq|ڿwvnКVP?4>Ls˫!}p_! Su>ZHLjl==2N:Lzux֎׃~{ 1ϙe'X hX[ c}. l]}#qv$M=篧p2X}̶={@ {*wwgo J5}߇tT. ߾7 $p:'r'K?AXm:o_;v{>DQ l KKKtEH~\CDEYApui2!z9 L+Lj옰}яN.CDDDEYA 3.\/}Kx N< S->=y{CgzeeeeeFwHNl1`C|[%ob F { ]H.tJ.tH@}K=waZm)cIox9,Uj-s?իjPޅgݦ/N-+WmZ/6f 5oۈ|4-;Qk4(pŢHֱX1()nng *&8O wZR{YD WpU+XbPN;:\O4r\g<;.ev3 iz:JZwW^/ͦ鋞FIFR^s|>U!ޝm ]Jfg&Ÿ~ ]A ـJ>ҍ)k8v:U<ũaUnqzDz%SS~59ަ^:\Wvӥ\,^omkf~ww%  g9h4m.E  fg6 s7x H>^ +m!hNd̺&BlY6VJ;Ǣ8Ԣ*s͑eK&2F?d'a?7-&bU*&n ٪KS#,F)~i.bT[ICE:ϖ5U?Zb$sLs]޾ý?FF*لT6I5Mֹ Dt^Ǯ2TJr={hjIѽVLQ<}Oc yZjVj9KM亲()ۆC߶h=Ϗ d`|oL:]RWOH#>lكiQN'|FKh<0лRݴ2gtxhOt(0x[R(hklzJBjQV;v3uvɧ@e==anJ-.u[5Mɩ2$ߎhh.5V!u)I:;Z/:h)mzʔrǃ_& += c> YPA][9a)J~;-d&*E{~V;]~1M[8Fm+ƥ&נFAf"qo`Y"a詑lH>d#VL7&l$Qaj7Շ:Mts+^Gk HTaxD/PPԒT35&s&]&Mf=U*4r)Qaݴ:(h2BQlo[YTOKyy-tS4dE+X:y7M9JW=ҍ嚖5n[j36 (W,QT2TiXI碖ƊxN ͏t.v* )4h5G=z$URRJFzӟ܆jk=#LZ(UxQRN钮 ysjtLUKVj]]ʳB͉׻㐮_>']2u몣epKzXZcWWd^j5nh}ʥ|V1Ki&ގR 23+Q6Iۆs*nGѮAj6ncj~kC.E%9p hUoSN9`ch.-A(KM-Fuڢ b`fTS%/UZ"wdI;Q SBT.P=YOϕea͌}w?8HWroq6FI㽛ʺr vtY4%F-{}FōT,G#4-<^97ݥT,L/ȋEM-d׼̠Ҕ9/43K$wLK]*m4Ūl|u$L_ɋzruIB9Sv\MS#Œl2i`I~~j}Ff^l|roW!Kle!%g5j-LҲ֌QQ owQa;N}֚ lMx,Z}O.Ƶe`ȺPFmAHDGіpf_vNȁ=^- wؿ{FFZ5^л;ockw..Jv"]SWHTU+u3bTmsB~Z힫w椂'XW#m;{*W*] W4l@V+[m!%\> D]# &v$/׶uk?~ 76Y{]ׁtZH9BݻlNES9v/x#_>}gp`N-ɨc]]pW;4!M ) AuL+A5OPL'ِw08ň)fѦ6x6FDHzz{ WO ?xBs?HqQ7.7`IL;" {;^<#\it$l{ӣ=ݐtmm 2\cgHWxq*?Q}VN'걝entmdDaqnys8s:￯LxZ< J3(X"k9t 6')); 'eLP]W~Rҳ9ֿl5cXܢQQˉz^I>m=u wjF9V5qOޕ gio5BP-C@oj׽$i$,g6}Št (|*CD_ 0}Y." ^: I>3+V][c,i }[j$ 㒮)  Ef?&>A}_28gIWzqιd<&>G:vPuC)%ǜ 0t~ъ 꺔F}ۅ o)kMxbC ɋgr}n >ڝc3T}N%zqo~%;g}勋\ jm5BP /CPg  p^Hkq\ɑW!`pp=֚pI:y"lM:!5߆U8NqZ=*ک: fga d.GWgוh V8?,gssOt`Iw;IIubrSN^x8. Ǜ㖛ƾ f_~F9s.]E9wÎrm2^ĜJ0i_m 4^nO>Ig(>䀶L K^o390 `A(]碬}^ q6/oɸb5jSNƕ^v3(5ςrfw뒓t;6>y5etd=PWu_hA}PpUKays[r24o::wWHm] G™uVV+j~y740"8eÖcBTڸ7N o'_\m8琮 BjPB8Gnf`1jgr NH0`=f{Ta@$y|r[~zG) nH,IlJc+k^MHQ%slE 1GOk2e+9! RTk02_lɨ/ȑn^*;$i|O3PPC"|޺.B.Q3qP˭eҌ++TTɧ<Ӌ T-OS+i=rۤ*Ss.LSP%ӰK@N"rGF#'NV/|$dZ2q\-2< \ԱeQ"h& 9o}UkR)#HHsȪ42u|{μ,:rQՉ7Mua ΊKtFARI:onHmqfgAj_ݪ"\dTiv2dk9)˳I#3P>s}a+OiD-s#EGUh9D*l-dE>u{gp29*2[p4EqjX^NE[ER|ntEejAZA--ed[hնӻ٦JɸSRGIWAEe^ΗŤO}Fy>&Z%DJs5< &]qmR̾ {hS6l.L1NMilZ@IRZH9Z~M꥜5'(|C,pci8ϷT/fxfȹh,AnAIkqMPNA)\󲨛ZVY'seU9 n_6ucjlytnVZj:]AE ˬp g͋FLH=d"%jt>3)ؤdEFd0=}vݙv;^녔 QVte *MluG¯zǝwQwWAiObnH+g嫘).xU\gˮf1vI+?U9iv^aniJi| s%cu h 4-z ȣ1\/sD=SݿzYi>He(ZA .tH@t H@ ]@.t{vkc]3ڤ=_Q=KN7<;Q2;lh3FZ3 f{t8{B49g(R\6vt]Jf'p(eMw N",ejSF^F6Nը\i |urIP%e]H37S͵^J6J:uJ8;(qLtkH҉jdpgiv;@X-5dGW7,w/VRS6װ]Q$\g)r4Sk4.iBt3$]2eRFGnO/֒7rۧ9m`Qn,oO=;Rd:<k6)ΝSjy^֑璑5 ׊4 X2KD$34nXZ<[4IJDw *لsM"[qU9I4کVLk0@:]*fPl4ޗ׆_^Y)s4fym"sRJJ/zg\]D)]Sy4[UJ8xҼmR))Ȣy$g: (O%eb9(gBe"_d6XjM#[I/FF'0Zwk.č2O7X;7;P)˚bZud:Mqg[7;eWkU'DKBL(3LWtKNNlb,w.h&k,%f",# ۠*U$]i(tݚ̓]ɫbM٦8xnP(봨m2d32.Ҳ iH1 ;vG֋0'2I]IqjnZz[|DT h=*'u@c5mmˉnrn~[tbpI+ Lz%IoмeYB.YvtZ/n'?p_ p4T.(JM[v,  dEPMȇt"Oä;*Cyj%w-k"yc*MI7_BAjQYhW| ~m&&].oGzʚ,KkO˵QGh " Y_y~lAQ]&^*$'Hy#]T֨VVY_TM+mNF=Dbj[t;NEj5R| Pul,Z-]tN5倲c1Ҽ*5zlB \tԢ0RNH#&Ǝ2*VrZ-:6rQ=y͘$}/oEl1n * t7g/.;X{ 8RKAu񋎡s ƁF{x|7}3՞Y2٫` x!m0`X_cK$<p aif eI6ٽp& >}>;AsBM7w)AՁ *ި+8u2@B~,@ W4vӔ$2J'ZQtI9QeK]BHodx!DBKk?ȓ97Le2^C]I-z`!X,Id߶op]rλkn(PlݬMf=~oQ`ʫO;"4^HHHiJdM;۞qYި\ v?H3V9s@|C66+q->SmrQ ?qU1 * DTP 2DRE֤Ԡ ][vjۓ$-v{g9ס@.'/մ74֒6XA=;v |-. +@ݮ{TZǠj(וj#N$͂m+lYYN jdI83/ sA'K "ҍV{N=/Q26d@F"Ynʴ[ow}x7 n P>dA~Oz=@xTL{UK +dTeuh#ilB;𜨺>FX#܊cBSdd"~s=]-ez"(B *`Y~M!]{HWu:'B^i,E+pe*-:``nCuCZJV,!. ס Phn1\??q.CWѾ ǰ㦳tEfV [ !9G-OhT@4ǛX]&h+2sSw?\T}NϗA9@ѤT$*|;)t};"Mc|ҽJN$V21an ҵ P+eN<5@lˤ2G}u= &؝4qۑB-8x8e> }V6teI.pޠ)wtl+5!OBX {}f޵"25U {bU2ғWW߅ yX?2xFWtymCw} n PET{_ tN{|8[/60Nje67ʠr56Hwc+&@d|2a‰4U^Yƞ l ;rfp;_nڇ&&vB H9JPhЅjma{ 4ջc<z7".p!SYzJm=XⴲYO:<ךS0yEjIQ.GNH^0yZ#γjKٜfK9BcaiB`l,,uoC+=DX 0gsA F덮ρCn+J;wMttyϜg`nU Ҩ҂֞Wt)IaN]w+#[P5c Nе$!Hgy?i>ܓ)597֗*0'LɄmŊ6{, ҉8*0(`=Ebs{23%'Ot;vo$WppÜ{5WU.9w:;  }+A:z/@"L:oE{ F_JDždxGiCUwr Z"UL2}_i_p gA $|)(䎭SVs |Hw0҉t;kB3+ ="Hm`՟:UZ&/8 @a?G${1|MH7>H{@#e4 WęD뒭'F(;E+]՚Cdk }ۃ$$ZnЏt}n ~|Zw?LAQ"!Sam+52Ogk)SomJm.Kz.x@3{S`'$2/JrհwA_P'rse9ݽ~{H71'A}.{uN7nެV=uewCvH>C﴾/HԂbWIvABTڳh ̋ǟt}jRτoAB >H47l|#NVEZ=$AcEKZvp'8;kxug#Xd=z5*V97@CYgr~ EOj?c;  ] ЪW޹=SSk**45NKћfAZs2s:Gcx |̽7Bum۔˼ν/ȤR+sK{^4 yS\Z 6U*phfزf=,ޣ>nʽ;ńo4 kG g1{p| )QjԺ,-&ֶ`rktg5O3O8Jot}̽©=;O"JUAIvI'-W&>דb߭pk('jV4?ΫkB8(γc۬P%n6S`oQgA2SiU04OUEY=f'` Na[U7C,]{yfV^"p6B5VP{&9Fff)lg{*mRF Q#$ M$) ʤp^%vj_Î AL. D)XPD˚$;٩KAV$cъ 1xrι+:AIHW#ImJo K}ۖ]M, x0 NtGJi\yjž`پ~@gEvΑ+PjkíҀIk7zGUԧ^[P{TM+szSL[Z@YΔ2/8<ܨr4)R 뒮 k;nlcxN7*ųFR!5Z]w߭X?/CĔ}Mץi\lEhT>ӫ58r$JSVe׏j95<1 / 5P w.=:h2ިr?N/3vʍf<+U9'䝽wӫ%_Pe NHX *Yo{Zm{eoe1E hؘvt x  е ٔv/ T3͡ϱ~[u,8$TW|AsWa6MtȦX-IZ>mꔉ -],ҝUq ~* G99}bdz}]gwIjk!2DY-k5T=т5=XījJ[nIW ܖ0rn{cªlnd-ٞk(.Kmƚ'dzxUXkrR9( ,gs" 4|tI<]IIrmj74jix~ڮqHezN&Fh9 IWV`ktEқ]$DQyϳM$AFbNVTq7|24 jO1@O9Ֆ9yyzvXpD_xŴ\ɳ,䒯}̲>.|qZ7`@,qG Юd&Oi"OzJ7"wl5#&՜ ֐" i Av-nEUycjЖ}yT;ȅKfbʭg^V%YD4޸5WM)F6aqiԥA>pa]t:܉m00L߅/vl3?{sԋ]f1GɘL.Ś)yk9 %KN+kz1hp{vVH"d,THЩ[}"j鍧y\k:[EG( 8ʫ,!AťKIBD}ӯ?#zۉ[O2NZxr'="2&=vb~k ]B4GD}?-:dB~v9mD}y5uj=۠ƹ_ r{+{Ν>Z_+7ium}r\&cuWOXEo:Ճ<  ;y`S]V¬>vt(hXSH6N©ֺt_%c:Fl+{fs[HoiE"n.&֟(ï~᣷>N_lBz 'չ_=y^٪ |߼y)Y>{k^.0$H[qx6Sٮ"z׭4(`vnX8H=6DM-/S阶sZ7>zۂowmnKk*GL m8we_N?]5/)]"P;%4<]ix39V#R|u\;Q22Of)/ιJEHʋVK3dǧ{̓ J}쒼ofB-\}i̺qBfڪI'2i3Zަ㻃m:|%?, \&w0/|ߢ>vw!շ*rWs&WiY=]\ :Gړ.y{  6&\-PvFhGRT3-g~5& Nä%uts{msвUkLeA\%jtNZ=/׍%&Y}O2Yu:@8+W\seaĴH;îбaZdfLSs⬵Yeƞfif$jqNަ_,2>ug&ι*ҥ[we;;6n0 ;]]AJ\HK>ʹ\H$yetMAeg*TjTg6.aB>#H+u]i1*<Ү ڳMZs-XJ+u=t<\7˒L9&ݬuѾ Oҝ‛ee9/255COtYt{BΟyNoE&ݿO?ɘ;'Ƕ3-}aÊh/5Bɭ𿿶pW͇[Or{*c׷ uYss甩YJ2cl6+]MI|;Qٺ+dTZI/)rъ$.jiѣM󼰘=yεٌ=dSn |qyjS.bPk1H].T+ S9TEZI4/sG*ٺASHCbMy|]J_|ʽܪ?@^zp&Ǟr{ЊފIu=Ξku9 ^de[<4b#k_5*bXb}m6b}rN&4TwOk~Cwv S@i< " A{~wo}n߿vyf?@  7?;woO}H[{?.O}oQ ] BuiQ~w޳N?q'$u>YK{A]x+p=𫧷鯼.{0*ߏ@A+!Y ] Ҥ*BzlEEE=𫧹 ?Q1 YȪrػIփ.!A.\/}KN<)OXe #<=zGO?F$?z؎w }Mwg-rr-i=.t H@]GE qܼy*vo#G;ۈ/lU6AJaFVIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.14/0000755000175000017500000000000013752535025021035 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.14/computing.png0000644000175000017500000021474213502206677023563 0ustar noahfxnoahfxPNG  IHDR 3 %iCCPICC ProfileHT/$@D)ҥХ 6BH(1$;ZPbî,?,(bʛ$y/wܹf93plTQ$6ğ5!9Ez@`J~11:@+vr_?W\i<)7ApXM!a@Kl& %9M ȩi$Uy\|n 'Anpx?C3 ddgڰO'c((93roɖ a M ,[ִp9 EECք|US@0h+ 5LPY(;*bP. fCGyxX'; 1GKnS,JIgl*')DQU!ߕfŅ}asp: [S"gH c .p ǽgz g}P}ጿrŞRFP|)V?Fjǟ'XgJ<ܭ,;z_a*ty.8PMǁ{'0}ә~%Gڹ2IR@jKpﲂ9 |A $)N%`: "PVl gE;pt:@t#E7 B"X$IE2"Cf# @"ar@n!n5 P Cxt2"t9ZVzzv/> `*307,RtLŊ2 ۃ5 ։`q"Y\xs2߁+J 0AN("NoH$2DW%3ˈ{ljG>K%yIRttE@V!\H.#$%_&?%S)J4GIYA4Q.Q(T %ՋOͤ.SPOSRߨWW)W٧rNG&͆@DіjhihotݗBϣ/O?2TGUyT+UU/TMQ+P+S;vIGnQ^~XzCA#Z#GcN4IAb` SFXȨffti,ZZ%Zڴz5j'jЮ>ĘL63yiKGqy{::|b:t>tttW6lMۤwZgHϑܑ#ߦߪg`hb 6XopҠǐikiaHhs6ˏ*gbˌX$5gJ5u3M7]cbkfdi6۬9\`{ K$ ,u,ٖuwV>VVUVWnYmPgM%[VhѶca(ѨQ7hv~vvuvF3GG.01)cV9;櫽}}M0B&׎6\JǫNt`yNNڎ439ysWnW3T 7ܴbܖs'sovoO;,ϝY㏫ċիӛŻ؇Sԗ_.C<Cۂ4*g8 9J ]zmkٽaasN+FDH""ȰՑẉDQ :^eLncW;;l#njθw+$X%Z'%&O L*M0fœ ɍ)Ĕ)}&5yRѤ-'Ϙ|~ޔ)GML=JHMJݙͩ6r/x5n4+4YWnL# V_efn|U57sX)f8mƴHܙ둻6W..EyZ*"{_az34ffδtӂf᳸Zf^09["s3h^;Pd-о¤M _藐_T$E7{.޼_"\Ҷi_yJKJ>/.ï,O_޶eŦĕWQQZPhu55kޮ|زd:#כ_sZ nx&M{6l.ipͭ![,ʶo{RX}7jm/FTӹ#vǩZڝ;Wԡu]vݸnֽ̽%>پ翧~}n4?Pq=R?A٘q8pKgӡ?FQl\yDȊԣ+8w\|DƉG-S[pN>w&ɳ~g:| n.\oun=\/^jlwooqWʾzZԵ oޘt&[ٷ^οg]{߯vtya;^<>ܵ IS5ww?EO_mxi߾Nz%y5z75oǾm.]v|txSҧ?>5݁1GQ0t^@Ogvw3 bM!.0xF9d|ˏurn"MwrT HM| oR \P.;k9Ro KpTdh pHYs%%IR$iTXtXML:com.adobe.xmp 856 1260 1 +4@IDATx`^H!Em({؟{y֧O} i XHB$lwΝ$솄H\؝;{緳=tzQvkQJ! B@! B@l*XRdZBB@! B@! B6j+`Yy`֦)#B@! B@! @LL_;=Z,kT2B@! B@! @C8n7m#B@! B@! L 66JC< B@! B@! 8|Xǹ+ҼB@! B@! $6*dM! B@! B@4>)2u}8#! B@! B@@,9 B@! B@! 5Kvvhбq#ܫWY@QTt4,H[huF6 (Xo>U%֔kvJvu4wÞlZbm1htp|w[ ! B@! BLo߾['74Bxx XZE.@a1*zA ҾoGmݙJbBy=?7JJ87q ;! Fh(؈7+بV奥paX@\nF ! B@! B޽{T**D D@-XGp p 'L͆*)mN BHl$:QcpK;D#Kl zPD?OƉNwks9.nwOޅB@! B@!pN>:>@ܯ(2+-^CyX`Xc ݫl .7-:ԿS!er9ak֫ G뿛g።<Ϣ*!Ӌ[iO(*Cӿ#?2)B@sӷWOkaI667K8}h'?<%72| ֶтo$ ! B 4]Aws|D7K"dV,U3tv2zXVl"lxszm?Flr6Q}0b@1#y*>9|f[!U~wsg.l{nҲ)B@| d`} Z.ĝFau׮I%Nݵ2 cZwb< K/ƥ Vz JX;;B\!$>@Ƌ&b^G(D=A38YB@! N:6%۶fx aUj26Do\"OVϞi՝Mo &qO./%_>P?׍IFrĝB1:Y,fgN+oT3}􏽱Xb~lG)HXl ! G@Ÿk4|k-J=,22wߊ=k{QB3CS C1P6]g>C[E#u[ϞƎCj zJ!63;$G! 8yhQydAʦfC+{ĨC6lT=jf%,D3Mr{d~TSYfCY;CPr "^q^>tB6% ! NJ@9~ WɯDQ8)uŚ_ůZ+~ZfV~3s[ۮ{kfa5kxkt ӿK]G;Z c VZ!VZo7FB@! [y=@} ^Xa# дd ! &!m߮FWIғETo>-_%?[ahe }EU~hex9ajQƍnÅT;W^bwO|ׯjڼtoS=u ŸMnOk9[lVd^o/Bk/v_H[Wcz믿y[Z?pli}@)bǎ$=聯c{Ny_LӛN͔k^-ǷU,C|H6~@+x1Su! '{`F|DC1,do.j^~ާV˧M5l+!JOo#Bj-sȖzݿZ(ńB@*t?Qv7> ^kvnޅ2oa`ڡ􁍹sCLЯ? ;S١IO}aW)-C9qäFQ?c`ԍ/m]p;F"ydF!l1Z՚{"\\CJ{m_ZqԁZ_(S 5.B"Poh0n\v;Gj_w䤞B@!*qR# ]:],bZT/;-Q߾>K^'E@ۣO4ҋ֝S -7 |8%OVЎYa2ˋj}l-FA}tm("[~&0R! h}FEDHOiD6zڢTTnx_q&ۮo!0JEnԹ 6ٶ$! BpTu~)}^M9qc` wvfjڦe\dBÌ*K[ r:X opܭFPqfUS~){KnD淈d ! M@=B 4D'au%ƴh*/$W.@pF'q9 ^xzEh #$dJ72Mv$B@! @~w{7|~U1є#S _3݉wvR G:Iz@Ɔnv} cra:I5|*{ޕ@<̦|A|eɊB@!p锎 <"%2Wf^uVdYY)<}wRs JJ7!**\k]7TZ72"Φ y% NcvϨSYQԎt\&TTZlKK7|B@! I׫-m!LW}9vt}cp?}Z&TW?FDtM Gi?t^DHӚvmsy_}INkx= ! 'z?0_?MKEJ2uqT'T7"[lm7%FnۭV>* hI&ۮo8xަֹ>Vݚ-ɶ% ! M}\\wZIԽG$OKVDf8=1 ^=`lR| f ]p8e˰-KB@!p"m*}c; *g/VH42~MI;D MuogZbÓ8M0bBq{Z߸IUQ!W;M]ulaq  7%h7j wRU_Aɶ?s.B@ԎHyU06 WPbҚXtQ8L=?}??Go(w"|J5oX滛Tz=Q%?Bqޟ̹7X/cwz'j?0RU \`:sȇN N]9᥿W[m:]! 8[bAj:F/ * t]1Ԣr-*R6!Y;"p._ZTz7iuay)@{x9uA;dLk1+#BW3hiWWҧ=I! BKB&ۛjx_ӎjSAu[F5l:? i{6>w .|x<7zbHK>1nR]_/#e n*˄gc4q62j6D bчј}HޡtcB@! lS:>yx/apjyDԱQnH.f?ũRg/S{i;LQZC5A^@B%^cBA֊>ܿ?W~Z]yB@! N<76؏T/@Eeòj Ebe`9jfӻu6 ?C M%'s8ђ=Xs>,j +/G}j>ǞWⰳqEӿ˖B@!pro_0w؄Xlc%vK<4ҊXD˃B@EB2N ~_|VA(^=Ep7 TG R VV#Wv{ vVpd-~PD?O':+"~\|OޅB@! B@!phpxwD_4Bx8_7r >*:6 7m[ A;>*4KFd`j3*z"ˍR)Ddm.ن}V!8!a$RK''sSKI/ÐŅyl`*B@! B@!P LïVĻ};,$bY7o%,ʀ7* ޤft/WvfJW7#(5E417Ȣ Ûo!(cX737c}5֕d`#1h MAȮOv4RZ! B@! GFq#B@! B@! lW5! B@! B@!phA_ҲB@! B@! x`ՈGv ! B@! B@ou?i_! B@! BF"`ՈGv ! B@! B@o2xҾB@! B@! @FQfb-Y [\Hld)B@!  V]T/pBugVUm}"`#6,F,1s +ޅb],d5}`\:O8V5b#6 W>޼&d#ZbDXW_$iO*B@! L u/ZNjQFdeKA %R GJTq(+AX a7oV[MMssJ\bg\"8/_~["LJHG/B=jJ[,^wJj3Jh?\{bNX/'rߑ/Oa񔙾ӟqJ;l⧆d54L}{2PeH̊tkC7wULy^ъYy-x<g%k yb@XKd۪-{)yS g~]{6#k!蚍z Tx]PkztGd>!q:B:T0P+%LY+!;-C]~^:,^{e#)1ÆA\ĸ33u! B@! O u9{_u/0idy2߾ڮ(Yp.WՌ4Fà yBX |%,nMi }>M|U D>O)I^8K͞W1}o"M% XXɛK3psO:7HW)"|p`gfd˲ 83~F'wB2r"ZǛGyg*} qWw%<[?:h*+jJy_ђW / U<<'˯eW` */??\<<-oB@! B@(`րr6hfwJV5uЂK/͛c՟YʛE:$kZ$7z)1/9ߗ*o8m?yh1JJQDgknYv VP\b Sزz5eWcbk 6r ^,^yȃK?A ?Vd)OY7߈KX,\qZoɬ;O,xuKa@ȋ(Oo2AXEl8ܰe>GqaU橃nb{\F-y2"dz㙗RuoolKaL!c*Y*h AZ Wd523.> Vx)OWt%."ъY}U %b""Kܱt7dVXRtڒ<(sl^5i=ಅ*\|1ɩ|*)O4i.+B@! B@@ X* KNW0VA6+ ||Ը-hDUXRKJx)@ł az97 YaԮMB+m%Jm'ݒEܗ4wO޼D*bZAS.}E]S9;Fkpl{,oUXor.y9?bYlMc}_Q*"$yMy𵂿f8Uк%mF/;H43qݻs‘OMuEWBxEo>2X*V=Yyf_ΈX'Upr-}|$C Av;#"0@IB@! B@(`5_ȍ~D7KBQ#ՠOA~K(˜JTѦz,s̫`F3`iWA^bQq1 H@LL1\[@O CyQI)H ;Ƈ { h1Ǝq'9! B@!pT15Px5M q*O4o܆6ht( |K<:;U2 NϮ(,^76%^uIi3QOe~ٗī0[ҒīP%`#$$`/ oYQ7܌=!aWXn}EX,8&և~UgÛDUŀ aG}܆E_~}ѷJw9x7Ы@XSR{Q4kJ 7{iǧ z=qSƸɓ..¾ys"{hJ@żpsQ){xEjވqv(%BJ9W|dZ6.zEDSkoU:b9l.O?2+kc:MiuygQ{r"NӔqrFǡʻࠩKzoQsoDA\s؀]r4W^UQRJ^m~ivSz, !SKD&-΢׿>Z+c< tq xS+婘q/ԡO~IB@! B&P:ciUCL!9Ⱥūǡ*(@U+Yw*QK~KX\x>61l~+,^Uz^aD^*YEG^w,؏n]1@U '+"AAAC5MMEbcgQ@BKضbXېO/OIlU&XeJLj~e ,6Վi^gekQFN5WQi/Oz&r4UQ•VBwK Xȑ}+7fbAS ]u2@ {bv;< ؓHOljq@tS{WVPi׌/AMQv8,$ZQ$FJNYq{6wҼk {^>,~$cuhIp^ գndb#k.)ġڈEX5*B@! BFo3 h4xjpe* Ad}g2C+kK}PSv*P!( aEZcoX=і,^J~I%T׫V`i92VNoa~DRgC hHL܅g BjV&Wˊ7AiZv")?<B{qtn`$Rp_+<$J+ъh%jum|h\u e{yHKZCyx>ÔoLCg R|S6T1~Ew|_v6Ѓ6JO%C˵n>N%^D##ӔȞx`Sxi>X}IdùOǣǬYFu{H7/:x_ 4uHsq~41z~Zf f]wf~Tڱ<6,0Lh1ݻp*:fw`Ĺ7{nDS_(x\:Ww+z($0AbDHx^y hиe5zb (جqI% !GSu,XKIMR.jyc]7rXO?D!8>]lض [7U۶q\]yߌ˞3'c86_WŊg.G;5m?4g~z+ ZYgkոտT=v6xz> Ó{"B@! B@@HXcՄ+5xAt|!QשGnf~(DjZ!`)  cϽhm[kz"a -r3u*l7t ԏgVH?VfsYC,o? mMfG>7&*a{^7͂ރ&G47:x1{ekɓAO$j⣄g^KM)ն}PS`鈥va}(vmcAm}]Bj[mjR"R]IIًݍ_<7/zg鯔`ϵà,ՔX SQiCsWanSɗ{'>BoM3S&+#˷׏UP|0⹭o3A SasWQ%C 7%~^ lG{)i4gb (MΖjjUĖ$\q!J M2GV(oUŇ+^*+>Hծ5'@|X\}eѯ,ULfZҠ [WfH>灥}N@(R{4M)gFb߆1(OVҺI |PB@>htWKǮ9*٪, lm\,WEEphOKQ1Ti>R;=ecYPJhe*S]n'~ { 'o2&>$: ȣW@$1'݀ NP /y&fiqx3}[r`h7xt1n_=f.'J\eJ:m%߳/W,>=.>}+# =vAnOYU~pZ],)l[/w"s8Kh@F^nVLqo#k3i.VӺaPx؛3߹K=kS] ~||sbtl#qۄҩ5-ՁzGt~>$4<0(YyaQN6ݒ(NU8y6q2BN+/ʁ9{h'Ֆ}}8Һ 8cTq{h͓O!R^X o+_Qߟu&CNi@6/0o,{uJB8T2ُ% >vI<۴g$5Kdg{TyU}~4XP鬜HV*i }m8M}bgqO 1wb|ƻ$|^3|_ =df@ޅB@! @=a?R"NÆ *u\ |[5go|M=.hDSWE2ʩz@UɰL:}ѫF^ #{O<++Q- =t&CB)-gWZz1hH}I941Pפ{RXkH 8cJ<^"oOWgXEN_ YJHﰊł6u2.&C{uߚx`ĿR:eEeXVHe q<ZZT2s84AUA46Ĭ e[d-8( .n@bєNLwn= g5|l>r RƂ_ϏcVWdMD7}q`"jxyy9yFгGGAn, \ׁpM$B@! @@G .]"#J 6E,SGKG,Д1.^ӑymA1HVU=^: v;wSOP|Go*%/bOOV}^/aڋ>-zL Q86/̭4=j-rYX/M9ŕz\>ZI QsiWoVvtx( W1'8;Gk5ȋDMwٳ-RH"\1;iLf"wc+|Fi^#u^D,X=iS;c &@\$ ?Znx;|/0 T\/LG,"1d XzrXC\F mc).c{O]S7_mP9kxLHګMC#>L\U _^P5ǡW:t>irawVD6j/r/j'&+m=[B@! BU:t(~'U*!A4,tM/V-܁r?1&% JDE-:7pVbyIF %D:XG/zYb1-؋+FXO]b3EמޓwB)ԱCM턒Uז,`~_ &4U !EMdz{\:F,Nimy+Zrm:uE d2Dߘ R!BpaITY:4뉔 bص$4mWL j8yJ•G|pu\B\wBtJv3C(42'&0K#5չ3k Aq-)nƋW>|pN5sq 7|Po&jOn[B@! @m ̿֘QnȐ!j>6 [RM"pǣNy`q!RjKzMs&B"S+PU zy1fH8׮®}Tھ5׶ZN}NW ]z 3j"L0 ,d4Uig ;ju ;gpѾYX:1z &i5=m(Jhu[6M5W{ʓ>w6  Q Yc6燇,yQTp 1qkZ$Ɛ76_fV^h[W(ҫ{iK;hӰml7e0&މM œM(\/4F|0]z4gMxK1;+={@ZD%CVpݪQpxͯD'v|4E}(t 摕Jj,a\4U <V0uJx4a ^xxgF|q4+@TMȻB@! V6|mpEN>$[ٳv>[oWl妿_(b^n b - k˶]x~WJ=%arYu(7J$S*%1'rNCD ûŽn]>W~'}{*ҹO<̞bЁ`z1_'=ke lJdK#2G_U +F>kshS5,o>/ +j᠝Lkb|"q91c,=E wpxI,\Q@vz<:XT[viW*Ǵ+Ey,\;>voAKH+=/)*ekԘ"|b,L9!A_?ibmByHmc)/Y4b'""1ɩ\hoT)m7 CF \ܹ.z* DRy7qyx%%%#&!DJ8X3'4uöy? X~=34&2X<$NBB@! 82'H͏BUxȽy:Vg@*Ee\ ]TN{WG؎6ҭ1U'n?UZ| f/,f/D*lwɑw0vnڀ_ݗ^))h6&5񙏛Nf5-^35!KJ x\N ;6 Xr'ro+(e,uAE)C2D+sY_c(J,Nc"xl᱐Me=:Ȇ<Tfq+Pb3x sܧ@)Pui#mB@! %P2~q]/<<Z/*_/lk_iC%5-[iTBU© [OAlZGI?bչ;iXw? S;S_͞U G`e=Z&.yʏWBpw/>ﶵ:}{;o?Ә,PnUKZ j~W91rb͇O>PJadфwp]Vxr\׊u&v,cU{ ]۔)U w-z))f#S USS>ba`jVl`6PPWz#k}/$Um*:瞋~@3{}tΒ^j8UHF\-LKiDVzX텠VjEJmյ[QWx)61t%0bGLFJ6cMޜ,MVm5/WhF^u/L1MmۛWVŸ΅YҘT>s˥r @ @`q `-N%mYv:_z=]f*~Zxͅ_~y[HR-^ְlŪJӢSX6׳{bidLޥ_Z +|qY]-NtNMu ZU8*u {+voKXZ  S ~~ҺWV]NmS݅'nʪq薻w?oL3>|0rAt%V.eO'\G)}RE?JBZBwt9ox7iӢ[׮FVYO ,=/ܽts6 @%lkIsO}*QXiՒ֓7|b›{߽VatEkR HU({y8+ձɂ諅ކ|dVKLuQxY 6{nR[n_cԨQ-]6+.qi)``#4)X iVk1ݔ?wqNŹw<{㩄7<~&m󹩯g~nf|..^)f7֣kX{YX{’Ɵ^zŮ Ϧ S;og&棪R8nhk_eۅ? [)/Z׽$@ @BfXKTi ݺtw*>5'z_/Lݣ˜Hf5/Ή*Le,|M_oae-P3$S"XFooNͧr;5Z/0b^hi:C?o׾cǎI&n?կ6̪4-pAV)B X Ge5B_H;nV:+Es:x܈!{@o<އwV,=cyzOWU>ªa^m+'@ @ `/Ւ 1ԩbfaΘ߈] n}] @gϜuLśߩ0f^a$C9rFU-}<㤓N] HǺw^5Rۅn+{xJ21¿0hlN!ǴXq : FŊT75+ۀ:<0MMoM *YS-N,_`f:|ٓ@֮fnj.Uy(*\! ^޳WtZh~#@ @c `ul^}#ƕy64.b^= 47-մYPXO:Jl5UWk (AX5م';v)#}zV+:u*{>}dyٲd'J @ @:th-6}m6pD T@RݛmZ̙3- @ PF`F?ie|8ٳn'ԫW؛4iR%H|Jwy'  @o#G.$U=[O|w"=4|`^kvkf)$@ @ߓye+a#lAք4^+ۜ'fyٲd @ @5! Uݤ  z#4h<ѭ[s=A @ԖVm2ڋ.(_'= /0[ +zWCvaőZM*A @ԌVtUm74M[uUn!żtS") 7A/H՝r)ѿ2dH5^xz뭳Ve @ @5')5e9sĴiӚ4?ӼyEk__?R,M|bu׍8 N=ٳ"P @~hËZ{Cg}vWKi;" @ @5.` aw @ @]@{ @ @j\@;P  @ @. U= @ @5. U @ @zv @ @| @ @@ `{? @ @@ `xj> @ @꽇 @ @j5 @ PX @ PX5ށO @wzaG @q@'@ @ԻV#@ @ԸVw @ @]@{ @ @j\@;P  @ @. U= @ @5. U @ @zv @ @| @ @@ `{? @ @@ `xj> @ @꽇 @ @j5 @ PX @ PX5ށO @wzaG @q@'@ @ԻV#@ @ԸVw @ @]@{ @ @j\@;P  @ @. U= @ @5. U @ @zv @ @| @ @@ `{? @ @@ `xj> @ @꽇 @ @j5 @ PX @ PX5ށO @wzaG @q@'@ @ԻV#@ @ԸVw @ @]@{ @ @j\@;P  @ @. U= @ @5. U @ @zv @ @| @ @@ `{? @ @@ `xj> @ @꽇 @ @j5 @ PX @ PX5ށO @wzaG @q@'@ @ԻV#@ @ԸVw @ @]@{ @ @j\@;P  @ @. U= @ @5. U @ @zv @ @| @ @@ `{? @ @@ `xj> @ @꽇 @ @j5 @ PX @ PX5ށO @wzaG @q@'@ @ԻV#@ @ԸVw`5#F*/| 2$.䒘?bٳ/O}SJ+Enb5ֈ;?/v}N @ @S@:.[uyȑ#>}ƍc9& :-N?Çq=Xb-⣏>n-vu׸ M @ @eM@kYѣGwܹs\wu1a„,x5vׯ_~խKeSZ^z)|x7I?~|T @N엺kչ ,;,8 <J#̙S<Ɲwޙ>3cذaŢiYg[ne̛7/1 @ @@m `fTg̘{oC=IoݻwL6-&e̜93^wu[/˟;wn2  @ @ٖ>S\ٳgh{IeZzW)=MϚ5+N6)$ @5R Y_|1kk?ri$UZ=/wzk\xѷol!^x!8x㠃ʞPL )疾1"3fL{ @ @@ `\^j֭[Ǝ҈'x"6lX3{YM謔A @ԌVtU6wޑꪫM73fȞ8|&gzѩS,k|8ۿ{O~e$@ @jG@v[zgA+2⽤EO8lSNְ|1pa}UWw=>⡇*Oyq=dyiw @mio->8#[+hu}  @ @+魷V%}F%vl4X34K"@ @%ke'MTS=?ZoV  @ @j @ @; @ @& Um== @ @%X%v @ @M@zD{ @ @JJ8 @ @TV @ @`p!@ @6j!@ @(*C @ PmX#C @ P" Ua @ @G @ @D@ @ @@ `U[h @ @@V  @ @j @ @; @ @& Um== @ @%X%v @ @M@zD{ @ @JJ8 @ @TV @ @`p!@ @6j!@ @(*C @ PmX#C @ P" Ua @ @G @ @D@ @ @@ `U[h @ @@V  @ @j @ @; @ @& Um== @ @%X%v @ @M@zD{ @ @JJ8 @ @TV @ @`p!@ @6j!@ @(*C @ PmX#C @ P" Ua @ @G @ @D@ @ @@ `U[h @ @@V  @ @j @ @; @ @& Um== @ @%X%v @ @M@zD{ @ @JJ8 @ @T@jk ⭷ފF~®D @X #VN:5.6ic뭷O~vZk_җwiRV @ @:rK}q%J+TrYf>xckq뭷Œv @ @ U P6}ѩSw}|5D:Ҟ{_|q| _Y_olr  @ @@G `u~]^jfm\vev5*FMNA @ o˦5RZs5K.3s=zt6:fɱC9$;vlI @ @@G `ut;w.޿1gΜ,F\5L v|͆ٶ  @ @.Ph@%VXa_x dMO>%Ǻuwڵ$ @ @:mQV ;sI~Ƀ]krL @ @# H6ȑ##M+b7w1{,>4rk  @ @@G `u~]裏.,Liavmӟtìl;Ȃ[)% @ @Ij eV{7  ,(RpolrqO^z#<i @ @@5 `USoT-C#<2ua~7xcgy&~šna<O<1 * Vpc~7:wid؄ رc_~qǏź駟_~ylfGZ>K1mڴEb  @ @&Щ0&[{.❷dkP`޼yq!o,8- |qUWX:ZF^aiW0W۷oL2%upg}6STĴO|lJgkϪ3fLڕ @ @@^{lytBQS={éeF`N_:njX)X&d#~_YjƌqfiWFQSe/HAc=݂We" @hsm~P}7[z+/8##ǟ"ZJi6lR @ @ `U;쳗ꉂ`-96,F-g/reF/ @ @L!k ,־R5 ˶P` ^}՘;wn:&Ne8sO9͛} @ @jZܴ6Ԣ^~d @1J5w>я~=/v}땺Nw1bĈʫI7tSz߾}cM믿>Rqe.'@ @jL@:-N;eo/-.~z.וW^)蔧qe) Tx7G.;ŋvX?W{lq4e0]멧: @ @jS@6u 7TTosN6[/C 7x#F'xbɵ4W^y%&O\vғg~ꫯ=p5ֈ^q&49W @ P[X_mUVY%{„ mv42;4oڴi1~|㢋.QFeAŹ3<i4/O?tv>>h|__%@ @TkKY^h+^I|pWKiu։_-q @q#j+wy'שS'S* @ @F`U:*{衇ِc̘1qWgP5i+@ @VAÇgOkm[,X{#hi @ @EvanTk_[lE\~q7/V  @ @eQ:wչsXqcРAJ+- @ @(6;܆ @ о @ @,b)N @ оXj @ @)` KśөS*^  @ @K* rUp<)`nM^Oz @ @$ UMmib8 @ @jJF`I @ @z{#@ @ԸVw @ @]@{ @ @j\X5ށmyb1sE>pV9 @ @@ `9q]୷ފK.$nxblLNbܹ, @ @K裏>SN]䈫vn @ @K@kjib +a+r}ّFX]yt1cĨQ⣏>>=ڸA$@ @):K/4 ^#FniXvo_<0|vm/A @O!^p?f#9,xRzy睱zŏ~Rq @ @. mg1bDbi` ^zu7s15f %@Z/ z)9lذOrJ @ `-]͞~k~, @^8Sz4-?RJbxYٖ9F Z|3g @u.F[}]|ɋ ^;w/}Knc=G}t6yu։O~1bĈl[oUR#<,X]w]bi 4ba={v׿Fz_bԨQ l' 7ܐcnQh{n6};IENȯGjMyq-=a49J Jk~oI&e kOmmZF1 w$@Nʳ7 4MLR% @u/BL6-+eKςWo_}GҕW^=PJ`~܏lTP*ϧfSZ/3jHHk6k9k%=pi_e\}-IJ~s8Lt_i4\J8V`ץK,u7g.(Һן$@\%3ŴgvlTV @=\[lQ^oq ik+R4m-J#ӔN:) n 4;vls9q6nvLJO~L )r-@MzRq Zs)u]i]#cKR ^;묳fXcH4.DJ#(;]v% =?!ҴUW]Lzb̙3ԯiJfm֙O=TXjO~l݉4 "GuTهDڮ- @R'OZDiSRJitU^a8I)R#nH`U=FIy{fm&WKGY)r)QZi:d+7}SNوtiO>YrKSL{'K5 ^SG?QV1~e?V4 iO9s]w_W"KS Ҕu܃v @xG3s/KvFZ}뭷nrs_lTi~~{JJ&idXZ0\J;ʼ}'R\j8⮱ϣ>Z{:on\< @`̭fJkbaiJqLJ[42+6uQP +BIŌH&NFN6|l-T]wfݻwXk5 OlĵXyjyjy +מ>}4dž 6Қ] PYzVumQijɓK諴0m fq%dgTMhhcUVYxSww-i!`:c&4@yҿ^{]-=/_jر-֑AOZI)_'~ū4<޸:bل ZƢFxrfƍo}_'$@Zj>ŧvZ 80W\qEGUG)K/M  7#_+ ._|Uunbw1NO~'7bV~N16|lrO~ RZ -B0 0 M ?ٳ;,HARَNo}#nf=K"@*+ UYϪW_;/SrS҇V={}=jԨx?۫4A`5ֈYS(;} /ėռ`=N:餘1cF~DJOktbHOQ<䓛,f#kU<Д7sm7//ng}ʽ''+daʕi3M鮻;ɥgΜ.l4| 7-~1<#eˤTGzyg|sNه0RJwuWOe} @p1eʔ@iͣgɼliMnUwߟ{6tboqyqeev->l";a*wݻwب4x}_"HK!Cyume\s5Nh4"k뭷F_6j޽{ĉ7MY%=!y̘1M3xu:w}:]F٨w_GU?|g?4.vi @:~ᇋw5dȐ,h>I @@Rp&}tgǤI"JR*{gN#(k&IF55N)Vo|Yw]iܮ4:/袋ky+rKK_K/~ӟ4sK>镧^sr|4hP֏&~'xb))FK @@e*XUߐxYႣUH!@5"0rH#{?gI;( =#x4 :MO׿5ɔF\҇~xU'rgR> R賟lUs)R{"HObL^iyC=4_>sOA8O~% @2 9f{?lTo>8oӇxԋ@ZGimn'V ~TaÆEZsT"@m)kEeZJixOKpjپEKX @Xr4.ĒL @ % @XK/lsʤ% @2Z @Z}=ֽJ뀥ƍ-zizVeB `Uz5>4wX> XK @eSc<Ñ}cѷoz#-Z) @ @5+0`HK l^zj!@*.` @ @XT @ @@*NB @ @J `URS] @ @8  @ @*))Ԭº&L~{7.z뭘9sfکS;, @ XN>㨣MV ,jR^JM @Pok`{{oZuUc׎~: P1}xbΜ9YކnzL @ @U7tSs=Y;:x7M>`<3oO~X~s9'b9 @ @j^pm6Rseձy{>Z[: @ @ `-_U=f̘,`uᇷ}Co}["?O[uB @ @j/vNz`J뮻nݺu+n'6N#G̲?4>d @ СX6u+b ӚXJ+eM4! @ @PkUۜ;wn @ PXU m +ԩS 6,ۿˊi#S` w#@ @TVD۱.Ă OSIrH;? /[?x_r @ @@G `ut{,uĉW8蠃ⳟlzc|;Oger8m @ @A@zmH]͙3'>裒'zqG~@Vp?ꨣ"zY֨ @ t Wtu8 :w.ѣGs9ֿZmղ[UxD @e`UVYޭ @ @"P~N^; =И2eJ2Ny#@ @tVGɷuHӧ*3fII @ @j @ @&XMH͌”" @ @7:-Ǹqc;ϟNkx}[ZH @TVM[? _)`ҳ>[߰LfS߶i*Ǿf)xFbu42kIӹǏ=sIp @ PXU1KӬDzu}i)kvXpźӈ`֬ɄۘV J>(ET &Z^{m|K_w߽5(C @ԨVv\KNOKO{駳V[mkfy睑TtPNcǎ^{-KkSmf-Uz왝j.͚5+;ԫWS@#VX!~+1iҤISf8H @TV@UQ5\i_zX5jTuQsww}*њlK>cqkR>VN @ @x aϸ[g?Z+f|zK6<dj̝;ĉl@K .W_ufғ cU @ @@ `Um,yƍW*Xk^뭷^|_|qS*/Ma֭[ib㔦<>Yanv?=7(y͘1#++͛l @ @/ U}-LOKiРAs.]w5ۼ{򬊽;FwUW5nx۷o >驊)WL'?>p, @ @~ky*OS)O׿{zcBKqg®ʸ5b'pB)D6|͑O;C  @ @`>0`@vWi ]+bc{JiHo}s918iΑ#GƉ'Xr?^y啘яh3ovat~4o.'o"}ۨH @V{gHgN;0n>OI+yG?Qm&}3׷B @h$ L8娥Do}kטW_MkfW_B io4 @W y{W/  @ @ (9G @ @@ H`uqu @ n!( gϞtW;3=SK&LHK/tZkɓӛfs^ Vu @ @`$F|1&d?{UVY%pi}IG  @ @!>OwK_Rj3g}7m6'ge @ @y0k S FjԨQeRjʔ)iM6I,L?~|y?ntE/Lr]veiwLR @ @XE:G2qiumٓHlŭzkG?n4cƌ}/}_m @ @p PcΚ5+s1UW]5U_tWHzȭ:*ޣ>: @ @槀}͡ @ @#' 5rHZmێ  @ @ Z@kAG`饗4'@ @V{ǧ߳j l@IDAT @ @<  @ @] AE @ @@' H`ur; @ @ $ ȺH @d Ns'@ @tVY  @ @, s @ @. " @ @$:9zΝ @ X]d]$@ @tV'GϹ @ @@@  @ @N9w @ @@H`uAu @ X=N @ .. @ @:Y@  @ @] AE @ @@' H`ur; @ @ $ ȺH @d Ns'@ @tVY  @ @, s @ @. " @ @$:9zΝ @ X]d]$@ @tV'GϹ @ @@@  @ @N9w @ @@H`uAu @ X=N @ .. @ @:Y@  @ @] AE @ @@' H`ur; @ @ $ ȺH @d Ns'@ @tVY  @ @, s @ @. " @ @$:9zΝ @ X]d]$@ @tV'GϹ @ @@@  @ @N9w @ @@H`uAu @ X=N @ .. @ @:Y@  @ @] AE @ @@' H`ur; @ @ $ ȺH @d Ns'@ @tVY  @ @, s @ @. S?4eʔKqƥM6${iΜ9:;3~imI+B3fLwOup  @ @XxD`]-pGjƏn喴ӧ_iϩΞ=;Zu˕W^9oLʯ_Ncǎ׳B @t@ق엳n33fz6hz*Fh͚5ѣ=ܓ=#w=r!e 'VF @ @C$:4pt>ly`^{wm4a„OK/FJK-T+z*=cr+ @ @@g H`uf:o+QŤ}M7-ޛ^ - @ @@g Ľg׿<׾iᅛŭ_|qucgyf|/Gv f_<\?Csn# @ 0ͳ {b/C[qW6W2ݬ*n @ 0B8}ȷ-"-=]trۋ/زN~.+n#HMl'@ @:@Rرc.s̹n[7lֱ @^ &=0:6MˢW_}5O_~yZuUӅ^8[ذ+mm @ @p @Ceɦf=S4+jx:ӊ+O^4 @P  \'oƌS΅u79Yf믿,|l[>veY&]tEiW[u @ @u)O0!M2W\1;6iOO? @ ~XʞGN.RK5X#r-i}M{ĺKӛtiٳg[/=?AY#TR @ @@7 H`uS`_g̘z6hz*Fh͚5mn+?яc=Yg1c,L [xy睗"Af!@ @:[@gϦӧlTn&Lxdկ~UVs=ѣ{4yk_[γguVm @ @' y13馛+RN^bԦnZ_{7yꫯ Ɔ]ْf @ @#p3_ײk12*nkjn:{j-| kl4>szYLszY @ @h|Y g; @ `Z]qcǖyZ-/rikU^yn fmol,<;ҤIr-\z衴f+Z T@(.nC[ n`H' nCb[m`H' nCbkFb7oacU,l[K`qprj/έu]d`xW^m5Xix$s4{.q.ɑݏpMSs%n#g=G}YnhUmQ-kYO&{:N$.xra,]| { @ @$+<7޸L8w7xc>Κ5+]eog{x&lR_uU7(@! @ 6XmȄ Ҕ)S}::mڴϦeY&oﳽY;\tIi=HӧeKm @ @' y13>蠃ҨQOtp-ۯ||S: N<^?>ieM~{>FqO~孊m]zӛޔx%@ @:T@Ci[C=4͙3L0i6*o|GԩSӗz}ݗx&FuHDc9&Jo~szk_H|p }) @ @:O@bֱg~ߦ޺)u]wuCgĭ3,n8k؉u@g3g-v>OUVY%-鵯}m?]lTlx饗7ʹ[,-r}ok֬sϹG|«r}CiWLcǎMzO?ݬz,G}'7%c9&mq\7fv|m]~u\boK?O'?ɴF^aqg̘Q~R|>ԡ:;\rJb-ҒK.&LPz:u(q馛*-i̘1iOm]ओN Z9/ĭ? vE['|I5\o*-B*p׊ϪkVmᆵZ|;fϞ=__O>noxjE6~z܇C) ږ[n. nZ(<Պdd-^#,LH4m7$[y+R~ѵO?i~c+k6}%Ԋ_˶/ڥ/^& zڪZ֋hqɖ^}ԩSz=_}״]U ñ5p):kN1_W/Կ7#HD/EB|馛^xᅦaǂz^{:S~y{`v]w1Ϥegl/N<IJNjy-TiJs'`;[a(}E¿~C\JJyw&PDak_jʄiV7\4iR]\G.kԎ;ړO>Yc$>o[+xɌCJ1«K]$vyxhKC!0~*FzwvDs:]7'.>l=^y啲i?X/rlj.O#)F2.c1#b۴i坲2ظ#H?׻˟;"{/ s`;鳰[a(v:lTq!Q ;?VX HV 9߾ݬA3F_TR0Wgy6k֬u[)Jָ 3<H4.>b1կ~ո`"{y׻UnL,CI`/#.%FenqS/#b?yǑG٣]$BtTܦUWm=\=0\Icܼ}ln /f>ꪫm1[ݍ`1,ν+[pq+n (0oJ1Ly}#Rq{a]>&%*koTkTT~za.u5v?X6[z{fZa4ĆSݿ[?7>+}-e~]1-}xޚǯ4*Z\sd7j*PQQ?F|e𸬓겝Ȃ<&`ݛ}9Z'~t}_MxbL%^,9nK\XKoޞk1irxmU=ʑ38#QbB)Xsyo>om#-:DK[oe+)$*{|e]s-iȆ}OS6[Fygڬ^ˊ;./6FҽxŜg)O9.>G)l1ut Xg͹..J` #] 00(b \eE-QpoӁR#s/)oE)&Fe9n=*oryԋ[H㛷d8qb6۪ꫧbr@X-x^xa|bDz]Z9ƺ>o+@Eܶ?wU/Ŝ~瑥FY߷C͖f 0k>kzU.7e8>]X({GR֊+Z<7?Ӯ?Ƹ亱޸\*f1rfu^?+OMӨ{u5eQ؏@ Rih3*G%&SrvX|≀ysh]0_җR4/u9ѠULsfŹt'E*&OoV7o|c~>Lb܀s=c>Gu{wga^7ƻgo.Qt\3e ՟ @,ӷZbq,hjNyke)~=֕{mGqDYsdybuc۶jsvQmKrZ<1e]V}6 [}7.:flOS9.ÃknP\^y]H]WO>Y".⩪A2%DKz?G#=> wt*Ǽ1lE*?G?!57$h$aI%0yu^=輳 cC| s,rl"ouթrY󝮺iCknHl]H+¬K`5,O(Ԛ-\Ye>G*FNmh1f`}CۻN7o1h4m{y{o\1nL.-OwG1&116[}#)͖|}QgA&?@ϵY*EO^\wu`47ؾ6cx]o=]}0F}hkTu XoqĈ[bNK&&%& ] [o-'I5:=nxG˓Yy{TSLl^G_0}zY1_6~x<˷wm&ӳ:+7渽;p%>q eq EmxĨ}s㞯tw7.Ccڵ^ۣ~?q- O}S=7>GDirE4]rk7Mzr*CU+ ! 7ܰ$)& D#\㫘+LbD"W1~nf喫;_/x|WoϕkbҲn1lqM(Zq[iY/~YbtQkwKi*׿.faPt!Ҵ(/Y9~9vb$b/5Oż,e۸V"IlYq#ʯ|=>Nbd\3y6vtnĉe7ֳa l9Seq*^oƮ1{qSu\zrJ@Nzk_$6hG-y5a8?tF"/DF_Ə_&N>j*nAU[/{w|dT?͜9e#Vo=yO-~%:(IG(~G4_cA h~Պۿ/]J:E9׸n8s=7W׸z s[\ssmmػo[sva2O1*>Z%3a$򖷔?)u`6%F(#HDe0?<]i 4zª]o_Qڊ[\s *5*2Y7Z;K}ݵV @ @o~8˕Mރ @ @vj8 @ @X=8!@ @h7 v!@ @! Ճ @ @vj8 @ @X=8!@ @h7 v!@ @! Ճ @ @vj8 @ @X=8!@ @h7 v!@ @! Ճ @ @vj8 @ @X=8!@ @h7 v!@ @! Ճ @\piԨQ5\3&#ZG?Q~= @/ 5HVOd̥^:}_|Ve @@H`K$ @G?r)jGݾ{^)tVGI @,[W_$8묳ҋ/8 _Zk7|@Pi}IZZa%} @]- y @S({.}:7)bՀT"@T@M @Icm^?ܸ; @p9Y @[^קɓ'ݿ ӣ>:WGy$]tEe*ukZꫯN{WZs5ӸqӺ뮛wt=4m3g}6}[*o_\zӢ.V^yx@ٳg}{_ZeUرc2,7޻gi\Z=k_Z<3_*c932lY̘1)- @ H` = @@W_M~\ۣ^,]cYfɟ_NHwuW?^xtc=6:Onl6뮻. oH|pz*+LӦMKm]>PqW$bZ_~9=[nI|fm_jC.iҲ.[?纟8SI&͵ @$6 @` Ĩ[<~=/xu]T[ #{tiIH4oEx~ק}{oz׻UDZh'?rT$N:餴z땻/~QnkHtE[o-7G*uז_0X'Nlּiٗ?)mnr",R;Λ먷HE.O| @XxveO @S`„ }o 7PQR?)'abK,ѣʩZ=.FX}M7-̊UW]UN"5|c~>՛o.o!W?xGNDr**y4Vl{쑎<4gΜM-cdT|-eHR-G*Gm_Ӻ1-xoߴB @`)dВ @`ọzﺱ~wᇗ靼ubVF˝wޙb.1*F, UG$rR?aTy}W#ARñ -آUӧu{x84 H`5jX'@@Zq˳TZ(%M2;vmeYܒ8e7.'u:3f̘[.y񭖘8>&M X#b[?9ŖG} 0,Xh' @=zt?XO_~y_vei̙eYԋKc&ꗟ/?Íz**ms?M7THpA.1,nߌ(+Zk!@W@kx= 0>Տx`6ov`S c[n69,^~$rz)i/6,J M Csӊ 6`F'ꬳJ1TTz/bngϞ]/1K J[ {;FIK5~rbv8W@( UŨ PyOċ's9eBϮ?@Yfe61BjnOWJNvżP=XQTGy)e],t}+dmo|7\&"WN6_ @p aW[g  @Viw8OVy 갾6&Ůڹ+O81lIWǕʷ^tE|]gyfz31y<Ds ЏV?@6 @Q`ҤI)H~ y9!sK5$V[mSNI=PaYzcg|}u])&Ma\:꨼:lcǎ-8U;?\ު#~o0'O_s  @y}z9UW]N>zPO4gH8Ź|$}ӟ. 76ltW#w\o?q}}7mSOM_}y[a?|&uK~{c~׷b+R#nuau=lǒ5Kܙ  @L>`*  @%?O'㉄-1_Vf#}o~ex`猊Q5C "IDATX1W{rK$ϿqJ+T&"!wwJñD2,ٺI'T~.-GemV oxC;1J, @/`# @`m RzkX>a/Fsň9seȩ'ScU$z=д馛Yd]w5OOQjdӟIr˕#.K׿OhCPP&oSX!@U`?^#zhw @R9*oo|Ҙ1c @$֯cO~uX=X!@ Zo:s 1M򪵕- @`8$SӾ @*-sq>JU @@; Ľ\ @J`֬Y+'u9.5\ @* H`U9F 0Ow}wZguzcJ{yC___{'@I;f̘^UWA:CΈ$@Xkvʇv/;$ ^  @ @R@- @ @VJ @ ЖXm'E @ $W @ @j˰8) @ @, % @ @V[I @ @d , @ @-$2,N @ @ H`e  @ @m) ՖaqR @ @Y@+Kx%@ @hK "@ @XY+ @ @@[ H`eX @ @@^  @ @R`Vg/~& @ @Uࠃj y~6ml}%!(΁ @ @ YOQ^S廃 @ @-WY:nio=$m7'A @ _~m#"y2ۗɫX]- @ @%\R$'j V  @ @H DK/MmZZ~_cy>5: @ @ D*|_S6w3dIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.14/active_subset.png0000644000175000017500000005375513502206677024423 0ustar noahfxnoahfxPNG  IHDRJDb %iCCPICC ProfileHT/$@D)ҥХ 6BH(1$;ZPbî,?,(bʛ$y/wܹf93plTQ$6ğ5!9Ez@`J~11:@+vr_?W\i<)7ApXM!a@Kl& %9M ȩi$Uy\|n 'Anpx?C3 ddgڰO'c((93roɖ a M ,[ִp9 EECք|US@0h+ 5LPY(;*bP. fCGyxX'; 1GKnS,JIgl*')DQU!ߕfŅ}asp: [S"gH c .p ǽgz g}P}ጿrŞRFP|)V?Fjǟ'XgJ<ܭ,;z_a*ty.8PMǁ{'0}ә~%Gڹ2IR@jKpﲂ9 |A $)N%`: "PVl gE;pt:@t#E7 B"X$IE2"Cf# @"ar@n!n5 P Cxt2"t9ZVzzv/> `*307,RtLŊ2 ۃ5 ։`q"Y\xs2߁+J 0AN("NoH$2DW%3ˈ{ljG>K%yIRttE@V!\H.#$%_&?%S)J4GIYA4Q.Q(T %ՋOͤ.SPOSRߨWW)W٧rNG&͆@DіjhihotݗBϣ/O?2TGUyT+UU/TMQ+P+S;vIGnQ^~XzCA#Z#GcN4IAb` SFXȨffti,ZZ%Zڴz5j'jЮ>ĘL63yiKGqy{::|b:t>tttW6lMۤwZgHϑܑ#ߦߪg`hb 6XopҠǐikiaHhs6ˏ*gbˌX$5gJ5u3M7]cbkfdi6۬9\`{ K$ ,u,ٖuwV>VVUVWnYmPgM%[VhѶca(ѨQ7hv~vvuvF3GG.01)cV9;櫽}}M0B&׎6\JǫNt`yNNڎ439ysWnW3T 7ܴbܖs'sovoO;,ϝY㏫ċիӛŻ؇Sԗ_.C<Cۂ4*g8 9J ]zmkٽaasN+FDH""ȰՑẉDQ :^eLncW;;l#njθw+$X%Z'%&O L*M0fœ ɍ)Ĕ)}&5yRѤ-'Ϙ|~ޔ)GML=JHMJݙͩ6r/x5n4+4YWnL# V_efn|U57sX)f8mƴHܙ둻6W..EyZ*"{_az34ffδtӂf᳸Zf^09["s3h^;Pd-о¤M _藐_T$E7{.޼_"\Ҷi_yJKJ>/.ï,O_޶eŦĕWQQZPhu55kޮ|زd:#כ_sZ nx&M{6l.ipͭ![,ʶo{RX}7jm/FTӹ#vǩZڝ;Wԡu]vݸnֽ̽%>پ翧~}n4?Pq=R?A٘q8pKgӡ?FQl\yDȊԣ+8w\|DƉG-S[pN>w&ɳ~g:| n.\oun=\/^jlwooqWʾzZԵ oޘt&[ٷ^οg]{߯vtya;^<>ܵ IS5ww?EO_mxi߾Nz%y5z75oǾm.]v|txSҧ?>5݁1GQ0t^@Ogvw3 bM!.0xF9d|ˏurn"MwrT HM| oR \P.;k9Ro KpTdh pHYs%%IR$iTXtXML:com.adobe.xmp 76 900 1 >j:@IDATx]|EHR"@:(EA)*JPA, *4AA R҂H ޛݽ$䒻$/ݝf?{3yɓI "u*®G6<(o"<|_2|3,Q(D% >2*,=>b H7|Mrȍ   'NCA@A@A #$`e   6(%  @A@@Q>    `EA@A@A@( QA@A@ChA@A@A *a   @@$6@(A@A@A@ GA@A@A@x^~=E    ܒ(eiabŌ[9    8+Yn7n03S    !h,i  d DGG0LY#ͥ<==兢EPB\A@C-,,L$4 @DwܺuK1y-/5[*yq;M, # }s v+SwXr De4 IAܹs_^+d|{ͭ珍8VmweCwPK K 4"=SZ‚[n&r@ =Fx4.-]V(y}=ysՂ@@@<1LK###Qpa)Sʘ@~Gp5*Hx!2 ۯKB0_KP`~Ѷ`x9T# ςa 03 <3e˖u&IKB>ڲ0NKzdN7qzJƃ鲦]xJKRlW򦳄F旐_D!'DBFS'`5Qxw]w 3'FLR3)f)fO=_Zs:E&1XCKa7|{.v]G-Pe}I!M!0?҂d?S",`/uȆ8PSAⳙ%+ FPRtfЧP tԫ甓 . $ CxRT,lM466VI3[V sP|L HS̡Qb6J-Jt4uRU4potNLto T~O6 , {[eDY%aw;G1 ߻MVΫvx7i;7Y(1w7 4DAT:(QGȫSX@ZKBPSN Hs!B6}F%#򫙈ZI116N8-|29lD 9@1#h0}H 1.B,jyAԘGDA#"Ĝ 3}kRIybLފWvodp;wNyCPP5j#4vu!?~~_˗G PrH=0 `$ZxAg)avm[H f#gB̏PbgnҮrI$C8Aꍡ.ʌLt>᩺h-g0C*goSi)?C9onCoD;3f`;%eQweQBvz?^{5ԭ[7_:8mBPp%~ӂ&STK&iVX^EMf Z|$|e01\|9[33UXơdɒJPtlSNCd L2 GREQdp6t- WnOo3o>bFp֭:u*%1b揷t0SLÇQR% :~QbUєpx:~76aaRlJ1CxYB llʕ+Xf 6oތk(斄~ETF b%Ѻuktò!d+FV+ʖo6džqӡ۪zkL![=z46mڄGJ*7nʕ+1k,$ qơHmSu{vy`_9񀗍F^gyѥmt4Kb˜Q|HQV1⿛P$,YC qgNaF Sh !gA: mn5ь(Se6bKa]0{[ uaԏ#5R$d6t5ԙ#y.[ҁx'8s(Z/9)9vEs;,O3K y *){@3qI2ؚ$ ˏ$?k:&J7$|0Y !H/\+H~h苚ޞ%g'~nx EPD"&O>N3] I~<|Z JcTnRwN^E^}oQ5VϬ2 , 2dΝ; ΨK3?l0ݽT DIk:d 'w_:]#+^J;3'oJ`%,# BvYH9H 3h 猍z88rd3hO=Li򍬘:-ѭeԺ&ZM=SHKzPzr!җdy-fܔ(ljb붍eLKrdD×Ho;|DCw(:ə/&|k2,&zH$u=|MilFeכm ؽf%&,GcowY10qy8/LmWcc}K/ЙcY4}5 cǎEYfƮ\rv͛7OY 3f]eiKN/mW] / w KգsY#G Ke2 Ɯ7|vZ`^oVZX_#B!vp1U&$:,ϟW_i ;sgaOx$8`kLi,H$يU[ !/Ql8VC'DaAf xg{ K' $wČ iBbDwᎧjk W9g QLr"ic5<(M͒B=v֘Af6b=Zϱ!Ua(ېG >|2W#$ A*ՈxѨ0U/=έ?{KhChTtx .(-'O{ܜ?N:|v ij-If#Ҫ9NT,ug¼ɾ[] l>}kי!mC4mf;aS-,JCeaxtjGq2 {uF]i$"mТ|)3#=1Ț\/pKX37姜5NmSѺR}֒W),*MatZYPQKM-RIfSHRAf :wab ?Ԯ5RΣ14q;[Di K I * 峼M7R\C(]\_W~=ʒd0eJRH&jpʸXw J%@._տ(NӻLz'eE죰VZطoKH#} t` .ʒ#~xYB70#4 SBEh$BG!!ɮ*]f Xﳢ[u<\;)ٵ&0DYxOu۸J>{vj[Xn/*+6z+G]]h?RZF}m羆Q_i l1 Z`?6h9nhߺm1T`T(Ԫ7/]g.!6퟇à%Fm_)A >YE!ҟbwO7Sho"IjVoFn,E3]7tn$1H 4;5#3{SC,4hϚ[.f$N/\L(Mxg e) 6ד0;9<hvsmvnڵk5*YiR*^{P|͘1:xϚ+~31`+{KyO(3C*W/}ZH ާw:6_±c] B&gDDrGsUrgQ~(iܩVG-ٶm[v58 aԉmkhKHzbaŤV>6mJ}_LZsQIWȊ Bgp{>yB Q=_ԉ%Zܰ}渠8gK}5(ݠblv.A5Q>nWS2 \Aώ΋(MKCW R]ŋcR sF,e], +m>gf*оG1m@{$\3}Z7џf}O5~Xl:j[m^l~7nETh A1g}Ic.öҳ U-"ؘZ#C>5h&P=ݯ't)ab9D9WqqD z:#6cJ?+_U0x~hl(=jf9mƕw#%;Q-`Ts^z0 ðP%M3y(ӤS dR4*{=sֹ+0 y=iz_}T %;ϙL5T>Gj1iQDy ՚?{glBH/̸ۢD,Į%ɶ3-ɴg9 OXdϚXHǻ)w臑նeG9ɤ C][hK>%~JۃV.M俨p~gSO=ҪYFoaWQ z7 3.+o`8w&k ?b{̉<>>>0yQ zIs717̄WF|;v!_T9x\w܉.]g`ĈEIN#7(p ᵋTֶW3WGv֤5ôZzQiðre|QF T)g&⺮պA9ޓ`PF*-6@fXw̰4$|:3L!S꠬IJRhaЊ_+Qʫ57d*0yK֧ܷ7>oj/y= 7דW:mVͱ{yS{ ܦ<1T}b%t_ _B)er~frֶiw캞ӛ}/U:o^ jp]ȴ] EʕmbڜEfxKWbT@L?_uʘ871F01̴33S<&T.Ѫ1_O(|hB 3j(Q"h8MMc&b[ g%dj 4P&w`*T9n%zMVIuV[>:oHВF):0~ kǚ5k㏧H"xЪUcf](#x*[¸X1Sv{q!Yh+nZa̓Xra%`sUc:i)M> 57$d,j1#UH3U]k}맪]EFӹ5xe}?YBc -f|Ğ0˹HWoP70&=J?| }MfHO#__㓕 #nz'LĀ$U/Kfbϱ>d>Ѡưkƞq|Z ݻZږoi`#Ǯj9*m?mx3E +Ntԧ08>l⪔m{x g/ꜝO0F#D{jQɠ[V6vZ1&@ԧj*5GJеOȅ4'yŰ̝bX1i+d4lg VVK7 FРCFb@Sͧ6tмSw!$՜m ?UEhxf 'c̙.hh- |܋:=MfŶC2j k>K [#sRR0Ͼ8ykκV*vPN<#D8ЩWKv('f za\ͲtMYsR N8v3i <//7.2}sF+(cA+WĤIHZRQz۫0aN38T(;q{,>ʴO=.D}S^xB8Zx"Pz~~&o\['/!]w^{Is,{ ?5ij%.Y[x~+^H[WR,Q>\}ݻM_\mCS*8c݌X'4EUp<}󆺮>h^bh%%ksn&:\!T~zӛ醁>Rܝ^A2Ǫ,ղ8wclUPu7w\F4i{͚5ߪGȌRZ>.ck- I"KXrژ,pr(DG>v;B9gF8Cnݰ.&_6='M8q"XR23?*!#j+q۫ /V^xr&[ˑdmO'ѮVrOnU_=: hꏖ\U+Ѱ[50* q]҈7-G\B[۹=~n6,F85' )ztpPMs߼iAЦ G Կ{(uGql?ym$QEZF]Xۊ3uƹpp#gɴ&رmkyV,'$Z@luᘵj+b-Բ ymgՙיK`)2A=? uQdÐ r^gYjhC(Bje Pu`RJ El q'E I^h{pS2 W*a]!ci 4{(}=LseU3y;]p}K5o"j]29]ڣVsz.G1Tƾe6Ӵ9:vJsڜU#+pQgGz7&։uNxlj5>x,Xf{X:ֲZ ǫxc{wVnjxW8jK7g[u(9鵕}HDUbߍSp wz/F2ݥ2dH1K)x||I̙3'W6;,1۷o7uJ0s>Ҿ涞 Q~h=[\!*/jcL{rΫb#5O<2UnŖrqľLSˢkFQԮ?͞R!ͅ AZ7`ݩMX:}cW4ӣPc.ØP?IWtw ĴvVn|chNs= gN d~$ksud>>IIi2C~y耺6_G,EJUV7w%/)…=-EQfKj^˕p|E8qL'UvƄuFuNU_-۠/tqrV'bm2…`3 sGR?3m=ۀBT]Wk8?>v; ޚ63ĘsQV_?w?LW1咬L/2Mokӥr7#5fӎ2K#n5?ZseUs3cTN}jy6a ܈c)WO!0t*$g7[u*,?BSNaU:}cBo~ 4(YyզMTX̚vn_&xXFC՜mww:;] .W$xL/Q|| ̟??iuv|ѢE38/}:urO?r5~gzgyr#RIy:Ο{~4km_AZTG!]G,:ҜO_0f˰of *Ɵe\ 5,@E˺\p l@]UJzqKܹ?= ~?Qv'2AmJAWF$X\vƺ\_őmص7,۴|>FzbM3ؘ4F >9" nG2u\k_{w s60 Uߌ{@9l) VO}S-^ծ!nk( Q ~XUE[3[qb6]cѨ(uU`jQ6>sK9s]*cng40\_&g:jWFX& uS;2*a:d\GVږ2nذax5wо9gzdeYtIΝ;w}j;o Џm{z?"/jR![iٲeӧOnw\':Tw'waG}5uɥoT}1 9'h`4 ޿>@c3~a 2 #Nƻ5ʡ!g+FSeUQG~C*TPv/4o<;mOy"{o-2]4yzHoXv-ղ6ǠK%_$X74okOa#Y!~ QʿSOe%[οRA<֕{B6@c>gNں nw%R~ѮNk?}gMHfBRճ^pwRƥ:{ƣ">ŐX ,t_ 8dջ;?%(!ٓ6r43 5+fcj _3Ag {Bx-w(/U%Z #?}UBvLOї^-ĆiϘπf}1(uK㓖BܶO>iSil-!`%җvs]PAD0cƗmKd{wGe$5TO c s2t=mW"|PGԘ^,Zljt#Lr,4X/F\" !jNXG ZgZY͙cb t5~u;DѪkWjΈb%q<ބigɴs9eEHQWXwcI_%ed?%1׮]CJRgȅ OYF[)頭V8pjIZٕ+<̌ 4FtmTkZF3Rʕ+\A'n>i*=kW:ĤS<"ѯo#1GNxžڐ-XZzn&aݡT}>:˄od}Hg2[y;6$u.uAeEEu^iԒd*:M>O'ΐ";ƼH/{IS_Š]۴$R!'F%˘j:,l5 վh".ءC8FjF^@@x-}Zl4Ri[R&cYi'-Qr旴־<;Lipl;nbXm WF2:O{W6$.BcŌ3=1%F vEVyVć{cm>m1Ҙ:݆b-I ?d5 ^ 3IPtf pnʍI=6j,U}LXг2Cf6B̡Rd ;@f%N#[ymUZ.KsUso`;j! 0aty.5?k5ǫJot-M"}x^9g6H_,toߎ(?IYl=~tқ>!xNo}^6.m hdORsuۺAN. v#4ڊ1E/eZէ8}u1/ Nxy}!3[FZѪ%*;}lWALdw6'lTS}8 I 1z=x)rUhT|,~:*D81H;'i_2?4%?꩘@fgwE iJS0(Dϲx9USQls^s)9~zvr "dV~˗oc6cRfI\ U4o[ ź Cen_V֮[&VUcΤZSߝ;v$±8C{d$XxSP𴞥?yʕx " AއhhdO_R`;7 Ьi;Gڱ>Ċ<}hwn"vC,K~5=?Sv0=iO4nfY28m']7h܈&O2y.ph>0ץ1n}mI%|Z1OO׉z^ ߽~g&3hlhG}NG:ss4nj皬׺15ތBJU|YO`\x[[ډI~ɴڮKeiϾs!p_ؓr$&zbITNߞq63vX7+pgx!mwy^ChS]Ny@3q(LTeH-1jj|ۻ^Biܗ@{hssV`]sO#T/,P&^{`$]⽒ժ_ZS }ZUQ,ߌIiSa /W%kؤ7nE? UR,1V Kf슽?eG)QgPvdq1!.8MJ"H3[$x_vgS{9b3u-\RUL#%[$|m)ERuk;߳x|kj.oڋ/d%'Pظh<Ίl6)o<8[|7i10&j~)׺Ey>hU#[ᜋghL ,.G:dZf^ M6ɭRf߈VVY%U3HϪOЇ򙭷4(3ٳsK˨l`\ wѨiJ(f0Jq }4icǎ))#cc2 d?Fd^$BS_h1x6fǭp_A5xzcp\bDpT4 G5/ Wkɓ,8eDkE_Ċq:'bc~" //G 2i|^0FK-Q_(qc_oxyyΐDdNs2 C֍wQؿ4feog^-iI!K#> ]=$-I w7:`YvKtV?]fi U^,t^ʨGVZg#2_¡v Bmd}ϢWul_3?uߜ˰άPh @*4+qoD/|#w,\nXfa둬:sL9 &nݺɩyfS!⥗^T9GS? A .{ʕ]8A "PiI1mjļ)&NyQ4.t6.F$ЯY5UFwh{vp5L~5MMp6Ch4_9|ҙ-[2&4[b4xٳOU(Vŋ81vKd7` (qơf͚qw%Al<]^?ydڵ SN͓}㏘>sJV#ib]Qw^*6w\,Xm۶O?THX=3w}T>|X\MA _#/Ҽy|O T&U2`Tuq.2 +SR{'#] i7bܹɱ1zI.K Cݥ&,SV9{ڵkU.!2s3f`ڵԩyjՔPf XӇ%7nIJePre尞]VO>AbX(:U i`R";};ȀϗQ}9r$ʕ+'#۲erZ_Bu T իr';F CI$ (ؔBzʋA@Y*|D*Ĭ{%L%,Z*MAeѵ,4_?~Awed!ٺ!r! j W~_Q^|%Ea?}֭J>$<` ؉{=i?3$jhJ ޸|3 0;oذaXb5)UTI=MСCdIBU!d$La/:i* fk1LY󃪸nM{;VhS3߼@sų08*yM$Ԉ'f,vX*/мۉ1ҋ<gUtA\tI]Ef,_.KSC %g`Ibt9|i6┳xyo~hQ6~ْ7[wGPƃF(5ZQ>:-odfE%,=IW&\"i@>@/[! ӏcsTXT GxjR,$o;sF68c 8y )T0ش5dU3?SpJfA&i1+j33#nGQn&8N  JЕFCRF~Qdu> B i1Q͸Q/߸ǞsVu~돳YC8BAp~a,h֬L yyσbMgD##!G)]A@ @s*lfp   0GA@A 'o֟xA@aIr  @B 1P?yQ OvmeƭA@A@ȓvhɔV @@,C i  d%KfA"gϞw   ׯ CX   TDAy   x!,   a KA@A@A# aA@A@A@(CXPG^-  oIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.11/0000755000175000017500000000000013752535025021032 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.11/plugin_wwt.jpg0000644000175000017500000141567513502206677023757 0ustar noahfxnoahfxJFIF88C     C   A r !"1AQa2SUqr#4BTcu 578RWb$3fCVs%&Dtde6Ev'Q!1A"Qaq25BT#EFRSUdb3r$%4Cs ?φOpE1h[K-1;wck=ZT+$I$S@i/uv޳>!<`! KJ%kNHA'| v:7ޔ<FzͦjI=;.ul:"k_TPr6@ ~6 n=!OEY/c]_L۹jQRS  z+|O<^ؗ7{7 ,,lJX / Q G_/ўOJq r2Jbi{.eq] oT޹{Ry^ȯ_tx'<zsJݝ8gd2ޒV.`x8\G+moKoz>;=,ގ|<۔Rtm{KnaiE0J(P9JXR{AdOq8\^vxK4_5%_Nו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_\IEU͹RGmŒ bTd#=;oW9IqIRP!*8Vw}^W+Tו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5E|jו+^W+PdWƠy^ȯ@:_u{"5YN[wM-]TˬHNZz%I"L(r3BwtTN c)|,'ቺ?+gԒU\k~÷ަ8rV'Ro#;I!XN8sy]'``Aज़*~뱧̽5 ܞУ Vݹ|>?GxL |5py}Æ.&SJaW :Zo=+B{F+/ W6W.|qpRt;&{?>*&œ&_F)|>1e(q>7 `+=woS L9+e )H:B<㴑:HF,LI|5^W~@|l>j6]JO|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@r-W`1λC&Zf]r!!>Z$+n䕄`R\{?}ոJ£/m==`뗫˰Zc;#-jno<&]򿤫/+4~0ѱtn  < *'달%O(X3-NXAU96"oSnDmuj}; !$m{uѽ޺̸h]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u49th]>s@:O|u4>չP- 9p縁*WTtz qYOY?gwq|"qᱥ&ƚ7[ݙm hZ@ JS"8 VV㔜~xZJUiqI##J4d2i11nTTň20KnI?n\6 `bFF62KnU'V1Y]9rЮ^\C -{BuGJGg&3O UړL~ZxQ[ZNnve6ݡhm%)L$I[$u$lz=ڷʒG>ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}wxP ڜ "?(^yeMc1Y@ {nijf?(>ջP}w'VY@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@ջP}w'V1Y@e:x6IHq0PJTTA$EMq۩I%[ѐ~rpGHR2ݐ@P< պ͡4ͲrpI JR ()J$ Ue%'I%%+oDZS7:P1iSJu,œtZa) ʉt2HH+ q{\WAtۣnpz+087oaSL_-dȵ.5dYÍI-AF0 .UӗVޯMӜ,cMGEV.lO#!_5zcD5$#Mu};dqTmtQmGBF1y$%rkGX7*{;)zg: n-^.3Tˌb,f;T)Pi:FڔѹIdm_4h͘crנ\$5ijvO!*Dt*$ a FYOzOܚCR}}ͯ{Z;'=oM76*rZć7WJCJAG֔*rW涵7ϰ{*4$d5f=MHBOatP/Jږũ;XllB<9I'{W7؋:J=~ȡD¹AѺN2gJ'%I{>KOx-b]s$Zu:P $ $穊'ܛ%?CԚN6prͪRΙ)xQ Ə!jt[ҢJQ%ɷq^9[M{zy-O?~ԎZtszǪ :cfuf>Mtdhm!.)6˙vJzfyt׸(̶kfxD}thj~nL=`/[N~anvwPc̴!{aH{yW>i{x9z<ϿɩUiODۥTm3M"'"[ݑfeL$RP%X,7V׵-3Y7Mx=t6[Q!ZIJ1m'mWYwiގzmו'}F+u.KѤ6o@\q*)T@d-MФt Y=rSp/*&ѻے匛SCUizFwMԪiʝiN'BK8!IQ@ISM+zZMm.U~uv};ǫ(zv.ӫԖ!U=} +mʓNJH"F*Gk^Dfj/Ğ$jz[hP@!t)PzBFӂ~I(,IIZWEq׳-?{mY5p0hv.2[!S0엡6y%pi{{on|VYgMImC 7땱:3ŗ MTwPLl9-!ǐA*w% Q,NֽR~ZŹ;'|{DŽ$^ Z-wa^m? 6Rj+ؒnA':ٸl/MUze슗K"c^ 4ߩ]۩ZDe+)*̩1N={ZM؟^mpsn-SjR@ĞN_ V+,t'{ 6Ԕ{WvN5q=& Tq#S6a#nB2HAȗ?cJ/~5}e$Tݨ >~@RTNHB2y +暷o?`x rqOʗkBֺNx.hGDfƻ̎2S}MڕrRҀs()e[~nw63]XK5-{}گnvKuQ!e>4E4}m+ASyVSqK[_Wv$T|U'oQxO_46ְM5%[iJ _)TrS[Tԝ'~I?~e^$֭v{_S4UuXܡpMԗXkӐi$2L$R+jXi*BU +)E޵PrP4}FqK2n^&LE\SL(+mA4k,mnWW̬$%*_rl֫DKn+Pz.Q14G$L-,J27d+խ;{O׶4OٯwL٤a ce t VҾ]'}fV~.߃ۺ}U.߶D>4;Nc D,)lJ58^ĺۉ.!A^Y>k˓穖$M{ua'+zܛB7Kg[AbS@D'T)B 1s76˚]#?u^Uطczk:rݦ{.ʞ-R8T;A!i;kp)AO{f VTRIQ͊Y5Vn7XjWnzyL Y:.d:D;#sxbc !f*$JʒJAJJN)3sj[۫U<>trw_.%Yߩn%-ѴKaePVUma HZVVz'%}V>rKL߻Gj1eB[X2SivogS:razAqi* mĨʏ}j3\n;[TƚA$jZd(y4]yoKI/]} jj&6#ɷ*;h2B[!"- eGl!O2TI'$yI$̒IkyXs >881:6wo:Ĩ [{HmMyVUw| byM5r#qۅ$%_?p"9n.cхqP)$}ǜq7~g?Fcq<' 2U9 S>r0.hRG?sAp<֒{7Kz\|+{~CgYqWޟshIcxao}*t@tL-@ڰI!"+"-M0q8X+Iwn#qUuM 7Q8찢:%:GQIۊx|v, :~Ͻ]^GxXbfēiZr[ZiM`ƭƺ0zE8ӱn)^S!h%$0O|Q/=&ðONhz\֢RJ B<8ޡ^MrM_>O-]Qkq} ZMAJ}[T$Hq%#\'h8iJvQ~OFi\[{4kN\+G_4}91ux8\Mos故B}aO~{ t4}Tէxqߺעx؋>$E Tmى #FY=*8 $y}'<^q>Ѽ|p18Q6O^> m|wrŧGTFkAÊB6(sJ $Gt<16;z~.J㼞%;~y"ng[/W\{%ԷѺ: T!р4J[Iw#Q?ěĜ[6װsjczM랓@:R:s'{CT{Ͽo-=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uIsh\=&uI!5Ʊ:/Dj-D!r6{lR2GBҜVݣEmJޮ2Ʒݾ9hrQ5n-Ҕї$OP_[<9*Iߖ֒s-Mk}_k6ac$LV"ESjZgӚa3g*Du7G@W%)4g.'=ބN)~;KQnqѰD];paOAxq%" KaWFL[v[Y8J%^ oNTZRM~_Y58wojkK#TI^y5~߽l-'$Y[ENJj߲I|ֺ*{K{+gv<3etSC4}=7Q[]nL6҆dȈmB-,)jJUfk̿VΟKk Ko&/B1r@vô\(mmb)H}ֲ%+^w BM7]$Uc2֤s:V=$CqӢOL.D&A|HmI+ #Fkya\Ԛv[^ыĖHkj$"u]#Y޴xֻ{{n}Jyُ1G ecmj-&B6JPJW/%-ߥvkȢyxU9w I"nڸʃ2lZKLwbT -B6Uޛ׹vjWZK/zzlh&i96[IeԺ)!CjJV9RIreaH3ozMT랓@rn1q9\:wVN:ueJLρ$F}Ҥ4;U^W%jTz_k.WVpec'Oۜ~d337bk20[l2x\r7Y#{S畾zTg]Z^|)~OؐI&-Lkh~4:(ېRm{}nI!lk5F;(j\'}N|\GDg_An9@Y.ufD%m{FEۮ?=};O+on8 Ictzn[ru% 82ːrJJNԅ@9!ד_yωOsRP\蛎7knoњY^e8[f>swnVsZߕxwW.<5Wi>~v& iLm`0.@\w#Imm8(-%$$SWK4MWv5J_W7uj_ZǰvfNKԷ-kmSCIFS]r:YB*Hwk%,EZ[}o2[j WxDԗOl>ޯȃ L-Lq2]hLgV/jJwT&Q׹V:'omeVgݧ{|4[[sG^XŲŧYWn2But"-Rl)`g($g|uQ^|"orW++"1%Kbbq1%/%yhFuHZ5,+RtKX9e_3¼lWʯ{īj<;eU(!o=sǣqX}]pqG,otW}=1jӺyVMUSRi)ZXZr@7$8nbv.n;Ҟ|.2YUwO:Bi ko.ރ> &DRӱ}6I €kg^Ȩ\:Eiiμ!ʏ!el|9rԧ:EnM.iON*~澿q)'?ŭSV= vp"!{iԉ-+cJH%j9lNB\>^d}b歁Ǻ|i]aӶsu^eI}D'I.˯;o~{{e#5 􆽻s[Ț4\[H~2TZm\8q*\oNﮯF:R8¦򆙉b@KQ)Ku 8[3Y6oi \^̷hˤNfT-7ܾl,”ӋB-{aXhe~Wmzm߹czzѡt+ mH-NJI*RԥdVmT-=5uM;?mIXj`W# -+'$JX\?]Wi5Tէ@=e~18bt%AiKem8z(R[Q Npf&)%طtr|k_a4ׯ[FapÑ6;9B*yߗ؂Q+KE%BiSˆ-Pls̺(,VΥWuh궫Iu^{zEz*L&Xn-UܦIC˨*%+ۗ7Oj~j~]7FX͊,,Ϙ컄H[mݐ*(i=䒽(\:禀[;^sAjn-ȏFomƢZRӅnBOV2jum%a[傒9Uji?i17B髌왳\8d::I1ڣ9gh* 7[k+i+;?z&:Յ0?!;ħ~Os݌¹cn'jJ'IGnK&(߿Oj*e<0zsۤ gAL%/GJ!mEklrzIM{kfƒb Zm! sp78C`-j޵|:M+I~骒:禀OdN%*!PZmiRq{\̅^oaq}kz^{}GD|:]jWҌ͵eZ헵}ѯMR;({c7n[ijIc!֛JR6%AIJ) G4ԚvT5ޑiCcL9~ꨟs͕v,<vKsT*9ͯd}4wu뱯 ME>){wEr=w}o&r2R'k R9)@nhJc25eAZ\BZ8R˅EJSΨVN*򴽖'JKtt̉-_Z #B%v4HBZ}')PR$mfU$lW\fre1r^9NHYS}2pn(+b2767?j-WQfZ$TuwHl-Q:RKvz?j"$Wb_CZSqϸv(Dv/{miWr2&-7qξh}ȩtG[@ )}V]_pͿwcfͻ pLU@Tmeb[êe։ؽcqReR8?dܡNWvc!SŌQ2өe}`u 9ry~}=ieo. )\v{Y;Zv5mKͩBLpA9WwmwEoz}f峅Z>ͻBU!Ns&{ $:?oXJ TRɦj4v> hM;ct;ԮedPa:8Z[iC}I|SZוޝMcv:2D8t!}qm ݒsDVUKv%$HuMIzh\s@`<-GF܎]h s@:禀nO^!qaqE|e$e%LdAz>n(w% 8\a-Iy:mgzz5jC:w>YJ^|݆l',¢Ά͑1o0eVSYRʔI ե%M}}o{%\vu|ΫcXpGDK.XzSWm@ ܵ_l 'c*7m>}O IGoju2c[d1\EPqIAtJV+HWTZ5^]~zLz~*cU'..^6.2Tw6-C!͟ߞ+ePUމnӶk0 L>'C蟔W!|woS+ o쁁Ҵyiw]-o_Z;Z=pvbb滃Ӹ*#r1V-}n )VTǪ\WHuO+olоODj"KiK8ʁuGK$$I̺8ͅl;S0\^~zdʾc[Sx?Wj%OBE9*k= 6èKloa9N:yG*tpJ7(FrҋTDd΀{9iw^o%햛~-YmM8Ŷ Bv}Ԕ%$V┵{$j͹nU$)zTخ{E\g)$|^2qjQtZY'-Qp)i |+n)*@s|^?@O;xY{OsӼK:MGTwh)_Gz3((/@S*R1zܞGOt$zpoix9&s ?\:xTpbfr6b!l\B"kf)$% zS|}=5ƿW5N>N>tLpGXq,,IRrބfO%=2R\PڄmC>4^4r;{4?EKz><$O nPCJ}jsn4j\m{R oĖ]iԞ*Дvx}UT='BdRJomİMi0BUm L>RqnXoSF΂qxqɽMwuN~@hz퐣iL 8N8m@) Ia;*ܣ*WoCp8,e<]NxtҘx<+nU?{zkϏI IJRH"vK\,"-(OL=b))cRчV꒠bBk},[۴e2.Zqq \JJ@uM랚=4zh }: ;ϻ>%zh\s@:禀uM랚=4zh\s@:禀uM랚=4zh\s@:禀uM랚=4zh\s@:禀uM랚=4zh\s@:禀uM랚=4zh\s@:禀uM랚=4zh\s@:禀uM랚=4zh\s@:禀uM랚=4zh\s@:禀uM랚@wS!m_?QkRcm֭7ԨBBw$ u뗃~U2%i`oAiBv)8'q;].|j0EtV!IQ.2[()'hک ̐J>&/yjlB%&u͢!(et(,)1[(ڦeFy%ٷ,nJV^i}>v&1ys.R.YqyխQE+ɴt]4tE1,.H`HDy Q/EJ̈`yUrݼfķn}-])-ovפK"܏q2̄2! H*W.ҲU=U?n14gXuZBغ|lV&[uK ($T^~~ѳmsuW̽nxu> Zp$ح[Oܦ<ۡڌVT!尕% N +MA|5M6$SD˶72:J5SJ^e&H}д R\qI!Sz{Uy]6*^nj- +Ha4@JRU'&d$F\Is@FjK%XZ\-iŔԶK?%CܦK \4i՚2S-uw QZ5^e~>-hV!A%I*9RzV03*~+~^nhxǴ@b#$-\.:rOZEEl]ȗr*6pԦiHQ)S4TT@y(%ʽ|ķm~e5)ۣ="SabK64NB:gy+= '_2A6z+J[܃g|g%F5=I\uTz%!%8N9[ӺꟵhVNZ$Y{\te-(RT H@5ZZEy&tm:R,K-JĂ=!tsv/ZA6pI5 †սv1jֳܶ(q}gPTZH7mS[-|_JzYoPSڃڟ-*{P{Se@=OjjAO{PSe@~zYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=Ojjѧu Ҡ?=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO==Ҡ?[TJzYoPSڃڟ-*{P{Se@=OjjAO4$fՁ-*n.k!ĮfKt5ІT9dcU6%ؓ%|{ +r56к5hZlUtEL Qd&)F8w-b"n/t޲aZ5Ɛ=E6cWqmﺗҞ@K l%@)TY]xmHbujj m~=YS횊am0;I N72>Ӝ88S#PdiLFm0ǣ($ %vU5 KE5kwN>X$xhhۭrkͶ8Et&##yN R%D97wô^aJ/^%/Axl>kmV5Vc2E3xK!l[A$nܾk#(ӂ]?~Ӟ7,O[_2.xmhuA7n4()mt @~ֵ[_oJ~};G%R_Bϣ$:] xeHش8#(=jɊ}W7q܉TT[j@im;mᮑ.7#QOoGXq6-1 G)Q dQZm{dnpɪf5 +/Zړ~i[ȩ۰u{q.Vm_:+dʯ˷wa@(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P: y•_.|4P[!8A*m]=շ:'ZB: \qD)C\P2 U!%Đ]e$m IPV !.Cn.@-0ővKn%y~.1qg\ TOɔВ̐)Kw>"nLr3ɐ/*Qi͡[vU$}FE2z)+WQoW8ِRZRUOCg `O [7JqFb$Ri|Wž{pC#W|Wi}T7? Q%p@HPHV^>/Ap8j_K0gṸk؜hE%hweC!I[~ գ~ Hk8KOh FnT!~ܠJJ9 m\{m?>Œ4+q %#vi$v2wϙT߭+I)IAs} 8WjF:W@깪_^dubz{)yӠ5qdM^ʢ4w&jZSS!S*RVTJ5X_3:#T:xN2wd g+sѾ?Dpx-C1{HruZۛ\(!$U*1~eƝ{/p_S2 W$|En񏊽Pat:;>vgWr[H`F9w濠 ᱱ:%Ki]`t*GYD nzB/&) m7(/#3 tM1m8W-NojZ<]ݧadk:'( aGȂlDdRVA9"~"x G)O/RiJ/+zoe8pI(xTOIoiIq+]JӀķ5zKޤ) e!JJjR34qiO J)]^JV>/TF5KWZ+IV7ţ"EM2qu֜m*mA1B)IV$'F|82SF9c7M۪M[zz1pq<򔔤Ezd#᯺q]\ -}z5,rt,.ؗI?Jdnj$@\.UDZeJm JR@gw/CWr>w/CWr>w/Cg\qE$h%ڑ5ޑ 02Fyy ~ОtWFv NG-_v/ H%7zz~t$ޭ+oG=ߩ#X?[?ni^M?b?A;~QCsO~:i䎆a9ۚ{׿SO$t7X߁Bi#<&8R3/6b]OCZ3hrJF@5,IY__+4 ]ivүi}N*F̻qiM, V)Jwd{.YH6jOAM` ֺgrӭ$ih-[cn>AqºR[ojTFi_}<3O4A{֨Pz4_qZ JӽwU䪫?j9HP @(P @(P @(P @T5 n%m+!+^ӎe"v,0/)#? 2yZ?:o|qP>@ H:=ՏVO>jz# :@5 yѾBVO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jգ}㏚uhtZ7>8VO>jf<▷P2@P9o9h s6y@}c㏚Ѿ@~X>8Ϋ|F5c㏚Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|F5Ѿ@:o|qP|+4)QJJޝǩ51"#|_d{z|aPS}>jb/=@=LEG051"#|_d{TTB RE]5n?\x?9%4W^pۉPmBC,j'HYHl!zh!{RHϗ>|R Bw ׁ/|INSwS۠fک1%{ML[~-ߝЗ/%ּxp*%[[-C.[/AJW ; O%v;#lG^un][?Ûg C dV 9rK%ЇoA>Q@~ @(P @(P @(P @(*Jj}r}sV4|GPP.ɧ.&v7NP>V8XR\~wp:8NffώnIw Ln2^9X> y_cbbNVL`aj<{>4Rqyr$CU,)&995}>#Gm:.38eQTlm4t6e-B0e.Y*P¼m3U:oz3nsJfOU5bB\D:]'~q .Nnqu4i('$yr/1 w7M#soؖ֗ OJoDR HuDԄ6)͹;rrI5AX_ۋjtOCޒ:S'P$}z;寠>u|;w-h]w@:Zu|;w-h]w@:Zu|;w-h]w@:ZO \\!/[vK(64_݀q)vI"j9QR~OqSM?$7;Rcj-Glz88onԇ] )L4Nɐc1V}bJFN~jY:OUwQ߻ȿX"56M)E[ۿ92"LReIICDʱYWN/޻uDkR\3X,_{چwtV䦘p)w+&>3q)MU۠ޛӳu9rVd)08Ԯ td y{ꯖ)j{M1wi=zzV5+-ur3q)JPqRA$\^m&߂*P?%)$]4ZKT88նq^{Q:Fc; ӣ;Vm["5k}6U}U!Ƥk~ͫrv p"^xym8;17Maduda‰@mKܭ+ؕߞݾEmeCAڎM4 fnTx\";*KO/ze (F7 =s[R}5q|͉߶N~F5|oqs6i.ҕQikJ}!BC)ޒL Čںq^飳yb^{ϷU·Ujn;--h[d!mJ?A6YbV䣞Khܳy_-$u/4x4oɅ Ļ*GEuVĴm’+:,ZsÆ-yueÔZ$M?-Uw2_zY4o?46UqgWn7<3&6@ZRp U@JN"^ \{⒧˜,–)x޾ᯁ4&kwQN7[J7 ʈJF71Us&ѯ7XawyVlV˱axT徃r 9t̒ 0ӓ߅=/O6mZmEZ^7x|ɾØ 㩑26XiE-N{kܐ7 vඬ%i:x{}Oʞ2O؃6 KRp#t$0tM%RcYJӁG*,ն׺6da \sIV/ {-rќg7\i~kۂƊK[B_z@۠R9Boݺ2Yoخ8 ӜAEI1*)rĩrISpYhZ\mA|*wv ^x©u+..ke~kxE^r F\ \_LFҐF6>>)5X5)Ir{$^uDF=?j~W#.q qb.tp.٘vc8ȝ(->VXAJQuI'ܛ{~yejZ]Y'][kZ8û}4[JPʋ;ْw<q@/gk }Rk*BByECIFN-J.9$Dzzy\HeYݏ)#rd'k;vUk^+CNkeΚ]'MY;; RXk$jQTd 0!<=HҜL%ܥK]z +@syN~CE8/J0c v8^Y-滴}k{twItK]R AQ OF\n&+ďɵ+oKn;9b󥧑6}Rk*BByE~j)&q'*v p#;ZGCy2 yb1,G,Huh[>uh[>uh[>uh[>uh[>uh[>uh[>uh[>uh[>uh[>uh[>uh=Wz6mܓi1wZKdӣi)$9<4?rko!.WTE~pK>L% e:ҶV0r{Ԣb(M77mIy---+$-f<(;-W[qdո)帶%  [vW9J__1}{C6bŀ(v \w/URA'd'K:/g'eݭw D}bkfjн[-ĘV)RN6 `pfnr強QbWhY8StӖ u]:[qOBz@>}Pd!w^Z}ddžSmse() R{z22]#bnᆗXܴQ\G:*\f0N돼YB㳀ᰜmȩdV,ҮS *Csd+m.PClda/If[p#AΩ!p[Xf5c-:0̄!ЙHA$sWQ]ܙu7;wx+dgݤݛ"p2%Y*DEXmNJJԆTҲ~Z6K+ҍ"$i$=.E{Z\lbԵ_gܰۉ ^mY6۾ji E\"~ :y:nDW9<ǘuܘyjWDXZtĨsTK]Uvnf%[Ȩ$V",WZsGiĴZ>:ɵ'>{Y~2kRFu)ؗ ^emu4+U 4 潴tU]m>hk͛SZf۵-Ż݅: RVSGjHI)$Yj;^7wؙT~}w'c:O+W> ZDr90yhU\:{HV5}*LL4פ-Kyy$J<# TMWe'D#8ˍ-vM%!0.cZJ[2I*mz_?e{Damk.]lZ#?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:'A9=*zԜe)yH+%o `†ybČ rP(s 纀Rq=#D <?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:[<<ӼRjXo!rdF\kܝĎ|> Ej}dۧLpE]b\v:SirҼ $a _nW Ҫ xX `` դ/rV6E}ો~}:T+[}JQGF*Qsm?Ӹ6kEEgWe0e٭p9!2CrLj=dֿ~_b^ ~ ׷&wVqrl弶C$r |vIE$*1~Ӛ^\RIr,ݷ'mlgԐ:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:?@:B]?G2h P @(MߑMq{)tn_,vP w-4bdBJZ})'B |Q6lbrjT֤DFqIRIJV lN}`PxF\!J˴ݶEېPVB>6m8.n[)]6wv{+OMI ]uҶƯn"8!Ceiq)IC)[pr'%;䌠E}S,j{ykjGkӷ[<NVFRV)EO4^I5|92[kξa.}/~}/N~Yu+p,93V$fW.UN5NaD/wϾn=NQkuovMCJT޹[*߄ejVTk&~֩{9~]Ӓ_"<!ܴ PYu64KHvGmSHu$'!+p+q5щ%Өy[6÷۷d4Qƻ 4yOLDyqhؤ:ߐX5HOYg7be~N{8uuvߞv[ncPmIR )!9Z-{Y!{~Zw*Kbgמm}r:oDr|ZSj:P2R3F*⤽/JR|~]@P @(ko+g2bJ丈ZBХ`C {8)q =Cw׺雄J;Ɔl`)r̟rF龒px<^>M.}tw >/?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cpWOM>?ŏOm_?/4?1?_}(\DX|X(?qSO`!cq4ϩ'5PZ\Ҋzw[)Jy++zq^sXz˦FﶴF<6~x>RK;5˨[~o#şB:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">tm:mCrB@rh /Tiv:>Am@HenZ<^Hݷ9K[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">ߴ4[~o#mM@:6">eS 6-YP @(P=ɠ4.?\x?9%4W4'gR_ +m5ERm%(R݀PgHR7,qA!&޻ΒաxkN_ibxR ׌r#Y{\/.Uy%"I4|d3O B v7;Mge͞b`%QZSLr*J9jҥMm+7_R^Wzxrh+ ewlڶiKn]aòۄR!+) >'"]VnDԓkd t 5&KnM\t:ӭ-T FnVsbF[Oû]7 ϚiW.ӢJqg_ݴ7źLKԿ-]:IجoH'ifĎZ_~[>:}v(P @(P @(P @(C[~[?PO?P/}} jj̕ΣfuHL,OzUg~Oy&t_U5=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f _پC@=WoU4|f \ї\&u )篪nUλ"SYޮQ=Kv9==K]Q'Eny=٠45E.\Xا~CaJJڔ2v'IL81|8xﶈ!YNHQ *RPGpFraNpR}&Ea79UkF:&CQma/Y11#2RGp4OuMXg@qlg2ij,3!֐RdHqRVcqt}+ȗZQBՎRpG=s,.-{YKnS@qa*YZ"7 W<(u`iqIo{N S>z}D^8Bᮬ6-*>E!-jrHyzCkJ﷟Êў8]h*/z?^AbS8FYF{>PyjeCP⣚}4GSm'N@)R9V9|$r,OH+Q`C-m4ݹ_!8_@gWV̆;i|ڼ5ba Yd}.<8g-}5:ϦպSjH|2gȔIVs4NY"R8SĩɹOeIy gNz=ۓ̌y#aSqƵ$t#g@:Ϧ^7?3sd96毒]~0T= s ?îװ獪vFQH͐ӉmM cqJh*>@yr,p\d.%:8O42r%j,tcsd́Ve7QVn"_ [wS%$qgw,23GW3쉰.%bsNiD+֏)f23Ǵ1pnK-˨yPRTGx>z2羳h LO*=ƔnEKRݭ<9; Nu i=)n/$'lkuRvXm%mk]Rn6OuMx:ϦuMvWWq(*iwܠ0}-^4q͜g@:Ϧ.IYaqGRI?Z1rj1ݐj4"_pCMmlZg!8>I*p|1&UF>> ,5('ZʞiSJh#GՊz2*h/zV@*B2 G,A<ظQυ,Ѷruan NFLuMrA'H_\54+k8in!! 3ʺ0|Lm`1an;rq%Nt}@=ܫY\FA1㼸XFϪaWW:5H

    R;-VN..s?T$uM>}4hYg@:ϦuM>}4hYg@:ϦuM>}4hYg@M[?5@(P @(+Sw{@h\~~s@Jh 8 W CtE뉚-\iӴFn39NBi*KDd°-TkdzAMmjk0B!oZ!ߗb1O}+ t :J TUU.$$M-iFp +WuEդ8z~ h[]ۓ! ⶸwzPO,MwYoi{4,_2Wk!ci]On^~wlĸv;tH[gxt:I̚i.~漴~9^ع@%Z4f[5EY[l\4XZ:lh`veĆ")-~K Ai5ݛzӣ< g54ɵNTxȵu:A*C !{~J˙ j JĒ^@(P @(P @(P @(*Jj}r}{^SPd_O>.c/qJ oЉ//?γ52|d4'hO 9>s@2|d4'hO 9>s@2|d4'hO 9>s@2|d4'hO 9>s@2|d4'hO 9>s@2|d4'hO 9>s@2|d4'hO 9>s@2|d4'hO 9>s@2|d4'hO 9>s@2|d>?/ƾW,$%pKU1A^B;䯕=BQ B^R9(O~|.Ƨ͚,G'cZF”OQNwe+ʏ[F+Kpvbv͘iKYBW Zy;ǍapHJ8llWF v¿v4z~~Jr̤rkL xѭaOvD6}嶕-((9#]N=8L%n76Hڼj^c '؏v].lH=#S%2NNyw֩4e$m{4z;hl,nyOZ%FRv[*Qyk珩?:eWw֓ˏ6ˎqZJOs{J8 Iִ[.246EPQ'dz;\xos'fmS1Zt8-Zy)Q>T$GOxi;Z>zϱhԭg\վ[Q]yqѿrʓ+'^#>&jm1 6m"LT-]sTTR*8 AŔgz$10zeoWSGI!b%i ުӎK*jr*;7Z>z4/6vic@'jn$$^5_eO;s BuυMua 8ggK\"X6*P ;@$+͟0c$ܨq \Ej֞fui,3mGs 0c_pDۙ{G\gTxsV4yD7yY C)mn|R[)ɻ5;L1^Ȇkc6يq!2IʹrNO9Ň{$yOmFW_պ zu6B;JnMxWGabcM{W'8N;`zh=-o}/#rAClRR%8 8@+1r*G6%99;?Z>z =V PndU><с3*E7Sߚ"em!R;kxL>+ 5U~k;QyuRCq Ne}'"ħW\gq.,&VԢ46Z1^/!pnWq ܒHp}zEcapY"3FI~f[K]}g#ؤ}թQǂi\o<~X8"LYbC"U/Ob]G_,}O?4(*[s^|#(oY@J)|zkXxx4ZvmnOgvEϼ\ JÅ/ <3]06)erMvJtq1aa(e}SsMzP!NC6Ϲ$|MI]Jst/KJɼ_Oo}KI#5e=G@:|=G@:|=G@:|=G@:|=G@:|=G@:(^t+u`P @(P=ɠ4.?\x?9%4WBMzmFtAs,vS<x^jsS є<’}Q>a./WDB^NupG7}{S[ĝ.}QID,uS W; mRw#z+4S-!_;GEP @(P @(P @(*Jj}r}{^SPd]t}g_q7D~fMEYM1D^3nM֜,| ~]1?w?@?`.X?hEK Ȼ`߰cs,4 "~]1?w?@?`.X?hEK Ȼ`߰cs,4 "~]1?w?@?`.X?hEK Ȼ`߰cs,4 "~]1?w?@?`.X?hEK Ȼ`߰cs,4 "~]1?w?@?`.X?hEK Ȼ`߰cs,4 "~]1?w?@?`.X?hEK Ȼ`߰cs,4 "~]1?w?@?`.X?hEK Ȼ`߰cs,4 "~]1?w?@?`.X?hEK Ȼ`߰cs,4 "~]1?w?@?`.X?hEK Ȼ`߰cs,4 "~]˳f4jM@Ө[EǞq唨Ask̶wPjA)Stl#ngcowtW[DH{kiu+|%A;k -O,OU/$@~u8&7pzneRc}!XPP{ϘDk$ϗ.r[Qm$;sِAvOk1Ä6wR a^^b{k⸅>v>7d:GZך{h34$D6V\wIRG|ܽs#8q72^ô0:|$;aWI'1n S.Da\Z1!r^Q!E@}]n[N<̔huVR-(@p oVz$fVըā|g&$7NqyRA)#̣Ws929L4u}Xen#wGE9~s~i/X: mby7tzǙy7!䔀Sq8|(Ҝ}ەrfUw}iiE5\ Fsqv|_"5Z̋zm-#8Pa>^~MN ./]uyO]KI)293] O:кў!ln3ɋ6v֚?koK~z(l.6vgÀ2~J1&g)nϪT"}kY4iq*ka%'g4" .m'Hnq_{p)ljUtI3=,TZ8c#lEy}9wk司k@B;de)f)YFsOH9 wv*ߟ8o^5)]KI9bB暖V=ӭ, $6 .8AuU1ݓ82ys5q8Tb3xL?WDZ)TosvaŐ6RËK,Pb<º#bRRkS^"ɷ4u*;)ydz τసj"Z~{WiZTr;Dr.x0=^v>{z}KI:fGrk,k@:fu_ֿ4hZYk@:fu_ֿ4hZYk@:fu_ֿ4hZW @YP @(jox?2h #h MrcsG-lfܯ]0`&{%U@ϼP55 0C, %#Ӂ>ShcI?VlZ\'<1FAWÞIŧG<2jjw}'è}e;jspHICͱ-kqJa'b렺?RTމsR(zVX]No4NUI'Γv֎lο{YVd4S48OmڴRR ˻*Js ZVkduLZY/'|: pH-qJ-$`SgNjQIff+jV:ӢfงRF J1n S\ٷn$5~6_vW_W?VekVq˲/\mjT9EPjvFb8) UҦi첸{~TM֛hם.OFGgnj2o4n%LȊ=&C-lHf7P {(~+n%ˡJ$FLdfUp5hFԒO$uN7\ ,;N7wZ]$)K6zaLs#|noa_6M4W)X?3HFH le^pA*Sw8wR?R2`p ٛۏp *CВ,¾f9Ҝ<s3(=yt=_y %mn8=Uʉ]}*n[@s=_ uUzͯ^-.0zJb=R$p*)sT-D'p"p~W\ 8Lǹ<#`yU6W#=s3e.;)#zH!)3W)^kI)lzx,л}[g8?d77~ӟ6:SD없3Y| OF\EŷWQ\S4>t׷on(|'LyJ^m-!GzJ"(A|njSgP9VɥSqƄčJ΁g#z,+fJ2L-NM, 3ZN-4S'y&w<PFR{-bԋNӌo?KLvTIR"@8+zQRS-}jA^IDwKU^o ׵D['q9ZH5GN)/Tn*Dhj|]K`d8Elއ̷HTdQ(/12aHsU^TlUbg]jC25 Yܸ {U&Ax"͵6RvsTYF^.;y-69gg?RI[|S=K'P-޳?'KVz %MG'KZe]e<2$~H?d!g*UFa_M**m%@qn=&`%*4 U4awhFumg/LԸߔ [1xk̩-z` B~bkj~'캎 Sb> 6%e!0qKWz+ Y an! wENtlRۏߒ2\=hȇ\̰ P5@P}SC%._TH?IUs'՞?i&Ե$^IOyrH%]Je:_$5H jC:@jUaƏACX5_xʿYl|Toem㭉=ͺ89y?6!dK=)!㶋s*I? ܕnMW,hBv%M2R09T؊Wfc{7j =!1;sF2gMi=*[hh>tKlRFOo<{پC@<{پC@~x7R?|{پCPHof of of lGc{7j =!=!=!1v%-) J+! TNII$Զ$Oo<{پC@<{پC@<{پC@~x7h#_=!=!=!=!=!=!=!!UjRXʔzW9ק軗x c<\L;m[㫆N,Zkk}Gg pqIHx7|Noҵ,-!j8DZ4DZ4DZ4bpK7vIB=4#ѫlPj ==rCV7,>=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!=!:L,Ww%Hs@(P @(+Sw{@h\~~s@Jh -P @(P @(P @(P @(P ml@F[>q@l/{2V.QK\~ү8"x} ? 2%_ԇ_({g+mumStùFq/*+s1k󯵷੖Vpr,:~T&'!tu UI9z̴EHlI $rUK1hݬٴ]WkD$\sbЬ|["!lՅˎs:lDWm1p%rkE̳a6|/@R@pv[1=E=Y{EjFjzqvKmT~U h6r bZ"-( ED`UORGWS Sa$UH{TRZ8W?ER٢ҼOiru#(5{ؚlȃl袽1%{ҁ"#vdX[p F QI8D:m*J»0Wȓd[)ݱ)'gT`FKGUF2pW'Ef*"zXRdpsWm=m)y)9BFƃPUNP=!i!z;&0<.E+O'y) $l-ƥwDi{j4LmMˏ”2*^vm-n%E >#ʡܲ6ζYO9}U;jjTDDTvgHϛCZ䗔}$mZWˤ!խKC5}lE%'1YߎfXpSYFVpQl[:=8MeI-BPf KJ$"mZݎVQVMdD[lJoܫgijfanLx~D^YNG䩌jU*.f jfЄa~tWq! >kry,s; g*=ԣ}YB[nP>ճ 삓%QԄ%(wNkJD! *Ԛܡԋ.mKD >CdCMrhcjBD]2:U-L"k̨5GF)šى~vT&b]K'yVi6٥$`0HY;ͩi-!)aDBNqZ'#Asya}Ȓtoɷe= *2Vg@mሹ9򁂑s.!C+Rq8>jVxՓ"۵sy8젨'>JkZe4BPmCDŵ$;E~/]vCJBFOe)֩-lo%3]K/+!#"ѣVe)`)wÚYqY^uE9J9j/ɓ0!#nx- 8ӫ!G&-yU%Ĥ$w/I"jL H''ȴcdg[ֽण'$?}Y)ήG!"KBC\L6%ݡd,޴h.̙=1$EVVHV=ʺ*e(wSKse}5o)z6RQ*qT*>[aBF_B Q撃֪Nѣ=&3Xڎ)ze#˰ ̇;i^sl:3D.R@ 1dZZ3eK/ܗ&Tp4tꆦ7q)m9.5 -}i<*jYSI g*F9!YQild3}ڴ*C|T\PCl` D&!ԨhN@! =B-tzZTїjunXtMiLΙ``ഃӏ-K+fښKo N1jI6a]UV)-+#nBG֒NZ2Q, R>;W/I" 22YR J%a Ų,KKZ$Ue5tNWVl(F. xѷUͧ6d$вZs`_\(rҋ֦z1@ R̹kK d#byjU-^k'չhF ʹm- Jw2*"V0 :y+TWes\4"I$&*[Գp/18T1+TZÀ\5Ugj19A'ƾW>=@(PZceuLIb!֝GzA@4?ќ,~W :Y*L >l6[<s?+@?iZ~W~_δ%˝i_JM:.u(5\OPk֟s?+@?iZ~W~_δ%˝i_JM:.u(5\OPk֟s?+@?iZ~W~_δ%˝i_JM:.u(5\OPk֟s?+@?iZ~W~_δ%˝i_JM:.u(5\OPk֟s?+@Wum)9%5DNG=s;כ;mq+XY-瘸Ҭ JVc`pkh}>7~_δ%˝i_JM:.u(5\OPk֟s?+@?iZ~W~_δ%XѼ#ղ'_Ʀڎ4DMRmJyQSSIcn{M:.u(5\OPk֟s?+@?iZ~W~_δ%˝i_JM:.u(5\OPk֟s?+@?iZ~W~_δ%˝i_JM:.u(5\OPυٴ}]$ɉ&ԡg hIt>)?%tw]u}ZI$I$9ڑM@(P @(+Sw{@h\~~s@Jh -P @(P @(P @(P @(P ml@F[>q@l/{2V.QK\~ү8"x} ? 2%_ԇ_({gOI5=Usu 9V&L˭mnnsF+S[?']-nt. HVpẎ'Cҕg }7k@l8{)%%Ї )9ܵQ'mm*ހy*clwFԖX%YBERV*x(hgx]R՗tH]W2@' hʹLIYV z9˴۳'cH闎Kh;ܢ2PΪ,f BS2OV>=39UMZtIPZeYol#G LQtJ2UުeKv33۲3?Ţ7yݤz1Tʖŭ+[]P.m9)?C2rBcԤe-FeӌvjMΔ%삤o.2>K')#Z9f|6x>KKq3ga&G㡳&ۈK۟N*Kr2Sϕ`]1pӰZ_71nDVPIsi9ܪ;ae%\*Wid %02r--QI.I'uLiWFW:ǛkRqGS<KKCLaD{Z8"?f=H5lHԶ# (kŴef"RRľp9YY7Zn: NK|˜EBl܆`9<)N4ҪS&s-y2Ь>0mkRLKZ=vy}ʫ`Ņ&QАڼ>WRiF2pta)REe`){QDWmB`AC)qQkm >v/_ 8H'wTuڀһ ;UoqधqJ-hލӌ8'UY&Wz}6!k cdm_m AS+%$EUQ* $藑/ˏ I*0w v\rԴԇyI}WJ"I#RZӾV9FrEiކRKrBfv=ͩ BOskUў)X *V㓑%kRln1+]l|o\D8dg!XyDch՘%]C(mhu;T̬>g*eq7 OxApS񩭊# *l,օPnUϡOhP @(s+*84ިMcP @(P @(P @(P @(P  ]Qt#7VLDcOq89!4P @(P @(Pr#\o.),kWJ9 jHFI,P @(+G5Gީ_UyE h v{@mP @(P @( ~dG+Fh uP @(P @(P @(P @(C[~[?PO?P/}} jj̕˼?BL~W w!R"ڔQrh-}ňy#9),ԍ9cj=nD1,r+5mѝ[9 "{=VԕćiV`2a?z1V/7LH!GH{ e ։І?4)(+܂kaTJFM:ܻק-M4R{ jm"Vى\ `=eHo0B{܋{ƚ2i|ϒQa{C}ʹSpIcJF\nI8PJ'5e4/-BqAM u,ULd7 $՝슮Eq_*RZYlBt(G5VnCrPYI'"N4hH6:y4̉Р)8RV-=hQA`-cȫ-!'pY[%}ijZ/pU]dH>h!N$nZF?fpLnjҴ%gzq2t(fFDh`$FO,hHX_ma܂᭚f6l2jBѥ[PK FSd IE*O38ɬ:4Ѧӻ,8TIyU]Bb o7@ lOja40OK[ye1i:V'<̊F.=$64g B+]25'~[d =cT*Qž8Xm_dY]2t|$2^ 'pZܵ1i5%0#/{ TZ-6Xޭj:?G06itcrv)!2' IV}*:g{IvqWTjQbaRh*Fwhӭ\1Z;NMs4,M.]_S+vKؐvىr+O}Ww/}_*{BP @(+G5Gީ_UyE h P @(P @(P @(P kN,\t-gi 9oܔ`_uN!l o$AP(*M-FD HPCRwsr%GkY6nhfyu(I Bj+#o4v*`rzKnLt3 Y>S@qx@)e=S< n)=l%HTMk(im)MnAcD-N1ťlMm%Ɗq) sFM*H{i%9oj'7ecC Y8k&imE.^hJԤ@XېG.t:~Bfi SɭMj:4K|hc@JwR:c݂/J7CxPc4Onq* \m$Au H\+V*%<ʈwWxbqQV87NVS*;+n"ЂZq)E rBqT{Y_(*]cs5ƝKmuIO%tˇz6 ;LۂuT-4!uB:VE r A;@?pt)V!q.TQk3Tfl+C*VlrPZkٚzJ/NY/341} +a%%# %bw@fխk+vvt^udIC9CY#@b᾵xz2)uўs@224P\"1eL+l+֕'gɞ,#%B^i# (c#:+*84ިMc!ԏx?2h @(P @(Z#̚BSE~ܿXnP @(P @(P @(P @(ko+g2eﯡ{MCْ9w}^_~ߡ_^I*N:C?!!ښdnqAss+9NYcejEVrŽ@5ΪZ`y,eIݻ8 L&7Q_ oLHRخMyFJ6kܧR|*З zioJdo(VJ@P;tG.M-M`$tztևo*;ZP8kZ]Z}:vI5ˡ."\K5vUhXLWi5g%țm>- ѨY^<,q>$Gs{%wHk OF/BA9qX;OVluc$7KB71Jz ;Dvr=&:! Q *eʉRlco\%;CA$tkIu4/wFKCqԤ+a9ҚzoC{PNgWK6mfXfҒUօ+)ns 8U%HNX:#UA-K68 `c5(;miFm,8M9|R*B(ryjE6li*Gi^᪼IsuVԄIw]'-M1* gr9ZPNKy2XP'yYJi.: [?TJ9$ XnDf\Ki').s5ocܷ/2i6{XE(ܲ\1)X$՜-Y9\49RXBrS+hdb]}򕒐 WM]HHn:ydLi'$MFgvS lqBR[ #gO2|IRQ s5Wf󮗮INJyI"LݾzcFy4C}ڴ[t E)r+Y5Jjfz̢Kjy ڴ[E1cyip cVI Dt#!R39C&,j/tݧ,p$"QFY Hꡉ)YR}hǦJ6" R9{XZA%.|̥Q䐎H/`D^a=MV3fgtBfZ$/8IYoE&mP9-7 &[Vi:nzV+fn(}؊B(zj^mֻ݊bz&m;O:[2f2;pd殨K3Ceǣ]:7mXNk^4pT *[rrQ̡^K2f\\Dy9v"FwJi 'v*ޏbLKy lmMi'*,?yӎ%js8ϖrʴ&4ޥv,cqrI-tETe%L!ұ[;fjڏ1YQsrEhϮN0FyG4vF< 㺀v*\X -C f.餯W#ݷ5-SRd H9 Vؑ\nKݑ8_^\BC LJP#j 8 Vf.7ޜj8k_Xi6p%+ AjZ Oh@J? X܆Z$ǎs[-9aJR6( s81w.oe]Dbj}$R'c8lbڟW\ɻ]ZbBZ[W4%HtF^ǐ Zz_vq_ ,bZdRu7ؠJ$*H53~i6Q"ٶJ3KmڂTBjδfmV;"#i ZRv%9ʀyq~|=XafDI+fOsiE*rRG2pHq,㚿N\_NtpȴJC!/N{E7@@~ϙukS$ika]Bո.E3%jTt-!$ $|`^j/{L]CS5usK@ pbG xBp@q[ ; a\И%0'l3ڥ1ʀYxs!-P:^/)}M\#ZIHDDd-4]5T;~.!;gSmkRѝ&xA$i9p8ː>8VNVOPw.$i~ZsJjĀJNN \΀k[oƈqn=jeu@ 2{drqZcNȺ;zÏH%#ENJ{Ͼid`jPx[ʀejG>JkKIvXW>ʤ/=c+ C'/mv;NjxCWhjTgzRvINpp(Ax7ERsHO5($P8jRBÏK:H&ڠP @(P=ɠ4.?\x?9%4W@(P @(P @(P @(P @( Ҷ?ڠ#-\x86^=+sx(%e?_YW<>@AymjC=õް\ t /#$xS\5rG[m*}[⩷Q딅uo:Q 3]n<ѵ Dȝty MC)hZ̫%*ԵqTw + TvU fivKh}čc9K8$d}MƶϛFђ*-']vg`X87R՛ws䤽#JPh ,NA!wOv &B HbfN-Hi*NrO窴2S~ʷI0Gm.2Rh+Ԙ,k)mX)y=&6s][die ZEΔͩ*O4gmKdųjmU.iK)KW=Vۚ&; (IVn)^Z6%]HʣMh_r\u85 NN}5]Qr'5c@zGF検[LJ˪q#נ\sk˒)JUZZX^je,XېI'ߊv̖NyC6ža~J7Z}G!iR9 QRweDv' .- pA>UFՒTKG%\ܣTV"zdӤtzYsNzڔԠIANK _pbJ0%) 2q՝+/7yՠg99ڔV1ͧZnyj2찔5fdjSȒLEA=;Jwfv(`-O%2T6'[FJ'. I-2ZQP?v4Eŷxq<)C>R/hm1_or?[Ȩ2[lՑyIѶ|-s8QT878#8Jܦt*EDmpfJ5d+rg)'2w&ԩ7 9k[oCN܂cc m0U<]ĝlIT^ .|;ǘҏ"يC%.)>NdC+:ar;.4FOkʵ5 iHlz/:+X<d܍ҊF3x}fUu3KM7EĿ$.j6Kr1[$ӕFr[C2\ 81^8yuDޔ{rH*m2iGwTI*{,mIJZ|R@'ᨧvKIhlG^Sd8`yYRO[ipvj67=)m:o =Gݲֆ66'/FaŌJԀAHJ9V̨l2u] ?JQw87J}~Msڢȱ?Sxm$ jKg29㭵UDg* PnUϡOhP @(s+*84ިMcP @(P @(P @(^_zcJNY+!6)ܬs V$Ž@%nGRJ G##@W[r5MjJS'v8u+bJHwSwx}-QNX}jXy’`$tJ"2Ci Qh9;&eŽ*Z,AC=oY@$%е:É3zB\TFƒuB\^%]5R5Ohb]6oJ*RGڐ>ږkkH:gCE-JtJҥyJW<@p\^=%II2gŶ>1wR07d-DkoS7cpZz,$'){}D`Nշf֍Gi#>)h!c yf -K 3ձIPBכ3@odeƳ[Xǔ)`(r(V UX?l蝎\Ys9)'P f:1*A2DBWߑ9Pd[-,,",8%Yla(BF}YFnfg%>2s|c]/t}[VOc{LIqY <qz~T),t9ڑM@(P @(+Sw{@h\~~s@Jh -P @(P @(P @(P @(P ml@F[>q@l/{2V.QK\~ү8"x} ? 2%_ԇ_({gEZD 8+4Q-qq䩗Eȥc+96ѴU*]N;G-E#-C֌3a)!x̚r؊$-W%[#6%9a:MIez4}m)5M" AP?5-[#d)Kmiu3U9ܺy^q)wWUC}k wH#2DAnjtj^&W-</K?`Z^@FRn4Sbݨ#@VU2j=}M8^u.:)y=ʄCpvKZռ%Lm=)VGdC?Bw&f Xak +skNߟr;c*FQ!;VU/J0wT ]qv#PyRppkeoRiʔ!tiȪ7DmĄՖ@ `n8rT6fû:F{$v(Ihk+(qj q#hDP1]jcl^O*+ PB,>y5E-nzT{V\uKFgl] Nbhٷ& dtS]S)VrsO嬱._$+7&OF%%UUg8Eje(h#:QN,7GTlF9|ˤZqhaй *JFΥT!-ʙ)2bLjcϳ𚯫[% R% f/J56$-kJ[dn 泜*=Hj^eԥzyE[ JkYwr[+@e\^gYSJKb,!:JTTH c+P0i-5DB[!ImJ'HwPNXp*$,'}uM<D)M>L8 !B? i;d֟nD eGkD62 H$?TO!HZO[nirB#+ 9J7n89*΃:g]Unli$qit,% )8U  _Le[zcsM(}+ddry@Z_fdbԻ$/-]N $(!i%$02 0@ ^Z5t\Dj j 貗)$#v哌>z#KqEٵn\zVjS6 -)%`8pv$D|:^꘭fbwDm QyFkk ;Z-vy:>KDVZWL7 pp@z`KjbK}nZ$!G*BRRO0Pp;6d^,Yc2Vp s9_k >,]G ?grK)}-i[N0V9ڠ=qC:׆&[&i:4+Ӳ:4%IJh*ݎdK':YL̘͊ 8ۨ9JFA9wxK~z~ 6q˰mI1ܚ6) Ihڽm7H%% җN@c<< b7R}/,$C㌸eĥaIF} OxrF`j&1їڑ IAnAH䒭;@"u^@B\ 'Tݑ@jqz~T),t9ڑM@(P @(+Sw{@h\~~s@Jh -P @(P @(P @(P @(P ml@F[>q@l/{2V.QK\~ү8"x} ? 2%_ԇ_({gz氀 M'Kju66eŨh7--[d J2O]t(j RA8>d)'mT 2sF5 ^]+m1_7ﱦ9 x"AEhޚZnJ#d$GoaGZW"%OcFi>|nBR^̅-1Վ);։h}c=rcу֑+(\JcOB$Uyr3~9Rb0ǝZ0 K[Dѫ Noyҥ`w5).b#t % NΐBgeԾj*r%*ᡦ,%n(w; zBLrBAi lXRe%8JUْ%djG[ hwVh}_DsǸ3\]Daٯ9M m2{]>1[RF?iRz2Y6:$]Rj-&y}V{ Y2QHh|I52bGbet85,Yupi FO" O*rEeF[nY+%֟/ӖTsI7+ _M5⣱]_KKq+"0;T9ӊF0Y֛u [NIsymFHΒelv:83Q{F'k֯@ Ѥ(ag~!3ќv﬛HS1 sw5Jdt9oPS 8B? YQ 2R*lYWp$` N27I,Y;'$VmhfuLb˰E4\JWmg*Qem孕HK,+9lh<%kj |MKb.b_mV2~U-QlQy}QRێ.'T(b;YKKJBVp rIS"eHK@J-loY^$nsюC5uK5̳1{;#'$+ܬ\kB?gp1g5'kSJe c*(;j/M 7!fH-6; f]=tG3  Q=!PIDKqP\IF2"䢈Q߃qӆ,4SkHOYז2C7]G)1?5_xʿY=| @(qz~T),tP @(P @(P @(-aMʼn2zy(q$c#8=./X!Z5+-KBp[<yG@`ȾZ46kqĂ[n* $';$4啩4ѷvu$oE 5B68%#9廐hC>#ԺBѨ"k7;.:7!Hm)$+rHܥ Hp&Mݐ7:V 2rrˣX L mPi^Ugc͑q KS'@{j u)'$ NIϺm3#E%>% 0qscnwv!P ͋Up"cxc!  Z2FѴ8Wl .N:ʼnv})ԁ("7d 5gxл֩ m.(]x,RKBSbŭx,^ߛpSH6BzFO%ܵliQ!‘(%yy r=j}c-:yͽND2%;H@t+jyv0h <nz/,!YbۄKDg][.*ZN01́d_9!lY*T@tV v㽳NpčKjQeh)IB' c A&^j6%'dTb+ڥ@dvriҼPjAqSzkJ͖RĕꕸV;9PP1@Z.-ktm4̾kBD ApFc+9H#ַ뾸BgEmvMܐ7IA < Yn<+ᡇmR/J^=ۈ<=w ʺحf]d' g@HPkƋu[o-Ӷ /x q֧0&[ƯN^n뜖RS` ې9g<6C˺nZRe)ya!K*s5T}P~_T_ЦjG4P @(MߑMq{)tn_,vP @(P @(P @(P @(P @T5 nн[G>/{/qJ oЉ//?'{oR|~IT=mny*ؤtT\Z4H*[;='TQ۲,wf`ڲOx +N]S6KL$Dؤ_^`39m=e7Ej=& yK ]"4FJؐJҜOrqoSD G.E 9%ciEENhs @N+d]r%hٶK\$EdrkD0-0J+(矂Wc*Imd|>ة'w0› -=qݚvnt=dÎU^TNGtT嬞R{9-̷֖Jz „8Է#*]Ф4mޖBM,+kDj9%CݚUP^J!JJ.Yd(e"wR$~B kB{s= [ۋd5)JvdUaӈeQwT? ZRˡ1F3{⾅EBA+sW-7:G%쾹˧רv Žw.-fDW62RHl\V##r<:풣͵4,&DrBB - <ҔZ"R%<{+R@ڃdP X䥀H>UVDh5#`HpO/Rf{]ROOeh &a=$fI*VsKnm;*"-0 [2*ެƯoȥyuI?U-ܛ"d]}+l 'RyR6Q{WT$H8d7`Vfm-H*)J@嚟V4)eC7 hw+Y:7Du%rc 9%.v)^Ţr*sU":|ELdBIQ[bHJ[W,ykܖ;T]I}IB[B r+"K4ڝ䲢] #Ei[6@Vҳ=ڇ&)$RFaL8: %R<µqN> _a)wbmJ%s >k' o\m3$"$ޅg0=#*R(mi([ m|ʐTBxWp{JR@W3muQr٩ҌPy+ZZۜ th+ REU]/v2lϩHm) D{Dϗ5ז1Z)I\7% Oy啩)#5TCaLSi)[jrEN<@X#ˮţ˺}FuJ[O(fͪV^@h!)-i0mǶIy֚.x =\IVRUU3Z7woК9!1MOv*z̑dE=Do6p >iGSNkcYtrfвrzv*[>X)Iꬳ۵Pzא`|Jk))*D}JHAm 梿)[E]f1uq_*=4旪'$4JAVpHyj)0{#x=wV5=cz*jќg_bk꿛lGzSP @(\?y>J ?{//S@XP @(P @(P @(w[t[>ֵ9 Tʎ2Gqũ-[ud?oo]juSVVTV  uuL;l4vO$=j)i}œl>#-˄q욧Tjd6kz]CɁSd(nP9i{/+1uJP-@pyR▦6/G>^C!62=C"%YrC6Jy2y(}Y۴ws)du%B:)RNO5"G3qIeMe I#=yy( 昃}vnr|fkrɍVȄ$d̚Qʽ\_Cy7}P4IS F%+sHBe2U-,< KPC{07ne;H:Rmp"r9%]+.s)<Y4W]scMro[ۑXWHYvy]/zBFR BWo~ՖB4#&kUҺ-$wc$I眚ѡP\^T]}0l>pCA @4n՗zV &,6L";mnYR}vw-/\{By֥XIq [έjmH-G$Rv|F֚PhKC") IR]܎~@T yߢoڥi# 9 M.F̔mJ[yX!CĀ`|⭦dO!{TGf2E-0@ٽiH^ crFԂFd 'O_^i˚cTŧNj766gRRv@.q%dkM7O]ܑ@JzPp9(rRB{gc"iHvLWrj8ֶ`sRR6a}f8ni&˰-ʺks)P!I9'qɵ?Dx#׶\BmR#ґ+ =帚zNӺ ޞws!Ko {NA T>:ϢF|3m%q]×?? 3Kq .sYSL[$tWUDi#P%wċ .u+S*!cv֜ ,g\49s1n254wcgrNу$  K'NŎ;|$ yTq=4=ZV]>fKJ.r./^Nq9gvry(s+*84ިMc!ԏx?2h @(P @(Z#̚BSE~ܿXnP @(P @(P @(P @(ko+g2eﯡ{MCْ9w}^_~ߡ_^I*N:C?<0RFFz%dgzUd웂O,s\VpkRsmIs~9U "WEJZj${5E#b'~zh:ˁ{6k%9rx/RޥZ5eJJN;`VRehW gȂyTNHH.>fEO8`O)P/Q͹UKjS)GI/ʎքܕ-KYD6R՘K#)# ذ>U Rl8Ms 6]:!|Re5H8JVHtld`[$ռ;$\^dp[rD7Y{ *t-*JͧXے%h3Z7"fFmaƐڶ |֋KB+ t,kQ[JRB)ꍉut,#fSvZIEdq攲FO>i0r on j*Yu-#CkEG5oސSRvG*曒E⹑ -2T$&Ev !#䮈THRe0;y'hr܋He!g)RJ{C<6ׁ V}򔾗vC&˴X|[nQWmLLp =ⲔJLb0RIˉ.y}fH>|^0}L$rGyZrV0XZ{B2 9ҷo)5,y[[0ir&G.LNL~rRq[3=rv^z(= գVto~-l)$ @ե*(̑>Se K)+OD<9!j9OFj²."l LToUQNnMnv[nGvlԓeWHێGed9gj?]+vf΅5:j^ojkKI@p$wj)BovHUY Q)7iRV̫BDCFb9m8JHޒ|*ZSnRIlrj -ThF\IRrWm^us7$:MjOD|(RPd'ՔmJ#/HtLHd,j|A3E`BR-$wln҆TTuo9A>pGOkBp2LZg'GN_bk꿛lGzSP @(CƑZGh.t6 0JvVr<T}P~_T_Ц @(P @(P @(P'NbDNcGmNJ2O86ZAۛBJ4{ dg .;=QUX-Iq)(`dr8Ƞ8zWm p]ma2v HJ*JpNrRی+՜]\f5è&DTJ7[)&ߗ %m[Rʂ ό8MJT@K`mNp2@Oy iy$ aAH swP NnbĘ  HQ)Z%A@>@GjV ֥ 3@~Œu@RC%E  I9ږFp9k=N2ނw%;I$h's@mZmrP.tKf'P 98-qt{ӱ횅7#-ݭQRVYcH# ,$~]7+ lSƉj*ܦTP @^ g Jj#֛CKQK؊((`@X/i+wXBO4 13; $TYʓRc¬(i.멹ҭD}pc|b3xV 17}G5ը+ݸnP$dd O S+M MX-&얙K1Ȑ@RSj@QFS3ȶ0{4S\CZj+ViDh!^JyM,!E*l}ds_KfվSny-t"sM(JڥdA= 1hM juԚBU.ldJ ?{//S@Xs#̚jP @(P @V&иr=怔_7/?;@[P @(P @(P @(P @(*Jj}r}{^SPd]t}g_q7D~eJP d/6RD~JX/3)m|eLCVPzE)X݌w_6zS/Yrr, z{I:F,cN?W*\0W.Qn_e7H8NyF94-'3^.P%۔%{Zm+#5|Dh慩Y 2sn"*O`vGk1t޶T,ҔvU]Ml2Z}5ڢӦXpQHr )ZC9nhfݞmWWDleoQl6!Qў6;p嶗IiV}"WZDŽnڲjSvܻ [4R3W,Z״4llچjS-r( `BD 0 싵luйpb7 IDd-x$^X4i2qtEӨ|\Hrztr]9e:jMhj?I^B5iEZ) [Gyy.tޅ穄]ޤ%aˋ=ޙJ],Vۜa׋ i9?tM)3h.ۢy{O</&mjf:?Ђ 6 ֍f)-ݏ[-Cjq#v}_}V;"a)-Ay(q\ȔY4Di D1 k8ɍf.MKq'׷ǔBZ G{D B]:5iРͦʖ`2CWDlYIvL \ݰ)iCTvC')Yg==@({:^}cH1xnt˚Z}meK(TTn VZ@oӗ BUk2ЫX '}=ÏK:@(P @(P @(P I I"nojmTK) m=ٌ`ø+ApTFOv)d6n9 |q^{QmCp\fL̵>Ґk+BPNBw‧ %6QʠDpi <() 9yr( t 6vrU/rTǐw19ϓ mǑpfHJ=*ؕ+@bCnBH;u)mH SN^Zjŗ^zQJv'%cy$rHlzNoږwV[Cmjm*rmE% pq;ymmIm;kč1p'+2$PN0{]L Ȗ^} B .c;UCn{Ӓ=i>?Sx6Zz[ZIb .6bnҐו)@Rp_9lպUܧmRSێ f:.( @|zEP\5=ot%i!zWS=\$(4L "k6&b.5 CsNjp eiWsgdW{ ~z&*~Dp4T:8i#$'PG5w.q -l&rZZ+se.4 _HR@)JFAHʈ eq/SMí,듥S1 i s8`_'PG[ռ%/+ <-Ӂ;8=jBI\UXh_v\$\IIaAVR rph )n/;gtL9.HqYŋ) @wmNA ZSUbyop`K Uh{9gaƾZ,i9}/_R\Lc4Cph(r 62F4.lV<ß-/uU>MeMj6mLQ\qY)s H̖^~9 y SD9| Tv8ta&6TD&<02*p ߜnVneҋima(Imx19@(\?y>J ?{//S@Xs#̚jP @(P @V&иr=怔_7/?;@[P @(P @(P @(P @(*Jj}r}{^SPd]t}g_q7D~eJP#5/FAjIeoiZv\HϣG*I< mKyդT`'\N3ÖhREuو.z1ټ%'G}u;0M]b47PPJ<5&$ΥQ:ܩ]m!e9ޮs[N-4[3ij !H/l$|J싴UmJVKJ'%y֓DUW*s o64ƥob]Z$zڟW'9tz[mkV9U&]HR㏇d^O)16c>nǟQ*JٛDG? noRSowiΕJ\ y,7cOuY6cYCqJ]ZI!GݬTlnh9z:6v8T5򆜊֙;l6 J<|ܙj:Y6cĔ1쩰JN=Y\RjoR M:!Ic}[Ϛ&Ւ/.3_N;)W+.HڶF'6pP~j22K,G=HNjIb|N=lRHЦh#^9,%RjĽ,$d[z! v5yhKxIc:QRPH0ZmQ%FCiNk69,8Ҋv+9Q>lТnˀܔ$I8?WVGD#֦&)n}#焏EE%p\ I8HrQWA&vԽQI  VmRK9SkY8s*B.up;BF=kŶlԩ %I~2y^_QjC b#ioh9Q"NyK?U3-FYw-F3;%g_-sܣwR~}օ;%ZI9ԬGFUTo3 E[276SNjsVj$)jK"Qdrϗmk78Ga-WXOSXʊuVhJS,K9["۩:d$}bO:j,ܾBqKsjy?E&̛roR()qE@{ThcqcA)`G9}ʫRkBٷ}Q Aʩ K4٫Jfڰ-nIoWK7-FZQ5+"nZ$ňB)5E3.)QΛfmJATwN"*-f * ,4F儌KOXpm!B:ڂ3JCЄƬL! sM:sJrR;&4dʼn \BT3n}G%/'<֥kF{22j! Y[hJzjO)y!iAOVO;=õ_Ruc)Z5')Eme% j.W3n2HrD(Jk>Ov%VNSܜEQz:YwK o=#)κcM*8PnUϡOhP @(s+*84ިMcP @(P @(P @( &հm^C}Ij62 UGIv˖D̹ ͺEZ[{(=ą짐!TZ4pZj K9ip_J˰0BHH<Ԕòw'K):ƥ_Ys-Q[b*[%)ᓒsäx5-z䴷huA椀dx3p$n>oETbW;R9?Z0H'-\3owKq6D8怦Fᮟu9eLSȎLp)ꔃ>`Pxw؞جbkl=FR1$%>SpA-ۆd `v=1}-DkĻ9jTJ<<Ѹq'@~ lU>&5eMje%L}M|qy*s ESZW-^CiFVp4 :U]ٴ<S@Y(s#̚jP @(P @V&иr=怔_7/?;@[P @(P @(P @(P @(*Jj}r}{^SPd]t}g_q7D~eJP'0gwjнR^p}&4W$4El+޸pJ9$yRP|KuYIHuғE)^߽N4P1ʭ)hR0ԪiA` sxwgLHlg>ZʵJIl-#+Jn}M \sO7Hqw6KN WTr6˭Gogjڶ !ۂ@Y=~G0esKڍqWlpp2l׸i!Pq{6έ)6kVRĚCuj0M )Xis~ V8>> &:f#VaPQ+O]E+9ԛe.ۑ/mBm5hWL[ЭքF>X%5<7׆޹4܇!9KM ;#,ƬÝVy\b.%unҦ¹IKR{Y`tZ4SR VjZ2ԕay׺4I7 (@gQi̒JP+O }O3֩lFOs,&)n+$EO͗wo8yr皺jVZ**JC}5dI2qq[umoKFў쌻ۇqvBa7젺N6@LѦjOBji$l8y|/.bXSW0)dݍ.޶]A)p ieajէdvhZV( FgDW3*uVAkn 'U,DwKІ)Kփ1?G>aB.i%H%`޲ekb"ܫϡkwڎL-QyYIno,.NrMVԴd5[;uN7 䚳t\}IG\ړ1Y]ǕĪ:ht|cgRJдr|.e r)&VC*Єnbd)4YC*VAiD{۴v䥶Hk*z͉t̓+sTdSyN G mJ3Uc%͇KbJfـL-.y;~q$3 hu2侰{"̙eI;uΰZNG8Җ~YMJlW,V.eO6ҏ$rL(ihRU]ziKnn%f<֗gRTRz Q63?_ls@>a--&A!lѠzq[Ó#Hzg$8*^&`el@W:ŃәZwvCi (ʤil"0'ZIՄal 2"ҕ q޴Y`cN%kDvYPRGdL$qvV,G+EVĸ7zEfS6VKJi!LI9k&u VLZPxAZ:_ɮ9&&KP+d)疳{]mar+Rv ܚ,jhSyulГUg-Z45SbK$jZ&8 Ǯ NA>j%>h!9Sj犾UTr:'äIlQ ms0Vsٟ8PnUϡOhP @(s+*84ިMcP @(P @(P @h_o4͢U)P"{ϹR?2MszşJ)͜hP#vq]# DBK݄\rFg `90(u#MhC{՚^0c[W5F"ŭ7kгtTLˎa ̶G,-5F N :ci1mU5Vr,:m.T2$DPJN6rII _uA7֛'ףN1r%hJBH*^ H);@VEqmM]4*1(kb OZ9 dZ {E_μ\θRJi!M!C-TۆBwPt4zJHW3};ngz$ZT@CY RrRI瑴 \I:_EzM*%$!)Uwk'RHӤo Wjo\$߭rnDI. m'B P.ZSS]x*u]2E8OO!R`gndHi vt &<\ )=n9 :zH۠;kQ[0Z2w$ Fzn˅K3 JxRR;JRp@ gyF3ph\UOKmK8khW2 pIN( 5ͫ8cZ Z2mLrsnKQNi 3lpnvU|VlGpoSI>n\qF< k lhz*Z?VJ/Ӟ|`r XK̘3KY12QXH%#jH8R$T Sq:#AL7=:7x aq*:OKJiߒ 6_ 'i] HՋ{nnH%Y!XEi,`@C5weϵ-a2AБûD n%z lk%G|G>@zjCj֞DAmdk %[@*@)';h nՊ܋ì=vLvķ"SFۻ8=@Cݮڞ>@dbV}U>+c@W8jRBÏK:H&ڠP @(P=ɠ4.?\x?9%4W@(P @(P @(P @(P @( Ҷ?ڠ#-\x86^=+sx(%e?_YW<>@AymjC=99=Yi^(ruv-J w WHoMH.EBG'yyK3 -nq3k:qS+RHC:RmrF\tL2`% O.O]m6$ [q6q ahnHN#!VIoE^nZ,G }+R t%U~Ff<iCᾮI Fnav[nXudވ^)u8*k{J[-'$5|\Gt4m/&3H⼜Y$5J- Jϵ| ihY4C`cZ< n%RJuDgώ꺋k]ZLLK~9,dCKM'k"jPZRBII 6ML\ZDmrQMn򔇈Mе;«DZͪ7[F40kEU'|kLrZJ +3ghՑm(-E{I#2V晬6,fDN` [kc/B9UWBw 8.vW jD^"UtQi+<[bI*Y%˚F;Ҟ\ hДe-TsŦ3ڸCOJs);u1b/]Y䄕sU=Qilc\9埂ZI= 'WVƳzj^2-[II!RNEen#F"..8_qIŴΙYVweslq򴡰w|'pݭJ(uas bxA$~`7=҅2VrUIhFM*,H\XNy>ɷlF7h 2 ʹ jp"K%Y:1|*VJa,Lohx)ˎGJpr7:HAمlԂU%juMGSeP_-*JX-B|zL %C{gW/MhB$I6wwy-(I]/K~KT9"ҋ""QtRB  F܉ZvvfsRB #q$$dZv TYჁ+n-պj.4vşMi .l] ݖ G5QXtO,wm[љQ`e~r}ROy'}a8eԴgJzr$I yMRIU&]KBCZ)՜[":ޅ`*; kCM'mWWKIT呻 lIg%.e\:PܖgVRrM$M*@4eBtM6q%lZHX: IS &et$oZ[i^'m6jȉo:G Ϻ+% )^۵D:JJEF"whk>e:{J*g\%У]xɪ3kR_š]*y jUB&SX z½#Ze>71{ sdvOaYq{2,7'i- #,GK&Ka^$ (|8F9+Dk<=e Im”2zU9RkB[ut(?¿x7O*g(P T}P~_T_Ц @(P @(P @(5nvwIC@Z12C Xn4Ve!$`% rԳ_mzS`Jo]lȌ@|}%8]>*mH3leX'SaC'9=UͽƗƨV޴vl;YVCVpe@gxm Ƒ*C,O[}hmo 0㌓bgKRvr|kq K[g8߀(A@yu}B7ryf+LS!* :1x(Bf{,bݣZf-cP$.{IPr0 8vļ]HnГBJ Rp⌞XuzT5." &[yD $%neD@ӬCEim5|{\iWKit%ecR2J w LZ5uJ^,VhEEԭ'S; @uoՏA]i7i;a^﫸!F{9 #N쒲Hro|CкzJ-1cKw:"EJJpF{ :M['Tرi=OD#-i@IQ#.q[z}jfvQ*ΒBD A}!+݌ל-&i ՍpܤEmTPݴ,߃@};7wM7dګJ7|sQMXƙzm֛}MEFqMR%M[Jpy[Xs 1,/lÿ́:)#nA"P @W8jRBÏK:H&ڠP @(P=ɠ4.?\x?9%4W@(P @(P @(P @(P @( Ҷ?ڠ#-\x86^=+sx(%e?_YW<>@AymjC=ME VqULKζ@z&4a ɉK4N9LV6Sڊb;5eU銦V&06rA%^0AkhT'{-ݛ[t~g"Vh&:+sjQ]2#ʵ.xdROlFV[ ېm 8|5Xb^s.6muo%qRp<+I܌P,OP:IqRXn RDϩF'EKIkD$"2^ ސ8%\`^h2 5[7qD++U߁2fKKoH}c p!Bw>fzLDb,`H'${ Klmn!% aj<rDťZaJRӅy j92%3PMgbN]MVԙ*4iJ"K=撕a)EVXJ4`YG gkϜjx]3yI-!y~cZzUD(h?p6.tjNGk)+HV7%Xm;0NOWbC×iZ/MOUآ7)Ydњ:4GQ))"~OM 4v +Һ5w…eY-Nj# 7!SVI?iTwl\- r>1ZoBeΪ;RG |ޚyD%Qv^_[A[<7kBtfXݿ]k ɩU.ƐT僕+,teU,\2eBRqe: bf6=Zť}o+%驴)q yݫUJmxٷ\DhT5L߄d(h!$[O}YF*نljdD)B3ୱ5K)b ˛QeyV$FI{zSQerVymnQRNEEO)yYKYtEUچk '* i .t[ ̆:۠`c]MV2ekX\Hr:GWdB~{j#5= 2(+F{l֦wNzk-(\KVB6 j䒴%ɛ7ye>ŵjZ-5Gt٤{BǔQbW:FZn-Ju9tX"ܕg%Ed(ƚ郇$#/N+؊tYN0(-cjyUIj̙fqN4;bJ98%m$L1\8))(AKZ$! coVsV.Ԋ$[e]Hp<tJ՗=%!BβeKۀ De&Dmۚ eS*?Q}52tm-sɫkvĔȝ$H-HP.gTZ̉zC2v9d,c*cID_0^R{$:/"!D{jQ9VTFZZ9PnUϡOhP @(s+*84ިMcP @(P @(P @( Vk涽&:u]mM6줸 +I<>z=}gi+f[Nԋ KJp l?,EοޖX$$GsT  ΰVMEk4bòBm}IBpF2Ik"g[ܔN%. \I*zK<{I tm)d-fKqX\UKZFӷ8 Ž@Ϣ~BiSy IȐ2KTJh jbE W54H1#<'Q u{+wE%Q!aƗjЕ @GC a^o eA@)#ow Ir䲻+PZV擌;1@n;*ؔ&:@Q vv9d\@:.VfDwL%N턕H.$<{ 52Ou\.E\)l̏9C~)Q J;MO4xv;)1ޝm}BJ!I!8"@^7 P$r k}/B[!HKJM!)*J,w*^#kWnRF3^b)*vU:Ar9 sBNF: Ȼ O4i JJTGq'@Jh;nqyɳ/KVDBxg.H'3tY J²p\KfDVl8A9%\@n2xgԺ<|՗ß Q9aҰII9 {9:EOj]wbϦUļ3s 閐+h ' t]k;5M Wk3sBvM䅭dC<n*ݢZ[ʖ%thyJʒ8q@l/{2V.QK\~ү8"x} ? 2%_ԇ_({g[5VPCiK9P y*'*{۷\GWr۽ #Җ:t<3&-Ev8H~1iYHC)=*k))^g*RK ]J|ժF;^l5c+oSEH_3]8!$)|ִ%I+ΰ  ğ5t)ޏCǰս]"҃ѵ 7vQv-2 UI[6w+)"n*ӈRm^;wy*ѡR;HQ I%:Ǧv i qX.iΈ9=hE,˨mjXB%8VXv^gsL)},' Qc$fw7UZSP:  ,o)?ֱZte]>[K{opGd՜R.TE0w:GPgF*Lt)nYJVyF\)-٥pMPiiB:(/)Vݔ~r)}iRr+neisﮗ.FQV-L}n@?ݚ)$UzEv51AR-3](m#Z i:El)r_@ŽФ*QɧfҒʛ* dt7nK(@W5:0q[8e'B2Le-"/sͭm܀hBK/֍+InMlGCh $sTǠyk9,ROc-T$I=UTVنzy؆Є(dlI$kCƜfBG0+kI9ZTBH=c@i(8a^0_[R]J@!Ĩ gw#8uk1uEulaob3ER%aI#8I-Sp//vBQ.c-O%'zрN\ZW}$3 Ǩm;W7tS";J>rP^_`ĴDXqn,zӄjQH~9#f.VޜR^ݜ SFϗ/ѭQ-[2Cre{HmMlZTw1UM)pf'ʤ(eiepRiۙ*DKBqM6 mH!hlUʕ*# 댕%eE9N8+#+kWZ_m!<]BFҝ6uNJzC^ӄZI!)v6q*mJ\K\iy8uEE~ <2&ֺOHIם7liշu$m= ($wy@zO xq5kҳ-"4-7=*)L($IYVA%Cܪ Vq#ON5zll.:jS-:<蹝hIVcxJa'M-ᾙٓK<^ѧ$y.IynauͺY#9!JˁD@t9_sE]&XZmn:QBVP%XʕgeM5 EfeXu6*1 !}Mߐ`\lBPQ V{DP<[^&#G=lWv jD[FO-W04tTRݨr$d"BAAH+PYߊއ𐴵Z*&km>ٺۭt; 8eJIXvWMOt6UZ[u]Cl҄6v0UI omeZRoiŶ:8nHڿW}дXSfL&_`#NCI998iAlv4Ȑ!䗒8Qz5SXD'k,rP/(, b:ٟ6F4{ wg1ĥkJ+Z` @k𰒌7?LIUٮ$]W&V.R&\쎉X(JHXO <>[^?V\&jZ[tEm3 jd&ь;R09Fycz#?LEpNkVJ|<lP @(+G5Gީ_UyE h v{@mP @(P @( ~dG+Fh uP @(P @(P @(P @(C[~[?PO?P/}} jj̕˼?BL~W w!GᣭmtsK/+ڝqgfT n-ln% ? 2.f ъ}P{)n+4ZnCahf*FMd6pCo*Rvj5ԝD 5wlGME9HppMoTMeY6ovC9TsD5}j[vH*;By7G[ftC{{gy#hCbb;-ͮiϧZXè4w%N IGxF3B1HokZgvR{D U =hf2T~Zf RVΆҜӤgqJ[[\WG14S.hl)=bkCZȑ߸_rqm,g'?ZeT*oHlK9wWMfڊՕĬwJzŵ/^FɈ<@j+zEG;j[ɭ|Mа7mRr1謧v*nrJ:2ܷ5S_hpѥ}8IʌYj4bG=Kanve,Tפy hoɺ1B27A2 Z̪T(AJ3Gu(jG3z',N,+ D-OKtEI.oކ}8$\|U1Wp Q7d>fr 2PCj鵰әC$u YH5i,tn;DnڏYąܔK,aIB20+w.-Lؔ]uN=FUWQZnm4ܽBr|Χ^W̲pȊ7QzJwTkc_bk꿛lGzSP @(\?y>J ?{//S@XP @(P @()R^nJl=cF 4ϼG<1JN|[(#=%%Y8Hڣ+<@H8)uִ):rs !IJw!ڊֲFyO=xqѷ-VvjKj@WCjʖhd`)Q8%Y$ 5oL] Єȩ ;5lRbǐϨ$:y%e$ \;PIj=Kw[,s+LY02e#@@ HW;$*!w2+Y2Ҷq;4~]r&އncKf GR-Pqх@9|{4itCN`u*ܕ]T7g@Sn 8x kJ/AVU*A-<=_KRuv]F-le-)R_p!@c@6gxіqa)/3Q y00&_0,v n1lc!rTsR2T((ttw8sԑٸց mKv{p y+9Җ6ZfJV#A@nt)NO09/̅ó&*= Ԡ ܵ"|,rE_;S4TE.|Q"0J#6]WN. ߗ%Ey~\1q)i'K~t'-J# 7c˹ĀIiy89ҷܖb)l`q{RY<ӷ7m4|W/nڤiij̷B&E &!(bѮ攻Ĺ_ۯscKl!J'J{췎EImlԒɵA0Wy8Whʸ /-\D! D;%*9)ciPojb4=5%?oQb;n)6R2/0;6yZv"GLRpy9(x)m@FPJnDMKC$(a$oG  TFVE3[{nYjlvvyRqR0Ol\x9^!=lBB^m exHʇ, >#tޗ'XLuZ{iZO0hX$ P0W5dmj懕r,Oa,n*YtϧIĆ7*PJ)J^OO}z>7j>7#ԩZjo5l|W UJm8!!IPI$rn<V1.&]h[jrؤg>xVNoG ŊdNޒ+*B|ZfԠ%ۑ:ޒTۉҜfV"U-LEv[[^1}ڍaK18vΆuv;6țuLq )lV/J}d'͸)N;j-+51Ьxfu.H6dzk` Ĩ0431jJU0]!]r֑d1KOFXiʖjǰ+j-\2ƱCћ)ouح)aG*qpIkw{mAO dwQ4M6dM-VE!ȶ[l)urp*т_$'̛MI<Ԣ|-(w!6ݑPLZOd]fԛԲ)HԴcWg>l+SFj]nQAU%N MG VpTʹeњ֦f6ې1UܦX`1Y,{^{VROɭi1 - $+8'iV7&ΜVS1T~2rk'.&m$~px=aEzƓ[j>Im,t.jKk wMN/Nh0[Ć,WIʵI?lj#{Gwm"4nQ²ktf%mad+|[#'1k9ےN5eȇ'F'zGBZw8Se$gP:B=m¤BcPQOi$+UtWL@oKqsLk*y%C9YO$vZ0% FT;rӴEٽcKiؽʻ־X[ԗv0Q敔SSD Rz- C%0 uiJϮǝ@訔S[Ons^BIJ7[ųs;^\OhڍT} fa1yzkFO] KMI/ҝHVsḴ0<ՋrJ< /Yw{ j3hJ$6ڥNr34)nJTT$\i M*4%q!I f-W5P-Hn2{= W#T.fqi>S4i7o:k6#-㻖j9v8ҖHS$P2iBC4n !I Q KQorKts.!Ix'J8*!=O$Vq%\sOX&F8yuԦ5kHëVH "j #-UEȋId3&shǛ(I5K}쓀~Zuli%Bu]B*-Pz%Ž]]+-2JsMNժ,6ţ(M-jS9.Gԕmic;IhRu,2@Z}v=ڮI7lf_B=>%#3*WGH˧z3?R1ͩufCӛV{KO3rn*W6m'w]R3!2"lui'NI5IhctQ沂BȭRrtۭ[c"IyKy}3ʬb(d<xW4I)YvԲuGmSx@ Jh␎g(?¿x7O*g(P T}P~_T_Ц @(P @(P @(QmڷOδ]VMy$g۫)1S@pO?)Ǯ!{u&i-zsvA-X a8FU}IapO8qpGͷ/Z;2 3&0[!K@!EJq<ҋw~FƏk5~>îB$JqiIi8#9'<~!_E` %~?#AڤS'ThuSV̫~h9k8;}?И{\i[w||M۱bѫ  l* !#$圊t1u8hOnCc3\QhqV~JTX$Zյ.;qsE,Ĉh:qN˴#܋P;Ѳ\ʇFgZ"(LG"G4Z"eS2w7ݞYRгwpiס KIc*{|Oc<,1Yq-(H[6QՙܚeθA=b&(o]-[B9 wt^։N5ז2o i̞Z1M]f)?cWkU,)_JwjO@:yyO1ER;#( z+jZQK[Khj]֚O~+2qCŚ 2\j;1!'wW")Mpyգ%% V mɲ䴕HU^,b;QQ L]UIL-ؑmw$`V2Q4*pU%:cAyJR;/9k)IJ5QQvl:S wWpzZgҼ7JC6",RMItt[ٲ8)|(<`jEvtJpGKIӫUm4[-޹]Ѧ8 CﯤPOV~'T[)R*6RN8Td%Z:0.SU'M:܆cF ^hٛտ]߾ XNhࣤ=+D-؈7◻qCv߄UT:wr%{u>3ʭ+-v3Rtl,go>ZJ2و:|{{\fZ%W -6z@8g I+ yYWH"2T 7IQ1ǦThiC(iiC9_As);C4m˫\m@$*h .*:5(!{Zec4HL: 5-)k-?H[sVqI&ON Ėԫ+qB\SAq=}9akKnP q{y)hfZѵFȜa-(lʹ^鎽ХD\xML+v/piM m>zI5cە-ĀNPi::k1w(vD퀦|D]SG-e!ie*!YٽEM;Sa6RdHNB]rF훏Xf9~$mi 5&Z2jRRO!wh܊YZsRdFՖ$׶:W4n^c %@yG*uG3d >?Su<1!M]. h/4o*p FMk&,-}F3.š 9Qq{*M2U)Sr gQR]Hb>d^LN64n B7]Ƌz#(~}jJzVe/}g!)Wh~QTJdLvpZ(5#Wh3=DHӮ |­'U*.씦: 5H--+ORV̮.h$=558-sЄPkBPnUϡOhP @(s+*84ިMcP @(P @(**ٮzdkM6ݥ4RRy+9 ->wi[ U֊cLu9H3bi+N:r `ZX\BB9BU#g00LsZq8)r1`r /x3֚CZ@'nUJ(O@eA V :Uo\F\,׹V p.Bž2ZYPvCnSq; ĴV욦~v$"fˁPJ/ +匍Øvn JSOK=eQ֐#.Ϯ瞀ӣT=tۮ>rՔjW 6aDFA* yͧcu{rFBC=&Ԩ Fh WhώR/ĈcH%+ rpAy"SG(Vp,BG2F|Z$V"^fdG[fJCN`sv; э1gтmNBbQ]!D9$z_j-YkbZ0GY-dA>';|MfFTZvS߭DzAN춠J+I9/| iiSu\7 AgsD-A°d}Q*F]Sov6VuTKFRvmgqJh<H;y#jdy`m[t]vE58y *9HJp0e=AFdMo\Ї&-rO?)7sc}: fpb\ե΀G.)hx 'jRRe^A@[Xf-6JYBՍǘP9SȐd4mzvBtMR4HHCJQR`vݺi5tjnl/#G,@I;);@: ZSj>]QbԵjVU)2yNA8jpE}68~ȍj+ʋCKmC _tmۼ%5E^rm pmF!:k+^jx~dݽ^ozł}AV,$(Ϛ.%?BЮ::\N hU;iy=O,>.'mBJx d{*~Ytw3q[nȑچrw8V =Ư 9p+U$po 5^jHv%%[8JӴ2;q88bE?EC>'Ə;i{~ߘYb[RД-< 崟6Ttd%=L3,z-Sy_2 &=:KM>}g,׫) u]!7N$.ZHBj J+Dԕxs“5fjP @( *½e%ݖl 02egjGcÝ*c6JH&ڠP @(P=ɠ?V013-Q@:>3-Q@:>3-Q@:>3-Q@:>3-Q@:>3-Q@:>3-Q@:>3-Q@:>!-%!#( }} jj̕˼?BL~W w!TIRT)Oy#6^(r%1J,>k՛mlKe[!N<R0W3вL#9)RAݚkCvmvUaC&A[$zVOkqiK$-R&1V:GRМj;"m)q%^ȥq$qm̀.hԉ+]R ) 0Erb&ކFwBڜfxrOB2Z.\}{h9L=vZ *8&d62![g,7_ ,LG\p $ /MoɶL؀P(FuZ \h8yvj-ן#Jh%x"mItMYrLQ#";=q_<nkD+YD!iKt[dnCOEħiHW?MY)L2$~㭻L-'f<Í4KH:R2uTn1nr6YI8 sWȺ"E:$#|55J Y)N%IJFy BS$쨛X++YcJaVmYϤ~֌ez-_ 1 |=ղU{smRdOYϕZ82Ym{a@'Z3ODv̬x犗E.%-K m.Ae-j$kyIBw.FUȭW$G~+,-qu#Tj-{rR’n9>/:mc;Npk6 6"2ˮqgV`R9OZs]Ԟ{pɬZQJԲzts-R#DYBw$ h'Zt rji38/JҔe)oh7&Bx[ԙ%nqHr#J]VlݭOȱ#*:$#r74Muf{3^aA(*%D7uQ*7g0R:Adԭ]gԤ**vg5*Ңp#a_1{V]QS0V!;rqY%f֬Ћ.$QfFV'Z ަ]Mt]˩`g#oR)^+f>NufGˊ I7NT(*rWUu?ԵYۈP8$Uz26 PnUϡOhP @(s+*84ިMcP @(P @(^05ՒN?65W*DE$HzYҺVzc#H;47FTp N{$|Ċ.bpךow8}*snpvdfBV7k{L)%m`(#9ߊ4iᩮRNY*%HX8!AI>b/fפۍ@\</sn 8ړds>zkS.LEf~فsl-ݜA ʾ?y9?}l.A'WU4|<<խ8RYy; S)%*2< vI/Kp4vϯk}§W*lJ'8T#x ^zb+ /[y~c)9dIP%') _:ZF=K2W[{{KLNih)1N2{Tp>J ?{//S@Xs#̚jP @(P @V&w~5Q[i}8vu(8e"[JA@ uO~rnȈ&/kO!:oewnmr 舚 }iڱiJznj\HC]KINVgks:wx/A~իju4+OJz#oyҢ#-R{i2w7*C?zi 蜿u<=X̕qܓ !a%o=Nĕ,TR d=?~[V4"{ֻٟŪƨME rDwFV'جV-Gn> +w^v(Uϕ)xzv_ _u\e^1Ra\)ɀ*X~P,ApMf[%~]}u/>w^}9?4k[u*,`Re\Yi^rCRHm)RI+VB<>^)ks;Nþ\g7eiP0 JBP TetZ(}+"=uldnnliI8Q*&j߭oŹfkl$m-HٽDzKV*~Ԭz.$|:o!]4掓)[5%%YSxN1}ɧq*J0>>NUĝ7lQgm7EKְRz<:)<e}˭z석E:~~wykω,c"[@#R+YZIkF Qk8%ok^DnI|>4ɗ+:Nȋo5 ㄶӒTIQVKo-Z_]kvZ]2ΞnF^s[E=rS!,u~TKLِQO\/e5uLJކ-KknA펰&NCR"T (i>yY:Rk^H3<-x{ W^tizjnKF/=)02Eg.JggN.=:Ã:Dj-dږD2bMAx0wZ^ŝ[vjtjIswTn7s:v6}+MpB XVIUZݿتz6Wooft -v 0mz:fl8` d)ߒ,Ϛs[ZҔ;_QjwUw5w pK -Vż|Z(JRѨ N-ZbXޜܺqn6(]gh\= L-*u'o54*OS8t43mI+[ĸqs2PGD(*8rQx#Um{֞>ᦹ_LJ}ǹ4Ç*h:WDwq'r1F喳.(P @k=/{2V.QK\~ү8"x} ? 2%_ԇ_({glu[P'gqW/R>9)w-qˡS G%ۈwQQ>=[vԗ\fRZZN Y֪2o{R5Xcb|JLdhA2҇2- Vp<µRTɺ2Cl{$֙oB,~bąaa*XYdx@Xy-(+++K*޴~@􈔒Ojg|Ivuf4j$dktU!ZJxnVApnQ=q-6_uPSv[R'Dh)zkHƙ٬v)$Ni&ނ(Jm7(qՅ+Q>OMyg< ލFHJk{/n;m6yť#I=ul~nqRᡗŸ|yyezQH82w>nWklҽE; R R6$yswDnb!BB?Z. W!][z5EQť F t+W_ mYZOTrX#)ex5BCmi'=5zAn;ABPy(}^Y&*ٞE,z$i+#7/5ǣXVC}3zyM{WϢ9քel 19.8z:Ʋ+܉fQkfWXR_pثMI8u(Jq- *mt:TOUi8KZ!^f/4KZA4nM-&qc/9RV7+%L60|kLk"SoDog!Bbֵc񏒱q%eoZ4589J95.m ݪ Zd18%:-̲lFTvSޚzkJD d[hBzD${fV$CONádmQP+\9+ܤ\@R)ߑ%֔G+5$d[ by#"O\ R捸ӝ=NT<^VťJٍ%kVfc&U\Xm(w>WMZ3ZEцԥ<6֐q8\+8m]3wPGΰ*/fJ8Ϡ~C]JS_bk꿛lGzSP @(\?y>J ?{//S@XP @(P @( QғQ˿1żTjZDKdy9h 4:iƤK;η: ĭeIyPL$zys&|qFW>Lbeqw}sޒ;P]'DmS6TLx5JȟfILXX{i6 XkM{zw3RnڲtqcL<`B̞'xX9~.ô}XOem%J2RsG9<~t 8O}\gM/*"cYCi.c񓜜vUuuwănys_k۱owV67KMRݰwrǭW:N n'yګ*zՆpqÒ?bTqק giV 炝sP @(P#TЪpz4?R=ɠ6P @(jox?2h[~i.,D][.fv[Ee% JQЂ;BU'.ԗz>³ğd7M֌d`qXnYӬJ'n9;jJm;Ţ\imk+<!_4ֲF6k M&%P&r: 7-tXIݻ Iέu(GI)fjKeFi,T_+{/g_6ַ L]^ߢۚq%2ío--]RۀFVzrv8֨׫VWn/6d8N40DgR\Rڥe$jk{?$\j }=jݣ4:P{4ɎljQ'j1ȜoowҼe^7KگbZ;E<-OźF $ȅכqJ&8@yA ^jsOY~s n'Chu;!i{JlrM,%:]6 \iI)*W,in$?ތ(_}(/NaE>b;AZVA#8jq%&-۲"Gޚ׼$pl"`Zii1v"}H ڞ )H+9~v;_{-MËʦMӽ]Hn[y+y Tr=cyTOkZ2ꨥɷsԯ3'e{MO/Кӏi[&e>&ZIy6oY`'l>wʛFe$~qGKqsTn˔壟DҖ܁! )8q+m8)~-+z-c'Uk滗4pkdT%$6vݸnYQ*3ysiޓWp(K"w.x7%OS\i#}jt-Sǜ=gu{{~ڸ }n\bח1!{֣v ImQn-8BTH#8czU>w&hw4UU$!m̴eđ-D), VאU߻gNY]uOouWb])sm}\! ) V) B/D[JW7r{G.{V3õƔƞcbRC1 f\VTH'^3qq|OU\kȵqk,̾'Rk]TGT+JVZJ II5i_Yy;w̪[cFV*ˤZS"%RnCY=*IjcݶsvCZn{jt/oTuΡwaHM)0@mQ=]V {u7e%՝ _5wD˓e\nGSS hP@ ()m@ K<67Oۿ-m)WwЎY-Mlm*es\yodXa]ˍgYG^K_l ϼitf8k;JH颵 [ OC4dGj(*iô ) `%IOK_>_ļ/VݾSZxh}oeg57vmO!;Tdr沸ќ9&J5ּͧަO:bkۧhm3EqH]lK60dvpa4+2y\&b{wUWݻed4ٯm}֣4]K0iΏIB՟*ywbb㽻|I֔4dp^6]n7v%cÑ-SmE( Iq2\ZϡeXB­/2z=KՊ$TirRN=5щ%)6{|8'->2uF42uFx7j(Wj_ѫ2uFx7j(Wj nFz5g475fJ ?I{zUg~Oy&P?dq;[ڐl,EIGi Oչ(ruGs)'JKpBoVt"6!is^eSgt- 0tIcʯ"tّ%c.Si$HhۻYʊҒHVh?IhΊ%0 pT g׺5՚P"HYuKUJOir*ZS4ʷ0~0^1ow5ބ:D֠uҭW,*[|w"RPk g4-=_fBzneyUo+{BiNiXibxiK /}wED*&PB'#&x<ю$U3=p11xLy(Ji{LK]Mx^/C/c.G{Q.RO(^%M?%0>[oޝŒխ-B/` }-z1mZ'&j&]q.qR7d|=ղK-aCq e#2)Yq_DȈ*vJҙP%UN*MHB `cjtQeA1:vp6J.>aSlm);R+PBdByz>a)B4.8$=5f]!m!kLt+j]xu%X,m6y*=vPhmP>JU=Y'ì֖P*ۓQ'Z "2V^~Y)GQ-rfltE`Rӎf%19q>@(sAim[#25g+FhLOSly =|]HہS1u6BBϻM=.$^- 4UvX5&fnhmI6J#HJ2sSPYYK n8yQKmmyAI25)4ʵI 9PnUϡOhP @(s+*84ިMcP @(P @(hTg g>"[]T3$G)IhaG'ĎpRoӫm̧ᅙP;qJR}Qc)@3rK;bUKaeq>.{Ӽ#r@>A@Y4~\ "p")+.p-'}<փ3khys[j1KnMK(aD@ӖK,oST, %G'3i㕳:Hi[%ɟ>puXڅ $# +VBS|X,bk(&#%%)[֒sB8j-+~űmXK_PL}"J h+3΍wH!nüzyWjmz⅄$$(%qGλjod[R[CJJmY_ .\q cV3ҩqiJUK(G@ԸǝoIم֮KaǂVorA܅no~(ˮp;IY!?5Ww~mlJrJy *)HF J@<%4akk InHWQD[z/%K*Aܒy}O'*P-&G58== 'zUl=.TH 7~hcTjKq{SyŌbEH$JJJ;PHq_OB7t`tONreLv\RRRHP@ے\w1i3z&]v%@_yK*JO2J @R5ů[ΠuȐml2z7[FX%J P'~F]+ׯ:ib"yfo[򒴶cJ*@v_5Fۃ|Kz1"zq%Ĵ^sx}^n?75|3EISdJ J%8#'{x ?#;ҵaz>nmvEx7-qȒO!G(q @)@ QG$r<,L,J\Nx>hi־Ԫsk-1$ѣ_TJB^Xؤ㌐H<=dn Sz uKVzrYJ|٢[OIw;I`8r)^ I8 99xEwGFNs>dP @(P T}P~_T_ЦjG4P @(MߑMp{s-.ZXmNRv\8ܡ3x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~x c~s.*n.T'{y( '( 5fJ ?I{zUg~Oy&P?dq;[ڐl,pN:e6j#Ue 9 El~=k\ռ875e($)Yi!-}KazW&K: HO1 H2Hwt4S'oEi䖦x#pzk}q8[᭞YjQ64pKi=JiF/saMMGjs$vڂm eD #\"Ǖ-[qn _'9qڊr:SVr=+Imў ą:я1@+Ĺ#"?5Ք })5;T_I#r*j\T0UQỦT4FOda5:pWeW2@"IUSxD;EGJ2#մMmY"Z8⻌CY[x/6ry8qobYy$-6يf5Ҁ>f:i}!qЎ;rTZH[vy1ZM%ܺJ]pYc-T`+lEz\{F6jj;ͦ3(H}.$RSnH.B8Qm8J=ʙI\Q}r;h >ZRڕ0 YehJ0O<<.֨QaYGK* \݀+Fb^S#t]$z ݳEKzL֜_5vI8SZTսMv+Ҩd*]mMl~ JZZZyƨ$FHJ[M:a: #r1YY5 \s#eA{ 5U\XRA 2QKtk]_y:Ww }I-|=͎?T+G5U7xrXK 14ԠR< zTao=l3efGړ>Y=Tuh?8<\9x fK2oFzzҖ%" vHڮ9,tia4ROHIgΓ؃vqT;[V#?Z/fCw$ YX'#{sT HOZ!$G ВkCWɗёܞfCCcJ˳):erl"{*6pV:E$hn,r}xOx)7ve0\ 7G yIVjqv~ۓ8I[''vw$Gjn1Լ Xdrڜ TYX;ˣ|:jR4gh=q`':6bk9A'ƾW>=@(P#TЪpz4P @(P @(X+a2dOe.\YJsyBi:KDcqՋ֠h[~y4c6֖ Gy3lD(ʉjTJyyxel^FIԮ -ôNH-yr: !/Xlt%3LYB9QI_d-Y(` Fs;6V,nN8eSvа0R%d8*ڜD ,N\7ݷ>NگVKyr%I$e99h S@]ŏH,ʕ!Pd!o)jPo$FNݮ{4'$¶:ȕ)ld(zH=4Ue 1ӅJvJ2sGPqvo:GqZs;МcD"5\OKyM!TWa){Qrpw(Ovy/w^_4ە$|S$GhYiԞ}Ά.G nv^gs273M YINRqctEۓ*`v׋evPCe< c@ mOuƥj;d0nA5zJR(m"57.z`( [S%mMKl˵O #Pp1$]nd);@jI7ˋ.JY1żӷ iWp:NնѢ;En+!E,JN/#rvwg* |"[^ &%,JZ (-rRjp1@ٶ"~5|9 $)PHqʀh+ajBU[╺I`%wDW"FH4fwsZ;gMu4P_H2;b\Np( wGt…ٵIpuWM P@m#)(P!C@L[5of<͑uwgFJU I>զ48lA`ܘy HkQrH#sCViqcŅ#͓jGM.M%ܚ q9ɆVF2b]nqcjx,vdD$R'JU2pdj{SN‡j=aiМR\7-d(vH# W,~"辉9hWr;%lP2V --)BVh;X'`Ib| Œҽ-~+"VT߳o ڵq\4zd68^qwe]W3xx(|E-5>l\n[5KF[۳gF NEx9[4wn^~XH9Hq2k*n[{O} 1iFvNwl_CbT T7jT@#P sqo"[xqħ+[S~¦8MڳѸ8FⒿ0'oúBZW,O*뗇SP @( {QWU@8qiQB@Cݩ~dTP @(P 7~G44ڿtUڼsrLm,:Di+e!=rFNTyCLx7I1:a"mD RFU@p? [z\ r]%|vĘN $nk\LTl~ 9/ NdJ׼ytw EEߞ3{OO@E>5ՏK4\Si-@*gtgќ_  8iŷ%z+S4e.+W'ǎ㮶eIwR6v^_P,^\/ljmir+E͛2q*J5q#]l¬4ng**^ҁ/ _^l@~: clVX8}EA]!y¶E"ړ}֗ em{{& Θzut4ۚr_+oC!MPJXhUkݿM=~Z"=oFZjVEaz2Tà rH%!Dr_ѯ}W~}_OݢLd:zSLtla;)AiB+6n~&]Tk ~͟NkYLXֻ !Է(kQP=F^H3$ʽ꽞ۤKw~꿝{hr5*5 d82&1hCgaؽNUi&iUv"-Ik^wS` TY5SV%Gy-km)taIYڞgyجYSK嶽UoӶcjݛJjY]2+݊|k\&rBB[paa%!9 Ta7%?*JM~M^sSxSJ%Y4n>bE[ڎ$nFĭ2A%t߲گruOy֞JpgS޷)Թw܄HqqBJZA6J\鑷(-/oY\+%oO{N]&-ɫ2 y*a#pmG3e- 8qodߒkM7ޕw^Zg"uj>hƯj&ADmJ@'wlbO"R՛M<}=v[}Ԫ85%-(Yf[^UcmYuҙ%x(֎56ZQ;i;ٸg\l$ns\c B-e\m+ RF}tZZNPMyrvuWA\55} -ƊЊV [ *JP J+}U>M7쓏ծ%+mwW)RkKKM[i!ݐ [ Y2{ !ꝴd[]i'kv"nK㾽DYxG];{d< $+ڔW+кm͕Iq}qj2.MlƸGm8n/v"0ȰBۊR\# B B- |ܛ"#qu+}om=1慩(\\Jdv-/5c}K*)okή#.ĈӁ!r*|j`f]^NIjôiN[T(SY}q۵ɩqh']bheb)2^N0AU}eR؛C-%ΰڢN@X5#l :.%O9z3G4_RQ*n+G-F!$6sLnV0 DF ܬ4"u=mU \ڎ$@B&( %L3=lY5ɬJaqZSOZ長Vj_y˔뎵N X# JIT$'Pk}U[WETBRI2{Jo}mtN1ň!Qrfzd;VI%hfxn.sIVAǻUz'Ȃԩr-3v)Qy*OÚNqZ_3bzcPYNAs kIKsgq!.a6=윅U*a R A5تKsZ=S=]ek:SҍInFR[[=voevߒtbsI+mEs^! uy#;{=TuZ7n8JR`UǒLŤIFBc,'Vj5L%Yc\nCޮۋ:4(H%c% }/D,EY%#OɊyQRQ ^[+JTa]P<-!;jo>l/{liy:n2q(9(Ue')R!E-K$ `m2]KJ,(TQV31\35XaCKBYnY yM^gC p49PWg?y[fto0Z8ͫ\͹E'IniZaJH'#ўFEvZ xR"KjHl&gdQmÀ>s˖6tbɊ. VpOAbJi@9TrՓi6y4!٣k+ \H3r4KKC@^KJGeUiGC$%ڒ!IT2[Btԥ6Ҋw$cj; t!A U͗TJc,nIW*^ZVЯ I[l$$5͢3a"ڨ74R2^zhx9VRʎ3+UUde6r*KyRLߦL069*+Kch.җvun-d$(Ynԙ}Η:5%kܖ o FnڙZzŤݏxKas.X1&jq=\0$τ\gũͼ[z{|cBq%@->RS9sus`9ˋ?tͮwVֻϾ`8#9%̕:J?Tc%j>/#uQx!L!;RvyNN}$hc<|c~[Ŋ_&Թ_[Ҽמ yl7Ч˻Ӵe{v#2^6e۹M|:5aK(Zz {}ƜԷV7+cr%~$/ۀ +˻zSĖi-OW0%Ei{y/gOF+SX}s|.'|-Uk[^U_tZ>hP @(s+*84ިMc!ԏx?2h @(P @(Z#̚B{Ckq_ ~EuLsY%R 8;ACA8ym8޿pveTGNuJk$'8t>qݕ) W(jT{:]_LzA'K$[W_dc?>5G/Yqn AN~|~E 5̿8O\_t? Eɒ"6*GfSr`ҒTe$/ }e7tkuxg9}`Ӭj\$uEUguw74e\ !}X;6rRUNU_f~xW)5F>TC}blvm=Zbfu}i/$-RJuUݦ[*Ź3߱wx"yVkT|iexhP\#քn*./b1фFwn-xӍ뽾|~jזkNގþ]8sq6"E+MI&CrXuVyZʂ#UJRkfVKV5i^M=tE߻kHjJmKeV.{OUV}^Vj|_Yz~%.%pX߸iL͙:0l92 Qp{ o:9I(&:K/Ʒ^i/xquK֏ys!}o\tDU'SI%˗z}km;"d$<2TOd*tdHCl֚d䀳ڨ!.,ۋ{G=oZGfKl I0*RԵ_J)H!gxXXJzڭ8c:6VI6ƻ&I}yqB_#gh-DneIi(gJ¨l)uck&s6̶+w$$Hz?eG2+D6ӴY,TuU "o]Kf|V}-Y7Tb}Ÿ %*/)"~kPq%mmN0Qc "9e(K]uT+ʵH+LtVHƶ.>RsdDTl Sbg(ӧ }6-+e`-t2`Zze"2v.1z#0PryzIH)&ɓTO% . *o8R (rwE!P)[RfB[RlzEi.~9Դ0RF+fѢ6kvQ\֔P0VLmcCKm6moAJOTН̭:I\`#ۍMWȶEql31;2.Pw}#UhEBoI+Hjnhe1R}JJU2GO] 6ZbHq<Pأw涌[f.4莼]} q-S&M4d+<Ô_bk꿛lGzSP @( +|m\`E!&S)p!ܴ8@Dz~T),tP @(P @(T`zLRvP\q՜% $ q\\+`&5B{/h!ACIN3%"~ͺK:n;CKmN3~^S=yi 2.^c:)ՠ-)8ܓ] 'WcpE#5tHd" ZeQRdr88 t SӰV3:@ld#)ܢ65)|_z<*Ebr[e  9쩲;@ೳIffk]]uⅵ\BSѤA#n'998;jIP-9'yy>2Yyh`zGpʀۻi{=q RFBfJ<8+G=.\YۤN.+3zoyBV{8(= b%mI Lx䅪jw BdE]떯)"z½SuIU6!\K$9ˉ#ӡpWY^.]Yi8*Ž:C.ў` lF| -vUʱ{CWPڝE,(JI=Ǽ"^/k;Vlrqm?cC7w(I_m/g"ֻ|z2TclہXǎV(=Oe`!8!@65D'jNHg@ɺzS@-N֔)cд8Hm\p$p}k(I'y +K (^]AJu_w$%N-b(S*lRĕ@*{ƓEߖP"#fŐY );Or%ߨox sa\Q,N,>6)9J|$͸1ȧ}a6uϫ^J 8YJTMhVax\[X!Ds$dO09g` kؖ[˗k :%ȥ8԰DyO4ߵGmzN|.ww6uvK1Ҧ\l#jW[W>rsu H]ZR 6r<wvfD[.m[ }խG<$9?84O}תԖۏ&4rVҐ{2 sRz*X Xo'o߱Й~+l-PPO0r}Ѥ33$L=h==eJBm!I$H8'k s8I?EMZkuj9c׆Hϸfgj'zy>O-tUZEsxj›qb R!m;J$ cǝM8K^x1pJ:SoﱵO_+-XTB\)@ꔞCRvݐ~`S<‡tt:6/VJU^GGz|,6VJG3>{m?0Þ>"V ?ޔ͢Yu## fdž3j'tOь֏waW\HkvJJG$2rs@/{/qJ oЉ//?'{oR|?n.\ zg8;ZƉ[b%2#Cpgݬ:nY !jNZ*D=b9C9yIϟ;kB&L¸2Sa>AT[EHא$IJsP.H% /,%YPn-z=:qI5dHm22s-;9BPwRIr%:ݚqG(UIPi1zD]*8Jwzkm^--Sz-2JsU~RE`߹Dn8@Q`ЙF.=ٴ=%*^{ Z,$Iu/%琡FH?^&}QT-H?prKHEhҕjU2)>Wi&:Ԍbs\\K+<%U6N%8͔CMsĭNleӊP*!^[[%|PlI,k윕!I\Y2y*TxHwVםTXvjln z? gw}w嘭¡6[ @)FW|E5Zin"P{ʑ笜uAFճ $ol+jp$fvk^.7%T;E):Mu侅8BG4M7F2"߷)XR}Y'|q~ڈjBKmœ15.$ K kF&^ؘjQEէ[*lTSjz0N;IWFGk&F{Hahhl`FO:3ٙ5һޡMZzR1Gw:5dp&" C.5`YD+?a1?5_xʿY=| @(qz~T),tP @(P @Co雋zqhՀ|#ܚ[xuɎؖPCJ{hQ{wg( יnC+i%֖c)P<#( w x'~U밺GTdEB":#4i)I~d`eG7xdm8K QC]JڂpNGwxo-Z>d~K8!lԠn=6Pˌs|cGu昽BVImenmSkKQlHh-jmb+%E:(Y=qsh9j_CLč!.U252c> J )ZV< $d@V`_91\ ;Jwjqqէum-=&c8T&0ai SEey?e yfӚ:k]L 0^pFHmIXݍ P<1cjI)+LRU<<^Ю \tSiQܶ)j҆RRv/v06NɭaqO@ߥkUڒuds % JJ9't tpjTLVB (mlC@ٌm;ߵYֺ-JWaIWi\p{ -@kFWTV%)g%] /WgA?rᔉL>G )JRTD U5}cpsN.th6b^ $)NFI@a㮙uׯWY*!y(Kt (3=nf]-p9LT )IQF0@Ë#xe?IME"[!r"b4m8lۆzgFgǫeTfeׇG$΍AB,fҾMVY`+yZִ\9d _O xkD8|{VR\rBm]JQ ZN3#,hjٓ5#kT-,5NBS{ίv6]Y"?S-kdq)[HJh( F>g4] 5`ș:m!+!T7: J`g?,Bjb.3y$ H mϐZF^͟E:6L2PVPoqZ0PBrU (I }sx{`vv 1!]=e%CG%wwdVXW6w|ÇgLɇ+sEA-Ֆy-i$phBXs?z/UK2Z v([8v{PrdR]}.+=xFHpF{:Dqisze>/GF8-eMm\kDPͪ,M75xJZRJѐGyycFY$=|q~W=/NŻ4<8oQ(ۄuF~' rr*Xj:0:[\Fɹk>|)պv8/8dI)J0rrprr!N]v}GbcRZHfKeHI{W xosĔd;$((JwTy\)9X2rn{v]' ;zݿb:N[5,TƺCjc)V48#+xqU%gqx$Mś6t[L&BaCM'jSyգQZcbfd(P @W8jRBÏK:H&ڠP @(P=ɠ4.?\x?9)I@D^87257'voLK{T^谒ҥd»%߉v}{U35=v/h|e*, 2( zwt`|_|^w[hG7G^̬񟎷]vkEcq_5*a:" L o%";*)%jP%)b(m/{:1-v7쿲}ȥgT\ק񼤮IIIe-2TJ[ܨ6ͤhn[[}^=D-k'5OܦsJVP -ˀmƴwutk_Me[Rk:Z\dlt`Q҄RJwRçU$<FB7Fo$^NI~{ѤYozx{.>+͗R,:y@N]xJkaRS]Ovh,//Q팮QqZN_75m@BӺ4j{cbJT[RV8>GF-bfO2f/k{G4^4 CR\ Oi7+nfo2"n%:PO3n/uKtb֩R[P9&Ӥu֗&^ITV:ҕԇ>inS%JOD4e(oծ]|m߅DPp tAc%&0֩ !쾧2JTIXo<{uij]o6זk:ѫ h ~pnC7)2btS\WȬˏc 8Yxmqy\_'JuʙoԜ'ӎ;@K+2ekf%v\ BJ[J:N;HbI[Ė'%yjg%Z{^zlmhG⣰ڭΑo&j]q!kRrBʹ6XZGK:y"«FjehY..uN]]N{P)$ǔ *k bMul|#wm|ݽ߿p$pF:Y!-kYʔLtI]XbK.q_1jEP @(7-L{j _Bښ%nr=*3B'п(U8-Hu8 bɀq#ʆ 3% "z;qGwR+Jejc7 #zҦ&$YNlGI.Ze>cwS'獒Njn l{j)Z.&Хh-`'n>Z͵eQ$- R}fh+ҥ;S;T{=D{̮R5t䩍"C<ґF^[ 0 emYQd)V`mH#obfQ[z2(HVЯtb*x2~RG-T-KKœY$ ܃'L,֏b\ZdDCBTù 싳X.橧R6yrXhdZjG*@K8WN6z !{r؝4PH l>Z\ʙ.yclLneXow!Qn-A抒d$hq8Y䀮h_!kVR#{z5Ω B'fԌ%Ujlܘo֏D p;IG)hi+VyuCu6N-҆ud215ZZtȨߝ)ɳ, EU<ť׮Dq撕'#hYx]fv^,8M`<4L ^jQ[8NҢ;ZU. D-4eVNO5^:XZ`S*o[Ż0`[ <緟> ^.Yh #rHܒ$&G >ٓ+Wpz^q10Y=ʚ̨VӪ(x6NBqz.D#bO%$ RZVmkzl%mV+Yjm۞'DKBK)t"1%N%9%*B '{XֆlN}Iy2@FM4#5LhzYgXSRۂJYqZܒIS-bܶ4% R# N=J ?{//S@XP @(mMyj]9o..n )HRs$@Z( Ff S>zXt-IJWdc,l.kKN^.DO`8F6F@8Wv;gY^0F\aE`ڴVr@qk\`j+nzqɳ-IvS2؅%).% W ᎧuvF;ztm)2̹-Y NРT{XmuZqkd%vq-:0Pm8@q/QKHP HIhvv=TiFz+n4,RB B}"bݜcOujړ:I@VV $$dzvǫm>RW /3nS9iryJH<:D(K-6B:oa½(^8XN4hm}i vwswrQC*t"3Ia@=ohIJH$ZF 9y?ӦGZܛZO0)J|f*U.)Tyg4΍q4):!ײ#̼͸(ŠN;vԗ-K5 ȶiH=b-x% -+hY߸*Är@r}snJ*ӤnPt{WJB!)9+HPvmHmLQuxiq8z42y( hJ7N_}Ÿd+4RpJO=UѮvj'nۉYH݂ ]@miN68y፲I[j ԶR杩 vr@S(bw jV[m%Ҁ$){F $ݭ|2ZOV^|yEǜ$ NJ3,p41kLy$N:$${Hh ]b L?sZ<9oRgtWXS #a9s;P[\K6k`6=)P 8$@xwG_,G-.s}Q-IHyO>dyhSlm{7(%Ļм7$<@{z^իlZ.Zm{nvS݂0@#j_n$>m#ni-͆p68l'nیf?L{to>7^U/ڣ@m@}D0NJIO݌z')O>GɯV7{.W| xŪ}r׽;%ũ9;NAV@9Cnv|H? 0rKv.2VU}1ePI[+ؕ(mN0p{>j] ܺDqz# /c{5]s[ݟHÞܻkH,Ixy3͊f?7px[CũrS01)'@ Awy|g&†.TUknqr$εmT%GmER*W>%d?އM:iԑk>@Pa}9Q]f?! ݴ>!lN# ]o*k}z-qe5632q.G+4գǜ%' kFe((P @( {QWU@8qiQB@Cݩ~dTP @(P 7~G44jy)QzvJpv$B(qt~AT °09+?VW.TQ0~8I@b ~@LҠ+q`Um*T\Ryy1VGe%ʎoOl}>vp s#(ǯ5&'kd,xK.-rLmBNs|yk 'l;%.lDS%ږyռㄒ}*8`UTMK)5 Pw՜F*9FQibnORDgaM֯ ǸK֞i)Zʇ!ʹvf<1l6VLFz>tm 9R1@uM۲+J=Jqdɳ ^k[JNUC'sPI+]ޭmj%#e:eNvRPy<>7n\,Cn%N0>RGvThЅ?644&fF`EbCha.4m*)GG*+wQ7@ҶS9Gj;ns:7o불rA0aEIt0$`H +#ϒaՔّ`Aޔ0#U;}w>tz66}2b% &:8N6Fd`-ެ3#7v!&CJ\u+h)a9 ݒc͓$xouRY6Vnv\MӴ@{&64xm@PVޑ`dECJJMܛeCµuxQDvK! !)Hʳ9՛m*I^/hUPߴ*@=B_~?7Kf }@LҠ/hT%3JzoPPߴ*@Viq^mNtVNr'h w'( 5fJ ?I{zUg~Oy&P?dq;[ڐl/p!EU߉T0}cNiuJp\ٸQT}r8W7-5Fn)9eoƌh5wiܫj2`9SfM.F)oDHs?fiHpaWx@M[5[$7U$}źBT*z.W(].SH=YTU#,\P pgF5kiw;'x##sTJJŸu8䕡J紜+kRF ;bUS*R#?D[$ٳ|Cn9'T`qwYA5L7:R&w^o!ճԙ9U4y1:fKny2 wqrӃ0Z,,q ,'"Q=9sXuxyƁ$+ 8j샠՚#Jբ`f:Qm6i!QMs4? ![R+5kcIhXZbD~b痢qvUFvjTt%䓴`MJ%OECjg&SaTm㎔`U'eh;sK_V )hUM7/q wHlk*ػ]ɒ[@HRN01WNޤD+1CRwm~$VIBf)婆Kc;N:Dқ&2NȚIe+ɌXCN 3Ҳ+Z,q.]j7UyyY-PiUW=A5/MLӽ &Ҕ̐m[CyWD1")U%RTg X%+GD0!Lɩo%Im. PG ѐbX\ZU(gϏ5m{̱0nD5Pu%kh=o5m6a蚽HNlBBTs3XŽ=ۺ5yar Ln+*9iL^q/J#8y]O2S5FQ)փ+,-\KM7*42|T"͢r֤l|VyVODӰMid6F0z Qjگ˖sp8-0-lueBR0aE@!)N1Ts0tgCK>+vv[e怜pаUsG)6aIPx*Vl#xHaG{5U5v!Joјމ-Ga*JF A0@6. uo{=Ox8m*F77ߝ-ˊIg/.fpZҗNӱ*RJs''>qg}+М2^ $7W# {rn$-8q*AV칅 ! *xmw|7JX.ޫ2mWӇ\=7]հem8$8{䯩T?Ll|N&XxyIme>eYx<&Le?⎘p%{7B6m%_j, ~FN?'Ulծ_Wu$ ̉f4JR{0r9N{1E,];K2oΟh3eCEi,y!#>UEEr>g\F,oܫ @(P T}P~_T_ЦjG4P @(MߑMq{o jRmk-aIAiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{PVfGiig{Pښ&-խ"c9R@P4\x86^=+sx(%e?_YW<>@AymjC=M 1Yx+--Ee;Wv^wi)>Y(ڱ|i䲱ufGEė[YdSQ] zB, EBC_m?C\v!Y8+FݭiHBUa'#D&&j2]` xچЖtyJVcܪ)R؞d)jiVl)F)PKIiP±UkAw7UwL6BmYLN͢<{.Sۀ(pht԰DEs y I^UT(Lv%:p#EZ02ѱ*qZ'.:oZކ=#a <1D2ٜ+19j1I[Xpg\[~l%ȋYO2 Stȓcnz N7%F>kTsZBH9)WwKJ#xa 6 li&dH!fNIj )@NquωZ$&Br.d$zҲ@<*=mA.1w=ў竩q[$S nLYK`$mR{&օ)=M ks])S+G0g KV $|ϒ]q&pɻ$*E$/Dt)2xwU ZMOb(SFzB O?Tle7 痢؜$)I'9tm v˓yhd=J?-rN2|Θe6gXmGOǢJMY;yjVqVn%vN`F+0MjsS#͈p*4OShxї!4 ydiznE>Z 0Ȁ9Cvs鱖4g$P95mQ)iG"R2_ 2=YfZ2oswH*C.1`^)"Byrǝrk2am4opCր>ʴ:6pSMAvJȳ6ζHV9rPyPg\xM ,f'E1/\mťqr-+{E](o1[mI%ԓ wy?^e/kΏoQK:[#R\IQ c*9 V}kBA\1"v[\HHg8#rSs@áuP1K+Ne^t׍u/ n}swXJ9 RHR\_SFBˢB*6 rC[nswciFpFH ~=.\]띍v<$e\q%O6 $$sʴ^k}_ymiGc,MeCPJraM+iƁuE\Gj@- <#9szv o'STp?<)Εy^e"6xY#p>Tޛr۬߷kvڭ6PHy~IR3pNL7uj[3wTe&CBn@O0 @හICP;3/*!63`<5(+J2*g77VHժ7*&2d=RY8(RqUi;};L&uє} 0`2O ,]xՠsL,$)_%Y p6t$rZ:D+sWHԕ2s@ZBF9+5Z/ -5LOtY6Rܕ6l8$'vEsFG]"^/j M6JRd)BH#z9Vvt4壯K5cr *@ܥrHa[ݏXpgi7",[!ޑ7 RpG20dv~r2WyPsVR@agjJI˘З/ݖdG dOt8‰HING1̑q[ ]Dn;!M[Ra|\%C9N~HcaMU#Dz/oG2}kIUϴ7kFEjf&2R@+XOhs%*r让z(|.:zpy=FkNXM(R[%)"v8XlUI үpSSVT_Wj}6CM;yHy9Iz%tGXjmogov.;p%3Ec RHJ9q>C1^K20Y[h ZZ+HGw!MŽ/|$$ܕ#}$>+rUߦ]v2\2][8.985ߏ0#|E|S9_ wɯӗ30KZ=J8)XV|$zQXq?I|.~eNwQП Z]X )hI('଱dt~a⺌?~ sⶭk]E ƙd-Mģ# HIRnW.~k11$-Q_`CewUmڮ;z @(qz~T),t9ڑM@(P @(+Sw{@h\~~s@kP @(P @(P @(P @(P @(nн[G>/{/qJ oЉ//?'{oR|/'v&Р# =5%(4>JlGZ,LSj֗>XAe]Uy35K7i9sV=4LOL͕+uĠ* ZRVϪ,6qi XGig+zәT'P۹H᭖"E{sY(WYtU ;nIVKe)20FFJURR}%MEu%6y%rE]5 Ӈp<YڶoK,6Fѹ9#ܪ8kVQ;[sᥖ4;++&}ƱyPkkdS%@IZ"R\X )lKOv#h=ŏ8ꒀ3IÊ͚ۤvBSrq9ΞZH/$ uhF_"bJ~*75#,ԔP6=Uw7͈eTgیRm̥D#-Y"wJ3Uqы'Lֲi-DyVVFd`Qz* br}.4;@GH$xqB #ݱ,һ݋1e;NARެf(EqہmϦCRm2Bu@%!cr{oKCxyy}rC c9I +)S)-5hyef{ϔ*t%jǣmR;ypRwoRSOcDy's9iq>i- Q7lde zkLX)E2tݝ ?N B5 +im ۇTHVPؓ.oLL|ۆI𴊺D!;!+R2T0Hqg]t4mhbJtJ\T;Tps@/#ݹꛋ֥ѥ)ڽ-)(N/YݟL;PLٍʍ~p47p;@3@r&ڶoފL7+%Nv │J2TU64j-4TVMxnQnJ#GGHl BԂ Tv >]W.Dd7xE{% @<\pJҾ+׶ 1YrFwsQ@ rJIJi6ha:݊NqNO-5|eeѸSN9''+Ғ'c9/X]ŵtZX[>avBJU\whH<ذq^/JNik]Z._>p\qWBƢ4ŭDe'scÅƍ϶:wUk'p+w[EhF7#؎!dgnN9t=!ljڃk_jq ^:zCP섨?Ő]llYbԝa}`t{ŠUzrcWҩŚa>((Ϙ(w񢲽Q x})b%Zn{' o:%k%am,m PJxV&t +JO \`\= @*ޔݜ {5^ 8mm6/CN|knd2m&luw(wp z3q"%>71xLXʥ@ᯃEܶ CϹxO!Y֧t='8+ʹ%Wt:__0\b\ 6撁vA9xD1V/Qiʏz%%fW3}7UR_9ָŨ$p&_)ڝ$)Y!@ bڶ|GLS<"ŢuwAr U9D|VX9q'G ]_jP#TЪpz4?R=ɠ6P @(jox?2h #h j@(P @(Qs'9 |-8Lռ?ܹ?τK|'\?u o74O{gM>.oh }% ubXZec Mo؅-]% <P @(P @(nн[G>/{/qJ oЉ//?'{oR|(#RUb@r7J\RGajOx<+-Ut;6.2#ܴ88yA0ۉbSJ5mCQe.`{yK;ՈC",`Z[QskV/6uFA@9M-53z*vE} {s\ͼGSoVJJ⑐1Ty+KHs!5Kf%c6Pv$U_YZ!2DbF[F\0TPd FҶ֤ROFZ""l )yv|k[3;$CL{g~ VD6D6DlXifg$ƌ?TSȃ5JZ*7m)=3}*[ە(yZil܍:mN\/m8ɩf]֦X)2Pۙ`'媹4Uw(s->mmS FZt*Х(zލ\y+=S^Fc]#/q(\zr@ @Bx'"n%_s)R:0bYC=J 56T,"3тѼ)'f8*DȊQ)g`~ 3c?EN%2Zs( ySCц Z4zx%7GVT|,elPɱTǭAl-2rEt(,َg-(wP1*ޱOoa=0w+wF;QJD$kTdXԖRBoߊLVZ_mK*3Z!|Ԡ}W:¾G\ȓkT&{$+Zp*#IdK4ypPܣLlU>2ify3w K%Em+Ԝ|H,  6 E:joTiM[TOWZ\FN}5[-C3h՘ۑK*J;Js颜c9!պiLAs]*Y㡄=,K*;ER)}&qjRRi"R`K$%j牖GFM z:OJ %'9BY ! p#g5mGS>f:qv\Q'Uy- [(p6gyTf#n`e֐$ӳ3Z$beZI8ڒTpM]˩(XaҢɎ( rc9J-2g֖ia>Bh(NkHaSPPBv7i5hYfJѣklm t7]-:w.usmnDVGx}+=QyUbܔvKϱysα۴4J8#'S ԍEmr[瞐gr9A'ƾW>=@(P#TЪpz4P @(P =gs::UէRJsb>/֨=Zbb/ܡ-Pp`oIR(#MR8`~>6Ԉ;.gB񐐤Fys65u\~Lk\%B8h=a@r!? Q@|/nOޢ\_k{r(VRz@Q8ͳLїI\R-2㸷KjI$V܌v@u-} ѡLbMi):s)Lt-e)Seo9 #;E_n+wk)u*PV-is̒r3@_!k. ,||b8)t(80m+vܜA{, DQQvnHyWٸ;_eZ\Lai-I%*go1@oܦܴvZ;X5MivI["N {n녷;M^u\wYL7 !E7eINz_&m|'yICjBPBA$uu΂*YN_cONFUrw]I'?.%,,ERGmTvd(G&rGq)qrw|+= F4hŨMⶨZ;HqjiRJh,Z]Pۍ9oM aiۈ[Y rSKIr-V9P)#5>R" _cSݣWUX.Qc7kSh[q8Ԏ\gevFW6Є!)t%@iM&-IC $ y 㟘fPTAK7\pVT|Jj2`+l+V1F{{VT,ϒE|nk#ovzqjnҭORᏹ'9Ujڒmrp=h*i%vO58@QmpH5p>;(N\,N:e=`p61q܍K,CPW\⬮Dwn!8a%]V̮Eq1}[cem2t m' •E[4y㙑sa() _c'ei*(젌yVszIkl6} vXMI%lk[1KEI3<\$EBRB3ɬrv6V/!C؊9Ei->f[%թ% IKTLd^qۤեAz0p֟'RCV$;՗ӽ`~2~ JU xyQB»mXQF z )u))ld(SNѤc1wrʍ wU঴܅'ƞKޙ)k'O$ZVRN)3T+tQ78]fHZBY:ZhjCB&!96Yt2Hruv(U3Sg>;6!(9>鬤= mk;Gkx]Ei)igh&ƪjz1 ;P0~B\P"(VkVU23T(Zյ2K)V?c,$%UZ4Rs:젬'q]~+TpVިα %^m_Sƫtt' xٺ$W<__TP @W8jRBÏK:@(P @(XKD эz NpHk%#G>gM]qO&&j4wX*y=PH۷~ʏcF@_wz] z Irt@p%MMdd a)<KSh%<+W:(~2$O.`';׉N=B%R` mR %I°I</eiש7^c udtmOFA ')HI9' @bHxwp9Wӷg 0S) a; A<];CYcȷ4"(s/]ʟzס7(B$m ĕ'R7!XU[HH$zv]witx^NoRGGs $cdxVZf5ibݗ.'ZP6n^ @Z7D^5+ %p %zUnJJ’UA;F@3SjM+~-+/l)~1ao7p g}Wӊ'^ݭCK (Ba$M6]kx1XGd(YQ$ T9` σ Z_Pkk~[jCl'ȃJ;V >Zr{Uou}ѵ1.2YFsDʀᶍi)q Z2^pDm־ڏ7jAc/k&M&NrF:% YSYKcrrRg9nqnFj{MW˼"j[a%Aۓ;BBH@ᎃ\5IZqD[TշgY R<ێjn{\8hww4ܭj۝it] (Òʻɠ, Z,m509+mIE 'c-*ۂ;s@Kw7?X9f7ruԦbmJAʀ ~Lj$qkPAܮJ+;p۸.ܗc+Xq{$v =s . qG:1ohe .""kɵi@IQZxsm0{ҖRHkm)~zlAyE+r؉u+lwi g= 76 @ܩea RT`yܜP8i.f@3^'R=΄~&}?ZWk22lJ@3ݯx=|C,Mwϸ'V-fޕAp(% G $Wz˲OwE5Uv0YHC&C }v?)`׉41l08X+{۱lzY6եĆ˰KP'9wxU77IN^ͦ$םv\sםqX&ʀHBH|U89bp8/q4[\$t@kFs6 RpU3 gcQMq*|ڥM>N57C+#\ѵ,2Bp'$!9; )1q!Stpxukڳk@j鋀ԲT'q+V Hs8L)9qC#O[KR}ޙ.ƚV¨`+tggcp;ˍޚ71gxFiFp97ȅm$'ʐA$+X+z9p RVըoow|bKm-|(5ΊuY(Zv Ǘ߃.ioLpX|,'jWȸWI @(P#TЪpz4?R=ɠ6P @(jox?2h #h j@(<&|Ez2mx% Rz5ZJH$w&"1"kLxo 9ެ5墳U/.4M87@KeN8=IFKWRO_gEeq~϶R֦:CN٥ ,U7c0nJEإM#Es A R õPp)w672yICVGuflj5pXH6> = d8-Hh$3Zss|4U_}liK\W>6e^6`P4>vϿ5|gs~T"Y߽@( IgRP @(P @(6m\x86^=+sx(%e?_YW<>@AymjC=n(Y*)ڙ3ϫ\h $\ҧ_ԣr\rtޚZىc'y9P2]SCcӒGJ{ZKOM `6nO5gH,&"> RZ̨_ħ.v1rܥKc &147uI9 ? JĒv˸aOBdNJܕrE=rtE23n 7 +t+ QS$WD[(GNݨn葻ỴēZQJ#yGVJR"5-RmM3ҥl}gf^n4Q;!UmRS *vk<Π(=,d6T0sF,Xy@e=%Id_]٫t*{E ?y~'&cS%]iNIJP}58rziR= :WkWϏ-'D`}X؜'6M͔3= kϗ˪PZ]r2ƌ]V#% -C^0sTĒ:8t'm%Idg|_k3ZWTubΫ;ʈT QF6 *@%ԅr+Ɲc9KAIq= <7r´svj0I<-VX*Z -"o]Lp! t@' P~0$zW^DNC ϻXMEM:0z2TK-9$Y}Sl1fҕ&3jY;歱#({C2znoZoiLiE;9)"\W,>롺|_&Ӥ|{%A_uvc4~ Z6Q'$j@TӴrV<}|<Y~U1L).-jpƲ+|9LčE<Ӕ_bk꿛lGzSP @(\?y>J ?{//S@XP @(8wu\۵F@mQYbkKPHQq<@)'Fiu ZTjl[/ɵOCaN*Q>|:oåuEhW;S[`6K$%aO$tkZTm=pAlH^т5 IPRy=\N#mz/HٞrV )iJKI #8"8Q=MJejiwVےJ֢NHۀѠ?n}G-JZk 8hZF% ږdUŁqyțo!(JApm yz-w;gaelqi')J9MҸwċ گBXhx]`[ġ8 ʶ$\:gvUս1aZ" % @ QqNBs( ECj7cj(Sd-?=M:>9>НXRxAJ2fH p$96]2n#?l,9omT0JN㓒7c&ZYYY_cfa:Ts<c lK JZ5L[lel*^=w ;xm)mJKO!'@|/'ͻX^rǦ`zљ)Q Q \ $J@s$ϛ%fǵؼNGS{wE> L#8$-aA$e@'~'?7B8:RT>{nѣ3 3Q㴆#m4 `$9b$#Xsmü-w3Zpz`[{0|'=7p>7bEe'~G<:O1ɘ[{̽ ܔV  _-qv~oE|^8wuoo9i XO'0T $$DyyWbqP–F5}qäoW։!)6Mpڐ.crvW(i7 '8È-W/7nvn rڽsQ D`y3#;<7rvW=! aaF=g\ceozKB> Ic^V8N1Ϳ@h-WfGsM//m9O?.G1&QW'-^߸OFnbO93 G>^y寫j Ks*xx w^*gn5Cg ;; ۻϓr 93*u:O4v7^ gwG!eTσ'ڝfv؎qlmCM$%)`!RJ,I9|ٖP @(\?y>J ?{//S@Xs#̚jP @(P @V&иr=֠P @(PVťXhQ O큜 ~_4h/?_3@? fpn7[+N_Nx:F\k:LӜcs@}@(P @(P6O?P/}} jj̕˼?BL~W w!be[ړ$N?bGNe] 8;Ҕw\Ė4n֠Ė(e֬%oBvO\K kGYkVa,.I Goyh+ {Jz6dL?{}XRcHe{LUʬҕ1H.j('4SksvF:b3X8n.#V'^NWUk6VE;)\&!+9${3}k94[mKg AQ~ZF?(C>9EL ܼZ:vYA)'ƕ{6%mޜhiN+^&in?#􇣰?ˁ&iq*PVAdUlQ"Ui3.Aȭ/r;[7sS\mnmoQ]H3kcNMLD8[/לǛՍ65ROFlڠ%*>BNKDlaH kd\Rp7l=j.%KJP n5"51]5JGBI5ܻYPfmGgjmCsasbI#ܩw# ea]'GѼۥ*K7oKΛGmζ`(y ~71^4U?u[Ln rtZRȥC qX ĵť͵Gq?q)-oK~ιRV-ގ@K Hl1x)j9]-lJҜ9OH(Qoh!(&,!/i508}QA==me37 5ڟ4JA *:B6s[,V7^-Dv֜uR<SJO~Aj%M8xn8q) rHXi"YJ\{Du-dftk-u -ts8U%WWR) \BZBFNW/䣩)w3!8BT`bpdM咣Q\nOF (OܪmjN<H Kn ̎e<֒N_]1vp)*g"py\,)|b呝:mYG5)!*a]ռR{);~%-F~+ˉ1Q.T1N9kσVVd]CMaJqtaE8O_ *Jyy5VF).CN9ݐ>' EZ:q֥Ch-6ZT}y #H?HNc}Lo6- q`%bWRy=kmJt$VLr6GݣQU&7Is Fļ߃,tWԩ èuҢBVMM-aw-"1,rJGpdVMLáŰ閎GpOvZ3kbi8,"{„ۼ}P]r-k~eQYBdm孡Cj.^YxÇzt8ҵ5DDyVFJ3jWy _U?g?CʞРP @( {QWU@8qiQB@(P @(wKi6&4OI#rAh62qyg$F/8:]q`;[Ɋ.GCjIl@_5nqj-g:z',!N)HWFsސv |%>UXnLRo֯H'8375O)$K9uHRLʊSݷ IXкJڵmOٺޑ1ۍe.m+hVJW84=9ǞWK+C&4$co5GS'rpHn4$%ӭ)qoHRд%*$.X񦯶m;vmj☪#j1J8PN1!E[US\ҖۖhqUwHR^eȀӀ'8BUI9.\7_H쭰2n%#gN@ʔO<|z8m%鷯spKU(K~ȄJ/ݭ|/ݮvlWd0d }ġj-Pwd@eQٵ=Z@CyTA>>"CziRSyKBݵI.)IRwmߔK^ӴDVbɴ"E*<$)%*,bW ^|uÅ뉨EzLvBf+PRVPvdV6m7@Uv/126$)ۊD pNF@ڏSi[Jj 1je0<%KKP* y9@M%}+ʬLh3")@t7m Y{@IxA pѓd~"kHnp@D҅@,e%+J0EBj_6+FE \QTd8%#q9Gw7+tj.+^fGI#MAp06(\#^);WxUn~%Q!I (4G#IҺ?S[ܝ24ŕCm.-(O> `ȓO H-SF%C򢫞>FrFy@Bqg:RF-.=?y98@l&J !ҵ(% $w ei][n*7;obO׮0ۍZNϯ`h'C]]*) OH| ys8Nj_PҌ.(xq10ٟz \8mgvwG@_pͿP*poHu@ ԬFg] N285Kwմ[qKFգNp㿣8Oa+ݤ<"<8'.J=ZkNfݘ_l)i+M I'Ni<&DgS9SߓBqsbs)\%8B 2SdIs<> j3>zNݾ߿>gMIk֡R{v)=ܓq灇*ugGRIg=|ֽ݅C{A`LR<!'8.uϋÒÄO_?bݥu'.ꞇcvhR$((f,>_~=c$"wU{+-M)vT7$#>btbaXH>/ǏŽ xIR-eK[E!!#9d+ 8Ӷz})8(EF+yk@(P @(s+*84ިMc!ԏx?2h @(P @(Z#̚BZP @(P @( wPCvQ=FFxu%nB@=@;ԟ[} 5RoO-Izw۩?зP8ԡrzN_mm )@-z7M`)'vTy#ɃHZ (lMfSSֆ;*$)'#FP @(P @( w'( 5fJ ?I{zUg~Oy&P?dq;[ڐl1?:xc2mgq& \ť)y*'=XIzGHq^I STo1m^nCܔ -%=W*Mi47\S6s01ս[ZٛhD/Nyfz-T]xD$A*"Yy%]])9 VRoshKAm (Ծȹ*K ʶy]*ʌniq% A+ AIjvA[)oJ* Twl ΡB%ۙye0=85eJrԆZ`$vVW,.Ѷ%]7#'MEsT:H¼t$uýmmm' W2=9Υ>ѮॉNZ5tv3IIO>e)W~oÏ Iqm*zVPIa +st0R{isv(rK!9nZRG.Fi[s[A+֥yR(56ड+䟆ە].\=mүp۟-alvklջ<gUX7PEjaoa8ܥs5q2#6p|[xry޺ YǶ7[ 8ן%tw|MeFn7T[! IP)@+̮zQϝ [3 "25e9e) '>J?YS7 `+֮4ZoSxI%_ǹ ,!ekYB0ԘNXo4:O.uጲ?pX42re, =UcaN+:GL|f̮NKZХT}z]6ٗ8nW]"q41äx(@ңq\He9|+ggtTe RܥH[ }F>rg,֌Z;9n 82Eۖn\iQ'9 %f%^hwWB8?nI%vv[RFzAWߨ~=eҊOue]&YB[RI^2mݦP5v\Chu@dw*ZKjGzVp'/IxR+=o"G(gzs?5KVdvrKm 7(+ ƹ mw&GDzxEՎzԌUҧtC\VØJ R;n::`o~q]rsemB[)%É*Iq:VVKKM#- ',ph-qaY'Y5$%NuJTTI]JH< @aK u`ڑNjTfsN"YHyE1q-XAQ2eMn%?kcuFFؒ1Ȟ^d՞,-f+RMRwcq0㈪FQŔU\(xHB5*B,FeuZ]y =z_&YqԇBRIҵDz3Y\4*ʑMWfh#M>eF_B5Q舛[R}5%%a2[jLiۄ]< mݵ'MS.mVV.S4#NOD{Y '-Vrfe+Z% 8VF z;[`oFN"t~ˌ\2CIoQv %ˊII+}/=kqig+[sXG)*#%;H:/T\0iM7~˷l0n*ӅAhéyJl)(֦TU[QT3չ6JZqTX蕗JJI,'a-nҼ>f fD]mr8%@c\ǣ^:U: +U =HH)N\Xq Rn("kKd7a0Pkm Pv'sH/N+ #=oXL+-Y@RJJ$'%D9:ERh7iZWSuŸf@S [ RB @yZݥ_Άln7ƥ%).(uQRa=riN-j\DjZЬT-=.C%xBV\$d'=p'oEW{.qRhoh%~:nq JXQʲvXmxÝcSQoL?24U)‹vHJp7f(fEzM8= /֑A5_p;G^tHSxrl{]]c8Cͭ Wd$Ns@Uu3WҺfCXQnS) *N(Jod'p߯Z{QޚT˶"[n,2>a@ss^ AsE;Z6ۣDg攰<$jyd@N{Wfε ð^C-w(XRҕ+#!Y-3њ=MOp@DYsDĴP4m!$9oH?zwLZZj"s$'38$s9c΋_flvN'x85(iʝw_=C_8΄ᝈ sĬ&r #8JPHX)NIϝÊōׇE#oWIRZy軮8b=vۯ2c@B I8Gx \Z~±$].ӗk侳qWRqvzXȳ[}bꉪbm(ZTIpqIʮWO&'$Ov Яjs.z 87źBO8mzvTh+PTbGyKg :%IV25+׃#૩zv,35 9 l.Lw#}BRiCkukB eڗv孔Rc{n?nHP F?c֐t}qz[6҉"0ZeKh=PJTl5їmDpnS6SosqŶT`p֠P @(P feﯡ{MCْ9w}^_~ߡ_^I*N:C?% }n>Z\?Wg{lg<(,oю<|dn8Us W2)Yz!87m6KMUɓ$a%X>i+LHA4bއ GBHk(F Tg']Od$Xj^h*:lK^;W<5SH6NmڷIX¹" .!&JY@lC Fi* z߆$M-MWWlzU% ,6˳]I>ێrGF}W>&gdFTb!@ia? ],QgV1G-19"ԟ/#=ܥJ dnMb.$ [XݜⷃSۊ3T"«h$䃋n!sP@!YVjت&JҺ@y! CxJR`rJUA;Esɪ(Wn*mhm mqT7G\eͪHK;Q넓.>]8.m\U$يOQyh.-(hj{[4(z-EU,ΉRʭ3.ma +G,jk]墠 M"2GSVGev=qYjgUVdexcrgD6ߖl֍jQfژħڊg)%0;Х:%R5m)Ag"K @֚@pT¥.⸍.֨utaƭ# rrkٵ宴FJTctIy6B†>O24BV8nH =߉ 9!,ZZCm흣+R:]RA˳Unkafk2IqQ@N0 G+2V3QkcmIn4hRԳQOWE&,ҟGJYwH)>S+8NV4Spݙl$}Q^]͵rD*;[u[Je)-. 9*qn!\y]jZfفo(!G,6QF[1jI+!¬+e[E&s:Ԧ mVќ! E˙9A'ƾW>=@(P#TЪpz4P @(^7:(li;'jw${c ա_a*&ޣ"u.KLj[m*)PVNyv}ibqҚ" pΪMm"RvcqJԄmNI  ƈM} 5b&E%޹%A)8JVwwd(Qk~Hǰ/VYXRUt#*9JI4e勤ma}%-aո\ mK[ඓh8Э\].3zFmkeaCIv8*8JscP_j#z6 :ָnB٘C %ݯx% _:ָ4;ft̫rZy Yp;Ot-_ĸ!N+0- m]#pjA<Ԥ($cAZ:%ػHp- x/@Q X /Yc@^o1mқrdZa I.R: P`p@ʳE#ɲlf$$[ !Ey3|%: V6v.,"2c2BZ¾IyÎH=@V_c_uz0 i{gN%deX.s!k  ӡ+f׷oOtKRZ])M\*HV A 7^ q Vⴸ9sԓ@t4l8uo^4:jp!d$w%.\ ұx|caȊRB,)XPQ^P yzSP_chOZmٶlWI%\{) ;l 'kFθiإf5m!676C;%)삒A#Q ok'N[5 Ngu7S*GBeͣ$6 (h 5QöF~e,J]J;w NG$X?R%"iC+Y[sۜr'X15Yq2RW!Ͼ?(Ԛ"eӪ\sץGhUmg PDYGaV0b$$#rT({o\rϘzHG'_CX|WJu4ks8V-8:I\;># q7[Qz1\dZ[;[ۻ?<նiaoz&U)+*R9v;aS×TxH8Ḽ/ҫҶwZG eݝ)ؽ)(*r]GAk}P&ɑv7)J &Ps=ÇN;1h}YleIRA P##s&+^3iOg=ba{]ʊ@(s+*84ިMc!ԏx?2h @(P @(Z#̚BZP @(_YpEqj!aȎ̵2 .$9 lr߆:7P55]L3. ;'CI|fz9Fft,2ڵ*.`YS-.wfmtf mmNf#h}iVKn s xmĊDA$t?& sL\5((!֒CrH;Tܣc}!#B 9 f!FTT*)JQRI$js`?ƨ̓Xz{kPW6mcj@=\==_mwc1NfKithW<"]˩M&=mfsLgZu Ӡ\BBǟ"7  kaD{OzL[uiCi)HDtP @(P @h[),۸nH3qa &{OXIt'>&*̕OQK\~ү8"x} ? 2%_ԇ_({g_CM%)(JN1Y^^m)+> (4w]渔v7eYMtG1sˢ5ϗ( ?-hͲj1Hm;~kq,K4i\#J}o]ZFY -C|7!@iH%C+Ssf*! 8T(G֎荵Õ*qR9Z&ꭕ0c%h;}VІmM%X{W6ΑM48:4}VmYT归ÏJycʴPUG$Om1n7lWd.ݣ74őYqytXV<ܪ[M>ӆ +g݊ԝ=ɶxh A·W]PYf3J}rwXոӯ.K!Q[m dfn.JYeǎ+QV'5yb5J.ҕ$wK l :FܹuWxݐMs HVۇWjcM*(ay`6㝬y#z9lܛՕq%-X9|Փ]֢پ?>egWV[*^٘-BSŴÆܧ LnUJlrha Hi8J`aZJ䲭Ke:))Z\e 淏?Xq bT;5oD~c bPm]DmM3 ʊxsq!5VLZB#9 ~ː/ KODA'i9UeѮTyb{ 4-dN JZЭK_$m96U;)#QLFX* }3U5U̗FbKopk..7=w`jC%X5uJi1j`:JmD{JuF㨷%R&I9i\tzɝZ4\w+y1TgZebB3T]E۩Ӷ#Iao$H哜닥GvUnI9,QYF;oZ+muQ%i҆KIy-YleJ?FEo'pRE+mvRWvM9bSl%9O3T?q~s;xlG8Gt4wiv-^CRv z7zU $P4o$~ t\hr=k Yei`|,HݮZw#jDam$=*KSݢrn|%0ޣW:jhŦւꛑ1e wc:HJ1F~zN P?*%,1ظFӰiM@U6)c~ө},j)$8MW+&M⶙v6-)@VNi#t&9(i)>H[wDO*NSB)JiՀy k ZQٗظ%jY޲r yk81G7+Se;J3t:#RpFԖM9iq;u}JJ#ǎEx8,6HH?IpA!s-KmGK+)J2@'N|úpz4P @(,|_J.i} r]uk޸2֥4hQ-$$-;NPTd΢'洎7iG$ǷC[GzE` 895qvv-{BLP6G_)܂tRP8)/(A6M,nb|t-6w9r@r;u&ᶽQ: 8VuUc!'苂Z{JhS:~fbxiIq/3!*;|OpZ;m1x@bKU44z22jWhrRVG!;xÎkWnauǺr+LS:pC8Q%9|%EA]E)VcBB[^lBINs@Q/`J լ+*hF4ҕB{**VpcW҇Pxz5o.ZJ% e.l@sE_$q2rmзttթ2ON[ۉ uXH'vyvxޭE}mk]#̱iKIIO)Z J `ki;+].+D+uQu)vA)C=D {^պjy=-~XLU(,ˎc*#.' P kdX{5p*IW'KIRQHNy6I攊OSZ}lnnVtLGe i Zr瓜-i}'uDk("UL]m@ra v'v|"L"[jR 1 a0R20Ț";kr4άq-!*+y>I\PSC`"b%%P.8Ut[ @5.,pcGp#pwݷG=_ kn!_V/%EOd=pQ9~g+~ Z|*jwOth^} .;ݿOw:q1MotiTkjctmC$샜7^ä}ޖWL>5Jv߆|,<0а4i&JRqgpk ;>;:N]/ϋi=b] i3; f𥸠0o'?\ my88Ʒϵ>^xxm;]XO蛭c 4 O>%8:?8X0r6niq RT E\iL_x4իuykٵJv s\ÖY=OW3x8wᯅY <y;P @(5nVطš$Ey;\icy|YEIeX Ԗ̌"ˣyI\V-Jp2N9^_!|gHq<{O/8P @(+G5Gީ_UyE h v{@mP @(P @( ~dGP @(P @(P @(!+P b3@~!02F(TP @(P @l۾>q@l/{2V.QK\~ү8"x} ? 2%_ԇ_({g ň8]Gw9TZ*\lGqb0'&m)eTh686p&fDEtVi~Pryg\#:Z]&hs$+g;[:N1Y(DJD9-q|چ+%z[3y{KB:A픠{*ڣ%m.kEp5h+]R$h4 7*h5+κiCm ms!(|Ia.4ݚd.B ඕG*T"k3p!_F{lkEPCnJ% cWzcw{2a)}ݏ)Js3h։Un\[2@褻I{4&i)gtcSQ(Ӡu [n*4oSZU:RWX6 s þ/88K4.THOJoHVÚq~F+T_qRY@[{qA׫06\鞅w[KmMrhn."rQ]1GOB;SAT%=B;*l->\K9Hmd!^謥;UgbÊwGo= j@pIiiuCYhm7pjpV$mys377I޷yC1Ԏvqj%HxmHVFihRR&k u (ҢbS.JrSRZY&/I0WLGe?di8qrۜ-5]|̔] RlGj&M}ܯ_F Hc2oXZyI|"Ye$M 8C J) ٱxmwbzDԕs#XW8k-x'$4fy16tAB~Qs]NXrq2*oڳ8/2ZN8ݷVx';fjI iHSk.(y^Ac9K e㊦FۡFU^lR2R@'to2r.kQK}O"EDa'&4sr"ėe7g+▴ О5uV.,p[nE:sRBp}gu3C jPN)5T塲m^IOF- *o*R}XSWVb7U}r7;*Y}5X2jkSK[œhfs%\ 9'eM1j5f=2Ymd'iF{ǦHq8%:1P-G1Zpp\X|lcqcaW69=H̓v{J)850SMc,Vlas S#)H%R6m֘ybr^ZQ[@A 8 kAD]|Su٧噕X-m8;R3pU@o]?ii{!:Kb2Su&לJ Cy ۠E HtT T@CqN;KghݮRy:[-$=r`k3xOUސiDhِ9Ɋ7a7wwdG,8:)DAg Z69*[,kV Y},mDe:P%dY9 NXZIߤ"$yKBZ!9,HJVQ<ҒR{D\8=мFҋڒŸ>[;8W%9 r&wY5Uh떑i?. idd!`Gr;DCF[n7Qr)Kqqõ/-M'iG$ ;?ߋ@_/6qDG֦7ւ +$m)"k _^Di-y6ۏ͝d3=E [y;HЀ3̨>Y֜Vvm+ "BFJq;yYPMnedYu$" 1 =/LO )*;NFI{e~˝ϧV3iTKK+Fql(PDd>W:"~._U@]!O0p@*ػj$o]pUEƆJtQHQ $u#DpS]ER`*1Z``dd\H#UHNWNi8s#jN2A$+s:b/ qImEgPiewҗꐅ)j RZ s=)8!981]\,KѺgoַKjyͩLAG#@8Rs@` h+J#BMÊǞlnP:+jNA>X}>ҪԲhi7lIl1kmy2@Nh{Uh:[.v Zsj_$(!G&xSV;.tqZNBv8{8'eoS+￑+j>d)R=F?7}S8j\::'ต,Vg'jMQ3O³68:8Ny`(=ʯ7ܺG\.ᣊSQ䯻Ʈ<%5=ok as*حN9yrS..O.O|| VO]c]=Q) ʊh2Qsǘש L;qz8Q|^.Qߛc{OϮ1q&>ͨ[6&J;,IC>о+Rz읪Ϟ<qRTˑ.Y]8ᷝ:?G~6;W5w@q׮+̳],U(hӽ)JzHX@| IxmjXp%ͪ( Bw{*Ogصj:> ysevadᑞQɾHtc njuͷSػ0a$ $(O#4z+do*܋ /JjY:n. z "$r)SNa *^˿exB-;R_/ج7-;`8}0meAhB')ZTp*e˷ۧ wYt\dXu5䘆{ v0p_JRK}"T㳹$g#{E޷s'u 6Z[m#*ZS5l9(izet|>'{C (ӣc\?ڇ_PGO?K¸T6lNp/jm~C=>U/c ^=P:>z|^>Xzet|T|+{PCk>'{Wb?|OR1C (ӣc\?ڇ_PGO?K¸T6lNp/jm~C=>U/c ^=P:>z|^>Xzet|T|+{PCk>'{Wb?|OR1EOi.0Y)Ju8hYJB4A$ssiLM5hj 6N͈TYI[-eI(I qsA%c1>gA@gA@@حԺ+ؗr-N\ H~\\:Rda,Fң>Rk 5eg+W-N?z̫2 C {j8<,~]NIK2V"8ᥕs&Xsz#/jv%KVu/) hp # h:s_wO]ĵS1aϳIޥ6<BwatNpewrk/iYnZ%m4!I ) ev|4(l{ӷ)R=k}XcD,,6][ £ssF/%6+I4Kq})XР^YeGZ3Ŧ'^(N.ˑ!! A6PHH8ut:HMV4SL:F>A^fq!2N\nXyR;#͟%'&KоURlq%)HXCdMab'Q4h.;8"O#i]elZmR@z܋d bdwqmÄΓ2[1!HK$rRt&m`]!ulj)J3e*tLuGP2Nk5nv6ڑN!ׯH w5әr.K`+qHyu"ܧ-"׬y(?¿x7O*g(L&W8ʠ4 \̀b:0x<_x-UR6R݃FJϮt)Р;u\?y>J ?{//S@XP >]g΅ \ J%eJBS1P)oslC{MO.{7!8R . ,v $u~L n2*p+\H>qg%Y()x_ŻEѽ `rU-Qڞ# p@`mpPjԒ4rU<']8Jwc';@Z|&M]ەf#];)\[Qrb@SIZBRg$wwdxut%JF^ӖF=o!^2j6![p3^R7Q cUU賨#[P1\9cQdmqT@AV^,h❞Pߵ%j}Ub2BѝУN1@v/xA=;Rۭt);n\;-%**Ā7)%8&0G o:Xz{k<+CXTI!ee[ђUҷ)D>ZiҺwznzue]ܸ糹HI*>  x1wNi5K5#QIu/LZ6Ҧ$mHO2| * 'p;-QMƐJc< I N:0qMɡtݥR4s0#ꄂX@pjjxbu7=%PDd[Aʛܡ$dJ3jp^:< ^qHS,5*q*Ǹ$uOܸNBܟbyJBB/c$ /y '8oEG7YEw;юD!o 'Vb;ŵèK fM‡&NC*$= TM#|NҖ]cjLU599q.,INԗ%ą-VI+PEڬ7N:~Zby)+P Pw$`gF;ZU\'+Ikl# $$n>h.MGPZ3q D%RVHV݄5${'5* Wצ%-Q,#tddg ,z'z^36y,_%'͖vV6 4z4g4bqym+h8 qӚN+T=#QA"|e["on A$H!&M\"zF酢jv٩&C2ZRKAs= l!/lKr51z$%[˟?/!7 LݧORO %}ڝ\,֍mJ\l(!ჴ*8<͇|J<1{6ӷ};|oFDŽ7֤M*RS ϼcpc$&}OX tmhط;U)ۊЕ6))JJz4 $+1y [ތ. n=hҵ|峆>q :ŋgڞ%Tz@ F˴ 8/.Z2>H^5;ɓהʏ A`' Nqw\ pq|7BQ}?-Shݍӟ(8W"F{N@*J"y}^o҆;Ѽ'I 3Tڮ>tׄ Z-BHqHJ97mݓz+ƒE$5o]ڷ[;@9zŸP T}P~_T_ЦjG4P @(MߑMq{@(nZne-#ОSG q .qTsǵKEx7qe]'oEkk)tpҤBK!R/$ gWsk\RUyr;7wu^(toO=m}7g%u;ll~2 8Ndbw'Wsm7׵f=y/h[HK˽Hn$K:[^ZRw#CiGd' ʦ2py5ћY{NqBjIBl_nwhb;6&9Sc]p7Ύ)P^)SGohM,W7M%LVMuMF%TEQ3K 8&UzBki5%v}ռ<;7V(FNͤ#XӠJs:H s$[JjNOJZ%cow5W)rBCBO%WGcpDxRU3/pe+PG{ O_[3oGO gd4wd'ѝt3SgҏFvK?G{ OOKJ??}/hM3??5?}-( gd4wd'ѝt3SgҏFvK?G{ OOKJ??}/hM3??5?}-( gd4wd'ѝt3SgҏFvK?G{ OOKJ??}/hM3??5?}-( gd4wd'ѝt3SgҏFvK?G{ OOKJ??}/hM??5?}-( gdwʲDa20ʏkj@H^Pш0 ¥iLOk %dyF;Ȫ9Wke74Ml{S@?k[/=9 O%BҐVV8思}r}{^SPd]t}g_q7D~eJP:*ӭ )g}Eu3k Uu<#}iu:eTY,!]س^Wn\~Q䥱il8Qz6l-w-\KYu<^O$$mΆ蕗[rET[Ŵ;jsqh;ޥwzi&ZmyJCR0Bڞa qMQ9!3y3ϡ1D{Gn*Ƒv:Зƚng4r =[O5ȩ6\H^sϟ*]'θJeIɚa.;Z +ÏZ+}o_IaaJUS~~vFN5rZRJ\5Txgxg-TN3"0Qt8$@f#,g,^n=vEҮGp[z&buۡ6ۋc+:3^5ie&cBiWa5:焛٤Ҵ[2]$ Vq$EerW5):KBqn~D:^[Bb4? 'i/Z^C2 ob:l@. \yIo'W=2tr"nX*s _u"z2"JZE[͒ rBDtxzzÌ){$k\UtnIHeU3SF9>5p.y&c&E[w%n#j899tj8?Ӡ9T֕u2”3#yo9 [ B#XY5dto,~K-DQO{u]P[|cH7^㱒lA*KS/Ғǫe'ܩqINw)KaJ'Č.f.qZ4HivYx"p1sĞcӑmk G !ЭNc>~_V:W^;'RB=8$뙤 8P997THZJZq$fVkl";8B[k. x\.t7<'Rzw\/k-FԦZL!CxQZF}1fb<|ٱ_6ظ@O# Ybާ;\〪JKy;FѼre䅡}޼dR,_'/tR{;9)IIQ#lL&MP,}ڻZU2&qzCFZI[JskVsFODz7ϕѾvs8PJZvE[Ra-5זɚKM2] -Kiz \PXFk&n6I JxHu+9SWܬpܵFj`kSJ⹅(r5|c[hcr1VMjفbiLWarwZ뼆ئr< ФgZq `yE6QҜ+FW/D޳'x*7SZp!Om_.59`qi,[%K Q-h!A;Ikt˧BTCkiJrv?)*h44He^a[Ref3s^r+O}Ww/}_*{BP @Pl7m 4.\vyȃ>T@^jaq ZuƤEjs,KRam*p@W8jRBÏK:@(!˪$ܣڮ,z- \?\7YJS.T#oMFH'呂g<:sA)\Gu}\&#$څ(@E}ß4r@ל-qEٵ[%hE@FqR@ZI:wzˤU[VP& n@N@)'7o/+OλKtHD<ۍ*q3Ĥ)$9rNj62c0!씶B׹da#8_;-@a3%AhGD ,+h;A n miBvFiw5#6&|YC\ؒ% $J$a /0^b昙;TD_JZB$g~@rt2^/KiiRB6'<=߂H+Hq7[ٱfbXbqLdEnSl,wVBN;ᮨ]WPӬO"vQ*Cm(u-u]ͬSJd6 i$JԄ+j@P2E3sjU sO[*w)z9;4L'Uq"F=ّ$-68$+$"V~o9vNHLTGK;$$0w-{ܽ^8wJ~xn1TH RpFr \Sj!;5R_3v^/ېeAe6<0gvP\!۴+Z_OBVeqm *ƐF@'9H玺XY.*wەPۙe:i={9ɸ_ MhfwӧsQ RR *A$(NU.ꦎ@6#{p.+% CHi+Wl)v*\ZHkK&-& ެ۫Y JvN{j<Ӻ~H.Zީw tց{^9SVmJR{ '`dPM:GkyRmEqM')VQy(0=Qlf|i܀yp,L}d=q8Eĩ+j{߷\8'CLF5% Q,@Wg{9Oi,R9p5\n?jrяԺ\wP+⸘ˇvծ}5d~z{@~bvS %  .u: ϓP#TЪpz4?R=ɠ6P @(jox?2h #h j@(P @(P @(P @( 0Ӳvd<4(t *)*>=P @( w'( 5fJ ?I{zUg~Oy&P?dq;[ڐl'p;2jj@%t{,x4W$}Gdmh+"!#qQ0J^jI;"Ifv[*^d3hƶ$ޚf(䴅͊M/ZPoLjFm2YPMk)|ȌH#m=MGR;uLu8ԒgV.d- 4\֩L(K8NF*HrIO!O8OiG"MjAsFusHr kŧLmjdѴ\:vBr+F;ܫe (#WqԈYiKx#.>yzu8=D4l!0SrIF77Gm]eA* Ek*0ănm]SFQIU+lj0LT4/;3w01IgsIN`SO-ks+ʁTr)-:{GaO$yO1\ihtͶ!ZH% z`-YkbSnL,ځY 3g>5m+m9XI\%IJVdظ+CNV ZfJ$bK}Nwcw37=3R&d:lIIH^}ZQrޅTU2X8]hT5#jR%dd! [ 9>QlWbh"yfcI՝>O(t+-Z_GL6XZ  $c>RBW3~+(R6-I>O^JMUEFC&#ɈVT {b*"lu0K!C𚦈ۃɩJI%\sO)Յ&\ )JO.X&^fIlm*PRTDe(ncj1RGoKeB +Օr]R6&D t.$zA Х^6K,Bt[\ {V)Kݨn,AkJJ1ʈ$4G;myvSZJ*)pVsdӮZfM giHH>(cO;Ͽ`/[/sZR32-2\VPI۔9?6VݟtIoK$Wb_[,njz4cʹq'H6%9kI/-p\ܼՖ}ͥf*Vvߏ=lf=IkRx:7-xO)Iiչ988=fjHHOid퀓>dS-uOL-zQV}muST6Fj"&鮨ṫ1rQzY#z܎)X~Uj2-vRTv;pdrcI":#T}r]tWD@-ryT%d^cfR,-HO"mZf^G#W,Ue^͉֔6r9+ 1?ȓ]C_ uIC$ih?ur _U?g?CʞРP6ދbSq~ V{*wWe ;u3SBn&#NHȱH[*2Bqrqmy qz~T),tP ?P^[Ӷ+(HS1QTRTq( MQYUOm7TVrfG5swF=P`\Ոq-DZJ[FT@eVm t鋖RXL۩ARZRU'0Xlp̮\`r3}˒]2gzPPP{COZͦUxS=xuJFX8OrmIzFqzէ,+ e[-Ά;%%+ְcP; kжݚaYÏ''iEkQێj$$uCvj-יwŘtHiVJsR{IqKV޵{>}lp!yqqw'T}`]<~!u]ɋ{[@܈QIyM FBpRR|csDh!^V_-h/W oj@p)$ww Φ\1֚Gm *s2mqM$ Rş fibjl&Pԭ{HDs+B𐕒=jr`/ro^.nx~֦g4p_[,Re[A4.КfNpv fMKĤ% JA%daj[k^1mv7U ⣸nGSݠ1y~M`wHB/M1X ٪|̅nj;M.|ufzMxSH䡜 P_OixzЉa=+M.!B ص!d(;N_Wxu:ӧc_~kӠ4%v62(ʼ-M8 1|3})RI Ru+H'i QoL=kSj[A˩I/ T%)# hx ^۴|aQѼC[jRJϯXy( Nci}#̻m[qO}4v V O2ybZ|]YHIM-o!Hm;ʲ1?g#[)8R(Gr${j/@M>mSV6:o+)@vTs7 ^j'x+-M:R\0ʲǵ[t%]L@]<'lu-'90gqͩZv.'@\ŦW[m݋ZVF' k8@%+tp!~kV˥)-r휕 rpq1kF8,n erw}R[u"6u7y8յ+oa?T'j$wyF1^&>2M󣣁 =)Z˚w^k]հ4K٬s𒧔A^xXY!^'7\O]ʾ=Z8Z]a-q;؃ 9Ϙ?*yϯEa8ڬ˻$ q^ųq3^Mc21qn(%N͜B{$r Oy<5VFqKX7t^|~Ɔ.}zJd ,'((݁0\~XJyQ|ce2vowYvv9g5Oke|~3~d V-įiQT='H x1X+?P<~F8J?wߙ{g€ǁc\jXˋ.ֈ$SJII v<Ŗv}_|:k/첈H BR uJғr՞J@(>|O\n|b',L5)n|J\'+ݥזŖ$P @(\?y>J ?{//S@Xs#̚jP @(P @V&иr=֠P @(*z@\Y53h>[<7\l-(qi%9J@RGWHudYi9"}Z_n,'gLmJyKm4TJRi+iG3ؘ&)ouh:KhRxgJ[i [Ku`sZVT|Ŏj-d)QwPR!& u: R_iۙJ !G+ Y3uRtliAH HQvLzD糽 y/%gJtʵ:9"g.rRԀ޷%C.%v*;4oWSԽc"zG{gEԽc"z^=/lXHz}_@=K>?/kRϵ{gEԽc"z^=/lXHz}_@=K>?/ki @}WBHR֭J@IxL7KM[8~3f^׮7ާїǕzK>=Cg\A'j}#":7rR٬2Ot5ލq?Q4SkWL0-p)!*}:@~xkkp07&I۾>qX/}} jj̕˼?BL~W w!U"ѽA)ϓ.Vs,#c4Gm u%R>j1יس=עC8UNy%!m6PpazjjLIO|;zIp挖e3#DJBN䮙a#>tkn<KyZy%i#'Mee֚rg1$\NFkgn4BړkIIV Qb-ܐK;TsEe$ l&#O؝5Q̰vCj#4w8Aj|5VԕU3**2u<)E yU=ZqՑ'Ms:H>K~QX'eҏ7@p}Ğ^']mGݑ XszHlħpըNyvVMH:L$-4(m? g*bU.Ns>~b~+4f-E*&lpULr#,ږkqnZxos\I4ѭ6Dk8RŔ5Қ+kE^US4*E}kfاi+Pp9ʋefOimZp*˪fn6`Ȭ<J'fKr:I0,ne+)0&@C+qͣ)+H"9nվDWe~zn2әhVDm3&AC$rVGCER}o͊QW}q7[/\ vR\\Pj+ˬ74ﺅv yJ/fM͌w+"h%Rd!p6Rk1UvW8-P9V[TKD|pr:asXRsAF~PYiO4XBFS&a 1eʄm$k#ɜ|5YI"! E5ӋJځݷTqV%=9ʢ9I,tK3͂p-B}b\DLyzcek'eZ/ܪoBZVzvder~;JlX7K*$߳j'm$KoR2$g+hZqRЧ yJYʍ"H!-$Q?UQ[k ƚTFJ9+I掑DG 22*7MKqJ&o;N="1ď3UvnDPFMre"=y3jZO}W$]V팿:%a ]% 0 a$#/>se--h'kbZ3Nês(arHVM%b*͹p#LCd~IajuFsϹUSmGEF#$ГQqim>uvܹtpJH r|e$ʙ.nܕ:{A=`@^,Qi%;X<1+T5%6HRS( a) @}ş 1-]H[wt)jt Oi8q!ʷFN)SI qO#$7zm+9:^zaTuzmhRऒS[v@"h)*v:e7pZ^uJc+-:CxdnYXx%W I:= )m)w)h*w qT-p9tv("$ )"CESZSˍw`ZRa0\ޥm2W5gh +H9[qve˖=lO:Uv`*IK´μyx́wI! JB0NNh4汴 g - I@3;( _uVV8kQF'IX$sA@󄱤q$ %ڭDŽ̞Ut/=!Q!UYH4IfjmƟf .!m0Rm`J{}ajhK}VMt6'*l`r9dWL&%8|.417.[UMKΚ)R 9f;tONpՁ[많Qֈa 22ޖZx3j1os\g&-DocW (so Ù* O$9 W 2Wq8|'\n=:w$pQŴƸZne.-+V;8P,=^WϿoG0gW[߶:7 l!Z&ʔB 8lI5<,5|L}! RkoqP @(P @`5Tv -94FĞ͇&v`@ IP T}P~_T_ЦjG4P @(MߑMq{@(P @|S{ kN5F6 $lJSz0"YmEhqV$f5s75/5~?iHњKJ\v4^V%PPrC ڔHޞYyq'ť*V;I'ߥa%ok^mOSj֚r}|p\a6+ZB<,+6(Eg˵軗>3uoǗu/MbhuZ7M{j:&zrtqxniXmpv}|Ⰾtx9ҹ Ij.[i㖫-߿rW+K7~p6>qNzݾ& (326[.˶SyhpCqCJdpNo qY[z;bɩb|/׏;'ղ,Me'jY.H!%·taֺUi]W \9^&{moiNo[mRoJMіM3yhIsnݑՓol4d[%並TP[x4ըkR{^fI֕g;\oO_+ѳ\tސ7ol*]7gT R,/|(ӭK3PP @(P @4w:Ef"406B!$(U$ꞓqF2Uy/Lϻ?z;ρT7Y\s3#l[/!nIMKQvڇ6!F=wGPUNccYMZvgn>7HҖU'\^2_;?xOb~:5fJ ?I{zUg~Oy&P?dq;[ڐl-p= 3kIZT$e#C#^ t>K#!C/$w< -FJRyt,2ņb+ӹ(;ks6S qc!P J1]R{#rUݻ(žbFE؂rK)_uc4hN3Ecl\ADtj=qOK=&ԗ-ոW!|X EO ݻg"c9a[XdACKJPy.n\֤g}Qs t2ѭxmi 2!4XbIѪʑB%[NŸXǓzpӜ];+V)OI m #ӓʸR:U6mK^ԐvCcإW|i%)W4mV3kJɼi'd5gқK͐PA8 nkVFs]Zܗmɱ # kf*z!.70 z/.bJꨏBf)VRIx+%ĕYRzì\)::l)as!XjU32KeLGʹds N|.MJSp +5J B:.0zkU%:"KP*qr9?v(3)j1奠\@ڂ|8žЧHK9a+8+XR y.iYZIQ1/iK(, GDf?w=)r'04YlE\ΚCW+HaǪc).!D+yO(u*9徥ۢI⥼A^6##>ZhSZn".dFD>ZYYf/-7pخУpN)LE#8Js5h=bN R!1WDRmVT96OFz7Rz=5q"|6&%/k;Д<}oy&.'4ہ 搽>wWcM^ř.Ah쐍΍ xM{t}.΍_pU`ҕМ^Z'r& ]laeU4H6fpX) sP^܍mUFn9PAUŷ]/+ef?6Z'KԴ>S"3Xi'ÑMJÔw)2z KR\By5(6\i,O$7*t4E?,]. ZޑRҊMƪY@.R`R1x΅rb슣 ^)txPEiކke%N)\g&hhmɳfB23,dK-ZU(-#d|n#1xn|ۆ_4)Is,rckr&8|ԉ>Rr|iq.IW2ؔWy(?¿x7O*g(P T}P~_T_Ц @( Eb+6wb"0BJًRO4Ǭ(YǝS?T B6~%3JfuL5AG{jԅ䐑K?-\4npu9њCeA7BA @V4o.1n69uպQ9 />Cc5Ѷ;6H5 ޸5XM}l5Q2ҡ:JaII)<u^h?%h){}#oJn%Ɣ0VzMc6Hޖ{]j&g^^C͵wa0I^Cx@O=H"㖛i ܚ5-Dr-3+KoV6s=bڴ.X|\X-%]#q&g=E%O2JF@mAǜ fЖN kG:>h9Y6[h}-rJ;>/ ,V]͸Y J_zyKQzTI BH{8Jɦ.Ԗ[U黬]=^.K%>rd qu|6j-)bKn!ZVSw~IvFsԑ89e>Ψn]mz)mNnw%Mw9v ]+mjէ V\KHˊl':[<-ڟBx_i-GOjchP\)?;' r3F'l--aDž"̭x<9hQ#iGe][6-; j/-oq*i*Q! JpR9J―MhӚzgHL]n BdMBOa!)A $mQ)ȨwZC[/iњD/C)[+#8BIX=$*iiBcmx$2g=|,MGKdD5lqn Œ'+6C IpUebEn Jm<R@mq>GAKi[@nbg.TZ[PԩK p0| 7 v+5[˰֢ 6tJҴj_ГN2i1zhA5/|x7堙%M2ߐ*9Rwj>Z898nEQUMt_Njmzoş`۠kņ;IeNԀ~_ϸĖ,m *hH۾>qY6^=+sx(%e?_YW<>@AymjC=[fJB$cnp:)Ѧ\\fy$t ei=HKma@ʺhٻuy6֗]P(8Z'(w#5hz_PAIyVY(iiV{4>E|%j氞I_0S@M~-:U @-dw58y%r HQq.Ktj3K*4MҌG(ғYIN4HpKXT 9>enrºTjG*OTKq{2&r2-伎kWWl!b&i%8r:NsӒnL)|5Wur 8'ӏ)xfjm4ѽSl%I' 3I2prTC߮H1|ԦKJ' *Tit{;Wld1sm5 㤟atSQ̬qG i']|Ved֛ŗP$6RD)HP>^UK~I75c:-LTН|ʣ[b쇐Zm;5UԢ25Ho ޕ')S{N1KmMma.Kl{<ƙYZ6oZDȪ"E d rI$"w-ʼ Q!JlvmJPD|Խ km}n u(r8HoaF*(`ioIѺKQ&1.ܫԦgF Ck+ш.F#6 vң5)lh 2:Bv) #xv,$ݲvѯchadB0&sVN՘N(2$.M.\Q^jU26RVN;uWEmZF͹sΏlfbЉiȹ#m@ "Kζ`8劥"!ܷ9`5#-)# 7VSVrdP@q( )"YWj4]̋2 JGvႚi$y(q*Hq'IL &ͬ,HBcbIF*LŋQFkUoFa)dV;'-*~0B8c( JmV( riMKKRtoD1LR%9=դ"S LLBPDMIIvBq@5Tg]#ٴJ]Bw[ʭ5tFFґʐ 8?%k\u!3; K'ܫEE6EW>^}wl}VfDq%j@ܒp5xJf%3'ǎMm)V)($r^r+O}Ww/}_*{BP @(+G5Gީ_UyE h 2Kp![SuÄ d@|m~f/3j 6H2y ^RTIJ"@Ӽi)oF'W^P->o\[samv6䐒>mj-mǚ?<8fo 8hMʱfENLow P*$<h ƺ0nj uwФ_n4RYVRw da]&`w笑 Z{JqY47!'>^ѭC=kNDy6ےc@ X߅SsSi6i+貛-[YإJdm=nH<0-^?u%xũM^JVͧ+ Pj~V4m Nv2^¨ QgIs')iiPջNŨQ|.0m( rr0Zd~՚vd yIRv)O"s ^&k?ot䫼XMtBuͫq9 m"I@BZjڋQXhiqxTFRr- ,C\&q@Vg˟C̍_za}#IY ߜ=gXjFEr /: H@Zp9g nnOx\aI7~ͼkЧO_biP~$|{[{/^Bqˠ9{_g:ZA%,/ *JI*>_KUUqظQrm%7{8i5fZno\^1+j@9F k,Ww 2-ߎ/D{,Ѻ D>zpa'H#5,wlhɝ)t]*U,(2v*>*qs,7r%a'^GVC$x0Zk*Z-;)*1i;?;5NTݿ'nH$s c<#ga+˳WˑJ;ȯ?G>f׈nYS+)93@1#>qOeo -|Qizߙ{g€P @(P @(P T}P~_T_ЦjG4P @(MߑMq{@(P )+8H$ IAh@cHm%D@mgFG #h p1@EӥnY:<۔b8YqNn@0yFUhm#>yU.&앭@f{+|)ޒe89僞-uCbHX,p(АbtCdvc o}gl?gh~''QOOѠa<@=Fy??Fz~2~d64l?gh~''QOOѠa<@=Fy??Fz~2~d64Ѱ@|jv̍R(!R|uN7 bba%?]7xPŶYcٹq'Y˰nsܸÑdGgd)<5Li-kϦOpƞt~4/BO(!xX|^9FkF{^SPd]t}g_q7D~eJPu$ZP>B\8IV[|r؃ \qYF:ě> Y!If:k2wE%ypGY$5]m9dv(VrhUdLIˍQ5 ;k0ڔ#xjU?d.mzh H[4UOIaTw *R(kmN,㴕 aVjdBKg)4،ͽK q6B;*OTi4{+qdՁI ^n\C, ꧩ6 $!՛J@9k8Ze+{.PZ1hF@Q93MĂڣ \zD82VVpcp ISEm"Frnð䮦8*Mc .ti'M5M0%\I q r5hKB҆]Q#1$XQs kBpږNZuLBDZ+J>眲F[m-2nuFⓐ>jxs7pXn/r}{YAIBYGrWkaC,䤛\-RTl״Vσx/+TihY(mRN4dԬ%ȏ7X[2YqEJhx՚uL%\BV<)3E x3!R32''{TQR!3\Sm (VS&;& ڐVVI@vc*G!*J{.<;yrMBXZ=~WIqX|,t}˛NX-̪pԛ}o#76IuW n!)pฉ\& pPiI--֛ڒmBm _I˸WhͰԦN4Rȅr#k(4tMJVjzw*K=ZhM[a ТIB=YEqn{m)qeHj9&h){(FPȏ'+'9U%&/YqJfv!ff ;mu+U_mJи;OzO ٦RȒ Hū#յƌ<8RҬTR3PKveթڒ1j/ EqGIDz.v'kOap99Gy+ћv0D>\W_,SXxG|=K)(ߩmPQZkSolרq _U?g?CʞРP @( {QWU@8qiQB@(5.5 *TvZa q!$#<&֖]+j[mr:8#j<84qgcq"3c"%ƙman!=9ؕn_v@ݗ.3—xm5,hSqiJUT쁹8eڶ9`Xz9ԙ.7VRW.rM!NCݞXǟ2h ֜8 tdžc~Shz^ܩ%8JUv:w~#ԡSJi ={j~'RŗQm1goH2#EQǺ{պGtg2@%@+N6*柹Mڊ~[u]LT㔗 qۄk2t^7+Jk}TZңԗdȄ0h O858۳r(-Ic9ՎYs96H<>g,2Kvcm{%]q{P럪mگN"X\u-ԶB0Fz뼁4 2{ѲX-jhŶC Rxl‰;RB {c7T|0qUTcKŎ47My kH.pX]o PRB'pGwSXsבxE>77ziu~_Oqn[# y2TڎF~C|0T>98n=qKrHXǃ(:GVK]'(Fp2Iʱߓ]^!cz=?I Uu76Wc8s҄y \bJT F9^.:|2\nt{5!8)<^ʗ.%$$#NXɩ-. RWf~7^jAyDuZϗg9^ Y[S֚&մghTy YK!GΠNyh\Q9'%BXriu84%?h@9lx[(Np;Iyq+[>:](7XP @(P @( {QWU@8qiQB@Cݩ~dTP @(P 7~G44P @(d؇׫[7z>v9ݜsjfma߫]>r9|/Zp!=,0]\mp:jہ(S%`$UےrӶu\u|mW]ן";\92'tuQ_aKz%(16ZIuva';e^+~.˟zҽRUzHpcr.vM:TVtYl]ˊGakIi^V߻c$׷_vd5h"DW:tM>#q:>=%?ng4\Έ%,jP!K!ĸU[Z$–euO׍u_{]>Ӳte/DH3n.OKJ[PFs5NQ[yrir CZQQF.~ԨwO$_SRFR֤K6 QSQ9J~ {ު)/Rӿ^[eWIKO+PXx~4M1>tLF$/e}] %,\Wk5{⽹ʴ\K'Nj=)w]_ej<5%V'Mo KʖK2-,% H X#iIv9-wѳБ@(P @(opQj 1o~6.JmTOY'Dkm5G\/ a|3lo6t:t8.fuƹ5v i(0glrv_gwG7Tn\G]YoWu[x`>'f]e|FC&JٕhLx\^2_;?5}r}Neﯡ{MCْ9w}^_~ߡ_^I*N:C?\GPBpĸjE]ۭjRе~撩k߇,WtT[BG1i4Mo\GnXgZ~YC4.$ʞԫ6һ*j2v%#;*sIuBD` !mq'(h:gq.,i>*N b3^5ae@Jݚ.WSSoXi[%- [`C-|}/l{8DM7<Ȃh21-#&yU't]S]w>h@".Z |՜Tnӑ\vy ěnuԔb\[QbzOt~e|RD.sκeRk7Id*Vk좄\[RFڲp}禩%Yeml5)u-I#mKRY=Q",=k\7({αf|kM\(x*e4QȐOHAV2ks<۵Jm,.$)I݊S}[AK}Ap21j={:H!EG9RsֲÞ6ev"Os b}m(3jT:GhzjiE\T))u;STyR4Yf-M(8H%Oѻܙz,m{9hrj5Yt(2 }rp |} ٯ K])';`M޳ŗi]uY8)pBĽyީ >[uŮbV6-I9jٌ+HP*Q dma_OS8ќ^W]]ɨ0Z#HP)q"?j—G𘱔zMw4_1Nzm(RK㣓` QW775K|+Æ]5)6*Iv"2>$%0{U>cb$bE*DjT}yyOL$R2(gUV^4ɰ-u* PQf;Ēt&p;ZzU4fjΘܒ%, 3ZxǎG@;jWKΔA鷀3Ɋ uâCv!vy9wt?=y')j=)0pa'ʒ]٩X1Kt ޿Xy ?4xNՙ#]jmfaV:5~P): $fB.L[*IXɿsu,tzg(Y/+hZtk=&Ssv[ZOh+\:js47,j hZJ{-SlCR!YyS)>GAG|Pec9 qDW&QejKN6g[{F8Jvoo^&ːDy6nRYhl_Bu޲P~ E"}Jqow/ς6dǶSw2||򓤋J)j[~7|>iDTvΏ2O[jeq̣Z#_bk꿛lGzSP @(\?y>J ?{//S@Xm7kųӮ7nHZaq ' $SA6(˵L' XOy`@Qg\1?+G~jyB{RRwvV{;vݴo[jȐ2㭻-@F0̀ZnVklk%%$K2D= Ow-G ZJ%]5٩)ms&SKN0@W0sM\_lNV7`dk=mz*m7^ 噊u<ԠJRfҜ4 Ens|[onXe}r`ϙ9W 8{r-5pˋDJ؈YRw=z* >|9i4Ɩ}ZǛC[H %`-y4n_Kq̵ܜK4d'q=J{*}=cTjet}I)mVV^SuFn"F:ieChJH[ 'vws$H쐪ɮ՜9gIpZKm HNްHh /ncm[;i-BB˩RH E*NwbkR$~\/FUkkf,5mIFғBR`{O/58uoS[W!i",0EtR1QjDK ii} ѻ9w%9yQ(V~g78O,uŧ-j*Z +rc5 8᪊/|y2J9+ z'Yi7/U(Jެ!<8qxx%H?IxXq5z:Ӛ6#+'=ۏ9] Y/SWGRJy;~ʾᵋL0)xrpr# c.11p)sz=vkPv8x|N+Xخ7j8P @(P @( {QWU@8qiQB@Cݩ~dTP @(P 7~G44P @(HZ9JOP)[Wt>jI>S1IdID81@Y솒MKvhJӟ.c ߫h\?OҠp?Jz*!h&DR鹗xȈ4w miZ JAȨvMD`cX$Zm E*PH +Vmʤğh\?OҨ$z*Sh\?OҠp?Jz*Sh\?OҠp?Jz*Sh\?OҠp?Jz*kdcp}*dg4ۦBad3>(R+ƞ3Uv~I/a)n`hl/{2V.QK\~ү8"x} ? 2%_ԇ_({g;|:]~CBW/9wXִC;2f֠.u):mѶl=IdӁR=e3с]9<;U%=Y--y%Bi%'.wcQ^$f&P+2Iܯ6qSRdKT~75SAʦ3eT[*j=M|)GuVIjTYn0S uOQԘ7eLq*{tjҹ]^Wujv+ad\5, X.dg+aE<%4mU}ʓy^x>k@28y5T{G[鞰R ltL(XSKOxnѺɖV _iӽH^χfZmP%bqꌜ}*ؑ8OVsUJqrUȯΣą$JZ)Ha]ʪHLerNRKq4jNz5 v+⠱s>Oqe((KEk0]"ԙ(VN|'RWB8v3Ti[!GqBɯz1XpP18i8I$]! ODGWjֺ%mݙ >m p)I?Oô\hŮ&=+pJ*ѻ[ %A =ՋHۙ3nB )QrvbVp-πF d'ٹ}93I1E{lRKD`tOx9mRuJb29;$SnU 'sqVeTuaE'h1FFs5I'b&ѿ<46I-Y%b:x4u6[sY=Yw1^7 \ XF5"N<(S-K}i Qpn ϐfʎUm˸=H[QT]\C\\ i>SbF˱1hgUUK+< HajeA=ɧM5lF>)ĬJ|wx$siqwy45D14M^n*-8Zpp+Fq@$U >}ҷ [ZzOݔP M  >lZq&qYo[l|K7Uܷ H9+ $kyq%i.z]ŧ$yJHa@TP'4܎dž|ӭڶW/ߚaXc$pk(l9Dg*8+ʀ1<my" z] (,u+$S;q@Z4FM^3ncGr[yKqmVsݼwܳ@A{ؽ/y}gFձΦosTRa!AM.d1给6|_kWe]/KKPP9x0O-*(Ұ5@ٙo@91.ÅO<(9ɠ;jR@PƯNnHyMxN@ PJ[xMLt՛K:BMbux΀]V1>[o6,:UҥR$dÌtwystt)ku lZRR3nT0Udy@&LN*O;,>x&P1RqRk[i|j*^VrrEF򖗟YiMq'h8$s KOvLm BF<·M|;7!NT+NzJV )2Ź_yGMtl?.jkݲ;Dۏ}TD%8Un Hv9Vs^g&8| pyruww@W~z(<'-6k ^'qJRH'9R@y\G>:"]))u5nga`12ۼڎvG X̎'Ci[`P @(qz~T),t9ڑM@(P @(+Sw{@h\~~s@kP @(P  ci R4iOPiOPiOPiOPiOPiOPiOPi鹱XKo`5P @(6m\x86^=+sx(%e?_YW<>@AymjC=ԤjJ@IXdUVNCY=MKI *8h74[D ښmgj5(e>y誺3;J` [as7-V 4Ac͏-1%3Vjnq 8\/JniuwK*R )*Oy%؊F]yf[*(q (VH-W-IKkkɄ_L&ޙ0R=6s]6ŏbcjiI5Fn4٢qu[AKjGPn9Ř![[Rs5RRҬu h8sJ8GHp!^m +BN_+3i5)iF1߶3M-H4JӈWdVs4/W^-˹h(6pL#mIJӷ?+ J]s~ 95IA=,_v6ʷnzOK8+pqiB @&:#*[oDwZ#9$rѓ-5A^,绘(V)i2d;v9} 0ku(Rj/C !ZWU+7^Y\TI[WUЋQz3+ِۮe^p+e*+*)uAc;f=M)ZͣUlu*hߵyhnli{7Jc ە^1Y)ҸBvrT{59YjZzV .-M8pp3T4R2MYm +aΓi$\RÜꋏ rX$Y+=(MGVY TS@?jRG=]N ] 'd֧T#hq)-^~ZYwFj}8V֦Q--2I["'NqlTQi/HWN {FYlk'7lF1t C9>z"ʊ+{o-]p+ғ4J4ڊ:)s *iV&gL\EvrϹ*8fISfNq%0՚WfnѽhnSZ(N DjLv?g$ss'VQOFs'w:2Сkm̖ĕU뀩*Z3+,oڙDgJVUd+9+xqQ}qdh(#xSpbb+3_"=M#5ͣԮܓ9CBjm 5ֱtewF$]U|Jy01hV [v.uhHNA>t$7&B[烩u$T52W5)\dbB6{)KFU\u7U.+bUJVѽ c%"a)#drZ6hz`g\P5+WD.Ih]RNT\? F&C8BTӱm 8⢔!V_wtXϝ9hnV k*խ;J}ڋ{i]>}3{x);qk9<΋*HͬJnqcWnw[nXX9K&ކb $,R fe]DG$z߇ZBVwpoW8zېKԖĶX&*YJ,ir+O}Ww/}_*{BP @(+G5Gީ_UyE h |DԷ'>ǧ^o0m[IVp{.{'NDF!)<J2q΀W&cwM{wj;*ҥjl$%=0A@p'CV~ڱ/˄ \{t#sByrJQ%S8.w7pi;QN@@ lj+>ל.VTX3IevN BvJwmQ/4S],iGsٚFVJr6q zʒd 6Ǎ_Rv6*Jݒ̙!+I[a"ij•p7m&7ETIJҪ\'9 E:#)r-D޾f>sحRT-I) QJ, 0kӾ5ZZ%|I12&C1q-HCh^! -#)˅W +2RC'zWRHq/7oSzk" Q&8;H#pv< u-/ăg/V[:4-E]1NJn*R[.X@Iˆ)G# ~ ڞ|ԓ' c(3;y|2si֚=e$/ JRiې\ w_u54}]*5^e܍} BXSC+M]/:VƆMmH(8h9"ۉaViN K-̤ FI8#pV{@i i# |i.-ÈmE '![doF|?fշ$tT$=8R8 u1Ë>q|QZ onDU9(P ($# uҧaV/˵(\pv FRFA@MKo-Pޫ ]p D$rNnsȞ`rwRѫ5&OKuջxwr9PS3t",h!Տ挏2hE8y/˗B:rnreMZJЂJqED07+.^@.}K!\x$yD`^8#UӊsFE̽tkh#wry J=-QaK" !鐝,iQd VRPW'H+g?fsmivA烃VWfWpRQ ig-w+vQVuTT:CѬ,Ns tV.TiZ=Sq q˥'2988\z\&ex޺[X}TSʛN}<ߗ/J\TJVo(v*W)N.σ %ḥ_ﶇif#Wc\-,8n.:GGniY32q%UV4x5xQvnpqFCCWsqv=Y>-Ta~ 9>#qvSqp--Aq-) <$I:M.+J-GtM֚[#pψW.1ƸڞZm!YmHH Bqg.\i վ63Ƹޔ>PŒTdoLrqlB WMFO9; ϗ3ʅrXr|Cu}m`Q{ki Q)J{R9s/,ݣ?K=Ύw ]ޫgeY@(Pm/mļl#Ay\c*#ttܰ$t,mZI$OyÎXsq\V/ɒUsP @(P @W8jRBÏK:H&ڠP @(P=ɠ4.?\x?95P @( Sd8!(HMq( PzP[.lْYmn,RO/5VME6F.MEny8֯AsDrv Ka ^K/XXJҮYJnYkV޴f8﮼X<.^lq#Hݻ"m}6[I{=o}ƪ|#jZz6M.ΨjZq-JRԤ';N(µJ_"J*|2Jqs|_yPKCwi,c-z%T Fd;g8ޏũ/:Zi!jKDe5-0:p$!GkLuz7}C; Zdp4 |3 qKO:N%NAIJ@ZIi{l4.n?roXE6]W6mn{bюjǭ}Uo|:^}$WXMn)L\L)MWFlV iEmncZM9r;& xkMruߖR ;YĽ=č%Ĉi-Qf CwԴ3e(%XΜ\ʼTYMh vƿܘaNv୬dg+qմs+B]T\ {zMY R- m9Hsy=lvdr59^o])z]}WxxitP%Y62[^p/keJ(C&|2klmgk_6yPVAKMnU4F̨L^@q)&AuOTҢ}$PP @( w'( 5fJ ?I{zUg~Oy&P?dq;[ڐl"pֲaSgK$Q#HJAD[P;R7o-Yߛ*fT[OikQ+u9Emf4[ɋz.GC%lx k(l֖熖,4H#VBPnˁTguo*جenJ lA'ĕjC{!i;|+Rn MHmvG*3tot7䇟^Tr=5ΥVsRo]2&I%KQj]O k[ *RKeW]I"ӴC Vy[%QGRewiהҚ;i"6›FϫnMYdjfAY>OA#7ItB4"ˌuy䟖# %4ttE@s)58+-NTRyPJ> )]Vfĭ QWp3]i-)6 9Z\J.e%B522RoR9{Ke,Gj󋗵`|IdYTg붗䡥4tjVđ^nꇌV)ݟ/>O)BCUMՒ:7T:z귏! Dȕ-|I6mQil!žQʜ5HNJ=οS3)pq)PiDI40U7tkc<].Dɖ.EuCF,7+B R$ !^ZIW4GQ.glyiN%C=+ʝugq,>e$f0vV\qG[-'-9VPcR9\ur5bn&S5Ď^(ޅt;6&\-)m[{J>vΖɶ+Sf[*X@aʫh-Op"Zd'Uҵ-2vp]C/(834$ނgSl/ik Z5vn \R^H#SnjZ_K"ʲI՝Q*hZ+Py #5VqUD,HJӹȮm\֪Bé#Gdj̉RgZdՀ?j_2"蜶:KY lgOM$vk4H'4J–;0^FzUaD.)ǖ{HaVdƩ`i6thp(I) OyIIXt?=K_9oh+ &<`Du ![ZSH SW}ƴIDUƘv8{&!($cB@OC]@ζ:$VzI5O)&RAuqA@@umvNtbu▢Z= hJCnvSRv8*ZlzqZ]ꕡlѠ$`U%䍍$FI͸àظj+= fU_藰$ R) >׼Wvɩ.v$ta5(.86 qy(A' aYs)Z6֚jg. wZI !AkS C[s;s ·NXz Lǹ]㗡DJI$dw' $6-1xoTm8ʡ[!Ma2g-qMEmUg N(D Œd`߂({Ǎ*:oMjm]ɉ[ol0Bޙ҄pHl(8pA <8 v?iFlw_8Ͱ۲׹R+V=q3F0߯;ƓI2ШZl!>~`$ [j&̈́4 iqQdM/#pdgv;@\ƯnnHyMx܁>A$(NFk.S6pK%ǔ,rX#x/}ע}-O):]k-Nqzd^Hta$`A#Q8 zGa^tpvͦgև,uREh6^JӲSR`PRs y W %6}_Npaִ[yk/-vxsͱ B ^ VHRH*Q돒\&]X>qx';^{\͑qd*PO%y2sߞ}lw0NF]ҝ-jNߒ;U& !n'!AHU<7szip7 <+j}45o=8 Q5I 擂O]X yn1:/]IKYFBr]Q]Mɿ΃wVFGoʖ]|K?SjEj "~npnc:+í62o-jJ9dN ([Mi٥r:V"ⶔeQs5gkӱ%>{rF72!PQCRs>҄Rg&Y(8'}YJVKzwy6fz,]I[,-\#< APlP`Ky^f4-?'OặzίNý[6ҧ<빹Ź<'rZ}ޤqSJP]U>9oMSքď_w=-=Qu7Q"קURzvzymo:a$-ҳ1J. ˵=Zv|%e{[?{\t~Nd2[q4A A0Y/NS'ZEGoTKmiw%{{#E8nX-xbD.ɇho2M:Pˎ!*8Q''vuk̶͛r I3ōKD-7-=f0)q.vॕ$$$#d = ҕwɿj`cI/>u◂5.lw{QEіӷi0 %iũ\2BRVΊ9(/8tҟ.M~W%;^xB .uC"]zpC:P~De,%/%lIrk*$yN{ڛj=Vm <[za0oX:3Zj܇}ɹPF&1DE[.=m-;܇<|*llN[p2Q$Ș]%CX-;JҬ{J8۲r&)ͦ) RQWjO24g@I@w:pV]]f7l[WuJm ʖ qPKorgMKm;{E|{[fH4Tp%dO?ʧ;zUDD'ΦS-$D_[qH:VLܔtd'wuZXӑT l+nŠ=mF8ScK[u !ʎ+Ǘ#jXeІ,)e!gg)*U3CP,ɐHJ@Qq-0ur+;Q݆`ˊi G/O:Vp-;DnWIT6r\<[CdD&̷P_XO9w.ɕqFjbcj>CZ|QM*.PŤv1>=W]=Nk"xy)q( BI$u6۶O{k CiѥX&z*KɰCV֐@ e6x)bEF!:KK\fԔ,+P'MC),RZ4Nʖ H@jW̶T5Lv Mm!C*ṹaVyݨ|Ed2i*c#d5gjMYL?JTeل(2ꗄLE-uP@P[B@ܬct%9VoRK ,yەDSH*宧M,ӌˊ°=uRT\Й[QMCOr(Ie^y8]ďvpM.ңyȼ4c#k3dԖKK`s[BOwRaQgc̥ʊ1R֗Fљm"$}gjM5HQiٶo(~4*#i ~ s]* 8-"s GYS'm=>TI7eTI7vap-2Pϣ5\Za6T]Cjвw=ڣaTIe-9ӶI i*F]qF0X>^{8ǰQ3Ѯ2 s-1Gfة{R婒ih6gF: R4JM[+*[.{ipԞ\$0jwMUћ|i S2=rȩqћfpM-B6VKC>fR( ]yAmzGui)=] 8Gô; ]JPBvcJŠj'9e:,M]9ޔ9r!t)-L!s 1"B4MY)7#zIjj[B[l+9A'ƾW>=@(k>#ش5uvk &-7p1؂—jK!ᾦZBUi͕w{ÏKZ:Ǧ.3gkfڶ9˗:UqT,>eb%*!8w(d:'Ȃ@<+6]4=-C3T5)ôqZe OY{PR9k]ݧ]UdʹW#礶Ԇj IخҒ9; {ҺϤy",0<]-4CMUTw,d(xqUZ42z6a" tݵlʟܳ;Nh ޛfy LyCal{\Ԑlv9ysR3:]LY4۱h->-$ĭIyqN_WqGXsu] ' H : b-.EHZ&[ܠg`°SI,}}?S^_pZbH'TmBBV6(+`9kzN^GoNSmRSe}o .4=Γwa6s,Hv%HF  qG$c5^O4񴮵l2q? u@ ێ$֚F'8V G;fB5*2KAi9T NHAru-L}ھ#\#;䬕[Zw(%b,CӤeXX>4պO4LgB,R( -i7t,oa}*\%-[wP$rr@МAlkR7 1Mhӽ %iE;@8P d@m=_lNV{S(%ʖsP h~|LYК_\5Tu6˭Ie}q9HϮ'56RueխQ qT(Asȁ5p 9ٽQr jZp@;AQ=uކʹ> 䶆$'viH➖+Mn8G)i}A 椎R.-ZX݅oG)6Hy=#5qp= ~l5&I 5|S/T{v}Rb_ÉWlv@h݅#%O|ú/a"w[{-EDFk:fsPQr0*SrZxn$Zۿg5\sthݍ k-𦰉^NX^_%VQRYe.&"u%8`.SlФ\RUBSJ7d!9kO 7}N8qd]>tfACڿ]qEgs%$%HإoNdWq1qn::s>szUHW~hrn?|Dv4TK#rA )11[۵[x_\>%#1Ȏ^C+Ɔf3w!+Z5ۯilP @p ;ƶ ;CmzF#$]@/$i>ь:Ɗ&=;vkUZ6ZwVRD@ 48\_YK$2ٶ/u|^Msmm:KKDԅ *ܥ$݌yUn'Iϒm~#뼍kr.EJ0iWn;0Ϲ}-Q@lo[KMKhJZ#RAU_N~zlZsFb5ĜuQص$Gů'?]@=Tj?-9QkIPU_N~zlZsFb5ĜuQص$Gů'?]@=Tj?-9˺)4M6*4U=N(@k!! H@(@(P feﯡ{MCْ9w}^_~ߡ_^I*N:C?eY Ae;@4H>Q NJKaP*?Vsq#u.m=&n2\i V 3cΦ8zPZɅ>JdzM`"WeR0 sy4]4Pw ZN탺TY6#ni2[R{!XYhd[lYZ= I3%I(֔V;p==Wut1M4-jYcU٤۽YAAr]:r =ޚ(e+WB{m yKHds2 M(I)ZIL`QzjJ4of4x : J~ ^GK͡!Rsґ>^$tȉtm1 QVqJ-V@[V[(j:N6RRԫs[mtyRxxI-JDTeF{vGcJ+2M뾏F螸%֣kq#( T Xtq&/rTϪ{_# k+;=w(?¿x7O*g(P3SuǨ]fٕkS^I\weҲc'껒 'V\Uz\gYn%w(7&<*u7)*(YRrMryE h9K8y 7ejhҲyDI>Or<9qi2Onۊ!e*XZUtPg{ e^G7Ŷը5VCN>PQS}0Z3G#m'< Th6-NeDA9FѭinʛOVr Punɂ>i' Q9J@d9Ȥ=5X:kmp]PKQR-nW!G*v!̲RU,S9q JyOd$PMU4O'黅Z[DTͩR B@J}h1+첢yyļV^1ΑGpW|+DjpQBCR Am)'z6v@*v8uZ׫Af3{J )YPgP,Ԏ鵮deL:BѴ³+pNOp@hYசղ_5 귢mvIuEi$zWirqbkn_ w)Ve#֓@q^k~gڭvRe/u8VKeC `'rh -h)pkRZe6qi1$UxyR֜Nњ&Vqn I~Miw̮8<Ֆ,=dos> -wG-7ps2f;SI$RI5p7x=#]E;Uz'Ŋ@(P @(P T}P~_T_ЦjG4P @(MߑMq{@(P @(P @(P @(P @(ͻ_Bښ%nr=*3B'п(U8-HukcRDqCpNB A‹x :.Km`'Q̴rbt6ˊm(Qi|3ϕO+E!m-?bsQ推MmSdcp8O2Dy9ڴL6Lnź! OgisiJhfWPd4CI6OrjCBUoWdNC)=R] QZ ٕs/Km2wU\#"ܭ5vΑEKZ[VҴ.Db(C"ANsLgI23ޙ8Z27k\\7*l}Tyn/J,2)>8P3)d-*O1m%"4^Zj%zZ-i./\Fӹ'UoZ[~YPZY SYn"F!'%|msf{Ą39y6i=M4@]΁衔%;~rqٝJ쩩e0m*ʁNe pѸK }Mde#[ Ze.*orF9}Ó[\-NZdhK?,mBmA9Xֲ4nF;͝VqLj TZadg"GuL2+9K,ܐj;f 8myy0Z,ަl҄4wHVH+j!A'vjkIiCe209Vi*mu(eòUUlI%c))W;Ovulu2YO:mK4=x-@Y(^9-6lb䢇P[RpGUĂ%vnmF”ʚo.`W<$EbIuⰖzÈl`wwEُ=rmXsʕVNir$M7DO>g+%$"ݲ'XȋZp5rX`uwxlaUͽۭ(yǞ\^)䲵)sWmd$'Z.l8Nw^CΰFPN}>Wfo'TGDز\ P(1\yd)ŭkq[eƒ{;A_U2a2]2\vu_j#Wm{sn! Rd=RZ2=3DZEfFўL (`gF.<ʶmGٗ;ЍjɔT^ʔ#im ۿMZ*(MKvdQ|@*]S"82rJf{]]i[epv kJOTrNrC Y۷5tTO~@0 )*ES*C̗O2)K^ T-U֢w4SNAWN,Eg3nGHPw}!MN#r{Zȵ7"t +G-Ǹ5^\݄3AKڙPGq qsʎF+B2BCV{Ŀq5jkG5'ъZ81m6FSimjc8ZcC> YtdC"WHYHk&:[mD:@s#]~%ΰS+}q[% `4^BYw#m.LKm`$I.vgKqJ2.^R*to!2h!A v}iw)Y2yYd9A'ƾW>=@(P#+*>o=q ިu5L8PHRYI(27#WߔI/,ş3GYr\6[SM6WheEJG.ɠ>L7+zjZS.Q%( '\V2-Rs}JRB I =+sҗNn07>LW*mJ+RA`\0iN4ŭd*4Ɗ#)V ZA`c#*##hRy; xB"#h8pR9‹ܼG~uTiwvCILPжToG r =Uvaiz,m?o!ǟ) MQ1?g'x '"_uMELơmIpQ9$pp\`Aɛ6[&}QC+rUyU%%qzb`MƤ2+YB.sirlp%N;3%gޏgc 7Mo#=ݯ֩vєBZC$Ø#:a3G~GGjQYjUٳLBP-JďⱭ'm|S^揸:QZA2+iIHd`qGW75T[ݯum(4%EoX#r %<ݶS©x>gۿ4z q 9mYI 9s?%w7,ϵ^W K}!<$p+U\|;+>`P @Ul\=a/2%!#$#fdž f=nr Ez"nxG@x?Cx##ZBJqRT\[jۥ6F-NoOvQn _'Ĝ~4gwot;.>ۥ-qn'R*A| q^pҖt&7 ܚO

    '>!4s؜tN{bs9N|C@:'=ψhD9 '>!4s؜tN{bs9N|C@:'=ψhD9 '>!4s؜tN{bs9N|C@:'=ψhD9 '>!4s؜tN{bs9N|C@:'=ψhD9 '>!4s؜tN{bsn+KjIHێǛ2/{2V.QK\~ү8"x} ? 2%_ԇ_({gG^Q9bSI%&# <ϣ%<5ԞåaFTn+gSihȦքQ̉[$v`T8,EeԜK5P>YIO5o!r3NjђmXD;R1ZQ99jޅ4əY r;} kF'`_iNGތ[ Xgo )8+DQ44)I]0q殼NlOBnȒTKQdcw-=G3IJ-\w\ͩJ!G  Nkv"z21&PmPV3WeZz]bDRG$h_U-/,% Փʛhͬԓ'-ȏoi .9{Vy5)JoGB!,m΍=9 zI"פJr:ʶ&,M#Y]Hs^HcJoY z<&eT q^Efq{ Mpg9rM3sM2ijWy 9jRݲ]Ue61:WHV>7eeihW/S}RK07hgPYyX'1VoXg$;; %ÀGZafHmy,,'QԪNLEbGJ. I܌FMBJd[ [)Kbc5i6ӦJ=qN+kƎ2md8Bү-J1RdSLD$QkEhOËa9Ѹ2#Mfnl(WNd '$zj#VpKvw5TњiQv*!"BSO29\l|Ի L(A҄_ -~_W*Q$mv.c\̙o5m9ۻ22G)Y:jVbX9;ȯcVCmr"d%SVz`;N>i%QGntI^bכݫEл[9<.m>ŌfpJVcCLqҡ@WzkM'b^U`(((KX>5OWިڥ18tC\4X4aJZdrOVUкpz_([kUkB-TaRNG:#W<__TP @Wxχ\ЪcZPٴiXilYh.SIArP( 9sRCj}qw 7+;i-֔!grPRsTRn0ۺ͒ʥU)Sڎ-rvA97tMާ[:tHZDL\rB<$΄֓tL3xu/l粥3{rv▟6/)uGFYRͲړХK(ܖ;Msp9P-2qR]M?*"N&J'jQ$8 @r< JNݫo֮2fne7"aZ|^fn 7хa 쁊޵b\Xm*D ?8Z+ 8WNN0[/Pu%AI9fC~RTV]BJꃎz %)G/uUu\hE8N  ;NO>Ytz_kT;zdNX[=7')9$ .'W4KPn AV}i s쨣\ : shs[a%o4\S{X7 :έA]﷗SmVvTYqkgwvj+4蘆^%tWWK {=*`@sV.͟\jY-55ݶ[4 OZym{i)Y;Z{N7qewX\m-]C³,f什QnNCi!amikx'n7f_[K[C2mKN[V]I@M1q\2;R80_@Z;} m뽑kaMɏSdd$@.K1*#47!) -(<<5̸3yVvg}op [T$%jl()yr$<3eN/^Nu>w qi)z4$[@Kq%@$0r+1v>*{wŽŮy'5ONrlzv[pTAA Nd8Ez3]Gp%,tպ^vnd^*!s$WH[{BHROiV;89Q g>\>'fnh}tL[6H%5̿g'r_K 8O8;1_'˻Ǘi:v컌dɖʙTPCR|G*JՔ nXu9澴]5JZ[E){rϙ1&;I5 w~[-bsM؟ $L8HIuWJx/Ri3N'cQ!$d3BV4pۉ#XR(8fzeG7NCFZn:N\XR$y9‚<;KxhP @(< ڪ%([ukbJ sΩmi43[-6[󃒼W,5RHm)ۜ6=iVoE٩&="P_#q5S)d:)qIۼ}ڬ9Zm wVqR*~cdI( )/->gdY5AxB)heC;AboBZ'2W}K<<j EMka@N>䜖%#o" [-V ~UI,1uG1ХjUee&FB8C}3VRќqv?oحzCV S#ɻܯS(-F)6Yu@B{y8?tE԰Yt%8}I#wW.,f[hJt e9s1>diQ edV-Yێys)SNi=H)+nahy<8*a.If2RR~R;Do$mqe Kh9䷈6eh.%iJL֊6gArg[ۀdݟ!Ik۷i~ zJZq)찵gW6&4%gfqNKm) uyvgڷ$")-!MFg׬֩:ٜˊ4ӯ6GeHs zڷ6DL؂D&9'5i޲dMmXJS`.s-IR%L7n|IVW; uŴJK||I[VуZZI Mf, #BVd%%;-א {USijFֹ_f*ZVވ+ %]*;+6pn}=AXq)q-p@`8ףB5ܴGb"\Sm6 -9XRY*P'**P9Hkk73EoMvz*A|oQZR2qyhCĨSn0JPT5[Ye!݈q-[a[T`*OS:'XKNeGYDtARAuI㞉җD;Jəqyilm* +#n[R O ƛ\7Nc "prLVG:+Ke=!*< -@(( k}׃׉e붓{xo[sAJKV2d ^ qGqS@E]Uɟ|q ! I^H I9KwkMep  RlVQ)WǭZA}5lַ]#é7' ;:m7 U' <̴i}#-= !̰ra2kBA;A;*VWM_WmR]z&Ҵ(R P;=b!R%:3,baoV I 9~@Hh >j.rgR[BV;$^@{xVrm:({PV΄QRvim* mǐdP.6֜'t֑LݚjC:RJn@$Th -8!mKĝMDizޣ"CnӼ:8IAThb<ȮHm.R2>@඼jz:C֔ݻys᱔kL.(ꄯ%XJQFԓ\wyz1^g羐GRm^"HZx{iǡ.C( yI* 6)=dzjbžJ.!eW맼"gl\z(sc.fz,H7E˄÷ꟹjnfniJ[QC R2w9"!c>'xMi֢MqBvd¿Z{SwW>~3z7' Ӝ;6TmWIW~f|hzԐxjKwi{)V O~X+<&"z]czCO㖫Eoعs>`܇zߣ,onNEg D8k,LHG4㱖ػî'8lvm- G RH#)P5Lx&F8Զkb]~'㾄P @(P xTND}0 $#*HJ |vwE> "X9ƜoWnos4Cnj[D[:dr+V`sV9V\ 8%JzkdN:io/uFW~n(P @(+G5Gީ_UyE h v{@mP @(P @( ~d'xfk凊$@M:%ܤ-⩒-)+Vy cirJmQo93J=i׿/.3CGZ iِ/6i07PO2g̯05R[Rv?wʢ-5{`➐lWyf4$W4SVɗc۸$7:RXNRַݯ;AxShljۥb(ׇEHtː- S!*-Fʰqɳ~Z^uK_ںW/UX]P-Ƚϒm6幗\ 5+m;I.g<#] c,mݭj҂<%5 xo%]cZZeF[XZ=oPS917_]Ī+_&J))?~/0OP=7DэC-!fjWqoˉ RT\Bӂ 7wʰRidtoBw/vZMrU/ N"Li5DE>+}SwȸmM2 oRչj8 ܡN!8-%NN׷>r4>F=ZItÁ 8D0^BOy8Y9?ᅬ鸺?bk):)1&C/ L±͝ĤurI˚mym'W~eWe|#>]6t",6\ʐfY$)ܢ|mbn/MU<8gmiB7pj&9ciD!%%:yJy8!Č=(iƻ.)^FIIL_S~ݝ; [vpNjVk+ Z^rՕEn/9G}RsRo%Z175Odi]cv<;kIk6lo6X-D+a (`厖5ഏVpb]M_۲K^ּ?S56\z~qxˌĥJZN )%;8`rK~G<[$k{Q bj(P @k=/{2V.QK\~ү8"x} ? 2%_ԇ_({gk'ҲB ddC'f)4#VO=<2ݝUv PN8ZwIoڭhġZh%\Eg791ʒҹ+5a zRw ~ys殙-Xh Jż>\&Ds)ݶ01j隳 ř![ZHHei;4ֆ \SH2:#jcڦI[nWI]J~"z'{Bbj,T\Xgܭc۶bi̟S%,*$}"ER{Ƣr? g-.B!) ut1Y0iFKqR;Gk4HDID8&Q^f&Li8Q9Q湚(Thϼ*T09"u&ε!)U!Kj6wY緼|f2Zcslȅ ~ IR.49.3!ZrTުF"VI{T*L;&(cCc˜~UQIfֈǶ[ _2y)UJ2Ӑ}AC?E㭘ۺI6JU2]ϗs"[GCNQuӗYXI J U(+d?gUw ~[9H]~+nÊC{*5]Wc%MSJB_qC 'jK1PGRR JINhMlb Oe|=tZj>ܶ&19Gܬ.5t7kOSKRZ+fJrXobv >@{); љs@˲F;qX"'%Dua{85\ $))R֎TW{oߝ1b6lѡHVXU^;2ZEHcqۇ)-U6-I u5 =d%+?q _U?g?CʞРP @( {OW@2=Z]5o[qmqD$(%) n1O;1J-$LRRA9$%rԚi# WVٕ!յ”ryPjilf*WKY0ۊP6#Hi3/w鷾;=HP JEouZJ,6,[I,VGy턐r6ܸ]KV42ч\ R\ZPT;S%\Ν97gv˙aݙ-CIOHBr{|]=˸Gϊ2vY,) l)gp F@o_c34ENȍ`/JښK p(Ȓ;\ 5mAe2mIY GZХ ^BTF( ^曳Ez/IqmnEkTFe -ya^L$UۼN+qV5C T=HH7 ؜ӱ5U_Zlu>-kD!8'v '$@p/\U4[=ZڽYIKxWNq`s7®&,mmL$Bml]2+{ci\aR"曏\kwnڒ7aq˘ǫpFθr*[֐AP+X$$n)U'9BEWx ( N° O1|Pn(i0b\P#nH˻Ǎճ:31OXVe>;f޸ǃrw3Kd({q[,h87Il;4#)CzM8%IPRT2A4Ӧ|}g4-L{}ԭ )HQ`|Hbaٽูpy\bwUwIW9A[|eUUz}VGx9Ֆ[e#xP#ha=0jPyH8LN&/s:w$(P @(P @W8jRBÏK:H&ڠP @(P=ɠ /zj;TYol˾ڐ A4Z60E^t>wk\F-c. uI)R ԣ< &{~Si_Z<8[aЗmV(b]n D,Idb]d濾.ms,@i]iӣ^uililRR稒^"=EQ9ksom1mӚf=%D-Ri="V/]3K{Ƽx\\mĝ"䮼WG5Hۻ ߜ9ʏWȅ3gR3:q^%l]zĀUj@8VRRFG.h7vUROr7& ď-Q5G7WOSp|c'q=SHCmJO}K+'R抖k>UWXn[\)N$>B*_ZNOwN%Ikx{wmu7BYdi6 Ɔ֜=) mm׹ZTa.z{Sfid>{s$f }ԛ!C$I?쵻SJg@Zd&dTtRT<n-VdEXc#ƾc\Ld:p;рS 8mHB)"'"f~‰:s89:ḘJFs(Sݻ;FKU!iis2j/>j4~iiSӱ;)Vpnج*Qh[nY-#mo2x&VIzʃ./YBŽq!Uѥd+ TGۿ$|HVN dF:m;Kߍ>tRwϴKYsgBz=Eo?dZcI@bl7KҦCނ -5sw?^]d03 ֊7o{cy=s6;έu m/$!ЃF|拪\8z#_Lh7nŦڬO;1ˤI~Ȩj{%UYI~owW7{?oӺ>oFqx))m IJwd UԞuz~+JY+GϙѼhZ#Ǝ}4stK-k_h]?@O9؋3(Hجg(_Bښ%nr=*3B'п(U8-HuxREjrG]RuZa;&ARK2 $V1$#,TK؂;,Gk9Iȟ WirEpZ cuǖbhoָJ[y깫r{ Mz hai QRQq`g*nfR]ҷڐ Asea]4].h-}_%yfDI{r[MHPH*K] ڳ$aBQβ!harJWB\ѵm۫JsH) zkIU2rJil81tVFnㅹ+۰ZyMleIc6l'!\y)=vrͤk\b"Gy/wTӬ͵'hGzp 5wc:cl}Ld.BV1Y["47Yh\!(|5M9"Zi0;l!Xe{6@Vn6'5= 1Hu֣AỲtl\!n/VTϖ;|rn&I)<{#"2o46PFUv=dAK.1EmL\- >^ csKnxNpwyKY ب\x P}mOLgm!lwdMRRЪKZv."J:yn?s:Qշ-kA-.K!)ي9T:nXkeUGѥ6eY9ϻWhr#[ N6x﫨8zS[Qj2zIQVX^haqRWѩ%%Ր+ukTh]0k7m”?.XBJBRUV{9 Pz'zJ9C]KCrQ}V丒TyI@ :X7RJu!ʈ/aN8B1iPNфyMе15Ӭ71q"RmQrRxA_tiqv$ːi} JV07/oiƠQhbvk)/N甞3x`$yѼwӺWW-ֵ݀1#qmXLWҖ;pVj>>i{:b,4$ʣ1RAmHHRS]&H)'* !#xKI^$6ӠiCcxWPH #9q]i.RZ-Dw6JPg9Ie׊#:6yNאu1ne31Z(eEJ̨n°@w7,\^7z-NfԻiis]=4)7MtAҐpA 92*.>-K:ױywr;rmC\%,)vT'p2#Yz|>Q>鞕Ğ*im{o^Op{+F+z!wf:eKJ)줕c$9Ab믴<&|L:7ڊnۊ]uz'>ZDC65nv놸 g;r;$GxN*yO%qٟ'K.Ɏx-tW-y};"Cnf 4B+w؅A s*014s#ޕtn?էP ?PƗ2qbA9!3)!'>NxM7Wծv/ N\LmyKS JRɎǗ$\6:Z˦&| X˳ǻ+~qOk+;a-a 8@D/*xb59'HˉuOTm,U|'4NmPCG Jج%<88y|f$Z>Ѯ7V$q'v_k]͢mp *XR ĥ9NWfe 4-bp%](P @(P @(qz~T),t9ڑM@(P @(+Sw{@h\~~s@kP @(P @y.!*TgQa@:TPGQa@:TPGQa@:TPGQa@:TPGQa@:TPGQa@:DPoj wiݵ[QB\ҤA<>J#l MFTn q4S[l^y {I7&cH{K@- UIPEYEhܟL' [@=<15jH1@uq'Wim\SsGy./*%9I!i[R$%J-!LUGd,<1>Jʭj4ӾGx 4LC\0oXj2bBQ%,D$3Y8W) [^څܞ\OF|y>J!'/SCRw;- !Hopj)6ٿs~jJ:&H I皲ZVYFH-)hS '֩Z4rۉS*7 URka)F5=Y# Aa\*n,Z3I` s-KxsDUx[\,=hF==^2ovR,*#D[ImzL{smRVpT= 5+>lSnVnVc(-HOBHiYi[G%kMfμf6bNlN*RRMGHJ~^]/2i" +M$-QqxNRk8n/|}bSj[n*L]:*e@xt <䭱bh,sA̔ڳ9AE2[Ld%hzjԢ+Д!է`e\ͥ/hp&X#֤jje$kn\ͷk! cj?%N[&g]kI2,\aT6j5z•[rl.$`X@e *B<3SYYYw9*BԐAǸqH]QYJmY; ̑T:6r,@N0u]wĺۨĎíw5̈I3yZvքwYk!ib \3=zAѲy'>y =*߾TxR\{q@Qm`c Y'yvcbLBflgPBw PbۣjN3ŶEd^-ԌJ2GFE\C@- HX!ҬdH#zn |@r.7*= [d?yI + t *BV1 L}tb_t>n.(L”K%{’,-8J\Ayu\쐻]zBŠN1O }Zw4Vj Myr.18oRS`@9WhPt]GqSB3."(e%.(lJ'qN z]>&ؕqsP8OZ m%ĞA3{VF޾(MIHP vqQ}t~Ֆ#Kg,gLjK-C!I @IQHOJpv0Lj6'M0܉V' IW3D`?\gn! ~2I;^Sh|Rx 6gp,qNG\ʂy2wdv@9,!z\>7~?fA2 `q}0nohfu<+ht?pAY#-#>E'I<\z|iLi՚gcȷpLJ%B^SJq7g))4м"uPZo]RBC=hu o'01@jq+IqV_؍\]1 t^i侜,]ݒ6 ׮Si)E?rx 7rio zF3P,rn&B)S0gvhL}E ;+(`Ƞ7f VcGi,e@P0@?՗&ݾ JzL|9=]kxfooop#;պGͷ [`5Ҕ?YËĜT证Զ>=QqPS-IIҞɃ\1#&iB2ZW42q&'zko,4fFQwr=c 2GEϧ偉֖Usos-M~0I@%PNR'5q MIjW_GWn2Ѧ?q{W \ONYҰ&:Bz%F@Ghclx=Y c i<Δ^EF+ybz.Ѯ,yI8ǟyAVQōHx~`=^'8jж) N, QVFсv@7"L~l\Lq"UJ%^w}^ 뛔 h]"!kHV% `˵H/#ww/={J|n/.Nh5[ehyVPI^KszOqi]yhO=utA n)$- s \p8eG yG{FP @(P @(P @(+G5Gީ_UyE h v{@mP @(P @( ~dGwapDjUrnCEKq]0%UD yu|Zr7kD>&Im$vV *ZptA[#SM\#-|/KNm|8 |?gpl玆^4ݢ - ^HBF%N%51n^V˦%[\~K_nv =+ugLnES][t-ږ ޕBGySJjozԷmWkNߡ1M#g|_}ڞVMt]鋆D1iSe$mm*pҵJ$6ܔc׽Z3R=*Ιb_tΤ^+[ri1'd8P2T d%kCJv}1cT21\2^CBWMS#pZdϰh{5u4ӭKvkt*/ro2j##w:2ֺWj ~k++-Xv7 Cy Z’:7vkŠStf9{{yv=[|$avmkkURL[6˪RYu%@tqIRPn PJ*~.o{yJpzbNN\{8sЃTHU_a5bgnYg:VrꌠP0w* 4Œ̻m}fpfUH[aedXYes{Fϳފi5UsȾ6+zo}T%Y )»S2Z:^IMIwE+y|oVdp}Il5Bl{+JQE?▴t)#j^n{U;d߳zĶok\1)zeŶܔ{)$irunEc]/ |mӱΙr"\H()cBMEn[-{VݦN>pnX6Yې|! >\Vzpz3|ݾ䢷exaۊud tZ+7JT>ۋSKHPK-0$Ome#1ME&;=4땪U @(Ox uMj)a-]YR9/+ KvT{W?q(ҶI7o4X ]Sn;՜faJ%;а sI9/,ҴոV?.)`F h7T}~ JlE_Q N>NJ\}On| aq}r}#6^=+sx(%e?_YW<>@AymjC=e-@t'&:>7 օ wr?5slN}N3N$jkS-QlhL%P<-ASvL[#[UZތnmj 7[+բ4Қ/% ʻ -;{A] 1YfEKV\w\-C#;sS0JEmB"M/퀁grr[GRyPY@'#T&p Fwz| ,mܙ#ŔȒJ,6i1aۚ|)WZFd1-`Uc8akP)ؕ$re  WبiɴkGEhN+iƨ3,a*SM y;OVNuLW#vz< wVÏ8GTyc ;EL2:&6i#6FMhfa,53Z^ySW1IX_Iv/֙)S<ܕ-;qա4V]]H%))Q۷njItYeJKc54vLiqRR, 'tWL' jH?m`e9 +{Hj2ļZChiodAO[mVSϖoPNAcPm-·sWȍy $5Cܩqh$m@=iʂMRJvf"]qM~JxMU ؔU)sV9ZGb76ְތI$\Rs؉tlI${9r"SܦZRz>yV0 YImc)k![mͪ!.F?@5dd5cU;GnsH=l5 %d Vj{CO)Y8he%Sn2` 1=w5$V}.VsʳhsW&{rݮOX<*۪,ǒA=:ԫy^Vc*%ny6z)̉7A-ȎJ_aX8OR̲+$\EV1 l Ԟdj;U\8YC8IrOI<=bC9 qzQuFRSQZt ~%GՒvO#SKF6ĸGWPU$︪-612zVŸ?Ɋ)GC0ۂ! OގSk4]e53Uj-\њnZ_*%2oM6q cFOTgj_V=*q+cv*YBH"i!;2ëƵӑk`Wx{_|0}j5 (DR뎤9c)P!ښ%N}2HP@B8Ù@ )=IHͦҶvo/]BZq(%H k@w3:L[ͺĵLı&z@7  =v_\24Zt'uARBAJByo$38euzb0^SѪV\HsjڊBv<°@OKmII7wz jСa9h4wO|<7F]>iSO=Ҵ*KiIZ<&ke)Ӗ=9nR/.vP!.FZ eJvh cZ_Vj g"*ufZJ"-歸;9# P5֒kOuG3\\e[IY|E*ڌnB 9 0ƭTzޠɴ#vG7rA8#Fq:_Ñ;=?I& (CHZTT:0 P* uu[cB+#2m'J2.M.aj9s/sg\scqGe2_V4>( !') g4Dp.٭Z-vu/%%)*@V0; .m)Ƙ:SRY~}mQ^;$Q( ւ7}O{:}FaH:A L$!DsqPLΠn}vN[r1ae@$RNP* HRHRH# hx|ARYKéNJHx#J cG+=N*WjgӾZ~x;Ƌp0OʁH9I+=<3ʼtjs8Mq1pig>{v7wKlKN@ZZ ۫OOB @8Fv@$_?cbdMgGp e.Umw;BpMt0yANzZʔI;uXj:GޖI/%E P @( ͯn}]g'Y\Ät|LK/vYoŞMz"NTR2I$@jË:^Ǝ2Î-8ć,)EJNFqR>|XƼ=tfWO]'(P @(P @(P @(\?y>J ?{//S@Xs#̚jP @(P @V&иr=怣qF'==dl),ǘ[iRR $qY.Jj2t( ݲ&^mSW˔m8ڛWHgq@ G"7oiFQQn۹ׂ߳ ͪMS9'PŪ"TFFKL3 +oք/+قI f#Qʗ(䟲62OSg\-s44bʦ(A^;87,qtHhZt..:شT:kI* VG+>ZM7v7gqTU -+2Wƞ^M],2!3YۢVKr- $Ty{-ʼ>r/ .41Ns[c)A23=ũQҮ\OrZ~Ҕ_oƊ~fIw++q?J=6ƻk֭?lrn ^[Hmp[A_FIY)y' Kr~];9Z{]__]V;\}S/nWnhj< &\T+s[-UU*]kNnR]^}I._p"nd'ܑmimÑ)<[ (s-3kOޮִ}|48)M5&y(1snbq)lsUy%,7Yt*5wk>%{uzZB- @VH3MT`?^jm]_qoە y^5]ډa\A\ۼ%[.&`}h4ŝaBaKۙySӬzIZ:RwMw-c>ne.5iHlչ-M:!F,Ǖ֣m{V_E6;1_/{O|v]T×5hp pۼ -"nJIR?-(`TΜeIy%\e(NOM=~gxu7$jLEHn䔉l[4]ڥN82pyIܲEOYo''hVy5n|]HGdeXu~R]Lw&HI fxmA` aI'շո{R~'MCOV~wPx1oYSw#j}!iXR^SA8f'l/r~8c#{R_Q=8+'Z8M/P岧S-G[-#Aq/nIܚT_ma}-}o %D; &D뮬{kY1&Z1Tq:?QE{NK}f5ǮR&D^K-.%JC_U%D*4?W|ϹhZRrٶߚw]gO{Ԛ⎨!*T(?RYY㦰U|2Ј%\ #pJydο8.B?i=UZW?x+=/CnΟ}vs4sG@l[ȸۜX m##+pF:rXUJ2]w.mQ[E~7QHO,O[v$}'Wg< |6tuJ#x#GyvcM5fJ ?I{zUg~Oy&P?dq;[ڐl'pCuƎ. v*WԬ^oZΰq9Se8ruwX67L)o 7֏3cѽ|[(_WiaXMVu'!#uĮ1nXpgQք*zT/-g8V@1ʴ#l= wH u 6FҊт-V$+WZQdKaMYRLWFvWtrM-%V sLT_R[p_*pGn@JT|纳IpYaRЬ*!$ vգlԶ!݌F KJ!6(W녒fXw7*1JM*6kLIiIaՔ?>sRzE5u.r00\-9ժ&ݡw;*23\urPW%{x}\6n#Wy( \˭K [Up*VJ{}n-( 5î)k9nς02pqƠ%\oA[$]&'(?¿x7O*g(P T}P+jM mqq- $: n:m(NHBvGvN/G:T+\'NءM48tmi'4ywIh;J,ɛi-Lٹ+`F(Fs  cAk"z ֑m,ZJvyP s.(u$)tm7{F#.Zʎz F UtVK,"ȸiJ3=` ^9gW>/pu4HYZ-2G5' JA4xWH:r ɴ&(}i=K:>F^9ȼS3~!x_mYKZ+q H$Ivl u _χ~>sVhȞuĠlGq$`Ԝ)2 N7_lB:5f2YY!E#f3H4Nraɖ@m$,w-`G|DFɲ9!i#+Se`)#;>/q niK-[(BJN[+QO*P\p!*J!$mrHW3Dm'bxjދӋbsK]: v^iK*J׸,&E7-mųxyĐJPo2N@*ʈ _8mľ4fmVؓ3P-3ߝ%G3q@uJ~j9Bӓbt62)ÊI -+i9g<ԇuwas(r!1* L,SK{G5HpM 0Gr3i)VMHh:+Mv{׌Iy( z@(YK7y&݌\Έe;Nsss㩼7spp;Kx0Ͷjޑ-JдJ=g\|qۼOIx,Es[w׸(Ţ*tl< HUe%`O8PM%sqĻL-QJ OzN;|ʹ0xK-Q=#8ܔ:]FlRSwuy  IF| Xp|^'# uEOZLj1xiɶkgqUKO?W9н[G>/{/qJ oЉ//?'{oR|kٍ{Դw>K})n$z`z1Y - 3'<9e8вD<ĩw!8R1RvJM-,ˌ东}ڳIuJ-ILsyqV KyVUT!'Ha)=d-"Vm"#w=5()rkҖo';k_{4($XsMV:BH#{()O}QarUF}2d)Q[ <b-**W!78 y+Zk5}RHp>|r3mDm,w-I^B}Zә)#m.Z[sWE%Abm. sWvnJ*I_T[3d\u+-x楞[LN CyM`dvr|ŽT3Ȑ= ԉ-I-WDޢIHDC) $<1MFs+^e-thڧP? I-F\3ў=kP5L;iZO[Fq_}hRr9&21Huww:@5S(1%1] h(FkfGm]`QPqMl=:V >2GĎR-noˌrJm4VGY[Ӑd>!K3v (ZZqEy򋌪,F0+}KurVpҤr#W;@iTJGk/3x:@q3-:m%A[)TRIJZYr+O}Ww/}_*{BP @(+G5Gީ_UƠmtMGNq,\T;*ߵֶʎ[g$vy)ST{V*vORmKl)2sbOvAI݊ע|<^Yu- ijJ&נ[@ jkM8kCxsc\OgO묻+rSҐ%b6%Y5VnĻTh\(џt^T);49xpз ][Z)"HxR!҅ Z q]-*]qn\QoNSl)RVFR@WY:gS&l}Kj62JzH׵Eiv<^1kD\Hzf"BޔHKj- I#3@uk;_ɌzbWL(IAZgbY*SϖtZϊz]gْLӷ!{ZR9Ry u-;lc]V}"aD Zg@#c㖧\ iBf}?he,%BrJ {•@w+/D/\^|yK ]jth~ ĈY]HVyU4q:녲t^ ȁ>ǧ !Ae*-')NHI4ҺsRi*\(zMz6^BHédݐyZp9P^tYC{F[̥Ja㠑W#y=%[fXh2 ML۽6s9匐 f:.Ժ;i山ʁ ϔE$d.,ko3dndҎI'síeY] dd%BNA,4_x4իuyٵJ v s\ÖY=O[3x8wᯅWqIަN+ؗ;% $[sNu#3TEmsK^W?x3RtsDV9'4Pz?|eqUUz}W_i;V\49sU SD] `o:=#0[wmϷ|\1&-<\N7gNwX0O "}[qQqCt$wc8Ӈְe@'**#9Ub3ƏQUn~t' S̺7ڽ7z 7sIgpO20|o g 6K]m|u:8R"Il;ӈ=I#%$4Ė M;^(h> %m8e**[9{)'r . )fGK$?Y~yN2[=gbl N┤Nrb8!`%}DtDRRej^±"QjlMWj)RZRKmݐȒ{XO-N+Vz/\llybFn"=D3  X8$@zkx֧=V-ZWQ@(4X;d:(\ZSVRPFqcRN6LV_p|}s0mGsz6ik"]G(P @(P @W8jRBÏK:H&ڠP @(P=ɠ4.?\x?95P @(P @(P @(P @(ٷ}r}{^SPd]t}g_q7D~eJP Jt)m|pUjV>QMrl0Uɠw N+<.Kؓ1$AL{ qYTodDk|KIS/,>VsW-n`х% )ج|4O. M5Ҩ:!I{JwVim`0Sճ QeےIȃ,>bo6=smVu dkMeS,zh-[/NJr];єe~by;`9ƕM6UeL 8R&6웰\_E$ $>ZZW؍]V Qу2Ԓe){Y*P*HSH95VUaF^d]jj3uRmNPG'ܕMjlN2eoؓT:Բ+2JHYJ{HbgUdsUnSK^.*$)2Z;y)` UZ4@aўb#CĬg%CVR @PԴ“(zbd@C @Q8yMStɔGBJ)acrϗ~Mo%L'+[HiZDHqJQJ;ڞ%ޕZp1|T]%.oo͓QKDmΡжڰY_')qW9gʝe<#sFYKk![؞J;5LxR 4M\Qo2ҀkEeLa2[Tz);gNjTmǟYlK|O4b$A*qFpjҬLw=JVqUBNZ qxeYqgZӂs0Y#ECJiЂ7 |ZH1$2wTܵK傔f#6byK}''e)[#2jxufR2@&)"Y4J]PJy%r%7 ]Տڑ.$STQ=H=Yn;eKAk&d_mۃ/4Sʢ*4DjałHZdϴ0> 2r}©wN]%?Uܘ\ͦu] ,Zw鮆j̕لZYU뀿/wV.]rD{TA,GQpIjj'쑍j입㣰sgqEt $XmVkG) ԅgD[)ҏ?VKhl_fl0GP;k-WiH&mr$61T}D"ӲkcZZq qI廑xhC/XIRFB*7e[ZndT4VRnϗU$;j!*hVrd50ÞiQkz'FZXm@e@f#4Y2)(6N()J guffQH-Zr <-3bΧ-w80nci'5/,gk\fOCWRI@hIP E<>j+q JIS|U4vrc_bk꿛lGzSP @(]?y> ނټ]]#۝3d4V BeR/~CP;3cFKNcVt\\!N֚ґzzyqZR((RIڜp 4 _1(!FRs#i'wŗ?W .&b.FU6 *|^;l.ӌQ2HHs FyRr;<qv8$m'Q^eAjPh))I;@ qĘ|=:s]tE]Zi ̃jJ@9I#9Ge4kLWV8ܶ\ݑH*O=ʼnϦ\+ZJz 0UDGcKdXc!VC+^*%J88Q#_S˴ã]څ#c;T’q>AxWoj]O

    vQY %͕mR~IVIHJRA<;G?G + rNMtptΏ]͊@(P @(P#TЪpz4?R=ɠ6P @(jox?2h #h uݷPjJmXN},$)ť *ڀHJH#$w',^st-o;VLZ.LrcG.!44+I쁐qu7N*z_~W^uCf۷_ k&.[5#nizŪc r;([AO'yQ@*|.n{N'NKu1lٗkey#,&"KGi;RA׳g?fsW/>zi,_tZZQ"C l4Xڄ]"~6$M&|r-kNR~~Z[㇗'6&SՄsIBn(\*H)ZI ٥tonT+~K▘7r2Ȑ͆βv]U R;}rqTi(QV ]A"P-h-I: d:J&y{ FO' ?~jw'_z3?mxacS;ߓ|{џסnޖCތwGg~ۺ;w'?C?5?-<ǽz01o>=twOK~O!F^ ?~jz[yz3?mxacS;ߓ|{џסnޖCތwGg~ۺ;w'?C?5?-<ǽz01o>=twOK~O!F^ ?~jz[yz3?mxacS;ߓ|{џסnޖCތwGg>gwXDf62{9>barU$Ʒ=q@l/{2V.QK\~ү8"x} ? 2%_ԇ_({g{Ϣ7n8u)$d9eE#eҽk攏BEE<s+o}cRܐ3̓Pڈm2vZ#AB~ Xv"-~Z[qܺtZ,eA-w~YJYF@I*+Blb8T]ӓ[zVM^/[ڔz@8i9lkfj^8̈́1m!$$zk,hҖxj#r9zTUB;+O$(pVyobɦDt:+jϧWQM+9Kw s)OY~2+" Ӕݝ>Nu-EPt=&PT=5^;ihp̧bA9QyY"caISWϘ#.%i ܏>/Y q/2g'jESJNtQЀݑ*9kCrm+jQmyʔ='QԣħF.:5#'q}Cu+؀jFn,um;~XgD T{!Yٚ-#G5׭ xu*i,}j0qvh[F ݛ!r94H$yq7 75)mە l+4s;HGѺ^_JwVؘy3Ү bEX4n?8{KJ}w'<6/fc}I $<&eTCp>6JP9R3fkKe!Ro`i-nzgXFAVzMeNM1t̶h-TFrҽJl *i;#r&w?%D |) *W?v֤&E\.nIojY5+p$[=3!F Q>*uu+Sje!< a'#W"4Kk;]p:,U:}ų(hvHJIq=ZZ&ؚ9BY s5Y8ՓyHd)5VB%R*\k4^q!Y$%`34ZPq.c*5W4J֦K~խ \@|#V~Нќ&6Df6{%{i{xGڦ;&Tüzs]`t_[U|Qa򋯧@9`'&~vIq y%^m6+iGSEi2e)ϐ*L3-Ѯ`Ch<\)XIeۤpI*8!i+)[bQ U*J *ÌT$4J:jζ<!lmgQ$=])i:'68ǡeh<֛]%ndr+O}Ww/}_*{BP @(+G5Gީ_UEV{SMj9{rmQg75d*NvFI& t=CD}@S2HOh[eEZeI0S Sj-WKv{*3V&hkIw'f1I$ WFeUcG +ݵajZ!HPHwf6WZvorgO>6WUcNܤ ݓH_8qjy6\ʤƹ4CmBJA+\N;oƮMlMbd4T!$$lJJ6 [qLE&"JcB]+ ]"R#Fpt Μ~u]HeF "vVB0m;@+MY vmƶfsI,[R^9W4ƀze-v1Yo9o0@ rV*)$`Xt4U;/Kۓ2}, 쒹 - ( pтFNēϝm=H79XjrUԕ2m@JI#?~.]\KU/!/I6:Sa$FB'PvNi+/M|ޮ|z@Wd΀ ;GlzgxZK:bS;oQVF<@u1@q,4D.# _14s( r4KTL _"a%DnOx̒Z5X7Gf"B[ڤ~WHFR0h djezitZŧ/jzA8wG,zaCO=bc&mM/ [}'Qi녱S63Ԡ3-%'k'Y3|/ x둧vpx)#:^52%hp P#'(O g='Q?AK5 [k[~g+?4V~Ё@Gj=1gsӕI<>-1̮pv(!3Q`dϿ/V8JXE3F9TqNA.6O RRIy}ʮWvi).Ōr<2OsuM5)I;3˿gT+ĖOwxL7?mr/"N!iٯj8*N@P$)$$dnxt)VFY.T HwPG-jQЁ'' ~yWЯHzV)%Kxo5x??j-wSտ]-DEџ!Qh⟞.C.ڋGttg~Z;C(?Cz?=?]-D]B)"o$>"wއOOK~Q!FPE?~z[3?j-wSEߔH|Eџ!Qh⟞.C.ڋGttg~Z;C(?Cz?=?]-D]B)"o$>"wއOOK~Q!FPE?~z[3?j-wSEߔH|Eџ!Qh⟞.C.ڋGttg~Z;C(?GFu!mۚb)piM'ydz< LINXݷ絇(,8*IRFXn3қ=PAa1ߎ|ފPk!)@zeh }jqND PT̏51nн[G>/{/qJ oЉ//?'{oR|kv1n8Č9Qu諝ǖҽaJp=QVaD02}85)+܌nh%H $2A$1$+{ dX>^vDb%ϔJUгfkT+!JR_$3A-;XI-NptfJݲ D3tٲ#Ig)@-]V؊c]mDϒO\GJaHi; @KKUkXVybDf*XAXY <{L(0+ڼeBfD$s-W-ոBh41cwRɅ%mLХ )95!Q̪ؓxHJ̶5N9w 2IkHt)$ٳrNjyK9F*E[{=͊K3EFBĽ%RBGj5VV,F#DP)8P˻.Z7lW,CmҢwa@{ZZ9WDJZ7685ڦm7- )+X!|VPRnEaV[,VBIhm6YNMm WfUD泻vyXC"rDwcWaE)T\k?aݗMIi*uMi;ٛZ֦Qb7Yaj Vzԟ[MF s REٜ5 ̊%Iz5*璡JQQZ6{LQ[ +4u#${̦5' hIY?nH? c.. ܶ ~o-h}RÂ`ʐw.ߘyti9)ք:z)[[ۚJ٤dJ&d#go ?]Jjj;r\U'GKED*mQ*tj\- u~HHƷ-hƚ&Xo#$i))U׆6yT>z8x8j7 7bb(bUIimMiiF2])+O44I*\=!D=~&"Ɋi]loȫiNI{~,r{VdHkx%L$r5D{Pn(eI~w̲&Yw;nJ0,l93_WFV9Z&ZҐK(Xw42^~zƘKuKaZz2zro:^:Զ{XRGqm;d5Qg9NPnUϡOhP @(s+*44fmvU2*S h>E E˼%7ow)'Ӿzd[JRyNyR1yg gܵ.$kV9EJPN$xtqJ?~uz%rZ $䀞qzWi =yQ+l@!…% [B 5ec'U'[_b[66Ie)*Ǭ^N0͂ᤤۮz_ʾ;, ~0 PJXm@$dm<qzr;[JSmOV2H>S@K^ڶrҳb\٭9{) S4bw w>FqoGq]'e8*+-%WUk"_ g6a@ֹ1*Sg=rJԮ$p@RtIQY HR̬yOdhV*1e&UkRPR9wr3J[+Z׮RqD)9<*L]+rCכ*vX`8S.JNQrAʱHJ$@T5omaH0S:q5 BpsP{"m `Fm4 ┵"TT`(Gm~ne%3;n vܵ*׈Sq(vJ{qhxonjG-y2Xq*i% A zc'55q$\դծ=UW%9PZBobۉ ,sRrs_% HO~1[bjڪҪVy3{KiKhv[ks]m) @P _IWw XPq#:sӾ7z1A-m~/ѓCOMPJp8ߴÒH=›ĊZV/#V"9d6mGyrDgxn&8Ix5и)tw?_^kYvhZ^ Bwo 9p]S}XRkӏ6zߙ8ufiԹ{T)Nܨ$ngɜ.s?Gn2q:v>tG:MARd%a(Zyvϟy8ps#x^xuMR}@n&KM$)$A+ bGx=^tKuP\m.HZBС%C 1)b f3 e=ʹT$$%mߩ~ն}0YK0\u!! H*PNOg5>#rG|$8.ryμx{k[\ֺ50sr]c#>0}~GhD@(P @(+G5Gީ_UyE h v{@mP @(P @( ~dGP @(P @(P @(P @(?G!-%!#(@( w'( 5fJ ?I{zUg~Oy&P?dq;[ڐl+T,27(?ChtCVdS$ 0km;Й)ƖG#)m'8P9=qU2mbBPa]&@~)O([zݕ5QȠ`&++|ԠIS7+:i(3Ue7Kf["euO@UwX''=}+brm #{CjuMnf֮DT>'Q )H gB+I3mDYR OUa̴JKkNU;*^9{x<7,Ғf얻"nI!c3Z,i[$DM ,tI9Ybb*5t`\U!`xsrMF Օ^]xDslv9je#uSO VzF-ctK-G"7w ?%^e:b"fKZ@Z$(ֆHִe*.8VRÊos%*"֧YwVKc8s$r#Ϛ]ghyQl&vPPϚ̵*zଃ? S[J(FBS!]JC}QRЮnmNBuǁCrI;@-ImHj:~TAH>.UEfw\J(hՔ(7""3@2TWh7\rljS&3z+QRu$mRєEqtG$+zBs=ͽ=tr,ɐdG ˃G:wWdд[uԒ;6]Qh9 8ҷr4R71BʣjHWMfN'fZ(ҾDsdmJ-RV x5kB&lMr7`@yT&WˉVr"=TrmԶ)nX!I8w,6IgsRGMS;sFIe  qnOV[QB#j ta@ҢJ"28|RSTW+OolM-nݴo? Sը{Ј-J= p=FeqzN9(FS+jaQ䫨k*p`rUl+參e4˥#36ǖڻ 44]OZY?/*:mƂ,uO%I15g[p͢#.1|euBUrMY1+iȌRB8y-ֆ׮4*%BeX܄t%.8-LJR\Se=-j=T4n8JPO rT|&v-iN#[SmQd٭Zye=>lqA[m+^F F t8ZۼWYnCGe`$(n}wPtOb]>l*]CNR2yw@hh7xr"<9 qjRT-K Rr~(  O6][&XSJw%@-@ sW0.<㦈k[dfȚÈwg Rr9O3/?lL&CH\ ZB†O?I5gF雍DY3ulCo{dUPZtޡe:)o,*B[9+X @X*'ljaK#>/F%ޮs2$2ۭ,8ۉ Jr<֝GJ. KTi\N\-fHe+) YB3JΌ+M`=HUSJjbjo bKJi{{#cΩ87Ɵ l=Dž^jI%2$Kl+G*W<`s>qpYS tU[nϒ>yq"G\` YJ7[$('ӂ9׏GOu^E;N*Y_'Mc"٨fT-( )ZInrc?eϘp1ƌgO8 r֋.hچ2W5y%)??v2xWv($cLkt?Eˣ\pvVλf@(8x 37;dw3 QdvF0;᥍%(:z;X8{jUu=%a:cMe!QSq<(z(3q? gUK֧P @(-em:R,e*{CW-8(hյY-6Tպ x-(R#9U# "^li>f\P @(P T}P~_T_ЦjG4P @(MߑMq{@(P @(2.zv=;sPHzG@=N?#njS?۟ڇD|C">zv=;sPHzG@=N?#njS?۟ڇD|C">zv=;sPH^0lY\Kr;hrRpzN_] h$+_OĖ+khu#Y62i2{ YSrf2wMuQ3 ލiJR~稸!&a1lQ('*QUR֙ ҵhtAvViܪ]mR;u)c%#<̝fվ&Х0w)JPʾLվOX͡\AhjF̘^&CKjVҔ VMEhYV(43އPqhL[Rp\Qśz%LʰGiOm ʵR̫I/v2ڄӏXx>WvKB܈Z.RAǾN*RfFL06Ԟ0IFfn*;J.6QZfXv rsXBz].F%VZ%MhйϓP̅=纴!-ɵ. `rϹ\)Jjb)1V{ y=h]l(m.FWhUj@lj"Ĵv#=Մ({Mjۃc@1ZE6:3t]2&7w֩Aػ@2^F!I욕# R|?I쨏r8cZWy@给5*sl،\ aYF{eZ3} #o|⩕tȨ\ Mʊ*#gqR_Lj h>p hQWd,8 ¹)>_]3z \U:\en? M$픷To]t{iT$+V[#ħ$$՟J1?5_xʿY=| @(qz~T)2kmmޖtB&ΓozKNEW⽢4m-.dyzބ9[I OCi(yT}-kQċrsE:C+cd+)"FֺwW֍Sl[lȒԘۘ!A+AI9l x/6K>%Դ:vyj$S6g#>Pp!lEZOLpL{S[.G9%hQ)s1<VrfcmjFAe%҂ԷӸ]BvZUowhwkն!%-21@y |rNк%6kB.b_8t~q% J핀de%;ऎd _!g\' 1N}Q#FXm m% H DQ76LZ[B#%DRU&""ɬlC[.LLykCj#;\e Xb:8*xn)&kSP ^-8Kifm)}1!IIj$ݮl|x%j:/qzRRPU߉35|{ػ[Դ)tr< b,hfpXquk^揟- ѫoV/KR:DBq+\yM?E~t~&c8ui}o5h=k#6 ;; obBMR? 4KEiW1JQ;@pC#8G8|DZιVϓP @(P @(P @(P @(s+*84ިMc!ԏx?2h @(P @(Z#̚BMc3|"w;$ۜvJen2JVR2VsڽU>k৆ LU~iұ'D- q݊^%2YTc'ŹJ+7^-^zjdan7=I'P+MӰCIKBևېX,\9sg>Eҫ߷ߑ3SreZl7;*KZbu}sL֣1JVC u9G}x~f3Y'?,zFš׸s4Fͅ|KQ2`l R7 (w'3lۊ|[?j_5]D䭮YZvrNfգ'2FVĹZn,l=n,J@FER-*ZIvߡy,%o5ݳx[ZnZoP5Lm%*=:hrG\]ZꐦTj!HR.&ʥT;)'#(jњQ'\v-F.)Y@V@A Nrvo#7оWñ)C=k@Gw7\kAx-1,A'A  'J7ZSS^VOj["E5$6IZTKEw?T(J97Ɗ.$#d﹆_tMK~ugL;j} ShkXVxX$Xrr5*8 ;uVGHMd*%ۢ~\us%'KY] e#P¶-0ݯ- Jvd/}6j%\9`]~V9%]ݮ9VߴړᆇFٴ%ڲ<̎ː\TltJV\u ejRRv'PRi3eU|wiI^;.C-~-"ta1ժ@mC}QΩ)B9rv{[[G'u..ŸzPo՚t6-1\ ZZKa@_0@'ۇ6ҽ9a;uޓRGּ;ѭ5şSȹ[d˛fb廡}뒔[+#kn%ITB˃%&*ozVNȍAVV g-~wcY6^]PßmiIm26[-*# ,5U_>][~-n$h;>DDAMŢe7~:ӈV7%IW4e"ZU$P @(;R߬wS<\rP:GJ^^* xn"]dB\V70#8o*zw_iQ@bt;z,w= gcV0+7$fZo'>OQD$#wM/&'ۿi/ _<;;iG:H#1#y;{w'+l{^SPd]t}g_q7D~eJPֶgSi XLw}n^YYl}e+(K!#]+ 1k-(׃%hf4hi1ؗT'>joVLet_LHEcƪ[J)r bav#+jenCQ$ ~j&ir#IZXK8J-^RUŇ[e 511U shWUq4N2Ítu+esF1 *J]Oh\Pң$JyyTM:-7" u o(NC?-V8nw+oǐi8:%l{Qmm%LwrϋԷ|gsݟ/*2zr &_a޴iprVp.8>/ RRڬyj DgmTe: L+TϕZ0W*dlXrk% )S%[dK|Ĕ|G5E2[R>]Mc\͝'Ɛ̤')eݗoI><YQ܍{kZ:Ap{wJ|H%.%ꖚZ+2"0)VQ[+HU| !lkM2Ee}$mpd`*86q;nw!Yɽ#Zw{iPଫCD\lm,2ZnJV4HJnMW VJ9 {ҫ!2gH쁊a}"YBL(vVdQj}İYm*N6*ۑ+dLZ^fj ʋ7Xѕqiy{3iqm}X> 752ԙ:B)V?HP*)2RSʔR#$I;-b=֐V+f'jJqmKܞD5IlimBڑBv^0Ioq!UFMSmum1$!>L4E%\ SHpsH5YIh܍~júy!ؒ YN=7!&yS!$ M[2Esfu&:)owD=$KJ:P;dhkY܊ێ%80~M9+D)&%mq2 k(9jYVщKtV&eIhV6F)֝ûR QB aS @%\6R4ժ^S }qZ l!*Y6Uъs50ܷI+؞}"AjM6ҢMkmWG5ȫFZфd5qZ8NH'k\֨ƨ4l0/$hޅfPNJX#5jm[9 zOkI pjj&ekC_bk꿛lGzSP @(\?y>J ?{//S@X$ Fjm}K]9ezpմ \VFk p@O[ jԞvCQYR2ҏ"QA'# ԺӓY(&(C p;XKY O1\pv怩j [u-t2+nʘ`)JG@VJFЊwYLMsjtۛ*RŠ@}YkՂᐸ6%/ѹMEϖ"koG%-e)!CaIVkCt(l>;PR#)ҀRI 1rD[CCyn #< 3yoGb+Kܢey!P9'$)]9ã!N7[{uHs.)viRJX}g=t]ãQgeI`%M*FHrsvj#eƋrc]">eD_Ni-9Ps<4=~{L+uǠ㩆Dz+nՂ1;@M mw0A֐onv_ (vHRݐI.@ujCLftGC:ܵc8怒4hkd ,eO8[@'JJr|0'b 8|Wvy6DAX)#8#=cվ:3'xm%͠NʣZ/I92IZR xćIʏqxuPj嚯m>R'y}y\!ㄮFaیտ-]sNI>o=yW,Q=%cI_&V0oVU#JIvsO[?sgq'EqW[Aexi - ANB;)8כ,l>*jUϷ/y8SRu֋Ziuth,{E@YJ>RqGppS8Q/xۿbDjp@WVW #JB[9*PH'@N}=Ն6's q\8v?^Ϟ ԷH,4wFGr|y\c?G G)Z@( [-fYNY^O-VRQY&>"W'#46]jet$HRs(GƆ/g_O!W'kcP @(P @(P @(P T}P~_T_ЦjG4P @(MߑMq{Zz:Fҷ7$3n[[dIKi  g(*z%:8\ uS/[3abUݷjCR !Q+BA{^RT%m$9 SjtcP:;sK鎾8l6Ӆ6[#hqI'7ɪ|x}-6Z~kȴF\n)~bTftlIKT@I(Pvue:=;Ӽy-UjeiuVmB:z~ZJ~C 2nBp2NKE+ӿrmWfz_~o|tC+.MVe}wQL}܈TFZP6*;jHPYwR~.ͷ"m:NVQUܚ7~Jet=6 #;e˥_wҭlJhO% ʲIKe۔ƞS\bIyAW%' UDMy;HoYw|ټpؑ5"S1jVL甅:⒐TMS"5^OZ-kzY' 4 nשySx2}Jog]ZVFHSsroozk(%ؒmȩX<tmo9*wm=xZ.wgBtn8PV)C*^_6mnߍϩiWvk%j,t7"xJ9[(Bd]R"*]k^<VI (sWMni=Lqʤ4iv#Y4ez۲.;%Osh (BRr.'1ʔmAG?ƕ\!8x~.vܩɾخ[z}u@AymjC=,tG?ʼrЬ# 7h`Kd+E,۾C%@%Cd$Ws~ﶂ0Kc~jZ"ι9&2Ę9jtRsKaVR qsOrބ0-֒y%g}5v)ʊธh8PsH҉)Vޅ=Ѥ;c h˥Ķ8{q<]7fz?W*q/(;L{j\>МzRԺ:ٽg—2\P2<)eIm32t+u٪tXG3IuEtl"^JY#jPNUSDo)vZjy)N}bA9%|FK7'9jM:d֙՚a)Z-‡WVVcbg$6Iicܲ}'L3Qm;ue/XjfP6#}/i.m z9]33jN;]VVڷiwZ!1g}9ԳIly~1-IuN ֥a<Թ yF3-G0<ÝVm%G>j qҾk>QTjKvi&U{ۂ:ہ½In.Hݽŗ-'O4=ur#c* [ܽf6l-8T+Z+f"⅙N ( 9N}>jnIR4Tɽr0!5qukeIe\Q(,IEhNS7@t@ܥeQ#\MRr*fYOٓS5Lu505, ԰\ UhZOSyvA!؍DJݼ{&iW3Ѫt3UɪN3j[JÅGsH3Qt-fkeI}+N0MeJz]!ү Pj@v18UFYa2-e +jnZ2*![SBR=jdf52BMpC`YHѸ 0[' 5xL-]"@ۚMH+\>~u_W{͘-wDS Wg{R!>b[*䴠> KU"T(y}Ɛ&TrgKsxy9aذvf:%1=b[3s*]Dޥw9jjM%#:MKWWEU*f\KR֖UiC"5a=W+- 3u!7m-QͲk{M+U6aߠZaF&;ˣ#+UEȁ>\ewrǢP܋E=9h.>{$g AԪzH.*miO"TYtf: cJ<-覨\v!եMLD-ĕ.BRMl0zXU:!C3_bk꿛lGzSP @(\?y>J ?{//S@XJRHD93#Xy:#웬a0B Tvwd 4[)YԽu)ZH AԜRҶmV(NE _ZTGVuH'!g@ e7MtI. $rјo`yN:wiVN[( c)"\@D4&M2qU0\.tjѫU`@q7 ʙ:p~Z\JPa)QP70")hxZr{AWxA|@VޕX>cݑ72%Aڙ%Ӷ^uca#%*]c(1lcm`ܗ3.r][mEJ aӶ/\/0صC"Xp-H ssq#}4\~*=K0_ : mW4 ֔+̭@kjDO **ؾ҈) I РJHI#ℭ>#LWeΐ܉P :JNy}p0+䱽~%>EvV^ھ ]kICn!JHg$$=j8oijzCQśq9:o_N]Y@ ^• sp潜>IhJbB8U)ZIGxmKsśʐ@F;m8֒Yc`Kד;37 V#JZ<{Χ@3c`OFwP)*N{9e)$~  Ybn\r$6-ZGBPi J@+QTgB E:݉zGBCm' 1<'' |۞;ÆHrݺ{>v97dy%!E :q=Qz&?O fZ]o7 dNA80;$=5\nŎ=Mě}4`hP$d`FA$<6m6dž7Yq+FZNBj狀^JUtgyxGm[>q@l/{2V.QK\~ү8"x} ? 2%_ԇ_({gWIJh 0%ʼH[2c؉,tH l#+{mm.<9Ic-܆S113 lq'nbh"n8L+A_=hДӳO+N䭳h,(-:PȤf}RnnVvaJrjуNa[ ՛IhJ}ijn3jA9R[{AjLĴY ZcӚV6솜F94Р)˟KC]ffGnVB&)Iz cls?>{Kv6纩{!pwJ!e>R@RQ+ΥңXĒ,R!'Mh29 [<֜nÎ͆Cpu(Im9";"1T.H`cc=GәNt$?o pFP`un^i{nӊPڔJQS0ꊫ]HqJe^RfS<7W^LU!yKgnL`cѻUUʊsc3zy%JOX-*eQX[n )'i=](]2Da؈\d2ˬKI7F}GJ)@Z9$њeIᒵ@QpI#iFyUS*9lm%-Zh֨ Dtm]ZG"If3zPL <;"Iof4Bt)CrkV$`sϺiMUI- q AE^F-4BDVˊ!Y0].o>3՜[[↙JPVNߓVY\v"TWEjRʺ 5UЮBiVrQ*O*zm-i Rw(f㖳xa*ݍN3@8Tn"25zG)IڷAu-άձO%rX(Rٸ#oC68/k;vW_ ddQ4g@զHVZZTl.sDeGRj #N39ml(%N2@? Wm(&=@(P#TЪpz4ѽZ-c.O嶗^#)W#= PZN5HO e.2)+qy0(ve*箯zyksM8b4 J9r'֫804oگCδۑ6`x+Turu9H$<~0p@cG+>^uZhӒ78P{JZ#%y(g4Z5MEWN&v8GL%N,N7('p<]bjk3V洪Zwl:\si(8#9w9=é8Enx٣vwUcc1䶇RҞR@G>KV2y٠=#Mj'VF"UvqkTNy'vr@fW]yz?O[t䷨RS D T B3@Yz?FIv{=;Q.NCP#7􍤭$H| Ѽn6u/̑&JaYڞӊ!#*ʀ3R@i]u-mx"1ҷoeօ% q)ʔn`(ZCE k R,䈗52H% ?۵]o%m/w\KmUI$}$Ph۫>t.),%OxT$p;=n .-pmcZdR@*Hv\. 1'JM+gʱ|-Rӷvu0qhԈ {c')<|z;?XO)tZ`)zx_j\ N,HB|aE%'i pco"~ZXSv5q,U-iGkOn{m3ѯ4FWNzS<<``y+ϹMˆ'].uܺQ?|&x^J 8[TVF͸* 21^L9<\ץ| pyruwwCfS.#uH!N9|Ev 4O~ ナS}}^5⭶cwd7 (b>ӓ-V2RA^`dyE}F.Ń? xDx+kz'|}j>0T IBFFIDO>}qCఔGI|`H_mwkT6=eŰJR$ dr&3p8/i8SѴm{5,ZWN8{jl42⊔'.X0ҊӴ⸬q؋=^Z$K}XP \@#2%);KymWupy^lURTBG{ P @(P @(P @(s+*84ިMc!ԏx?2h @(P @(Z#̚BZP @(P @(P @(P @(P @( w'( 5fJ ?I{zUg~Oy&P?dq;[ڐl,pIsd82ʇ|[WH&f*ZRV23UZMmV۔0V}rTG?vOT`$yoqo=+Nf:L C%d\ڗiӪZȜ5xIFFmZ e\ϸ|ZYZB:M/;F [-;+Bk,LD{a8ĥJyeUg+6qInw<rIk1doͷ2N# E'fkX_ZO@94TEhb(jbqJeē+њ$R>qqYzܳ:RNH-]ШulQJ@ 㘩f\Țm`$(6MBOR]SҮRd!aJTyWqMYTfkc#:3LP-N-V~ZryI=6(v5CPJS%%hQ< JYHV78(}˶wvUջ"W %ImiJ9u͢z0?F1JK%vܘە[бW VmՔ[[ ښq[Od!;]Q%npZqJ`6A#5v*MnW.K[/p+jz3M}(Ҷs*guM-Qv>6i *J13v;ikX9'jv"[9ppœJ*j\jp A(j$cɚ4Zjm#_[+/8Og ?Jm nHɲyI'#͟=gSʏm$ \YHV\Ԟdf$Mӥ*9۔6bHdWZL.!AU[-R5D%JiMʶY-VFݿmH;];03{ =Yt>]ZkQXxIh"dY䤥 U OT?gHn3"$ǎ5w)$ gVn1mnppdž#}p[;}EFҁOsjGd9ß#^_ bbbe}OtGeiny>\P @p |ow҄6+澓sx%q{;)41^6-InOM t~oޒǎr;n>`V3q^tj+wqu{xUr{*EʾlP @(P @(6m\x86^=+sx(%e?_YW<>@AymjC=&{tvbVJq-|9E_qfT[zr^xv^pUVm4F5Kals)y{w%jބR֩]eekR&Fݩڴ+R뇠y*\$ d1SO(reemjE[4]LvgeHyl$$~Yg$~qix$5AgȄ;Ѱ20>z)Ң7|!!GgHF"9-0[taE8s>ۑ>q\h;}pKfn]L\vBUo-b)>LKBvor-A%NOQH5Hڍ%1ga=s*+Z#hŽyG+@S 5guvBIuBh#C%2Vp&@"R1Yz[Uo$:(z+'SceIwnBQ")/:]9w wd\^V6'*+ʹiAÌ܇ʰJI:%4ѵ1t"VYc%B_eii$)+%lv\mܹJIV|),MV]Y]+_JJIkdh]Ԕ_JRNݭ3Z+5nV&/PzE4KIǔRY}Һtj2qdr*#Uo+NRn`ߊږl{QbHW͜rdӛYëaGn'n=u)غszҾrJr mHׇgJ^q5'qXpTivKOI%m8#zVGV[rlĈ@t-*IVTkM sڕEdHiQ OJ+HVOYMZyhI`)A8֍JZnj.Ύra8#c X,n ZyԝY\i] Ry$#bYiJ-tY;J+AWvb͐لڭ-e*>[Y=d#T64Fա}(}"1֨!1yǝ}[;JOIFZi6OYK#Vg"Kc$ܕ5 6BJbY-ٮ@i(6 W0}ʴBx݄8*U-+FNدX)v'Zm@$vO*mQ>6mM*lIaRZ Fn&{ )CJ Y9;$C4J*qfs%dԘDN'͓Tse/i\n22Vu%$s8&[XWEdJ3_bk꿛lGzSP @(\?y>J ?{//S@Xcj4Xbqyu+h9)Gq4$'j /\d^ž[ X!v ?vz$::*t_ ¹`oHYa8oa@Anh kMݦ@ÍmeћKm'' 'M@|ux}h6eNv Nx.+6XQǡt n'y~ ~"k-nTY /:rk$i= +.xزS׼});R]swϭ78v:S0㧽HM}N0q1 .O+9֜ㆇֺ6+nMž[Vs`T|9CŚKɟKH<,1-vV΍C}+{zdw ԝJvQ<o>sxoH84*Ϛׅ_\HHu qe#b7y"w؜ZiϝV;z @(x 氕y)2{(Gl '$9$rו.m}G 5kOat-7ҐV)x9@k 8*|Ht7Ib,LjUKdYs5@ N\;WN}u_qn=܆’H-XBG0ܪ+)'<|Fo2mӣi'jԬ$*3.p3ˍިݫ\jpkf-/jqm+/ CW<)(~6=uIhgG^Ԛ^s},8Ϝg{Ǡ›Ă|DŽ0 E߿N@(}ah7}|4e^p2=DG4Ɵ*bB9wI\@BZRR3N2{!@(P#TЪpz4?R=ɠ6P @(jox?2h #h j@(P @(P!\45]Zf ʸFHX\TFA)ݒ0>xzKތڌ֭PN{壶-`Sg-/Vڵ] 2eDwǑui#}I󛼭$2Iy#x/m!zK.6̼\Us'Vo춾zOp6^P"Vlf#Iż=(CPkN"Up =~גucu.\F;z^4kM`Mj *[SV\[@t!ǂY wvr7}.cuk u?Z kg.Vwlmj9r𼴷;A IN8?& rKM[Uvo8*ioJX|ݽmCjhvUgz%VFn8YUS RLUи^р׆XkI{^k}|ƽyjW97ajŷ'[DE< ~8#'p$Rf}KdOefo׃u1[ڮ}Le2V oB0K#(X(6qqW.[i1RRGg~U^uJ#~)_ukeۦxFMT&#"cit1RWER 'ڤ/-.8ls{5xNk{)i^uu;9) b* 5{$:PX]9V1sw_kt]➮՚.V[>clᗑuWF-KqW=C)A[0Qm޳۹M}W9^_#~,{ֶ\t,%i duR.FҞ`]g'u-ײm}BEٷ}r}{^SPd]t}g_q7D~eJP\m!=7a=U RCS 'Þ$n2QoB}.R}ʤ؀34oy$QV]3ЗwŒP)B@QH]dMDF-=~~ ڦg~elHiܪzq[2TysIW}p芲 GnaフV9&kBG2uEeFʣ#vz%6biE:V\E6!-(>5e(֥̹iۑq@v=;/)m9@>zfhv1r[j@颂OB\$!RNCk`HM:t(JFN3WZIMפGgUt9S|Cl)nz5Udk`D-kՖndJ] ӻ!G=M,։.PIZ.w9He%JϤca.IkB㦙-(B`͊5H Uz4" U 2IvE)X}+jF;3]h!c'u^2jVk5mr&/蔻 $hiIKci?a2CnQI氃!HBJ9 m qW$&3)Eg驔$` yqY GᨗZ6`ܡ7!Ĭ/Fs਺0Z>e6#NSD̵if{bZ*_tKO,*DȐ8ɉHJIVq)$=+t$exDޥ2-ɱZ@m(;pt`3ULgD˨HjZ#VjaْP}'p=,(1,YCrSX%)Ѯ-/'+B<ҕlȰe)="d|e^Ek-P>Z]"B)Gypd<1SjAR4Q524Gn"V7ޔGJOR^ˌnJvfEofn${cqJk8*G0+)er?lv DTl¼ϗ;{DkxM<Qy9v]%RKH)ɃQ*[;6g9yi曹:[qiTJZ)5hRf^1nDKe|"ʿr[P$ᇟ<Ң4iitꔷ5%/G[k S3W _U?g?CʞРP @( {QWU@8qiQB@(+iI4ѫ5k, G43/pTQtu:L![i´Egr?<`y­;q !;CrqE( ӑ@T3 nZYqkĔ\,1s_X_>7=9_!]ލA =j!g'4Ɖ|ڊn] ~qxl^릧?c< AZRHF0MӨDqMJwig8ݹ)I~0NG=v 7I>юPk6{uo,EoW[BobpvTJbTaN=pRT֗z6ޏịxZ{NDK椲VB0Tys<\.sʣ}7~:yn7x{n>!Rnr)-ւWy#$Pq$֏9ؼiFw^?7a}{GFQEfdus->1*J ySq"V=qjnmQ NVv\+bG?NbAp bkN{Wn\޴f)R#B)_H $G;O0|]8ďS='<5)\<@QŻG m,ͺ/$! ;@I<@r͏]EN/JJJʣ&N{0AJ6G#Ȃ b28 .sF,v;Ĵ^#'[RN3NY"a`')f]13!L"8J@OzqPpE%4-|^n.|Vܛ{[MMJ;1ikP[R/sp^CnC#*1:Ci.' Κo6mLyNpL1珛+M,k:~sl ^%r @P>zʓ #|#З]q:r}1m*`_S@j4U#Z'Q[t}&,Tw.Ym͊ARU$$`*ͷ,r9W-'Qet^, Pio|X)Ic֏0\oROZE[ȑpZ:V6RJ֔)@`Et[s;æ,K}${1ޖnuhH.ry|EK`v%Z[-pLjo2TIA}ҔܭBw%# l9߰]C눻w%2YX)Jt#rе)E@zNrj"Zqҫݷ`}dɤ{*\+MfxĨEo&V6n9tR߶ߵU^U=3+\%i3fIe^IH+9RU\|H[ռsq/e!ڳ}niDm+nKR]}$ J;J P$}m<'Sg3t^l6}@ Q.`%5O%z{e-7Uz25/`c ۳;;S|w cͦl֋ES-"Y*K!!N}yZ,`yP6O?P/}} jj̕˼?BL~W w!`$q^'=qx,ҦCVn[3^9$>Ih2 RWHR{_/>CxL5o\;KJ4N>S-#Xs-e*?EJz-M%6@}MKµvB[OԜ)+<5GuL,QC:C#z֝I8J+nΘP?a+=:52\4q5;"佶2:cql}kșjV5EH 8*9yVOЎi-l+啹|JF6ryn g=vӌT-Yv]ضq,= kEkrWܮ!JiJMNV*+1͵5Ĵ0%*"jKThT+HioS1laֵ򣅧{ -q6+ZYIÄc^ e) YRI0HnѢhᝁ% |51l*Лb8̾6d}{<&ܘɯCMvY1AIV1z\͙7!%A8 ֈQČBZmN%}5qi5t/q%?t̛4#X^[K}X"l*-ݰ>h纫ul"ΉP 3[d=NemXSr$zj nJ?"DmP9Ik'fnK}͏\bChfj^bFAWm)5ҵT>m]Yo`lF9ھgHRT"Oź!ĐKrF}jPjq˩w_P < IʯVfU Ԣ9pLh3^m zJE+Ud,6rI=nsUߙ-e38ڟSo$|լu7fPmF> i5,,l$$!/6FK 5 Byܖs-$&itOJ`y+4z!Ņp\ m>3WMD5G)IQm$UUU#Hܶg}"^Im| o,%өuK(O/MDꄩS-M: QWVȣ$;K)#*ncDŽ0sζΛ)ڸK9ݐ( N2=$yjIl2Z&89 sQ56ވ3 J+4K+.?PrTD1ғݟQRQkB&ly)*eW4⓺*lOh$%)V>9UA!~q^3` Ck+ 4VYmqnQI{U Cj9A'ƾW>=@(P#TЪpz4P @(Wf [ٲ7!d8_i9| | B{8u%I Qs@ϑ>Aý5xzq0/O8<:մ' T@$N'_F*}Tkj3U9MK*900x瓀Ttӫ$'YޔdS ˈ/jҳe\ostMtA1e *[@獹s!yVk;q6̤]M-wn%^s~w:}+ po;UUwj5tOZ4ͮťd-{%N$g s\VQ[le3mBU_$ޛ5ߺ;D i?*:6҆p6J;,c*Ě|/J]bxm5{;|L[aGvKM}@WGbKrěݿb٭'Kf$V쇖$JQ'H/p'KVp}kყ|nԇR{ JQw>|w*XZ{z,/Y\hgzNڎ=HmDd9?/#q8K| bt/'Y["C_l2ǖN:BJJ'J$SpWgc KemEC|BsJ68~-ьw?8gu9qi:X*L^JHHu;HFI8gκ1,U{ z/ƚ[UR5K^Y8lj- PG#)Ill$ǣS%)`R=m)m2ŪDVs Oy8~B(qv3r%%I J H"8i)w{^፾Nv'DAPSQIG^)Kpt]olO;6ꖆ JO#)$wJ ?{//S@Xs#̚jP @(P @V&иr=֠P @(P @(P @(P @(P feﯡ{MCْ9w}^_~ߡ_^I*N:C? ifCȑ;)Ӱ}qfD#i*ijJoS[IH6eԎq\N27KK: =#TWT~F Q6ϭjZ4&Hyb:_2O5~=^y,pI}Ď;rzL"rzӉK~NV=j)k#oD[ Y>TJOK-h,Ѹ*sqVPc4[)ndjQǴ-zдȷMA(3`/ju;sERSi$ix|%=xFjr+NgT&*$La J@IH.{T,dHkD]a=2 +hS6v)iTWت&f*SemQ5)V촦r~{yh+sRudwQ\Y̹N`J'"-:"{sm|5.5$'9)n݃wۓ:$-$ 8Vz S哂E=xçu؎Iyvn*̎m@8۞N9 Ѳul[DD&ԨKQSS'y2 ^r6㡵,rT!@P/d΍`iI$Y/Im6B b7m Nmsw{!A%7ho7yx$ o՗?mt~下*} 4;JO1߀( IG.Ǫn6ĕie09>LyvۘۥA䴦\H8%*<_3lYbGŦ[o}7* SsVTwyܱZk]GO1:OMM2qCM!-4J@y{VRrnRv[]wX slm¶$('9A<K nzq ->h˅Lw<ԅ% q`( wg gG QZ#~;Œ$:=>4yRr :ݕ)@疼'F;.Iq8=qݴN~vYn;(i%HJ9Wh%'&'m!GmQ>-G %* bxn(/ZWT|yŬN9ġNs”{V2gρכ$lO|qp׻u]g/N*uXs >;W29WF 755w<~;8xp,+ٻLW~^p y+7IqA6͟6T2'88{=^2PqKD~z7pqƜy+UT<{;U ڧDuޞh N2yYbXgt~:>hW -% TmHs5̒sVXNҝ-Ғrw/xbb|Y/2YuNm ReJ-'c!@(_f.)쒜,)HHs~)`E+>z ]',g<:Z]j:lp%}+k*K|4LyOxJ9P @(P @(P @W8jRBÏK:H&ڠP @(P=ɠ4.?\x?95P @(P @(P @(P @(ٷ}r}{^SPd]t}g_q7D~eJPA oPW"*\ͻ 7seFq'OE!e6_'-iB`OvB+(NK $LT 3/a(v$!9|y7TG=uҧ 0}Kۢ~ɋ#{mJQ'Q8F[4g(Dk*PRy匲Mb=jK#8Z2q2&hRy  ed#$")ւ+p rUV:׷UF/r1؅4]0H4Z7kֈO@{ R QvVŎ!($sY&m(ޝ%PPQ;WJ3yjUmA.fDk5BCVT03ϜՔWd6yiEdUu 6amr˨LvwZr)Gn>p^=BQxhƋ]'-$rykZ ϣ]^nm#%[ E >\'<¦ *"1RXDmjWqdYy}e C|2HVLDhfRׇ){h]*rPBZQ}R̗v]j\yQw/-z_ ֭.¦;UqeR6QRғ+tKܹA3:{yI{%j֏W}JIGw[]i(qiyH `nҤן\yv4ZUD*bb;``˸v m!i{}4PŦ3IRZ{-KCm<٨tǼ#diqVUGhf,Z[rw[eWoJI#*GMKdŪz(9WS;<_ݞډ r60+< 0\N|V iO3Uaav*Dm?)Ge 5n٢ئ[YJRT I'u]ͨ?^jw6=h83T^1HR%RO"UVqwKٛuQo[mR[woaZD1=yǔ6{ ]oCN m%M6]<*Ήk@hZZO1Z%T<3⩵)Xxokx "'ѾDIr^̩ Q(_HyV$ *%)ے0{v ${'oB-lؤuM@>rЭV% FWF;\*2f* F^{E 紙6Ki;KOƒ-̕ૻzT@ڧ/ i3$+R;iSKOu8uj'} !qyN{Z!עRrOB9QzNPnUϡOhP @(s+*84ިMcP*I;ǒHG\V]&fm`SR~rRviMkhk4۟Ѿp0y#۸ J$BSHZPaOPT^i_d+P3 6i H{IRY/r[am8ӭrBhldq+nNWG.kE rrK )^Ky+Zn_iZ̵,4LԔIVUA6oɸ>܉ JXґs"$$LS\VUӥV &mڈP8j)Ȕ?Iw! qj+^et`Xԙ n:мסDS{ Vc)ԐӉqa$眏j^,֣1{/w0+7m[e+1]}?+2ZJݷk/Rb,tLF ݁?R֒)I3kWZrF#6Sw*ޤ|r5.#+5s':mSM)=$uyQ")&!0 p:D٤*nб3W4Y*e -TF2+Φ.z#9!n;$zkx4"EBRZ 妈ܽ%@ڽUMN71P^ӂ5m- Kp%%';|5d&Hv|tSnΧmXEJ%l(} Qǜ|>DZbFfbWK .O1U[oRw2tǑm  KySVpa몞$o院3$IϻdlEʒeKR.n*5h'L-bGF k-iFU"tabᝠMDi)np%ڹVޠڃ6D)[FNzE(> i8*ԺT㴨Pjkr[Lղ\Xz::#h;MQ8zH\H)= 5L;Zl~,z72RVg>z\GQ8sɭ).rRPC8>cnFf?*2Z*eי'o3l]-Y= Vm8[LTf9!$8-v"f6Ve-/Ny~tTֺ#ӜBŒٕhl E8<rmTY,rפHOE)C-sd n%=%mZ-ў._Rf7N%*d~\y37l%Rm*RrRH\stZ ne-$sR?%ȑPrT6цm(KJoԐR~\u|MuQ2CiPyZ%nu)dMl('ڢ"V'(?¿x7O*g(P T}P~_T_Ц @(2܆VӨK-%+BRyGP{-O[YGAdhFI' I$I7hW֝OxooKرnknuqA9ɜc#4d=ZlX,LHU!+e(4s'OSH8[oҺѴ!% WF2}4U˅zKvR/,e8ʎCz|PkG ڏWZǗ=B ǣh`nVd =Ru1.!-?:%E%'i dϸBV\|ղ* 0aBS(m`vBAy$Ex|^'8G57EMVxtѷE[^iqRH<ϐ:qSΚiv*P˻iv:($,{N3S="χy$Ҥ"'-@`j ;wmt y ڂ`w_I3Kuyct/ OO]}v/'~iKE@·,lK/P=hJFYs4կz#0qq18(rYW%箯[;g w;6ü,{H!aJP7'8c^e 4}-bp%]B֖;\[w砐YB;=Č`KT= NpV>&P|yU@(P @Sk2OUn&y#sqo,Oo>Xت5ޯߑTxU q32C toP +9Qlw0%30:FXqZFz] @(P @(P @(P @( {QWU@8qiQB@Cݩ~dTP @(P 7~G44P @(P @(P @(P @(P6O?P/}} jj̕˼?BL~W w!V໽˦]C-ϣ&)2rNQ$iz+Jێ)յAĮ>Dž\ucilԹIFJTܕg?%RN QDeJ#!+Oj܊R$}beR!'"yjdt KYZy&7aW#vQ(yj{PYMFm.DmŗI$yO!ZFI82=Ȭ’-ĐI=oS=ɘwH3!P6N?5b4R̨UŽ,NnKTs2f9e\C@ޕ#훖Ģ%Jjv\HI=hԸ_i!ʃE*iJEv!#1N]J%8N^O2jlķ<$z&;&fk?+dJOZp{H1.wTJ#ɋC}rPAjc\DqKkOd$v!6.YKr}OF{e4S` )ۓh""$ZmhG3TwYJہn,6KܡGfZ6j!PyEƠ)T!Lգ[dI@MU HBp\fP^i)b[3wQ!.)?o[3hyb);Y4ݲdQ-l?#ll98>q;rhpq2C4_Uҍ#!^{nD#fZTd< {Jw ۖcǸG=#IR{MtY*Fβ *syB<=.*S-m a@ el{3$0BOy? )21\H] =M6NS^uJr&;3a(qX- ;V0|z|'t8bLϭRV*tB⇧!2KH35GctUڍɎa:dI5 ^  J-#qqZz"Q]KlRyxe /lajVQ #S2#b_t1iU#K*H }rVm=Ȧ_JpNPNH&/g<RpM)"m;+ZR|hK^ڀ$|ij$yJOWJ,NQ%P,P-ZzGD>TI-Yih6L1ѥ`VUϝ]$*6!@#f;9ԯ?diN~tHEmqm^gnR켚LZ#Bk'#]wGRJ@`E J݊[R DJZAJpdZZ*IeZOy{Z㖄DS1a33S7N䝹*$`r*VU.i(n uYن+稊=1BH{:Ʈ<\.IlHlg-yFI$k[?5r+O}Ww/}_*{BP @(+G5Gީ_UyE h P @U4mZӎXرݩhQu`l ?]@FET@]TkUę,, )yϝbmmvaZryh @(^+{q-lW3Shl=׫Fh8u/LX;PJ,nqWgrJ@W~^tcc;i]򺻭]=tpSq 6nR.< VV38)n篇6 oKzcx~5-WcGȌ՞ ڛNBmpvmRPCN-g xs! ӽ"8~'힋G1E.znx ui{Rs*Nԓ찢ˏ.2}sx̜6]_Iܒ2FF2 v6;8jzD J\Q*0'8{.PRoD~Ipry*wT|{GXP @(P @s SV9P5 6 9ÔRGBwV!΀P @(P @(P @(P T}P~_T_ЦjG4P @(MߑMq{@(P @(P @(P @(P @(ͻ_Bښ%nr=*3B'п(U8-Hux4k6F r-Gs]cӍ #'MW,"$%ZtR}kYuiN*ШuNjS#kVe+yJ jQ%ڍ"]z>YP~ Qܓh#"No!~'kKMR)Z3 au+Dd pd|Mymr2ɷʊ0ԂQV4zON5Q¾ o`X`.)}  >V hW{:/)'1TwX[ s;७$\m? < F9 ҥ(Z]}diIԈ 2NjR|hkn]mDt/]t*ՔTGq% 4%+d$t6npV0UWzlZTϺ]l6j5ak ,!#gR[\ZMnbKJ;;{<ǛV7- !zM^QQVbC^Bӄ\b-L:9Tv!4ʅM$ٕlqҮe? Ss4ݕ \5uȳ5@kd{ Qp[%rkN䥷v]$j[!xS >Ji1"^FDpHpZVjVV6+c;wV:4+7ccRF9y4`K"f}R$P\V&ITT-w H7I$dMr&J1H?f{\z֙tJRy>O#-wl`"soRH[)$EYiHuGVyVjiZX IeYqyj4ܫlj <|Yw"xD.P܇"Љo.%k S)7)V奥RtW.Xej}^~kIXNo.lidlkkv*G93ί<۫M Y@)*Q~ѸÕ.:gu mwYH?1?5_xʿY=| @(q$íR@T^?֚:Vl#âN14}oOz?ѠiG4վM?FzɧV4ף_&@C-e9K w&(c9rr;y1_&@W:F̼JD~$̘TM+tIghʔp09 Pq6ϝZ$4`uXr<û,ޭiG4.ּ`L޸A0IRYX1r&[^hglx^4վeeǫ/1/bV3yU,7]U=N ~z;|aʒ? 5> u=zCx88Guѽ8nӗ TՒm: zڒí%mr;T2+%H1',YzVxر6wʼnLx^w%Ip2r纤| 2x2[/V ;ycվM?F:_xh<7Fckm~ 0>G4վM?Fzɧ7./k+Ugu˯MՂo~86 cHr'6[^h}oOz?ѠiG4վM?FzɧV4ףJqXk;[ o TvgͶ{EWcex>Q8&=[^h}oOz?ѠiG4վM?FzɧV4ף'P{Yiq@l/{2V.QK\~ү8"x} ? 2%_ԇ_({gSKC|I)ƃ7aGExhwœW7$Cܤ梮&62.rE}pkƱThK}2L~E@ʒF9VQΑ@`GQO$y֚M&荩,69qTvi* VLݪQOd~ZPf92OXX+eԣZ šŭIKZrwL BhdЭfxdk8HC 1"M.^9mm'qd+d\ЀRKI8ܕѨ3;6tMx" !Au`f$6J7^\SrF[\q+Ji+[w{g'ZqMS.H%Nj=Y&[!!(cvN Q.eJ6j]m!(^N%X-JmBFd\ LFA?f+ɤ~Som } ZlCR'|Zd$2S@dV ټv By# jq\$_Z`Cmg H>U#oR?JaGD#TW^JD+A*>OXI.dH%[Ci}YZ5I%d!9 rJ- ͎t$wVrjMeܜ\n݃ztM|"Dird(?˪3j&ڹqItzd&6.,RA#FT};؍5 S#0- 2;(R*'Q)kBv51B$2{+'VWbFlD)'=(؋wuO6>sTE׭njJm 47KZY*XTr[0yvJ+ [J25]9LŒF# }wફEL(jZ9Vy'*XF5SN 5 Ote)T╵͎ur/QjjnRI3 Yp[eŁwXvFuW .> '~WTse"5kQOyH‘Si>o%k({%f9%N%xX\Vꔅ aEf;N+#q>pjr+l3OJ:$:_J]Jr+-Ы3N!΁TGX*NɢkKK%)Q.tsWUQ1)rX !l!U5Ⴗ^M%+^Gҥw&]i$:BTG+W&ZԜ۹KT52dwޛΧe*zU\2EZC[ _U?g?CʞРP @(J=Bv/Rd @VWXIӜc@W?Ľ* n!u-T@]hP kK&GHMaibCf8E_a@iijº% @(_z/ lA.,e-}@RJAqI8~^mȵxg}!ƣ#KGeH,eC#qIJJH](P 7ǦeϹh5QyrXt+bb Qږ#v ^xuy W | *)}В^I浏][eorE`TXl[FRSqZ Y^xԮFz<`ezF+ 5UF:?zeNf";d Y<2&L@q%we>MCmo;llˑi\9O'{D3zhem[VQ$rWrnnor?U}m;8"4woAZ]^q<"V葻-[3(w!zNVˑ֎H>5oK[1̞(r擏Êy0ڣ>K㸩G*d6}CF3hh$$"#+>sԽVڷCeT'p"̝ \mW-mtQTpݟ-7HhiT@#Tuwytyd)T\q2TRS)9u>LihKcs7%ԗ6% $,wգ(T=|$QvP+[[iСZ{j%niEm2k t]H#J6GɳD:IiSE!YlGAEz푓 m_Sl݃2 XAġ|ݚkfZ] &*F(}f#P[ CǺ*bՙo5.Vhw5#VUؖ]ZnOiDs$y}YfZ2LQϛkP .269%5t!n`Jp#Vd+F7%D)sB {{3HB[-j:$Ζ̾5.sإs>_#hx# yxT쬧hSa[gs]Jni3-nm )w6-Cf*Eu+ZqPʓ>jZܓMf%KQJ"RJlj#hsiTO*"weԩr%"!''* &+ajn!eԖ]C=1%£?Te'Vm[GW-JÄGsSZ:AUtQ,WA1?5_xʿY=| @(W5vA;f2#عGHSZ RATB<(:WL۴VC- !Z@B% PP @(5_}S&FnjXij\s4VeLVJ $@(POW-NLȨʒ_eđȤ ٸ7cA!tE6i@i4H 'fJZP @(g!:W-dWMLĘ\)ԐT"4 aNaCp)íր̌g=u\'spR$<%!-q@l/{2V.QK\~ү8"x} ? 3{Kگ'O&B'Z,2+z7g7g0~Pϊt8#K_c\#-[nw!I Y)Ӳ a-(V֞?GW*жa?0XFxqnqsd2hChڒSyQzS(DLYP*dVQW;6GB>%_ՊkI>r'Nw[8xQ-[mj鸠_m]; Y#;|HOH93֫u*[!(mm-F޷\ӄ1qT_\L!PBRSLm>VƋFԉ y)TCLR=Nc'vKo?:jPG~t9])$d|lxp$k)ӸEl9tq#.sXVSDY 2WRq4,nOdrqi r%,Xy5 m=#5:Cz1ěC]JcL4%%D;6v_3)kSKK뺌w޳Il|Qs?մ+XAQst:{:s~WdFFn N-;뇤u'A&?b7⊜$疝[V̿ 0+?Yd(cj4/֨ȳyb9⚔4ZEGKja$?nJek2z6nRZw UbH-QJR4?Nca1ryzUj•~ƶ|K,!O=k䨢Wc)n*s[(cVG]\=;u+EC7qGbI2,ƚ~UsO_ 6S`% 侭>NZY[=M3)s$`SY9?bǢy _:D}M?*נM/3!=m\# q,m:W^svzܐSr=MugOdKq8`icKf[Q[$#Ƽ4C28=&?qvcɉJH& wqR![oMYOiz*#mZg Q*9zCf_s0ʕĐ}OUH=O 5I Z}jO`T̹ k=LR 9?_"SFc +?lĥ }[Z'18F<*0ؿo[itȽ<?VtKg~(Zp&w[m~mΔ8)̂Us7̲ĮBw_k/횔d`Sy=gq- KDUsCþd+l80(-%$+N]&sDc}1Ma ?/%ΏwZVquW"#7#ZС1?5_xʿY=| @(P @(P @(P @(P @(8ֳh Dn7ewW"ܩm0TFzTO7SȸkmUk: R$1~\i :ѭQ]$<Ils€5ƍGd>S7}YmI6JepJBԂ[QABqo[iۜ!k9XJLaњr"r8P߰ݗZխZ-1f˵L]1-ĺ䤩ʆz909׫תNSTHӦSI*PYoO"pg^w :dX/ )qÄ'x!݁ @(P @(P @(P @(?R=ɠ6P @(jox?2h #h j@(P @(P @(P @(P @(6m\x86^=+sx(6x>i"6?_YW<>ֺ_PB+PW @:_t+"WE~ Я_(B+PW @:_t+"WE~ Я_(B+PW @:_t+"WE~ Я_(B+PW @:_t+"WE~ Я_(B+PW @:_t+"WE~ Я_(B+PW @:_t+"WE~ Я_(B+PW @:_t+"WE~ Я_(B+PW @:_t+"WE~ Я_(B+PW @:_t+"WE~ Я_(B+PW @ru$}By~7O*g(P @(P @(P @(P @(P+lK;L VgAڙ~4Í:چ$ F\0E2xqgrBe,qzJT[I @Q(#NDDt1`ja-m!jۺ2pGb2읩P{z?IBǥ,Xp%.tHs,7BSKy !ێ `I wt~ 朴E2-uy J3n:7Te)*~Я+m٥,nc,vն%JfLg$a=8#Aot}MZۙ IR@J*Q*8r'P @(P @(P @(P @Cݩ~dTP @(P 7~G44P @(P @(P @(P @(P6O?P/}} jj̕|m.JBLλ RžI?BL~'KnQPlj>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-Fj{>ۿo'KnQx-FjY„;1$M 꿛lGq[W (1\d^[BJI'QIH5 /*nReEt.ڎ}iɺR/҄DZRH=YI6WLMHt>a"[EGqHOR =  Kv}E[}_[_Nj.bc{|x݂&fJH JPZ%'4Ko}ߗ?i_#5&wnuk;Ԭro7VVN ǽ^uR&3ũt)cyJSҷ+9ӫ"/ou|wuDr-<ޥ+%BP9Fjiۍj־r/DHg8סէMT"cTn$)ߑ{XZeysܛ~ԓnZ{Ż=O6)L3&@}rmƒ|F}XjOGRuRW>I_ozjYyyK6چ.m9Fb\M!IS[տjTm!XŴ:QI:j_c'ҼRLJ .IZUW2zfb~z*4tUd=`$RkOk2/ݻl:r}]-VΖ/e)kVԬXJTp'5tYk"CcoYRBvS5-TFm}ֺumJBHFrJ*RJIݵI{Q/M/%Poy_6?T6M^krwJ5IwyE.c$4{_R>0~VʤȲVnlպv}WHN#e%mj2$FykIEN/tFv׶MZE~vS"Rܹ;m^ lsaWFQ<5'Z^krǵ{R9Y@~oi_#,zը{\ ?Nj6> X7c 9'6tj?'-+ SҿY@~}K y_*v`ERȼ0qDcuy\q8!PXP(W6.h˾p^v'+iuBW(ۍl Z]ډyyy8f.wڡ0!.魮0Vq#H*ʻ>߶}\k}HcZٽ(OLV}IJ^Ucb^3zSR)casާ -45ګ%p߶oYPI]l5 \7چ4*q@;(JT!:~SwoWb:QMTǁyRCr8mn o~7.mb]X]ނ#o]en-%B6VWtYCcݿ+e@o_+e~\u~Y@GjAkҶ\n{haX. u 60n gNiv=}?R^W(Խ~W/l.ߕ~}K y_R^W(Խ~W/l.ߕ~}K y_R^W(Խ~W/l.ߕ~iEoaqёTjQ$&P @(P @( ~dGP @(P @(P @(P @(P @l۾>q@l/{2V>^:}jd:k=*3B'п:?_({cǟΠbcK[u>D6OiWK`2SoIiC)CJNLb^$5w=6JUg/М فPiBMܱIjiɊJb;L_['kNyz|ϒ3JVr7~vƞ}]=.Z,-VrZDq!a"[dkڛi<;<[k‘ڸqqԺ~@\Rd9 hV%Y<}UZzvW}8z~/7 j Q/)[F3f_ TvM? ye.گk7u Uީ=f֟tQSҺҟ< %=m3w-yiӓ]?A*.1?=Szj-U"SE~gіʖTRd9#hʫƶz=seowoZ^ҵy]W/6)k~CmgXI^D zu㒕T&kv{< "Qc5ym u3Sz=,3vIڄ$)H7(FIW3h[ RKipR*k s:RU|8 ˄,Vp%IRRջ ?k^f&sk~E߆brҳM}ta뀇#!+-G˭:6@Q߼D&כkݛ؆Uޟ5׍'*lnb5$[R zw@[Hu$ Vj.pI{?/J_Z%ۿ 5tB̗bI*qvrR]KmJI!"НEu:Q}ړ޺SJk~_}i*dֶ vv\F%O@ *h!n UfMnZOUU6gW;wd_$]\C5Ln "L!m,n(}J;H{C{Al@i*qkMk^MWjɲm86UUc܉kӚMϡҲ#i C2C )I-#~bw%W5}ׂИ}k~|Iuc雪dK@=zyJ)LV#$eY9QPȡx+if!+LXnO;k´Ƒ<&QlJvHTKyv֦kKzv}3:%/ 8wE.VISgh░/'qBQkN_7Us:%ɤ䞫-x/rs,Bվ 3˛:z2-6S:e)HRK[׷ԩUԛQUVkaUvS ޑHyH$͐ |_ZU WkEj%Iw֤;kWu:F 1,%.q JJ+T)-'Uv%֔ͯW֮8_-ȴQe..rLᎯGkpi:nLI^YCJ‡4R e#U~_}YwKֆyʽxSoT2S7VJAlNRVkޟxU-?*k%unN..h<EYFdGUu(uԯvYKJ[^MmZkݧBl7ۭoE^ݭ<`f/W[V̭n 8”PG*i5'~ˬ}w$WpoZڸZrADqbD{ Oh1\5WMeKk|ɝOk_)#Z,v;W; \"Ϟ'8'm [ѧA\6C1na7 YZH%Iܭ([}ueUaW54;m}EWoKk&cI퐌drX#ᦏmZz;>Ұm~EBybޗ6,Km۱t%Sh.$oߑ[”e/9UᦆČwݵY`=1qE[6'"Ci=OFhJ)-'hI^aZ촴ݧy^ Gm4ŲYMxq ?P܆ih[]Fa)NfrĔd9?))'JJ1q\W/aTa JF3k"T8 lU'BRHZiɿ{z/r/NYN=m?{mjiF}ִ8˸H;Ir{pK_U>qd:% OI#RHzK.5%%I.)JJ)Gg$ ,R}Wz{)Q/FJi:໤zl!H~*ԡ="Be]Pq?T-ǹܯҶ,]?*U}{Bp&Sf A U7{r%NG &)*a0I}l@ RA5>J)[}w:S-;~\tRCyjCiX!$, b2|Ij}/:j^2m?c]* VWl*nFlTtPG_Z\Xm\ +a#8__Wz굜NsKEV3T_5$T_4E@=Q8T_4E@=Q8T_4ZGrTF;%DdIyMnːu HKJ}C= @(P @( ~dGP @(P @(P @(P @(P @l۾>q@l.{{O8 ?n䮬:_FRcv;JǡO5iIoe|\[IS\L'*i?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۈ;>z|]O %S\LT?_ۉ{Tc{[pЪRR[V*qլDsP @(P=ɠ#nٝK(uJu Z <7a>z#@:~0S؏=oMb?|Gu7a>z#@:~0S؏=oMb?|Gu7a>z#@:~0S؏=oMb?|Gu7a>z#@:~0S؏=oMb?|Gu7a>z#@:~0S؏= 2pco,c9+w[GyP\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~J|chW>s@=RϴT}?Fzs>14+h9Ѡ\Aό~>S:=XeNe H*ZI' ~Ǐ2c]P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(?d;q;[bglueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.11/plugin_geospatial.jpg0000644000175000017500000153501713502206677025257 0ustar noahfxnoahfxJFIF88C     C   \ f !1AQa"q2 #BRSU78TVtv$3br%4CDe&fs5EduG!1A"Qaq2#BRS5br46s3$C%T& ?S,Bu.'Yq;i r֗֋%QYLBZ[q$D*XΫ&K^/%BYqZi_zQFBy6Z|Σ~kɃVfOTvH X5DGRmdKȚ3Ѳ*SG;896cv)]7S&(ڏ[s}}ig"5UgN7o1i~AD QjWi۽qz9 g]x).ʾh4ǫ4)\ǁvZzz=zG۩ַ𾃤_Zh(@S8al/\QCWVKq`.xW!vw׭-cme݅<^ToO[e&u|%|1? ZkkjRIVt6y7w˜csJ{쑇[Af/U\kO .">MR=2{c昆iH:o>FG#AUꗜu'#jdE >F[ }F:wPqujM\-w'iBh\E}ew7$S캑"٬zmvRꞳMJYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY}g΄._鏀nմ`9|'~k #/Me>q^/.Z=adknCݤ0CҡvCk؂~GnŴKɜpw kaX7솊`٣zy[V3[3m =AG>&$^֡R-nxIVHY[4`A0h+o9K~շ7Iv ?zfwoD%oAߚF_ok6%: S:hs/h{ zͪ%ěN샴P;x(WޕKZ= P?FG61?kv::|9R&,( Ai ψk`ZהmOw(k^̶K%/8I/EBn,5Am&{t:~ ?G:M^]jn7ڌZBZ[O2KוIcN.+zϝ5=B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!B>t!s}gpƳyjZ4]Q?%tP|916hX~k&^ o}Uѥcc+Y"4VՎ~h3q?t~/գc+?L$~\_GUW?4~?K?W?Fp?ѥ~u_}G+]KhX8?W?Fp?ѥ~u_}G/Epi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp?ѥ~u_}GEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:Gѥ~u_}G+]KhX8?W?Fp??t~/գc+?L$~\_GUW?4~?H"4VՎ~h3qEpi~W_xg#?GZ?V:W|_\H5yH!FO6tY}?#>C)wf'Y_J?M>}~e[=jnLq[Ziax>mGfh;GvNC}/\r??>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o(?6Nt )CbGr?>o-UbF|<>{Qx}'մWt?V54|`Gi~=kh?>VS_Z>{Qx}ҿ|+tc͌Gr?>o+߹NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#OPi_Aܯ換Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>NC}/Gr?>o#SP_Aܯ換:Wb|+>8{=kh?>Οgz V6QF1D?gu͖I~NJI97谋άCOo_J ?+?uJN]>_]_fFy] QK G1Ѕ΄.-.a,4+7rj2ʮ'Gc='7tOI-e?zuɣoG_`yj\,/> 11J(t;:΄/#}f&:erlQYiYCa֏qC9> >VeG<#e_\S~4eǥ؏`:U >s~k]>𶪖گ i}U=\r9tntSIȌXeVɱ'^!N#3V Q[GrǚP]DDx-Xt";:oH>tOFPy-a5U1;P΃?gRq 1}7msBጄy9r'd=կ6wYqn(i 8G:?I|cS ld82amF+xT:H`zkEvppsNGoBЅȽ$zWjiMCEX,P  N5:_Gw\Omn7zS@8@z3kbV|.u]s?f:xY4qWqˎL?ocdɍ.ϧu~)躟oYyGoBЅzca苁/²L ˼VvLQn&W}_dCzbۋbXӊ4ZePO>̊<6v}t鏽w^βV;:|3s_(B{jڄ# hЫKh I "s?谰Vh=Qӽ"A~t3a(^ 2mveh4ؤ1UZe#8Mg宇c-s[r>$a-^=3>gKymp.۰x H oOdÝ`Guu#BnQxyUi@.48Tg7os%azF46QN JwpDP\iZ|=^.U:.w_qoi7c>vIpA1HkЄvt!i^$\2Y-55{ukg< |_'B9}7zS Glt&XkSs܏@!O*ʔ th!>c h`n)}:A*i-{(ձ#|9ձ>TuH\So8Sx>-5tٸS Jqܓ(>CY#QYDzz}ɣnuZayI!QD-(Ѕ௲oK\k}(1PsgcDN{spڽӏ^Z^+=ذ ?WkyyvwprGϸԱyJQl mG}Eѯ .-qgJ_x- PI8(dpY+P5 wZ$md}F##Ws ^3oP-yc[Z1[Br#Tߔ;KJڇG<&YFղѧT%@c$LwHo2V;k\Iǚ1ni/Eu7U+ٚa.rV&8d=|qč[se/Mvuή\ӧu '٨2ZAU(|iJ2|tW3wm#'6Oyb"e8!EҼY'uo{8>f[_PYR6`I94pdbZw΍AXK%g('W?sw+̂-4} z|-k|5cBG$(=9uHsrqs`@$US~ծ Awq/[[_]Xzck,see 1Z53 w߲RCvZN}A.%USݞkeɗ6+khY1,lPy_kk]An3<9gMk[յh:}^!͹1{2ؾyu1Xp{% CHmGWJ{F*ȭ݂GwJQBq|yVwFrdO+st}nkiqXpbA7\sP* 1 3q1cn#~R?:}Fy49 8 oυ\?}g4P`X+u!'<`u(3zl]W5Goo/U]96po:b΄.?V^u%k(B{I%}Jq..Vs/mC>Nrsq1RZvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!GoBЄvt!r&}e΂E'n_\Bd=4Y/Z Ir#]7X,,kK6H`9%tJZxP_Ewޗ. ;LŅEṂ9 Vç u.;:%uM'Lptk;7Exb(sIgճ `JQl㹓^Н1u!@ہW He Z4RAg緎EhˊLr2akZgN]h];xo#BԭΫ.j37vqar,y8ztQc7Tru9W]k2;zU4ⒺAx k*O0x'H {vbBqSM$SFʾWe53]l Q=:sH7Bd0G<B˖3_Y=͏G,?Ym<3%z6/ui"i;Hd@qH45 rɡ_ϧFqݲIrסsq :Df,(EzQЅObq G7:wR}y?Ñu(AU./y\4N|!k~>ÛcM>`[RݝܪhzfU]wCk^>ˏL9yEťkW1<M9#ƬeWݪ,Fd48= ƪvJ),)sqZ^`Y=5v7Au4w~ m-ck;n1=*dܵZ*ojrdz՝UguNN^sVaep?ڷ^ {/ilX0RN:Ԉ~srH[$Wx$ָ۠-1=ϵd,ҒK K_txgG+nEi.=%祧zy6;YÓDQk zuNO6v`Ki|-wUrdG8XW uKZxZ[z^ut~Nd9 Hx^g(-4e:^ՌZ/Wa^Qz_]Mi|3Vw}-^}4ze; /^QϯIO;#i; /e?C 1WۓDWn x{#!< BQFk;{}b%}WK!C) W/qƺvdl-tuem#>f4Rc2zMy8ŪW}_ 꺭_@LVWh :U_`gs{ƾ[Gt"k]y& ՚ܬoc*68OӞ xqaX?+zD, Xʒ3wNWB%ioD$`a#mSt.i_ױ<>޼}Xބ-k?7&ȽAV3ҡyd$Ooq&ۦ DCtEz|Q97pL_ Fz ֽ(`6P>Kr޼z;zބ/0e )bXGZ<"qɘn!{')0AQe[+S4B{!RɤmpǪ^[552$2q&A?󌟑}C`n5 ehK Wɑq5`fdvFgigWט,ay/w+/L|!q5B{ҜƊh&K$.%l;^޼A}ބ.1^v~/_uM,hB6=*ba5:jp^u0k\殮_K~եﱖf=[^?t^B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#B;zބ#BЏh~ ?ҷz^O{蚾6b=@pNOZWD* I >B ^ێ}3D?n9ҤTV2fuRB\O'^ tiR #lzGyoYC^7յIBt!|,5=6y=j%̀ SEv]=~;@;6<w 꾉 69_[n\ʻ{5+o3iw.oz=ՒK 1ʐyqi]'O5{Xcw%1BCb|!ngmx7BCŵ1I9*ʀ3srWc4W~Pt!y{QoGX6OG}t];G' @<8u&{֍Y20)sҼ8W^ ΐA?x-V>3H w*1XL2(ʷgމ}*(tWW$`!}àed;"MEz0at:t!C,-5.Ng6F =,psTrFXX ?B|Ii--մwhdQoc<^=~O͋!|Bڹwt1[(eibdnd}^^eMe>&;.gtxU]3@Ć9nq(^Oy5XƖGzGBG & Gm qDQUE@WI&EXJX:IoķLj#em; ,) $1@;C+,ɴLz<]=c΄*z7ZNomY]—R ʺNkqgո{n^(3ZXZ+S@G F@mC9zh|YC7=czO-|++K/Hb! >5,-O< xԿ1ml>N>Pu&RwBoqsIGvrvpA?(:~'3Ȝv84J*+n%zǝGyЅŐ|zE2WrL=H&4 _C {ߔ;QsxCG?ԽCOIhWS{bGO%}8 ~HB=J u`=~oَf䍲 .\Ng8Kല]޴⇏Zo ~~+ ʳ7rOU "ʰZ&;o>p[ߧԩ[:~AI~jӆ=h|)jwz劬#!ʐrT>Qg@yy ]#7+2yXev(:=xsW놊RrO!s"(A:՚6SLiqi)FށnxR9ـݞgoa3txƾ*15uzеmZEkgV EB\<8W`:jTzǝGyЅkSAV 1%O4ytpA-lnт/Y Mo ីJo/Bqws"ha㟎k~n7 L.O|с099I\Eqh@vLΑ#{ ecj]@: M!sjɟ|kνowzg:rX }ހ)&1^v p2yM^#nFr- hpgI?*/ ;!I5*;:΄#BKȲ#O0G#E,xwMOm1 *O{#8Qbhct";:>΄$>YUL,kp~?(T ?bOcT _>ĸEF) 䧵gx~ljiZ΄#)S:PhC8n2=s{kf|L k[Ck@ߓ+_bO@5,A*̸~2ϳҒoycpmc0Bt ۑz.lPr΄#B=`Єvt!GoBЄvt!E;D џ'R:΄#B;:΄#:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv  Ч! P{j?H*M#B?6gڏ?ʄ#{j?H*.}j(2.?ʄ)yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!yv˵?GGE]'B>:/O?BjI*SOP}t_ڟʄ##.T!qOdX΃$}͒e+`:vWctZ:^O{mq+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE^mOm7/.uO?LӠֿ?tܟWk](B(B(B(B(B(B(B(B(B(B(B(B(Bkn5WYp-mRi chBBBBBBBBBBBBBBBBBBBBB 6Z%=R' 6 e e^˓(BBBBBBBBBBBBBBBBBBBBB }Mo4oS9n-@e; BiZ^Z\$ ̠р# }EEEEEEEEEEEEEEEEEEX$($̓B]ӸJ5>ճ@  9`)!!!!!!!!!!!!!!!!!!!@5;t-Z :́#L$$)hBEEEEEEEEEEEEEEEEEEP5M{M%PKkjH1LbBƟ7/n_\;8 ~A壭?F$PPPPPPPPPP{z/iͬY 4!%f v!QK 7H{ Mq ^%O!uQ9.?aڰϽ$>&D9?~دۺI?Y`ޕ$l{(>_sIgN!ָfV%մ֏Ji7L?㛐W'XEϾ.|y#cCӨHR+ gpӃP'K/Ek[upi5d|>e.0$7Tg`H>VLGϿ+9bpb7Fpq[ihWAϬ>TէF_#x{]#G?_n-eb.8/+| ?YwV "LVnM,M8u@jwz|>4?Բ54 ^Gz|,M0J[!9n{nZE@6FGsǢJ}O3_,fz'DŽdH!qh=?HlO@4IGP]h|dp%Ӥ7pxF=:Ή5[+:B=HU֫6ԭ<k"qZdC/hr4U{}>$eP gi3J޷bUc{2qN? dLdw; _؋Ԙ?lS\n&=^WEz[z.^Ci5E>m'4 g,ӺoF?縍8/UxvIg2n8,Qm֏z,I־nIc~&~ۂ?Yc05yNgܾ3qk<lQ Osid%f'~MJU:?xhu0Esyceo<4s7oŦơOB\pQ-bi}8::y{ߏoU"OA@KqIg85۵_i %r{^ ^'ՙz#fVe1Z[M:eZhs}Or?ŰKhM<$ep4?H_Yn nw`h4ӖA)&>ُ[9 4xʻS%lm\Gzx~ nx$h5:~;[_kte>?y'| >inïOn{;!2K ~_"K@Ur/OSavѧ;aK[fMٟ@`=bjf ~4F$ $Ս /og5ZN8*GFAw.K{ bD:$ޠK\,|7gc7tӆ8pھmkmtMAcg$XncEoo܅ptrN&U;Md~820EYsFiTlJs',fޠ~TlO1+I{V9y:aձSƧ$7j2*}oXRORtIhrcFa|dЅ|i-6\_4E#_q=oϏY(UtY?M+zkGޚ#_ϧw$q ?/R;5syZy8QeK9!h%F_Raޟт%LT.9h4oL,?sW`d K%ϑjH:g4IEeA0!%0|c==b;-P}?ls[ ]΋8y'- ϽD}O^ [qj9~(Հ@ŏ8}3/,g>t֔=7"hݐ,mW~ի}_)q,8'QC{zL0 WcwЀw׽[y߂ѧ?ik*H_U8X>_qi 7-ChMUwWcϽqcj5ƚ=LF5P;+Q.;zxi.-* j'tHبa=pxi8*d]pq:ե^!'?3'7mzЛF(Pqp0bs?(::tvM-QdWx$s&>~GF'b^.4GWCRu ӣt3S0NGl#tB@|Q~K?f29$KhvΪu\jVд?N>Lޮ +dOBŊ{dbdL*Oҍ樃U'+#8l4YQ*wZ1bGBHhv. |Bk1 ~yѝ&pG<?96P cLJi&"vܴ/)A0$'[]v/":!v+9xzv5 SLҮWB13\fcZkV\ ɟzwBps\EjP[6ѽ>]ܩҦNF6{rdtmoקse-feuZԼR?ϢzxEsR5$uE#Rv/x=?!#K9 Vƞx&X-_3l\7}=Tԏa=<!jG?w\R5$M;O TL<~eWO h֜*fG ]iҼg;rǺl'<?}ԝz|9NƗ7!\՞UapoH=Rۂ1.|K "F;һz'!?Xiը6V#l{x-{ھӖmP N C#ҧ8SȡjvZOLU܃K~_c KxVDVHkm[3#!IEj U%Q˃ϩ/} ˃ vƐaK+ nzG^pv7}:Z'o-Fಳ)eۧLN3c9Tmpq vW'Ɠ8}TM[)a=<"JT#?Ox;oDjG?pw\%a}a<!k+;n;?> pa};grx7X/=u=9Űo+ ҦVSGqB̘KX#҈>Y1l7<7}J?&ϡ;ɿk?xCݓЍKzxCs5#oOx;oDZ?&ϡa<!ɿQh>jGޝw\m;k}>^L"N$ Kdp`㲳6\ ?r/NFќxWw~R[Um̆C]z꽾ۄ9k^Wm_f=c/ɍKW>boOx;rh?ncsЋZ}L<ީo±?jZtʎxW܆qΐ=/xkmCzyyB%9' K't,kK5hW.?$+iSx}c=7±LuGi}DmI_}?_/EzFƟ7/n_\;8 ~A壭?F$PPPPPPPPPPMqStW+L+綑oq2JT8lWL  vovy*tl̢7 nEi6Q*z5K%6PJjHG,.1WFtg+ DqQw\Q[giQB9\3ȨWeem F u{NuyVݣna0cp$f[a&␼d[*C$C]OJ`{$RɆYa}U}dځGV5LeVcp"!UߒZ~[u/!)wR- &P0 rsP>KGG};G ʧ{/ idžܥSMlnfCMPH&׵w^BGj+Aϖ0}.<3|Hż6uiӍ$jɉҳ]u #&1}C#_Iv]wDVھ),tiw˦Ǩ*`l&eM84ipolʍķMK%ک抜EL0H <'cfD[' آ m{$ƥkG?㱍amױPoua=e}jVG"9upCkbl=ȡ}=eͣ\Gvsru^-s|u߉b}ꕧka=NT) IʑsMa8x7(WvIPNWLAv-Ncq!N>zΑBt@Sun]%{ xճ[ -h96i,&+L8".8_{ $aUwgΫݮ!^Aiv*4ԠJG؅sUQ Ex;)9YR`򶴗*q! %9PN=$,(.qR i7"jS{+m*p*i'r!Mys!Dmqƻ\j<ډ6!ĴHI&uSljSA捻CXZ޷7{eY{+Yd#_Wcy%v欁ky Grc KWuV* q D2j'=mSbaG`j[ۺ0GD\or`OMfIx%;1cN^ axpy~H>c9.4*;mجyn5ycaԏ sqWUv 7mo[oT| -{9 ;7Cpq)_0<دak;6>離8*#H S2$~%nT1qo@j&dP %\a@zve(zCݚhʉ@zy tit*8O͞Nz֦XǝŭW8D,g${ 00sַk:6UwگMhn-`Bߖg5.vml25ktE>F% j{]7v^%MowOuIV9J9qVY÷!UkXu,,`ΊNE^YKe3E4ez1Wa:r#,w*~k m`&X',#luǺlsrKt8eFƟ7/n_\;8 ~A壭?F$PPPPPPPPPP>9)>N?S]J8ΫaߝS.;5ƣ#2 Uܧ6<+!u,w)KQk 2IZ#!rHrI:yP7wMf'N+pKV[J{lr}q=+"^Եqfp"¹|}uλ J܇p=<ΑΖB p1 [qqyW&S2. 9p|My u[oc\_ò@P$dR𥈱;OdTo#ѶJ%[3dE&DL%GBϹ[qy]C#P3$0\~c2NӒTNCS͛cFN}{km-gt쪳ND7-sjZ4N47RX@1X]&-lA=Dz$D)֢]YV'$F |@W\>g$H>hߢezd4lRC 5^\]4 /uH}9w-;q>emH Πo_rbiUi~J8f8kOZL p1^u^G] r%ƐdDs#؍r;k($oCΥƕr9$'Y0K&K3cPT%2(|z\w?f8ZpuG#G>uJ ~k8A sp _-kEӮl.gIrO/:tL>j߲|=J2dY>죹 B>ЌbFGdf61TH>O/޳\ZTqGM7'eZi"-k"eW{6>mAUZ*Qd!G1RtT[Q-o$X hmgsi *ֺ+-W3z|Z*B7H$R G?ǓSt"; P^ZwVޏKI#FC*fE#_z@_N|ۺG5Z)7f *dF݊*?q[&ԙ5fB>X)V N: $q犾=ۋ-ۗ HxVyn21tcKKg[Z~셸$nzIJW`8H:Lm *͝wU2:s ga;P`4zI6wEǍSsh8 :] {pݹĮA0zXh#bo]"hYei{W&(጖j>z@Jd܀U=jE]26Aoj[ڞ̌nMYd-p W%Ɠ9,GnݱTDs'tZaeW;'%^kI=oIȽh)c2Iigfqsrlf̭ <>4 )+]AtKbqեWJ#>l&HA.J9x`Ewuޠ> i$ aIAFM"h\zYp:eRLҵYfʀ'}!.`. lC$koY qڮǪYOR x`f1HD쥏"OC]NY쭸nY-cK{t,cc Z-6TGḒ4,a9Ueҟd0ˬѮrS72joFMcL1dv{k\P#a$rPBM1u\UۛT:.1fhw W&޿]Ɖ/VG+qCHkRgYr yO F7Uʘŗ<q+Wj\*95^$N. n5SG<*ْ c>[tB#`9:֡-H+*zNwNc1dmSո=鹲 J) fs~Ha`Ե)bνt,b_rGnJctgK@ &i͞o4ߺzؘaI  J#+:f1.ʾ.;[})ZƋg[ Zɐ^cIuFJTK wryo9-njlm}3c?_':kCCHڮUiέi5h&֐7lcs5##~W?ZW2ִnn@X6J-?g'04+,ޒo?yK(xĒD[x領09(dtx|= 9R -Q E`$5r(ؕs6y To~'|ySk{ Д$_G}$$VdjN3JE 8<9o_#8lQO2^~J177$ɥ4vsR kݕAUds:lM:i~~,Nq[b h^4Mg*V9mǺ*XJ^^4֓{ jk)@xgNs aWʟQ63/phz:r;"|f!}4J#\4Ml/Hi *`$ 4bO\7Ϩ-̦hfL)$b(G:Ŧڕݿ-.HRHhBGM6q0sW(>5DW*m Di۸xW͟D $x-,hg?NX7O,~krǐgg2݊{R,-8嶎-Ɏ9yй:j2b׵qoߔՂoC^fsWS^A|7P\zD RPtυ:|ߜǡj4W]\4d|K㼊HD.x1͈+5k-la۷iqmT;S ht&MT`rV'[Zv*cjZ4V%*S2G!=yдx`ҡTLdU 0ײ;WHxh}pXSfJ(XbTkѣ^u㻊V;O&'V@s_=NJAl_8=`2r?(u)'917co^0ea26iQ&Mw693hEF-’Gʎ\ɎZMнl;) Uy1LeH Llf~TuhQBU~6)_vne);Cl+XIZߡD9VPR@BݢpT17!ؤ\"6;ۺ Zyʥ`PS[{Lc,.1 I ^.'`: 84♚ =Zv*78qul2unM-l6,1h}A{YcRTR'έɤʟHqv,.a4wO}6HY-I;7$6(6kT:Y0M5.>{"kl:fPD$Aq N-i q mnYGd/UgH t231nn|䍶R2Yz/ MVᲢImEo["(] =k`k5 ]TmK۪dc#1{@j\^@Ɏ6X5q{Ej7G7T.㟖1X.Q`xnL>X^mUX.\٦1;@ o~.fT僟}NZʆh)A ;`)dyRѺӃ.IO]0^_i?")[L:N#֗N z_@Z6 NIBމ>6B,Q7}HI?ƘD+~$t8 =Bip0X`>U~, oGDq\L@H =㿲IʝNMaˈJO}a6V1J737^]EU%ܬl7aq>r[7oRv@fdn5$n`UYiIiy2gP+I S;1r]F:;“,zYPs+ dwTL&'yĪs})*Y$2 3ܘù+riyP5j$[kdRM;\%s;hVd)o]D1/ZA 5އd8^W`ĩ@ESF٥; _-amg$@̌A]679/<1mmE&nΚvTwWq,h5b)dk c,InG=Zkxc7&)'mf$}|TRqRvP̍C #4Ƹ;fxZA~Gdi>MN4vQ l ,ZF9bGSMkC’|rtq-,dvmyI @]|Rɐ2Ai'PNǵ>)QNN3,R1۷<)$"B_oUpɧ*i{s=`eUo- Dd=JrIO^0xH|"vVclT\Z[`՟|Li[pgB!xpʹ>mE&%P0sQ=3Jqs7SnmJ P>*oDZiI-- j$ȦG1S ZtnB9b@־Q atˍݛ&3njD02Q'QESvBRPRHyNh4$YAqBBxc6NEu4 rE0DSr3M,kqeO O7֠ƷEbݭأ0agd@i<7NFzj"y"KBwߝ88V1 =di?ŷ?-䁌!U2c鏞vĩà vۤUQߛ ?O}Jجb'e6H"ǵY0Np"ɑҞ)AЅЅTRqʄn(gU;smAո2RFËѻn _auTX/t%E^mOm7/.uO?LӠֿ?tܟWk](B(B(B(B(Bٿĺx{ImtƖ煥yAUI5&.{-섟}(Y/|>,[CWЀ϶(0wB{.#n[ѦC/^n-$V؛TNnB}]mHj'kT,Q{u[i\#/ @. SiFbh"T{xsΣo&.]͸Ì.&wX\O~$Rr$Ϧ*G萚zuqv+_#AAÎS'9d{<,K14zW}F;rY@>.:n6[5f)9@VNTYfS L\#e48b4}Sn(n<ڇ'V8AqG5̥ (&|p.[5X-[E%dQ l1+U /xw1=9 ?nAmW\[pNܕ8F*rVu\+jƫ ƈu^\"aANQ䬹^ك6Q[m[{DgHu^dku03텯u.؂*wΗ sn`,"$K7F3:FJYB݁fHLJ*M0>Xqf2 pHIR-X)F0<*oG "7Џ]kЂ]y_c%,R!"ϣth7B7<7E(qKku|Y-@vE U i$jđ'? qUո^kӺfE kA,sZhn@M잒5sno,.Ɍ>$q%Ť7WFk,% J+^=q5Gޛ*aqoh$1ʪ^Gm.jMM^g>:Fi~ĕ ;LjMNZX{LE_IÃKE"VRX5]5ZQؒvZ3ҭrErN5#۞2I>Z7h4~nD0it4-mEwXK;CśSC3!4V\=;8>a!h/zᲯ)]6;4w b9ŏ o4 $5^l@ =_k ,ԡW2/ls|sm!`ϒyVheQ*{Ւݤ6V`ofe4# sR\41<$SP=ӯq3K>ACnqܟSnuURe BJkgXkdI<5-Ocmv߃I~%}HefUsc:VNTON >a%[`ɐmHKv6lI`9m6@KOސ#:HXt/R;*MR;m>Ɏ 2y~t-ײF0oiNVZQyUIieC+>ؚ@L ' ?IF)HpU_dtbrFjqtf )8M)ywQsN0YKlR<ƓVHec3<6\HDu*@⯁GSVAIex1Muv[v hHӥ" <P ~H~Kudgu&j%`_\~Pv={r ZO"[Ŧ*n%[9|XR˕:bn4}v)\'!Ң~GtҭMNk4#\; u_*l`0bZ6)'zIԢm<3f;We:':GSB?0~dKBjUU]TXKZs\̛nBܴLb3S4`s7ny: |{K *lt>96K2N۟jKv (mA82*u,jC֞3<<(Bhp8{m,j )cy᎑Q³x9|aM[l/=q 3«fB^-DžD5v[\VU/}{n,P](2nfPZ1w =\Z +x׶zV^]GcX:${<~ r9qYe+BaKa'vV0t+!M?傶 ;k.d6}N@ED׶)]l$q^njKq'G$mFdݹHzTQ.+毺"N~PE$" Kj<8/ˉ GnYEv8䃧b̿(J|81Ǩ[?#ou . ݜ|P͌~?}du7؃h~z;,lܞ$X\n8 z׷un}6Sk⸖oN@̒@kT>Pet/WvK#-QcapԻT)T$: ֽ5y8cHX0@;ޒ8i )Y:MWTǔrgnudH*-JVZan//#o$03Σ]D9Z3%h{ ؅8Q p)TIa!H * EIӝ<ĵ@/7 $wRRHV)*(A9TCpye 4恦o~QyfY#ʑ*.T')KM 66r?!M4bK2j=b[.yıްTltSZ<0lI"V<mA&K"h;@e\3d=沉p<͔3dhñ`Q6o{.ㆣ3W5,!odkf^cvn npNJ;͵ >׃0DFO}PepXE]r.[u^ Ps$FJ;r]K궒$ow= 9JZݽ4 0yߞ1|Ii 5ΡKYt&v{ZR>$~xӞ+ ;կ*+"i`~~+bH^]'nb@C¥6\d-6nRsiAHhn$"R'C+^N rvuc$DWq9EE t!ם ?M'+B$c"0wSްvl@͎z:J" ?P櫿XiMOoK# ePG"G/ϥ>]&M&+}j`'@= / oz/zk ]BGHPGz> ^؎w,cOX j\Zy%c1?h`(5v6[ݲoIȝ-kKZ9/z65%~ΠȅSxD8d}g_4vQuaVq9T`gE3t{^)MHFywU{9RsvFǁ6<d Qo~rޱm 0<-Hw6Π~R0ӗLy_ ||pEwMiOdXwR`V3u4ՎK{K 3;=\Jא TE;Bs2z Gjhؚq4qc^DCA5q:ÞՄq=i`ߺ˅A{B6?RXN- #ygtŅK$4n$jcwvOę?0X_8`y ^\6Zӿح[MrO M au\wH nE+YkVӄ_-?1hd_Ѣ0xy ܋Uz^i=ֳH]sHAXpG Ԯ4Gh`v*c[+-h@ 7L˯ZܱԎcdRep+GY\VIAQW3uHZYB'8黫5~thchjYFܴ|Hqikxn^SX4*7Xqa]_B|ؙ걺\i.Aݖ5h,)07k0䣭d4>ãL3:f C\6F5uڊ z֣'n?+:oF̹"谗 ١PVႽW)#0M@iM]o". 3|ի9Xҳk<4u <+,Ί5!a7EThH.uSk{VEw -F0:1^vVs]Ek]V^Mo#viUD2KY\IFl<{r@"IcM؝DvH g ic'{T\H8 Fw3Dc\0%'Xh#m*>kF=eQ%l_ [ͳZGjT(${z3ߛMQc;h&i[n:a ,ooPQvPAajHXGIeU=\,)#`ܤU\},a&=Úc~ՙ0r[+٧QhhCYeGi;Wu נ&C1F5[F䍭V]3٢p٢u3AJkt#/^]MK9,ɑ:C\F7}.y\@ τtO[.{1#MTZzVY/u`6Ʋ.8$!,Qjx帮ױ"nG1O%@;Kس7]ڝ]&gA { 2ݠRc?P-#H $vқ{_g[~1ofY~ =Q]8bHkH6Т /v-Rc|?qCV+:Uʎrَw{[;y5-Bd2`a9r.#^_辴;>S@:ǩH:B_7*!ӥ}N[v,QzsW1nF~]u\7E^HuH $mÍ\;s5eųa'+8h$h[^Q޹*K\hڼ0 p@pC:"FOsEZoN82=P6 EsFiIΩ < ߷ޔrxѭHYXmT6]"7'0 &9| \Mr'zltr4kӣP/8P6vzVAXX/cqc 䴝ccl^w*p~;ci֞H^"޴]bbئ*{[x}FܣOЯ.-ywuf(>q+u6VE}%䖋owv!h`*VH;9^.chA|H^mZÚdPEǔVGMg%i逹=‘vsVt#5B9#"|RbB΄'ni8ځ1Θhڹ5ōmB4vp9"c[纛Gb4G 5pmK@y3 |cf,#$$%5S14܆QߔofF,Z` ,z^x/hIvǔՃ`7[w$30<*1 ":#4ɍ1` aq]V 0L4[bUHl^ klٰ/SmmQ(V  @R/7ɼFG ZB{MhVfb@b/fG&Ltd9AI6sX%‚Nn&c9-1RۆGҦ/%-lH5nhi u Fhm5 d %A}OeXXŏɦ`6XI= a%wPF:9׽ctIz}I D+SQKw[عO3HmӮIԳkiq#:dJX\/p/6TiM@ tVS h3, PIJnZsAcn*XoVD0\ٛzƹܕE$nuMGqvz>inrTt_[+;`+=3NRkYmf(U4`ʗ! ܛ |kz&F6*9* yT',m:K qKd #*?" ەG*XeCHmUOGzeSf1 )@)[E2:gj/cQi*VYPqhc{^X-q{Gyk&A;r1f5{Ixp,+驭,ޞՠCV4P۸oPawy*c 'H:Fzܤ{COēʼrd~8uP%B5r[n9L]څ]Uz{BًөqU융Q'v؜~ʄۏ@I[7N3=u+ ߤCƪilW(F10SnN|d'u>h-U4Y U9Ҵ|RF:'C#mĂgjHm O9;ƛr kl:[M[^[עvx`/*Ïo9ۃ{"wUkXkVNJc:k\Cs~\rz@FE0#"|å(hqvUsl8oa( Ԉfw0<)养sE^璞Ѳ'Ƙ4+[M|N~n(#e S=T[B!Q*F9T^M{{u-BY1UP3A*<ܶÂ5P7;(J=ǖSַ"inQ"ccjXH'$ʑ ܤ'mGl8契0AOڪx7`UV%7Jf&TsxHn - KᐮyRG]~VH*h{<`pۤ0&_i:4N.;^{ {^$ -q_?[d y.XrϝX-<>Q'Y5[^JUګTokZ;:d&8|^7u LfǦvX`KN4 8o2VXOjܨO.kIl>|WIְzzvC@ @An @gtQZMfǒVC{Ԑ};W^;a*O/qh'`6ZFf, t#\w މ4Ջ+z#k"@oh6*겹&{{2c0HV*=U\E_촧+IqhpQT5Xhziv+4/ Lzs=٪eMX >ԣcCHH;_>=S`ϵݟ*څiXx'WOw׺Mųھ+ ## xx|y1QFo*۴!dq#H.[dJ!8BA#l k)ʲqDEi1Qו44H<5{ ooMԫ{{"Y q@'TNtq~Oԃ?7&Ւ<7Pzy/&&ambs265uuZ9'Eq# V?F`RԢA T7q. #iU0}ws.ѳԴz%,ߔ}#Eu1a0%` ]9kX8}I{/5QG9X-6ڞyR|dٮ!- kCw'\}ȀO  emoOv*HḎIbDX"\h1e$h;/W}|ىgʑ/cYt4ln.p}$PݩdB;mg1.̼c/ln/STM=-Coݤl%[,8;0N-qL09sH} Fõ f Z\tYO_X;?Wߌgw;=g^;W= b|aVꪽ^suVrzaihdFXcz5䆛ݐ QԊʻGWpE!de/ckI∡TTz-6 hYHw۲fKpv28©>+ y DwW\4MBq$p Ae8L-V,q:\}n=] V:R_cuTԉ}*(Bohsg45f#G\JBBBBBBBBBB~3cn7(0Uith PӇ >t d0{gE5C{ʨm$k(wb1?J@4 1Zȑ Oߺu%Uv914nvu[ZΗM,C* p2 qUgChd.:$qw؟ H7(=Ǻq֥jU?[$m{0 ֔XK,xk* "N =mho.pĮYLZ-Ešl&QKXnH^HXas֣\ W!2HKT#m:cv AjH^)"H2;r= ~ow{F\l΁= gA]D7)'y{=<q|nD ;kq<,(.42m_8dVvL9+?JPǿ%@ն{gc=7WHpYcyk #SkJG'6f&~WAQs\-h ~1kūKJ"AdэǢƂ\[4aIݾeHOh;5h i18u6j<ڏò6ȱ@@C~*7\n -2COwXuVWB\+HX Guڍ1.$u4t*G<*NNXQ*-dIOdWQc 'ʘ֭CckE7~ }r%Yz(叒}Vs0xzƐփ{iܭO#-|rnF ⡭Jh+vI5UCg򺜍dmvh&R P4hLLv'2~Jbxgċ#OǣI}' ʎYꭁBɒckd}b6N` {Y5aD Zb*PhOǢٴ Lõ[€s"}y*[mUzx}I W;(( %{/z潗99>@n9 -"ގlM[YX@; ۏ~(xےtC-}46jwWq4>mh~m_ Uo@:ϭCGWB![GZⶃ^[n[<p@bQݪ=/6u%Q&ヷW#Lpo+]Shjh@ԵmKJ.'xd}Hp$rZZ Mҳ:l8LĪ*lRp[O$ZA<_Rr[9Lr)F4<RM* QPfgڛO"ҭ8뉝RmmkH ǗB)BTP49=]-vr_.i߱k:-k/'GOz6""""""""""/dYNqFAjw!| ukYjhȚ'Y͞;^9, ^ayg#;2I46Z +!eYuM -pD}JdX#g1 ]T;`w=Kv%5i5VFIjR4*11gëUEM,D r!7H :Z]@bވ=366ߏM uW19dk`"_ֱp͸ˌm$xUIؤ 7R MKd腜FW+os|*j޾rX8鶯0[ \s[{[N97_ ˝JkNGNB" XfE831뷺|[d"T͂C{oV~h11=ƫ&Cԟp8[ëV/jM5"D'oN}̖8y+!wo.%v'T^@ kuF4{6H]oG:G8poj6IP5[so*@XR7`2cY@l\j;Q6R;O Du43BdcoOz4bWr7]>`t"ݽr2ًޜ mioN'[i"P$ݞc R}GUpc cヿ'T˞Vi<›'!,Ij[30ߺ($A*tIlLf@ r'q[pXW|=?MM ƪ 6dԳԓJ^U q7YRb323$Bkbw=X繼.-CIM'nĎO6n%S}x$r/;[;+k*\nQ쎄KB4wdW;{(ﵓ|mb(#1N.Wj,YxylpW tg-l):HѰOU؎]1V^+*U`N6UBG6 BAܤ,fGB'ȱ;`A0|MLbC{OXRIsaO6F4úiml @K ǺFO*$3TqCb^lZ}VzwhH /uUv +· C?N[8ͻw5>t-Ib$^S`uDjiswV7ob9vI҇ K-ʑ֦lLUX_1x$ ft"h6ir }׷+n7 ֠&6*C1JZ IGiYe؟dUvQ,7ԓ6 pF$"ؘ@^2\D XPd'F?`Zq*@A\M4Ti y0#ztݜYṯ]yd Izpk7A %RE8tߐCM'୬;*m̄n|U@As.;b Yd9SF‰V!7fQ[u>723 WJZ<Ý:!#G-uڷ>%ڽ]˿9jHC_w_{La֞~U7h_ǡ\k(ܬpqשּׁ6VfCx58 nΣ4Y lnrg_wJtyJ34??kt}uvsNR8~MhdeNei^F뱺oO{ohf=m1<7)ʲ$  qd池 ,m srĢwM1evQ@M쵋ַeډӑkijai<ӓ#ֆ[ =ߕez۸C>J]6m~r.Uq דt/.m 衉 %,\j- iF Yp+9GY=qlV'6'9Fdh%Fݓk\XX4s4:\uu]8/zq3]p,!Eȉ0`="N0L oC}}NVh5IM:80łC<s,1bo/hFƵH sˋ_Qҭӭ%Feg`gĀIXrp}z/xRu eq Zb ZHH=WLEs'pPTdul{m )fV$,ɿ]}=, ,JŒ:ovhsG!0 %`]U\1lPAv:x+Co*÷yo'oj FÖym/{bg,BG FE=1V6&h(@Qq1{MdؓK5H5`I4i5eekR2sgC=,aiD֊"ߚm^zeՋHoc8ώ)Y+$I3+e9ȈI}t \[sxѬ?Xx[]o<4DShvKw2 x8@wgs|qMcɻ{1[z\ 76OzA*,$<$ctM$J#޴>/cY4P D:|wԮdk- 6<9xU0?r˰F rq5>^/L6^}WJ-~%nm*R&6w[Φ}l.]M. ,~66wL1g9''ΟTˋIiSQЄyЅOq ! t9s_@yUsmϿ>˽ptvFq,m"nf\?}qP@;V5,Vj'l4ڦmhCv篍5H:Mj Sõti U[Wd+d 8 .ڹ7*q{AZ]H$b61KBDdy>}9N6TI\[w_ZgE RKW@yS)*H|/F8 [sNIO}(I'QIݮU" gE&f@X)=L cnB>=p#}%!8.ܞ)7Fycp(p2|Ou8H.*t6& [;hf&I"ES+(Nrd!u[)' sLAi%ێl? "Fִ?{WL֓Ft/u=VGB׺#NXytJ:Ԓ;92ʱ9;+΃:GG>EQlqj<ʯMƮ86݃+blXsGc.p,F {Q@7a,~rj"Dɨ0*#+ /쯳 fq0q%zG vI^YkVaEA~ZϖVɒ*nCcU{TCh%8CyV^vkccy5Scs|J_l5sP`=߷u ([+G ƣe\qĂ6]G~TI@kU&WN<y"!i8knzwM$# )E~IzIҺMs[ |nSؐ;I=!+fpiUCsW{av!{ńV*F<z;#9ǘ+\ixAGTQa'-СXY1b`e8doMKlobgM)c$ #ZJ>xL/K.ǪYW@fpHOޅ!s>Z:{ Xa!!Pe$rO*_'d6{ 6#0#5ulUFl|Du49kIȯ(8H[ebй O]u TMƾ`A!i*=PgbC7$`Wɟŋ~c+'7Lx"'rj EIY3ESs{ -BXK48:/cE(#N|Yyv n6mO!ujz}>I<1)Cj6Hjq;lS4 ?@>z|ڝ۷xܽ IsCX^އniZq4q-(#>rTbM%r=F,b;T!EOԪfF14l %R9)&l_sl2꼆O1Ak˵m<ʃ[I-$@oq@\_eY*6we\3kˏ`&6FNcͥ]*{.>/eL,cȎ]@Vlo}8VizNah#*2HL|\u,"%Z]pDq$ `,ڟˀJQZèL'4a<<@o{{.}Xrmu^@9lwYnPxm:6EǹLpmsA]C9W!)^vpu[o4'9*9R8HeHxxۋ'd0`{#5rnUͧâ20ڪ5|U̸#Bxm;-~HnrGy6mSЧ$O۩&ǚ -pD|md#OTc3mlwNkRG V7uæ =>dh=*Wm3v"67T8DqZv)uUC~w?rn.wB*^l<8]㲂c  qȟ ՗9J 2$,oD@ <d9yl9VS6NB#ugcK1 h:w $A6\>jLĖϴr9 0FZtܒ'h~(LI6(~WZr+ ߴcStJ$o-qDǨҊZۿܶg;EL}`q E ns+MRMhXZUlnF;^'iAws)L-mVy׽Ӻʭ>u0pp)Ƹ'u<84؜7@ޔkf05S$(8v>HVh:{Lj%: ::;l~bK)KpfLEybwȵՙنVL"/4imh2Ge 1{z-~y 3 d@05X, hqwr)Hxvf8j/@q7YMÙ$pZ\m½}Z[HRvF 6kl3Le9$5RΪM 0 >  s\M4V p\Nr4njxd~ؿ '%i~}s+~#L: -$C8اp@EQƈ:Oǹv?(dALMsӡP"ح9IiXW{`9gzJ熐udG$Y[_'~m/g-{@!ϣ bF%]/ dsHU=}TQUH[QÑ>>kyX2 lv7_R߸'Fmai|e2Ù$s~;wC^3Xp3 $40dQ$#}ÉJ*j22,r Gq85G|):U{#4O6^ P'+]\YPe*>?|?&妈xa5cx6ۺydx 46\4o~C|Y;nN ^T)e8= nPqh\^@‹uX̋)W#C*VMfaHl=q,̂"% mgƚ_WXnxamW_J'MGT6pvmY!;0P$3<1;K>f$_w#n)Ύ)v;)P$PH #NŪ416OJׂ#G h~hdoQkh@8㝹%{;w)]LF9~NiDm%mjvYGe2z]A ތ ǗJlR9m6Su<(0oͦFm#,?u5ncgDVHI犑-U  ls;ޅ׽XYX)HIGdq tl8QHcdԑ^K%uRnX90ymjStN &ƛ n+~ PyT):x%.X0'*yҚA 譶f6DcĂ/oJ?g'NUPZP(T%R=`PJ*p1vOѓ<.[vw^j7WYR2/oEc2'=ƑK+G;s֐5$V2iʢr95*Q,"gF Aת%e"¬ܔJ3Ґ[[esg _z 70F$q3>C]T~m3մ??֩mBTP49=]-vr_.i߱k:-k/'GOz6""""""""""/%~xm]$4h,b+X̝ѐUZ")$texUHrdnU}=7F\+^(巸ꑱ!PYsK21u\K6F_]5'l {aRoYUEcIht8e2 թ, Hvߺ`Yo/GstK8(Ē`c46,ww^ko{4v w|#$J-Lp{MSX\a[ 7),h,+fNvq>~Q<׵Rw8Hv~-*[CcQ~'T6:zP8kOaXZx&$L$:M.eH{ZY 8m1 l(a΢xgQs $]vZyZ=#; :Qkm`h =;Sm7{T._A!HN:5U~$X?6k;}@zVh_݊`q̑Z.wGgʗVQHsdyx[ƀq)_yUX-l|Q#v<۱d*:H ;0TƮyFN( ,AH}CgcFw@RЩ㟊MZ\~Mt~+@pMWbA$Umج2vlO0;2_guFL?\a#Z &w#A CDMzGa$3G+LfWsp䇻˻Ihʋ4FС9t2zHttXgqQr zMf@]?t?~{G۫oE13nͅ;fSNh.XN3@wU#0n69 FuyAڽm3)8A*a `QAc fTA ލ ;{.{O5+u|ʓ@HE^T×0\warto5 A\n{m  Du۰>my#U3ƨ6Xq%-Q# Pq[cڕm.ђ" -sKوy3.uj@{n cyoʯ č'óUj[Zaދ#y𦗁)Fz5]ϲܦYChYG)28 VZ-y2k.El8XbfFǯ:G}Y.uK %(t-0˼S3d7m[,apB𡯡\3G<|Qh$=,d LV)uv; s,kX/A"rA2i>e>T/%~c$ e/` T.  [)huoGp~)aQs' d^NȖ x%ᾞksj f1;K%" Qd͆:jDGn8G8!Ӯ|)sM(E#I;ژ47+O:7b wQI ]n%GmfER叚7+JV8%O!ibu 0,pH&;svnKo. ^{wM#u]# a- ۟~9fIFnx9Ht9'\~ka8b[I70*s=-Dg n aէ5hUkpE?R{d( @y(Ӡ^f;F{}I3 ds/or9$5⡜ir,fPӥR8 4@E,A `{)/ ]Q$ߊhs oNmmlI|--U90F[w?&?Ns`$.UIoo;s${jՇd'tuq9m-dpl^mJX+ĊVە|*l4!n˖\=k _S\P,f@Uyg6:=S3OFpmn&n4veܴke$%Vd<,lxOՂ=w;Oh_ [:\>8!ͦoR8.tcns|ڎǹkumf"6h# k`c!GA dwV wQ5 NZEҞ.8oW$Bnnϖqsk;34 Wѽ)"2$uG@kiċ:Xt#˝jhܞWFߔڱ왳v^]mJ `"j'ƌ{CO Ly,"u%ƍmB^ܒ"c~8v!m[ y~ vt:׌ljHS" p035ߡb/O[v8h'ccoE8ѥ\ڟX~VRX)ctR9X_</ a;^HʷWUrt$O]zE9Ex=Zq%CHX߽zwI4m"|/gln6ou.lFFln/b>(mF;ucsoSfOAah\o}8L#I#@*<3q7Zѿ籈<sM\[IjeR#^-F43h> s9 SƩL%Y 9 $V$cׇ _zMSu@'=i$_ R,@y61[בRO, tF\NĬn xЄZP ~Q)E-rTA<1HR9,?(^7#)`iOT,wRE T>{K{iP#R~-D9Æ6PKw5oԫ9?o$xvK`tŢ /w{$ϱo~ҠNn9A3l̫na6If':w@MJ9PՙH&#|7De' !{]7U48v>ފ>H,*iR#BU.VI" j3wBd,{-<)]fͽj|DzJ_R8eF tc8.̅ѴJϥ2goE/4HtEyVuf7tӴ0DBHÐH"u}Hѿϊ̧]p{v7[ [cf3?eD\\63ͪV{$0Of2@.*nMT" jj'>Rp4Ǖu؎K.3=lE}= sJPyGV%"lab\)pqMȲX叴=C+O,^v)i$ @ Ӑ^]%n{k߫0ZqRnsސXFw}\u ؋Ɂ?TA&[|φ {nktVmCGc.&0v31Ph `pWcz3:L QZ^1&svZ]OMVcZ9>Q.5$%@;V'>txھŞ,yCes{a6\ui:dV2FN…B{5J9Gޑ-{Z]rGWޮUdm t"XG*z[Cb18KHLpA2*75~KYc8y/w%'#|388@4:;kQXcF|'y/9Ň3+0IG8`Pm!Ay9P_>@Wa;fbMFI:FH]xABW h֫kQLӱ m9: (SOIQ8\6HfPe ދxcb4qVŽjS;QaĂFdA `NăW|8K`pz4ml&R+I]N.$۔GB+ nc5$ `|C3M|WtN.1Tl{v&XQI<*rK/2&5͕⃀c[_@u5x7D1+ʤd [s` ;UC+(CdkI Cw.ޜ (}q|iHqÃ3#xo-(徇{+7|!H8RXYehs'ΩL#ûu;+@w .57+*ls G@isIh;QREM Un9_1Hw~ 6T (1Ui# ,o@8X姂=AU{Va5o¦7eq"MF/qV\_Zvڵ0ޯ&N ^KjU^ {LfR Y'` Lf'l6{zn;ki Rk0~Zt AZ{JFS;gMNCNjamVާsgux4@lPjhl2I4oUxLFVX HmEuAν8WݞCoxH P[ ְ"pT`Tyح ,I!؅+G\Up/[uTɑMUYXCpio 5j)RF`&&n s^ Uh_dTD=._.zk9KFB7FMlG֫G0S" #C>fIraS0PbdZri%% {#mh7c}JKxa@Πyx +Xdc\F[pe淫"2];Q1%|9uc;\ǦNwe ,tSvH=̌峂yPHӡeZF6,xeqpV51QA"(qs kZk$CzdՌvg0Ws=3fZuϜCv:H&{];֋g1pFU# C1SlخEכ} j^C"Ɗ;W ?#>,ef^sϠ7k޵ :{dmټ%OPXxԺc֓FN"+Yq v)- 4g 2sQǩY{au'NkUOP;Z{uu2n =]?O͊V?.G.7UpY`m~=^<2 Sp{+nX[1 uQ)pZF&u/)`*0|yrZX$lהwR"1H X@YȤB!d)E"ERBTf=!-"=f8qϺe4f0O x$SA;Bl O;VU㴝5NO O].cjڜ:/6k`:vN_o~h_x?:O~+ѵĮ!!!!!!!!!!xc@QC7EW$B SZF_4-XEQ/twZ.8|/PP.rm>c$7 p0qQ6VWY>\{4?w`w>;?5Yh %8ۿ.:9U#!򰣦T@L2Cr=:#Ieyl9T׍MOx? _TBv,5ix"*~X`PMsg嵁hnc~+.̲WUgoQn[cq.ƶ_VX]9]/ Ez-\{CoKwÝ_ [>'o{^:@f*rnJI\x]Eu&dq5[獷)g[-# O>}j5Or1%Cl{yIC7)bc$Ou9+` W(k@C m蠢ND;V^g>:tr7ci\E?sMm,mvǪsq Ɵaa +{qbcvo܍!xR>ӵ YqIWy8c-Jà wn-^3G^n\ tXsnɝ/ͱ#8'VӥWN|ki{_Y#% 8`3ʣS20rȂf:'vp+ȮX ,sO}d:|"uj`r"E̯"@e2<3jC:ږq?*ש(5Y,.;DžX Ĩ#2G5#?[Š1fV¯܅q:a>f9kevzXd ;+m;T [J# T<iQȉWѧŝ hm~Ʒ@u[4ֶ|b̓rP8l.?ce sMXm pkku 8 ;uQ˭5mIlX%Cl^ABoh4.qr\ˏjD*|&K4c:}J-:in. 7{d.x4!N!Ӂ-ju@$t};5k>8خ8cNm&;"s,;F(v˝W\bT- AM24WiY#l'T͈@V_,׈,ze- w(q/`548|GC- kqT9VBsSQ$PN T ;!CLB˳@#d1}i/i#>\D@A3niLYd`m9Iqo(- cRD~G(,4(b2#H%)`(r> dk|GeDYΌ~ϕ.W~mR]Zmg`G.TK ],q?==@hnx& 0$nH5$̳,Z`Z^@m69Vl&  8 |;Yhv5`V_YH `|oNXA{~1> s-Xk 3b tE7qi2,sVX8"­^[ѩo6SƤoHnϫJL Xˤ<̘N3[L#J!K}ejH+e!-u_@mL}(:ZSJH8uru4sÏeгlF٨oDn=IP8V l s;4Nd d\6n"iA!IO. QGC ]\w+*V^XǾ(SW'-ӻT% .)K>Qcu&~!MЧL8_GH<7|=wYLlsCIvƹ#u{2GiGzkGo~pjhsGLϛN$$Dz/k]\Yz>15iT+z+2R֯c j"YX<&Jcǹ;'L٧DmǏW|t(<_#eۅiIozj|}G[| H(5r %F^JgL -v;qfLgȒRCz[vIaI[A!\w8Tm:».9lqm5ڪEgFF.r5(hh c'oZe`F̜6Gru6+Fg1C2J:ʷl Ħ|)9XQMܩب#aʏ?r۵ "&4})Cc&j6\|̘ԥ3VoWFʥYo絷7+hQ6Ǒ½vF4Dd(oFT Z,b*X 1,ݦFΛ)@$ ڻΜRm}Nn2^yQ zȭ-汽wI+؃ܝ"-EIm6<.樓xns1Y<>‰{HבJTJkg9& u:Cqogd4769|ʪ(HЕ":R=ij e6p]MF7QW11_3`Ol/g4Md#ʄNDT##"iiw_[j  QBxu|E~ ?~7?|u?'?WD҆~ƭBDS!E1mG⎙]Hvbd9}i!kXwʴ}&)`h$>-b)v_*X׵ڜjp6IjSjZݧ])UNV#x4 .f7 9uis@^iMox%*N$aytyh◠7Ɵ :!Û: n+j*_j^8 O׶QdCWϟXh}($azZbkkIvv6=k?W[ki^Cˡj90Ȅ1zH`x7$;7OH'Pj K|xrK@:#,Y @>3ʩәb{Xi( MrHhtnsI u"R.#dq 7`wvX懳r;>j{8)8N3ZKyHTݵomrK`q纮$gso]zRn@s[7fV]O00Hm yTEO;W˔ɢp,ECSmwI&Z}"nEY5z76`v\ItC$ytw&#(G՝$8dh'#q?:uvW_bHcq|j;թ Σ!GY W{'riMܬr#К>CGU:TWcs.Ks sjT+C4,<]߲,gߜq%rO/1NhO 9oz6ΐƬೃJ.=D9ɏ+0I!nQ!1˕c89ߺ6wR$ֻ;hTf˹nd!v9:xV% <:5_BFqdbFM'*LÚ2i܀jsV]9B\"9᥌Ny r\ab}|Umoi.黋Ɛ8ze4jSFdEu,OeZFܜ68.O=qzTsb!$cvHC:LG~mԎ@QN\9s)%hett9cN$Ű:VQG#A$.^6ogp&b+n *檱5߅q22K@{mT 췀.ȍXqklʵEm<_1sKZjYbSs[#c'p6wpw48tE6:]9`TdZw]ySE[xc֝#NѠ8Mljxl0ӱ<~4iئ;eѠy-^ArBLҸ-xN(^os׈,/n!%>qY26~3Q3֝۷aWugFm$`B{Ly X[dRI@I"WjӠ0mqD-m4mI5H g4HBI . x Itu`oyB;XhnޫR,lWaɑ$d$}/69T, Lu#pyTEIZ+|WX 񅎩eEBڕTݜy rG}QwO!tpixJ}3n $i'ywi_ #ү]ApEVb\[:h/jͰ2 G .RL#1˴:hO>a϶E"Lr3AmnZ],@GK7ůMNݻI3_{9';NڪLdԔQOPF(ؖb7 i-$-E2"ccI$ A/ǫ[*[2q={|&.yP"K@ wk1,<$]f=I e|ts$ZIx"hs`P ԓ8\kU D~3-X٭{NJG"=4EF9kONzO1g%Ohi8 Ԩ:s6 :WuOQɐ\6 [Q쥉$eXHa 'x$[jvF܂M{]Eզ2qJQU:^j"apw"1S{z+N5IJKb+a^ǽi(Bz 9Hi!3組xW d[AugX `X̱4k"BG@XZ,X{)ʲ9u V3JgV *Ds"Tf"R U,\9R7M~<'N&A6wm  q'ouw&c9}\=G=FJGBvU\cیO*c]Rͥ1kݦsNUR%XFG8TAO44jq2$lP`~. 9ex≝K2ԟu5k@$#%&H LT+g%Kór@Z.mJDXxD$EȠv(x\FC5yT1]` KGѱkZ [c5DIE M0IsQxn n3LȑZָ SUQܩmpaX0~j\x +zX=N8Oљ SkwE8 .#rE r;Zg37la4E;Ffm,l~r $x ܑ^mvS& {GQ.s8atlx%&ԝH!ކ;Vv$ov8WkW/ϊNdᚋP$4FtvZ%s,1q2:D!n/tk`T{kA=1B᱌ Z,Ժk_3q"Z ]\gMǭSwi?rי fհB'Md Sc.lc%PHƺrRecIZn4voXݷ`=㾶U3"t{-k]\Ԧhb?|l^<]6&湒5xOq-x,o]k$[=*@«Ɏd4wWt:gM3^aayۺnl7Glrr@~pp poMƫ۫X1R\;Rog;' =5h\id@iD~V4?!ZGȰR.Yh.Y|*fYM3'!2w֧4`O, ,̀x/& ( &eKV2#|'*{qru;5&(c1Ǵ)v-U%$2o`nmJ@Өr֐~emo6rwF^7q(%d3p(waXRbN{[9Oڌ,ßkPr,z8]Cxn xtwۏZ?a 돴 qΩYDY=cN8>),aOiޥMkHnDgj̏~+WfM׷CbżYo<'mbY4U͐Y9I c\MvR3`0vc%Odb [;>õqj4mOXK;+i.&V%,qӝgsVN+l v~ͥAkY_}>uY mS44A$SYYm=Q-tc |)1d$mMsµU]˾C TZddq4\\kH5CeX-L§"Z59ǁ>nG4躯OB ps\n!n0,ʦPڹoMZ[4i5@׿Փ(eDRH==jfjJOv(]؂==;djd_d <*sl|QL4[wnە7ҥP 05qˏc#A4jv5ԏG 8&lzH pyG ry\L{Æz;쮸ӌatnlBF^X|zV~ kǽ.(̌nkF-i"EWrwZ-͘Rtq[ysE hg'w6ۑ~VFř#A'v{c[nq ak4;np;$Hܱ&>AwS;ʒ0Ch,sN] kQEI%r U<2p]lc7T2x H?XNL쟎Yw>)=z♡&+'*t[hpEO'ptf8cGA'Π!3t[-nԤS`q-si8Q?ET%?i{qb+7(blLjGBxO4Lh1ڶ"!I9鎀V_Onv^[˚tz3lwUGUz?m]jiQ% cjQ%}q XQ4h.,msn>)G"GՇ6f[tT^vᱯ> MVH 0`L|1+[?B kw#S۲:B#g +svԍ..ul LJC$ 'OϺĶOou ' dg<9ۺloA ;]iLHdH]7q>TmEC iwv5F-'2z͟*FOn贰2cĔ@%Zk,Q+]$F+;Is\@ 5#hY+ߖ:ø4u'{gVj)Hʑ*JTJT"#>/iw_[jDЕ!y?oW_]_ܿ?wp3NGZ Ir~#^%tH fa:ց|qoO“XϪKijE0X1bs$B>V6|8xOki%hk[a()$_#@\T!w-g^) .3bfB {BMڏz+Om55nMbDžu8Oqq(HQ3(!Ǹ .,+zJ gWZ:=2Ȣq^d9 ReޙS[iY%9{V%FGF5O0ǐǼ^m>Pvޜ0 &iu8]/3(|3Q; cD$4pGH2~P7ϐcB]{-HO|3 qI^Ԓp:WHztyXx iyhi-K4}EҸn^Is$7K~jool6~7O&$eRt5`*NXn%GlY>96: _P^ŁТM2_ 6xn7Pn^7.XyN-oE~4vno[p-ˀO20<9Oq ΰhO^j@D쎱7 pAk$my߲>cxNjs#=k*X']zs/8=EEkg_[}^E-d6f8lV;$\ez|fs(F3\tsCv v_9l,g&dL7woNie:Fl\n{ca"Doy2fcxXcKC{#94W,#VXi&ҧo 4Ilޯptа,̭̲.'HzeQvXݩI%ʮGm6MkO0ÃI\nwчze ,,HlVL:?ΚSCOrgGdba;lnH0^>>X #nWgPt8cFN#޹Zi⭚Zk-VõU) nYͨƂ l^>q-$<}kSF| m ý զr`UL:7WX=;,7E;KIe)ciX]N !/ݧW~/Qj8>y9c ;mIKXpGh{u]^k}Cf7A:DL%u_|#O0(^7Ƈe 獽yIs5Ro@{S@qm nF5v):c#xYө(X)bK 269c4oL$j`5Kt-ö ]E?r(B3BB҄#!Hu*{"F(BTq٤Y R $GtFeA`9(΂s!Fi!8MAP ӼWF3LcC[VΙr~ɞF""DB*zk&EvA"# 1x%Vnkg\wdNUʕ"0)Jf*Z8/t%E^mOm7/.uO?LӠֿ?tܟWk](B(B(B(B(B}% K-ho^'x]TdPc&C츧8wU6QŤ]G0Ǜm#'ABATwNtu%vLB0qA|zFtX}5tG $gܪar gB #Gjv:ͦ4:>y|e%pv,d;Pn(Bl}'q +ltڨg-zYӧU=˯]E'eΎ3< mmMiVDeR XtZOD0˖K9uu6=M+ }GΠpTc Ҷޞ=G /qvIkt:, [GhZ:J{5۰sK7/,7 xޡ xZ@(wVz=ʕLEH\a{<>奒\@pPuK;X /*XI4'M~Vc=rUV,amѺ1q2yw:w$eYjF Xch> pC [xV|mv̤?D#G]™<+gNHZ$eؑ K+r3+rzX w4۾?ϸd%C3݊f-t_$%۴H@^Aigy;jsTx}?#!h`y16tسN A-$`PLXms:k`t6Q|xO+NX l4kj}y.=a5Q^s>OV42NEf>$ATz Rn##ͫ MD ĉa3QA/PÝ]p%z6}JmCbk|(K7㝁;U/ w8$xRAis`, D!"7ol՛Jup6^c%б+FGPn7{{,\`~Ɍu1 ܌|IH=W=14M, g81۞7Nf='iHxqTmc3 -8zI5fA0)?3N˟,tNHqq^K.c;"&:"(;i[ J'5N;${}rQKgtiݘ@pI=պ3[0ed3۟u;&PeO%烚GxK50k4lǪZtXy zK F5tÎ54tdqDbH YS.<:.>5H$CoKQ{K} ;.'!kAPhg!PF\ƒ)ːjkP.awRvaXyT1ʼ㧔7Vn m**nӷ!m>C cb f8.ǟ]7E "GiUV_}!Wp@SJD gԅNO}N;M'g,b8yT/ݬm;-.Wњvs02+0>ŭ|VCEVﲪof&9S`05ucJ4L鈡@ O=Grgl?m;%65S̚]aqrh=yD3\F3>uRF[#]^d LK2x50 X_Z~{kti.9-Mr|քln.VL 6#HY٤$ G3ve.9jݛ]›'bm!b4z#T p{u7MC~oen^+Ee w c^ Ю.LVâ9<@<r'!R/}a20:yTXS&KgMǭ ԟL -fx%a|ԺItVdFG5f@ܴMuil.=V@VKk5ǏzK.wwDjH̎4kIR4vMcgEwkI>6xr\,:7wKtXwOp6g-h^nvRluK!٭'BR3ΤKRLX(A5[ uK15K0ZC޲[[d"-6;Ww@M}\{!su^0tH/~7=v#\tYXn.ǕFˏt8@kE$ s :v0do;s̚[e|3:Ś&w'uc֫4VnCҮ8ci@湧Y"[mǻtvѯ3ʖݔ's#=Iɧp$ҥX`qKN@6R :-Ԟhr=AY)"ꇘ߯s襶}I6; u:B3+4Zg z4Sy;lEj!Ӗ% HdmjՍ&NcPa6AujDs-wUVh)Tᑆ>uM6OҺZAGԐRA KWPŎi7N#M3'ΚֵKEɛ2gOrNt=WGJT` N!4,G׶iגۻ3m~B:6*f,;%pD#H5b1ОH.]#lZI6qrBr*Q4KQM*H|-`%`S0GQBaXiR""'mLu7{g i^NUSeinSDi@":ЄPPw TosX 4S6,f |7MH,rrM< y^d'rBXB5KmBhU2{w+dX"5nn~np)bRtHd9 *SH&; v1Ž,}`26 P@ :mnFu*b*k,ҡb*:P QBBD Q =Վb7Yd yҾO e s;PRbVvH'S;YH@CchTV}I 3h'ެKDy\gJywuǡKѺn+_{4|}*lSOKtg=61'>{~ەV>F$.,3@@qQKs%O<@־G c?. <>>[78Zo`HJitCݘ2 chl8:lf9a <- +c ;z櫾2[O)0دHٳVwHr"29ihpr<@[kQm;1Μ T2)ẩsO(kqq`$]G</^ł%4;~*C" <ߺ( A㰆é^eoú>>춒nFN\6yK00P8p[ZH ~_*lw5ڕx%ƨQmUvDmW^4 ʟFI A&%FftcGQIMaIϑrys '=bc)&!>u Y˖($a_VL[* K"C)"dmAZ Opn{)G_q$qw89B[E }T#IM+ydI˟IP'e};e\, JW`J F?a۹:g΁viLΟ,d@M_ڹ?5# ϕtSF9 S|^Ā VR~Nm2vRR3Y(`ul,\T$X! *ˌikY rO4|[k=Jլ+g (y?kv:t.fޓ4 ׿ Zd,mX} sZ%2I ow5լXkϙlhi !fϔ|٣zMJr72Z42,x“<=1,٬ɰh8_@Ӳ)1(_rOAs˪^5E',䎄MevX]Gu$̷\6-4wMF#-V>E4l8h.Q=x },~nbAO[q*0ŵo97sc$wfjZ·z3$Cyh #90+ /, 7ax;K6}$P\|EOjv%,$oa/]XPā <|i5x_ՠ!KBgR5^ʰϺc7o1=ŽLd!r"U5bq0#yUiu41 﫡&;ڊHdqHFʃsJեjGݾ)SL1vkL޵&IH{G?P ӈ.h;(V{"%R 3"`@q[OoT-HcCbD{&5/F\AttyS7d}l#=<*حvXәcC[h9Gc㞒-.Zk}F7TFG<㾨cF]!ըF7 ؀*ZVMcڳ 桻ǘ#\X[t4`O'cc~obFԎU<"+-vd'Pâ#4" IWZH샗m7|_֎3y#ܓ'#F1nj0G}Wy}N=^?,xS_ |PNCK-.Vf9=žIfuPM(U.A(կ\샲*Kg9noؾg$A4uW:zZΈ'YHyy1ޒjNklM=@Nǽ~3j,_A˘##4[C?y.TvcEZMGhZGel=HCMzɎ\)IHԙݚzRw$q䪓g)@%Òt6q"ڄwHEPN6K~gY+f{`3CtaScaKUlw&q)ꊑwk [#“MޭPB \97zl?=9WRDK5+eU `!^u|VVke!4@o<1J##$lc$廹SY =ev:Oc\[ˉЫ&ݿ[I ,mkӱXL'n ӕtyY ;FpSHHo*FF i4/oA$=9*I嶎-;Z1tymϿ OKCsZLGiC$7aZz\rў@z>)CnF${o{w7 Xs#8yPō4{i!}Sd ,i4lIVP@;=Ѹ=Bia[dW.Fk£p6xq a(-F߸.\7 q>tקϲ/c@jWmײd$#n~@NGJh6-M,~.[)ෆwٹ88kO.&DǑ#dѣ_hHg4!E"(r)R\ PPR%XHB(B"(B BTBJG*BE$Bxƚ,m{0?:oxe_sJ6$z?iqR!cUhBfP稧IR26=`qpMwm_z\ND wsj7GE1] -!{@ڈ'52Y ﴻ]x/-"x}*(Bohsg45f#G\JBBBBBBBBBBtb@Vbh87``nX.eiÁ>!5 vm ĆxNiZ]{IiWTc/M+X7&2a(ޝfi̗"dd/kd|9VvGZ|x- V ڈ[j\)hHE&k<,-UiwIJYqGs3䊨خHNad#,l_%f;pŔ:lp+;L%.ˏʪ9{^r7+3Ú)YHp hDfXvٝOL_dzG:n[ߌN-qmKK`9) d9g 4.XL̖xl 򱁤k`< $(\|^rh6 >T|c+zt 0IG9u ͰuGPGd"U\rίˎH]c͍4}«t)ӵX&d'8#mV}7nc~T>1/o[|riQqm_O,q%ԥr|Y`.\3~SgLWUUŎم>l' h{9;W^m{E>zd.q.(ˣ"nFm?,f #y vV!= hvƬ9dBd4/x8xuLZnGU*ھӸp44DRLF#ґqiYm΁7%:i*0k `[UJ}e"q5.+v^~Ybuf SIi?*WJE ~WD)ieEi qݩ\?Y$ޥ|4Hh6h?ZIp~0,i:<C-;J_W[u%ă,3 HÌv\m{M;67uҿ,]\g83G}:'A.CY;[gFw>l, UԈԱnp; ʡ!#t:6bćˎwuj [Y {\拢/UW'=\\b2!npDq4G{un=Y||Sm}#GC<1\l-d &!lֱ#Ph_!>rêD_\׎nfI\*A2[GmIߚÆ.r ` c/`~ 秘{{N'x8ێ~ڈjomf,]v4UcX",* ѡ8c9bqr8 22cWii `PHcAvfjG"l^5|upj'L쥖+YriМ.J\$x:'O{0$v;7}9+ܣքnJT"nU) X9ZZ""r###!hp WC#eg- [ z+k(nnnf۷9UOn Tbll[@މ4%,b=<ƁIPO8LR6TJǛ(A7M -\Pb-l<[1xqsA?tnIks,JG)b D~zs4PV~>Dciq&FcmsץJ i PHE Jf*9gE"2  kΐ <^G'~ AӔ(,XI tERqq" HciR r* BTbH!U;m s<*3N:WcCcxjۍzv]a/eqC!PXs= L6ea`d B,%X41@$g)'}Ml. O>᝘{^熑<{"KmK`x1F7bKi=EqL➪,Rf zҡK7Ԫr'< nѹC{)THɡ*O 'maKU 󴷘#cTȤ-26*H瓧yM8}/&7Ԕ+6O(BКV1J@tmsLcY8ݦڌOt^$'Cծ䷳XZT=rx(mBTP49=]-vr_.i߱k:-k/'GOz6""""""""""/gOxL?ګeئ?2K[_S =>水8ITUΣXBd5W{X7ͷ VU;{$mvu'*6n]y}L4cH~U}'=o (̡%F>+Kʇ7i ~fsKOu[ĂQߪR0vǩV:TsS\ k).Zf^S6!̈$>j2E]LEC"=,w9Fs'%T >.VbC=|tֿE4n#po^( !<*Oܪ3Iz怀KN{4ĒğeyTba\WI1:,QhShy}NP }tmYSi5ASgKK9݆ nwXOFA¯,%&gFt/af|N4ԆE\[yKxW&Js۾6?{],"0T3pÏ.C8'Y%K+qa*`SOk";u%sQRݻ-\ -h5{'-T$4Y`Ƣ}#'#sqvF޵z}Q `||gE1ku`1wP-@: 5 w҂s_z4R2AMtGb߯ൄ+:kj6;Q%ӦӲ'*k,* H0+BaYghx!9sEEtH.m,9W6(1cŒ4A"yk|+tD,o|@u1-4]WAW3J飹wgіQ,1jZq(5\g\K!ŭ27ffmSd׭gvh81h^\\Gvg'ăXk9</a@0u%q->Z,SHl{Ll>Ef'ey>5_㥎xNpv%½qp~{GuRyi;5SfQ "p5Y ;jE3juj6_޻? 0(ClE3wȼF"|>t!`HuJ: hg4EM]H(V 4!duX݂#l)3%o}+֑$>~KDmfn^['%M_ǎ1RԷ#FLp# 44I FbŊ~NԒ W{XV xυV"Gط0: R@5Y7|7)w-qo%&պ瞕2Zٟ&2a$Ʒ#r}\+oPpO'#l mM;u옝IDĬy{AmSW>6i&K&o <ZKO xkM%P[gWRC |UGp=&Fdrt9̈=&mۆc ^Ce`gx^ACCsm:~gMs1ͯ.%-%H\aՏb`fETt&fZ(͒vk/ 8^G+=r~e9F 4GƦ6b8eKaһ'V_ oZw x['iguf=d$/sJ^;sZfm!'2 x0؅ddv}GdR]\""@u=`!ӱ<]o_^no5 >"/5aTbHfd:}]d,I.@۵,o@ojK Fe,ۆ:|q./Vȓ/*vv9 %P΂V֙oEr`d @qãc@5:{y,QkR,3k1Ƣo׿D<|2'6h<_q [ zU8~ qᑭ~B_}#dPtXq Qw}iw؎x[% STe VK.W<<*H>0gŋw [9ċ,Oalv]^xRtzm aʋipm$b (nX>T#8)bi:z~3Hn/nA ̸Ay[hv0B$b?[1ҒM &r}kZѠŹ|\uF{ȧ ?p+d~w3կ C5]zmNv@էcEcbK Y ~C5m_e%I5 i[`yVc1Z0ҽжIw v:SYY+3,tmEZd60EfKx۹@[ |WQ,zKZ%͌7EM"%d4EKHZ6{#ϭVf#Xږ8n5nu4eѓ; Mi]OP{y5HB(=0*MJr mb]Yw =<ѕXw?6/uޥN8Xc6bePҥhu-[ pInuآ4~7UHgn^b#sBN=3ey:h@a5DOq6Me{3񐎴192>h hN9'؞)-i6|SJ @HW˝Wt %_cb|،5ܐ+bOq=$Z&ŗ:lvVs!I%s8X'I\to؝cE^F}}Z e^3Thg61,R>|PVOkMW#1$}E XQ&3݇.q3b]s< b>(Ǎo :;oN(㈝NVP{bM4s= iVEvsjKi,nzT֊q~jdQK({Qz 3Aʓȷ#Hҩ%I<-We˓ÚF)?nYX Bsˉkc6L7`K. 쩃Gz{ږI-s3 n:kQJL6h$mSE|1YChUcd{*\1Mkn۸6t+Imca ZYm8ņ(t2K lʳki\oo;:?fZ6ʝ7K5ڗ$AզcX|xAV11iQ;R}o~ X7_hnfi#%ŌFwCkuQ,ۤ9qѰz4sKx.I|sR}Iۂ)]|c5mi)Wim5 d|c[P@ xkdN*cTNa7,23v䟞-Vk)RGesC!oP>H^ rfqDs_ķP=*cFjGn3SK,^ wY'pøŏauHVhx&{_a)l:EzF c̪г=xxWdc[[R垥S)vF#H hG'eYz|@Z+{nG{pw*GGuiàG+?}EA l5Vމ.c8@V\zx|x.HخG oOPxE6'j)eM$lG^{ Į ki2*<*QۑPMHD, V1ʄ1Ȩ1&eIn6 '`y99L E$THAby`s&ܧ5{X,¢!;1ʚz45Xwp=Gdz꧴xX`I2d.9s'PC-k[]Tk"6Fk [n,IGWdT!'罸34;*qtk:EwWgsL/ aòkq%JY\5kCKlz,vJbgI>,h˧궷$qg8(3$Nh3O0dj/W::~oy}e M "#3Ҩasb7*eb^-$HawjiwZZyR890sӥ7VFKN==Q]ft]FNq{C~X.qPoMiʺ9PHR! {N[+ |2;#ڕ,t]&$GEjtIǐ]ᓻh4!'nŰ[[[G{[6H#MQ4/=zf1E- <5(^C垖m׷TZ(̲Agr/M*#;ˀlPΗ[h ڛA6n)or+׉cF8rp9V֛r1'GMPo5z; aXcŇ>&oX偝,WAQ35'OP:cqGo|Jh`6Ua?AqJv lSWެCHN1QɎZ-9f?- XQV>koޮ7Q)0u0~;* ;+VKjT[YCz^t&M3\D+#'m;j%1X`c*-GPo{ ;{{WS,G!¤ */jw{t<ΥqigDlu8w wĊ7XdPp1Zm3lum=o-V&AP$wk G'zHt̕C-*nli3Bl@]l)_7pǪziV*k|끘4Jm'c^㲱x|$`)i4TCA[VvoW}UUBVxCFrsKHg1ֶ<;h_a{#k)=H/zWN;Q"Bmc0:6c\GwgĮip>oB?U=Y vN4s|t3`ᆸw@' mBT(R%&쪓ck~%/"gN=R?MnGѠ7m_O\1ܶӥ$UFxxHȉ Wv'nǐ`ئʞCʡhUM+ _5iaXBW!IMvuUƛbJe)UQB1_ įkD?z`ֈ:c!'8Aj 0=Y}3Rτ-e栰'>${\݄|{ <4؇8UWpT榓XEny-l X8{w貺`dxw 4ii򼵯"_  \U@\ˍP}E5 $nGfeXDOAStt9[kSm5n}úH7 גU܀H浺B=Hr2jF)#\w+%=E;2rϼUb'ɓV@}}>}&gn8Zse,ՖɓC"ǎ&@IqɊFzԥI*K$d2GpuI ] 出2 %t)r1(M 7a.mVU $J\Nn?YْӶkW ܉@|18g}Un)9{];-ހob,p.FNHA* wN!\ 罌mwzPy'8/7{ͽן*e}tLiDo mn#~, ~A#m7oQȪ9֮3 DS(VaYbߗ~^9-[Bjkk c ; Smtˉ)]y:gFfelE7E(-p4AA#侕4Uﮗ + 'jQ#hث)VA4QcqkBb(B\vʒ:!deS xl0Y>s*9 SIQ̯Nypi,lƓ!ydgZz IoUG+`ۻ84D1:p1UPZ{Qqq,aܶ9 ӝJɔdLt$Yu%H;P`@lx: qm;tυ|ڭ;&oU{,+ Z^{igݏJz9Pu[ZimonDydE`.jysgx&_IWm9#Q໓U+msaw*^0a2¨ x#Vt~1s%ht8ٿWNb*}@l%\iq4xgK$|@5ӷ;&yinIlUQ7tY$Aʄ mehкĻ܎?5UqkI }ʱSb :D:e ";6P9dujC Vy8^\6@XP?U(U:+hB~a5N 8G~)v?r(f4V5離JE\]ӭ.ixTr8YWp#!O(4\A!ֆ4>vO"7]ێNyt"{PiǝA}K׌RR':/6k`:vN_o~h_x?:O~+ѵĮ!!!!!!!!!!yOc.8KmU,ѫAS .OvI<nK.m>5ul"̙<|N JC+SHyvw͝Rn( Q{V-Ȉ'SѷK5:i_'EV蘱¶vd[ ܭ \q۫N9wgk0_r.+LuxLΓbzO-pRXg8WQ콇_E2|C?Ii{ hpihA%wcjcT=Pf^s>[d'4FWݮZ{-w'ƺN$r'ik۱S:ݦ%)6o*Hҩ6܈ՀhҌGr؜1Rr1ɇ=O ["X]#K*ޗ#u'N[+lD8*},n{T".k?)-$w- lVw6s6WQQŀKZgKI܃@mbhv#%{7~c窱k~W|<8Z<##,jhB1v`؎||sҬV[dZ- '9ZK6FfC0y>)uF^GѴ0f{vmfkT܇raKSq_^ ZN殹>4PQC2̷P<)Y!,x26a7 _WrMbld7#V[l0ǎ#-9$W.'dbOh)qfrVT WvELL`r/ipYqdlм !d\r+M6C~'ږv2>r9{drvKL23ZkK9/y'KE(7 =Rܮ1 9Y·PvV =•spM!1*v[YǑٚ1;Mm3`A$1Sn(Qz7M`*yxa4=I*=6ifR0E ]-Yw?dEm0in#E"`=j89K"N,xq%H( (䝬ߡ[TmR(I3ԎHvp 3!1! e[M¹$|,M^3q9IlaHxiy̟2b8n.#[>ZI{jݲά ʒ{ubd2dl`: dqX$$ZmIhT:'K$mx9;s`e6H^#6~PԎORV #ʖ">}oC.z5 ZAu]@x0` T ^ Fb6 !Iqsә#l*y> hH"%A,;kb4H+F 0ӷi^Q&7Pbȍ ߒ,emd4S uC֦Cǃ/;x~Eשĝ&cSty;K scfl2f#N$4O6 e)r'f#xbNgddF.6C}n}nwf"AǕK+DNiLoȒ@ѻyZG޶I5 -$g$Gh .ouS$!FުZ?zM{9"q;y~W"VtPZW rk:KJ9*IJ7@7 .٥@+w ,uN<Ƹ,G8f#+Hzdmx'z潽g{,FxUQZU:$$զ=v sB]n^I:[z˺ I$ZQmsj"܎5\%}mK6UQMs*T!]tW(д2烒 U4e>>zOdE06ѹ}w'aDFv~H{ݸ(󞡅 0 nog};xV_`.ji3̓8&>_ ܁/qk鳖.Xh&5 Ӧ;gf1 O.2c=Wac|2L:iS'qř1O3R 9tB[.v[)YȾݑ#RFNг|z5.;XB(UOZt &ǪNKD"u(`;(f,*%p '>4TTm6GNQPEqn.- hb&(bs&ns&r 8q1}^op64}҃Ҭ,4n* i[UqZm}|֪(ǾT"a/pݹfKN]|Ѵsj:~F1uc/2l5W8LcΞcP$[.qwjR;\QIlV\߿"Jr (Ӝ0ӅBjE}KیRR' ͼisx{Z?ο]ӿctZ:^O{mq+EEEEEEEEEE^.mKs%~!w>)sZph&ɝ{ lFs\Hr/n> "lx\7WU[Fˮ!Ӵ\fh]vao9>N=dt7i687ӎWŁNfDnkTc]%9$cÚT!mlhݼNO^<|CM.'Ѻ\M9:ש՗j H'*xiZP$X'_ϱb}g һ ZAwwcu>N#rHn4NY\n~nS̏dwr/y%2{FZ+[RO_̂-c㶟lZ^hWp3ק1Hvە$sE4M]VB˃uQsIyT]:suFӣsE @7mXCS yc 4v߀/xQY^LFC)OG 6`1i g88;qkZvڎ`?®LKy $o*ˉl,m;9рX?Qv\ƶhllb?^GʤsZf͍ݨvH}:c'${X4m=MԬB);\Z g*?>  ף|}3*l8i![P5lH[lMHʱ`n\ ?Q p}{{i DS`{L5]_s šfd=A5e&d1֗7Mg~̈=sP2Ӳ؎vtdLhi 8q`3ARq!};'d>[?G|S6דZ^ JKMӃVVqjʙ۔8KtI>6)Tt6#}pKq I &4\]S }q.ዞZhTA(426^ʤT܎P> !1yk7n$\PG pw I J(d !rP|<$݄숣1`Q==L>ZzEm9t4qIvUcL.n%Db{8|5KCnO0ɝIYX^%NAd$tȥ-i:5ȝ \CDؑŏn4z/G[xFQ,nt6:hm4P4*ϹUґ f*Lr0`!FOxK@pi8ڔZf(9;ICHsMkOm{~OV4sw|gpϔݎRXbg34n ]n77=e퀆Z/8nY8yUI4W5r.?X oC֧Y>ϝG} BT*%BA#B@f88ʚ&5;KIjw4:|cp Q3X؝5V;im.h{غyYӷunVMh wǢni#t$b29'y_*sAU7@6ooMaibeEvVU|$r>nM+kƹUbY Œ$ :X >IU T ieHnKܠsZI1$>S]F;X溶o~ y`<.K~hl],j6t,6B*y暑ieI9z{O֚*)T%AHE"hKh(H1/h0F3xHHةbs{u >?W+7BpI-5pi5}ԍN%KL(2Jvj8i:#-a0mtEvFǝJ 4'/i}Y>~1 yĶJX.W*QBxu|E~ ?~7?|u?'?WDq?bd2ĺo$O:i5a?_+;aom؛3 3 ڴۏ&0ƃwu`#m֫ei:{i՞Wvk?Qr^Y׺cLv縼7~(sGuV=Ge,NVds|^0ǡeᣪEɩ /tF&I[jqzQFEI[ sI$ rvOۍ"Wig[9ܼA\ eceQtr(=/]8-N۞i}4ُ[ kWt 1#DQe:a\۬R67Hw{>UH_ ß#@BѤ5$lXQ |B"4UR2̙dy8۶ݔa+)f^ʬ*ƫdK!` $yxQ6QnSfiAQVgHak{Smo֫xo /.lW '>IQj~UpbwחHijn'*@4WUk dkMMme]C%Hd`#5[G H#z׹T w/Üy΍|R+5ӭ\\~uC;^M`y vܯ,mʈ ʍ)I?;Sڹ_\vC0-Np#IkQVbU ԰9g 7ȁ2f9;OdYw~ʌk ~4x۲j*ic19ofsl|*A+|iϋӞ(IvLC `pXdԚ,niKYd9ތ9N,@Tky%1b`8945Y]fl28+C &O6vS'y?4찜$ŔNi=Q&)sr-$V4جϖ&d^I~}웝F5iUh2KjƫӞc%ȜN7r;&*UnUhɋk(k6s$؊?biv&[8Iw x57S{zr[LQ|mL [_uϊI?ftϽ ̓MnA\xT&ƞ-6 %x1 1Ȏu&l+ݱFa$e𞥤4j H$'/\\RdHeWy۶;h}nav o]v3c ɠjT~'l=T5m޸RqXMH'v!Q]K ~jUqla=-by7!s!#F`o».vDdu]J۳f$%p9R48rToʙ34߲Uţni!>(R}U$qoFLQc"GحeO&;\A[:mYvNsO{AB~v(z Mh+G<' ؊=^ZV^\0ݚPBՏfy_lFl5yZ[YBAov_ orH5;c,}#َ4H#p5#f>cO-m义 ;EFц݃U]ȥG:GbL_50;gװRwmn-DG<"n^G±~+(T!syqzǙnF/ m#Մ*L2w7#ôMdC&$`Fq.Р{(|TJ9#=T?>\Љ/-)~揨VwzcLu,edq۲Yo g7ڏn]:\C,A Xh!ҭ c丹:I$6z{&$C[;B~sҴz:Tyuro4O)v8[qcw?@&W8MHHc;հpQQޗt}Jklujҝ3u;k^?O8K۸4"v[<7h- SgޕI$saqefb ?u9e=t:\]2Bow]0O ='θjKAe ?"RʳHXI>Q6[3Qp(J#_=Zc('e*ˆNZ\;(f½v~.w Ϋee 2jI;P}0yY%n]^rK|cclCe=G3|qc\1 OǺ[[iT͜J88wթWN+&1@ꯥkGuW Gm$fXɜn3S`|qlX}b_X@ eY>B7K@Oʛ:P hl [LvzҹC4h9.+[gHY<{ii 's™91I1l5 yv戮,D#Y*^}u͜;Z)ǶB9FB̊pqA;x#~ 511c\|VI<>i,QKnȻuWdυ=TA b"KH.U0XfHU+b{Xo?^(SVn!"hށf~!?|6W1ێ#-?SAoN܎#RHawi=rTY΄#!b#׾x F|OGb0@VNz{_ou.!fҦD݀QBk˞(lv}.^&Fd9w摭G?H9%8ckXt>5;嶬ZvX5w2B GZFZ4q~k'6$a~ѤnR3w4FA;+# Ϧ7zmӖ;P&@sMq#.G2#64y?v{{AN{s`Jb5Ѿ9 hD+Ua$؞ PvVD}ݡY&,#(a1 S$-'nu~eE@xq y]_N?~ Ey>R%~eIg#ΪFipdǔI6eUVګp$ Vd)^ 9|[d+wr @PmکP`W9PDw<<Vg5+Nn[ y_ň| Q wVµ.$̉M}>螅n{5L@#uUb&E[k $Y}O%@ F=Ms%3`m :Fz@*2[;J FĖHe;loeQ"#hJP*F;^t59ݩM#z[!pa{G`vF䁽xyȑ.{sn+'N4=Õ- 8zS\uBl@Pf ^QsH/N#M7b!sO,劎2Xdײ1|<q* |!*)Q0 tu+ʢɃdU/oa8e̘%5UHnZa`It YC (~3–ipv\ 8+> !_|2l0A#CHb~[']Hj7R<.Ja0x\tAr.[{Ut͟EPNя5Pƿ~U=⢽HF:=EB97tQ#j .e/iN09Rj]*t.8ݞke:xP&9!)8k._@][H]^Q$`nM8d%bUPBG vcFYV?腲>4-IS6s+;| 2\wZHӳ!Z@4޶N$DKd|nmއ{ؤ[w%gFjb<ޚNM:{vH#fěO*Nh HsIKMD s T隫vHS6Nled=[@l'uجO{Ȫ^!^Oa7 '7 &ǥSQDH\ʻXb6^5zt^W p: hfdO0n{v `E$ : gUw-m='5g-c.V49sn65~[-}m!\5AQ{1Y8^{kL0zUtJn.ml[dήl;lo ,3"T`߃SHH:PFhedI8J`)-_?EXqI,YtM>;@x(=EA%ӹytTvQr7Ee!S*M sթ>3 V^Os)pfU7F Ԁ'Mqc*O_Oʵ yYM~ nP 4h'* Ta:DJ8%tLo@H ֒ ;2&7DǐUMzhDxIHHFBW Z\v2IXO4=ho\pD+4;I;FXNXj@nEՀkB3TOŪr00 ,BF]zSqmv!iyG2_kv(l+SԨH,˕"TxЅ),}6Ks7 `c`z󨵸H[fOvX4XvqP;J!J)n,+.+yǬ[~୎0-'lE;^nx!Q'js\hUYasn`ߏ ;|(*-4D#o'R1J@NMY"^)F8lמG_@8!|ok!5;RLn9;U 8)9 /'H'qvR'Ӕ Scȱ!.h'w|Fmq199hEom2uĥz"A@No֠91܏'٭ @7إ SU` '$ٲBj,%XO*EqJBDP@|iJDJHռPLW 9x>F^}qQuPb?SA4{$z]F@f٧غO1k _Q-jlhrg1*M#VRq>@כKKf3J20Cr9Ft3:weYAKt>)lбBj\1TR71p>zG"ʖ(3mؿ1;q_[jUlЄP49=]-vr_.i߱k:-k/'GOz6""""".LzϢE -WY6ךh,l!]H="碳C;zDWÚi<*xkG[5'7wtK*TvcG#Ƅ.myu7pB,n-D $E#&BB o׉uoL%Üc7W<9k=isDݫhK(r1B~;-뙵5-K@XUv& mȡ Fc6E%FD!nꭒaxw؝3m䯝 c쭵x{IlD9Hk9niMPIDi-\Ǣ=?oX;rH`N*K&`xcQ$1GMnvcɇ HֱKvMںtug'hLKFd5|A6Vw,S- w?#y@sA؊>Rjnʶhn]{%Opfts6lR}Qꪨ҈ny :-[J>ria5`P7޷@HdDz]#4T$k~W/1 [!U.;)qD8aZ$-'av(=7NhbG^i ¢W vSDD : lht>pFȃ y@vU]PV :UH7otM>WVmR˺E1#p?S:PY.va@lDytᏳo tNޣ9ѐ> ~LƻUsNdNx6+1f2Oa,ؚܷ>KUX:e:chpg̓_#u W B{櫲~? O@\G*bjSԍ#  1)#Ey&m8jLgبYMێH Y4p (R0PE) v|+$J & SFuRfiB0V4kZ݉n|ԝKZMau> .cl9/*auHbI89\G᰹ݭl%,8v4Q[,yvVj:TPLcXIm!kmnaL%1oc'ݴT2Շ WG+ƒ,*ʷIlT,eio;vRScƗrG& z+OŵA|WL~#_ܩHL=~D2fissSBN;z\@O۸X60W<{|*wpsMגhDh@6qPI4?ZfLc[t (znkb8|TxRATlPş;a`m@޿-7FwXMM,eɑ\dZ2]uoFJdSa&CF&kJ,`p1 X}Gs[e]^>ouBUokhaX\fad|yՖ{K1f!@?XhneYYh3xh*6jҴ9dq˗")9;k}Em\ qw,Q NZ!m5cF[{wQrE2'-yftq#e]~!f[WV my2eA4@*N>ZӑO[QI*f C'Χs8W1|:UȆ9dFmsXY)\{$>V\aclNi6]cqH6.yj l!x#o,58h;_|Rz7P{fć͝--oMJ7M[V^]@σV_#o^Ӻ|o;K Yt*8U݄FTj=rdmSw7]jxB,s,:y 3)ieٶEKʴ]v>V!I65vX]sTs'-G ;b+i [d^h36U!)'n鳛 6"8r8 8dnrM-oCŶr22K4)ɓD /#r5m6ٯﭕK׾|9kJ(99nvV\?<@&&)5^XdeRڽ[Hdsh#pvتOm˥d\x9,8|Z9eV34be#iK<\rthd5[!>w#N$Θ^âaS`bܝz' -2˸eA% Ֆi+E_'e7·D8W ka{l#y9cTF|?5-GH.~ ϽfkZөVy,f^uXT",aڔ jd:Cc~'G"U>m=rm\ F#(ac8MC جʢY4 47CCZ}UX#^.nJd%lŤ=7qw*1sU J]K[y$gP ']d16 .Dӵ䆊=P5*G8,w>sǙd-H\^إ mns|U21f%I<5 (*.{q{͓=qSy%ރaiohSK6]6 UowNݮ_ѽ(eU{Y QFxShklHR.%X$2iaqC ApCn;˘Ċ$zd4]bM:SǺVAS6M,s(MӔ(*9,8*.,}"UTe-n9$<|01P?_0I^ F'PmpSrRT)K~V:3NA+̅͌/su_U ub\#ZMt$WjbG̀[ytՍOÄYD)b]:g8is0i\_"=1%w,;< ̜rݚo>艘,İ^۞ϲR,'MP)]#lsxrǍ ^'HXh#9{Y\${02s@q$iW5d|FՂ7u_]j$2VB|L /`kޮɖLO-[_Q |HT!`Ѐ*D,whzPiR%C$i\AMsEJha!8Z\@,X'@;$HDqI3"is`,✢E VYY1FFFGQH)K*0 _D&SHҠd&ѻM-ΘpF3f;Aln S]@ʡsR s4ZT,F"Ĭp]q `=᠞OmӶwmapΌJmٶ )" EZv q ME낣wԫ9d)c >f֗lko].cjڑ(}*(Bohsg45f#G\JBBBBB}9}O;*ҵoCxǫ% Dh<\ MHڿ]Qx{]h?w#ڮ=q.qy!GeP42L} 8MB7ӽzL}մk +K~7 Fiڡrs*>DѮ? p ἰdS"7ó\ w C0{N⛾)ӖTJ(f`90TH=y1d&F׸n羥$r]D66 {똂blq2:UOJr߲k[fe!я3QJNlB͑!AM2vZ/6c"dRO;ڪ S6¾4Zε:TL8p?5]ѱG2@3۲ƽUr͚xޖR!#J݌]KRH:Nm ߪxumP8{XPҲ<1_9lT=ߢti pfKu3@M7$^Tì*6M4mtmq w"5hK8JRv%.'Z~*P*rc]총4yPYνV*݊'acr-9dq>4DdRI XT;6y>D$٣CmjmJoum4sc7ⷺsb"e2@C-BL:q5B/6'9kC|W{.6~&;xÄS(weT44[Y!2HϴF3{\e= np>$uM[wcZk$ꏅC4O9!~έG{lZĺpݴw1ܾZh]@ {#Pgi+0\.{Dm^(|Ls^PMJg>gn;W%@]DgM4i," *v''o8\7Z$K"Fٲq1gp!h:6DapEӾzPteHwu!EjA؟dh6>4oXYSE5 hpmPu*LGaμi,vx{31{vC6zخ#X6*8? \F;_5H-N41`s*4Yt\dᵻ-xwXGklW^ Ŋ)IϟR9}¤Cu2Ţ+Je#Ԃ;2`N`3Es7T9 <)Vb/VHHlFGH?ߍ(63X1I9rT3· pXA$Z"×Yw=B nT[sUPha: &+{lEO )!n5dyrex1]l=E|Ue, s|m*63RÐA?q-ֲmmkgLx9)nft;ZPpftw6 KiZ cKvI l05 @AڻͭW\[tʆRL-(sgZT|;!o=oІQo{'g)j#<%k"L[Ue 8nm5Km'w>mOe#ǡϕ8JIV~Ly\I ]lMz^Bҗ!-Wږs.ѧ{۷Z5m#,xC>P4Ҟl2LЮߓvdK.$w q)|Myu4w)ˑhv `eױSF40}n@mSVu~++?oƆQ#Zh9qi29dQϴ*|3M{KFwωzcZ4As['9vDqSںPtesfWiAWRAM==?c _4f6hw#jE1X*$ e1,D{(֮K^uk{D濕_Jqͨ tO!ݶMt#aɝ>G|`A5`lvW NUUvI9+4cyխVNqEOX* 1a24v.Vbm ׍[Ƒm̆ 5ɬ0|%8EŴ]^׷+܋{iSiN^9H[Pcc!Β+yM$aWtlYdl+ G +OIofQG61=FaZbOpR厵Op (=*r#3NmZ_1;5mPsHM_=uui%yI13 ^s򟭻uIsl]7} +y (cSHGVykRQ4~܃:24CGeBX’,s1hKۭank383!%K,#evleABW4(^$ ]OorW#[3aOOuJE;tӧu>c|ɃnN\꜏%CJ{(a' wGY')0󮧲cIivM2\jwʱv+@p: CIi9=Rigc,sM )RViuxʅ1lP9#/uoTd{H :n4nHm^)xdEeH87%JO:f o96So29ysQWNJM8kmu[cMvnc/>}WygKXƕFhJ΄g4%X*%O.[hp:B@JȤ4~!a˸s*@^9> H $ E"m>0<@pwWși@.nIy;hPK#(BlaUhCvHѪwbv{'k42-b0F䲹'MKH'(ɉ+",!rO,jSS0tOdeTts5O99q)a 5T*lbl}aO ߔ#^^]%M<(c&Y Vbڍv$Q43A5^A5O9,c:Z>$RL0<޼zVGdw lvGTN!bݠq>>mVlN7؟A2iꢗ鴁YRR2UD>MN<-H3 p'M v${M]AR̎cbmpz)vV;g~;%5NUPFqJ!J(H*hB";Xn;n2g#F;FU6´0c( ŠEjFyosڬ}(IAmD׷ta7WLyhN@-.KhƝr332#P!VcPm#Gqs_CQGU{-B;aٵJ ǭHD,pVt=Uyuyh X=ZTa`:Ɵ7/n_\;8 ~A壭?F$PPPPPPPPPP5SUEQ0"Xn`O,##`Ț)2-G}g!38aSY68pDs[iG!H؛!nk;B0G0캗͙gQEn@$6Xr*#uNc偘&K#AHH,<򣭶#v`0}ݑ^GB  =EF-RL.lNm| f$J"c<5I|Oc#3vFю@x {i괰ld8 ,&Y'5&a >CR{-uǐ%ɾ@2ǐ&;ᄓ#5 "83r8{rcLդ^‘K^qR GmQ2!j긭x%/lPi,W-G49!`} տ -%l[8>95#1v+Gg3kFF5( *WU'@#I\*fU2 4X 7R8SխfV gUM1GdjGܿ\Whw.!c֜$XIt =LRu8lmCTl(~,qkZ[-Y"(ߔ\,RIs<1Z [I*zr%&2LN9_Vc\kr@CkzqIܑS+=z)?ku%ncW *-Sc’C#둥 6xFޯ2[MŊ(vH=.,QAn;!v#p)|ex:Ox/D&\Cke8c?0F$Y= {qۏB&^䍶 [&Ùo}ugP5VtEtߌS~ZQR21#vwV]$0`;xekٮ>ObɃ)M5{FsiWI#ՑKZe]رdmIݕI^Usߊ_.K1'lύ;w.gUdxv<ևhi 2gd8 N܏km%K?8 # m+nNphCIg#dIA9VB/ix0$ ^$ Mm$tB_l;wZˈi[3]m968Lb(Wrq0#顆0kEi$['}毲LrNe^iiH\y_4 –pl Nq'LSI@VX22鄋uwk~8G-f9,[!yu ~ZXk!֦ ˢt?qdE l"d곱F9UdC0"㼞{8{!~iEގ?eBӂtƇݢZJT=I{A{h+B<\#ė[8Ps)=XqvpZ4Fvto2m/oDm]Ӳ躤> 0: f6\߿j,qfjW9 N3d6Zѩ&Nfc1:?*)${@eZ8]?*!/ - &(2B @ :ԭ +:gx 2ȽpkߚLR*I&,B(Bp\ʶ,yO)[Un'<&ȽY#%/$WĞ=.w&<eLxE=I@:_;I` ]AjOåM%̐>i#q;lLt чI3u5nRLl$<'7\R n'rdBB]9fcc8m蠏+. R8n$(UdXǘgǕB &X&26bd86'p ":bU{dhBŒʄRAB9R!c9B9PR!c*(B(Bvc2Nza$QZ5 XK@YXjdi1`Zy3~Js$FY{KqtcKe :~vUEPW,qko}J6 u왊R;m&]FABHs.[{HmKəQ2wU{c4->avL6>Py7{_RTB'Q8lcZL;m[~GC cK8i%UY'}oXk;j}^ȥ-&}y5uf)`dF=եFn&B 9N8U\?wI&/iw_[jjx}*(Bohsg45f#G\JBBBBBBBBBB>~kLjqq8{$pK2erWD n='AoÆF;ET?ZrPv0pδ[_`8N׵z{ڔv ER{y+S44v)?…#0zUYcadA%kkyRd{E m50s# E=wo ؉(9ds-B׺;;?tC,Qvu*,|*RI=ӱ$3H/sK 6}{PXyGD7>]}6-#͹U?Hd G`nHF샾f]K/D[n4 FTN4:]1[jh/-¯w_]3$9*yrkA-'ŕʑTc@mis.<کA1VhƴeElv;Co$\C*mBV uft|Cp]iF]gP.ޭn]ŏ=S2;.^Iek77ovV5az֦ #ۮԑ*ؒZln\t+Q;&w98rU޹U/:sW#ix -iv;3[gE *"*|l94杈4B={"0ZB:VFIwuqb7Qqdv=J2E \nWoUF(y/ /sCd["=)1'[';;1c\ .gT a.Su "g>:i@PjKܺ{M ia6Ly݀OLg$ckX!Iq[Hid2"i:aEfP ?=hG0_Ll<;ؼ6餖9V{Co,n^=R]gFH.XNTQ1iSZ7~F;}.7MC,2{|~.?VK]-Uw[̨>b?H~asx}:}=Oakiq)eʖEWaY\ #0|@C ( g0VF\{O-?Xnߴ {𣏕yoIa=آ8`;[UDK`)Xu8\|b]'qW u4껬mDU#,?,ƈp_ؽ0/T 녬15h -}-Wn`pqGCA]OѲL4Tl0N2{5tikGTaArkԷV81nvF~WPzRi:BB8MQLZY-6/u,Ag^8Pu;7CrJ'<4-l,]4"@ZEHwicX0״l;y̌ ˇ.7N<|dpq&(}4-O6~o8a\ U2#|.[Um؛T\vN[{>b $zԭ̘8=~ljO#j l @>ڎ0 vfSk_DU{uHSKa%`gϟ:c⛍w)kӱu|T`8>ꕧPO"!8:#p~ 89PNMY Z5c{5l\dh'` އk)$uRU>1Ԥ@=,GhټZ$v{(ݻǿOo{ײlJU]sB3o"9rt'o紊(*\'g *FsҢ|myiwn.~F$sE 4`l]DrVzU9l+G5YƜL-  zfFeŶP#"WK3pM@^+2}`8bdNztG-GwPʄ8 , Ŷ xR GHֲMl/$JS,tQonqBc Guҳ4Eć8zA;IRk;+dm<VKәrwگdʒ*O8쪵ŧSM ]r{'l|긘edccOkzP7K48bdTnP9Ty%n}g?QŇ :64H>~ VѾ;ݑ֭'jй1?kd56-MwMM;h"yUu=$g;@HYd2Mbhw7M[UMtU{G s㺭a h;i'\]yNq1 pLYkF11?fc~ BscP.{dDzN._,\JH$w31'Ր@, fy,.sy'"DJK c#vj<ʘ6DWo{7=UXBhBiP}?֩m ͼisx{Z?ο]ӿctZ:^O{mq+EEEEEEEEEE^jY*C|4P&n6B{Oj.ӻ*Fk nwScZY84Dm fꡥ+$vt( X}*q#Nfd쐋:wV8KZ $vc$(U:OH#H{[0yӸ֨cov%v1c[u P{u7=>RC8;곱ժBUH.&{m价X;PxgJ؎q|R|ٮZA"'C%G&;HP\hS@kp"g4=V-1x©e,r1J=)FFl@-n=EC++r"yɰxU 2^H؆%˭<0^HuMnHa˦I|JM垡XO0I)#5^x~rҬ $ Q;q=9O Z# Ct渴6®sܪTtv%!wNv[\1Ko~Ƿ/GmUBqUdGuZh M*f|άځ(P?@T1F`ho9y٠F7ފL%̄uCELbɭON=3;wYRw,n[2llG/Uz]NHFXF{l"/rrI^-2k%Yc6A($ɑ뽬j@{ǵ~u!i %<[iԲBߤMnkX+;YgF?>%hӵC8i;QQFYhoj[&. _{`R<,>j0v=px^ѹ+c+;qiky$$AlݽV}on4JSdQ1k`MW5\q6IvG|Go6C?̱y6hja, 4oq@Gz|Vvvl梅m @/Xsd gyVu|]AP}f)y[bH\n*nx!i-,iSxv~Ub|.)K鮓Pj&Z[qrhtKXIdcwN >Ls3r#KvӨ{(ۉzy}1-wS 3$LvCo툢;kN:|Iw9۲gCD }k*'m_5 wƧqfXirm2Fmc R%KBƑL)9 /VC=܎Lō6@6+ح8SOE5/%չgd˟R7.gc}GE:]erIQ6w[V8%PwykwXU#O]FkrcN1S9$`T,xi5](5+,Lwȷ:F)w!k9|oYP=ҡۓ +wOxo8}%S/#ܬgi{Nv+ HS=9{V,mޟ:M"v.%`N=nS<suE+e4iQkwGLh+K]8~- #Gl@FVAF#d8 ^GSiOѮ䭭Z@e}}fG'sli ,wQ#"Ɩ$nGNCZӦ䡐=+F^_rͱ鎵 !el]RY,$Hqd%qHcWl@RXҁP@$NowZ7n7TH[ZҷNx#VGj-^IAv1ׯV6T|o|mp.櫵oPnnfA˿+FlVQy+<1B&i!c8 C¤Ys@.C)Hjrd%r@'^cr@xr{sQ6RǠCX)@2sgl1.4t\{o@zbh ^6dbR4 : i {ҨV1sn$] c\BYǛ~!Μ~-U%'fAPy <4tG|x+6\)bHBJMu h[6pX.:僧fP?Z):>s?CVQ$HY{_ KI r#c8aίxύ6+)ZC:dr ,ҡ8o,4Qc{sf^ָ4ʵ,X{ _ q$O;i``rIH"o^?ʢBŋK:Ok1Fla+;wb7H(waW&21{_rG٭VlvpB7NuHO\^]c?fcLj?ޓ{uJV[L&Y?p9sŔپ*CWKdkHAw8 VV6m澙3[i#cNAʪ&DQ]BHPB66G+h0.{HLBɎ[Bۜ羗H\cjo$0ƖlYt:N;i :5s"㊮-R+ IQcb*3%FOdQ]LF_QfcsCMXuf*ۮ!I ܉*A #"|zՖcD)y\W(|M(lcb}ytn*rsjȱ\䮍ᶶ"s ȨAnƚZpyaln 8QV=M@=mk kn*3'.@GNUk'c^~DNǎ&I.nm웸ؑ9piZ"oWK6@|]՜iI &~:tF;ldM-T(zGR!!f"%O$2> <Hq<ÅsC ?`_R>עC\E'qgN&hKD i۶] 6I }E*(Bohsg45f#G\JBBBBBBBBBB>DtPGSO;ەC5xnE,16?Rof貳^A9 ~^5}>Hs!ar> iy;4څP7eLQc5pjj(.-2 +P*"E6qos@Iff<{ʑ?lkYfj7j=Gl̐䂟AuZyG#4 |6$;n;8T~lE 9}kc$l+{'>|xq$Lv͞O {K=sn.wM o:R̀Ulk[}8kX-Cؤ同'b&<rz!CPxY2#:ȯ-`W-il~\&A2Gx)Ώ ؋N Fj.ヴ$;ew/@p?ulv/={sBe7Mu۶KM3G\+-̤Ģwq) m.܂=֐9*r5 @Y=-;(/5G >Nf2D(ގn*_׭ns{^u80JS+ݣFQSoV"8slC~41ͫq=~uLo_xFPU}x](HJ}IX?mG#cH˚&H\ʯ1R2``+%s\Zju4u}2kC1>'ki;)2\YO *>[zAw/VIU49Zȋ1V[]O-vH'ex3w ,-}9]kӆݛ+[de$#Ǿ$eݕ<.~M9s;)J@Gap끐 &x|yW߽m;ZP.l .?uK qpk,FG i>^XɪXdXU.p3 Չ2ӥNJs NiޠwN 4 ib0w^i斍HEq ݒ]3(؂~{ibHjKw+7 qH \ۋk,J!:v}7Zě;!Jh5P\{G>cmPDAswXI,;_ Vӭv&䯲*YhN&k|׵l*rojU7\KZ^4(Wu|b3,U)9RI,wM }{-ی&{YK,AcYZn,rpiBqAحߴZ a|.Dw7Z/$8|pϲ?Z i8dGw%v/77!W^[Ib xU'A$O 0aaMu(;BM[Q"e(f ̤ (cG(:~< A=- S@m].4{IIv29*ApjlU,QU^}m]ߟFz9~8\tcĎ u#N*)72[Ȏ,?=S%E2кss7 4Ez~v ;Iq#ڐ~S8h߲]D&\v\5< yA'z[$~%iX1g[T!~8G6s#%ulA EK?jpHKS=ua#YxyFjy@ ;읏dtN6c;CHHڿtq vj^}rJ )h XΠʊ9c%ַ|3&O@̛ @nEcjm}y,sh)svOD冕\(H%ms<:rsO .xeڎRݵ[٣X—qڃGtHmKЮт$lE.!;@retank3N<4faǠ7jV[mG_e{q᳒32HYYGɁTrz{捽S߬۹@8`лEoJW~i Io|sǟ969bvF9BϚ&G;U'LԋU%p@g +NJySecɇ3M ^B=b tL?=\2%Zu yxMe$9f![9eKl_N{-J8ZHƹ>":&vVJwz[&6#&M'=vmc\`x~W?gJ~hcg㷯d,c_'ܨH%Y4'& ''bg9 0EQ5#pŗ<InA֍"4Fݽr03O$P:GVmv}?"BHb2n j]ԾPӤ/NoOg矝WyъmbEgQvs@n}o "rJ:υ-61tbg `eH$r8PYU]0`2:n^uTkxМhHDf=#ʘnW201C uvMS@=(B9D+ܰeb\nڠ9 (wr>O'f9@޷ KD⛨tsRI6*OZL%v(#S;sY'&q4טe#}ϯ->@ݺHQ"8ﭦ]^G>/͵E >#]GGu%t{Tæ6L&3A :y!7emyn6i,},'P 4`O.={9W73Vk <ˌmM}6| FyiY,lM%YZlwreuꎍ,W9riKZ\j\8JtO4xM5O @OS4H[ߓmmI''O,}A7L{\1p'}JXCL,sXtSFhI Z}F[E оf,? -P.U;mלTy./u hyT[zi5?\X*x˝K;DB6;+7O7TwUfknZ/l5M)Й-8 2 ~CMi!NG &8<+1ɵU]F6 jL{f?q1o앙6:_>Buq[wQ":$~pȩBˆGB#jǨ)FFy{߲cjw w/ \û(3Mhphܫ9ӹ-e q3\Prv(kC@hLȝ2g՟@4yX=iR,'"scM 7Bdϡyu_NFut%kfSYe 옳;oYdG~|G=Fx:auѪwb}@-me#ifTAƜ.q8fL|v=ƀѻ#NŅ XDlV1iPR[_dq)?0v<ƗnU̖Cݷ/occ^*ȡ ͼisx{Z?ο]ӿctZ:^O{mq+EEEEEEEEEE^>iwi4D9#1PwC7| ipF{(>9|8pZ8&7Lfǜ]w;o۴ά# 9W1k\`|Yh6`z'ߕzH'lU2s3cNfq"6㲟'NnDxFv/e"D5 .%Dۻ<ȞIxAYo'=m7g[C5!K3~ ۽:*լIuǪu$GٲsBjo`2e%V?%ZG73_g&/Itwte8)9Q1C׳:+ppsZ@аh6Ҁt̶Q)p0y{+;~6 p؃(I܇AWwQKoe*#2@+4ŒHk#1&У%|ItT22 p~5ϹwfRT5f䂹Xr֥iԀ*Gco`:w7t@6mTw#n뿠4׸9T,c\t׷ئ&svV<۳_}W3 u=Ԡs`;hp731,0kVN2hs{+1Ezg j`m; ;X3'q皖RR~[FgI R|.̌ Zhe2ID oo[JYnc?%8V SBucOi\\ ꣠K@&XZmYL!p#%tMcOmpەU$w ƑOeOf/p7>nfvFo![rs~9cgJX%$o$+K"XTY}ݚ5! .Mqvu\wly1JW:{35| ߖ$lm+r#ă^X!-w4uI v .v&Y=af$|l Z Vp7PUjw\>oIwjUo Nq7[\[(]䚥[k}0q=p9ԣ |/̀9Đ85,E;sNx-+IH!Hr;O>dtQCX.' 8\1dzˆi7}h / !VŅyUJ\|+\Mt~$q|7M@s-Jm=& ŶP"fm>$dS D`5Ͳ,]Kӝ]&F6@7|'Oܐ &)|ǒ51HQ=1T9q$= |^r,9UptѨZ{1p(CU'#-GN˓1 k?q.lXdlF9^k[a_ˆ陓3cS~Fy-a]aIuɃ{wW81l~>YuuZ^aߘ\kםJ5w .I7PpVL/0ۓ9ǝfOmR!WsImkM'Su.%raΫDYJm'K;]uh8x3T+[9Q{b25l ď$H$"&޸kv,WjϳSdbOmkhpvq=v1ZK~k'H-'e&Sl/gF;*$'"FVpO46?or㟑К0\$|K]8 ;)w-gr"s$ܡPCSہ8Y滽~#E*89{ 0d|zzuVڪku-\HY5\ia$sȄ8i+nOz8-9˓x[ɻf‚A֜O)Gk|i*sqRسi3x7G)l1A+j#)G{ii{)sFb =El?Kz\,vQ\PKtVQ)B'Fݠ6QRcV6 ԳqmZӓF>TMku.Tf*ƍ qTMrm@n5@ԢdɂiXxxU@+"-ù 7+W22\fQm9W!sY#] 5lTQvawc=6ܮGPD[~= Ե]Dd&\{*1o1bI2CxۿֵI0"0H#¬aV)(H RCBz!.hLRm%Ir# uSK o:t(V;J)ɥɨ(_WFNḓ<<3#[LRt0"SpnݚUl#?Ns^Ne^)lnêOz̲xmN{;[n*M6H!%ڴY ֘,\}%RI&~'1;Fٍ/1F nfdp&FLcc@BT(M '8•>JhP8=@:ҨՀwQK$E*I"I>u2KAndLdZǐo"m%xR6dF܆{~~^6;O4 -ю{51{Y'r8ՙ'U2CN◢^]q/S{\۟ui}Oq~1U˟8͕sslߒ=;5LiD~p[טJD|sdd2VHm7*G\:[-g+hdq#8珞M, w9]I7v$m{-.NmuuIqq|NbAa|\B)!y7\E) K)Fހ;^[<󼭎kM1M+?+5&LoUZem,[$ tmPQə0pڨ߁C~v WTXa8N-r,Hk@<-җ,  kj)$ɚR6ޓ^TYiP6"j((ayWAgdf`capѰډ|}YNYFR)e<&qsO rdr %EGJ R!yҡE*1cJ R 4y8+." ~⍋MI$&W$E ֕ o].cjښ8_@JƟ7/n_\;8 ~A壭?F$PPPPPPPPPP϶7}C霏6f5._0;(d^fL59#duH:(cq:LgdtM97.#I-mEܣ+=iqlX y żoa~Ezg qQRv{Ĝ@\{I ܲHY-V[C~+n~Sb!N1+>ҿS@SF<7[A޴Pgl{=ӭ%BQˎMm {$9\K 2_yfw=zvxhd%8j3;!c3 wۿԷximUnJ1{MnOL1rX#o(ڬoE֓or#32*y=΀hNRY/,.;^r;Lո+^ R+bO .09^fד\$[YF9c8':o^Qm)^cn$YH'kn3Xoy1Tx&'raM3g Aݎq]MPEet6*2J(N†KtۍI=B^@O§24Z藍.p{=86)K~ø 7h}×*|p$T)cSDZ6+Iyo&1`$h6;Zsj =5ſA4Vb5̀ |lSH ReSlrB$bRCFȲxZuسq7%@؍q R/7hN N@"ĎY!vAS l c$Ƴ2ck3'71]N;aW],fwi- )O\*Lj2=.^&Wﺤ[cp0:;0OVC}6 I1Jc6eDYdӟHy}FD\&MK]G>.i Ai|U84~cMtnbE€56 ۫,=zu]궿M+rҵ( ?Ws*y~# O+>CapAt_hG.˒Nzt[32C\1 W3g0@vonm(b>p͞κ 7T(W֥݄ 򦗂x%lb@٥s"-=ku+o "\hjL+8U%k#i{^in}Ήv40+:̑a`i½ t\^[3C>bw!Zv!lMMnLqm=8_H|Wd0JC;EqEQ^Ws2W|:02sFA{% 6bJp@#Vc'"{w ^)Աt/BY$v#J}2ײVG1W! ktA:O{; #I[݊&Z)a1V+J,̐|fnO.P\ Up,Ui]'2]`i#P؞WV}_Z~m#x0v^JMV|5MpJ,y=%V8Zt6f#p 'ccgW.ä6k%0'皟:奍>͎ p(kxirii_JypYvdr4Ŗ11)ÛQ䔣[uJxB|%[Xr ZXRn5.nFݴr>t+-onM򃰌MtnX0Kc8Fr6&' kq<*"~F}Ӡ$y~K2YWiyE=ޗ{ ʣwb{'s$5]uWOs-ͧIpHHg4:v HZAM-2X]p61sኟܥ-ܹl^V(J1f9omR GB|B3BTs g4umx`|FlUvDyͧ\x<˩HL眜u4"CԞ%7PvkR˸+*Ke M|>%h.-"s}/u$XiNFɨ)VHdPpX7*erEk8iUGBDdYcMͩ{d~4zHȧ(RcXMV]WBIf ǖUI_-{pb DhA:vwFEW+ i e€{j7g1½'Lb;z܊{ZK}d`Dm$8zS􆶴~Ygl6of7pd.G#@#9{J~{3eqFC@ښL(M;AS=$bF^l_%5siO0h?@)Jz%DjMe:˖*FtIu T?wVloUvw֕$Ws3M(mLXAJ PHo9վYullCk\Sfn5n/>apuM$qHʒ`:ɹ!h$8J华!zҬ%Im!0}R=K6 Ȁӛ}c;-QtvH_hq'p$r^qNk!q:M}r]ʡ43V?=ۙ|;QZOjg !"_Í rʪ*Q"#"Ѕg# !R%FhH3BTBDP!=tṃՖEM ӻ55ם]8/U^qLu"HHJ v'"QatxwT^'<:<]ZC h;Nqz{]UXRX;*kCxX&"/iw_[jjx_@JƟ7/n_\;8 ~A壭?F$PPPPPPPPPP_}_^dNRi<>hҊ[ttkvlbFd2O$dSWBnk4Is컈!.sl5ۃH A3ou4qgmR \f+Y#t##(!oM;9U a L.*ߎ+TJDgwd Cq69\zKA;V .JRG>AE"5khjM 9¬յ[Ne:N1^bAȯ*N<}c-Ō1 T%}>17G+b;} [G z=ɽr!{Y8oTeC kc\EYBp9cWGts+X;a,npGZ,!c%L=yiɖI킁]n~#͗O:qf5&Vՙ5y_y uH$z:OD 썃bHz}t]E.WU 6L@1p6צʲJb(PqM"]:M.&dxr =qa[A;NVo%ͬڨhѕc##–Iׁ!uCM+\0&Hmo;>~ Ф^8 .sV묀w*I|L(n]?($j2HT~,TLY ._ {O'b E`0烙;]d,$cDzV1%ҪcVʟ MfGHj<3.sCyum ZT{ᅳ=SA4HGvV.F!OaLx{uAW8ahfS d{THs ;H#8:A#5t;}8..;9x񎹩e66Z~b>9蚍*%V= $ʫKwB0ǵ(4|;DIjvQzxs I*e`}5G10MM۰u[9"K8'p۟q v+I ZEڏ{E3XEӚH˼>$.>SMxPCE?xp5@diav6Kro38ރR-paJG\S04M`[[uQ_:좿&'3D.ff MME&#{0kqgTy=9uY` 4V/BFt%:6{sʬ=\]^ܛ.9.3zsO"4/\ '?-L2l-v-<8qbm2[>2]s5Uo.-=W?3RցCnConv!&7]%E1x쵶hZP>ok$hDRP3X&x(긑cFA7pV׵xm` X&#c=y$: l ~~ uo?;ZqU#7.\ ܭ_7Fh~X{הRJauXa9ǿ4;;pdw4L ӆ4Aخ4˦W I<˭sp%!emKl>mUX!L_RmPZ(O}5ZO\wSΙdh ' OYZ*kٗ "#Lm^u1,o{&y}OצmPBLјIrN1ZhknUP [}^o,\0RekL74ϩeH/}ĚTƆyoedºGJn4>S6,a@kSbSER[v .g-To\5`㞧h1>u~_ 7'sʡx൱7[Nքazopytڱ /UYuy|}?3R"m40bL3[Mf_> G#)l!F({zE'ɬw|۟Ug)6m ^g{"ga^P\nJ1\9vq۸X(:GFBCn.TEm*Ff$V"\Շ=,(11\9ct1'm4qdT\nbd㙩IefG{cg$1`dyL+4hر6)4]`t!d!I-W)pd;;3=MD9mQ}֎^48L$/mO\fY R iw ]ҖH]{$SH" QHRXBkkm92;΢:I F#,lHؤk{w%W==>Rvr!}KیRR':/6k`:vN_o~h_x?:O~+ѵĮ!!!!!!!!!!xﱞm (mPj' 7ު[$FIa?IΝ =\PÝG,dKqqud"ԕrs֋#OB$ =QgLXqnw,tdeD#iYۋq'~7ĜCk{noe$z 6E4,f?U6cuwHmwcDU#w9ğ][#a&$dʤodEt C 5X2qqWmR>5dC,0HwVqǂdY` Por7?_ |Kx߲U\nɛqV`osC{4sk2D`;WP7]Nv'Nr]6ClibܭDM.ϰBZ2>5I,!_CNqi6+\6sS8Ksl>Q3c4LKNߒ|qh'sɞ̉DLִt"=xtbđvY|%E4 H֒ըvp<Zyvo+hIFЃZontsLܖcƀ'NiZյ0%V,{%Ǎv*iyk˚[4HK2Cu%mK]%H'2[G=9:ͺǗm >f,`ҡ@&k6$`9UfX[F #G ^i69,EIJV=^G,M#V£ܒ6&*)?Chd`|U*:k _rƛd("gE/lP=Y@qyl[ˡ|Yxգ,0GS42 e?pΊqӝ *||GHXH hmثI`ݡաMmO0lE\vccCBѨ#cOfvKst}> F,öq!GLbx%Z=_)4ۂZ4k J .1SߞUD%\eޡ"l&Y;yPtm"kk!Frp>UO6Ak v-qSH7OCxm$H-) $8~ǪI!:,>#F-4MANՔ] }qᖆbsT2zѰ`֟pkUk:eN )[,ssrɰy{Wk0b H-)9V$5ݽxen=&ǀzlvTjPGmd ]=c^͗_F~N7ZKyPlԸe[bNG*sg,vIjU:l8ckפ(lFԲYF-c^۴2bzm*Sou@~kvNwKjv"sK˂{@\|bFާrzq7TmEOJHnNxLdƧZlM3 ȼYpp$*LGrב?X֕=ໄy6-𭯿3KFpj>Jb1˘;/w~j'Sh DГ@5^#ݺ3uQSG!)i}`?[|Qi"W3٭S"9 uɨOl%y&c[3VYf~bT2I(-*qUZ`]M0I!#d:K0boWpCC0{R7'AHrAX6>A`l hxwG5k6p֗R\sq_MM7B=K/#-iG~܋{w\WO}^ۆWna7gNT#\O p|.4[ #Gpյ[Ik6wfqy{vz{)ّtZxap!BO=˜qͦ z니eR@ؾ\qsa ptl\w*]k&uh*еgb|:jGӕZO,+К }#K':3d47U7\rFd1YDvX=(A|:lZ(,yek6MDֱH7η"5RI-lvL3- w*y |B*\k{&Wr8(v REݩ-5; q'P_+ i,k^Vqe^Mj h`5@9=Z&ß DMN]QkYϩ60[Zbcds@$/ XpF"a.hq}pwi 8p}E9=.FEƤw:cIkwZG6;2@ydAVai"{5l5:2y۰>}6>l_Nِ7_tQOݚ1wMwp&-<5kˑf>C捂AWqulDKjߪp:J}"TjW6W:տ^iYf$RcǰmIҴtRwDĮ~QWG.$A3d1#G-$or'9S Z\!hڌ+k04;)Yoz?[esƊØ'0A,DF* >2|*EAKS 4YVº[7dUPeNbPi39R5VYHvjͿ>ugc-?Ԯ[&˜ڠ:mb+c= }n ]I;6P˟wfXg,c{⦚Nv_[6ҴIP]ѹPWɪru9lou?Wޡ-/n؃"~5-WT3|=2}ӿN|m˸"?5 <jU---P\& <&G҆}5RrYa4^*'U+zEՍ\dX♣D@ޣæ ?zP)t5؁Zy. u-nݬs`<#Z .w:y2|Cg M)$efc0$^#A6ފώLx8tsD6EHוYs @$N\g;4mpSXjidcM+ik<0<)ʲcMFD O:mb`|j !lǧeզ?6hj;h@FdKw5:$w- y,lGE4Ǹ 9W1/J#.#{vL`Ɵޕ&VFW1;|qIBﺐ'j:nתlӔJ]>-ʪ'L`rҡd4zvGO^#C۸6ۏ`#+dfq _8)(TMY=vڴ7Cauo19 gjXD?e?w[@jޔ WLQ%arm45Ê ٖ/}z3(Eޯv dU (JaJmsXKJDMB<$} lsGiڰ#0A diIE#܃I44P7v8]hq~JfRnu\^$: AԤ)B48S5WdwBC88y`x{-&.k+S~a{}j5)j4Y,>Ѓ>ySK$k]wwZ8WSoa;wOUD#V|]WCz@^m[}JtI6chr߅$a[^cɰ֬M>#o`:YuW=;3C~*uN(^˲ i.n:Aᥢ*vm#Q,^$>aϠ>jQ9"Mej ySice&/'r|*2X!pƗ^$)0$k+H$O Ye]_ i6W ɐ} wTX >5grnmܷUXZ pDƞ4ؚrz~`I+fˍ>K糊"#ٳth'8`"w>.|zu#Un\\j?e-nX`#;S4E7]ZDKi o\:L-]$NYC)kCHuY rujV\9ui{<2͹8!!Ҟ`DnYKN|3!c$OO%$B9>'&,h-X8$o@|n8ne+m~qƟwm"Ջ 矴;i Fx`-b58%' f%ag'˺lE Lގ]1pkݵw޵ʲb gbCҨD|3>)_(ls-- ޝ`Yh9Ѭ^G8rf$`x|3 O&?Y>'쌀pz&io"J,֏s`|D:Hcfs]$1'sˍlvn19W{o9&78VS)KjN~$plݧ3{+@u[=Ww.g[ Z#WOj],G;\L*wdǩt ¶n1` oPf'+5G6\mu C/B^ZHA6Gո^x6yte42{Z7( {_5~ws%WL<`X9ve6XexcWpT#'9ei Gz>#" |dԛwZ~}` bm"pD#iHOjqVqFNN;5Vn۸\w);ņosM8;+*lq!0I+CKD`9m=b;,p!r[ o,Wa`K,r[h;߿)|3YӭL{pԕ;N|R=-7K͊ ^A-s3_q5 zfF0ΧķQt ,a}-Pki?"JJ+$`@\Jk[R3ٱ CK$sB~ˈq0Y'Zs)lm!{r1!f*Pvkט=n e)d&NuFG/.lR7p03Xc6YtO:om>{ycǷk]Zyh#]' ep6 &s\!4IOFjLj eab=ɖ\֊ s[cQ<;US-1YԮ /dBc=?N<~yÃy9N6pU]bMխ0½%9KdPbڜw4uƘ 4 n4,7i1o\nTF3~.TܝU~Mmڷ[[qrFeم{$ƓtszN/aUB$~lV=ip ,mbc`Pyl>?JLh]1MDN8Gݹ[X>KӠ-*߻;z0X5[+:7\L+j7vO6Ye:q *tY;ʱh5-%o%=gwV>k7uzoޮxM:STinE1JYTQspeh{ dl}sVrʐ7B6O^\C5a l%<3Qk4WlK^Ͳw+r "ƽi@ O=G{IWEZWE7H9mcekBPUoܰyZhȐԍ&uC$G KYϾwMAJzi*)v'(4OdoI7+׭*eVuAOOe54O qw2uVM=Gz˺ǀOe~۞*rKG/iwq_[jB8_@/6k`:vN_o~h_x?:O~+ѵĮ!!!!!h~=5Q{;;xma3M#!=Y!Wqލo#ִ!}$Ͷ4L0PRimFZ۱۩Ȼw sbG@:Ѕpm8.NxwZ_$7Q!F&۹r3ʄ'8WӎDfk,)EKc{#i]$`Bihm8L;|}f F}7r]oG B oe@Ҥ?liO ;^\kr%jتqÖ ,4ậ$lOsobfqٳ;LI5vpq[1SKLRDRyqi ɜ2P2MgrE@in7gއfM]2'FSnʄ0ddKM 9?AdDV*K/v2.憙dq$~#ﵼYpQ{֦A F1ֆ#:\Cr2tn܀%}(؞u1Gj[^_NY;-.X#/ΟizVY³giql`JotyC{HypῆoZ PiKws34q,rv(C%.a]MdS`N [$$9߽I_Wlnhq}deu3 !yY}aqn:%Ý4JhxBx -#Pk;_)3ċ* 0j8ͺn5Cob\ :RC&`-.olk4DrGX[-Fei (ǁ>Ԕ*bWi WsU֝4ӂьBXխo\\*7D <Ov\Y =ܬuA64HV<@>]sjlw{(^Z\[q|q4 Ьr"I6`9wu=6L|6+d`$]vP$&<:!$cс;KC7_ dP7ԕ]YUmEp2R͏1ن%#8XbQ!aQkMoRtR\Dҝ.D2i4wp][ZOhZ;kس2Fh9*P9jת}j $4 H#({(rGhO,\iksyrӷ&F;ťSHJ*pA|@ l;N;b<659wU9"i٣qlxr؟.|{!{}M[x[;j'4iuO& &dǒVmN&UHQV?ԦnlW(jwLc#bqf…AcmJ״i/,DFXm^CܪI \1_#a݅qM_4B m#\oٮQuu6ޟ< nsX۰۸^FN7,[9noH~͐ utEP"`crcnw]7-Z8 I#[ S8Z=[dxcZlM6ʩY] ;CyiXOh4׿<-&0H iH,#{[QϽsOz1H|"-'%x1'p{k!DӖw71ol' {`G>`P\]fȡZu<'$Kf08-'SƗ5@ Iyۓ+6, ~7Xa~ޕ?L긿-;2>vVhsx"]qMqLHd*Vc'NĕfAPky"ɿz.-ut01wh݈®sRMUMѳ^wBOkZbgi_\*Qet0YX9U][haQy.ho V] PHy.c`H̜Dٜ1KؑDĎ.YY%PeIߛ˧9Zfѥ?÷vEG,A˦9$,1VZݦhF$(2H DlbRӤk^ioDf[maN;L9_]hhwNڇ7|];}#pAd'¤/xۏ.uX [s1W0IZbi0ˡtm Mv=iϬ։w-U_Æ)xl&#ֆY\hJsg*0/:,S#cѯz/ raeN.=AG Ky۷Y OO>i/]Gqs;~uߥRpZ*Xʭ3ny6̿Ã8v$.m諮$s4s4]dvycb' 鐴@4~ߊZi$)\m#Mwk^WK6Ny1Н{`&^mqG0>[__eeKo*#LT(J҄r* 3e}4icOI{odƕBT 6;'$i - ֘5j6s4\Ӱ;yHB,`"]A,b QҺ' i *Xt o%/,R3JO1  #{#Mowک.mN+[d'E>Hؚ^~LVYӖ6#RB f\@9ds<=N,|1vA"˃ho~=B~[M* ʲDAUP<8,}Jtbt\ h@k&tŶ^L\Fȹ1S)s4WfXȍ $XvtIiӧfшszwT"8V<Śi-xE-ӉFh"!Gmer=\>okp 80Rthp.-qsgM4|-JYIF9+ay6^#H/J_G%V,cK$J^E@#[nޕ4m/Qk Q<{ .G%q'f*&`&[Yp N4sKNœ]>5&0f B.61#suM=ڽJQx H(^uNB!V_~3Ɲ<h dEyY(rmw!a4+PZq+̓K+ ]7ԺqѴI܇9)]jg6f= "ct|S 38ٍw^dXvY{1^_$ejqZ@ķCߚ`!ˢɍa4(u@vҴWmʎ#LB4u;u\:2]mn$S͂r.쮮d|eo{|~̳ Z`<4CǗB^4\@,3uVߑN_R8CV5iw6o L$3[F3U! Wt]$ac Ɖz'av(eMQx :^6S.3q<$1 6b9}Ԯh%f湹=Ky! ȡ]B;"3+ w~|lmձ49-fYmPN(]K$G܃$ Z\7* 2qcxm ::]C m" &hm,֝GpY]Mmr&1̷R--ズ (1,?Ag'c(%K\1{yyz,plPM: 4=j=G%Uk2︕dF}8':Ϊi촋Xz~ڵW_FG?/:G.U2΂ (>,3U4yO.En\!i4;HEQW,NpZk: v bcV?(9((Ú&Xev:0mdXjCXk;J8#垵4DY+uE(\]&9wh@ ڷǥvYmN?#d`r# 5ぱ՛ҺnN Ju2OH+4E]_qvF 8 {ΦƊ6rJ2( n1^uoWHob{.l{R'c=NrDu ;X{vLil@^R'KZ]Cu`q1gSE - {P !`,6OJLw=oot)OExm,@VG;mՀ{e{ŀ6vwT yr? ['N/T ӹШ^Kgf^k'穣LzʗTX_"Y]#9tMݏKtA~7zDq_I5H~Rto aEoT< uYj:VeɒO0<˃d3++*FdϐCl/S+Newj82F$P ݏ6I?Ҹ^ClpHIٴ7W5S۵ێ t7Mq1%R9|< 7?Ys q)ͰJV{Nςuj1k[`uvM/ vD-KE׷q^v/f@eZ5(OVnfGI+ %pp=ۡhtLey9VG͒?3-&ncs=O2^:#Ny.>Z|mmZn^d2L8>.7P˨q΢r]_>׻Ư4GNʝ̔Ap4@o]Ü5c7B rNr@灏:NثRH$d[h-iEDa{l$VFJ~uP5kSfI %HMP]9ZJ92l kFZ._& qet7KZ2˧;/aԟXos]?Ra{4\NGui3H6m69j `';.丸bdH/v.uDwugjpcѽD14 qq{M" Ҧ{ץAob|KdnOƮsZi$Ƿlo,)+69 +W i#.'\I۲Y-f_cIzR#[Se#PRjӲVB/eWE;*x$Q)H#ԩ{Dq\ 5f9{ZH֙r>I啍s7m4"24Y,F&z^TO+\WFF&;rb.7;qwZ19]8r̚(a3TH=vN-CCRC!-i* g;Q=+FZز?f@v[Ic[i_m{?J}I7z 9A-\ ?@~G^* +N֍#FPf2ƒ)e 9JΝk~ qkCP:EYޛz&i7H圓V|Eɥ|"|}FG}Wd{ly]*>,ȀImyw:=컈`#1턆Su]zx+7]Q`HL˼f7 cd~Ag$nKwim{i$\##nBGo`eL>8ildYiǍl-c~CQfFTܰ<](W- &,\hSFuA=b0cejk6,קsWcj.dz<-6nR*H"J >4EFߝN,n:F \MoM|kyҨ4]6\np9 翗J5h>K 6|, hI.:*}U c D{oMV$ XGeOHpl qs;(U[#kH09F^[IVFj.xߏ{JKTaT< )ښܭP37P}~eg[H졕_}cƐE'Q-yp@@G>gLW*9bp,+ Z,L`lvcv{V왕>cZO爉>#ХMs(Dw"FLN٠8`(qJzN))$gO]e:MCú{k.TS6|wisx+c3wc\\Ds$cYeokףp{9mNkܸ9P^iWv M- l8NL 0^fo.2uGc{S, P'JgJ4+h u򴼀p'eU4YYH*T(a\ gsKy*XJXr0)nI"- U7S(:D, HBU!*ZN/t%E^mOm7/.uO?LӠֿ?tܟWk](B(B{^9E,hB!TKuqu΋n5-^9ex5w 02Hו]"""""/}GXFpG餃C0&7+8΍ސE5bjڸxރJ\v1oӳp&O P2j4[M[nnZI@; Ǒ'b+97wyYm50&zT\`v|wh_ U׍g [q*юNRa2$D"Bk~NkP뺕m+vq:-o6+Bih85=}{Rf p 5g1(~+77CSߚ_3@ ;jÿi%EH`xZ qfDܗi.[O#Z&q.C$¸mX{Caw{Woy2ŌLxnp:-z.[(e> nDunG A)8qVȭl#! PzڠP7vFNji'{z#'!^A #`*}3, :v&Pڇ)$XhH姲F𯷧eGB漵`#ոPt#oz`KwY^)A k[}곆)ǡIl7qvwĺgic@q˞{v\׋d-v{vŒ[hZQyJFm I=I4T[&|}C&AfoE*mcuS"3s=0j8i \)A]6kf'l󑌏:&I8)8MuW\]wR-t ˛eA=:N׽SJcS6%ؕn9=E2iͽپF sU.x4ȔUC:!}?5 vB`'Uuwksu$Fog@dr8Ǣ1[5^p:_3[$xNO 6>ת,^uq@ #@Hr[3 +.<߹Y/a& iiUƁݏVFd#|7+e1+ ٻ/Oj&`&K2G 5m,|pńn\ꌦ+``H. y*\Aoɼȗ3XH6E`/aFHiPF)fgO#e:s|0<#bIsYh5}D%2<17NVnno\60eEė޲+S\fL̀n-̷{oc+* G5.&P4X~vM6^%)lffU|LF4ѹѸ8h$Dƭԫ(eo.u3lEY=]?;2eDӻx&76El991ɩ ;w*ȸ9dgO*@slO~ Lc2Z-ݺ₲%_j`e[3J{!kN+tuk'#%C{'熟j pY+lx8A|{O D~FHHP00*g6#-nwEmF,Jr&Á v6595N"[&uMJ_ *inQ7$:{ӫ-~ԧȉˆ.$X su\Tv# Un[NHcs _?}O72=zAVC\ Mj KT+5[ AԸ.jz'ƾ堍E۶٦*5ҥ+y\(e{v%n9חX%Ž<.!ntFI0C nEm[I Y#*nB1R QsZCUpEWgr^^K!Jsqu8LF %C|t-JMG"0p9h{E3*LI|h<wiYLyOUw+Fc{psqKjs-T,^K`S00[As#e3eOw7\.?69Lo*U|,?f~'NN=s< CVC''ef!P3%kPGa׺Y'2H䵛,nAWOݥ黰8shg` yZWE6Fs AkUR6M{)H?$1UqGz`U q5d˨'v/P7$9QI.ȻNF6c]|XR|8+X:q`I<ߚT6cMieHa}ng4y3 6 \y]>$OsdUv}Mm|\._rY\=:7KsqNbۤ}][P Acڕt5\aB2[yG\xiV5]oWvph#F~zZYb&<8xNa{ Ev7>vIX^KprTpI7t|l{f8FhaV/uqϬ\Y)H3<aήCQUu.9|"q\nA#D4o[5H-NMPIU'1xqFO._">ޥȍކ:H$ ێV8+Hku7HCa n{QQډ g0V"A } .JzlqEi*^\!z[$3~GTd=' W iI$xq4Š^ڻѹ,Cm\uRF2:ϮqJX:mI| Xi5珯I%>>^9x yz9lK}SIv I0W < K$okDd;&m1KF{ aI^Z"ǡ~И5"L_Io%H0069s *sd.ad{Pq-=PXG*'($X Zk*XC 5G*yxPPd|ԉV=Ƅ8=%X kdh05ii?vNZA> d4#3G^-#9~|ǕeٱhAo}ףOҾQHـ%so`iU jzLG+{ 1e0]纟Nҟ(0vo}TȫX+ʄ#(J㿭 2hJ:yБ%J2c.L`@P2#p;y'4%PHJH}?֩m ͼisx{Z?ο]ӿctZ:^O{mq+EEZg^1~ ᦸ<)qmẵD,vf>wׄ5=yG淩XǤntyPv!X{8 !!!!!!x[ϱpd}5AzTÉ ,qM;#HùQ8eMڊjeen?K 'G՞{G#g3uQ@]/m# +@:FXC|`VfcuGNzV!iaդ#†gnne/;(HK2F@ZH~ c78D mk4+}CW 9ims5eS+8TYRf7ẁ-{"iiB)ɢa {Ń@TRj[t&P$cZѸutZYmIiKܯTs3ӱrZ4;SosGfk5Iq (1{{>Uyu8]BVq&m%^]ɣwWZm_1n~pC6џ B2bp`aŝ#?OѶ}] l'ٺ*{|_'kh73G hm Ki~)r`ԦmlY`M< H7E[w~tݚla $r-Qf7;D%/gql*({t !BĠ`k϶ 썐;I4lY `L(2F+DzF5N˷/f@du٤݂:3Mf,zJMPNm!i׷;(8)` JuUkZ-z+Eq Ll\>^r.26Z=?5sNIzE$g"#F#;Ә*xP3!Ѻ7;g'tYլdg2GIM1G1ʐۀWf;w٤Oh{S^MVp)p]K;޽^Y ܴγeZ{pRc󹋝!ZA7fHZo]X,QűIl0c&V\A{|V2-s4P\<=ApcZ~Գa&T,j6 vM;qPU ҸuU7wT魽Qlb^A{yPoYbB-54m-hi6s'%B q,Khў 'H)7/!#/Qͨ oRԊhfnƖP1ښ(vGipeD`W:@EvߊIMc,D϶9';Gu7>a}*KMDƒp1i w#u&'mgnhǬƋΚ>Ex?AǒOkHPoccas{x{As͒@w+wt3|gFǗ[)obJA5}UƗ]Ȋ!WP87NɌU5I*qBDu {v8ҚS2;\i=! {B "E*Ѵ(ts9#`P;;3Pur7c$l&CZH;{gXK~)X_zKjFr%t# $yI*?,n(BIJ.9$te84*h|.On9{{&#L cxVOLkDh{_1I,a wP5n7zSr6-|OC}au_ߓ ?Ir <5;cqc1*TӴE`Y GAҚ157SA.ws$rEDJŻv3Mh!8ReIӾHwC&ɧ" U`m=)Yqݎdc*O;e4؍;3H8ߚ"5uNQeIcۏ;O/:s\,U`n<&Hn.qh➪"#4!_c~vTBTP49=]-vr_.i߱k:-k/'GOz6""*#R .le9Kul>Ϸ%=j-ij\fvD3݀*DuAѺ|2~0Y׍v E- ؤɹC- ]V&cZAG;vu"2 *"$tlw~ꧨ@ 8jvA7M]J]\KOq1ϖ8AOۉAv94c}01ǗFVH!XGm" ѡײPdsEk͎ @un&KgK(1) OwuNȘjWS O#wn v T;]ԥ3`;x/*qjuS|vsGk6m+&$r JHiK<1I P.v_h2TgAD:hs=]@mb! 3SI6E& ;.ǡp^-WUF׍>U˖ڜ?":SK~t8kg~ ppN;W}:/pySC9Hu^ Z=F փ(?py-푅da(|G'k[Db!cU C0bL;sߢ'ks gXoL΢ FdFLj,'Wa^ܫrh|Mcf1fa \Hƽ,M}ytZ4>41`;p.# |C؍v/}!iPKl7Ɨ.\#Һ6]ɘyc:H[q~ޛkZƷĩ%,UD1ȏ`k+7J(ӨT6#wS'K Ar؏DH>y"?PI,{]^`=Bof-=A'Ie_wɢ-6 da#;n}*V@'L |,η/F#3öP.!!qێ8eҭP 6Ln[#deN>a[ WkjҴ6hoY쮰)=p3YK.=w$Xx1Žg5`݁z;C}qU) )<ɨ@WwڹرY|{ѥĆԵ\ ͗Jvuީ T1:/Ew )p'k$nw#ָ!sdG,I:G>Qo$ߙd&xaCRy~ϐSY*HFʏ-~\+`7a{$hwVSEmYs"9X wRʓ;B04F kwn*li"^kTj6}{Tg%$eJeq}\[IBgkq]sKcS}r.윿a??D9h|qBݴb'.E/{)sl2ohsHp5{]A=qk%c"w$3w d44}OMM{ImQOd$g4Nw-<\,ǘ/ YXpփ!]r%O 1D<3gNc3X;X?xZ\>nJ 7)x鎵~(%YX 58pnsmJY̦E\t'/nG#TxMÐI1<[ZgkB$!9 JH]p uN}E47V",({oͤ-quqU}#]v$2ddG D8 4dO㼘M)`Yd#:CKFUDzr"ʐd?VSGr/u'V^[@V3);Y،dƄ8^Oc\a|wB[]4"YsQݡ`kvQFv Zhjm=lX`bmsU𙳤-]LX s@AawߟMR#Io)dU{,)c 2ڼi&ّ>w=MgniWxb62a~Lc]'ȿ:CVՐA 6=NstYB#ԇ#NmW'wuCr=.]U+u" 1R2SPh,η7bBL451~,/Ն;Ŋ1Z5^>nv\F&(D2! puacNXnGyoӠ*)|Oܺ98B9qH,iY؎Ֆg =.TeikvW0zO3CM M-<uZvRO c;p%yDǂ84AD(~/Qjw=^TScL<79quֹ7H>nCk{๮IŚA4d8;I4n=K{sFp˞F`t7Lzw쟐Z[G{8܂8={m8mipy-=6%B2$*;Τ{I *81D; x޵m~Z)EcΨd0NlėĖ+]}.ib?&ITL9fQijJZׂEncb5>SVyrnDG?F̨${Ο,35_n;^Y oF#Gq42Xp o]9Go ֆYNnվB ZF8 gdk Hή27IkA*;b,ډy:6)v[!)fD N4HF-?o,݂[ CLԜٱ_bd"!$Hkdjo`vEnf{;r1ǻ$ִ"՞k@pm&CVBvlᲶ`6D;n?QLk|LY-Q4y3tހ  mA9zHr΃/:c9O8+8'3- Y4|^o'nY9t[SHŒ,G֤ 9c˭Cz?}zӖ) WiQTn,U-lyz~Dn3Ӭi|mZ>LVWSP0%qsTFHyeLdulYn#KE7rMȪlZߩ;jf@׾E9gqaÜ->ۇ5;=C"i1Ws+#1{ w q4z}ŹpnQ/h$Rr󸝛):{mW E̚%%#;珖nb{hHt>Z,dF;[wR7SuU]ܒ^$+$rckt 'M[Ȧu l:kk=REEGfP1I!Jc(.QttN.xXn߱ uf[;kˉ(sQL7/X|av!5nHyu_~5KBѬA3П20ۖ[z皗j:$rDŔjrXBO9 kèE[|5aemI,aK7CzsX $:è{.Qf;-hcDlE}@a`8Gug[mP,gc0qZbl0;S ~\Mċe'%k J| K~eRF2;5>wPn->;K!dHW}c텠uQ)K09$rU[O3Ҷ]!amuj -7` ӳ3I c<}ECStė'qU\{]sdb&e~ݻ;[uw*840AuvEؙ2|HvTKT"4nBܜgљXZK_33&Hn[vKu5֣O%|LT)+YZrE7͗$W쎍`u;Mr|F6[zn\-ěٳpIO#Ez C>Z#㐶2s9qqƆN.SNDz@/n\:E04kMU:WP5K?kdc:pF/d(GHmк?B? fd-..$^Ys*Њ}.,OJ8-Y;WlT|NK`-w~B֜ĂBOd?]?s>!hӤcN_A=֯t]Gߕ<=NxCMpsCMGj¦Y(ݏ*ABBUp麔AzF'|F=^vAqmק*9f>QN4O`Εe$K3Z$f$+̆F^n54(q+nO1N*:c9=w> 2efD1_=أ]*'ָ?Pu)-g# ?5c2h?b3 6Lh8'vWZW-VF-#w6:+1y국soz!d#c#W#7S g*9G+!r!=i9`)vrT)o۫V2C'8[O݋ CYitJU^ʐjڹR1~Ըj؃~*Mo^&6w1ҟFMm4DMُ~z57oUfO|?WgU֚|ݨ"yBE*EaEjZ9f7Q`\Wo@w[Kϐj-q;־rO[r#TGgsncq{nkpJRKI$[T e1iAV9dOH:r7{_Ti1E"Dr*t!d HR!mgI!i'm;Ul_^]8rfh EW{_/'q:s$ W6QV;y$h֒6iWJ^{Wi8- SUFie˙ԧ2r{)8[4$ᇴ}CeI+gG{ ݾE EuS ]/@|9*w&ئfB%xtD4I] p *" 'w6om{tw*UJ]m4drϧı`k9rˉѯQ(ߎ\\`w?#2;<n|fjcj(zP&B @9`ӰlW+ \0Z`hB^*Ky]a,٨}8\eC X}y5[ZPf LڨU-#f״ )|vr Ӥ'$? =d~2LaPƠdz #r\ʠP.\LGҧvC6B24Bcƿ7jQQ ɏ3P 6/jt"Zg]cNkZR-OH!.wwYN]98؝'{i["ۥLNyᨀˌÀܣ.$G oW)q7VN.#2%s$hQaRZ\z3T";(F4$BD h`p|f $ /w6\tr~jֵͮx>Jrr;fq Ij.( gѭ mj |@yQtu >Ku=9tStdKE~='W,JVIU(`R;|WسDOY;k _4 nV$KII 5Dt^xLZw3A 'lz ?ʏMЧkVOfTc#]gJ,pl|_?#tER i28mݤi'q{'v6Q^Xi$vr\$YcQz~JA&0Pu&96\hz@/R[) (v= t.U43}C;笇q~kd.(T帚5/OA ֒, 7虑zV|d{`񛧺}]4xbN  OX_nzuvvZ͇<ŻGRo߳ # )FyV3ӎVl 5`WkGWwNY@RYq)kAh￧Į O ] `dNKiٌ\SNHh3E}dK0C1qn\=Prb<*:Nhkd17׺pMoe7yĩhQCι$U 2WEz_EǐG]9Ϳ]?7źI@kP8\6)|=RX鸭qi [>}kio7?mGlKM,9 _*"  f /j+%%#|l3T:o#՛76GEo˘YWϢtMS˫A'Rbes\Couβ'6=<}OA<iB5Ǹk[X*sӡX$mu bE_HlbJ`BW~c k(1l\KP<t%e@3cb?U Y@]踝 ΋7!L .sl#;wYJC*&K!n|ӡPy I+9Ē{VZf"DӴcܪv7/``>fLf4s`fh뢹XYz?MԄ6;CӸtiEicӲsxbip$=5 攛C!5e `6$)8ʐ:xG腣31IB{ݤ?P%7j˫{kh r<>zk'qS#HǥLKT2Eg6p*㼶yU)sϑ}[xDn׸G+J 1ac]?- qӦ|aKՏ>t[n?P>sp5hm,Q,xrf^^,wߍ3^^AD>uFaMsgsy)3%N]Ⱦl=Ϙl>8!5DU\gR.sֲ hzmu!#@7 ȕ8#V3XY?)zFm4^X^ c3H7 r Nyjy_.$[O. nb~=VeX nA#vJ1gf\5D x6֗[[wW;i#BF g0[+Գ1chh}g9Eoqeut(G?x>bp3m_fu 7(27grX7huǛ}V<LyǗf*v>w+YD_ԶXo6?ɬxWksp6&2>쓕nN,+ pD5ƶzg HX.:~t>;%o?s9o,8bǝn{( Gg;kV-ߵ̷ 1oϺBo?(ۓ'StD+Ns[;g6xvv(W$ ;5mfp Qȥ{*@@6/bFzpT5VBĞ.3>T~z ؞w",ղ\f+MF܀j1󬬞敚Wӵn$Ki\?pOsultxQ@nln{6ous4M%5`ry߹US,a1r8< [n4 gqh4M]_;[c IW.:Z Cyk}o׵.I ᫙$e:N+N [BC|V@ $mw=ףbI<Z@!=HxE{QZ,i wb@$qcܞ ?H0̗YjL*#@s$Xt4װ|IcEi ! 4&$] Va08 )x~V4vGz1ʕ*)ЅqB %Oۥ˺pQ4K˼ Ex͖Q &@٢bTR'EFe*2mH8Pv}K׌RR$/t%E^mOm7/.uO?LӠֿ?tܟWk](B(B1pVގ'Y=Kh$Iki0,WO#t!xƻ<# Vk SL-t6Kg LC#Y-6Wb;]Mw* b^7p, w9$QZ Og6c\HݬޫtJKy`|OKM<'7z;;_/.t&}+<$DF@5w^ֵYE=F\OZ-hWZLf{݋ mt՜sNT/*iݪ#k6w[ŮY712e.+%حcIt'12~$lOu_{_!3Lo.F34enr~?YQ5;]M4vUOYf,H8yVqDmAySj :%@kv!j݉ԥ:=x3AV:п r%~\34=I)SZݐ2n9ǍgL_1˨-Ϳ#VuMӒ۷91LyY \B24d7-q@5S7F┙4SN_=V$1*=3;*M1Iդ{vc~2\w ]d2xݱ5qa29ZH^Dž5mVԢ78Ɲ8Vs:>MlS[^\6-:Fr8n8:sqHvXs&$Z oBwۛm.NeB ɏ>t5D,X:y.mEi<9Zd~lʌX_6s;9B-&d /7;!;w5瑽r۱Սɧd)spXPz|)bfDwPiV Ft˖#f`H=Fڟ^6c鲺& X{_wP;4>,lݢLαcd7m@S[YNUXmGwtD\\P:v:Gu 'Qҝ8vRS,r+F` cj:nuꭽEu yey`{INqΞ\*h񦝒>5kudAҽ_K&)z=敵M6S(G`P7_ al4a&Y d89"WHsviCdƸģMjھ6\^`(>잴'mWu\1csV+j;BnŵWkfӼ|44۸QubΚxOUFQOuVE $l4>W< vֽaveO K.#Oiqp'gPѓM[y3F hv)bc$E=O_] >n謺ݔywJ$dJ0zሌ Qn|F&ԣ y Ȩ/{[F&W֑y[mgjݡ˽迉*`}z6?Z:|FHt.s`m1'{R[ê.ν&]#_sŃSvnWXKDSKp䑵9[6ihsgk)W7X HmO"ydteH/czWu? l 7`Ej E[M0,Ca@>gr~Zy|r/\ZI$O&͓{aWWlwjxO8/8ާSX&>?acen|&N6#qւ%:GzI{\rYlFrre%l@'QNN?]2ݸ/5$O.0xѨl㷢ƝH7RV$ѧ>d[!@:&W[٢e3#'a4E&UG0;]s[~1巹opG*% AVNDދ呄>k-"vv*|9DCY1;OVݬ\D\c6ɲ9'wԛ86ehǡ~j_[,A:G9‘.5,jMo!*#<˦|3] kA7۷jK~D9LtL-4l٤r K9L+_ͽ^(kزbekAp!4'(xPYT wBD{Tq^.Y~L@%{<5\gchCn`b.H~Ytu.^/-sCN[4vrtydo؀$ u͙7p_,MoH? n'&pBk9['j&%TN:mE.my  TqE$3Mu$7fᎧ*]%7`Vqذ$WΠG.۪wUΚN4 $v#>ǵݞ`p"Շc׈ǘk,dǰsKbc2ii>C"3$']n`qF/#C\5m[&LZIrwcYb1#i%w]tC\\]]Go?aQvk+x6_6л>WEȱOIPIO@Q{e(ߩ4yKTj6m0)|W5xs4;%魭 ]!\B~_H]pnJ?z< an!ONʘ*\Dh 9yb%`ݘr8 Y% @Iuu_6wQ+)l4'"wc Iؽh+$ dtn{&ȧ(@JO:QKx{7ew|Owa0D2;{* ݚW{ni$(;6%p2c6P½7Mɒ { 3S,0^DpFNO/H`cĺ,ۄC?$c`yo$a7iGiꊰiajt%ǟyUwxMi[~~#`{'U<""TqsAꝘR1oh~W?΢k ̌' ? N]\&ney|ұwb:OcCѰTre̝3{$MtOU{ł&S2H=s5_hc6f5O-vBx>ɨ'P;F 2{)­ ąŮ P+^dյ8mVCJ8=nָH]*g'O5 A0--4V $HM̡qA 1kŴڵ>#2ZHq[9S[A 1DG#A)D-jgDm-e%Q;$6)0~zp}'6ΈC?mgMu4puB1]Pn*~KACu0Bsscd= 9h.}J,*u&M.f?掇%d`޹;ocyR);XcywSIh.U|Gd/Hc]7NP Єb*(B{lM4Ic"9I=~Z xy[]őZֽ6 Ͷۯi{q_[j  QBxu|E~ ?~7?|u?'?WDje^iZ 8܎X|Ѕ?Iѥ 7o>hs4V֗31hBUJ4!z^"""""/}41g_mU18DH\$ӆA_/dT<ߖp+kOsDp[?60x8}MAoaSo\?%8Ktu_G7a141ѽՄVP۱#1U)$tOAe&O6y۫S[}+ \wL8>!*9s1q|M8Fw 9}uEnccl@Χr$,U`;z&.0@H_eDk9d[`{o|YmIەC*7CvW˝s Pn/v {K;a05I$SbGzέFi:HR۰xTͧ8jF,w1t `yֻ4S>sdG,[CMf|!RI,M2\`b0׻IPvv AbyR1]Nv `6(9:8vZej,uz8? i㺟@+d'9S*f7tq J;HJ݂|j_4vY2f7 iekmH s[ L^TplY2) CV)V ݬ2#??{AqC$ }G`1(sy7!vx'|9ˢd=֞,{Xi&}[B;YH =3ӣ㱎H*Qյ {9Ot2O+ 㟳}^V7rAW:0=k*C>{%Jc 23Nh.o xSe 9m=wjnT9GZ):κ#h[Mhض|ЫntvN{e``?[sʗ18 ۽vRq>isnbcHЂ;֌5n8vh@ أgnGm '{Stp$f/Z29mUؘfG ..PiPzfXk"ʎi* s QU|ꮝ)\Ic BPZ̅+UByR1ڱS:xnmƆS5TtX"X^"gh9\Q~?dž6 ZZieb\M}+ VI!w`݀Xq^Ͽ«9ou_4u82>oHظh++`I{4pe2ciL~](ːWD+p['[/5Qj;\.]ȄP/*\uWD q\{*n Im, A99Ӆ`'1&<7@ C߲87To K )w2QC {(/ -[TRA65أ"]s޹UmxwP#XX##Yi$rG&յ&fcq*B|3VǞ̧F<;_*A>Tƍb+_(,yᵮ35[]`ٱA^U|~VI%J >`80Qyk4]tY{UrZڬiH8kpI-.gA&U8 H\MTP&hpiؓV8$蝾"A:ey׭GC^ǚ$$+biX+<}We.Gs,9+(V&n-Tg h<3V"[(au,0LqcI}]:MQD3BWx0#? L6>9Vjp,ǽ@܉#6Ή  {hn/7Q8 r '߿mCΦYHvyPd ~T$Vs%ճmu5l'׊ǩ].z6<lWkY&k3[rdm{ % Ǡ?ib|UљM,D~pմ/!ɔ0k캕f8ZLfB\sZB,피I{}eJ6)1Gr:nmcI(=6m?#ϲl2E,vO#ߺTyey ē.<yT s:|8̆ -m=xd톞ͧݽmKM$=}W0zs݅$:SOhu [)S!L|ȩ Ӳě O!]@ױ>ks6.G֑Ӳ\|o9?g'cOJ[f΍쑂9 |S:{&3ˆV͔- 2m@1i $:7Mr0ipw,Cv!i;^ӵ-݌cڪ?8Ig߫VUTT)=o D*K/ɦ8̭c4Ϩ.h'nnOԙ-NUvI3,}`#k#iOT;]o[;p1NU@*Ep;>TF1֑*BDwPc`oŧ {e158ẃp%Сtqv.%FFΖkoZF[==zwine23֒ yEysp)V1R䘢!|m}oaf`6 p8$:RnVy$ gQ@~+oܓ&#x#KEߛ_"U!L@TV\ -^&A \C{;r0Gn}dvjO;1-8aŋelf7zn<%A9AnMnp9XiF=@y"|mgm&{8}VcUdF}AkFē׽mU[ Vp(ӿή>kbtu[%֣"ƒywOĊFgFX@ Fo,z`q F8XM~Z5dtu^B"jz4&=^@n5NpnuwDvīJ]vi$Uj+. e07܇bƲ ʣ`n*T{H !끁Zn #I-\ٰ%c8Â9 4G3%maAk^Evki#sn_!bMZx6mKH'TDs s#PG\|IxI.hA/Bפ%ԤYbA4MNk@ns12:S!tc`a][,<1k-G̖+*  LI_3 cCωi,k+qޣ髤ea\ܼ|"y ]ѝFӛ[ر{",P").X!\v.wO@A&D8FVT*Q?η1|z͟ΫRKE08S~cJS]C3C7myI˘_3nv:ǔUj҇u{-Hd)x-"gϯe7V-ŗt ]? h&4i&yođŹө5{[C-ט?ǯQ؍.n}Bdkf#/&16˱qCh]{@;ʩy$hlc#նA\oSP؞tA5@m=F•bzGq3;(O>ΆxR]G?"(qMn7LMɤp]ň"`bp#lA󕟟?H]~k i&5өvF9fb79I+C#;sWUO.vk-nM}lXO Ov6ymrTéܟA/KI?oUmf˱,0wmۏ/:ҁ^mLlxH%4G:;*߆츖tH}+nrL&0 'Uwo*}&t#,Wg.AیgJX\M})jMԱO(:M{@ 78 VM]o# W<|b7km.?3Ҙā]]}mtD'iVm@w`t}XGc5fIetqaO<{U\j\u0 l7{X <V}^d>#45hZtlt{Y 䍼yUY'n4S~  d=w<4w:-)ycspѽnQ݀=^U裚 ?꼓.,uu@o!ܷ] (>vAl#g\ͯݛұN\tc3{? @!XX&jЇ19 +-I;Xi(30^]Oě1k] )h*r<&ɯ㐯O1m % ^ŝu*%!%4$ n\G۱q;lC:zgk], 'Km$ [ےݾVʲ*H˨5+jK SZCM+f;!p =/Jp"P8H9E N;NԾIΣ%T$&0$2bۉh ڢv8&C|W7|ڗsN)ex#la3YݚZoĜbe7CT/m3N.^aFgG& +z[Z]r; 8`''$oQ䬙\壵]e{#!{cvqqSwZ8rQkAvE5“aaiwz aFI>zI$gm>'C'$ CwF&%w"9`cډKk ^mBV2Jm!BJ ;>Pe#n@/o9quu3Yagn(kϾYnQG?aM7i͏5ijQA$w0'SƗLٍjyYgԝͻO,/Fr29V=Âf|}&  =`S =)Ry9Б t!')RB<e"'"b! )No[{tO!P67)i.umWP"ps̑ʞAiۿǗ٣.yAorG{RgH$΄)P΄9#%A"1BEO]ܛ{Cq{!vĻG!``rK݀ =r5"H9DfC$@J}5tՌwDZ۩ahrXB.cuFs+O,Ϋ8v:$5@hv#}Y5isBTЄ~hH?֩m ͼisx{Z?ο]ӿctZ:^O{mq+EEEEEEEEEE^4QMw J̾C5>% ֎4f3wykhuC03yu4E;-GVM邏^` <rz9养I3jI;r}6bg{uWP0#$!ܮ fy s6 wֻrd.X2{[>SjfUt[u'V"QkuH?c֩S Ľ3ѽ5 ѦQ.jVHc8C a_&2ϓR,ȴ:@ D|=SH v`IJ[־f:|\o~=8lvfF(L6TWhU_e5WW_o7%2rMO?b[$m *vygh(5FM6Sı1uuo"6R![tWbc<|D< lhal<5vT;IbY-$:ǓסTk-T,w+搵HچT!UssMډ>k6џ8%P;҇X2rOiujMRbefNp]!j*K2J>sC$ԡ- ;d|pD_%{+Z%wI٠=jl죕G p{A;qNb;f{k˘TA $sX[ZY^ \W9%y[{?÷zEs D]'C>;.g4tyMj6\`?u l.5i4Un&L *T6549kȥg0tq:E w6=M{È[?'B'F>utɨޒh2!9ClvE%\؀FA8|+Xlf3ˏ%DRm"#\I8޼aڃU+oŒoQW1#'A= ,k,i}V{%aO/l`txZ`kGmz}qIAN .WO ]J[6ff+Hq;::پµҴKBDS4{i`dXHlH.IIkhlr SKT9XG=m=rA+}"^>`׍zC&,M73.1GIw)~E"/߁Tc(WUIueny\)_RU?ibS:K!k[n҃䣝$d#k{oMf{GR UBs:w%޹Ў3rlqlNs kT No l2 <F#F&=X4 7D\J@ϙRZ墉:#GޕطXa- ЬG:]nCXdk*G ~?ar(ѵ<.BFUJDF/n.͟&oC3yGըmth 7~=VĀsxU_d𳰺$x1t9Ă(CUO*v1d]1Ͼ8Md)rayfM{K up9WX T H9Otok 4sHD]ު4(YzԬWҳc{ [$)vYcc4j Gp8.<Am('a $`41-- qV~ z1%11 \4VuQI+@e[]_Z[q]ѸfTQ1gy6 d5G,5'_):GNA%+Amڜ&l~ _VֽzAHi-ʢžj WaIwe+OM7 \NS]q7Ƕ:Ƴ.(K+iܴq=QVdp.};U5>w?H,6fއt&WwRi1!ŢcZ9a)@O$Q8Y3Lg:fSDR0E#E67<2xo+h푂8 @=<=rSӸ+a4 @@A-3My`PnOEM>n9~TL+s' =Ai8_5էtWK%\6[U͎8Mnz#u;o/"Bm& ɽi`S)Qp}wilFLC3ILb1CWsbK B3Zrx nv:Lil\S쁒1ӞzW3䔗{Ⱦә# sH*{EZ^n3/{UxK'R8o`œi'We,P?Dq58$p;2ⴸKfC [Rb}žX0oR,来% K8$fF#8`ǯ,F]#r;dEӃ6.ϪX~w]=XאW 5q%x9=<\ƆIDoa7zE5!nqVbDžE.s#ďHs_c}-f&XdC7h;}kF> e|Z4G;;')g\Ƒ&(;X\F-.F^Hǯkn;mQL0kY,gYLł0A!F*onR/OavQEwrM4)pSʴۈ. <@-ӷT_r>O o}=adXxċ¶7/uɛ=Xcxi58q(ZNšg41{Rhs&1"q'sgx[)Ԡoz6(fnvܚjaJ$ \'l}r! o˕iUk@m{!1’oFHڭ1)Hl{QzIo",$  u p.- Yqd RF;ǔPG=FI&hwis ?`N%vFH>i{EvLS\t_}[%ҦRKXD>*⁽~G)51! f.)o#*{|7wDςÍᷩK]D }6!,.`U^6,TLyTqkvy9ƇܓvoMփ^0RdX5kxim5$}" E^$-ϺVp~ C s]n46C C1;F'9paDzήʥFX{$E i sXChLV^ K) edw$O@v[ĮV;s>p(GM*Xi" SC Ϭ,m"_SM5-S; F9 &;q` ֌vrُKnUs1c|J$>. km5zԘFy*-! ~5ԉ5-j[%'()~Z e`-߿mY~K6 F2oS.@5aa,҄*Eb*:T" =(B;"]!`}I=|iRg t!砡! $AƄ Mք՟BUT,3Bb$Z4jHȶك4njG7 wcA- 8AD{۩n%$Ѵdҥcm &nCeO$,MSdB yЕ!}K׌RSSBTP49=]-vr_.i߱k:-k/'GOz6"""""(:ƹu ]2Ժ'bBp'!Bx߇4}Z ._9Y\Gg, ϐ YRIcfv8 RMU9)1 Z$&, 'hqwqЅ䟶b؂Fpz^H%ZK}漍 ۜ9f֔K⇨LFGH; lU1q}lTPWhݐMn3.d\LJ3Sqhwp|v oRbx-Ĩ楆9 O8gRk\Ӛ湬m,p࿍$vX ;' guc$X-l|oefhǬ[JUFog\5ㆃm1.u?\ܐ3Qs^wZ?2goU`_”YX݀FvY1W,ɗɌx5FƱg59rMcbt[v;aMHQP 5f;Y*#첎TVLl$sWR̫4 Ty 4KOSkAAkH$[ḽ앦^a5Qc'䧺&H#e鍢WT`|=mNIQhwP@WZ@4E鷪fG gF܅}T>zfN܉LGMmw[=VIF2kW=⻪B `06lJbG \v[k}A(t5 n/Y/&DxJL/7%W ZvN(NeVu Wl[;q0 20by'SVlx^IU~+55ڬ~γE%QK Cj8ɓ&v˫`5p@j;vVʒZ\Z/31bdϋ*] 6X-"ߔyT̮rNֶPKi/gi7M!Ydkjg"saQѳ{*L0qe[TjȊ&:<8 sWX^47跰L8}&X !iuMӚv6Q{ L'ru54q59^ק>d!+sM\[K 1+m>Ӡ丩$tn+HAWď.yjWZtx>dӻ r,c(y]k%8 -hGznno4ʆjqI+G=ӌ塥4@T|Ծ;f?z\ܙ)p$+tO üƋG W !Fu-WՆ G)we2LÄCN?o#t Iypfcnsl6rQn@8v+816\Ɲ@Ѫ\)ZiK}nZ:Hp+OSw=-y.5fijwihS`u-kufI&XP>3H׭oIJ4*p?It2c9gP}),H(խB[@ؼq ~+h5 'rO|FEmeo@kwͽ̓yujB{֜f\<db7m)M>yVK ܫϻ*F3L9%B'7{eb %s !qUӛnbZmbaՌ$$YS1q(2%!0ֹ\z%ɶw-qɯHFO.>B\׭wQsR脵:`6ݻ9Dž3WJ8gWU{w\׺-4gNU `M,q+{Nӗ>UO#u|C?Csz}A:Ǘ}_ciy MlZq<3#K7_U|W @跍n zZ8[IUs6dQw w.Iyw$^)xl6Bg"ȣ@;J_RP) pcUq㰁̰<=~Z12g.3`d`Q@ܹ Wi+hRfHgVY-9M+',i+c\+n.H(B M&IIUVIRc1ᵤ`ymu%SNƏ۞;aX* lHZWWYdyO=;f-̤\{N `fuZC?Dn춯G:ZvRlc'ݚ׺2cߓg?HKV7 UY+,a9bO7cȟ*)5ۖ8Fǭp4MZeCɭ^Qj=Fm #9gW}>@]L䝀PUX\;&F*r{P7'KfqZ`/qbꤹ5( XmD. Ny4'xN>g:H.vu6vwJ9eY"(T]:c=%6+?Sgc4f+}$ $dGe ï?qh4HWybkc؁hg9BgHk%ڝs&v :Ǖ3@ׯ ːJC"潸LxCsJRK.q2/c!(1u>>u h&O#ĂÅ#Ž[; ĩ_iCr##ʣqs6߿kpҗhpT@}. ' i ~SUr= XdXlyRTx+D^kHgۚwBj,7 ; PH4$A Vyb-X%]FYsoI.$ǘSH#<&Ξ'Q [vaadxSHWkbh`GEڍԗ7dC~4FLC]ܟ_1OTԖi(ɐy.96,TzN|Ì0_ ͭԣT(H(B3B)RsB8aGʠb(n,4!z: Kb/uK[/&B[i\{\Bv!TWֲZok,1g [PU#lOϐ4mBOĜV\c.5';8եb?([2!AgW7>PXO0q"jIah6\GSUl_^!d0^HQ}^k2ifgs0s>k5LlzntN53ʢ1BsҶW@xWf4#%r,4od$ϧ{x$7v!PV68uu,$0[$v#Lƺ7(9>l|) [[s?r%-aE F |ʜ(0jnoo[XϲG@\daIHCϓ2UYخfˋ 2 t 'qJKB%H/\8UG+G#l=^ f@V6d]5gS"N7`\_.E?>(쌘 AܴtzIP9$Co($ *<8AICm.pFj o2|VjGӺlzv.w~=jk7تEAVм0[%@oZTeY%$ƍqqP=Ķ[;#3xZލNd2fnYl늶W>| ہ 8 = 02]ڼ9qAVi%ܙ{WtdnɦJ;R$S`Mmz]/hmu)6J݃tS\N bc;csuG][{Vݨw9d%A$w Mmi}G:o [hU_߿wolVD0>z$R Qhsj >leZa[h g 1wTlO7cƶXg2j7fͳa@pwB_ONܙVh!_] 2$90Hk:5o$xYJd0ӟJ mPw>C4Ъw =hq=ƙoqT=2*w@qm71.ձwYl.̋v$N{X-.h㲢N\RwqWɮUPAnCds;~/>)v+һғ k$-ĪIpN߉YS#k4G`oq=AX-%+\4x{1YRul#fv ojpZϤ.!a˝3FYn g5v( Y}72kGf۳}4uI\An5_i{ K%׳Fc=vHÎ g>?6r{ xmag nQϥK O*{q3BBWzB/Yna puC|{3Ա;]5qXj!j<ÉՑ| ۶m5 ϙ:K{okQe o佖H9@=yT=`? vƇp8U"E?#ʤTvU iq)pA ʬk%txŖ?6=egös)ӦSPy71ϟ!TNkK0+ Ƿ.گauk6 Gø^c8+ۅE8&h)Yo*^&Ӎ夎&1٘ȋj,^?9|W4i-NV}ZބZJۘa 9Ulf#Ɂipl~Nΐ^A|e2RZiܝZc%{j)A`Ik_"@ʍo꼳K0Ηh`i7DnORm^ :: ڐB}%6,l%XI-IXt+{FL\..NA !4VܓZ5HKPnX80ylvFv;.`r/7:k@w`7WGL*H8ӨZyY=ѓ*{n;FQB.y` *Y-2!SʞZ1 [fh9 Yvt^ޝ7\헳Ču5kaq!cK+K|]7mnLr3í9o׸Uq"xZ뫪㐖\zli|/)ѮXӤ/.FX>4bd - i$ק oWIӺv>#tQȫyz8#fKP3(E0qX4 /kM5ڗf.w k\Z$5_Ѡ5W+Zi7WW/eew3#FkuP6yEdFN:y6(Xv>Y\=;+f[d~ p 69d":Au6;_ll{mNQUsHv&ID7n;Umܹyewn(c{)3Hߛ57R12kv#IQ`ԊzIYc(28 C>[y|Ѱ6ʒջcu UyDk^}qQ Z8<Ⲛ(ݾmzVeŚ ĶTxm^|SwslX:`8C!۵j US7M$ #{8<-w]3wB.ۄEcß~*FH9^41[Xzu^O4;M=PJx4Fe¾J@I=k6wG#JK( RTʚIAX8?M g#Z5r͜;KNx Q(}b9‘qSJ\Iq{AYHwuqˈ's{ O3=c[H(;=*U\o, TR%}=qʣc^`2fƒY ZC5چrzN J h{ ͑6#BV\{W{|RF}=WK}$\ji)R]K 6c:R4ڍSбzЋY"3KuGspwcGneV+Cr(o xzF(B9,}!Е*Dc* Q)*y04 i#ZV+k R/LI+1vGO<[OkxKG?4d1{v{'-$3vt|5cDb4 -NSrB@Z|Vė+9~#LvgI$bDzԮ֖ƋYܧ&ul9C0S6bX|{_?VG 7Kʍ ƆHA_ 4LX§i,acpYX3ͽ5dyX,_$h4YT!ʞ}k60 3B܏P۟~UKSi!'F޻@r'p>^N8,6@mϺȧ@HmnԳɳ2KZjøzyhq|9\M \C rOza(|,gA&O5Uؑᵪc6#@qD! 9$--CpZf;}"k>åGG_l?cwb?dNe֒s|F\["Ē@^eˈbd u٤.Nn"o$D`^Z쎄wVShe{3MG]mbŴi` g:B\VCGƒFvyOm9ev~ iWR Y0+F,`|FK iKIs@`h4]Ϙ+kV 2j[Sgq8V>0P,ZQ>cZM[Opr}f8aB1|'$PPpm 76z 2ysE>o;KnN:vCrٽ$i62Yk ,u#dH[##Ƣi/p WF{d/|G\F+c2{ɝ'L|U;ъ3]M]{^ַ{RT{ycq rvF/ld#U\)ERQ?f{ːEeM2V̗n %7>PDqvdo֛w쵉l( p@8#= }M$P?!F ޶HְXLAB0P8?=GKlSlfW܎ƍyЬuow;\wp,y\=1I#ֆN?&d2-Wr:9ČdwӚ$G& ,;gELCo%̥JB}p0;J piÇ,I;+L`dIU"SSo1ibFTBquF iaO<[H"rbxzM]F링ꈑUt,P*jUİ9ܪ6I7r<ldan7vomo]/-9V/t%E^mOm7/.uO?LӠֿ?tܟWk](B(B(B(B(B(B(B(B(B(B7EdU2UC՜]q^k ;3CxಫkJn/ v{p@H&tNHlaj;DZV6vx]ăuR${'xM9m8Pm'g~@; ckƗ]¥}Eu`=u@|"K'hR#_ޛwbHWJݜ-@NZ ko s̢'nKqTZ~0!ө4+om4,y' iG%>66Xtֺm q?=T l-b{#c/G 1x\tK ;l,+}bբ!ז#5]ˍ.SErHĒ+`6Vyai"ǫ.HUL5Dצ?cx{ay;{7V4Z}601g ySi{:Uk䋦2&9,"F[㹭ޡAiڤ1[[GDb<mFe vw\i-/YTֱc|- ]#m?Q+c)Zƛ  S-8_ղf6T怾*J2}y4~";l%q3xH^~޺ޠ>k$t~OZt4.?(c.4#srr$BG%o8RXZ\oپ1*Y%Ya2H잿ҧ:_,lm\2I2C4zcܭ2zVNkhvBѵ-}bӵJcߎXL.GQ4V6[& t֚ 81DY=&;$ɪtQOwcjS&BlQbW&~uMz".\K+.PZ.n{eqߣ}k\*Ndϯf< ѩ8|+#ꌛHBO-aTn ¢)e{;QϨªH I@:6:'H/>f7QyYP%54^t{8Sjq-ޛlr>PgaŘ M?_,F ^7:1Sb,, t:'A#$cΕ ![X8`Gyt60S 32ⅰc=s={5{RoR79K$oPNޟ->'moS5xҀMQFt\u\Nj&>]0hrA<O1,9\M鶱M$qE1ۀۺΝ+4rJq`INY-kjߑ]&#3>p>SNk_v(f[ DFT7NdĒv ( )QOC%}[GY>ta@W&Þ+ ' l~^>T:'WCx2X7pޏ9l]XG)Y$gd{qݚ] P3wzl]:."%L$,gwO:nkիoEl!8vl58=&-[!y`wr XИ#i݃;ln\ʲ]cCΚiV!u^zdY(J纄"v'lXmLjۅh|]zW{|R'MKuxFC'qg1LH^F+<ȋp ..ڵ]Q,%"*g'=1OxaT–Hrcہ*4ٴ{EĐs`&3H%.'L1ڙ)WM)U0[h,9,15λΌ恣QۆNm~ Q3/ }cjڑ]|I nvu#!t5FMΐ7_M|X?'t*!{tim4~ߚu[Ap2X9yǺ>8^[u+@bAmsPb\SɁ$>c tS;|KIyTlYeWe$v")p0N)~n [dwRXiW`tNA 'MmUٺEenyTj͍fqۢ{dYgy5;:o[֓T݉>TFB4 tPådE3 # |:$  _4Odh}˜uXɄ94˕O;p lڴ<ۆ^e,14Kk.|F3` 5('6KZkܑINy桐tRdoQtNDFtw째[x[v}Ua3Z#Ŵa7mեr |hIMxWEA[$@\7bYfcٱ !鲆k߲<\Ls{GjKI[qnL qa5Z;V 9Y,hƧn6]/.-wqw`)LuoUUzę1Lܖ5n_m羚p< mCi5>|ifH$v$pOjRݺ7 U$0${GU+N,4s2w#oT{Z(\4? Uv16a&K#WEq}4u'S:"hj#U M걨F( }¥e7K#lNl-ywvNi,s#]NJra3_ dp7pTgSեi)=CG$m < sCCuYkUMMP䪾?Ƙ{AO@sϠY0XmMFdNf 2uH?E@JӝF=>Jl\z8˞q=l=[_cbH@EG͸qQe#!Tr֪Ez?By,-$;XT`G\:n .+|o܏)P<Asx$^fCǞG倍 dlrv7FF)S>~#]0GuYrD$sA W7\H"/u@1$;O<*-qgyS9m]9!#qG߂ hI4r-yT ؽ=&) t Ǻ]"ΌPZIz N>ů^#{wCsIѨ B71̀U#V͜nm'O[ERݞFHh*JCS8@jqؿŒmGN*Í8pY-Xlͳfhs~)ZQPDX;P*p>5Fg=X ]>. N&a6ӹ9飛>[_Munbf]LV63[$ yUs͆VdeN M>{eq.Csɜֹu#V^บn+^-'BXVy!˿J OeeG̑0̎"ƇFܻsmHf\ !9V%Lg5o>&A&FۺCL/dDACMM %rs\xD') ]C\Lj@)#gJ#t L͕զqmE=opA7/=ZZM=F xwa}#!`u ⻧cdklڼ_"| Fz%s` av`}] G\g{/>W+Jf?e'R_v}+atʹ(4HEM`C,JˉٻO_/|}>ި i{?an֚Lqi"J YJrڎ_N+2 pq{il(EAEZjҡd0ܪpH *XLbF-`lk{җMc-m: $΢Jb Z]VN.N uSt]*m߲q8$* C}oUgqky ]Lx=ȿGNud%nݹhڽO7at9Dr8R6i8N`M25*`gB:Zƕ{GQw\3V,r!odY'-Nm6嶊k$B!:<[ Ikm ?yD;`,lt-v˚v .yؼf=XƱ.+&lߑOq}JU׭"!O:Gw}śZN۔ZGpn$GB(!#5V98%m6wQJgmɆ7o]rc}#J_usBOL\usV@$@8_%ooEا'.fŠ5ۍZX&Q m$TbEOS9ͫNOjw ,"(E Q bik{jUS|h ;={z.D;xUB$QJOy4Gᶉ2&yF恠8>+B%<@;G˩h S, @vƆ9ۓ4i7\Y8`A’)3u.r>=DVZV:Е`ҤE Qք",sR=hz9~g™Ϫcmu~WߎRe)"R=|7&,O6ѹum[Ǣ3~ xn_u~{yu԰s ӳk/Cty \s=3VHfYy#mZ5gmQ8ѭ Zbqؖ^<ӓ K@KcauZ)rN4;;f9$Ӏ`#4Gu*DBEABAuXBqˮiϾS q< ,mʐwU8 8TKeMW26 lA ߀I;RbxS!FuQ|mkbZ;MO%qvi096:`gc8)Ήc#E Cs[%tHC7~jRf2WA+Ouh\g=mHӱ55Xye`Wt?G@?Wy~3Y}ɽwX,zi&ic<^`1 9Qj'Y&J?2:W\mZY_F>7k#ѡwK $tt-mn6 1< MxN,r'[|@#!}?*ȡ ͼisx{Z?ο]ӿctZ:^O{mq+EEEEEEEEEE^=iщklmn(Pd;LNwBt_3;L 0I <2kEO0bq .4(;i)I#{7}AGq90y "lMDI*{vy0>^znʖ#qH .# e36!ՎC{A>bґ( rvj^ tywXx`}6LY>8jG5B|:gVFۑܝ~44c,lNp16@ptN=~3~-egU\ӥ. YL20v{qaޟ=MZh5txXt|"q{ C\];-tZuJ;h Hye@뎵)cg΍++IsicA{MKڅ*>2]>i"['P38ۑctd2;IsIkwPB7[=`Gp%ń<#pm8:yr!mF 䱥|؎I2?Y`{i#m@:K[Krj dXyfo 3qn(q[Go%ig*I Y@*K[^\w`Ib}u{@n8D[[ 9<@qIM(:\`O>A9h 1cNDyA4.;-sQ{~ը o28tnPma?v3Uv]+ߓ+c 0L~Dg#6nPQ/i-пz5-uZdxƸ9@%]>tIix#s{ǎ1PK!c=IKqbɟVSbewka d6_0IFZF=NG7'<1B4؟K$EpG C0Fp*SH66W32RF1'ѫ#`7A6cCZayOɮ@#Qo(֡-CGؼWx&W|ɆG YngNiݭawt7>Z@ZM v[T:gWT.0]h@w؎ uMns'6KSO.u>XM`oNvi65sk l7lx` ~ULs5-mknւn87 9$<>=}i0:Q\p)[#bw#aϢaiXX3׽3q4z(<%\ɍ|G/}N`?b϶ \:d s'bn&"kؕ[iV;̣y=V>Ku ~Ktyrm Q nw„`K+|v`]Iy0Of;\!#qG} k nƭC*MV2pr[_`ٰ֊(xāwS Hp-uّ>`(Q6{KS&..A0,W>xVtk:\Ècwpf6}T+'hPbA1J'3&NF?R cѻp)- YP8]@Ň˂YpW.66l][Pn !˂G7wO)l1BzIL/ȋ@;qQ$vZL-"1&I71|Qx Բ'`0\5PUGVHm!G>'ƹ\X菖 h;Wz.emTW(BL X֖ ,< I} C@'7\b9^`` rHnf'448nK'~C4dHK[=Nx0oYV5]{3X*.F0*rG;}\9#a̮J=9#v?`vVvdI)Q4NM-(flɽ֠ȋӝl{,F Ow}MMo%6 0pzF8[Mf\w虥+c?XH9Zr\i"X%QqƩJ7]wMQb=wknA3=񫫱ÞaXeHCp?bNJLi ЃiNbHM*NNG1OVꄱ;ZF9{qBUBD԰!JY۽61z);ru;G#H)l- Dp2=\羡kG mO I9!<nWiK+|yII=q0k]_b2BEwZhZ=Jvi!V P Eձ2$>@ZnɳB;mZ6ey6zDX;F1 x:ȟFMGOAתt~GIo5%uq*Z)] R.Sȸ`u-Po{K^/rx p`%shj,v,Y/`JNԭ=>[t"BH (rf#eʞ[[,c+;m]!3(| _-`ۨhU]Um*Kxm#hY[vr*d3uպTE%AB#Us`*Ց,W"eY,=QBerm;gF#FQNzr羽t8#iť`y6"9cV&]* )k\ApW*|vI/-k8揵F4;<qW,CTjj2@4F1ϵ7Jk8 lucw<̠Mԣ q?D .3R=kIhE &O & iW 6#O*!ǪO}*jqHHBU‘ 8",rBR!CL͐XENEoJݭp$p: +5>')n^oX'16T{V61yF@ wp+V{sutdu+MlT#88{`۷L#1Yy=N)/t+PGD2tCvޯ[Di4g10JOm5r3YNċ7*!dsB}XiPXhd wSI [+-3 Ӂ;KvB„1ťl;n#40nq=<$݊UHO&╠dCІS(!Hc4a_^)۳1Ӓ81UV Q7n}] Hrq:K@nƶnݨ0Z.@h Բ9paĘKKi-p~bC?mH@Oj>ZOfm]ܷ Ou,ef̧ͩ^۪ޘSk;G7;09Α[]7`;%E HPrsB$0b>0,Am6IiI@޽E9W+cay!hޛ ēd7~<"^sҤGcI.i/pp 6aZ=m{##碅-hp=OnnUD" sq)ǝNG8ە6wf6C[MiÁVjh ڽxi0 { C26 .s\H$M|*u)l[̌u+%:8C]Zn>m"dB.sCoUkoPykFt{sconUeK+;!95fg8Hsm8 @#kvtəq#h_=ԩ&yH\#ʢf4NK`r6ev<iw()m?iwe'5ٮ#22!9!1ZH/[Gz"k2TWxmtR^ё-!h$-{èW"83,54ղ3s(|Dž:Q[1M(kiAre?+Cuv ~e I%p$8 js#5d1dhkH\7`؏R,Y*z1{ƺMqSJ#=Xz\|SMժii-RYd 9ѹf3IuJDK'Ql<,4 lxh[hAgse \(9HZzn_}F#sh:T+Mj'[l d|qᣒ{= Mfh`FMicΪME1q y`iw{.𩥊Ka1;U#[I;n#LAq6G@%.sjɒWQU=tQ.ucr1 t2%^L yUSH$KiLȁWA(k`*ݕ #ƶIZ8UV~NZ=i1ܮNy kjO[M۠ncV-e&,5|@]U*9V;[3; (A6TNdB#Eۼ=*Eh$R݋Z^p@amq% =Pż7`Aw N.lMMx8-A`jMѾV\K0|l{`mǕ4ИcݺϚ;ɫ,YӶlTrSR1⮩K> Q pk{q 0+.aj-e$ǩ˙) h {TyĒL(ǍMhklN]ͩt쥚5x,asϧ>ivNׂ0څց u@v0ӰfxplUKC#3eח:yn/Ov+h14>H"Ah ',NN~Z-d~_t_":oOA(p;FA!ڴcp\r#Ot. aZ*:}GIb2:ZZZZ*P+(lW閹v0%P@.r;"gw84`O5V:eƕwr=]x $l`CӱQEΠE]\%tit9VUe-sa4yp{˚rOPR- ye[u9//s@-]Ă(MM6n޼_ q,LWy=6UY2MIY׷X}sG#<{ wF_>ս\cpzs4Em€e2~x\|@Vywj9y"ku|#?/*V1ooPos.VLk}dZ6A XոzIUY-cf,|pǙLjAKkvqe`u&vB2?c].vFyQHw'%'9w8'oC#՝y0<6uhF6Դl$h mU$sʟ:oOe_q+K04yogoYq~8ZeP s$wvtvD{+ƱE熱^ܒ$t/nokt_UVFrǴOu6v)D}-#}cdu/OkNWyjb Е=-7sٱ>x[4ʺL=޾KDkbYP'74yCw)'*`# 7gg;=Yk4P:Б82KH%4w y"e))$) c>xn/Zz cd3!xźrA7[qʇtNG,#RH_.u,l,`m,ɐt} *EIYOyksi4F)vYeDd$|IV{K3 '˃ݤ7 ķ,.Dv$w*KAw+!g"y*P1J$c= qkE­SSoU {c8VZ7֟ޛuu& =UNEi%\l`RKX8u <$OFiR R%Y*,guG,4!cuAzЄd:D$`)R,AB5~%30uL;gQ5;LXK 6vHKH.b:fX7 &eO/ccL*=iP*֐NsK ()STA[YTU@Q@}cDcko<-(/bJ< V(o웚 "7xA4@<*c $ n)hي&^L;,ߊ5Ӝ 7{ t5ASDֱ L AÝc}D΅#icx>W. 1I2C7` YvjF$[ww8+!{p+}}#3pLΒFnmz^b*㟅f дz\oֵsޫad {!0:|Kr@sN :iw' g+yLav,)vI!ݸuGCkj^Neb_9|.}{ ?ecb;>j<} JN>.5.bc=ej |Cۏ$Q&R]`Q7DB ]Rz:?9Y926`]`+ʪؕPp%s*$ipmGokgOyeD(M.TH$Ӱݽ« OyVq/J乞,g$ZO` Z *k #OQncή5~, ?IkF\7>o{*verDO,,M zY#/t͹w.h\@ rAE_|WZakr KB\g$椐1k/#q~i#憖Z-u PI#'΢yquBsCiw{PR.\ƍp<*N)]@-6,X,m7wnmWĭ+!ux;w=ܪ˜*(pڲ2=ƭ$Vnv鴡p` A,NOYڼSKHo$kUv$<^T+G&a+u Ēyڹ=? ^_Y;(]{Dw, 3s/l1,_%s-8 9ڹxe5ƓIW+ÉB9}Br@evA%=ڨnƁ6H;l9m<%kY r/ŵG8c2T쏔B 1.GNO5< Ԭ_"iW$zs[_]^^+`FcQ4G7ulKXԣZk7\ ?h K&4AoN)(F]>PwZSVg;y0lϭe-O:^k>m$44lnp|PA<:W ;Y b91im0c\q<1=7W n7DZ, y{ll,g<)|QdƦDZH ; Zf`4ŦBYw~V>8uC.uWe}Tu^s@lrCX~Ӷ?{MV1Z`14J}k 9 U-%?l975@kb=8Mޕqk,a2T<~JVL׀AErdehD#}7F]J08 S ;:79yfl}N`M>S]%Xuն[jZkVW+lyøϝR!=<OEu.dv^Aݭ;V{;(k`ӧG A;0H.u9yjxo]C=s2n i'NUTG$7$"`ߒyX01W`2O\wZM9R)tqɢ i@#CIulR-_ kDo8̙X6wPP>oA&MͅŐ~2d1*XuFḪm48li2%tMdpiFȽq~RPA!f8W $ ^7B<TevȪG%!{"p2 :Iyv01Ki+K>vedX <lk]9YYQkCqz$$}LrA.2#Dۛض؃ڋYkԩP"JF1lGU W1pp$)TIQȎ8{)Y+kxp}IgXViZE;8ƱO;˄cKoyH' =٪ i[n2!LqY]Ԭhf}Ac{?ÌqУ5 -Dꚵɮ&xl RJ*T$B1BqBHBBZ:Ѕ&K>4L$vN_\w* yeZRa#CA G`{sR,՞ ZExJO 9gʄ,USAN-!Ð縐4,Ԛk@+YYsN쌗=ےy)sh4%Oz;@ \{.r@itJ2\r {djدQǾ41H͵Im΁`oʎSqSl&_@)Th ͼisx{Z?ο]ӿctZ:^O{mq+EEEEEEEEEE^>id/`IQdT>>PAtj|~2&2 \RލJ!8g [}擷w}=V4):8o,1r&wZ4c˾MkDӷpSԥ1FMgα撁#r@$H[.;Pt/͍AylXm9y$z5[C3Llϸ컝?&(l-;bqdk=Ca@Sf z/ihhPJ׸Ǐ5 .$1 XiaCouj(p݂lOn4w.yjUWqhcT~(nE |^W㳈G'ޖ /{Y ~9d~s֓@X`h6VD67]nc>y]TeN tfkv&,t6*UvqS.>h\$ڼjS Lr1-4ts2qw6<VuLaM9ǥ GěO%&hm &lW*n;\SZ ƞ3wsZB;MpE)V%O-;uU,){-ײ>K1Vm-:lbI|Y6A^AلQ ~//;}Tq`=E4[];Xc>Sȥ^MUz"k2C v}WrYO8y9UY8=sw&-&!0{L~ASF TjeY*Xd?֔nah]'YODZd={0-jf3Zq4-1cxUIEzR$w$R?7wi@t툲#Tx5kn]4w⌽M. njuxW ծ\МƏ#FT񽡑7]Wޅ԰%~Ryn.6Џw:?lCv.SZt-ţX&mMjD(16{}qomב?L8x,F*˻L;(ӞX0x?V:w$&6Kx4# H֦n΃<9p`yK9DoH UlrI,7n9dQuxq@7U5"',$hō&CgTEۨ¦Kx+, g/u=uf s@;.7[۴!$ƼTct(!#bn@44dU;Y[މKnP~5QGZtn򪅣S~w civA,n8 ~V[M7n-W#p\*sbVf,kkaGrM Tsu .ZW<܉ΐf|x+KixܵQR0ams82)ڬGxVp%vK!Cү OhA^FHrJ>bnU  CjAEsoq8ڑ })ǚ!gp \|K;PݫK-[yKF2HPYcAY\n[\qK`x}nxMH&Y(><ꫜA$mKxA36CFh(6mCo{4EaׯeW8ySX2\ZM uq<\n>E]ߐ Zc^N(Y}bIYW+z3Һ8ቐMI i1ZNj;w"Dzc. '`wyWok#8&obѪ [CoR$ $ɐC/Uu½+Ǻ8xqBXdjsa`w܅uWT`]ˏ=u$qpY^nKt7isj ZBL_OYݷ٩`dy}oð;/c,uD5[&mBkwJǒ 8Amz~zYbGw~ʗMӗKLGlp >}qT_"=<v0,|f+gH'ΰr1f$:'߿{_ι7JkfJZ5U9ͯ- {X c tY;h׵%\Gé9VLnk+N-zKW62ѿ m#8aYT- Ba.J (ƼQ FydDbܣי]/L ௝ȩ34`6m`os$r%aAcDbI~Js`}x\\|'{^@IF jpW˩[rMq&]7)H$lRD#u Rud c'F{SFg}Շh~cWzn?? PJT1ҊJ v{Y-$s>y5v+s|TY_ ֮imQ*@KwSҹ#$F\K K[_VN7g~yMF7 ivO{w_ZY[oN2BXH*M=ե vgjH80V?MƗ1h@߲j߳3 hǕH룧JrC/z潯KqgzZըXZN;;XStZFp`O gVqkmh@|B]OSs-HN[|껳a# $scq!#5nԴU`μRÓ pjSyp [2K׺9,p*>fSra:FNx踂[yZ'jQ!2Ӆϴ@鎸tlk^ szt.הul< 1hD=--x#P,sncAΫ SI#o7l6;$k^shu艥дS:RG eq~5AXi#('c64a76m@pͶᣍeH>r?֐󶮏bL8Ө߁2C$pŶwc \v [͑'|WL @u+xÃT${^ӔE.9J0# z-+c# ޥ˥n(qO?IkIngM5hΩlnw8f<΃N|ƗIo3Ȳ(X%χ'?K}1[<J$ o4+nxQShs>f^괏4B}f@ЄT!!`BqBI B);&h|rφi,]^_ M]'M|ZNiTk.xU0%uզ tC&HL!Lħ](`RD@{j>qܒW.8Dړ gN@Ÿ8Pjnغr離+JPc KyEA#bSoٷba|r3n{o)N|M9WABV([i(9t yixO`җg?/ mNt{T{ܭL 1M!L2Sy\܊*6H_az}S RZb2`*)qio!#J3iPDt/yw+JBߴ]/-5+)L;~6ʂvy*/Mu}9)X,vH})_}Ht2rOSPdK#g~ֿ +IF8!k-nVw4FM(z1kםM;3-,;ڸMDB1u9#ޕE,1V׺ӽ,zXKq1>n(R|hcfdlrq{3DR_VߜZDm+gO%bb.kwtv'W4}^CqoX. ,9 ^wRݷW,vc׺ǣ³`;8nk>9n#c6-}$pJy:':3cð,Y6kwoƣp;* Ӧ5gy%6܍˖ nDXE C#b]I"M۹mS0Dcq<Υ.N !nyj7 5ɅX2TB6fU@^oo lq 6vv|F#9cq|Z-z)-1·$Dc5n;pm n!9LU$DYWfc!xc@;lջ+hHF7L"it"\ t֚@O&ʬ\fIK" r1 ϐiIuoX^t#Kqrʍ쁐5N ;-,:^. msFlܙX#isи?im,(r%(s%4c]@aIԚ$c`id8죮3ޙfP9y5,L,͏}[_'K-:-i/>B+[Gl<##s^zl jӧqu~DFImyBBwj68coDwuߥܖfi4fԤ๰{;8!<>'BZY?͟?t^Į5CA;8-s{1]hLmŹ҇' 116~P߃{>MJ';0ċ=ԓ;kخAHCu]/HKZ֚~fi#Fx?=G .e8\YUu22GkQ'sFF&$4?rc`[ "ɢE=iZEhH6 γ -ouq:kOZď4 oe* upʼnY8R9\` x%ǀ/6E{m5jUlgsV9J[652@\˽&O~!IV`! 9RbKvuSqk^5SGēCUJ|eu[N#ӭq7'"#&ƂV$泹#xs$=D' `qYYe~+Dϔdi\AWQ64*F=nKymk=p¯DӎqK&>^7swcNkdfI%[Ѓd>kAӝ }@Z3EK}la\3hvݵ7S=2q_g~Ka?+WއpN딙_o']CJi\;. 8[3F(5?+:Nv.IE6kyNRMEvvw/(^eѷ793PnϘ 6ڕ$6Z-e3K͑:K{Casq7TăSavIbCy>]i}RH膜?ܤh N7nfF^uwetI#~l>h0թ&VzլEZ0!۸`\dscLZj6m؋W B.wg4)'d!Pn>].,$Ip"wp-$[B`+>n%veTv{]${l6_?uؽU=>9CFZv z-lo=\Xi$ vsJs+{_e(pr~j1~^oz(wM0IV1#/y|906GJˆ!!x$yhA7Q۲6GiG,R Z~nםvl~Q=Mu"*a.,&2!Oq+J9cD#5[M{mńy!WZ#yixO=aqqַ\CulLx(s-u:'sH#o7Wè(݁ 98mvXiAjK\; nO)O*[YbiZ6 dUvG`inȕf 7rFiWzev#*Ss w#҃H~Ip߁;=[x YPHljpEXa.;aQbch$hڽ=?g}ǿmIǝ27WZ4^GC,,s5H TeACID;{01 $QMe1s<04M½dzΧk 4;FQuUwn[v:uB`V>0Ey)ecccd%%'=ʏxE&Xm.bF-{d$ኹ\|L9׎e;Qn W=jIpy+)g,D]̦֑iVl@8HuhF:mCPZ!Ȩ>5>(F۳zGEF, mZs@&ՕGsc:C5ˑ$=IzUhycp~]C,pJfn"ͻa)6j$ ?WvW wJk`UX=GKx~CVь(8m+ZK3#`+Q hhed;.w<^I4(Ya2Oiʪ/u RH"vIY$,>3xĀHUKIjzt0Vܠnqq 6_u 84'94!(R!;Sv`Ed7LƙuhNq)yզZV^jacLsyB#puiIk<#ى@ ,8.aź ݓ+G|6Wi;0+'gP<=E C7`c6=9;Uxhq 6?/}*jFfFO(Vv-l3mϺtqQUɝͷnNsTzR%FyЄwt>$X o^.cjښ/t%E^mOm7/.uO?LӠֿ?tܟWk](B(B(B(B(B(B(B(B(B(BIƭ;v&ڠ6znNOJkZHϕNGfP`lK]6ЫLLT)yr?56~tܘc];ꕤëi}$J9+HyC—o |S[H=ZE$@)/ 2iSU>4p#:r s^U7W0]iRʒfL2ζ1rk`fl6aіhJݖ`UuE̖)$qZIwCꞋ۴+#!#e6+a|[ J*^U qݴjljÚk[kxSlq6 ¨5@qQu<&ŇHmSwu\oK|m >@UjJF]F[uKkCqw$m ۀΡ.m ;G Hq%ppe[KP@:`qJzv&L״u5gshi#@>2`Uw O- J/k-K\VEUqk ±_Eqo%ITqɽ{#--+&|̢K(v|u˽n㳝B„39-5j8h mddCgvs7Pt$20%o,\,΋3lCI:H%$pgui It,X)uCq 8c pq;z+k:Kϥ)ph60zdٲ0WsGh9?R߄+q7vSu.uiZzYsLh`4Gs[uE= N UQjYk{YϚX#{im)`ܛ~3;ś,{P\N"Ē6H¢fm6jm F©f=s r`'=)Ti!7mw<5Wu`-?_h=G'hq#`>Ɛ:酲{Ik!_`x[I'1)RhpwKn/%^Gz !NWmЦow7Tzw T8>5Ep-ifDž=Oezn|̀{[tm!y`&X쬯(V}bHBdf0A9z|V8tF%4FF2&pA-WV1^\,0ZvwQ((꣓'hu1)~x9s|h4-3v8 GxUM FGZ<.Qs(Um`pݱˉ%S^@d=×}U{wb vM5 ݁5goiZYn›*\7}9gVa֊]7K)bՊ8?{.°ZYMfĢ/`K,ʹ|?`Flt=%y] cZ{˫ޅq٤xobՙb)p"$W2>^rZϓ*b@x ;Ł=[(!+/,o{DK[ZM@83?o-D} \}85xTvDEu;HQRIH(@!wq(qCKu 7pϰ9u/RӡQ"mXvyzwxW],2Jwu\}[Ǐ3,FCTt;Xk$V$Nqc #-DpOQ8Rp,.ߥp;!"p0:Y.+KW ^C\]l һ<:490~*Iە2$"Àw=.?R5xܕ~Wz>58h-U8b;qi,e%d## H6*nB\%lvtV[ xKb3pU&Xii$vmh'Y Zմ2JUBCakluYgTMk>ޥ00dY <2*O64 {^?6癢̚ c +눥D [ <0;!lM ]Ժ9=G%v nx]5lHSr!;).wXi&lM sy{͓^a< u}( zfء\L,v+C\A nm}.}kҨFnˤrӛCBF `yz@ZI1,QBeQqPHQQ`r9ϲp\M;Pbn'TL2 n.)MB;m}wvE2bdZq.lQ5<]mj{໨ c\h{'^_\͖۳\:| H AO+m&֝y-&TyS@9_+ [!Mw$XN˩hZX+iQvDIbߜfwcG/Yo|[-..m{('=NDpx #O>T7+!hu7|!l$-cdتm_/^6{5wp垽yrXsŞ#(>xc\|ͭMwpD$#jw ǛUU' SGT8[ cJ y'8υa~)%,TE7jC؞[iWÍL|mf`=ؙQgb;nj66f{z6MջM ?a `xλ=IxLŊHgҫ^mLCeܠ"7[*l-kx|%n̠,R1 7F<~zhvO!O,v:zf $ pݚT/2N=TuBk"*p\BS%e\2f+HEk斃}7|%P"REmCh9n#W]:KmmR -j'=I$͖FK!'1(7n=ϿԮcXhÇ ~25}Ŭ:mh#č;=GN~TҲ!zm:w{| #1 H4zʄBEWWK<Bs9'Q1qZYYL"h 7ܝ{fPBa.r`p=r6VjNq#PCeHPY"ɡ*,3߬C/eygv<;/.|>+Las|VvWU1$Ѕ}?֩-H8_@/6k`:vN_o~h_x?:O~+ѵĮ!!!!!!!!!!ymtr}iy#{U]E'6Rƍr7"5*l> ht$w uNHբCsɥ.'f9=5}.,AWg]n WMLP1?f&ܫ}{兮}-X- q +" {E.3nrbdl{Z+wkZm;)^h@e)kZh oQsc&ۛ5*xd!pj :vPa*G0z9(.zqi. `@JqVq(|hz|+Ԁۇw*P[hdoFu3C#a5l ɖ)Ps 0GHl>*6uv6j |Xr1^_su7PxSƌ1#NTr'ZA{HC@{;=#.\OtqvG:j>G|AZpMHþt- 6v4c•cܞXʪZg?|H6Z?{ЂlzkMǒB" VSl0x~ ^Ӫ,{Kd`!1z8i |?u•yڛUM9k2y~nH5lRtHsFztIQɕah5ګIDVYʳܢFϵ#t=.w>ѿ)ŕo-"BLT Fڛ#݂ҙB]j[s}fooAv´Z8nTl8QMZu-v=:I" 8VC]nUw2^]Kf,Yj0k'zԧC;%PNhtQ`E+`FƒCpD;+6b+b"0@;斝)Nς58(:Pn3/M^}r[GxmfVy1[O|,phUL $Y |w*2]^\—*'{#'P;V7fR f=Zz UFrZ{YFP~;A7-\eȅj./u4:m3/ "#Xwh++Zw>׿`kڻ *17 %a:/#mQ^%X׵qٕw|GZ&X +xt5hۧrtNj+ȗvL;Dh9Z\H\ )7%J(wU>mؐ;rWG+Qu9l{&Ut30Wʭ p.qڠpH~Rl-͵cbƯ8#uds3O&CC8#cy>$meÔca9sݻE_ou߳]n-o&v67ޔ&N:gjp^+oźزV;Fzɉ#Wݫ}s>Nukw5H;HㄽoP[jG5EC1nlBݹmM3_U|jl48* SwSz>S~54nr(=>R.8ZCdsnGq&Nڈ}} {ƸFhR:HtchIz2C>_1 DZ[֛Ŷڈर %0>Y pbOo^ӾUuܒ3Z܃7+kњ2[teucfŔױ>S֣WM\[C8=K/,`9Udž`|V}J,38I% ݝˌV|`;K{.<疗_mqW/&ݯ;d&rr;/zɗL2dq&{{pG.`w3& e@V^|AO@z+Znqۀ {px^~(xM)KcʸLI0ߤ}P -sfxHGU m#)쑯ݥUvZlhڅۉZwڜy6~v0lݥ exC ]]ܤϲ1X}P@hC)GDHqhkH4Wil7P!4Ռr\]4[4 k1&. ݷ9gCр 2j c>vt 5yҿ~9OtAnm"eH%'@\͆<ڍpA;.p=> q?tz&H*84^p݄mAl)G!psb+#˻' t=Ek[F w"i;GHĀc9@zՠk?f Z Ǘ@+<'"YIHZ Ȏ6C&d˅#(c||F7ih'DPÝr$yT!"!k+ي\3%w`֣86oE5`IUQ l v ׫'I߶qs.44`٥Uh7s|. A 庎Xs;<nh˗HcA(nZ4Mӽ{}WqWY)>P2}c>ǘZ>x^w,L6ct#[RuR0 Hؑ=s+cN8a Avժ^!^̾F\0ݑK/ \ABѰ;>OmY#wJpIr :o|1I hA@Křl(9tv 9$0KcsFm+@`f4p-lwb|2\NbF @c=Tiw,ӻK I Zmj2Y\at>yY;{|~yLwj]m4|z$W9|4J(bTd3}}M IOF>65߿Í~TYR*7Wm2ERF }F~>ȑ̒UIڣ{ d>t8dvH>@蕱Q;VĨ,縊Yc,eۓMsNOFDo&5h_Ħ'STC30)OߌCqTb[SnRis<W*Ɵ7/n_\;8 ~A壭?F$PPPPP߲ozz<5-λ4Ʊ pFG29Uv/B&/kP .c"bh5 H ̒ry_KtޡMjW '!yqnBSq_iw/X[[c{ʸ,\@])vBp YǑ]q5klɷZ.Fŷ.9 /Kz&l8N>)ӌDsqbNLw kkIo+>+͉!Ip] _7K̙@C͖h۵ ŕ)ɟd Q:7{Wɩ:WF깸L1%Kxlw\Ѳi0[ʮmFW:Y,x>Lu~=:M<77NN؅'ݱY4Ѿ |!ci!Š;˦׾ܪ&Gv<1VjW=z.<D0nH&o6< & RNۜ_Cv^wUL&h4֤XkLvCHhyVvN#2psնo"pɨ]pB͔\bis{bkY6A$.2&9O|(ʔB`H$zIKtXل}J #^\\7=Z뺗Nq4=ăxѐxas#Wroscfcb7Q4%vʼ_1CM66?n죶{㐲yW@tF5]3dGq.h.;ܛ?۟E6l\(\OgFS.nގ?gysᣘ!Lcj0W~O~AVWErtwK-,j*19l,lրhy;>9FQ(ْh6I$]AĀ\Osu)#.2s# 1s,ުZ#l>^C#b>;HB04罇\.'JSP8 \1}{y=Z~`ccOehS)۷ֳ]ʯH"nTx$lL>4  ,J4h{حcP(1,$Ɉ9SfK+G\$&6ۿ]qvEHF.( |1YQ1쬷Bd{ol p1|֌qV9pRj']"6|{ m1束֚AuP>^Wx̔i.#PiQnIF] 3T<)]ڪ sX`1:,T(A-:a6^y!iUɑXVx+ oX:l lnm.Usp10XȒ'd?wq-'Hcᚯ󇝻-&&.I[t\w.X6#qi7fg$ h`wԭ(Dpu cyn0,]mm_XX<)O:ΧWdeͤ8>$.]^-x`"sם_v3d< Z9zRtJ56\(bX~!}7VE*"tqDwV[k'%˔HZGvP^ihD`0<<*\ $sNd`Y"gmz71CۜO_ dxUw>ݭG(E9SK.C4`$n²܉  #GX7pQX9Q g×`js-ؑ%I'oqYMtmo۽C9<0$5k?sOcB@R$Vd}f#ƴC(+iwlҲ.ҹ0H9AEWsѻ݇# tH/-E6t گ}#K톓h yIJcn.&^/EwXTO*-V5 {.1IV\Ob?˃˞H99b% Ul͗ P] #}=TGVh'Vb7'U90AkȑG>4,lv<{n44%Q$m9SϠ#U7u,3$s3X{m;/b{5{3\T&wWGN`ߧXt+@i]0=ncXǠLe rl#qSӐ4,l dA4Zb@uMnY5 y R[%O0{u5T5-]+查Õ؄̙%Ç;v(ui\:[-mq4Tķ?|RfH2"}+Nc\{1]@W=+Z*3,L wK9X:̚e ;?~:_Y.f6AڀۦCt' r7b;}e9P<'ƻXasQU8f3u\d}86U զ"@ɴ.Sc:c'rOA6p ui #9sz5G< L_:uo;f 26YAĴ lZ)qqq1qyӺd ˱zVLw--s 5)my-aG J>^m8Q&C "ш ]!kHڹWY^U6VʢW dY {*5^f7jug RŴoAzYYLPBrHfе)a0Co`w5AʜO\]Kt#D)٦03Hﺽ6Scdk~,SR36z^uV:<Q_Wcu{fDUhE|U`b@b[*'ˎ&'Lg虥m;>&5j@gc\l+dקG0@<ʹGrVj+^uRKעgߤ*Ҳpˑloc#UMzgI\ rǏ}hDM#nӸF[VtEfg%CB4!~Hpv+g~S9nȥe,HB7c"u$H+dl =1U`aasIS2bˆ a ^hPk[n;bI#!FN7Tyxh -w] kV'!UlMImCMF(Hq 8* L$hGԲccC<\y%Mar $6f#J9O3UHoeE.lwN`i5v6hNߊ[uƷA"ɕϴB|ھo/P0=jk! @u~>h5gՌ7 ?[@ s T>k:D7L:%OcI}Wz8t'Y/ʼnI̟WLQ^Ϻ쮞`&AZ,GEBD\g:^5# 0;z~jYz>z@GmSw֤FV+րupr2cmdQ#Q=zyqtIi%',=M+X}IffIK;dq(d\& 6ykA^Ičxr]zS4kV1)oy?.@`ɘn.3dLÉ hys lD_4xfv͡ ;Vswu)=zMqU؇|vwݑEqO"mhM^ј csg}n$M=i ;@s}MPu,-֠@4$ΘT~i|pNk}4ƛ&s4M&3TaqNeFM}M x-qn-ÐӸ-mcQ&f{ I8Kx U#cb(*8A؍=@$ܗz Squ%iby ++#.\]-Y%?cM83)b {zmU[+%CmoaP$e1RML{XY#}r`%Rel9HMpu'nh]إ04h{ح&;z.I6 2|nZ9fFpGrI=;>JF4Q6KaI\=s˕HPo6}?R1^z 4 a~-=Nםt,s1 +ߖ0ʟژ(H{)!y?oW_]_ܿ?wp3NGZ Ir~#^%tH z`}f~6 cR'֢ɍmU5ϱ=h\qÃOM7Jҡӭg"03T!tn/$K6=Pg}l~'Zw ];5.iwVFe $"CP[8B_߱WEOsmS]KI5 >׶&p}gTq(B*fZނy㵶N,ֻ5۫c5G9,1-ip_?u`I!n3C1+lξ/Y]K:FuPu ߳< _W|]&Ҵ86#۝fbHogomKXsjghY 7f>:Y#(ݩZG@o gJd]2W0[28wCu_q %#c+ןM%P?;a39ښ? kH6*m r|%s1>_` 6(D`+8HpOx*!d%9-&TsSւ6Os 1'UZ1p-o{[^08HYp#Ċ3؞/klڃjoq]-qU4nUet&Sow%=^Jʛ.GO1.qejLHKcVZqږ6fF0;oMaa#.Dž9lT3kH7wc%Ēy)"'ƬG lv+e}(Jcw߳o[kNe┍v]A,g)FZh o Wme ȮE(-6Gqeh1Cݷ+pkIfJr.ğVh5~ۑy}m]k}ⴊߓ}2FO2+3kkw^l9&.J[T|$W4˞8C(2q]Nm5X|mR-fG V&NXA׸n$sdc47H-ҋ--nuHBiZJv;Wc{&' I.8Q4 {nq@ػQDKǏwUBVKk|!IiⲜt;F3٪nyۀy\XbױKyK[=5PL}{*2gqWvk;2R!V"XHeǝ*E+MyIT)+PXѭݸNCݍHB8? [iLf Sb0oǥnvz.fc3]vp6Pr֥˨B;rp@ 1G~?d^LgX !$ɯu\Ģ/[yV/Hlfryd )O6?H$8VpjK6yQdx A%dQe3@oKE4ڠ^~-&(uF BIUr~j͚7:GAK:Nl1uV2W^L.h5(/aA⑧e(e2|EN"-p',\8d1y 6Ʋhm|,;귰vsoTڹ s9t9 ۱=bu\|Lcq opAЭ]?^l% ! |` |4u9guH$GV:jof{h M%.K2eTu:p ΑŁ\#p߂&ZdU#, n |'E]~> )uw6L >J{Y5ƎFƏֱ1c)an7GTvV[(VJziR0:| y^DsFm-q\F.$ ~"sܸ<e~t*?َ?6rIGq@9ndxa"fǻv֬0+ .Hf@ 'f4=,S9Uw(@4A*UծK'hs玵 gMZXtzKMЫJ޹JHlE%ĞS1Ɖ6aq;аxn$mng[mWaW[_{9ugOmab#10nh ;zRjn4֞(4^PF$jyn%95csʃ#VOMtË.Hw︣Uԭ%n݇c×ljGSEr)QsU[ vQH"Ր$zI+XpC (V 4!ЄdP,ЅBGu S&g.Q6n8Wȃ\XL 6={&:TLP(QHX8B܎X"q 2p; ,q>RCz%^ZKepL%O)M6m=mT8dNW .+]+KZm,[(-r ڪy`3gG.PSE'|OuղI.d:BFIjDe[сu@גӰ5Ut}':lF3&"]+uc#Ãඋ~-KdnT姙1_5e;LdcH 1Ct@wV+nejG {b$8b08O:,E+x."Vos[j'q0dz-̀Ls6Ar;W[C d| Yc͆-a}qYVd4Xke0 X0 3rZ lop^p{~%8]VVu=x-հA8RyrZ2c}~+2ò3KɷHCH&NZk@O^:&ܓ1 *'rbXj6Ak;+c|y9|K-Ci qUkik&U)?D01B񼬗fL˗i(Bohsg45f#G\JBBBBBBBBBB>ٹeb3}iX pߡRF؜ٝn%b(D3g09gp9WNw!}y]Y0. pXEzMMw"(yTKx.GuZddc>//Q|)R% ]YqjhѺLss1#o-"ɹ&>%.(c̰Ӝ^il9-CC᩿ﻈNn,f *sʟTku6@^U%5Į3˝T/Yv8rOQ,x FԈy 3 5 vh@ z,e <U|ly3ef'`vo'hdC$2+x:Fijh5j:2+Lb*ٶN-›j+31#anaNi"gn,npc#¢js!>;.7,\d¼Sĝ+ᶃ>J{$nTG֥2LI^F[+/Os]!뚌8M->.!$~m >~.~FJꠅrRp y6?G͢nAxw;6!ꦽF,s ҪAY.s%-k+y&r jD g<5ļK% >sCr̘d프v *X: !'SF8v 0wQzG5VWk^i|p,Y7Ⱦ;P)\NR8]K6'O?mG5ƞ]ҍ+PQxRkd "G6='J.Ce g ֧h9mÉ5^;mhW XTvw7Z8A Rl3a)\>!G`xcC*SLVW8rSQ$oUIzxb"āi$cP$}Gl]P ( #ZkQK FaAVuc^ 񸪜L i oiڙ{8ΡsyJsߐ(Pkϵ~ۍ=kP-L|=dWnS j6hʕQUcKc"G0sMs+Ǻ! 6q}&f oaC4SI)akm z!KM AAPRi#h*@l/OgZ66@b||Bwg9{(o@yUQ=ǭ[ m[zWI[JkKySۻm6 r-][~=Ծ_$Ři#Tj@/[DK%vEiyp*ז8]~ ²q#nn3M[i$ZMoGxKa`L l':p}xT ǑǖٴA'>.FHpeRPSy}&K#IU6wa׾/ g/ 4~cg $3o~CA⾻*Nnhn\X0gdU9YÏu?u#̈1y#%Oe(㹰f'zHL%XѬǑ'8f+NqHoqǢi){k<<SME=^Ț5K[ )ayq@16/? Nܐz˗qhy${o7zw[ܖ60[QA)DYqXrhJf>7ؘ 8چ{v&hΚd7fy*Ҵ1z<‡޴z$>,/RkqgI:\Mx $ݨԴIGu1LQh1Ƃ3'8DchxC;[@k(]L vx 9j{₩ײpp#TEĸiOk6\KjұVln qc©pc]u/b4%ȨݧU썶+%I0"9rL#t>]2JH´ߔT23(1M‘cKˇr(b,%2'멗C~l決im`r w\]PKnb,1ulJFUϿjZ3nhaEͭFsC'Oִd:q,tfharH&ƽOڇz,$+kuks P7I4IM1JهC7d)$@SdG(dαS}?Iq;H/q A9V Ouqq6#/edz,病Y9m5$sl1ҟ6NKY+"kIr-{^P)WѷD?;>[ y^Z-=">>7Vg;E&vagcƠ& kP#q|8=UN[DLID(sOw1ā`Zr+ ޼$rh V+B@yxhp.-VDɍS{_)Xխo9TJ>=9o$9U{z08LߓCpxޔ@dY`TQ@7W4Q\К6oܡ#vq˯)k#|GMyŔ~u;U"Ty!c4YHt!?owE$#W8F;{')Xjws$i$q Y]ygVA4T̙X#asI oc4. yLV$0pq;re6_Z0~LxW=29ŗF+-+Ѿwjcv0,GUq0osԲ7zl׮ߺԮO}--{u‚r:c3sx` ]ۮwu>S@~ü;gV*ZIXvь^F|/mۣ1zsnamsK^:XxR&(V&3v3j!,_ezWh<5S~]p4uU;۟HVzT0*rٕ759jzlEOs?F^`Yi>_!షE oxc<Iw75S]63^AMM= ڞO)KV'v=alR3 yێ,gCˑpurutWTVi#M>0b3#Yau8@:owV{F6;@q[Zw*RkgmcQur(|{Zzv1zsdkX 8.to޹Mèj=qG}Fȟmxr3X~3'6tQ^*ivl,fe\C V} :MFj)5+;I|Yh%ͩYlE{HelWH9G4Y\릚u^$q4|h'oAZ`kt7 P I~7F~ i=wk-(2⏦E48kN X޼ڵzQnDM)#u:jŎtqNO;uE62)U$ kǎB *`ȼxo^4ąFA#TNc8`Hևt%&V1v}򪏗KK[wRg7H^͌QʦdV8f^~x1"+"cG$ _YR@R9_ezFGrّ#e{i#`NM$<]duNf?d~DI%i}jA`(ȑsI,E粆aZF6.#{}iX_J QW/u$kXfGiޝyI. U?URlgyP{|v}Rm ol]F疓k=8bǴA@bo*̍܉')EUl|eVwKC};ud`@HHmWR磝&w<=~T&-㸕V@g?%D\CMzc[ˍ V:Z$azW`{&V8ɗ$oEaCG!ĎFA'o쩀B(YGkeOՈ㷢ٜ1pXm-q$:Q 'a^{HVW@bҮ0ЧÅ _g{{PtuYP! ko8|TqiES{͕}c\F4|]蛒P&U;vHᴹtےIWGAAVFʈW {Z:+u,\\?MkZI$:QKH̯Glp&A256w>ֵ/K?,紒 NgK d؍۟߂}9i_h:MMz{!I<{md5Xfr#';# KFr047_UmTr)e*0OCʶdliyn;tzv.5MjH$X@>*FSbp? ?w'\l9c!vi󟫂=!ܽ,k 1P嶺/F#=1T:kC 뿬'mZ h^jr\D,ca<1[\Nދ<3n;#]|տFJ,EA`zsLy8o'J+H5SsIK~-I rHƖٓ18$qK+i[(n?<s4wVÏ+Ë\ofCzA}b;"6|޼a'yk8^꣍ho :HXy7 @8ǿ<լe n 1X[.7@]c//dr2c=qh_췰z sޢkCPpv g'smѐQ gϺcS]x Ziտ&jS8_[.!cpF9Y2%Vmq2xvS$?G+b[o_VvZ2G+`Uq{K^ϔ883 іjq o$ V]"YǀfuA2cg9,a%I'ʢ#REaۦ0Gg!H#4ߕ[!ϋKOޮAUj\ca\ۑC7<Zfv/3?#FN0^9@Ѿnkh )e 02H@^A >H郁QL¨TvUSikյYZ,2= IkӰr3"IIuFA6ll@Q'2G8H[m ʥkhw2 Iqj6`ה Bd K2c5W7[#sa. ps OҪ3vxRjz'T%ogqWd#cYj_!cJ%\(w d &6@ }3XO3G-)|WeueE6&f-!ӳyiܹ;\.+g@ 9d;>r!dm'xq % lElicT}vmj&A@ˮrsD,3e9m˯` 6oޓomqH&Q:x GavG(]'Hd o\{Hɷ1 H0>chuyo} j;2aav~GW##F8ZOeFJr=<_[ [[=֧I6li<SjM4)3o^=Km6Z8Ӳ(ep.M;oQtGT6+"0ea8|6X9}\jwش3I]4D==K(YIimib1vwd}54X.s[I5Mn*R4ˍOp (T2Jث_sKSd/6:ǿW:"[GhL F;.|?@*&mrz31َL4ѾͿ ];+-[-8Gjr@dxU7yz'f`G;P dϳbۆ2\Sl;5ΫU;x@ńwmaK9Tf9rOp8cMРKM꯴ G$J)'#-ߜU xsFἺx!ZIܚ -WM0B X 9Ts^]kI~i)j͂ Q&rmV-+fC$E%Nhy՘a$csVcsj; ΗAqsN"^BݢuR0Q@0ϟ3⑑6s쟓c(),]řnĔ>@:ZIk#R< LV:@uwEΫ'hn"h.ŢF24h42Zdߥ&WS?P7Ah% YmxY]h63O5&&dU~Nsӻ)0nҳfH]# qw{l 㴷\HHR]Hu $.$FNxrǸ;\( ʅ5ijv}"dQ*pֶr%ɚm#({&s~t\M2$b@,j䑴iV!: ;G=dz٥w3_WƼ=z O]t鸢G8u6A GAH8_G)/6k`:vN_o~h_x?:O~+ѵĮ!!!!!!!!!!yM% N)u{,4j+o`u?B^[]42= yTr}=Y[n# 3yfB^N~ld*'MkZ$ntcz۞)RdiDzG!Тv`HOTsXw:=9b2q k{Zݖ[gYx$FPt7=ԍ6Zh @υT|mp*VLX2oDe]EXb$,vkמzV3nC2v64 k!}!-}Uڦ c3oe=P-RDXjQUٖ9V;_)6 uR'kk>ʯJH3Z2X?D -䃽R)-9仐4yhƺ@#h&Y0yl,SqI$AXg89s:I#P 5scunkLycJ7PSA|͸/U<CD1ZLtyٿ;`{}47[}B `ʩO*ɦSZ@؀yjTH}Qӥl>6{v]j .`,o;8UӸ2ŮG7V/OnWٟ3\cupc~W7Q=O2Af r2DTŊ"_TY36cQU g|k^i{)tAppXjd l*U0btQsŠlx&W֛:5+IVLL~ElȜ948Qq VpArS`q+8oč iF݉py,H*fʉSWUX ,uղbșKX;n. EvHb#9RSkGtާG'7%k ¨QfgD]66L#mFƴ&(BJXN1ϥ# ->=`oKƷ$vc+`,8Y_*Rcϓ9,4$i{ To. r y֜fMK<˙qq#I'vwVYBat$Wp..'j[]3ctpe.9ۊOV܍)/$QƧo^bC{;rB-'x9< wK<ڽV P^Voͧ9 -+'vW;$h J{ nf̰7C}Ps\8+4) ;Ye)$sN1-4YqKQGmF@ld7{ xon8WadǸ[ϡZηqQ$n;5[5xt4"g!P<<4e,:Xߐi~CU wokLg%Ohd6,wqj3 "az;+A2܋OZ1IXw"?5P?oԴzxyNdu0bhO$EYdqԍz W L>#mq"޻iXҵ9"-Wp+k@dl\;]Ե-OW쿭) !d,1D+ W 7Q[PKhijI)uwx4F\h3s]Ymyuv瓹LEfk=]v1R9Ǯ1~8lnp~ 8U~ݔ;^JgX9'\ lu3tC.;f폲z~\Zװ}k컮/~;KP fKv7,0뎵JU|{K35ၪ#c,õ8 :,osgcXBbB7 \S!k&ϗbXdc5Hs^א|۝5 [Ǯ,iه ˁ;HWV.DfM zZ!DV:Hk;-T6)RVĀounz=+9hu1ɘ7#wDy9su\og en;݇"˰F? Ecˀ5 Gxk]'"7h[vV3Zд=WNWLRX,y|[EpwK ]ݼ̀Eͨ}_b0$gP2~^լ;N-brI>=#V4@5G^*M""0{lǹUi}ǽM]YHGbH 7|7C{H i&kmo'YJp܇Ww@W񠁘/*_Jߵ}Eifj9<k@,N^\(Y&*NP5Ŧ} YLv0=AL6 +2O8  Ԫw{ ?F6nW',CpV^ĭ!'fCv܎> Q$#㟍Hl:BߏԑwS(Br"}gY- <*=-}OnVgH&0r?*Fڋ楟"Ba'wThM_znhdp#`6` U qݢV>ЉL{g _t` >?cK`e1l̈PPYyuIWuk6\1 ;KMv>zu}/]S:0FO<|Ճ/Ikq*\늸Vm%GX~(T Ǘ ŋ0{Rץ :4 Ib|/1HQ#b{u}9IV Ǎ1UU=tF+M^1= c=;zqnldGaTM ܭub--r<9 ϗwfM^[▴4l% ujLmMkr&et }ǝQ@h>Q3OYʄxs[k#`wgi5.\s`\.WSc`r]kN2M2]gj7x]QˇզVm\k{Wȼu'yoh@liW-6V 769猜TXsgɕMdbqzE֣BMoD a K㎙SB_'YwVƸA݊v0v1J(@I>Y`wr8f1US&l :klۋEv$rG ^PHH ѐדg˧C7Q2h_e@gVurӤ] ڈPeIVKp gWZw_kۍm}ޢ| F&\mjv;9..iAT T`9[}E$9Yo[pUInzԘ&k tK(Rq~Gt%.]^gm> +땍y*9GS^ naA;YG'6۲|lqYI,2G4O49j v-s5Y Udd)>HfBۋ湝s#f2> z OAҪua58`YSg ƭc.$_庿]G,,l_kwPB8T m Q{9G1e&pmp8ȏC*pys4y3,'S,nD7Qu pRG&G8N3ԎRσWz~l55kKZ.=H2iW6I<̲2LAy,7^5qmzګhxbt̨̬X$!8;QnjZ$pqDs;/ %j nۏ ~++%#{7ڶۺpoz K8WGn(u9 kvY IiT# ͼisx{Z?ο]ӿctZ:^O{mq+EEEEEEEEEE^?iNif8O;=pd % $LR]<ۢ.^cx|ڏڦnx#T\ij",LUG< FѲ3K⍂'1uěeFc|*IO ^u 'Aݤnýay9 Xu{KK{(bc?r@ffU GP[0I^L'FMk7w"#Fd.K ,ԭ9U8+D֑\j#j`q}I(SyTە&dWr5m1q!Y<O]h2.T6AV\8̦J5Cvp#OVG9:n+)Q).$M''U^h%7Qh'xt {;'<5IzسkY^؊U}Ԓ\XYq+˕\0Fi1] ;{(K%wG.я-;Z8 3y .6AOc|w BjBQ|.C#A-Ў{3E8o+15^Y{ld۸VZ>(i!eåSm!t@ǍsЌcqEU7ZIgtϻ$bֺdɑ󹱺_-#`A;]VFn9#WHuet1zx05 -5'Th$5]PKe,wf#ݮS>  ZP&w{-斛͡ {qa[X1x|jiH<+G~oW'ܯd7}W,/V9ɄTo {VKYHyγ\㯄 0C AmM\ OIz~wب.3x|VZ 9$ɉp{﵋*TYTr ϠE5tW?gvƗ[R*KFb` vv+y>K;QwuϪM$'degUG\ۿawTcrxz5l~ˤq3pZb`0#yyW1d:[ƫ}C52js%|Qk͘ 6exd߯uo -7y8Z{|O亿z >Y*(f*$~AM$74PgYnV8w$K+&^oB陮YO' lU@BgT h[06lYdkN|iww >߆49l#;?@#,f|RwZG]d($bzvWW#\sf-#Ts3zt~4ilഞTKDp@r  fɜoDmwJӅ.#[b6B<5O1c7&XACOzɒ}7DW4=~66p-ZZ[ \ Wr9P!bpAV Ysu.~|V7_sWzW}RC|\k=TOlKꍗcKuCEGz彎{3_Add!ȻQKd O~+>\w>kiOoetο'G,'SXK[Mp$,9`HΛk3ꌼSry5FXZ QϦietP6i-vڎxt([xmv;X-B C<I;gI*{.g.%IpAWQ܀^/o<;^0Z Ͳ7v߄U\2hٳu!)cm8is-&m+i?hĉ 0#l`d> x>jjدk=Akk[RepUo~kJ {>yZ9͋?=ݦl+4jdY'w#s{r|p|莀Ht>(b7<+A=ԋ+oQlP`@ `a^+O3&\Cc[As];![Lj匝Xt=)k{w*NdƂI:l\܍x$HBF r8느8CQ6W45|8Woc\|l6ݮoU/ͼ_M+_I"m4gc̓M$4YRGx1dˁE{Hn\qLY'seێK43tJN Ϸ8FJHc7$w1:'i+=&GGGט^n콴%,W -t;Q,[:׍+ t1G |'0쌨y#h;942iT('4!(H[. \ H OۃP:&qڃh`qj4vvԑ$.cV g$gu61#wOuo6LsZKmέEDw^ʴuVW7K*YIsw :\VVma6qp!:w6c&3U^Ēӵصf$`6yc hh$I4Wf|q7P@oi0^#[|ķEBf4ybGmj .sCj ]}rWQs˳a*?wV''=nWz&O[78B]E!f4mcV=6׽_;:HlZ4ka4,CmLExR' VdC  Y6we, xu垝31%y,~,ϕ;ɒKc-- 㿠=_q x&Hg'893DpAqi3qG$p0f$@MT]-o}geXahs0=ac6? Y./Nac07) TK׷[Aḧ́k<4t~״'UJ? /_)3>kd{ŷ j[~M[+IKQsn6]iSo$I"2F4 Xdƒ՚^-6UIBzxMnĬ'Ls pp.l3N<WHa yq*GJ֚<sE_׺KG DO&k>r\ȣv, K-n*@Y/>t U!ǚx4B.>h})Z˘U±8m{l̬BpzoPIlZVu;՘Ϝp{ݓ1BmmuʥK]!,A Y+Nqގ>Qہܩ.*X.u(-4ەH{Fd3D笙2gPQkuSA$_J:mݴKNvd# TBX`XSu-:]R8p(ɍm-6xwd0I"FH@:p,wܮ\tO{wu3Q[vVϸ'tlUG5h6ON.>;2&<0dcDU7eE8V?YFp@\r8]ِ|,v$U8h [[٦k{i :!p=5b,Mk[d¹_M<8IhGWb}{UMbJA:|*r!ű%EmGat*Ų\+C!Rm4YnX hbG0,ym_׾IY,n @އi>`Ә#C­.CM6> 1DΎ T}@*14l}8  S+V;&p|ڄm6㝷U\MƗ_gv,c-Zʇ6YF 6p٢3( lYZnȟ&n95\uZZ \([Qs͘>$W0>wUu.唼h6&Hπ]ld$0 ϲb97e4"9iT]cfRAr(S`bE)lLؑEAIH'mIi$˕ T,{Ww9h::CZAܖl0$hUr1kÙsi*TrEIR;Ha$}r7<QXnvZجpkyƣCXv27vNS`~^[SrcXZ9)#o-<]•:tU1KgΡ Dt U|N.N!ڋ;d~6/\ tf1$lj$7P mZvΉ^5X\,hܼW5G"8ط\sߛv?A,P6`/$7f5Szr;OL$%kLwGoogcm ppyUWqxa\y/w1eE.H =r9 !*GUPX[{Vr7'.RhȇY;ՑJ=jP\OnK G_&XZӱY]GOrǑ<`m;wת Fdj0B|׾I~ÑZ`q 8>Îdڀ`d֦7M.zTrt"; uo`qtV^#Gi+ `u[8ufvCcνCaOp>W=p2MXY2bcu*':l qU;7HGnQY1ZBʯ/k=9Ժ\[[z;qY9\Ae |o&gdͱOmʫ1:/k뺝w G%%A@XI^9rی,fڬR#cPnUwگ4W'<2F.v.o;70\bqoV#9˻<#y_IC76lp-:AeSu@؍lj,=ƛpۡD0 Ց.i_s|,1fF82or<1BT$gO?#g6Li,ll'}xMM{5I"c?uկIU? /.B`6>7 nM/sK#QnS >F04?H!h C ݌5=uM6ȗy IN({^+[.Uvlm^c<9xk$G~ǝ+wɬ'I#N4inEĐ  [ZOPXfmi?dCAW+YcT6k!m.vh{?51xnay*˔׸oJzoɩ#GEz4OR#miڭTXGQ׸`eUc851ۑ ;Ko< ijn6,AY{ NlqdM( h=; RI`qTOoq$P4*d.1tGN&ea1][A}fEVx mkkzw9ĈUH'ǐ8#{p~|Fpsa-C+읉&%(Y͑bB}ۻONZ?2a=gRݑ$D8õrZ *|iktב! {H}Np^&#noeB<<q:+.YC|ag4P;{`$s{Tug4!ք$gOEu2[ }GB}ƗW"˝"7HH/-NHVhqDr41E_\a ~mSA!I2H9Z.YH: Hp,R@ZGcl r}Kn1K mM*AH!y?oW_]_ܿ?wp3NGZ Ir~#^%tH lVty{W2>;U,őG%4u.thH P40;q5#syMV7Wċ|a>ochzYK.z_" ~XɣM9X%p#d3Δpqʽ۝$7<s vxlmeQŻ|(6>~1Utw טs> Zƛv^9 MBǻ<&&; ܓbI*;FDeN[[C Vɬ-hoA:nY_,chB?gY\к'1۱P凱S$y/<:K%jŋ5u8b7Gq/`k\d9~$l۽ǥW =Bdt yYYX}=n[ u48_px#ؤDBqsb]3H6}To#UyULy[⌅>*0oѱUȚ'4+$Wz% #H0_uJx@^YVp#cƧdF.' =4bV9Q"dyA18UNeRZx+>i8ߵ)139"vI 1I,MqhόDSۑIJoh=Ã]V`IF6Ӹi jWC@x޶K\:L(VC:8ɶlC;F0yԾ.~ gt̮ nܸWI݅@syMw+΂N et൴Iݦ0^r_Yc9U<*>UBrFcdݒFEX5 C\|ebεG1ܓjդQ4ۅPcC>_ 븸yMuw@u|Ζ x EVT$L4nW$?/,vzK/[(?H%BħriΑy^'El :X?dUact픙`f.@VcO[hǖpnGrmB(d/ȝ \n!ˋ#*ᲁk|;Tעgf6véfdcVq~21'SGjOg->$$)^*\HS=f UW֑"C~de{X6O~K`{XnOj;ngS=<:l ȋ'%~Ⱥ7byD/FLqrIm I$c,”q!|Ć6vJY[YrųxOqGw{Rf^GI6g=TOD+ vk(Zl,/RJKKf5-Dz9U\/\ V3ۡlw^\6D赧q-i/OUh %As i,PG1Nmhխ~ [,Th9r{Ȧ䮃'ce\&5셃/U'4vնH!B?*ɍZ=2 :tɭ֐A";};_ɨB#hFÌ쌆8gnxK6#Ոd[]$}@bԵ Mg7dq^\j飘xL-HY&sHuE}1!Fut ^"_ ZwwY,<}7ֆԺ-󆶒n2y ΍:u;~Itbɝ7$nZwq=ι̢൒6"2vc5"y#'SPsHu}Z8 UC-Erv2*F3嫁.,&3-@rڽpףU]jَGX77]B`ײjc^pOúѹ{^-cG0y$c:*Ny1]#cfܹz~*uesѰC֮'e쑶6~X9Vhht?:^鶦ӵVܦn%eBв%,zuLN~.+q,!7}kQY0v1GP3X7֏OΗF21϶[K}/pOok[pG l@.wbvAF;8=A,!|kMqC)søb_wؘd+:fEK@{ry"F{F:sJp6=s]iAD$%;l}os[ki1o ϘFHkl[N7.tMV;H$ ;XHZF.lm9>}*y^ZA6p2ŎvH#4ꍃ~p(X`pA"C8ˆK2S{S\ʓH!B\t<gs嚀YYz_=?%dxW@- 6ֺ0EK+DABX#ecɤ4Bh4bbE>Q,+}WV\-ݾ;U ,|ƹb3&ܛ#fo}}D,@t?xk6Y^^,(l1EZq@qC˸덴)it/oTzěe~gZx >mVss%7K2рݚkȖ+Y/Ŋ^b Hvk(}I/%vФHʝ;`:Ny?e17>(4id۲zW2Yr]G8r>cSIW1)~9vCbz noPsO[j2ȲLQ``F\dúkßS>`ڳ~5AԒRadXZ.ؒ_]|1k&t$o~Q6|ޏX! ant- od =c6tKo("nwbI+4G)z^,+-s%Qn5$qv0L[I#wt•{̱d{#q@m}O8X\%tnj@'6a8`GorzKK`mnkk:6hWuUıۭQ&H*~383ߊ7V]9s vM^H~ %WM+: ܙK ]bv,cە@H<@\l恦NmKagM_6P2sϼԣWsƑ>RޙӱH ![o}(ZGq7 Ȏxu$GU<`ݍwU[\PNcBH~aetE-٩z nsSj#'hQGom,2qAx#bx#f>qU#l[hz=ąݙvtZAPwH^fA̐&.oPTH$>j@Ч"ݍJG=y sM!]?]UstͰt{{&s)"/e&>0ZtlAq#铰efTyAVw>V@ŌLuj4f#'VA(~)KsI\aRmMH̫ːFP;zvR/,V7TDŕ N\QG&0Z9c7BEPi.PA`u_VV YHo+mE.ɤ$ ʒ8+ >۬ʕFjWy1Sd _P$BhuӬiwӐj&\ۥĄ91#MtFՈ:(*/{HܖR?U?֩-AH!y?oW_]_ܿ?wp3NGZ Ir~#^%tH m?fu:g~S/F|䯛2Z;H@bqWZCFuhY PCP5 I}UiY_#wUȤJhtH\4yΪ ^ފF`H'fAእ+#gdG񝓔,kChPUDovBɱ ǖXUvCtF x j"I A~[$߲I009Ƙ/6l4S拰NPWzNW5so8%&;ȒCCFw{qkld[Lf3fG5Kns\€Iӭcp^H6|n>rp謉1)u< DM{U{(ƛkxvL% =a{o%Q+v@n{*e%v0Φ{ۯY'F8.̝x9X6()v+B|=0&#'^d@p{ڄQXǽ̹3k԰Ge1oZu >6h|wϙB V}&g492 qPFuF1}zY5 'v3a1.U[*cɫ%n8Sd;vN aVNy5x%Sty6[ѡƕ15i;}_Vkr\Kek$VFUk)ϺY:(۩܃.&m-,tٴʖ9^fkb6x6csƒGMʪh ӆԮliC|ˆm䆝O!ʩQH[0k@U:eFyn8@- ۲cKWV.W9bw;i[ W.tH]D 7ZJ~X'ϕeO1 (ɉ7I{K߿ukquVP 'TZ6w_& rP̒=y[ iG"hK0qͻí:}S9Y쨈 e Q:McHkTJ11>Nt:Y 5\l+%g7)ePOW}oP>8[OKHJ! ;{R֬!I\Fb `=yyL{E7Nā*v7G##wNun<ֳ|I-lw 1Uf&WER峃sC, $i#()= SyYK65sw%dq=1(E&CK0tM7@NcMl;54҆G#"W[_~lنxgptLi r7^j6Am AZُ tʗLFb 5 +HU[đ8@펤f{R8βOɮ{飉n #v'P?£xTҒ w^4lުR]D[U.YKBI&{ֳz|̺~D[\;6{}bP$9(Aj?R?WsdwCNj'8p=v[z۵Ki9欗xjGZ"qP7Ӕ$b0S'V?sk#Y_;v#e.}h crp˹< 4i5ϵK ^YkێNwDAQW®B3Ү6;lisYh Yg`ۋ6m725a"&eQV0gH&^tOQluniUskNG$ʲ2p\ -Wc'-jRklC[1t?ot):v?Mmj>nkX oƶ~\nlCK xll@wص(8 giIU|k"f~{(F9;]ꯘ1l%lߴAG|)088VqB`BR bDaIc!%]*0< l=BFט%.Z6JHl;hg[PrwrQU{R74׈ƖP-$:{:4epG,*3LuC"$)7px{]WLV0(|M6@m-u U YbR>9y5 QD4Y1 "&UlL٦\"ccőI *C&U=sg2NldXco$Obv.ז)ͽ;(2<=->oޫ㝫s{%V, 6 M## $9RQ"#S[e?ŪP^CL&]ő $(pʦ ;hJH*pQUuH)vՁbnoCsd ]^糲D,>xW_n=Pcmt츲!jK:Z; ܎lAYn$ j/6[@. %KrMX^%YNiLCZ꽍E:=hk6e~S,ucmvE.I,q_=jkn4[T8ӈ3%k&&'}J{w^ fW'9<>3qvvbBrYduۢ×@ l)%dB<9 Pd&U cz0#3 y~)eDFhH!PNk;≃XQk7>'Kqr{ r{,5s~#S=39`m9i2 <HRz][U>,s#$;*Y-slv3 84^N'cܴCOYXn/"1ϗt!y嚎iFls}:QXs#fI-a67p d@ ?U;,nwIۦ*rko,ޟ6fnۅeẗ2XL'f8.CA€m&YGCHV cP X ytN+{֓H7xO ΓC0 up2)\" '}td@&Kc7 g˕4I_&.`s}[oʯPfco,1;QsXlnl:'J!&纊΢ߜ=NuQ}7r*T;-X9;Fsӭ3HթY6M.^7W9X cpxnQwQK0v0FW2e9/xl>6 6q<5qQ! mW֓(sb<rRnfkdlĬ@ +k@qʖ9|CI$4hzYi\266^x&C<4d?h 7S"m>p%9=* j;svVK~Q}d~(󪅚 SmHcitm=iH^dAUE']&6DM9繞vkEy9K\FȽR70=*2#6w ϡōSV\\!<  r1vyMULc~ #5pO*~Υ#Z$غ>Vr;tNFht9@4]n5@;o2JcK$AJ\D=yx7\..*>LdAy o;4rV.oܩ0ݓ[L`5vZՁѠ4]/}JIK#xY숻Y/5@ 6 ª/-cV8C>Uii$)$=/`+T9$i.- se'<2jkCP9BN49ᰣ@:Qj,d5 ӹc: GSHQIYAI;mFqnCǕCZH;RҴI f~ɔ7ؑM8Ullv~E#Oe-wKMh;k'$_UF c_wOqzF춻j UGUCU0Tн{uٝ8je{djns ѫ&;)c T>ldꪺ< qzI۴HPӆxOY}O.9j1zߺ5kT6g*@ dg#Ru-̟%:x8qkt6NIvvɼc5 :04i:$}煩43DF[8@H1h2i1qpS͝vUv LDF|h-CF&6RF6e?E<"b`K)$=r.Fzs {6ށܤY]Is3$a_Xk6Bd8 fT3[\w'*tžNGq.q `dǀjit7Vy|[[R Rsh2v噑$wsvjH427N^Z.'z=U4{v_8>Uuh ZXASn)k\|l|LJ'T];)vJB 08ZHܪɎLaڴ۪tyGvʐy ho`8/N`IW‘{0IU0N,yUm0.^@ x!kV1sw}d4$ 񫒐ZHஸT%DKHm009鞵@pܩs߀b?u-an=aǨͪH)º r#P&a˕u6juE+39cB` kWzS1>0yq=hWIÆR7EUW`#be RQ11q[G$e^Dn`hA9@P*آ[9z %hW([np/:[BM&CE|׿ش^fY!vf$9zWD8'k+,s5[ ޶Tj2\XI-$l&Bm]m&GWѻ`VdS~[E`LNwPs5~JH0#Ө:Z8v=ySKb&hp.HAcu$#e;½7Oٟ*KCY Yٻ괋[Is{Ydftyyg5l";S3 ~rz{\,oޔ*QV}F5 wCoro?ě g>F.t8Ncŗf-]B=dz= ULtb"|M:R IWR\G(#l^:BLMyJ֊- 4K6涫[5%F ı(3WVe56P\h<揣}w#&%i,eSiٶ3gwNIJO|S"gi6\oW=#=KxdeRxV\I;lblMi` r7np=Eist=Wz\ߍ::kI{;B13C>m7Rծ-`cW)ʸrsOJqnV͏&J4P6=7}T.w9XjAf 刔J}=ܱʫEevtϖR=@mH*y:(@_y\4[i> -s'sN4b3#,Vtܧt%;˪W5ũVs\,6Ϲ$t!R/9[-K=r&q3Ĥ)1)$֏hw9`xlgodCaI>lA\x= Ik5# BƧ ˓FD) i׿?rj{mid;R29q9A]IWEKNѓ09򦛣cb ^ z$f)}BNI9ԄܩYJch݇ogU-;vxc; 3uңN'Sœ]`߱>@ +=zt\N9 kASll8Wpڜw{Vd%J`dV#c1=Gɬٞ聝gA]ƑXB ';{4;6BID_$z{r49]7۝rU$ʠթV&8‚ -hZ:F|}TlmQq0q@a;XXH4kv_Z| պQʽ 4h {PhM+vVڣݹ$} Y~P%;A=zrik^_%eUs*fF\8vYYydÏ(?7*ƥ2]RB"!V6]G 20E5`-j뷺AZU )P(J=hHCdd7['Hp/?u4Ky+Ya'`f$S&]ɚL LfA6bBU\G:369 $kݧLrlQ4I%{WuJ?Єc!Ѕ*;>[s6ˬnvDX5ZfL Ji!kj l'G$kC _`xYGW=h/QWbx ۴I e1Ob($FKH;0h'l O-œCOn^^C8S$;WvNnSQS[}(cu䐿I "_ IR fT=vSfkF|6{g`}++W*T)3XsN{'鍻rѥմ}*Yp7uXkZ\)WtzH|rp[%4l/Ys}< &;R32ֈ  0c{w۽MK,#,=kuX-".t(Gn.$ߕJ-kMyNMi׺aDz|Ų5Omi%Jdm9fu*8l;pʏ9- Mv;{/^VͲWA;)GR81 <(3iZGpB?t5a-:c-C}VԵ !q^ن{aJ lj9Cz>m~z srL%e&nA'Rد8tշX,+>4B}:elHݠ&~[{f& 5Hbw/I&i484X4IZ;rUlcD}AzX|2Gr:]߄[Wq ÿ?iWǓ6,I6b+p6w[.k8m5;W9&>Xۋ}TKV!<6F7{IXoWfFĄ`d[isj=*%h{'~Rns{7:X?cǭs@nT/N#gjM1āa_Ac ۋ|]> KC{f J'T+: ܝ.X4ز=^fAK}+N8+l)xtnqWpAM0U/LՆ 1@ޞq`.>{]kWeXE<ӣ@֍x٠ ~Kخr J$4 $ agS#5 >ח4$?J7N>;]i6f\|lrM Bq`z 3<{Ez2WC;}j%wp 8br*ēy_2ϰփVhґ%o2 uґ [m1K3ds>w7%^20psR/O!:FB@MobQ5ʈ<x }6lI@{rj]{w_o:./)вIU#w.Rg!119Y:FLQ ~ s~4K[,dzWIZw 0[]F 'N ډNckHwᲤǀɪSwltzJ7Dc>U2 dA$Mlqr+О1a4k]\#$NWZn`M$I C, ^4 AtzVr%{C"Cj i|WmcQ5‹1G;d lNӪ.S|^}j)@٤q].P.%gqs𮉛0Rd1[՛YVr\,R$ON]&/&Q"Y:qX;id_eckkqy[[I,P{!'`}R99!kb{Oe~$O9 ^Nŋ vߛA@cS+`Sϥh6=-pQ͞fmβ~߁E $+xsŶ8Eѫ޸~pV*{Yt=|W;N\^G+X)ҍO=H~mmѱ%#6Ϟo=n& :!N&׻́@UDprD(f9Č4A MPLrKٴYcZ+]; ܈:fFoT-sVk ImUKQ,T2 (Pz`rU'|fwnl\̟0P D0"f+H:WmfXw8tw=~c¶-sFp5gVFD{yo‚\ X*>+(P]GI3FmdӋ H; k-YBYls'r1ߊcaZ,sJyQF0Isܻ ^Ortq˒Uߎ=?51fË:ݹWfI>zDmy{'򇻦)ǕMv,2Ptk 渣}LNjs A*Sr;KB~h"]+q#΍&IGF؈ܻ8 W:B* br2Oy<;|% H\QTHq,k vۢK"<ΘܠJ Z1s_Kr5wYXNG,o>8i [_+"L;0 ןzPX7:e ={|7 <L5*m|zKmgSt̰bw^}f,G'O=ظxѽ31!b WO*[֢x.ž%PZK99$w'ƭA&6Gb?ֺ8x'6.qFeǗѶ׌m2wU:|GAf^/'ˮqtĴAFA[FƁ6Zk5v[w4#8W8HjWnԶ:3q'Fu /?LP <ت-]=z 2x+FJt錊:8KOy\RfA8jqhv;8a[{VFRd:%dT(:/pqtba1se<=8 4= o*KaRҩ3lsM֝KmԫmMMhgM MpOks$j^ca򷕚M zo-aQhm|  sru +rT=H5A#յ^Bi_(:G^}̒bd &yvowNUvenGk4 c$~N~KT=j@_flDҏÒYm:J/ zۘ$v jfVݏe>69-m^cO (ri%+HcmT2#js[kkތH"%nghCdg=4sǜ|y6w[QyF3ʳ +Mr3I?5FI 4Aؕzy0Ȫ;4&,F(G{NctFu"m MY +Fqiŀl?x%4A`$бw ێ:ݪeP7ž+m57ڑk S;f-!ƇyEqlh d ޼_d%m{)Otn9gi ۧX{' s1^/={VɞOTH$GSBTGv)PPR9L$+9K@D7ǵsIJ4w;)@ %^hum+gM2%4W 03t@ v޽R/鷺xo'tqR~L/2Bw ;Gv%ˎc;TL0T ncQhmg/GX.GJIKwWK<9a5d,o,}Iyz:;'bN@CZz5@EDa,v Sbxp$첧RgAS4[/^w_޸*HH=юWI# AlmO}UV}64+].?Zě-Z#(ؼ+Ls ɝu2%޻SmBϺI⻭~3?BO.u.l3lx[SF qܛD!rVj2Līzѵ7s,}hD#Cw!k7 ?wZAAWV"р;pvf-:>e)m;Q-.+ZHCdMç>}UOsA `C ɖ\y9%suVճD1zM ܨ>(iA{_iV32_!V1tWکt2" uv,QqF `I3i$*>F7jDFI9u|7짰kճ;rOU%vO274pm?RE y SZv/̎<~λ5{J=~'UBl#ʢ^ qj5/5D/g3ti4S5p8Y ˶BXu7@pW0F sE®X&=.zg=j~bYV}%ӁFn M6Ivң┩0nc x\.\NC #ep6F^2aIUy]̌>Z9"^ sL5ئؓCLzCդxmB;59?"W}_erA#@G]8Z.$E Hq&UTlspvC4Q"Gٝ(9M 1RdY!Ŗt v&Xt-Y,`4@Y驮}; ]Y$ǕDWeYAq"uH:&^Ƹ޻%n**;8^C8ωx1=&at;[,a?B)\=,c8w;}R8+]^D]AJs'bs1 Z|q, ۅ8gѽVyO% XN2\{5ƨ4ɶ?]K1=\2갤Y[%˔yb g${Q<eY1I&o{ZI̱nbSCTˈIk=Du MW6u~k }qQFzQ8}wE <kcaº*F]c֘#pɓ4-y7gߕir~Gxn#F3sH?G;&cl}u|/29+~e9ֳ<Dq(s]CբꌍGc^ _wW{A*S>S[3W&C/z<-4/ȪOe2 >{kAh%C g,Ԛpf i|dl/WGxU:h}eݒ|Sc]B1΅1п6Oc๷dwŶjQ(-5P2qn~^םQ48~=캷)IP w{[O ;+u3JKYofF9H v_h$ / ^_ [Ó*Oe˰EiQ Dyڂ,8KR7߶-nND/ 5ŮCìj=←Kx'=oeR^Wq{qPŇ$;jndC_ZFע|Qk\]Bi ' kn[%YM`Dg銶lJH-%ecc4Ghi;tݗ'L]y 8Q77 mil8B"/ ȐBAn8Y6/ 1ͽbx91K# 4os *4scn~lƥV^A%'O)Y !Tn!K|o<\ְvU\oC5]5uپ+oGmM%򗵘8?LWMOa}6qĂvhu; Mb6qL96UդVݤ5e^dHsEK`.1ZmZsf̟y1qZ# I*:V 84G*Pyw)7s=nfdfSn>U Ѳ̝#[꛶wfID>Otg*l)6@Y)}S*mgvv'eF`mGG"YqFN$es`t:i=*qGYI|m۞YQvGO'F h:2LzZGS= k0}';ANJ]/ii ׻DZZڊ+J9ѭ3C:j77̮ .}3.qwž"P,w&Q?,sB&qzXtN+M>{vPccb\d1zy'K_F<Ϣz=²> lr=i rkT|C}w$ZG!Z@0Hϕk+Xn򙻱` h;|j$ VAPXۦ xKpLQ@IC"v9 tʋus^Yq# nۿ4oM*+Ik0"$LO"n3hD6Ch,x&*TİCj!&oPr ]04Eu{smr;`$2|+c %GVPtl'D߻…_q`~XM V"I6Njh=d^%.E4xBzYGsR5XەV7;%]VPdBmL@ ߔIRH;=v59M#ii o R\LfFǻ,C[ Z\wE<6avۜ)EZ,CsT/09 3¨+}KM(h/1ﲽ kn!"䯓>QuN0dž2{nzX4\~zݫ<"\y ebEAs Aphl <hS挦ZNc4:8[FY>}{5hꦰlEԾ]ml{L IJ<~^;z@@;{Z+_ńb=v d^.M -=]fkܫ50Q:lb_TBxQey<ǸlmcI n,]F$wE`B~sN{?dZd`R"2di47O{8rͥS7 ;ԏ_d veAQ)&`D,C#Iab6'$6䫟wJZjj,J>0)\- JWCi# nبF nQ٣/SԔ;B#K?;HtU*-ch3xTlnNǕEϖX~t}M.t)ri;NʤKF>0?VLC1j' M$x[%5]SUBr]$JY#x 8mbM v$8ݏ w͔-*CzvYo<:,]\sW~ⶇME1e/irId%G;N2:\XQ)IlfI$ ǹ;Rd"ݕBUNW֬>HsMb`}Xe4F+c/y4!QZ\nW_v[Z$69bȑ"B;?\țTިnNsVx99]oGlms+l畝㹽;a6J:SRK#sNf9%x%X@n[5BI4 Hm<^=6H(,@ǙlmFɽ80ir{t8! vH746\liGƸzmqpVR2>E˗*6/SWĽ9w,PkeƶedpP{f>uBq.svZ8ٮ&cӤ #Hk|@].[{Ub DW8Ǎ85xnoP8Cՠ껟Tav Z@8[%n Kl*g² jjsc"M iϷqߋx_Z\^fdKI5] 7^_8>r*3֞>^F5D\JM:5g&KjكI9-9op;#8RH"I&qCԦ/'GuϤe\YvP0L|+q Je*)(mUzb1"}u^)"eGy*T~ 2H4NsS3Bs[E; a(=q$+cd18F5~u&j2Ow=pN74@LP3@%ȅW-{EUcvr5dW+94^ިzd'ž,|! '9ƚ-?%%7@7e!+ øxF ;z\'NjXKU=vĎi#s΀u5x#dvp=>yEG,3%t<tkKh'afP"{6 P9T>ϵ"A@@.?b׍ϋƚ7}6k3ުɸ;>Pxnk63dn^eF{J3ΌNYߐyg4E#cv5I:FvvCdis$vzQ.!m^۵fqP13\Noǀ&&]@܊G*'f`Ih?=2B&X5wCWz|N2z`i,l Snek+80qM2m 8 %bmn<#t.pרhnt]&]A"a۳a/dͳR3c&o~~F3g5V YF;NjpdEXZTNRxώ)$#zS`g^>f\;ptuSԌ<5 Ees ;G>}uZ({ ]D]rG%Ұ.<-+9 ݥO+bѸjoH@y0*0u^ݖk{:ơec'MsvyY[gRۜ`}5M7kthcCs1'i{> u%F[Z cH$h piAX/Vt{8y.W# =av:rkHίr@ѥ/m`Nw.Q2m5" zdskʁxn;}zwލknYaT]7. Y\eX5l?m;(1M74lVAx\P&]Ep clm}KcdZ4i+r8Wngq\嗟$p&C%#bzk:3Jp=<Ȁj)C3;A* 9Z"Σtk} ;CF3V$XH7}= 7ϯxQ'AyT<7 2_!ƀ  jKojQD%0[8Ǒ`h*=sFÖ%bb´Lhq3ŒrpY }sƋƯ,F'YG<\7H+(i{^ԤHteԴ6W,c'IQ<Apִ~YlFrzr+hiy wn{&v'eqcn;sMu3L~^kzibK/e6#M~h sXcUn &9)^N7ma2;ԥZ,CMl/0 +֤U=ABqFJ8cO).u( GkH=)%%El [yU2gӗ.Umڍj;1m܃)Z QX Ch!̱H%HMn{wҿO 7WގnT19 8Ӝ\Uxa~D1NK*Zʑ?f=s Ap'#%\vGHv4T03 [AK58խcxm˱$m[u5@y?L 6@=׿)(Bohsg45f#G\JBBBBBBBBBBYaQr?ڪ ثR 啡H5zg HBF Z&AzKG v'}5!^jk;3/0?푂7ǐ@& PդU+}SǕY|RghsAtwm)e2?by>\RY6ugO_;XI=[ HEHʲnTLΐ)&}]$GI0=Ẫ'Nqm(Hne|1 -,n켧 pHo\3ckp|dc^ ͅiD9ݶ> vϋ43,,S9".]z1\6م$1!!=s_ hT YwbBNOgC$SOGqﺝ%0YG &;m,*:6Z8G&Lťp`  T[p奍 d7nj|v tP 87{O^mc,{@LlOv2ZJرԮ2k2~;aD(;pTE5&FȨ@qV/N5t{R(}G'i㐠k=`li?Uqat/nDRBҙW Un/kg]oaB wHHy`8` 3[E$QK cdBn9[ēFA^*׼ZXTY H7߸.DlT[mAэGyҹїE`Lٖ؉,u P_IsẈ)qs(#6d ӆ;+a-ӗM  1᤟&xOs-o\nV3OsfPgh6$+&).6=/uHlm*K'i5Y-* .4<_m27'<x~ XK3bw\xOմm6($H'sU z9X^)t_t̶cJowOq}E[;ɴǨRS-b^%9gN|' Zڏ[,m֝Z˛D&&Χ##HY70y~w?|O.4<*n Ivq}+yS(YQ0ęjھ5}bT-2M_@)#2+} aG|Bh.TQFzFk7Pn}rht|)\sG{AړȨ1GB*vh6g3mQ8e;X9i^D:7gcrW$pF 4v!01.&L8eI#Xܐ5s3C) )xPAQySCKT ЀY%;6Z.i,nc1E{ C4Gs3>s{2EPn,W #t8Wg6X](qojV'j.Ib6,$Cq~x|sw Xq&оhNS3۵̐+vlU3q淋;wA!IZlmx>bKkx$p!}}qҚX9JܧL`n6߄kiiۘՕ& F}Fz`8&&ulV`eKX >y0#Pi>W;r}ic"$Usr ?rnd1i7f〻^iZhаP뇞Iduqq:CqߓANm;8-MlI:aclY=Z j]oPxQe#ͰkGniy߼W Y 4zύz4Rg#˥}qVw=6V5cq>`%mI?g;>nR z[(M}j7n4ok4ӵ^-JRG_1\̱$`݇Q};?\ƖGinOX_b<  <lC@U^YIN|#F#I{WuEFӘr~ Zӄ I۫Ϙhh3菆迈",1D²w-{^$OVFL3BZ[`ַ%c9YfFXEbG"^l/]pkQ$8R OR|iE(,tI ~u]V8 ;Q "Æ. [c)eL7Q׾S?#p"=ޕm,d(̬GdH#*mGl / F ;kmߛH8JKs-!|_}>,=ŝȖ!p:l57>"*E%!*$iY]ǟAMq MO g6b2M*wM0V=ZIH+2(bo; ؃0Oy18w Q#}>IřB#94=<@Uڇ7͢in`b1\w po>X/!Su MyN7y綉dWWc#PkZT LGj,UgkpߖDhؙ9!w51pQ˛/ kQcZ;@ܺvS $e 9 Z>p|KtjCUVtzj{t'8p@ 8 gj*s({{4!@",W^dqwʏ !6wK&Pf-FÓ>#a#\ZyzIݛnT O+I&6C˲^`nwuؒ}$'s`~I?;̉)i`/^(lTHMv_Ltk}!֭DtM Iw0dY spFΡt$cĴ/,U]F=z]<mYKhŘ~V_ddq>1Ɂw5ճsO.{,!kAhkF}(V'/.bcY/hu* n+adP|@Aw܋zq.)P8 ǐv~ٱ'nhkK8︾Ê, c5U ˄1㼇Shv:AI&Bsi1!1HݾMuK+= lkwU(']7u0ȗiiulgM-=ea?9Vӹ2`/&6^O8{K 669Oc?Ipdw4zE7'4&u/#' Q>S3ʶmc-X5ArG {7ڵo{lqz-1DN:m-{l|YnCwwwlgݓe~5+uݟ/Ud;w[7 )  *E1oشF3ȑae5ڡ=F`Σo%p4G#k@\8 wHv(?h8$y6j{DB"ccccM- U+#*2![8 6W0MGpwM-pMrIݝ+\gY uܧ^4Q' kY(dѸm ʹ?^)2:I CD]\_]6"a5`{]mHs!ˉ=3\]um&8+9Skn:fY75rL[ YA}:IumSɨ'I>ꍰT~lsHvAU_s=8B[$葠wS,pp~ +H &uy: ps~NI4c'n/( TZA8Xʌ2I#t8!I䈛ogp|dԺuW'uEf\Q!YꞫv- A. T v$IDi?ÿ56TV{9\aflaHyJ4}9qDQIajI$\'WU# (-ΓOU x #3xV QClVMpX1jL淊]P̗;4^n@r{\ƖC9pFy#˥YeϘaf7yi66km&.Fp>T9F͑avIt$U2mx)!I@UwIf -~7L/Vv8rvs;;YjOmqnd  N yz9Oت#6eGRQ9rF* 균8NˆGPi6Au_K$ ``]( YHZE}GU'ϵn܍n1̟5gR/nOk2Ve%O*CzS;(=4;n}TB*evR/\dN}9=ơe?c&&&S_r2xQJ **S@VtbI 9Ҕ wQaqD2E5ŮZ#K&mpb =Z@ά+1f[.8'YM(AFc,h:\ԞP}&yce9aohAG>kkFOvDdPI#Dd$i[8:8j (1l/.s"jr}q}%Sh46\*gO)4(UM{p =Q#tI P\'ͥ|ܚY(RFg"G)ݹQDb{u؟JVwrɥ4Oe\Lsੌ`)6+:ڽ~+ʕ/ mHM}Z(icd* /gEnS *ÝX'erڲ m]5?r+o+qвzǏ-ZF[74if\Qcx|-9flDN;j/m}k5]*lcx'$V#(qk'xmurO"Vĝה/! @7 zKH#eE LgƜ=e{%70>ɢ`uXXW0&1{ Zk шuBN>O\DOcھ=Wbљ6欄ujQӧx-&VdEPF\\'1ۚ&/,߲޴LVs]n޸ert6J&u߈.7;7jM_dEYzg=>Ec]G Dŏ_Ϳ|·!.o8GAciE[U:)Xt <7Owaٴ2P+ymT{H$YΪMҕk*.Ls6xM99KEm#D9l2s%!n7Z#>.>Kd̋Ō]om;CݱȌxTUjFOJTǸj4Q$Ѷ ;6Z/@QʮnĖ-y3 r>}KtZɼog qe_o6A˞Xxmkuz<p'[C4m§O_eesv2κ&=`.G4 Ǿ,kM4igD4J9]4A㷢"">Sd\X؝#Nk%[>? O}ђrr\$߭H$ b{ʂNzTR "ݱ{qoh=^e@nX$T ds$;+~y3Q8,+uzjW]Mz_7}4>x#nuN-MFH`B;cMXO,Ӽ}oˮJȒ(i 1#P M_,1 NkE{ TgY@L-gˑ>\9=pŎ^T;Xa؅ܮxT1Ē}U`|ftY~oV&kȧlɁoCR ,- |{t01:8I[N2d+`欽h'U0rN.S&k; ؎xx>#%s`:XQzd)##S[4F{=Vļ=ia%CYp˴}]ĚG04Wcr ^&m ?Agq;( ;F5~C\G6VSY'uj|wu2+/ f[W JGZ!qkҺnFJ~6loIաH W nyFy]|vr^H:75w[sW읎8@M,6@x8/ Xl@O9ݩtYvѡIneYd! <yW6:iq٭3OH&Iw.ڬmͮt *0kT+_?dc`-ajUuV|_eX8N 4ᰄZ>}H.ݿa[=';'M?m[ q\H( #wUi )sHd/b}Y$mծaO:g2q]*rZ=|Xǒv4ܩ.5佚Ha\qU2rw*Re1n76ͭș6drpΣj!b_wt Ɋ6xT%ğ05 C xwd^\uqq>ak`m!t*Sur%b?"FwWg9y?7!o^sAad~$0' pv?\jAs"ܦv`1S@28all7=={)mT\&Wj*z|GmdJ9n4Ub%" 4<<sد;jeN9SͯlPQ{eb̳FЁʢ^iRґ32tCpuO\TgiZ]Ok#V*m#L8=qTׁM?29!1!-<ګdW&<Ո p7+_ }X~)v18$߉_H Uڱmn拷oLuUf iвNV3pj\\@` wh|\n3UvoA?hӪ>j}ȱV[$2 VɌ?J \HVS< bh(kq"$$pczpv pl1!^Ƹ޽ӆ,$HQ`ui[56f4 o҉ G"Ep8ݣ(d %^t?$`ZN:rH$F0c-I)tdecf272@tBۮ w 0u6Ji8巍pQ#QwvJkc-ykC>DH\4_`f[KyB%6  d 46a %!In#˗:RD@ɌA9VFƹ'}ܨ# 0p:GkHoI1Ic"e +ydc,JVNgd@z'HN(4wT9>nhs)GOsuo0w) wiskJ4NHWxL[67#89u+\v%v_B.{nv$sҹn.]$no +TբPldC\avjY=G CnZny/5?Ԯ1 c H*AM)2]^s"O$v[ :KVV$I=RiK윣6ڸ!B Kf>rn%8ʄ_i\A?ra1 sY[B>3oh,]9$NCI7RO ۍnIQD EoE* H}81cGѱJҙ2&h?BŭimwYܼ>W?8g +=ׇˍ2BiNI wF%xV&RC4U P4^ 쵺FbVad vhvOeax\è5jaY;/eXw=AHXQ,4rZth%mQJ #7ɫy^e"IɐeOOsu Sc(4:c oXR"cS@g4ְ4wSM$6h`W|Jf겼`߲p5m\X˃2K1{޻^"mꠉ /3c9nr#4iF +bK1r@JmzkoU)!&zF|Wj2͊zoEJ^ym^G94M*=bḊ'e>x4nq^k7pvnD|SQaLPL@u q ut82hpZxnBZsp텉j۶# ڵs/KZ7@mv@0 fi籊Tѻ@.J#Ls,|+Qd0u,"Oc&*f |%OEv3hcnsw<)cO~kKjCEU]⻦zMa&AE EwJcLRs[$y1oaw_Z@Q,P-4UYcLeGy5 qcw7-Fܘdk<0)&{[F{@bYk_ڜ4?U2<30[M4cd =Q`ko.<㝀jEGō̑(Urj[R|͙)^nϠ)EB@Moef xe ,w޽-bUfX9Nڔ쭷7屳<4:+D3Z3ݳ6 j1Q9Pk48 h$UUIzv֋Ͳi ld`ݍϳqIR % @Sb 9{cm8-7f`O~ɘqhUB6ұ 4ݥQmͷjե423"鸀7~s d\9G;]9ŅE)cթIym;}gq\F6I ~FYm)i:Ųչd)Y{5GZkl/p:] kTit#c* <H W֟:p4Lӱߘ4@CsX02o2FV<%pA'LҸb4)Լr0l3 |l|ruޱ-$_FR}#jr^U TH.Wە~.o\8<(wòٸCHKSok{T"X8v0yH Ń1w*(!y5l <ѭۛh`/Z$-$؅WÖhV8*C;D635cضN{F6iӵD1&cI[u{R]C4Z'}(7 ckԱUlyXMAlփpv:3OJ˔7az$$wK|ޑ迕*ݰUe_3,O/荨~t5c$g; }99Hz(rXU;8S-~D#O>r9㉃ӟ5dlt47zsjY]2Vkl=AX%sa:\H slLxLJRSG;Ѳ'=RinMgvl\ Su% VMwL~Y0u} 2RPI+7x: f༻`NGT,7h]=ʣOشdÑdcpǻG;(jP:Υeu}?K5dG8/NYen693\٪Er,3ן3k6&ҷ)A|qi ff `ΦY*K%է12EغPsB bwZ+/.:7 ʎU*B嚗bighph}mՆofn$ghhUܥGU7HaޗCpg= CÇ*̓gAsri?s;1"Np}E7iY@y灿{'uŶ;U@ cqO>Uk>+O7%q:ZkQ ƒi-IVpF0wH.ږVf7OɒL'6FǤœ#V\,FlE_6cʭ5 r+u"}渦 zWP$;C =ԋ-kxm4 i.3r]9ͧ G7I,P ;u[,Kx1[og%9ߊ nI21421չ}\] JmRIzE\2^qpI#IEsllU>J ӮZz)-61 #*"W#~0kر{}}O1~ t.I xZt{R,v嚒: 9ko%PDV s>t׸1)q̝sz%^i"L(wxdR2FiOpdtsNFXn~?ܢb"ەQoK!b--=Or̛rI# #$Pq|Qs^3Ә;myo)aa*otNĽ%prLNKǓe{KM-)3ΧLXtB:ЕcH(Bt}[ޘݳf}qLM+>|}CߋKᴚm*[* 1RǙ$Xbvyg#4E`.E9B;BUP)if"Z  QBxu|E~ ?~7?|u?'?WDҗէ(?$^G ܎kќי q>Ƿ'P,i{$q8 4R8qepZlu[jϡ\%9.J>gK oMhqQ齝8ڣ|6wTq&[fŶv;X^{(rNFʬ3VŇ X|K:x'[.>!hTYOLDC^  M*уqdzR-_gJ&2|7u ؖ~v H<Ԏy&.KƁ\fmy;(O;'R9?mGO<l1yn; /a)ah$]WP;kg|f\t85&[cK2L+H|#J4wbyZnw/ l в'Po+b1kCC^@rAEUwFx,:uRsݗA2z^#ǎ7dim!AD{\F0o21V$s2֑w'͜M(D@<Zc7S˖7cBZj7 IBXcxTV pZ}#8; -텂~&7jIMRs6޺ZL.ҧkKVU~ڄ |P̌CyF?ƴd,:k15wo’v1 f\d atu+tdr2Ew׎ʸs9wxs(& |=C 0c (>gY;iM$OF[o0<)$!多T 8KDnVb3`20,b,w:D lEp0NC;c"@iGdO vuzqxӅdԈDxng*GW0Ϛ)1^g]$הoGYWh^d*Ӄ<.t*fY^P½+q5ZtPȱ'RdapH0=d<ܜ1DbdW)Zh=aiR΍ m}jvO n;}M2 ZK(;ŷ iPPr;oUp5 oFU,i"=Tu[&lܩX2k#Pd;'a> sjuV\ !WHS[+ qG;;zm誯f6E(K`ȦW'|/q]$4wwܟAcEPd^ lUQ7P9Zc!OI܏H.[m|+H&yF-Xg*@(0OwJ1W@K(_ұ[aS*1 Q@SoGwOn9C],r`Z@2iIxvL} 7ߊ\g֫-h 6в qs^ߎmh)p9ْ@bybrjo"I6);pi\HdE+^; r]ʨ7U}IǎL qHʰp2˜{(uH Ck"-kdR .=l- h}g\/@r}4jyglIlM/HdxuZ7ܧ#<H)`G5wi ؎4P؛PdW*>T:M[ b8!l=}op鯤w9HU}ќ0 fkuNi@i<uYYPO3`|+FAf)GgP4G(te|;kZS>H\9k!/Yb11mLfi1dHM4]MO"J1.aNF|i ͪS\idy)ZD66MsI TǞ(ǨP6F|mE E$ x8o#"Vl0=ły i9%V%iT@_yfΗj=q2"84T<7 wm21>z{4;{!xۛ{t#eyi7nYH0ܟEjr)ܩW#T@}X _ifN:3[]l74nOKvcbwU2O~3 ^bKި>ooU/mU[w_C"Tdߊn,Xmk{ xZn i#Y #ѹjgL{ @ `ݶFڍ HUFq/ HibA$qT(dnFoC_f8+{8=>^:8FޠimGv M][=;ǁNt NUw׾3, tzW[-m:EV!q,jpH<]>V^?O|y&vڧ$mi7kl1.OnDC̚ݗuˇ1u<[>UKXZDrqT2M+tU$=@;*ۢ@ۜR`Dʽ#㑎3=AUW{<*sXcylI3{ZOqkI&f] ՟dRF* *(~;đ#^ZEŜ1< afcLF#Avq۲nl.XnAeT|H4Tb紉A dQ0SםJ nPoaĦNq1b|Mwh&h8U*Uk(Z7VyB,R|o,px춞y{KQ7+S!&BN}+'35i4;%~Rt:EMCj{WB,}!^Y;YIW2F>!$s1\zl9c^ ;7)ǐ@7mBn\:%ɇtAd8ekjh[^Jg#"g u-V,zjr¬Ny nsy8 8Utf˨i-HwUZ;d|M4 1>G\cc^#@$ihg {֛epl$1ª< g#LfcTrPymdS.Mo ?} E;Ϊ,|É7`UmJ4mv9pX1Ò1kbȭ= J;1Sߚs&~q)v 6Q}bvd"ڻì<6Ѿ[)t6۵YX\wSK;\2Y['mfP"QR1HJǩTtσ PK8 f5 3*zzd͉/e.23'8{z6PRD$(b.VL;FVYu֠Iٛ"Qء*c9ߋ+-4H #bүcEQw:$\\MıC(vMGmbDVdi'Uҭ f35޽dIBƫs#ͭ Q6<YImD]´}t6`LIcw3P;%鹮@\;\Sjo{Vǰ9 uN.GGtcKF q6L*r&2MEآ4dj|t @- N숱F*LE"S9Éu7Is@ppZ[T/RXYd 8n𱣂P+K#̀`pi].+p&0fSxmˣmjAQ H T~tp mMk܍Mi ]csX\c;ƗyX-v[[p( :+DFV#hy(q31gM;CA؅+Rn8ycy "``\كZ=>,3ycמ:L[2qYIC1^3G= tS7? 6]l5Mjcaq+ĘǺC( ޶ٙ.(r9w .^ҽ,Bc_=y󇍗*SfS`nxZiڦVYcwZӻ6MUqIU üSsyIaH,"Fݿeb1ø)쁱O$:im%'gW|. Ա]( ΪmxaxWl>wczzrVNn-^X crryw;>`[5̢"EY;KI$<-v{OU>X/w@ u>fI6V$U҇^p.{.TG(GRΉPRJrf3qN &dYY'>/4xPVLi\֐ Z畧ޒdK%P1ׯ^UΝ]1ZmcXI ysgq+h"ۈʄ<1䅡v(oT}} Zkwx&- f;VȦTqV-uwt%rI H23̡>4xV̗IƼiknQeRWm#<7K5MHȮhzv{-BItw` $s=0هڬd`>nHD#p6Gr)$ Eq¡&D"Hyս_b4^ن |0X7@S4 xQ_)P12h}4[ -i㹌!O-Ӻxpun*#X\[z[ibp\Qq2ۭnJ'oyq-sjȢ N;~X>NZF"8 ^ M#{7CȗF,a\K67{ﲲoEjLv4"+ĻW{]+L{U#&9t5]#'u6!!3XX ?u]۲ȗA{v |פcG-)1`l$WqUFA KYo7YXcsɦd=񹮿/p~1kn8X[ <*h&'b#"4úgJ_I6J[W^ܼM wszwRi)=bTwv)f%9 V\CZC(u).PsYnoywqq&te`xwStly2qx6M2) 2)G+en=>n9j;x#ب9b9HdG\!ݍ=|EVb|MkČHz| vlx8eA>N9l$ <HL~8z8D|%$n ݓoNY/ckή^X`ix6ڍm{i4t Aۨl){ְLh /2OEIj̀x4:3ozꩺ20aqEWV'W~]Q29q:Z{ɋCiӕ-vq]#NdІeA ]H(t1 IW"LMK2 V>@i@nʂ$ 9o#p:FCZv4iX pyw^+g8mAuXi]Mmr]U(\%*A6 pŊtC0`\N6mՓiT=!T?SS#;-IY ܬH CCHm٧~sNt,_34F?Yhw.nL饑[@Tr 7n||Qjy]:H g5i3EL l;I®a*S]bP{~ZL@ l*Y$wobOfm̠`DjVq41 Z8 2(V'.57QikwQbӥ8W$ǵ6Gf#:`ƍj/$5&A{)%v 3HsAYkcY7c} ^ZK(bzꙿ7ѨC6]oj}T,A95麦$95~hy|aL-cV;uvL?;J!݂ᦒ'5yﷵW=fjZZ>+ql @l2ҟ]T)br߶^cX8faMZh}*5V|{*BG,9ϺC1VVŃ-j -ivć [7s[!9( ȫ},NnsjLc <]\Vћ,Gt"-mDD[3|څ?趽Uny$+EmH 8Xz}Fyc>'2BQ}jIZV[D@QùN8,@W|2Ȥt^s3|$q08:1?Rc"=Y'Qwɬ,[dNH^țU챕c#psZ6pun,TM*ahF˅c>$:Y;sN`.YQۭZ47`Yon|B R'j;4޵ U~nǴ5u׮.,LEpH}}55ˡSmZY -f&&c.Gz61DG*JǙ3)'8[m'c]V)~׊o S=ب|ִqcjcm4oNsq]u nS{?uB}X 83qp.uV74>+zYlͽc '9O,ַIǑ32vwstP"ss~Z šVp@4~:ߋu};)ܑȡ>J{cM3^[a5 0 ɦ3f՗k.{}YNq9zE}#c8Vju%#35YԬhg ך{Ur eiDʘ 캞4NFՖÎgg9tp7dmu%O&+D 򚽔c\>ю?.*(&`qm{vLژmcE!iCDɏaÿ8q6;6PUzTB*E7O0[c,HqME\ܭL$)'sS,ŢȰѬk8< D ҽײ C:B}*=#쏋]xm6v7nߟtr  1NkCFDO%;Bn rk6O}?M9X.'G]jC;+2cML!$lkcGwFK)YnA=5c~{{ 7_ZrM][pRR2y#\k5+XF;2'HEk`6ۺSdb 9>b !;D";"6 Z 8S<5 >?ZZb(#;`щPi,krMvމ4vu8CLQ$py#?W+ydkɚLghsh;ѥLj e>tr@~+_foR)#d;24 vqJU&cv}2.B]^ ^U%N\p^Y[13h*\ɢW`S\IiѦmWnn=h6.J${ ,h4og S\$1r\ H#v2CN .at[ 87\\Sۖ#ltԍhp;i/OgC+-;݇] ons:N@y=TxY2\.&Ap'}g4G aHҥ_edcnicߏM6WdQ:W1|!<}7^zYG _қ򋡲;[q=co h2dvzsp+`qS9ψO0nMjm؀/QgMrL+Nuh~s: ĒuPp.PJ.Occ;O"/}wS-Ւ8hޗU>E+vF9NZ7 "9v;]rƑkZOʛv״#}R9[ ېqH ZC{ّ</:t}HiX8XX峝øcI;+%Zֳ |Wj~zi5m8#ȪYLlL u  o##**K,BȋeqSX׷ݩnR06y #΍cS5mjޒZMwH B{`V@t9Ϛ3s/CArh]q:E{=~nX{rF;A+/*6n+?xjvjW?2p9g.5IБ9 \oӧ>ah$8 iaq#MzMorۢ]"w @ʡkA%_zԛ9bc'kK!<\LHW #q V\MYn9b"vӱhh;$goĒ@8:W@LYBE}X҄,P3H}KcZO  QBxu|E~ ?~7?|u?'?WD߱O>Qu[+OcHhz0rX)r'Dܘ<7 i8ޫ3Iہ{CNnge FV{y\|dui'u` 뿽P# 2Ǚ`" ͺwFֆ9"GNA$r]Mq|vS#(f26QG{sh; n9.ju]Lf<k s-9Hv9i *&u d #mz¡j6\0%|IwW~EkJ׷kk&kv@kJGY\׈Xu3[Bfe8\|_{,GIK⅀.:-ʩ:7˾.֙M,ǩfe$F{#SNvV>s!Sqis|d`5v "Sݞh0 Rcܒ|t޽%h6fJlbxuӅ`m0Op ~cL̞N ӫݴqw>`'GgWT3"cFwE[cj8t/}ɱUƩYiʱ*#Vx>WedxAk 7EC(-U [A#乧C$rզ'e2YR<]U/btUw[V٢u&؄DT\nGQ\C@8{-p6:cխeT[99A\ad W?&cp)b+p=V%r@8ϙba`u \1/v:E\*DK+&1UD{.;F &}<1>Eh7k2ϗ^4okS9pY=siHg"gbGV&`x؂H{K ;Tf2WhtҐy*_Ѯ{GOc [Eae*~[3Ud1Plw};G*oS =TddDy(FO$m9-QL%$Σ ITfǒ61iǸ‚)Ѥa,9 {iɋ1&XwvZod%R$)! -%ڴp e"-NSo?"o׼TNFZՃ/-y}.#{ ~U=^. ¢fC/Bn6濲mYF?P4j{}] đGuU<裨Udf;0m5yhՐEU읔I~ r0Ÿ8T7Y!tlY*ӕl`CJ쥭1bm=Ⳅ?Lt8x2} [pd$BYB.AjXm_jfS^̃ `18`.zc_pa9s1||/nZ@8 FP77@V ~%ieUس+0nUv6z,n9 d 웵ߌ's4reœ-3s·~Rrl㭭 shYoWUkZ]/T例KvFR\w _|Q>^qPuXL"t}$1H[曣vx,$e 0'Uu+<ae7ڗvU{.Qzez)"%97.MݚގI^`Wu> ]3~%⫷k76Z2 hC eOCCׂZm?#|20 TZ;vAT m|֡2s'OCmﻬ/u} VB} L.ci T#Y< L:CYc\g󨣏=Ϫ=C6艿EkoSPǘVBnwX] m}rPg}k_߇;)iujǠPe]c*1⓽t4h/sYR6i kɜCD#;uŮ7HT,!${;5MuwurLP&nz+Ҟ5({UYF2r1GZm`lQ$Yĝ@-H "@lBHqsDk68dg?Wf$^]Gtr}ukܩ\=nkA×WuU{?-MSr$[1#>o_~R*3~g# A%vAh/v[p$怱{es+3+eqz/qEM|+IfovXՌ.P9V۷iqc5sn1P4R:hb;:loF#IZ y ҈OAUcɽ]9ySX @v{>%*2Rv$>sD@s&mޯkP&(r2Sbk2oMbY'94DRnPqZڥ`L*<{&0r2cbiC{Rbtb1R #uBVsc6c֓}ا(qHdP/4HM*Tm [H}?֩mABTP49=]-vr_.i߱k:-k/'GOz6""""""""""/[g [Lڪثxȏ@j<eqʸѨ/Fi"6:Cy&l2(S~-DO)p1e̋9!n5H,>6z%~V'–9zke0Pw0t]@srS"%29{\Sm{^G Mzrٜ+R[H! L,൘ͫ{F*.i.fp=x{D{ccN A:y$_22n%ۼtȘhދK3A:d|xF[nkMr/ci "QR5XrA3+ \ʰGOdЄڗ<Ѱ[<)K_e|8p^3C:4yw<@C,LWRaXǨ mCЊ8'dVxM@p$v-a+1J;vv^WgOadI@[Td҈ ,ߚ*NilKTFvs|m-{Q.uv=;V(e$n# c. :v$dž8vLӢ.dz/2C`[cou:}!~gFR'Gj}&-t/ٮ;LiM-}h[%yq搜~. e v7[yvڬVGhQHsu>RsL${5nn:im.`ۖ!Ђ۟NouGOs3Cc潭ii8-T]}Ȳ  B7TnG5]W34bC!f<ΗI m46(\l` wCC! 7M@'JX=p;=>orON(sh=-m|"lUic\Ik+hcB O0 E0/r %WFCZ@&'pp.\FaS*Nyj R"3]dnOE{EّV@Ob%̲Z(i ҮLQD!x2ẅ́ ipG_أRDdqX`ԭpp١-p}R!Xf3|kZqb|S9 7\;_SgHL p tʢ{`$a*PKŰl}b^]:0v=EYw:ڕY@'l{񚷨js7@2+M__'Z˾!v|ۯ[jߚ;bYK4jضzV, ܤmt7)3Dm&ea q`/$v;{=/R r,1t#;mɍĜ s9OW{.7O䉥K"3@q189U 4X odb/fkkHowTF͔șP cRi#y'p;֡U5x% n9sO]ʯș#1ͰGmejrӽ^!ڤ@)eO79a΍AgoRks#K@@8u(;KLP./!4]&,< Yw#+1P{\E(u4 blځW N%`VE3G}˜dZ7!\gOҝ I8i纒l9f(nz'hݐ2^U@$R82SHƲc`6ƫonOpnM=j],FO-)q$U'Yccm v,OPV:b2QVŮeK:iv0@)RQOVT m="Ǒ"pIUQrͨK463reD:9$)>.#oSsc껊NGIw!Cc3Γ2ǀ.=zGʞśEKZT5}ܮ|N@k_>%nz wV-c1H'TcH_ {#-v8ZSs8g"ؤF|9ցޏ-l8z|ZTt2#]‡"7CBDcnUyxT]+Yi;i2aPy[ ޴Iɛ VF|5[ i,@F+ٓ9?’|oXNlMsMq͇~SKec`v&d$9>M۲2llu CIFVRLaimAHZ)߼*{oͥݬqK$qȮ{{.[LDKc~'Ep$0 :to1s]mE('E;c4'KY)C3gLi] ݏt9&N){dŸ_!v"3S!!@ HIr\LOer{#cmvڬW~RTs\1Ϩnh푇]mu%-[֭fpЛv?>K2ҏ gMyGjK~kׅEmrci{+#zxď" )ZfSe6j'|Cɩh%;ö́ +O '~+nS|+M64wv粏s )͞@ YŌ[N3[4^V ٠qDP^ud# ;ʹ;T˯sbftO#5-8C$Zy%, 1D;Vy]$],D(L'kx؀6"B:^Ѵ{` w,lc.m魝F^Iѵd1:JKu<=vw bnKDobG zk4hfBֿi&j{.׋,;hdu%bb¸$ŢwU%ՎCł<پuM7Y@y0Iug#@ !ފ'T$1֘~u::;G"s!{ӛDU1K8vZ~:9~ЗYֵgF6%gɟNOulϯe]QKHk6%9w޶n5m/hzg:w0Mg:CE^o&{VHK,vM9@w*,BqC,'K{Hݤɼ1^xx*c\;͕'GsH#zEJ D ΄,T%Rritc&Lg>EG0ENn+st~׾ݨ9H"X>TYD,`愋g_.cjڑH8_@JƟ7/n_\;8 ~A壭?F$PPPPPG¾4]O5{;yݥUP>g .KivfV{{`ec1G6aք-WN"~.PpGN MhIEof{I^ E@,#Ƅ(5Ez<%t"Py"&hxaG)[!>!p34Z4Q}.\͂Cz|̈́l~(<{~}?3KHhH~=X~5b^d0uiDOioEpV" کoP%c,,b6)K44IXp,;PARnhRdp6.A]Sfq̞vG*_M=2A%$%k:S6̽ .uhK2 iYVJ i>ƷJ+B]g F)Cu8#I'LQhŵA7)eUt;\ԱTy2jhaN(7.ӬѪ1vm %6\QKG\[! m8E4+ڹ@Emw hs<-dgO8@b9GI,4$DYe6J|dSP@}kQ>Q|N] mkZ$ªU{+u>YnnTg4mckMI8Ù)Cd]8fQO͊DՇCuO|[ )klNBȯ̟ i-p]ht9c欜H SI,>mm-./;V…\+V7fרdv;C[gaǕi<24{g\^dy vG<-&.Υ5}">G}Ħ'hN9%B'u^erhɐ{jR`ni\oACO[Kd#HF@S4h6VHqFvi.%ycRQ;?8K;G*| C|wxRÌ5YcA$=`Sۚfb1eqVѐEأnI]Y֚.gNl N`/ UF%m@ woi#dFѱul2 0y:7JZU܌z{<ӿ$]G-M"ۘ[ouZ-KUy'x#]b$sܥ<0N,qN?dtwk AtW=9l$^Ċ${k]dL 2 }U&!"& ^pvR_NNlZFFU˞Kqmjdb=N4ilx^E]Ö)"vnMLs.xo`jQ3T{`n29weg#+"H+ ڹ |lhn4g-r3BdTKZ0@SKƁ>_MmqAQPsG1wv3:lh9.'ӆD-&C*s%6g(a Y<rc%Inch+u9NOjWΌV(F8O0(⪮Mv^az֯<e;m@Е G&a$-g,3M\bynZhՃ>?_[Z DLlI n8 U. [LK 1CjO.u>GxtH#,Trr2I+ƛ) 6A$6'~8xB2%1+t ygYxyMswKě i跂=4.k5t8|EwiaRY?8luC>ºrXlvosjUɡ@-JhVkbv;/!X>W$L+ .{Z`kVn'r$^DVEc8vDXq>RװZ6m#e2eW=]>OzK8P.6P5V$9TGԤ- m+aC)W 2@$ܧ)xALtqd4vS#n݇tӳ$yl1R7qԟ#/7-8$ vʝgmb(G>Uu;-I"|=s@CI<5ߕ8@nn- p2z w+:auہvZ,v_e_csOl3tyr {+X9Phw9l>SG2^By>])kv e8紁vރlqGV 3EݒHu+tX :-=yNqӺ'۷:@#4jXxYp:_?sdc5#\Z,FۆDxtX lON A;^ ެUj[{G p>QPRZt;d.q͢v,,(9YDó"rOʗu}M8p=d˳ NPY;T o yϕJ4̓hlr] >}F (@'`q(y\ xL-ghHW#=Vu<!$3D..f|#scs^c{GDVXPt}gIE,܈RȟUE,2,GSӨ485œ4/Hp- uUX+(.9>xM.`ܞsCS [)c,x7SVHIx> GII3lH$g}r[$Ө4+lN31\FYLp鎙83v;:<*,.#! 9՘^JX H+cK$y!lQg5[EMAgp:T-[כ JN -۶;\$AS 3!hg?04p4@ 'pݤ8afshvCI~WUYaVO\PH>[ O4{+8R5-n==5\CC`4@'k!!^R@w2)\ӼhءwͲr^3d ^Mi6l $)Qؔ`1Jv41 Q4Q W2_1O*M83SXI-KIhQ]!66)iՎa܉2qu}Ul-Pvx8;x+^8=*{H#9\A |vJqd,_'^E#!T wp'HH\efAz^ZV%U}G=95;YnTΆل!$Q|R"(B4!(Br ƒP@+MYqHt |t$_bw/TԊ@P!y?oW_]_ܿ?wp3NGZ Ir~#^%tH ?f/ zGeM֜Aۭq7<$ rEy? ]Mnc౩ijSko*֖둿" 'x[~ Ni B% p>/>!pOC_O~Un8o7KK5K)1"IʽF ]ڮcNگj6R (v@W-١wp20!Kfvޑbcږr+q>q۽˔#9Bx*XgF7qx#h $Rkt:GFɄd5L]V]+d7!d)'ut{m٬X.1Dh&>uՀi%G.fq%k9?H!s+jp=NG^mwP^UԺtI)C $jnۋ7 $/8Դgcj %_)?DULb5ϴO~i 'A-@5hV*oe[:h#,}ycZ?G飨pɥh[~8ۀ ak>1bDF{<FHDqmKjLl+4CnUw=UqwL9wR+`&S#'ysl'Tz[raGJ<'W;(|_ S]*i^ys4`R fF(]*-ĴH{-Y識NUkd^khYb.u5"XlXaҨER$NY@sدf{;L@5NMvI1i2; a ۩؝pGI1d8\w$,En{Q-g.Jze{cIJ M7PN6<2rp2kyv8 -yTbgLUsiMg^F<0V{.VUy_f ̏umYX:Lp@ IqMjB[&4o.p(c"g ҷطoGבYM$ܼ7k^*/tlX8~{IKj{_QY8a@VOţ jXXі ^}] 5[k!ÖI52eh9+A=?y>5Yv;XFggn/,jm*LC@3&ʝʵVŭ8_2mA}RU*J],9Q&taMrbBni͍>%8*ÙO*QߕErN 'RՐJqbݚ5Fl-d;++KXۢ6wg$ihJs ;Y鿂[" = 6Pkk pӡKu+W. x%da5FZRF 0H!sKm7{LGy*3U!i4&Z>yʎYZkOuqf1 X] kve9]ٻe!%OhNJnRc)ݧ.qgVM/p t.C;p{ZKW$]_\ziZwktwXy9'%Ė;z%i;cuue=GHĠu7O!-p"i{|E;9u $o`SJM,)z 6w vl:nnHPc5{lUuϿd4頻!Xa#5~;.H3NMvڪt=Kj›Цݮ<|sLk~oOp4Fگ*#.m䶗m0|LJ>Kur2 ! hjm,_R1i"bO\~icѨRfq9H^ӽQ$lYj>yu%+ 7ӟORPRx ߋJP$ ^MWݓ*N6=JD $F*8QLG$fk~4)Su=FGxU^/w]D=C~?aȱmi-2 UFA@D\[\wU,|BZAI (fi }TGxT<2=dx~Af 4x?| DuJP8Yq:r'w6O{h (85WbkBD("<.#@O:{ckEr6iޒ ;*$ƫ1y/rJFPwC3pZf;*L1OURi#ciYn}55J^޾˗h#tM"m_ =?eD-=w*^;n$/DXns33)A8a{vFYN=ƤaT{oF9e؋i2|IIʵ` 97@Wx75[MNIapK=XYc&6߂*;%ǖ];G;vW_YNka.S+v`%xU<xl㷥V<  $jhuMW`U[8kia|Ó -n'nIm%xvPk`#ӈY>;,Q% 21R0FΛ]ybfRg1[^)WQ@n$tO쨛OV(;XnΠ,7'a$kf3FXGy>ȐM ~4[ga܁mWmX z0yJ>H8nsrO7+n"Dg`'rN:͟ 客,sGchr5^0UxDXv qQ[څ<*dkϾmV Ϡ|zwOo,PZOq$`sUޔ4hB,- ɭ L0?ÂWJgkt4E8h#I.vySe֗)0%sUܧ)n+v[贚q .yEm˝#Ëi~,ͯ*=m`t7߂pHc-9ygtOp0H@* )Hc")H (BZ  QBxu|E~ ?~7?|u?'?WDwԦcwXN'Y$ xHM _/ߊ%Af'0?mqvT1mkoQ-!O^]GOc)[zq\FwNwOx!%O.m򁿩*UhY5[BS奒,SXx6P1hkhޫeq6o y9nUJxe?aXXsM+rqU|wr!y59c }u ?|qaKDq|-pK=r>_y82[9hvۃ ~,&I-98H .?3&C2lK.$Xz $+'!dN!kz|RLTd 32=r9W-ZMJN\uBά3֘4tgs!w͸p"Nv{lCna!F9:y-{Ay85l8[_e]/h,>퇴ΤlRi6{,Yƕ.Ez,IVP Sg7 ?y{ 6a7"#@>|E9 F;r E_Wr;{%?iW=}a+cx*(!|&N{*."c 3apw]':{tje]+bk Vհ}i E#9Vn^\~,:G<)mX޳GR.?==݄Gi$B$Skb;^҇i!֬Hf}i3Ja{*-16FrHFUoz .&}S֖~as|Y`Ñ{KMga@\,Wj&ƋiJ >wC{B{St>/}Y)hpaS3.aV֗N]QstW#u8,*I2@hW_qƚEo`G3 {ph\N, WZFz2:ep{D9Mk/. ;@ jY8:;*!ܮUmn k{IM+eFy=`iJ[[!Ú > X{vz7/@\nUs3|ڶ٦vÀfꡨ#cz&T]sNHv(i^] |l lf*&a84=W'o{ reXqH&'L0ZOe dh^hN.׮3JqܡScZ~Txemjl|ccW]= lvvhĎEdt ֖xgߒ6/ Ӯ$Ax]@4~0,m$?o;^ ?2ɥXBQYyb\~9yaEoPQh׉g)fJ .l@w5?>C/]"p)w-hwf^#J#)˸nDc8i#k,:Ru`NNJx쵮/ҧi81C(E' h&XxcqGG\"[M(Hd5f<΂h7IBI['`X-xDMڳ+F_NY6:HE> w\jiukk0@';MOGg%Of[lUN'+>צ|dyIc]uzqƋluG(%w-\ N~A330MCÜO ۛkSa.3֬l,wyd;!PV`ا%k Y"9rN|1t0=ncp߇ x7dߥU KnƳ\$u8\HYYx򵳸@wO>܉ 0r,gƚm<1[ڬ{kܸ'[X`scvLn-fߊbtFI# :FѲ7L^8~KpX v|/&$Vs,r%k7B pYӀs=3:9$"ժKku,%֐i4 /.hOe:Ӎ)!S.k(?ơtsn01&cf]YHԫ>0moNX]s'fFCJcv<./6=ԋixuq.-#ahKxLC부mߑJ9Ԋ,v~?(E,v(RF8$`wáuvPY =nUhmz9<~FFSGcNDcFkT"HC诂üM~V1&c\EhnbeH\p+nw)+ Hi&܁_}د^1 >ڕ0pR%E^mOm7/.uO?LӠֿ?tܟWk](B(B(B(B(B(B(B(B(Bf{-qO98\!BtTԬ Ib,:6FE\X=Kӟ ql=ëúv{e='HZ7iQc<Ѕ>< _r|=c;> ?6/M }?6pO?@Є}'vAhB><? !z gݐ}ZW3>GޫdV#U?B~աpO?@Є}'vAhB>\? !z gݐ}ZW3>GޫdV,}x'vAhB><? !3?n`p ǘ[?:ЅӀ%G:Bcin*Cլtk E/jJ]y҄-z1V8Od`6Ѕa"'#|U:l>ᶄ%'vAhB>\? !z gݐ}ZW3>GޫdV#U?B~աpO?@Є}'vAhB>\? !z gݐ}ZW3>GޫdV#U?B~աO?@Ѕ_G^1Qꭧۃm eP7 {^gZDH5O8zNg`[v0c˯ZaHȼ%i=mM~^x_\`|hB~աpO?@Є}'vAhB>\? !z gݐ}ZW3>GޫdV#U?B~աpO?@Є}'vAhB>\? !z gݐ}ZW3>GޫdV#U_Bww) o4 #BǁREC|<NO١ 74+<.wtR[#j1ʄ.Sy.hB0W^~26 xBz gݐ}ZW3>GޫdV#U_B~աpW?@Є}+vAhB>\? !z gݐ}ZW3>GޫdV#U_B~աpW?@Є}+vAhB>\? !GOG~@<4N6 rsBÁCxLFB6 - /7QhEü=jv[V/32p~!oˁ$ʼ)íNmIѯ]!^4v̴!=~աpW?@Є}+vAhB>\? !z gݐ}ZW3>GޫdV#U?B~աpO?@Є}+vAhB>\? !z gݐ}ZW3>GޫdV +2x?@Bx?G5#-gҷFY/ 垷W6)J G1BzN^ y:t$3j+$B~T%.чN.sKpW?AЄ}+vAhB>\ !z g?ݐ}ZW3>GޫdV#U?B~աpO?@Є}'vAhB>\? !z gݐ}ZW3>GޫdV#U_Bg"n@'L }p싆8nGhB:}??XkQMNIT9Pz'>>TjC}p"ߟmYџ\hxSePpJi0Bz gݐ}Zt^Ҹr2KvY@6 BE^mOm7/.uO?LӠֿ?tܟWk](B(B(B(B(B(B(B(B-RE.4˝jܖli~*e\ȡ yBZ'մY#t~ p63$1V=EZ޵:2~rZv I(GlwT!vB7l eRm҄.S?fzMXux' m@Ğ O'<y[ >M#DXnw&l$.hB>ipw,R:PHSB(B(B(B(B(B(B(B(B(B(B(B(B(Bnt+nb+N%\iy\+젷[мMp_Xkvs ̝1K/ϼ$aI ¼!l:pwH4bc8XvyPNWkhv 1>ͽIӡxWS';el3{q+Mx?mi š_\i 3g 4!{kBD]R[/ϚPPPPPPPPPPPPPPqj\'aoqr/fS;۶9c'JZ1>lpiǺsjRڣ]X¢[w Iك-VeGiP.f^@1<ABx?5$ᰞkX2,dsE8cNxŜUtc/RIe4F"dR t Aw' 3.)⋛(+]=mdiĥL^}!{""""""""""""""-c8SwƝz Jt-[iY \dPOq菉 nҾ8UƳ-zi{d1d~,磚>nm}0elPvc@wP}9zou kv%ŧgE u^mд#џvpΣ.m26b%Þy*_ƚ8XHuƤZIzr*!{""""""""""""""-_WON?FmFT˭۽`CHH>.T!z^""""""""""""""-Vukƕ4~޺*IRNO\ =pf\s-ho,T/F*9v83*ai~ 'ҵ{BnwԤk'%c4!zKoL.m;mVm'*XpN ^[5o@?O>ALq\ȉafHeFn"Ƅ.Fefk7<ٱxGswp=P xZC3Rm d}fJ q^h#Bs׆_*4{қ~XS,vkb1BZfc&ֵΤEpבT+c\b.aú?{{GU"ٗN E+<7l[q,6z"ǣ6I5k0L7-VaB̎C PƭA4(N*'k6wqd-$*.ȩ ЄPPP49=]-vr_.i߱k:-k/'GOz6"""""""""*$-ಇZaT3NJc=!@F5KC3i3-9#8xtNtf~! /tMǙ{B^p.pg&Bo$&rs!o!!!!!!!!!!!!!!!!T׊ۉf4~Fdp0{]~J>?=zR֭~*]kRUXn'@сm}yЅpFcGši-oUV"yyЅM^έw 5Ҙ,6ۊ/.y\ P)'wK𶛧볙 d*:!cԨ v H-AյSO5R}BrP(B)n_qºlH,Ph}FՐ_Uѭ珈Z72**(AB='\%pŇ XhZ&S8ɑǐNy ?D|$~¥/ K3g-"""""""""""""""*~UJ᱗Tk"i*p!P[zK.7~0ظi} ȸRu/D#:Ιyc# ,QڒC{ (B{Dc|9W1pm!O9-[$Єz*E]ºl;[)QĒ@u[]EEEEEEEEEEEEEEEU3Z?h|:uԷ$#y$bz14!Ph^88~(Ҹ[Mק23^42ЄSk|7sj5cyIIk2_Zw.$Ѕ/@ޏ:N4 .=nK8ШyJW| }ir/hf-SYWv<ټ&-"""/6k`:vN_o~h_x?:O~+ѵĮ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!UN; k(-g})Zq'.􉭘4++ [.bGeqx >>hFQL}e%ZxVDT+P#.ci7˨Xa5=7(R (Y6\PtqY^j-.w%u.q1ք-FM W 6뉢CXAҮ[m UXϕ ,9P6p=X𔝖ZsobT<̑BcGuwKR-=Ž1Bn5"%rAO!΄,YsZ>޼Hcq G·1 PeBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBxu|E~ ?~7?|u?'?WDI^ sL*./lgxFOM^tE>upipz!uq,0*2zЅ?Q=F6Vupúuy%]ƊHvqp1' aj0"[hmxkCҵFBl1{>״ B=_ZָOk-T4)coźK:4j-RY hBZ_:Ntm#Vxo_//iFuYWF{B3П SJpݷIz@uR[M0Kfr9Pk>}"i4 GO2(gV)4딳RBl%)*APhB5O__ӵnm.'4+ C-%[qc#d諃GoYA+.!,Udq<ۙ k ͼisx{Z?ο]ӿctZ:^O{mq+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEHXERz:ЄƟ7/n_\jl4Y!g4u?+?6zWD͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6z͹l^#}7n~?סM۟B>6ztqgCM \bMvr_.jWG^_x?:W~+.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.4! ǍGƄ#qB\xЄ|.OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz*hQޅ>OүQz{_㙚ZL ˺[# M.Kcj~沸"4vXNV]m}ϖ yr1(r]kvqg&vs{_o/vS<ݕzmMM `N?t|i2:ʭLMYm?Q~Qz?վ?[zGuVghm?GDmY]#fS냑 ![Ԉ/GCU9x[詿Wۃ?`̞n~ϏP?[U۟~o| - WZ\ IL7sc'7D]UWS;_glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.11/scatter_error.jpg0000644000175000017500000047302313502206677024424 0ustar noahfxnoahfxJFIF88C     C    o !1a"AQSV28Wqu7RTUs#Bt3br$%46C 5ev&EFX'DGcfE !15AQRTr6Saq"2#3B4bCs ?*KgIf^<~Ppcpzs,qԤc1j5Lei\gM31{ը2>$d}#<_?/ن~agqxϳ8^Y9=w$1Z$z$#R6QwVїK$Xs{ns4exmw۵sMvZgC!/R^aFdeD:NRvVzƹ yWŲU%*t ^72}| So_?S7i\fmQ9Oײ}VVHSo7` Xo7` Xo7` Xo7` Xo7` Xo7` Xo7` Xᾩ6VtqT'3 M-}FFڑȦSUtӞQ?y\)m{BQ.jXTtXY2Q;E,GSD9c^,ť5Q3exڡ NKhT%Ϸ-1T8ӻťKBLq [ig*9;]EhKNpӔncZIDGkLU3g\ZG[}q֭hn*rP}4TS9m ;>nȲGBhҨq'WV_S?^W)[KC"C)Ar%JKI>*mt8 4ꦝqLUĊ՟~j4 C?QVJs4Nȕ!DRk%F\1Vj9u]‘14\s=+̶htj ڴ;d|bYI[i8k<yx=OjM˚lFq]rsI,Ļm{F62r,qϫ)=Q36>PXXeWfUTSTDz.bGQe.>{k-sUU=z^]1Wo7nǥʻNl: YY~]>mNQܦBvYJTd=,3ZhSբ8{H/]m\>,F_m),-OL9K5Eq6{o7` Xo7` Xo7` Xo7` Xo7` Xo7` Xo7` Xo7` Xu"AQ=n%&DFҟ"?UkcotVW;vӔZDO'TzP;X4H Vi[+W=|ڞI}pIwLő¬*S\sS%es­,*뵎,GӮt8]I"YulJr>n=VXgF\ZLmx3zUg^yՠ9;jo]MR MWJQH ?NoM+iQs9 %QD9&}$Q֫nMujnȶtx*n_*6mnXw cc*WzE{7?ޓF9-8e ,2%cԯpv]ޫJ.ULg13;\魷k_U"pԄd=^-&ֺjgE4Kj=3P)U sеmV-xN7Rvs$&j5136:.DDϵsYQݺ/K֮^֭uv͑tsQY.#ȿ5LEQ=U4TDǮytjTe:snͪM[eBjUa;GxOhK&-qkrsD TU4ƾ4sƼG4lPfWrIqPH5$x<OYGLQŮ:M1) +6q^̊*M+)dǶQm(eG57iTU|XUN^vmC]uԮȀ:j[l*4Tm3`Ŧ%ιL&'șT0Y:׸96LJk7f$Kf9O7}(ԣ_3e-K|zk׭>h9X8`usq:9X8`usq:9X8`usq:9X8`usq:9X8`usq:9X8`usq:9X8`usq:9X8`usq[0 ݢ2<#VBR9IBQ(umׇ]m*UFÅX,v?be#RPa%:+P㧉6pYuTl(GW{^kf\b?_mM ,TY#,l)yuĽSUTLUL0gpx~lB%9F_5x=ʮ=6qzlil93 ]""p"/%|ϻ70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o|`70 o"{[ؚ:Km{%$Ϥ#Qg0w}uU߯W[ ^,SbC'W!zNx)Ցb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_>H6 tTM  ZK 3#Z~KˇÕ8=SB|DoڟT=`z? 9S w4,WFj}PՁ\0Nx)б_CVsÕ8=SB|Dov}XTM 4T\Q$RNx)б_3B|%LdTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#CNBK0Nx)б_u2_*p{NR 9S w4,WFh_̅aʜSb"7;Dd/TTM 'W!zrhX: /Õ8=SB|Dov|^;+#FLwPyBu\%_z䨛NQHRQ*,O܈7";[VT^22H3gQS|o7 |QtKSt$ k$|o7 |o7 |o7 |o7 |o7 |o7 |o7 |o7 |o7 |@V-WObOZ<^:j|6cީ@bqߪKIW7Yq pRdKOJO$ C =)';Pu̇`6;UJnQZiZc:iQp22ARy7ZJ~uUґC)Mi'vHˇ+]-iSLNs/DwKuv^I[2JsNEPFrk3ə/l:[SbEQ]1)]Qŧ唘oڸ8Åq?Qx֊FvZ͕e0Ƕ1.^W]N$,5-]S2SAHȉFxUזs#إYge]YS9ϺUWP&\AlJLiMP"mxO>1WKh &ìgiaM31NzMSCdԞ<Z'"b&',F<Q8X|F[! E^GFV`S9Elד~^tMxU9PR[y孧<'J<]%LjMު.gk9SA/V6طƯ./TGoY,7` ^8WGijh噢=W / ҫYG3Kb8]1vމ\ʍzT&-fmhr%7+r;=^kPWmRi7eU. Q6pTtD͸'I7CGbIwXaѝtwb>Z;F_f|Zߗu3i /~fNƕrRcHiFvkiR HՒ1M\ET1?IxS,9ks'-C9; mkgk3xǭ*{J;Ѝ:6˺.z Iqf=<+9o]VUlQ .v!Hy>m1ҥg)Nr ]Ξ5TLG$WLQ+1Wi<]XP4c$m1ʖIi%Zi3ŵB5YjZSGCfF6M*ψ9Essv}sh/$n-|L*.|Wb?MKnjA)]lXoagAi(lhkUqv6[SRI*i uЕK; XMN;+K Xm+Yψ.UZ[]vȗ!kMY#B ٛOV&P7` X R5Ně6Lj'˒Fq+کd^j|$~gmFzgf~M7v4h:޲n8Lt% 4g.GAnp+ kaEqn%Ǥ+lM( E#dsA]E[I;kdl7`9{)ĵQ)jjhۀ.-񗣟öiL<ZE8*w'PhL/gI'b͕OUcXq\uDsu3S;I56ؓLWoF2op&Y#c&l#*Q+ kMs7KSBs92U`=$|‰­=ׅ7^=C'B[R-@R!X[jmUE:+8r{i6VQ0ru'pYFẗ OO-Բ!:Mvl)IoFJSŖɒH$/ͼXZZUSbo6uQi9Ns*beG'233ќ/l8M<*=:kZq+41I88ͥQ1/TQcEQWSݟDCw<񑯀y-f&g1tZv*FJlAt>XUavO~M2,KmZ *%ʱ"Rx.YKMrBog.jMqUVe6^(74USjRl>O//Ӝs'Z+]Ry^Ϧmk:2Pg;+"rojHt_*Ts:"o~\RֲBYRx"/s3QXVe2nƖ܈: 2ǜz2ګ)h5v,bskX\4= }FA3ҌԼ:#.8z7`9_/kQ5.Y7(iTN\?Ƌm3G<j/Jigȷy!XPW u^IȒ}XUYYUZ-*T4k!:o2MdF9⍜I 0oZ/7z&? .me=Y7Q`JRrLFY#F)?H [^R%Ψ:]likZT&f~m:%!Hd tFFDfy/VX}w..l/؅;9Us.h*3ko!E= ";GaդͤS]rF􈭸"}Xdf&{Jj-=FmM^S\_(2MBC;Ǣ" &x2<9_mk;^&,錦zT+|&nVtg5Ǯnm*#.W7pv0L qx"Mv-F7UDx.̻u(&m*01mV|l^eR/A5Oq La~ -8oV3D;u:-n%rtm,٪f>X宨O▞?P 1[ϺYAeN8JR]f`9QS5 a&K.;R"A>BMFN/h4<}%^?_/T_銦O6W4mƧȐRTegAyཔp~:~%4UeOoX7?mUQ1ħ9xZ-p6*JÜM8JiNwﰓ/ cZ.W;ڛN76kscvgRf#\-Hx&YO'[Fמ<+.ʉqͮL&h6CdH^9ڹX5;=uzޣnލR4; Q3\r~Ye3ZYWN[3"Q䕵k\<9Ar;%GlݴRH(p&2?!]nemUM戵*zs1X>Fe"i]TD=JP)G#5nnYzDn5gYܬS=4xo{9NĶ:{cXS&"z2D稱çүWKÍM|vodMp{z-,huz6-+)&x#v[/6Wm2\/~aRils&#r'Vq͟Stz&6BbRR᫤հ\G)e{h_A5YTa GV.=ԢeU 1 %ҧ2Ce(^(n5խ\J&U{B5jäI6G!>V⏤ՓɎvZ桮<.9nu8LcGF X%Ufʨ(׫R^aW ~h6ڳ~99Z6;):|f7jihY;3p_q {Okcř9qe6l\+QULjʜr=}2+ЫV֩ڕNRkm-jOAI,X^mqwV3:[σ\(*hʮ3{MXo7` Xo7` Xo7` Xo7` Xo7` Xo7` Xo7` XoRmp7!?hQxV9Kg+t~.='y߱q%hejm &iA6Y~0u );Qr՝R.<g-ZT `8S&,p*,cLU)E˫y;ju}.c'67z}s/UurY0.:Pjm,M2 J[A4NFV3"cVXXD9g+MxɍVѥƺhشj\wklBH6b6X5NUNS lEުm5c랔u3U)Db;6$xqR4x?1ڪҊ\~^llg)[H'h&J5ɐgXrSE".2Ek[M~.,՛Vc($8TgM)V^7r4wʔ2xZdg,ו6u[ģ[qʑI:K޴joq-{8eo<#X0]~̢[It7qJhd7=qMSփ«[eLi6T8m͎;)|4T"s?/EtkΘJyz+'͘^ZJ̟v&"zI[u3~Ut ^4 jkv nJhV:vYW 6T[͝|ˋ$W>=>ZcƇ%HC,zY,dljꍒ~m*Ή&E閞UnF.)3M#$=$W\RŴYM9f~(]궊r}ZOܛ!kEPIȦ.,nMVJ-<>~Ut(sfMʫJ)&ȆlJK(uUfym#x"|8x(\ie="Z'~q>JQB1omU⹴5]YE4$aor56+L<$PSԿ\J~ZLl_~tH1 ̧ZegjXIw>꿞~k96"Tەy'*ht4 r15k[ݞQ:3Dm}LtjDv%t17h6^ƭ/vzq_7i-п`#Mj;T[$DE6P#9 ? L|i)Wnio,0]W6OWٮ;fsM-gj疮ܻ{|mvMںN1Ys6y N+6w pcӋ3˷,s\1qE|omϟfy:A^iPmJigDf|1ё+ꊢ23Z~bvXe,i*6qVlZn%7ȜRq$qOèveg|LF=Onŷ5"g\LO^q]yHm P \f:r~b/mq[1MvMS9sf~6#e(d6 6r6M=%4G8 ocU:b]xSt,bӏD*$ȸ;L:IhϠKl1{iVsYNr|Kx^+7]-iύOdYs=P΀V [cVi,cx'|I]\Ĭ/wh'*ϮWm32#;~=_TӦBDCes<~b|Zgr}]Wѩ¯w\bUTFzve^c&lw# yS]6Yn+F},'&6K6 8ӟ.y&wUU֜Zں승gT; =C 'Ĕq J괮ku2^xT߀jͻ.ﱧBJڙl}#3N>Q[vS]?ZD9/!T3s1CE!QlL $gf\8g#}aM8˦9c/eżskUѝug>~~f<>vb- U^tT'E&$EI'ᜍ˅V^b-i p ]:8j?jrȩ"Sm8XS+#E2>+ⷌVo3]9j'tx}6ViH:DW<2%.eVlcF7xaKM*jvˋ?f9|߀o7 ~߀o7 ~߀o7 ~߀o7 ~߀o7 ~߀o7 ~߀o7 ~߀o7 Clk:cOZ<^:j|R$qߪKIw[oKܴ{)5l<9PĢV3Mj_7v.xvuݭK@eƼҟOH\z@B+& +W( OG?$lq67j{jJk6|[ Kn?CGYm|߀or.&v:ը2ËmǒYJ D81#?H=g^Z{N_rB[f 2kѱ딝LIi$8zJ< F]0Ѫe+3XK|ё%dga}#j:jx>LCng5noCTw8^ma߀Eڇ#{ h)qxopWd78bv~U'*4s)X^vI(Bq7rϝ^Vba_jW]Qsj2W:{E yNTΦUť^;T*՘5P鑔NƆar\R'>oQi&|{U|#ܱ;z"*npzq::j,Kj1ĺ2"qDX,N:mV65E66+ ѪdYV V O e/tW핅398voz"]uJh{;21y?]Cޯy 3ߌ6߀o79T]SO[o[˭,rDGope6H=ZRu.Iqi|\>ԡ!$ʎI"85Z$qt)cO곊i8[#4(ҨeXϽ?馳i?K~0[{ 3OFmZ*XrVUekQ$"ɞx 5}h2\a[ڟ'1N@>ȽvcSk6TqV|X/,nUUMNqO͙f G'^a.ZT:h.#MY5ccb)Ι ܭ=ƚj`o}JǿMWKqR-DL'2 Žyʔzg3e <1vTZܦ+>}r.kcka6UM3\NS˫5M1LE4P5W3USF@4gkw  QsJ#UeSSl!mtuceX^&fb?եZFU*o[S$U+f{pb1$WF磎Yk`W+֫Hۿ+R y> xW~bbsQWƉsՖlsBWMfB8e;?˫繺Bb]ʿ{X}̠1IXeJQ9 k=vH[M.4Ԛm tTSY|[m Cם"= ͦ7y4`57P{ zߗZRYq2c5M33f%*BhG2ڏ Q #Ei22~, ./f5ўcxL8tg#dXx:Qiu"Rѓ*V#j\6)ˈ6x]>Ll{hYBEk(m#톋J4F{,Ńjr,8[$DI#<NLm1_5'_奿MDjtӲ')QtWUQUYkLiĠNrs'v}=dG(8ʪx2v5Op)W-6O2'~iYYtͤHqX[DOk?}Gѿ(>/W#%Yi5WN Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rϕG@*H9>Us|=",TzDYQ9gʣ rוG@=@rw4 d'-/OMO~mY"@\.='yuH 5[Lm1jT֙rBi:s&dXQH qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾o`S7~qyN1;wwǘM߾7ɓKm(6|*eZ$rˎg92|:,sHB_^՟'+t~RwP~)Mk@]`5 zDVlٺ҃Q%q0uuuu z+5~Oe%Av.YI`0YzqpR)si*7iZHm;4ď=@;4ď=@;4ď=@;4ď=@O{uU(RW*8mқ=K "?  ]TD*u>TXyuD2OzZԻVEFE:<$$J8Y)^X练]7|pPY!,EtM?OW RǤ.)R1t׏̀cͨE6%j3jQ$f}#d}I%Ң%$H$ǙG ẓQ^s(A@yɤA!жl*##,ƣ~ !Si`AJRF}' ?k e52G zѶvPI,DDX. W?Mg|H_!?hQzzj|]SjiźTq;˨G?J@~]5jgs .k~.pC}& `;'>w;cb3ɗPnbLx:YaSnɸ-={'< 63QXaUa$-Ig_6j߽DѥwmW;Mlvx;k0niIqXɞ+'ćԓ"g ohpXe8uՐBsȳӫKiU ҹ8ykq.^|Hϖi"[<mi/3秎o?7%y_ w>ZoT—TJ7A/ccv9-J=〺rw4 d'-/OMO~mY"@\.='yuHя˦|mL.dUBIOy1ly!%gtyջ;؞߸/^vn.TaM],62 Jelie8RT^##o} e6a*oxMbmc:BqG鍥oSۃܦ1죚]=8 ,sHB_^՟'+t~RwP~)Mk@]`+kYkSeՒm)W;2pJIx@S޾j9iZKuQnmàw''>Pkc;T5PSNY::,g$̸ JhY]p!, Y@AFbMlNʝv▯ȋ&[dj6^S )) _;it~7`u{On}-;~BTfJME*$n2zvE g_DaSKGW+q-0'Nsuj"H4c̀Ey,ס.rʣFX4*< `+pk3>$/吟y(=5>WgI4rߪT# SF?.3XٺNi7HIx-/@~s-ufvU*bc4Mp<xFX0lZ==$Kp[G ƧRӋ62OΣr8QMIr3F{>mv+t W~ =A1TOzPZ>7[ mi%DÙLJjcD %iYc>y˷ lK‡>& bѐMm%.y˷<Gdۨtд“ g_DaSKPd(cF_Ġ8 W?Mg|H_!?hQzzj|]SjiźTq;˨G?J@ $%XfYt~{bLڄL-^J[&f"lF9zv4>Tx"nmK[N֙QzwdJRxetU2m:Ϯ˦E7RڍO"͜uv^-mM5*_8;b!Ai&8utNgTS|/JNꞷ(<:E^MGYi$>*:qjrمL۪K2 V%W}#rk:ok;=>6GtTU"3;MX R]H3⭒x[` wވ:Uj^mLmq$Ix@FD;D;DQL!rMU=E(!r$6A:FYl@^}mOmOmO'Ptҟ"+csz\ynTg(L( *}|>­\Ϧ6GRҒZM&eڔ:vؔS4[$;JI[q6Q- "RTA U^gS!Ϛi5'M9yxڀfI#3<q30q ڒBy#J=〺rw4 d'-/OMO~mY"@\.='yuHⳲx|$>P՛M-"S0R!̸귈#`n;fE99cjE6FTE/av]5dI#%82ZبVcMBi.qT%s7~ӊOhwjIԍO.=AzإRO qݗ%mDn8J=#'џˮ|mL.dMCWJ(IxFɖfY">ѓT0ԲJe:{GpIgӿӧ^gO߰[z|R2syIXǶpgO߰NN{` 僨MJ=Pљwry4{XB#PuQ#פHeH7&c-2#\DWwYwӿӧ^Vt)G:~žIkre{)K| 3nN[Iu It:ESNDe>qS}ӿӧ^"ʎ#B6rZl#R8 l=A1K<- I>#;}ie"Xy/$)q G\ ᶩw];U5>\ܺYNO)?lTܫS</|ȭiU:RrdR∰FfxLSF s^eiV̏P__PG& Եqɨg*Y)^X练]7|pPY!,EtM?OW RǤ.)R1t׏̀SIa?[EuM=>+.ŨTX͔FrI>!9>ɀJi%$I2t۶v":. 0$[ 'ge<( ' CiLX-tA~\g`5CMjF%n! :JQRՊ˶\و3&VRe{A&5t]8OW}{?uiOZ<^+@ڳ$Eqno*\zNҐ)Mxڙ\ -kJ+WȔF}oPq/SMTs U >n]Ԛ^ &F! (j]䱦{J6i2u~!-0u X-Jy 2Q]_g; <܂m9"W!i9{A&\@~BZ}FY973ug9$p'S[\ZS[\ZotR eFDX, @ru."☕)BF|O&G}{~}{~H5qJr]7a< fVlԄdˉsy[7f]W ԭnSJBЌH%RV93;JuN,Ԕȋ r_`r_` Ⱥ׹f_%ߘo2.=`7%Q"2eEnH좋8/8S[\ZS[\Z5ӽ,isQKRR&J7g%ä!u>|?Ib皅5Jt 'uNw{^AHk7%]}FinTsn3J&<@K~Lo?Uqf&@gy748kՋiL< #<s{bA=tYU.}N$"TLVi2 ']}wL" nZ̷%c)-_u=$?̣LDJڭTYe*&8B%~֍JHĩDe##`, lS)񡶥-%g$Fg$vzk=grޯ;9DMxѢV^5ӉIi>Tr8Mr*U<+;J'8$\U}NV6;YVj<ֹ"]>oNdkxlFKjz6N/'Н2dgɒUN^Z LgSfj?p`-oO]Ҟ!;R8KjFGrX:rh5EkF2ǀkx~L ՀRwo.?#BY  GSzVdH--KI]B9R4cS?˙uPv J%\Ox"zfJű\xdaꪛ)R>Y[ߩ\DLvˑ{P/.ҭ$vr ndκ&SmkSYujnb]vD=D|hK8Kzս[iq&еVp^ U~ZfӪ4%Yk}MȔwees[5+E+(jE«͵64s9)yiA;.M3he9m`i炷eŊձxֻdV_MFA:Y%s%K82HTUT9V6Y}o;:-^T2'wc=3dfx`?@_i؉Ic.sgSVz]jZz-F\7dVdMAÂoa͜8 .Ҙ"&z;I gxg).=k@;k@AocfZTjƯ6t52#p30Bm\5'zJ,W_Wc| 3sSY? sĪ깟_ ÒއPJEN-SݟRj41g|\OW(mZ)yAq2K'"M8@er7z;f'f c]?(C >r_K r5Z\KQY[&"RL4yf1e.ݍ!l@B"%+*5q"hW)7mcy]T>|Mlkg;]>,s>8 ,sHB_^՟'+t~RwP~)Mk@]`<&RiLemJ!I2;J쫊)VEF ak3V*={:'^1ZWfˡKJ\N2RqLJ fI X-6%eEIiBtF~|Q5Cq\zƩ99iG*M*f8Ju*)#IReQE*Ωv3MlɼckO=N<).4QdnFR:3eT&ImhHa߭~Z#Tտ>Ja6IQ+p\ io7⹦'KKwjmHMycu::pT^D̶]D'x/ %8#!uZf*w,Zjں2Maө،ZByimp^,gg"gn\JV&$Ty;ls HQphvZ9LitD86SǠkA~W_/ihugz_h^#NmVoI$~r Wh;jvZ_h fٴ!θ+t)U%eđwlV &p]zK[v@]#c6) ehJiQe#, [vEm a% DM Z5RڊƈOF}z8 W'~5m׽=jGDJiQFl]mcvf]D"><zk=.-_I>k':guEIz!lQxLC~Vi*#鬥-l<`ے]eĺڸhQdi5RföMC;9'DI3tt UnJWRZ#]u(gAqDDIjMޫŬ)SOg|Lvc>u2mdVn󝝴^Z \)^X练]7|pPY!,EtM?OW RǤ.)R1t׏̀SI|RIi4I>2OrZT뢍"-/BH.$eiDZS4̺^.VÙOCg:OR$,wJY.$]#_E):8K<(;+>-Sמ{V i22ApDzs¸HFBkNqI@S֡mŗ K 3/RJIO5׫VSΙ8='(&ɷ+[L\H56Y(TII#ɀRoʔqN=JfkFIxOvMuɆj[b*^'ρі0!wQ=*?V5;N'Gƾ MݣZZE?OB Ey#Q+%O' % \=NG` /%FD(;u߂c_xN*W{ϡ.BT$0et&ffx .ONR)1[-(Ԕ츤p3d3:5m023/T>PM{Bèˎ̫ɞٰfdHpӲGw, b^u@AZH#2uF[.Pm Ye5/#[V-ƶhZiT$ddҰdy*Hv])[.׆jqeE|)I?i@QV^4ӻ|I?9/@sfPkS%HY ٙ%JrŝG9v %A+{Y.>H$DE/9pOh_h'f'_/oPmޡ& u$DJJ=〺rw4 d'-/OMO~mY"@\.='yuHя˦|mL.d j*/6n[ŠTI2 "֎B5R3:zYK}ce "2q)]|_5,m6HBd> %e]svdzy.OZEos)(Id8 ?(ü])R\# QUhݡ;'Y3?w{L+98egOOK4E(0Pf]x!@~u 6Z[m%)G"0˝XjOm)J˄|5Wkַ{[ 33G7%uN$д(J#,Wh?:D>i9'3iRJ5r4'k8.~Ѻ}un ݳ\}t:,*R"K Eќ߀j%xW Kp&F8613y +("Z?=@:(vR*>ȗKu,i$dm,̈G:n9& *2pd_!;*-cVۈ=R~ \[R|jt]NSm>ʌ 4],;X5;lY^wQ>kKR', LUtԭ+Tn[$HޟBקr\ѦF}(KoTyY *(Dxj{FHR&m0/Ϣ?w3跾O}c}5z*δsRk)4*Sͭ N!9'a^TjR.Zݎۅ(Ni.xzK;uZ-Hgԧ*ޏS )I&̈;oS-:=N.g dKZ5mܚs\uaO:j9lՔq|@$uzp>;Qnva8kVP}]|;)$$"Ixf"'aԖKAxg,tQ"ҭ 3Ay0.hEhQ(q#2Z@+Q@I_FR@@X\ [u8u ,K2"3 Kr7 E$VfkUQ}!Jᬈ%O>Xum(*=u }m%d2 _R[e*ЂQ%jW|̖d+@FQ S743$dKz[LH&N A+eEТAԝCgNiOC\ҨT%(Y'`y:KS<ZmQ?QԛɽujEj3nѱsoW sΞ _I J2 oŵnT]ZZ,ȍ|#;=ǡmC9\ ,XQ@֏6;N"4E, 6m4ۍ*&FG}8eBMXq&7)w#36#%h"8g)^X练]7|pPY!,EtM?OW RǤ.)R1t׏̀@Gd 1MmWn{kyww*p{j}e~ƻowXqd_]4n)IN}C ,O'.,Z[emnx]U:ݥpZiT)!)QI1Jm#:ez[\=9Kr.1Q=^}̹g}7;q@MSˡ9Pթ'T5AM}9G=9垶Di4O-K{c1*5d3GRRKpZƣXXWFҘ"&Df\:SZ\gLO|(+fD:gnj>.ͥ2[)33ʞ,"7xvGd"7+??OTԧ o$GÉKt[Z{*&2^'6i*zu%7bP['"LiZYoۍv3E}'f"{/zRq&l])Wx&VVIcf:Ft]ᑖ kN8ԡ{; Nt0VLL%%iK-/|4砦dNy⑽ڈDTݵ茩 v)Pɤm0{E.1G0 HaܚSIbJ>'j )j2/4pQ"6$5FRdQ)I| 1{\Y~~#bW)KQD5J^&(KsOtQ*۪.i1 Rkhޱ8Uxi5o@=ǃ5wV>ԮPgyX5)\rTn4 Q09̣FL;:[Ɉq$ZxJrX2Hxt eCܽn6mlUOjvkw36vKcóQە׋n+AW|%-\h'%L:JّeSO- 4R%@O{p\ZmnICNeF/[XCH%d'xp%boGQ-Z/;?/G1A߬zL^ZCrejxn%)x ]w芲3j,b[N-S՗7r\x[&,%$ IYTګ NxCB^5[MުѤipJTPG/ۋYD*ܠ98+gkc,LŝSO;cSg]ʛo3q\*YpbvO')ޞ#$dVssܦf,ƧvoUģ"aҕ.ɋ%JkgLD98 z)ZE9 P\b)veg{K,Wvsi.*?|"1tUs1%l4xsh51zŔL\ElIV\8kUS2UUM6VyU=yE.fDFi2m%T pknUD} spF~?bWz2p+QLDO6sim.L'i'M5EqƧMeT2ǥ:R%Iddb~Ϭ )X@5^i ֠62J|fYOҙ3BZ)X@S>Rkfs7-RyIäw+Zӫl7d܆HW',wTHQ }$\2i];0rRX#xVWDZfɂkmPdeGIVj3Mo)kRf{I.){/Ϋ}Zik:==U.y-{ 3GZ (X7l;Hed~q{^" VV[X5>tcNS{Jke'󨸀XS. j L4oZ,ңɑKH >J苰k&=4m'\)4(vTժC$dZO^#P_MJR˗;''=񚱌dp<(6؃;Q4'WY#?` %]n*6ąSe6 ;'q@zݴejL0G̞OWPo&&ΆڦM6J _FĐ}=6őΩRo?[\h%' .<iƖF oæD-:@-[WR?h;[BЛَn%oSxrRԾOZV&!]FN+$M[ըPG([7C}8<V@-zvx7S1.Q:^ ֬z<@-2,YMPlunXi- X#q $JI $E1V_0#̦C.4JFX0ʞz5IJ->ʗB GF1g[IqjLnW]QjlNaKdmi;'?U|;_2ʯ,Kz@Tk0K8_l/5O?{z%y7s`-NR7ě gf:3fe(ԓ/ :yrYD45\HeRwo.?#BY  GSzVdH--KI]B9R#<@9Rʾ*y[5iOV,Z ayZQ==Zy~6\Jtܺh/B[oTSi%0Il`:STi5JϿ4ױInkkmm6|L RՕh:í5(JF[Jm%}ZX$MPԽsRU=2YTI9g=^h9ݔ}OiGR|O|t8ƥe5^84Nn [;yQ'tQj=@Qc9I+VjnCeu[6h-})#"0*V %huo$G,n۠2XsZ>1' !ZZ=9ɒYkrC90ts4J[ڧiS*CF'%L)2ieLgv ʻ֮-Q)9#XZD$ϥEE8&2 QDҬZ9WU*S$mAw|%ȱ,7[i8/c6[VKک9KqO4ch\Kշ*Jjف0BCK2Vm(g%X!}ruQuMӥ:E=I) BLH#ʪYuQq9 ֊ʧ1ǯ6Wc9DF}COgTjC.5LiJRv||1O~pinj2Id)9R[u̲Tj+hV%ZY-U:fJorjYܲ(753F\4)1`YƼ9`KN뛹sĞ}OR>krߜf*D2(yA/ܣs|GM?Q ~EZl[[N' R׻B*%|`7:i?;4N'!pd{Y쌎LG'")UJ=1蓑&;iw:%N`e`,䩦D-tGHrfs"0'OZuwUN *W:q$&\Re0m-aֵ+S) \ZdEIJu& 3,|H@JkAEp*çMAĊY `7V OE4o[*:kDG;E yn16Ð_JlYe>֨QN]P(%%A'==@ 뢗u忪UɱbZ87n (A4z7-sGj KQbɈN fk*>Lj KgJ;sUXESN:ʟeոIVSq+ٔ^˔rB6R$f{]@FLU˶ߣ>7A(4 Op`:btT\dl!$ɟ5uv=T13oDʊ((* U[A(MaE#`.;l,%$}G6]mª2Iy֌8@ JL7+Q 9^U:koÖ5UoB+\je.Jvj#KBQ-2Ի;.eԞY?bW,vjZs!uiw*˪#XG5_b,C}TM sd|? ^Q+ΕQ*3ԸPJ=#=&x{^x^ȳn;M}zt05As-GJtv=/TgucNJZ{fzF"VOd? J+RndԪO4ҔMpi"Z]qZmHW)α:2I'$]`6/}#v=/T2߸R> :'K-:2^m/GB'EeJ~ݰ}Xm0Hpi>=QiU56UZ$S{;^[}đN:PY@-OԍCӪl89FqN ͥ!,^S0m^ 'ͺQ輪ΜRSM<8{2`-]iȪ͌WBC{&Ղ>>=]&Υ{c'B"` 93=xs( zOA-3k<|UJsukF3H/VşOn aڪd*I{RRȑџ{6ܟriSh[*p<)20KlHξ.[[pȈGct]@9fGą??u6?Ƒ\[.[JsJh^>62G[X^놛Ŵh[{ݒ<&z}BӷYr]}L6Jc{VKgg9xc'>]G@(cdr32'Y)&eĀ{2?F2r >iRJ&fu6Zm(Ia)"IQR֭U5O:5ɏI"pe2 onD}f^,-ۻ:>|d^,Yb7~¼zU vz/xH'G,2dFf|(2͵imVr?WMa=^M[)%&efmkΞyp(w w'tgҥ?hi9Nσ78W۟{ѹOJS<88RVP {Q-(oi+sigj-zU1gk3HB #FdfDx+(94[BX5aڂ msda5+K)6Mf.'wOeH^fNskR}q@ekvtt%ڄRGIH*^zbw~z8m(Sk##,`ux,<@`P mR>.ڛ0$e=_ͨWM5\7 $I-5ꤼ]QмsL,z{R/",YfJRIe80cܝj"V( &'$JZKd ɃMN[W~>Ì6o ? U ԛ^C\I&WFYtr72w2D?)m",rgj+.TTpLm4-$GdyDcMm:]zhL< ӕDd}d`%ԛ[Dm-j&v;g62\4NgH68&R-\%>cZaRrlk^&5 fsa*ɟNxS\j]^rѢ嚒3soe= x˖ɐXMn8R,B&랟1 Wx(-&xǀ9,kNH&2ڗ#mOk"S<'Z۰jb5"qJIGy^WRmz}%M+43%kJM%SĒʺcXYuCqrůw fvNWj )l uJRS9||ta^oVOkoVrV{"Dy;Äx\~s$)ONKwCǚa״RDOcjl8֞*Eܭ}6_^C7&q@:ES;_g=e'l%><I"ʟ|e ȇ-d-eF\<&mFӺE.;qdM' M2fiNˊIc=IFrSfƸBKe67{cNpx<MvכEJ.V鱦%2RXJGĈ!z{׾!Ϩ%'ZT滝1NUtWtAԚ%;RrK&}'F1Vlk%FAq<%֫ xIfs><}1h`婩֝]Sq6p&{p0k%ؗUDRd栶I kU&%]rթtuUHKjso}etݹw.zUUi\GIAgi^%ϺJLSxޥ*'6U@-wt Nun.Ц)k3Rmq3>U\zCd@vԵQeRS`TJ,nM+?|h.rӛ:IS4}fN& $OGäMji}I a*.7c.#yN dd~@w$])]Vk:P5즶.],5SV骏"TAiLrx{d=ܜ4ٲi-vYMN٣"Sz+q,#%4L,;]>ʁ"4&eY4OƠNbc/eI%$> +N:=g]\ZZqRMFI<3y}i֛ҙ0{ MN+Y>&`$ ?. #g%nZ}۪JTQ--'fjYpFFDaʖKt][+:$Q̍Ge[)ڕQ`iILvkڔҚy:ҺPID RHI%$DDX"/hmR&HMֈt0!['( 7)[n}RF\ݎ.di,Y{P*XMF^"4̈π EKmVظc\yUdj ${$H{= " %U!S*C;&턙R#,/&yywTUrw(vk%*].-XI3L%6R wkRj$?Q]TwTU;mWO*#=0"aH&w%#tV۶ck~P";+tGpUaoji-08)6g<36=E8=&<:MiřIxyMU*֯i4 2$JIfupWѷ#LCéIng?b#{%$k] `J4gMD̩Ĩ g%5J,+d ';^hRU9R꒞KNc&(*i 7FSN  c * ; )>HtmMnk5F)P]ѠFe.fѧ * =-OTܹbYwci3TYq劢_'ddrF^|pXY!,EtM?OW RǤ.)R1t׏̀Uv]T:G5icB\h>[dj2LUl׵OVy z{D^tҜ-*Eøy4jbLĢdN@lzKIkRN}D_ɖ٬.+[( o3".*=&rkm*YȈ:^h3.H*+3c;CHyRh[nR#/ Mы o>)'-2, t,6dezTd"pȋ*#<jH#HQ)qY}jj#)m*VL@_ 5wLS횴;[1q.g&H3#jbc9Ư vUJ{jRi0vKZ0D1"e3i|(Gz?)H\Hf#|ϒ#-hyHˬ&"c)z*rs.fYf"\SL`[B 3tLQNÃX}'(jt7]UIXdX Т>y2mՂ, \t*29G^Suri)OSdLzv6ZK]$&f^oim_t9G<[.7s8s&̑+-ݎF;$EiYWic4YŞb_o?oRU1&vR:QCqNL nATM31+[OQkGJɔmN^X<",CGZNY,ƄMTe}s+M)6guAMA~#iͼ񶵥5Btw~cܺ{nĈ,$aE~R&[Bͨ8[lU3< /]٬Cr-Mp\0m'ք!i%%EI33(g5@|=bD_t C,ھi1,ZKUr{)h֔*"2"Ix@]Ҵ§zCaqXў~!'SlEfKScI1!iI(+8x[]ltLG9JFGd2.2¼l|*1[5Ԭ|oDb6Ō9=ʃ?N7Eoi)^mÔtdJ'R:ZiF4r]R$"c@~aFTNf sJͥ0GRfGl<@+'z-:[]M%J(J<ʤ{z VbSrh_OP:/:6%:ᑨ8ĸ$*A+T7%OKJT¿ĻtɔeqDt0\SZ7kQ DY ѥufUe9 N0)Ҕv#32,;E䭡-k3աPƍ{8N8GFHt%[-:\ϙCu(Ӄ`-ovJp{/&]]JNӭJRH/ ) 8XZ/:u֛tg?',wG>|{+y֩ BۉV![2%`ly&~Sx\,Oqۇ ӇңH@iQ#bЪTQDAOp*ʜJ"##Ќ NC(uVmi%%i2> 线hufYwI(Z'xrȿTg`?@huTo:ziP.-~;!Ɉ_9pa< `5ܚ~@-9Z!n$&^62QM8/M-C/9);RI&kh|8vX@VB$ųZVU!I=(*c`#֩Uz^M1U !N[jD1wZ.u Hf%9B=x#Ok ϓTC2 钞i+v%)JL˧)x<숬e^Nӈ#<Rd_k+Ov;.ﰜsw[k .T2N]gL'% DVXM`vK-8S:#P-K{S\GRdN‹-6ҫ\Z|cz-!S$KpEA>%kR2*cCk.'=@?LilYL蝑6&qxt NҘTknC}[m,Y%%v:$]YTͬA Zs;i\@m_ܝ+lۓLBS"g|H_!?hQzzj|]SjiźTq;˨G?J@~]5jgs &Z#Pؙ9^xlFs%]zNL@]B<&6[[#3/4Rjn cϜ8nȉfE3xNbΪCn9ܦb&ZެY$gZeΚw=p.$&dDfg/ juNb*eA3"ufG@2yF6dy$6I|K=v#Yt2Fѯ]vYl`JqǤ6XlƩ5r;dHa-J8hV5puCKZTvOSTG4_,)\gTLzC,Z ^ ӎyfEvLNlf6<@zKvYJQ%%33x`*nGTXmy "Jχzpӳé%(&,CDT\uSG)DeJqt? ?׏ՖoRu[s+RZqEjJ$^0i}^6L@9M)0vi^O&.mqлGKa.Cq=;F]~ [2%ImoL5'I j?c_*}<,S$fJJ$Y#e5cF+|N$7I̦[ 'H#"s*Kdz r:TMn+1S Oڑ~">7fXO~p~\Vki&d=t SnCmCeiKBBLsZGX_HGQ]Jl":C3?#-]i !/Fg uEE J;&Y $#2O'ӓXIwt'$p*8nZd2RjJTÏg C `HJHGz,tT>M6Oz˪n%ե&|vz +͖gS2WK68FSGvL̸(xOk'MMZERv{E8hu[Vrec-n,Ze_@~^{S,ݽCɞn&ٯ*^H7Ne#~8dc\SYUNLߐ[U\pRu~*a,9:J'N`e_@h=DKVEPg"(JU!D鶝qxJ@bTAJe^IYIt'Ȁejko{]kTTV":,JF$~ `UhTcB3 3ɐ?MW:ƂSr`;[S̥ŐH@nJ''j\ɧSU5n6?'fe Wz8MqWЎQ>c*TPf, RsATQ:8i2R@0uwt7J7RګL{NUrhH4kN7DRji(8e[93 b jƙUWI*]22Լ3I  tj4GhqhM KH"+_[AqTR>;mOf[*ߦu'>"*dEp̐jiF]$FX?6\=<]z$$([FY  jui=` AҦ<Wɜ5v2.]DJTdm)5 5{ KZR)IdJDwv UZuA#hד#,d5Z6(Ғ#>@$bTΗL%%ڔI/i; QxX>1rLEϪS awl`tE ꕩZ!iļOEQtj_cwz߄Q( W%ڬ=1sm)% Oamvo P .u g5$c;L_!A>ܝu #4G}@;[8vɾ:^wk#dk2\@HRwo.?#BY  GSzVdH--KI]B9R4cS?˙uӎD@%g*;̳NL,G\Ѷo;$՜x:@JZM*"RLde26z ˒3.՝d$x."ڤ[Sw%Hkk"Eq[u:\.3mDp0"^-,N-ɳqYVh,H7rl;.p.9E=xJM|HH V*SKffjI6gNIXV@h=wvanv)rm #'MI"³\<%?.8% @?@4u&t)TIqex^j*-? !zD4L:,4D[lC40ƶW6g3_Lij7ݨL26e{]*">6J-*s%7>ޮv1cU͟SvIjv&>3𙌪(Ξ-- k|kiezŢ4"ΘFy[Uok*?mI4~M; ޙHiwsf\k]fIy1-0u#usq\O&9?0=Yj2Zb7yր9}Gww pI,Tn.Egl zPd!ɞ0mYQg<j8k-,l#)zZWzvJwai\rj|ԲJȰQ"żZk}N\DɦSLlh3.<TzV37 EJ4{tSɺߗ#~#KjZdm'ЖաJnFtԈ̒Fg>$ɪzf[JA-8Q`fHiFYr}@/iȭ.lQluK$Hm;9g`W*äQ<"%HAxI`o@bU.}*lf̉o2gFi2/ Y荧 ȮFJ Ɵv=n07Wu޸uBǸî"s.oIN Idpω$Vg) VNoU؇J9|m[MC,"ڭ¬Uey38Le}jPƺ%*NtՔ׀1^TN|EìGLr\xdFfjMsDDJ$fdχD>4l;iM>]BTY#r'ZT8ʳ(͠FғuTMðә+(YO{Hz~2-$:c.lTDHu][e GE-w:fJ7UO8,Ht[nܬ&dLhj2ⷪ ?K]Yt£-()FÑM{;i2%Y=$]K6]ROLŧ[4w$iX/@Iuj?RzdAf$:ӊ`}>{i^fbBBfm'6nK4VB EQtst vx{ { uֱIJ.Fdq><]92;c;->0@9;.*T ֒ սḙ=H9,֗>esI%p>< -VLO$HQ(c06 EL6L6M2UIIt@lh;֕j(/)D[3.5T9b>Hkni??+@L[ɶ٩ښj5xN@Ϥm:XVʜ3I5m6FS8fd"6ԑ }KjSmjXw]]w*dk:N`Ucxp_8J=〺rw4 d'-/OMO~mY"@\.='yuHя˦|mL.d-dYh'B9m +EyK Rs""Rj$²ۮ%(Ҝ<O2^Ժ X%MI7L)i6]'_)T e6ÐՔQe$f4dV$Irm[f[of=Mq:FD{hJhǧ{Mi-i9QLge3sctȆgMR񲍲z;lY± TE1 TgXj;FhpYI5m`ygڔKaz:e&frC[D#+8)! h9W&NBA(%OvɤyHĢi UHZRj%A ifԣFjddgvs@tΜ醞ڎ f1$T#7|{D+Q ,b,K\\jO[-F_( $zb[h . ե-]kPqsfqFgJ:v:绪4-Fe&Xχ_:{NeQfZ jx@uM$kO  |Fbl OBRE/iWM-Ҧ"tSB;.%|Sv^튥.<ߙlS~٣Q`@":u,T >~ &i;V˔ԥOHL%.l' `+pk3>$/吟y(=5>WgI4rߪT# SF?.3X d[:6֝B'ߴRg Ujo*K0Q)L;2<E'>6wɨqӾNZpn 뎸k5-Jm*Q}30ڤƦS#"e@g,z,ThTL&",~U\h+[Ԙul%q"D"Rr]!M\j%9¸CE,/sL.=;+^ͣԺLډ$EJrYɻ{r떓'2hO&CS!R鑭1KD.de4-KeYL]rPm* If6לJMFQtɥԘS"O\iLeDJ#d(%^M$Ь8`S>HRmNVs2J3Lj],ĨQ/Wfsx~A-xd\/k_H(Zu4S"R3ÈĦ)r[վo]\i\$vo$H$''{b.jOQmKҕz3PvDnA|ԃN˨q. #ZړVj[f[ZhI)mL}`9^y5<:c2rYD'zZT^ "եZS4:zoE=SڪYys~揑"Sޑ;{}8-׍N8^׏xlcB,ёqZTڙ'<Ѱ~7-JJ .t0`rY(_( zǝ2gQebsCioFj4Hw}̐(5Dwb7J̒&. +4)ΑslS\H$̈@+@J*)]tS_eV~cqW%mG{_Pt􅫺qFN2Ry.#V.@KuVb /SeܦqqQMiyŤkA [͓qլ[隊L6mԖfe@Ku>I¸[W(8u7͉R8{NgQiWEhSOj4\T|u(-p)7VV)]d,23bBqv&J|`+Az죸¢Ɠ(pԓ'R2X kTFZUm&Nnn;=9>ЫXllgبJhKqV fVRǀq$j6Z}GQDl%fx>1%-eķuլ@&*7Jaե_ФGђ)-N-JJRjjτ3|UEuv *NSQ8YiE@jﻢtvN΍1a0eAa-%&.=|$ŧ&hShr$܊\,krYrҘiuV"XMm&fyr2,1J]FŨ3 ǒmSYJFGì-RS(5j}%.9hwĂ%#dώH i 7 ВJRT"  /JkCr[nTiN-g.##F8ۺ@k Ov|9҃^OF٠5mNmub\JMFDj$24-]s5Pq[Ʉ{hRd6k8;=vFk7ҝ_<'L~}Ld tŸAnsIjK4C*m&Ef/]ZL)BIޒz|Xb;dГV>@k^):]vL@9&cisM!f:qaVʶRJ4yAeɵUܖ?;EhIy)q՚/`-,s>8 ,sHB_^՟'+t~RwP~>)DdI"əBMj5>SwU2L$`>N$fdFx̌@,rig^P x5dsNDnSκPT"#0+.mYevuymY5``,@~]5jgs .[3HISϝ3tkqOnWkѻŗ6RҀ"ڡi}irȟCe'<>٤S]Z\v6chQ2hPi-MuDf6 /+=QmivPV(^3< ֖`(6ֵeO6yH:|kɤ&ԘtFe.%($ˉW{Ƕ&2c3]q+lf@ rFU8lˆٺ&JK\LU+ĎʬQ$P^R]ʭuu)kvqu Eޖ.;J@aݷJ7c4FLT35Lk5ӉN(I"/ TLfQˤVV+볞[9@JfH$e9<`hOM,2GԧZW] ,p |(THnJ1*VyM6 ogi&EgG&7?Q_=8"Xd* {!Y|L%}I-;zUK.z"Mj&gFDO,;vEu3JBA6IFt +E~A4E⚶f8 y?IqYr]SyOZ= W#T܄dNvT"JxS NՠPu{EjM_}+[•Є^-3yY̳/2p6V}(ɞq +V5݉TL"obױmg %:2Nft[5Yܸx|1*ܰ%%!@zC*RvKĀW}bubmNQoIyiZFxIF*elG0Nm ͼJ[dEyy;N-vUbL9Nq|<&+^X*!-QVrɬ|$DjyyEnekvWLunNԜEv:r{ uŗ`_SMЋ>7m6վAg3X\*6LWwa28*-"jDe )zBPD GxSn]O/盎$/吟y(=5>WgI4rߪT# Ddd|Hrl%Sar!H읥HJce)O"r͍)6nJ4R"]#hLZdߚO`FdF[E{rF~{Q L}F0ۈRv̌|8euNv+]Cˉ }Iml[$q : U5put=xS<}#SRUjy18Qqvws O%JM$e.[RM5p['K &O9mTc| {' 5xړpޕ(CEv %'q -*iYzVTҀBRA6iJMr~5xڹ(]p뵝ҋI*fc?F궘ת6 d8_x"?881f40-,h*sBraҥ}6 ЕʀѴ$fg31:sw66]`>))ZM*"ROF-6[we2Z%Am$D@og6sL6].7-QK,NMn]^i.0&RM,gGZZգ;>ʾ4[|jERH:m~Lf&F?;YLVVTqxu03^ j;LgvNHVAȨT$"$(7}P8 .|VdǨFuq. IHˏ/z]G˪mǖFv]g {YUňR/e+<ݲZF-iG$FVo6✑H6S&![mG፦:Z4ȧoBNmUFђ>y #*w 9{vϿ*6Kɵku-7VA22#՝t^t,;>7fFZ%XJOxg̺ydj-52o'iN%,e^0Vfj33ikeѧZ(mBnTLQW,6KږFi:BEc -PF}3YhZxύ)U%b(ё&\O9/VkV**S2uo|3"<@O{/_!Os|aL8uNL Ydi> mN͡QVSd˥#DsN8/o@Pzy1uUX[5ƞTI!+Z}ږ5&ܑQ[qjJH'$1a՛9KyFDdAʋ+MdOZUt-QJ[N% oQ9HNFkǿaAO.X 7?^wݯPtL)&6`d|"\޺RؗX[O47%l\0mGR/U4vPOI.KNè$weJMK{inLv!*%]A(EdG9l*]>pno&D+$d4(F~.$y/ 48 S.tEfIDXlDѺIIx2 'z;E-)n4-Rpdʋ yZ ^ФP纍Chݮ.p3M vggU4շ:SMAӚ<8<L6쌫_FdHV㉀+"͋aQI2tP8OT:f X.!ܣ-ZEO\3yQ <fŦȰۨ.ZL5w۵6MqΜkh14ug.vٗ5BWTmGRHQL9[:xC_-U]ue':դk/xPٌchЂI lguxmE^]EzoHP-ʜd|=.jOQx4t1SΓ($}*Lk.̝\(`Vx- $@bem]BZЊrJT2InjsފԼS _(8p ߰-ḑL7#u-5Ȋ5 i$, *5/#ۑj&W$-'oEt jB2˳mCHy԰\C\ Z@ӛME TK-%H:Eo (1 ܆ۉ"tɤ<ɺbPki72QRth4t@[W-9$ÆESذ.*2t9l )25{NɷihȒD6\28>݀*M{%߳F*Rf$DEңyKAW9Zi1CڸtuJM9:rI%"H(ج|"IyC}WfOeʈ9!k$Q 2^i:өINLIeqbǤcdfGCq]z7u2k5-/P!Jm;$DXJX<qh"\F\'UZ,5`w`$َқ 2W/zAJZ^F˚Q[d$KemFe(/Obgȸ 9FHv.-$' 93>> :]9wsզ&[zl \"x@@Z*\A]Q۴}DfFUFfGqvR BfٷǗq5(̈ 4FFFM~4Zs?fXuVmA][v(gЖޝ +B=TEuⴵW'S'Kb4#m-6R,x Wb܋CR)o;qs<-ZUuxh! ̈O;Z vЪr4%emxqTO*X"yí BˏA`t jvYEe 4tW`JIff`5ejU(z9RTW"rFbR&"Wʊg8lK֏e]=uU ɡ̺JRٙ4i0ݭ.^s_fK#|ڍ|_<ƛK5>gq/e#|X 9&$C*D})22릻{Zcf]5Sq(gz /L-Z}JZToopՕ(DQ㠸!tf)O)e ëSdCo4 RJOUJ坥vUEvsb!dޔXF$m-J,x|4SGKkm\g?K_Pj4 1g|\O|˜lPBӹ9Py7Fi^R09hkyq^6I}f/GӶRR=DPGm ~]QT63O$DdA" 2ٸN݇M8k)l:Di2ɑ+.JeCR+er^Z IrS.(UQw %M&3}y>nIؘLhK4)$Lik#AgtS=coLu{.SX'5ʓJHN+~%ts+%m`eǤFX:tX՚SjԦ U!nq#)E?3unS[b'%qֳem;JQ ?G15 cuUyw hJ %$G&EsݹwU. T5U43(os3<@mo&RIOejݎX((NLd.90j]KޕK_xWPvj$ȌSI8JQlћSM0'R%А4n`*Ͷ&dfF#M!*j>2YBeBIi>enioR]vҥwhՓLWL.L/ܗ9+wcML񝮠.53Q:Mz&Bʘ6Vl}zJR_Z)QQ#Ukl6nn>Gg%CM5GuĔz9v3dD{N> ;~ɶ =%lVBHe݀<RRjIqOOG_C)[I@Di2ۣ:*'O{t: <v@A9 1WcAuIK;g3h-ڜQv}aO9KJҞke&~3<-ms.g$|a t{~~iOZ<^+@ڳ$Eqno*\zNҐ)Mxڙ\ P *ҧk!)+j3H7k]L_nۺVeSq0"t2##=^ 8x 3Jb;MyhA$WJ Lj uMq(-QQ' f#gŌ gҺ?w\tv,6\uȐxCYmsƦyӌ7qu z&xӫ6.oq[nFx]XӓAbUVc(Q Pi>r_w\=eٺnr[36g2,UˍWO<&fi7եuK:4eSSnLR3߫Vϋ@ϱxb7y2섺أTj6A'8gXـWQ_;~t˻n,= ѯb!NR;$2ho\|@62u%PkFd֠әsxKD6S'qޒxto.: Z/bF%6Zx=l(q ̑,-"RZu@968ݙJxI(|9XSUZ3yOOB3¶xp,@ Wo)fB; :/J7֓`{_x9jn~m:EQe;DZ 'IdVL*s̙iLFjmDl{XծźqS)(}ϥ ݠ2g&Wvhn"j\)$.'MYޱh^46vÖõ ̉kNpE.rt7݊ڝl[fA + )7@QvqIX^c'xw&8r]aj4(OuIxH }ͩh]t*;^]Z.eme;j{TȫhaDxRK' ¬j- gb؆o6)m$@1tۖE~ÉXa&}Eڂ(q AyB6\oe#>)2s M.DBk} V*YJ3vr[weY{JIȯfIG[I&xKOH oފ;uETꅿ:|S|~7EH RSqɲ'<*:iYXGՐܺşl}KY{&8VE$.!>nQdv뗙 ,ZjI"!U+a ].+Ē[gǏP ct4'GȻ&@TufnMDYUWYjQ0M:mdDt(It)T.+~3K:V,Q~R@8lƘ "3[f{$|:8FJ,d¡>5* Dxn󇄡$Y33Nƫ6B ƞl$d~  "4>)hbb=BVe]IM{jkLɥGJ%x367w{W'P.ƔUu&>*>/GRU]>#Ͳ Ԯ=f] &q9'Em(9x"vk,SÑ)Vʶы|ӃuFԹTK6&nZB&W9Vx2XvBy2٪3F1)1ӕw'GQ:NK5lS5&RJ]FmzKbOkPe5Cp⡵JzZxˊ}`5{P'э=s*"CjQzIc -9Nm0ВO@0/Kڳq 2:l!oJlLL}v'GiOf<{,`zi Ĕ}D~:Ԯ<9äT+aR=FiB[ 6:cZۦ+6X;* 'QH (/8'}|Tfvs+5VlK\~z6kL}\3gǢ7 #&׶[2mϣ}\**I/BV?/Ϧ:eӮ(\YJRvVq2g $#:}`:E5ǜo#iig.,D5so69E}h vKBb9$KaV^T}{8@[Md>S8D9QwpFRG=oB.J&m!gR$`r]ӺdCKxRO$x5xd[wZoP.m%cg.W}LjKF7SWSms ޱ@cLXLQRIį&o# 5 Wi%i|T̲}k䨥˸7n4Ԍd̐ ()Vt =2\ɹDRx<> cNT^5NCLܴIχAjKj4iDFd&[+WuQ"Eʛ8|X"<C5[Q L*ʄsٌ1ز[K9-dǠPM.ϊe)@5.o')~oVZy.+! t5 @ Mͬ6]nLfKX}C&#< SavޒԆ*UG\4+SI de-:uZ\&RR*d7! t)Qa$e1 :GzŌr o)/ >HiIr]tW"#!J76wdqIuK/' %]ImO)&X2> <"ZOZX3eJQݚ6$χeg[}amSifNC~1-kKeX ;_fA֍* ږ֔AVExH tVrtVrtVr ΕQӯUy-A꫔G] |(J[k< rhz17|y) w/%;}ܾW_`Meԧ]QE%{.<_%eYRHϤ^]t=#^Wk 2J< Pn(>Q@;gjDiax]/Rou$[JJg`\J8 Hxx3BG` wDC/6"z ~{?uiOZ<^+@ڳ$Eqno*\zNҐ2 YNԙ45莸Y6VtHԞ gZWFKHkޒ×'vR$-3$Jf}j#Ѐ z\5Vd[P?}9N鮤DiT51;'&62B򞝓i^/Vԥ$J>ôy #MWaE)n*j&]AԖӅ2O@ {L4+fLFGu:;4ưrIOZm+ZR.7erFIQ)Oz|@tH e_jݳwvYVK'kꮊ\TpNol m$Iwh5ט˹[[ή(m&(p" 6mlݮۉRM.*T NH=JxʺaEX0{lm$ȶz:K5J)ΧJTKl}2X龞m2[2pDdm8#h婸R’ڌ' ui"Vθ-*Q"Y?+VMX3g۬5MF5A:!H<;{FG{?uiOZ<^+@ڳ$Eqno*\zNҐ)Mxڙ\ xJ-(dI$f}&xuݦuHdII593$!*A < 鴩W5+ dT< mJ2yI9BLYQ+:Fei%JHI@K@BNժ6SL#q-d[ %cxՈrDBu me adQqf#&ǜnmSxF ]xU5&'X9#g&IR.[[\x& Cf9yld5wT,d̒dEljt4&:ʳBs5gd[Y/{MT5gF[23Il:]RNԿZse7|DF{?uiOZ<^+@ڳ$Eqno*\zNҐ)Mxڙ\ 3gCoj]JrY>fI[Fgo<)ޚ򢵧j]wt>bNFIIH Nk{X5 6Q%,ʠS$윈p;Im)<@70.i5%).3Ir3~Otʺ{Pj7n4$8?Hp\7>֧Ze̜qF%g\qw(;~҈9`%]|/腨Cb:=q"N$'$[ֻ@տ}kDSxi˹+UMF*إBԤe M))#/LiΥVC QGFQY)w TSbQ so QTA$gnm[j²*ɸk*pO6JAmH8U\zܢ_YT6k4Rxҥ Qth+BbO[([&dӴJ:a Ru-nj̋a xDEz oUgɥ8١iIhR 9Gh:T()fo*<$\L!qMK-m'ck)nj@]}{v%jjܖ٨mDm6xKQ犖yiF<Ȇk&O "f)W{3Rd6ђ`+ʤm{~zrm;: C\Т%$`Pn}ojjM- 5&#dHRIR Xj[P!ݽY.zTDNb\w`2ˆψMJ%jsޮ@MG08Mn#guwk-&'LASʋ%6㚒DYpY"dH깖P#pu'BkPFMa 8F^ ѭY]uIEoNUD*"g׎=8lկhNyiZ]'l-$˾I OM9NZ(T9SQIn4_aXt+TŽK59*q3Z*ċ"/W>y}?-r\l^LV:zt~iw5Eu>:ZvD+x<t Ej/]TY+U>Ju_k5'`֓p07&jtx NRے܉uiFGph{-1(Z[Ġ}i0ouJ֟yNUiQQRTxI0iTIuN?vFFgϓͬq"ZtϪO)2 jOZ n@`-kԇ9G̣L]o'QW5JfVq.e%O=?[bP>N׽P1@6y1tgLaV]"k20ˀ"IٚsX*Teg͕`ԣGwS5{줧>搖H,}򀴪KCn}_{[gNҸqf@=ׯPzL$$e=&2f}nQgalO$"Z̐E }֬Vz-GMvM尅>Q%+4 YĮ]fK372>1ӂoZE^WnR mc&}S֚_}H EM6n7!;)4`7u8kvws_p;p*,EDDVJvR[egk=E|VgTVn~<e 'X_YC0> (8q:V\45ÓP>̴CX34}.NoJ?M`tO3JU-mvi"R)lZ|Dl2d>0  @HXnEH}1@5C\r鍵U'Kۯ:dE.'0 ՝;sR)LQbqmmfgiUw욓(iqM+%m4&KT"SzN#VCr[=ʒ(%Ǡr]Ss]IbxBF83JΔ橍ҿca2(Á=Չ]UuV<إ6q> .%?j OVTIGr$I(Ǥ*uVoċ[f=5,Dޗu. ~ڶreÿ:-nLim*ڗJRTH4]> -iFv$D{m$X3 }Q]Rj4 1g|\Oj6*P΁ nFrȰFm@Vlʕ{߷HM[G8KSMq>A۪.v%Ju!CT{:H 'o#?Xu'َM8nV%o0e, >Oj] 44 Q2FzGEU/Zcu#*[K:_{Qw<>H =ZKk"٭"]Io^B4ꎄBSWlXWڧAM G>RiEn*zjCjO?У3#Kiu[ܤ&Ur ͳIvvU>zMNrR̓~r [[Js-ku{].џ iRȶ6ӭT,:BqRSR diNHpƒ6\nX8mKmDJ]fae|i}b"-**%AL>E 6TtU*F8Fml8<;hP͑R8%(qE"ZSġO)cSUn&9K=ٹ#XWuZF79T";"ɣm&Ìm= /DbxӲk,g |=ph ӝsz߄Q( G% X~b)RK32Au 4FjNuSJmi>@k\j羫?RW=YL:ƣ3T6 [̺.Q4V֩2J١hKdYL5aT)ɘu3dJҕpW=Yԭv}V~h-GHBԸs%ߡ2 ;|)r6wuh uB Mil'(L58V"RcԲj#fȉn lRN:d& 'wWY ).#3V&ωX lI/?atükڻJJy`F6M;\ >pٲ. 4zjmQ`)%‹vM5N2dRb=eFƩ2ֵ2a6RdG88uR ҲG2";bku2#Sm1yQN89JZ-_DZDt-6Bͥt@# ԗ,ȡE)8)##Ixrgkd*8H %ʌ֑&MVQ' x\IO|p׭ef1EJ`Dn4yo7m$). 9|n[ThHunKFDn {{H[XzrZUZ3d| ס6-n›&kœ('%ml%DI. `六։[evhZRf.>2Ny'Kyh%Oe{lֵ$j30{+Ob1KF[Rs9˓X7Ə[Uz$8駽c )pjաIߋZ\XuKGR*o 6_BW.5I}Fd5,6Yx5I>$r\.4vMRcDJq8J[IΥz\0MkYEJۼ`Rg\N1RFDb޶hIxv.zrD<8@c+ k)7Ld7g= )~\~m:iO8JdN}ټ7RSTDU8dj5oI@ \D=,td@yu@~ʺUԘE|?D4xy U~9~rZ>zΗ܍J6 m|fDgzN \eٚFeqM6Yq{g`#y<!Ck#XA!xx8 <¡Y_tÜQ&Ȥ`X::IvlR-աoUgQ`hg)O)fCikQe̸N k$}@'6&d5lRSN"{,v]pJpx/&z>5 P{^Z֏|+O@X6V%t[N;p%ܲ =]+6u^/'x/ʾ`otFz޵u- EM\XQ$Uq_(rIo]#Y)6h=^G4%^ٹIKAՓY[W}/pZ!仅!mI% .,s>8 ,sHB_^՟'+t~RwP~1(:S'=!mbR.-*KlKL9(tGfI3 SF?.3j>(rM%'p;E  hEm*1=ێ3HgS[a@iJ[QXCRLI2b,QҧJY4f"3RY3t Wu/h_+<nm{FHlͼ EE;c^uشzMQ䙓M##2,Ip P 7hPrw'4䩢7^"V2@*MkƑ8 m;F&TwD*6i9" }Wسdie\#ˑ$6Ij]djGRaUMv0A).[>ޭV=B~3鍻C6IA%m+)ʖtnu㴷TFI$ff}Gv(b=fLyMPme3OP Xڝ^I:Y[}Զ_AQAǷ%SK\Iv jSKfa/;x[ĀZu6Nv]RTx[DɑL1hJLFG@>OS5CWLh5skdGF@];iU\G7G=%/+^m?_8d}@Cu/O PؠsNbԲHntz@h`nի*7= 8<p zf_0g%x*#O4\'d Ie t!7VW=LI O +pk3>$/吟y(=5>WgI4rߪT# dp7,:ͭ3Q7g6\8ImYFM3" bfWMoeIƈRh6Tft`.~]5jgs .KM^c0:XJ<%KWO`39o hrv8p_nviRdm)+5xS<JC-EiAhvueby)hb.UpKtҊAHlzp>M3#=F ˣ]Z`R:sK7i6Zg?\b¢˧VozB3y+$xR5uo}Bn*SI%:]cm >4d>>d[qj3އ(kyH#[E^ %|5xʩP"Oj67if.8 k ;J'} 13iSI !Q'$< UڀbjRk GҕFFE;5iG~~2UNI#׋3-ӜפJY,>w3(ifI)fwS8GH Qt%YlM4lRvTᒜe zdz&mSfD*[qN,Tv3GO |JI%""#{ӛ2qT0q2nn+#}ٴdQv6m3@ ,s>8 ,sHB_^՟'+t~RwP~*miK-xfը$Ô7y(%6dOc&^$`K[=Ϩ{Nd:4MfXpX"<| -lY%2K,ʈJh^>62 JoZyu>O'&Ӵ{^ %@9uٱeuwӞ3Qy٪MflHm.fX3J% 5m{Ti1j=[1[yp>͚ ,Ej@JJ/kg^ $Rz!uoXV-ʒ#0`)RKL4[(IILk[' n myy*$= ,K= (-kFm2Z(OKKhAd6\$@5)S둑G+hԅD+ '>PkRnXoj;_i$KY""FlP}T\t7>FrK OewSU>2n8Hm+Z [$j>~mϦ7P-Uuڕ ! dW[ךPLV$g}UOZ*m~&=ޒJ_MKl{m:\tZ)u:G-pl"d~V"Rw\N:qɸm3md 8ViiUIRLdo  u3Fܹk iF>LDN X{Jntާ= N!h,sB+i2So;Y"#?Im+ -&$ӏ$$f@-eͫh AԈA!ZIBK M3l8#OKD$}F`'4W锨q")2m5fII3jM-r+UU:N%O ۀZZ}и.ji1ڒFXN<| % 9H UώI曎K$I-. 1V$=J%̛ip;UeGMhzmdR-XS0Y&,oeYL+pk3>$/吟y(=5>WgI4rߪT# ѩ4:ucv"usV9A$—ЕF?m5t&i23/2Bt2RFYxj[\)27cFiz2>`~]5jgs .ի=r*21RC 4y-@R(ͦDVZS>2JBRXJHD@9pյBu)|f2@:TcvMJZ ;QJCj%dq2gtݩ5+T&̈́h Ey/$3O|R$f`>}/6Zq&e$Қ-4*Pr %Oiddٗ}='Z˰Y[. FqIc9VDJtgDɁe[ ]NKLrS-[w1i>e_RFeI&f]%yFZyf҉jvx?iz|LY6b.:gKq NR ,ELWȘMmp35+{:F@@Uw'mLU6ɸ܆kd̐fʳĒ]> r-_zyM{5*JfLVoI$8֞'I5$LتqRɴԣ|/$UrNfĚҌV $gJ܅%6ift^)Q py_GJ=+pM=w<GQ y-4Q6DgX- v V5#Ie)%$X"+z_-}z/ꖾ .ͣ(6xUj5qh3"pJzKqnC'^JtmI@ k_fUHqh'VMbV FJrEz !{&#WIu W?Mg|H_!?hQzzj|]SjiźTq;˨G?J@uV)4Yer<\Tm7+m0aYI '}heS/&:#MbEkmQ[Rg5q3Jh^>624msU롩 Mq%|ei7u[nv6w; 2s94UqЫ`E:^#l3ZҍJ&D}AqTo4CTjrQ)ɬRԪRTJstKQn6 n(˦I6"ciܯ$d]905zD>qJIűOZe!huT f]\o|2R4f 5*/˫m)B AK`j8`BӽҘέY5[Kif|ItO4򕦛VJa=T(4qQ]0=e4kbۺUC"HJUY"N#&,}xϣ9vTa2NE˦VME -K?6ITjR_eg%wd%Y /{ 3mtNjYD 46Ye>h-mF]2ܓ 0A%D{d[>ۀ lҪ026&dzS4Bָ萖•j/TRosS಩%Seҵ ֣miKSvL*{ ĦA@BJUpJHUB1iާ`9-4r-UN B RF𔄙c\<@,[0Ƹ^}A:dJA%CRB$ޖ $`1w}=]i{im/i <@No=*Dk{Ѽ';ԅ\u5Gi5$S.\dE>0;lJ[L8d8Q&︟SOO4M*EDlGRJO/]l{>Rfv-r鈚&KeDH?U@Jm96n@SkPS{IɭD<cAwN[h߶2n YBIjl7#΅qRO1*p$/吟y(=5>WgI4rߪT# SF?.3X fZUtAZG} ʻOط52!5iJlȸY%Vϑɒjĕ-Fʛ&$Bҥ`,+2Yb)ԦQ7T[m%`oWXLJ&Qj NM<6O=;'itީsju Zpgj;QPOӻUԧkNDTJ5/e'3<MMQfj洴WRڸgL櫢ڗWW5k(sqie)+#/~7%Sq8(hI-&e"=Iu i'O-J.v2E5g;8vy? i-).e9-TJqn/32{Q!Q.XZS_iYRm(jZ.܉2<uK BloȷD!"ћ Sz7j-mq`{rDw ٹm=p9;[NRA~Kj"Qi̎)I)M p[o'mT-UBuS`>ӏ9"SԻVH "BR"m%3jGV C˴f= &y0:TjRA(ăBmxy8%@Y֭oSI*d8鸅Ém'Rm+2Fi}]~Lz&=EFM9Qro&$z 4yy,쨳gr~V@4Ie.Mhu#۝MXw@$]?f ^5Qd S%o(4yx@ٵz HZ0MQ\qƌ̳+=/OnR/IYW[5H/çH\Vё'_^kւGJлK\&V۪B5Q~y8:dn F_t5}Ƚt:e"J?dKwnҤz U[B9)lKV &I#;hϮ:>f4['7hdq{4PfhHSZ5'}:{8 O>ѧV4MnqRt}d~(hގG‫hWt5I齼ـWM\J3Mˉp524PѧJ>M2I| MrBzMrBzMr>z fܪ2zIim! IK 3kڅVu24^A91ЬvJ=〺rw4 d'-/OMO~mY"@\.='yuHя˦|mL.dSDߝ>Kq!_y[(B|f~h@yJם<ܼikV̤^".&}@0Qor@q{(4r&VF_eO-h ݵ|׉T:*t[^W͎lM)pw5daŅloo 5q+$+~S{7jSHyfӻ`KSKޫۑfFF@%q'R򏷯:}*Z#2;ɕvɥ %#3E{iU?rL&RPO|{#IcCPtԖ"ʸ㜦a!kmI[IJO*, +,S*E1fc,DfNxd|˰otC}g(%˱qOQlmg>ۨܡ_{|'PSja.Ж{S,D j%%-BK(< V>Gv^Keʿ"~;T䴷"K"fY/Xեȱr]SMe 5q2, ֟|ZGV=15,Ҧ 'N8H1WuҒɃlȸCێ/+<GRO=J'PíV>o:Dk4[7!nǗn|D\8 ֟|ZG=kO-#}µB>ztӦ߽H:R9mN]l OiŤ}PV>G+Z}iT#کp}QXc=$)I䓌cbU!]NLM!dKmsi5kr6CyĞn=xA.B θUE\H3I235x@ W?Mg|H_!?hQzzj|]SjiźTq;˨G?J@~]5jgs .::-Zj\G vТ}v&c V2>꡶B"z)m[TyjJK? -DfdyڂB ΡZѨiF7:ZFvHpϧ N듚w\TU_&}j"Q ,8k_ P%MFEA.7 83 \PcB-9+dZI*2,]ڹkQZUGligH=$ǎ8ښKOLKґ-6^Q-%2/ 4CnNHBn4%gG}Ǥ]Tנ>m*Wk/=+eFO)JdddY#Ay=iuބPj2:uBR:ryLDD@5~lOx@4zϨVMnimeRvBPQL=Oɯ'c)w[n9itț䵶E0rwI_?VW(5^ͦVJ4ܫx*9O_:@[jQ٪H>t!-<6Ko4+!׵D_|Q :̢Tnp⋼,dGZC?O骯.mB#:񷝳24쑖0et:iN+dglaR|ܻz OV[ݴԠoMՂ. k*u:E[/tw[$!Eg| PV3Sq[%Ś$L|Hg +]ilze2%ؑhan\Z6j",3kkR@ZoԧYBe'ɀ4%4½'R>kO7:[kBRq",K699GRX2tp0hTX. pK &$E:{\P >5&޻1m9H H#Kędܚx~:-(D&9 ޣd~ 6/;ƮЮU".J-!df- I#"e.Ƀ W |YrJ&ljJʔ3"7Q-(3)?tFO&2Iq-'Y3"KUB-OZD9,ˎ7e%s# ;+*S$NdJ$ȰYkHIJH921]]I*9J؄<&xdCIU@RG- ASNp~6r*M L.Nᅬn`{%x͸28yZ#QHf‰=\471ʏDGd%{{W>MtbW)2Ժ%*E[f4c|@\o~ -+b.AuMĶI[ǟld]4[BJnMzr(RgR~0[mTj"Z4Kz+aHI <8H0Z>Em u K5džZ6[/85+c{ӍtgShKZԹC[sK0l>L>R V+(%jTZOJK"0%h ֝JwVr)%lH̕ , }rB\ʼlH&ʌ+QpkBXf4͵Qx8uP Aoj*JgRI Fh"2EZ/dSZاO-:pK) ,q#oHQV")6@?&i#>5e"9Rɦ˾YgdϠ%\֊Z> ТÌqieı/wŦqH_kҽF"JN)Y)w\klcrUBfH,I5Ym VIY?q궪iuw4B3VF#RIi4KrۺM Jo"3J' `ȱWjWWk~Wk[.j*femnK{$ n?U u6^/q*JP<v|}5hn 7QPve3* Ri)Q'te{u>xQ]@VnkMnۤRnvޤیE{mI5(GX~M:=Fkq^Q!yˮBK8rZ762W4Vۚhl ȍ%QtƜ'[BȌDFDe䨬B_6[7[)t[D^">J=〺rw4 d'-/OMO~mY"@\.='yuH&dY?xs5 UXA^jZa-6R{?C|О*#' .<jQufNƖ-LP-=m[NHRrf.ԗIZfEV-TOA()Okj:fߞH8dzǧW~˃5eZ.jd&$-wJ>8./kˠ_ܔh5iyB:^km>[*#,OrO4CYuZ0)Nv5=ȎѨҔ *o<)ޒ6+o<)ޒ6+o<)ޒ6+o<)ޒEs]V&*TuG[n%%a v{^m(L&%Ë#d䒍p<$.ݩARs&Ӥ%5$O:W'MzJ;t w'MzJ;t w'MzJW>fRK:j#%р rl,)ޒ{t w'MzJh]!b~E(KwzhJ":K'MzJjgrxr+NL\QijH VIékƟh^bwx' ߺNO{tNO{t/QuǾ9_w5Q[]7ǶxK7$6/ZotkK[23{å2>o+:xSj_xg`mͻIm(2DhȸE<_anJ`/)2#lFEg\X=is'[5)3JHˀ 5ȂódJrs.&I%-M^6y-ٗӹL|k/1Y;fg&Gy[KsH A)$p @WݧuFjO6SsY I3> Z}@}c3_h ᫕;}nɮ1p3k,hgIu~A^d6%ijWc`6}u_`k:eĢ3Ղ2#]kcmizRDG'#֟kM 9{͉ ?NʶxӬ0)@:UWLLiI+v&D@m V3w,E䃜}ELJ2(QغH ,NUBoĕYRq&oA'|6?ƾ]C 0VFDqӒ$"",`}nꥥ+bA^%qO슕{"~}9ͻo{m->e֛bdɶgЌ8p`q5gShW:?ik\K^2OŬ:.ZvmvÌͥR|HO0qk~i59G"7z!92"ɚp\Li뼥uʁ>LvI8vH(uv}S*N\?jo'T:$3*74*.mCsNn].4 I^ΒZ jFO|2nԨ2m;M\=p< Κwt`i_SUډuLk6RJ]D}FeǝQ!kR&`0:ǪR:f{U`̏)PՇ=:2?np=Dv5N䞝QFvMՙG0"}ZTODy3.)><xgvvgk9Ϗky*{?uiOZ<^+@ڳ$Eqno*\zNҐ)Mxڙ\ OjPtQ)ޝ+^N@-tR%V[)NJҖ͞ꈲI#@ R{כiEL5 9LNj">ɀi}˺SV\iOIJTX5rdDyW@VeP!BY%K#d̸2[H~v}a0 i]Ũ̡ϋ^]eLߕ&ci]DŽHv~ #DS`׎|\d3D`@5$Dgp)ZB/PN|7L w0`$E͗q]BsW4w&De-9}PIjR&`4]K_7 [%D<:H++U. ~TnU+2&ǒV)Ihh̋7@U7nsOa6'gǃ|־aw-Rʵ&ٔNSKVHϷ-W I#,2x<t;v_>7@yKw_{H- kYo IL1Y`H+5=U]-/ l%j"%(O?ڥyBLj3jjf\OL1MZ[zA42Fx5t@mrЖ+w'Bv2*jKM&,&iO: ̘rN4r4'e߀[ZazE1*ͧC3,(ˠX@<'GTRKaN3,dX]yו*6y2Lmį ~ҚYD} zmmi]nt dKdu8q<6 O|mUQKl4㍹WmjVH5g^ҚYD}<$iu [l04y?h ktq3Qa=X? ֈ-t7Ud\;6wM`>cU7{TʑH[wDHL ͧeve ->cU7{;yU0 yP+nø[`йl5ޖ< 2=o4?K^UPT,:"!oxS2,t>EZs ْAP)RH0^֥jn[lU**HEy2Nvj:CFjfRҍ*LeQSEm}e R֥=o4?Kl*S-ۮR'L$ 0{YJ:x֩^^.Kl.GӄyJqg`lX] "ʷyR%a=6z3 W?Mg|H_!?hQzzj|]SjiźTq;˨G?J@~]5jgs .ߩ:vYw||M(5o&6ҽҒّ:sEïWenWiG%EnЕKdx)}xce9̮ML)H5hKflǖh4IldX&R)3Qq3?mIMrZn)K6yKܼeSOXbWj2NTNMIx">5u.H}.%ŨN6EPtf\;F[{;w\I[)jz-$`xG&iu]@YOhԴ8d]p֢əQ>)~T 6\q] JK&gPI:bRqS _ZWgZC-YTID}$d Aл*SjCG"2diKAɸrθ [*:ewi+lFDIbXgV>69R4]<ԣ2> +nMʭPѨy3"d%Eҥ }Y|T2@졶洔!%"#oM:ɟr׊e%֕Yt 'lH# yDi޽dbp)33O(LjSIzsm*22>5ZY+/RmJ\w.2KO!HA}IyY;\Q%Cmρ}[ˏHmȬʒ$=Wq/ef:GJ M)ջU`d' ut»m u*಩5Y vۏ)Ns‹!oޡ]PW2a[!2YC&Do@6Zim$G&gYQ6|'eWf?&:~_q'5kJ.MbF+ӄjK2K',x@Z}n{-?DclR~̥&DlL3!Q e >vw1# 'S)<6L`j`߸eXOVcJ2D}J[1vpN,TݴuSޔk_`mT?Z;hꧽ)rz'H&Pȡ6˾IdijNLR({Ē,KvOzS寰F9$V)y6j*Y"22GX Vu7N,mK"Y%j쒕+NާꚜI+I)3"5vap-)hݬF8? yct]@9fGą??u6?Ƒ\[.[JsJh^>62̋/s&3#82aIVzUcNL*$;{[fm'ʹq|>@}V^UNvj>$$,l &`ˡSjoeSwyXLUw%BܳP]2hK+ݫk,qA=EZmhR e&\ C,]ZAa-%$]DD/Z`9WCQJvLÆkRԃ$xrd)Bk8ztAE7˄REi> cNjW}-5a)cXqOu )k2JRN3>=#%Hmoi*f6}-[$2#A^J>k J j}Ҥ6 dIɑ-h%.U#%-QF9F\*EJ5pciܒGI5~̎EFHtօU}+i$dI#σqnPv6uK\WM~%7mxR%>X[TJvz!͓U`h[%'&yQ|**[!~/"$dd'%ҧlI247Gs[G^44r4ޑUR]v"53N+k(<@t;5\4ִDi+3a iDx%$F@2S5ӡ3-'I$KWY#hzsM.MK4e4d]'Gћ+ͥHiKBiQHˀ ]U+o0m &˥]=&?ۺa}<_ewQi#7 _ B1ޖ5^rsL(N“Ү&LzT|NG}IR#,|dGk(iXPIJR`t_q:uUHv욜4*5/4U: "J[ !Qt$Y5U{^^L>reS78啙`_0 î--{TZn[quP_R҃y Ti624tS+I}qPÈ o_n?|[?@S'e7ŃxƐ<~Vܖ7I5a o_n?|[?@HM ;u*ZS|ک3zwOۚih▻ZJR4ff}&}k ;J/}r=]n T$*c4ԅq{d$IsJ)<߫=KJL2Ӟ&wMvo˓qR5]L䡦j-R%nI&K3֕p0`[*֩<1&NÆYxcߖb梦%z٘Ng1%d̺=*NSQ}@ʝRq.Tf%[IQMѝGf5y-kj'WD5L \r[*B鍒TJI]@Y:eh9`TKy)>92НY8th&Sap w:֝HR fj&ɟI0mC*JAnJA3xID}% r؁^n; uDfgȐiiqJ#20]CjBdEYU*2Pd*-YddGr_tιym}^g\񶾯WZ5Tym قvOxX ۓ-Oi<)= -OblSuJʉ= [ιym}^g\񶾯W3^x_WTMRp2q*4Jn23/ (+pk3>$/吟y(=5>WgI4rߪT# SF?.3X݊t髶0\CgеU TЗ-۪|OJbT^}kh.-~e>Sxua^&}pLPۇ͘K{n,`[^\M%YS3-G[u3Rrx0fm޼ejEjS ڞh(|Gh{r,K^uC5 ۑ 0 KԋCnRۭFZVdKe>7?.68 ,TY#BI)"JHDE"Ok=RW S)VSjpCt#WAx>@k_`lv-ZuB>U46pBKÜc{S* PdvKaPHؖj=UM(7̲dIAxz =-Q]5Z|fV\M;m*ᴓ##r*4c9tG}jJb6łx] pM?^= Ad;ͨSDH2c8IJ%;,[}iy/ƖF\JJ*0JDvD !< 2$Ͼ= ƳGY\#׻W]K>#ȔE/)E󬧧7LRӫ8ATi3fX<{8{z_@=k?/}-ZeV}U/iu-=`BTX2 ml- C4Iŧ$Z $f^d.ڪj%o"BQm;)Ytxn :"J(N^4KGӀ<Lr;NHg;T5=;'Ye8BHg"Z%WlBRLv`yUi\IIpֳH7*0Xɲ.~#"I>S:bʮi/p@HVJK+e59frUBqBp$2g{h䟀n'O5jrܸ)A ͉. พfv<Ң}^Q'v@߲*jqpb*6{1 q)%ٴK ڦx/D%GCtDG,h}6kj2RNHbY;"ލHnS6[y噙TNOSפKL6&<&в#I c~ϫ DV@*L\kݟs5H.-IhFR8tOO蟢&qdTV9Sd+iהk#Q(L@8VL.-4ö Ү0*w_-پQRBpX 1mI3$.9j/PmM*$& F<5Fޗv]Bi5ťBfe)JLN߶dQJI},?r`64stX1Q23$uy쨍'μl6éFyTSW ̳>F|im ʕ}Q)FraoV٠$݀yvG ң4㻣Qs<^,#@:PeZO/2|㚫̕ Êˏ{?uiOZ<^+@ڳ$Eqno*\zNҐ)Mxڙ\ "Qdӝo f~ffЦ[ǣ< _ H^ڴX ‡nlL`='iRY6-emxQ|4GxeaԜtc՗CNUT6*6JO>A/pm$qoimiԓP[)RA?JT]^)5ʅZk4-H^fx,om=Ƒ*F̍ c }jJۧNZ".P:qݖ$KTj[k7, @-h劋HN5=ݹD{Gz^@YeˮdGNqus}$}Ea)֍Cxx>(DE@/Z:9 -Äv}l 񙀇KS4$Âψ \+pk3>$/吟y(=5>WgI4rߪT# MJ>Orb[fktuj[жqJJHԓ4+TT[%6{TisQȏOvAe-I'W30ԩLq՞dD@* 7:M~udDʅ5jAeGդIq, h)Mxڙ\ _viY˙IԇTF[t, < 5=7vG }#ꆾiRlIub[񢸅p7*22FٚeJ(#׍6mq"' SߺrԙOu*EVZ]le%X~׉:@YZtiŷ>Cc8%YM9HÆNl_*Uph,2U3!̋ W%|:bʦKIC{O? t6]Bb,t{XJHf}]Vt>ݗΩU 6IѾ v,xwƙ2Ue$jĠ szRX{? ֪&ֵ=DrΩxDL`Fx/]SiYP#NW绸ۊOl@kh2ցi0mNnY,'iG`6@>̈Ϡ`>yct]@9fGą??u6?Ƒ\[.[Js̒Ffx" Y:-/ ,-U>"Zڍ)~F(,YIq@5Wֶ!)W`[U]?.smɭaSqcGeMnc2$L᮳R;zך E9N<ӈiF^Rȏ>n;-fn]FBTi$DxНnAHN2(V2g8ַA:c7 fYy #{ݥ%EyX2[YkuKlu vKxR8.@:nTZ*_hmtq]*?*ޤ h~,ضSHELXM$қ#sxEpJ>nCKi%֖F!e$d} 47_Boìie&c贈eHi 4FX#2D~ܗ/8 ;!o+yDnAz;$]]QGQ|?vcG5jvk+掚jHf4t+i6MJg>ԟ{ ֟|jOYt:ߌRfVNwdBK w6.#:]rNiT$tJO}nzKO}"6*{\C{Ikn"R=|q6%ݾnzKO}nzKOM`ebcVmPѸRDHKO* ȱ$KeRD.,nOd8ԍ =i\Ԕ;CuKqI9OBO ܦ]Oj5W)nl#;-) Q-$FY @sv\M;ƗCr2Qû\p[  )^X练]7|pPY!,EtM?OW RǤ.)jٵCT HJ# J9;BooN7v{YqdBK-I%d]gӀQpHvCe:iJOя˦|mL.deF>eBbGΩvM9QY KiZ)@1TaW%!8DDDYɘVjd3^QFu0IJNŒY`PvIٓXyk"J0P̨%!.6)ZLF]FFkR]GYE2왑DfO4jOԘS$1$TfXǀD( pmz=nAiۆ Lf̸ɤχ@TrԻfvmK[u; ffOᗌ|tik=_d&"i.|ز䒶$9 @&@>I&fDDgezsJ-9p!շ2v-Fy\]~6yhߺJ6IP]_{RXmDEޑ'6yhߺmv+tȦjk>¶ڧ0]5cc ,[Vo(ytқH᳷ǎv8-YsË7#Mg%l`˽m*Uz}RT&S|a[? yct]@9fGą??u6?Ƒ\[.[JsJh^>622<ی}Lkcmζ} Dݴ6)\rK 5(['uODh:$۲.BRg#?-eo~D: IYM,`3h;2P֘\)+R34KL5 V}Re ms$,u<0sLhVr[MiOwFxDIQF< 7wͥq?m4JJYtI,;FGe%ǽb,٦3B &iQKSk>W$iGѺkA6[6Ϗ -l7xi.͙OmfDwO'X$ 3) 컎~%R<[(ĥD\ .JLEk-˚wRRn|HKgd'.># @NEe[uƐ[Ԓ3Nzp~>:݇KjvJ]ðmVG%D{?uiOZ<^+@ڳ$Eqno*\zNҐ=+UAQg1U)iQwv . #&L~C2-I?2LϑUI37'tX$FaEJ0ݎʘn̈O?'KTR.n>˪I,a$_5YK|?O*Ne:n!8e@&`":jɽBKJ:*h<#ẖuPAb;ߵJ 'P Yuמe.M,)5g$AW@W֯-+~n0&m v0 [DDNY`Yt-zER=rJTiM%I4E@ Cwٳy;t݀n BXViԝnT4/dx]{PӧS+`Qh3"  $t-JWGK9qjկ[mj[.OWv2jTVi#`e~_oQu&QhE[QU&y2X=@5~k?Sb]F֝*[ieN}$w|@\%u 65/S17F\[ZČxP:MEoS+^vtʒdXG^cdmӲ1E\@oc˨EI9RYJyF~ IH/t}*JՖ46 ԟ2ZI { -?dt֐Z nM!gaRF,HJI_ oɧ nvM:ѴK{ ReǾ0-~M?0giMhAӒK>M?gWdW$3>o;/˜oqrgFG<_@\њd 6yQ౓?եV=9hF.5stt ns*m:De%Mi;#.iO[1ګQ[)n9Bԧߞ9=MшzvʖQaLi BsI8#2<:[+p.kcZ{_bMbe.m2)M*5mg |iWS*qJJғ2)nګH M 3~R /Fe䑒.6J"2> >Lml) JA ]1Z1% )R䚋wf{q?.M~M?0&WT4Ow M 6P PK^W-H MԓJi)5dGgoɧܷn[i7-4~`DҺVo<ɳ7U4PS&[~bܷn[i7-4~`iy$$\/yT nH-qiA<:H :P}Df<+ PlA/BwHqtƸm37`{?uiOZ<^+@ڳ$Eqno*\zNҐ)Mxڙ\ S/X&Ԥ L#aoܕBn CQ+ti&p?A2τOҴ؋Oo)ldXWY_+JejM1.%QS#m>e]q2UuSGu<S[m&6{\HYP-be/T[O]J5l(e$WܣoN5nm[C9:7",w͑灘 ˎɤ;!ty ; RT^#_ӮZNd[mFu"I 8JgNַ[~ͤYʆk%0Ut=I4\6JId@DFXTdתR:Ol q^w)_Uw)_Uw)_Uk%~Tc|ONSZEю 7JAS[/J**m)RJ2$@ Lm2 kW+q㶧\_eWޥ%>ŷy9ؗU%Z^%@/QdJNqzl{?/֪Z j ǝi 0KO$ץZfSL5@[p-$DIGG+pk3>$/吟y(=5>WgI4rߪT# SF?.3XӮ-ZzZ2$e┙t5M@Zuf“4Us{Y"@O{4V'M豪ԉIOFm>IDGdG@60)4u2SN2ݥ\/:m,c9Ʊ-i2YmJVtfufgJNO Hl@x;ICM 6yahqh#R3ӃT5Iʚ-LiNNҌY<.t]>}lUJK:[MA[-Id3<+WFJ+}d9"6T<ZǨnLGۓiDde PhڅRG]pjtMXrmD%`}>DS4WxJ;W |-u%ŧ@\1R"֗$8 J"@zZ{Ի.2} ImF(̓0ڛhuiFmdvagozovozo3oH^-\egf^3U~ov*WZ:]" ꃕYl6Hvk$)~qtyct]@9fGą??u6?Ƒ\[.[JsJh^>62OTMfc."+~2VF\Rj/@|AMxޑN!t R2.}VjBI-M" 'jìصD#+0K1C 'ڶI)/OI| 4w}CTYA%ԣ4iq!hY%È(TWvuMi%fkI$bNI*I{<w^c׈U)$%IlF"NL? 'g!i>ժ㴦ڝ䡷K>ӟ>@D}k}פuƒMg%I_ &jI ˣOOeZ5-&cWRfJ/8iQ2?HBI.D{?uiOZ<^+@ڳ$Eqno*\zNҐ)'t׃@[6L>5jld򝤞 C nΩC$J`YJ<@C,WZ~Kħ~m:ohjX+[4Z ꪋ(A$F̼`.>k֏ҝ,kE*ݑ BoF9j[[<7PUQ F73nDALZ8#׶s!ω}\/ B16P ̸xXTJ2-: +ie[,%I`GB-k]y [ O=|"$ ?<@% :?u^9cbE=g8R|<@Oh߲aZ-)I^,ˤwJrd]bOM9Őe*NKm;>,Nߚw+א EwQ( CGDJͳz2J.n{?uiOZ<^+@ڳ$Eqno*\zNҐh' @9I,Q ڔN=>:YBoʌE8<T,~fKQ RTęĎܔQVy3y7uW%Z!ԲN(H5FeՐVkVmjpSFv>c풖{k޶JLϤL_[@95Vޥ% ]@#`aʃz:Dԣ5{kRw) l Q#k|_ֽӧVKǹ53qҾvM[)"2# ,>ՖgtO74z 2ތqt歳j#)i&DY1˻!]PKnlˎpj}1ϼ ptibٵ0A-YR5`@!!u?%*\$I,$䋇@ g͋S}?j4ڜ^4z-: 5kA@ORwo.?#BY  GSzVdH--KI]B9RIvWo8-WSpZ;pt$Ԕ- xҤ#O^䛧%sU\;ŭ2Zg6MJRIJ YAPjjFK2\Y0cą0PJBDǁY<,;fSe˭iLFVId+%}{8 89i7*)mM)s' v֓60 ol̳q>ngDr-Rt/x%J2|z@mDݞ/9!s|inMbNn F&޹}4&Ս5\RܔRJR}G&w&޹}-=M9]Imr+gç Օ%0)Q6&Jݸ])V: @3V=BV$`JM`fY?H ZG&kZWmP y7VL6sZ:Mq[ZGW#2gZRM{?uiOZ<^+@ڳ$Eqno*\zNҐ)Mxڙ\ yFl:%2(uNy$V т.|X`#Wtގ`UL6eNh̸ ZXDF{Un5ϰE\m MBTLB7q[_FA0E'ZDUSJu,Oq)QxQF6c;juל<% "əU\GOܸRN2JV20`+^-m;&,Kxku;'ĸxE򺰢}:)D8Yu,%^Bj.VPql@5ѵ`àȾ@+Vϰ [s%m.#hx2dP)^X练]7|pPY!,EtM?OW RǤ.)R1t׏̀|2Hxew2ě{;6$xɫW4*G 7dD Rn+zɰu q%&9w`:fբSDc+.ˋRe&x :q5ɤê\,j# PD@/ 6f˷c̛=MFO޼x H}֓JIX:}F[yZ8pQG3? &x,s>8 ,sHB_^՟'+t~RwP~)Mk@\%OQAJHg-Qq]Ѝ(I$>M1d^*h38ϼ#U ~z]Sfq*qċ|EQLxCWNVhQd.ʬ 8ÍZ72|HGz(Ys${T7D+rj7h6\ңCTmI "QV(DeQȎt' ҵv{ns;<__ՍHnѷJqF6K䈈ˆ0+_iv9 Y*{ISwT|pO? غ5L6nY8i-삺q- hnUj-MM$2¶I' )^X练]7|pPY!,EtM?OW RǤ.)R1t׏̀iB\mdiRY### '}r,*.b?hQpDL=2.<ѩV> h-̥3$`.XtWFi fIZ]{L]v$HDX" @Rwo.?#BY  GSzVdH--KI]B9R4cS?˙5nm8웲it)m9 UĀF.VI(g^DX9w-*^Oytڳqߗ@FHiiIJ83Y@2-^\e_:aS*p!,&C6DgzER.UKC%"Z>I&}3 ],JYlRFIEB8 ,sHB_^՟'+t~RwP~)Mk@]`?Sn!.6’uӹO9n8Rm>&} !VO$+ׯ\sRbU#d:b|rqzZ֕oXHjIJnBN:pK23*SP!l2 ūHgΆ yiU{͞tx:g'"I`8Hi뭏|V+Pu*R&NH'I"=Ȍ|HrߓOtz헪 ^DHv""SI%Xt:7:4b=4q&kJU̲@?3gk:t8׭&ECB"N8f]T&EͅAZean˞YFIb$"ed8J=〺rw4 d'-/OMO~mY"@\.='yuHя˦|mL.dKx۶3cbuO2>Ӯ+j!%Fu)/bM$"NO<<@"%ڇ\Zkts8{UR-'~@>]S 68HCۆc;Dd-ҿsFlmlod8mtL#ѩ'XKm|@3|RIi4I>F\ !(I%$IIp"" W?Mg|H_!?hQzzj|]SjiźTq;˨G?J@~]5jgs .R6+N/ Z|i2 fڧY(zLrOF#3#3Q>0@7Ivs<׫^&V |d;!PYdJ⺗>Ө$/吟y(=5>WgI4rߪT# ԅOeF\4Qw͓t^^t8H=Ï:TI#4c0#/_ܩ^VzRn|v[m<4Jnj4Kqp%2E@Ϸ6|:r4z亮\{ڝ91*,҃FFdy@tֆuK'R:m;)ό`)MP5W\+vM&E^MKyB4Q 6oNVeggdgٞD(s>?]976_3տ7KE'v!.//Q4gScYvG[[r_tWT9+EF%\ri%Gn}H”F\F] ޥ[tȒw;j:1S4K|I".ӹVwsjܫk~W6ӽ.isQMSRԕ,2N3c+pk3>$/吟y(=5>WgI4rߪT# d:Z]TP[.].-.\H3|fqdgxdv~yARlDITȑ%q47MG -[Ϫet)YzIC,!e0q+KƵ6M;8,9SZܞٕ=9|Z`>tҦuiLrIBfHҸюq q=;+,?s---3딲(ӳg>tđ*ܩ $:!i3,w^P0Y8}J=〺rw4 d'-/OMO~mY"@\.='yuH*u?[c^IeFXQ)4? O$/吟y(=5>WgI4rߪT# SF?.3X sE)S [Nqӎ)^X练]7|pPY!,EtM?OW RǤ.)R1t׏̀~V-j$%RDnҶ֗)'2@~T5WXInDCK/*GFY_:A aTSexmQq]1/}arS(Ni0p홥9x = >1 8$džilůQ7 d,Ude@$)^X练]7|pPY!,EtM?OW RǤ.)R1t׏̀GbᠪryKI.왑qKj6.8E 46r;d%I4_TuCaKEq,\r8g+'}oW-;it֙nGK&ఝxIiiN^ \gYn!Kghյ\Ǹ()˛UeAjlj2TT;Xin%gQZ&e}? *keUyGmKuGIAtp5L637yQ>5kݗ@̦:9swOqI#ˤawYX~:ծwYX~:ծ ?yfMb%a oE3QxȌ}OsbJMң#8xxuZuZ jSiyNggO@ W?Mg|H_!?hQzzj|]SjiźTq;˨G?J@~]5jgs .6K@% ;T7%:ۻғRM %3c(b;qmДR,xdDDEX+pk3>$/吟y(=5>WgI4rߪT# a{MٛbVO~U*c!0Bmˆ˧L-4߆am0 o;FwwX[~i3  []AaT O>f"Ԛ[Q%#4qRȈ&`3-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;;-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;;-4߆,*2I41vԚ>I҂4wII F;Nѝ`ߚwӿtg~u;;-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;;-4߆am0 o;FwwX[~i3 Nѝ`0]e1YIr|"!jI덠ȔNLԒ3 ߚwӿtg~u;;-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;;-4߆am0 o;FwwX[~i3 Nѝ`ߚw ]R>J"e=in[ ZTK"Gzf#, wu;;-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;;-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;`ҹeU9N_"Փo ՄpROwu;;-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;;-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~򰶈Կȋ߆ *˸qTPN2cZVۅdH#am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;;-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;;-4߆Ѥ:m}ŇӭSTEQ@{wX[~i3 Nѝ`ߚwӿtg~u;;-4߆am0 o;FwwX[~i3 Nѝ`ߚwӿtg~u;;-4߆ 57Jj6ϾMtYjQD~ fg|H_!?hQzzj|]SjiźTq;˨G?J@Qӆ:Jb \mYQK#2+gd6ӆ;Ȯ.zب;= QH5q<'3=&@!-8j4KRRiM}Ɩk5g$e"86ӆ9r\%ƔlQͨdG$:@LCtNX5N\֔HݪCX3'8τiBd"ӶMv3RjɲN6M̭Jɤ0d_Mn9!P۫@T'h'[R D^mg2-J mkZEC!l'pA#hg69fGą??u6?Ƒ\[.[JsW&m*dxS1VedᲳ#$d+<y6KW[m*j٫Fb-BCKVFM%)"%(s@Dzz^|4E>]p&;pbs,ӴIRd#"4yttStResemSU74^Wa #%+FF@+nO߽[w |;u*i޴ Mu2I#=DEZ-gni6_dFiRRF<8sjE57%&P3&'DҥxBf$I aГB&J-rʝzZe6/aɮwS1K}q*4ʌ| 1yPkRïiE>RrB2O)IJvޛR[KRQ@ &=n֢^թS-B5u { Q4m)22=Jf1to=T·.6=&գD"T"\FᶕQHCy2"#3>/ũMXmjęt뎝~4SOǃk-`>zWZLH[uVdndȋ4uҵZa@^:2rܶ)hRtCmmZq0p/4PqϺ ʴTFJ}$} 7j:u{FݣőNa}nm-dрmHJ2$΋!:Ԕ:$F\8/ff 2غeOeuPCqpQrj~yWబ[jEEy6Njzb̚aRⓒ0Jev+SQ%+ ""-\0*U}$ʙw. 2ުM(BeQ$ q/8io\pIQOgHŤWBaL2x mT/[cj6vԪtG)!(4 dIQT*hWœO*K%tVhtt qqG0k46\YFxOHm2!I }dDIȋ3'R.eh1^+y.jnZ1n+jjm*krN}ʌxN%ݩx5aFD\z eڰkBnSg ,A!9?=j֥oUؔYSTS,4+2f[8j^T>y5*Fny*!Fsf!L}>/̓7q9pPKOfF4>v?&TFD%q=<@{T_:{Kq4=PN*'dEedk Qe$`:n$fEf;)ihIxE 9֥',]Cbdf9^B]51zO. &tͱ{T ڦjK$N}DQ`NK %]si[ݏqˬ-RJfl/h$)L )΍j^\jm&ܷJ-ߝ!$h#=$tY!,EtM?OW RǤ.)i˦Қ)5gỉF-qnrw4 d'-/OMO~mY"@\.='yuHVVg:lGd:7M%hBr|^.E62-ը?E&b*KhMH2K|rm?NXm,ʺӬ2Tʅ*,E5G󙀔iOZ<^+@ڳ$Eqno*\zNҐľo*ܓBRp3"2g=`jRBclBUjPM~ow I$YVx У+[KNv%s -.8jJY#".qMABkiM&f)3w$liشz|hcVI% %$]DDXg|H_!?hQzzj|v\ yOjʃ2~)N4%JO|'+t~.='ytlݮzsߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97kߛRvok;g7sԵvZ@\-~ lݮzs~n=K_97k@ǩ&YrщE ˁ GS5zw#2I/Tع\鰤K!mNPMF~",{UQLMS̔MS(g-.6RZ}:IDvM)T [)w;D>$~1rfyyy橈ϗR֢ju.vVUu㝒фLlHLq3SQ?1g1x=tHU b Uq8ef_' O53>SDs7U[L1$.sBL8lZJ╲3I5p%)$TS5S3W9k˟.7]j]NPv=6V&T-[iԩH4| Â3O6su#,7XjS1Ã=P`uHzA"2Yu4NUsQ\WapާxE?0*TI7RJQMNV'j)3fFh P-5ݵ(Nԃt!LTS\sLD'UUQݙZLH'˦hQ kCڍN'8I(X4WMt\NQW6jLUTss_mqUp41Y)}aDyr)kD/៥nj諘ĺTiPc.4d(Y.$e11\sKziZR [*M<ҤlYNIDGI4]Z4+96kЫt tG8laɉdYRV5KJǝT.UD<ֵ[{[r*)~4ϽZF^hfv֡RnJSc$w<-F)J}$!&`]ͥYQ5 Q6E(O^-Zn(0}~k-! %!O,I>?iVYY/ -){Mx+>:d6n>$'L8ondGYMho BZ[VLH$˨-#9Rz21]h6hkuKڤ)!m$"4dzQvTe?L#:ɲ&qn/11E1cnyՖ+B*5vNRau/KnJ{e`/8"jYLg3N^ٓ=Q:s7U8Ի6KMF3G J3܊*8 2MIiLꦾ|?Fs4Nq=W35ٲ4[bdEv5MTScҎS{J$hMQl,"왎7<ёgvSzS&aQC[z̛~EBi"))&6[3"xGЬtg6'V]ë/sݯ?.VvZoN)eNbs&SbHܥcd2%p1&Ү??V~3*,bik23P@icTT9vCcĭcELxٚŧ6u9g_n$kʜ9wFjlOL3%Jj!lmTgF_N؅ c믭i"KNOTS`v:eGk\&ԥOff2m:cDTW:_b, ٗV%9ӊq]u-eF[)Iqŷ,gOwI\ʅ^wkhROD%b;dd=223źM]s:^ MvƬs-w:t12tn՛mn FJ;H-^"&2-qy}sNw-X!V,})Q[Yge^QM/;9>3!f*"cVk(y?^oW buR9ɷR|r*(6NJ'ۉ^O+2<h#rQg33e4kdZKB%EbJkIdY56gJ㈹kgM9lח]]Y՟JMt&6,u +6Rlhm;WT,JMgގn/F^4i&.|ꖥ&:@5de=@g1e:+Sgϔs?2+L4hKszI8L6LݧoQ.K*ۜ~Q-64@Ǵr u7"![=)N*rLed1D{\_-k_ۗX-v>L//fMublT Xݚ8l"qml)usgZ4>3L?>#`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+^vW`Xez;+Չ$fj""f`* =l4iDdSVڤ{DdFI8 Vpz(r+=3uMbiy#v/Fa+pŒHϽ'eikܢΙ3%κh\߬b~U['i= KiE*j+55Rq\1eVxzl>TSg%*Lt)TDFXqfEѕ("yk*E~6:=n 9k!a+iMt ^K.a3F=YUVG5 OI"sƨdŌan⣳! ʀkF'CC嗲d9Ǎ鍭oSDu6)Gң"La޹['{{aQ]Tud㏒-Dy%L"yʭ&y데:ڔѹM6(!oe2dE ?t.~~0<ʭWHkXMw=f Fr,%ݣ=,!;:ʭ&cOϣ+;sYUD,g W q6RdMFmaeТAB.wʭSή5TfweQ$8X‹`Vnw粫dXƸ6 f0H #0M!).c)dXd*D[S9,ٛDe$HˇS$,b2~X5]1㨖)6 (= 2 /Y&c?x2ؕTnt-&8qх.w沫dXΩ6=۪kmmdiR GFY)^gTU,c *MB۠4Im- G2N2+޲UJ{{~iҭzC?.2YlhCCUeO]"< sTMQg%4tlA7;seVE~6&tLv6ts=['Eƞjl<ڟm1K:cIM244DɑpM>09eҳxkzo""tm{0F!+'3.4{n/9v9={[&|KxEnbw&κ)쌗l[Q]4#m8Uz3ޤQ—燳J"#5_9*9-[)n:={R2g:JjbGh!RK$E># ޱ.l;{/eeglueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.11/application_ui.jpg0000644000175000017500000125526213502206677024552 0ustar noahfxnoahfxJFIF88C     C     l   !1"AQUa2RSb#345TVcqrs7Bfv$Wtu%68 CeDF–&dN !1AQaq"2SsBRT#3bd4Dr5$Cӳ%c ?ʹ.&m+nbfZb:wwҵG*RI$M 4oYqʊ0%$wwsx*&Оk;nt& 0o6\I"٦uWڑpK/*(P9 mcJp# [kSC~9;.b%4yv:Tbu Jp G W:#_ QWdehRLe,pe*8Ϗg{'jb$[+I{_E)ؘ-7VViknx^)3Q4s}C*-`! KK)%kNH T+ma0)](㶇7gWxy)-wRv|mx^eLk~k_(Mc)P=d~A+UhJa5Ks{⼦MUm*âW6JRK2T$#=5K۴;/,Nfsc8\C*X N@) Vs u~zOgq9^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)^)+;Iк{kz2饵Uʙu BzP)*N Ai3P$gBCwtTN cIP>0zAvά'JST}P?RIZ:;4kں#JoLp)0䭔0O##;dGpSa8Ӝގ14*bPॽ{nݹ4f!ڑRws#|uHjp+W转FM_|NjSڒV`0뀴yW2Ӽz=>,Wl5|n. *V|ZRS쥟I 9ឌO]'ɶ ɽUW{ݯNtjʝ_='=l&&JwSnMeöަ:rV'RrwlH<8ҩR_fM[={<=jxZզ {ޕ: zQ{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@dZT;cw·fEN xCfB}o4o$T<:nU =GK^_.zz"/Waʴv+sG9JZ  MTJ5+;^|6.=v4mr6p8B_A=оyVI?㖮s(}?GN~=X3-{~r% ]qT3x8rlDV%rujy; !$ֶ[Ǐ[ z^9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?s@9{=z|=?|?P- 9p縁.W4txqXJLf'5iA=wdnwe6ݡm&Dp@[$iNMNퟪZRHDrRN2WLm;3p,Adr&a d1$~ӟ1L}Bzq\4i a )"V (iԂ|ӯJ3kK;wݎhmBm%(L$)YIYI(-|ʩQ{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91\iQT8g~,>|7[nyzOFQ.93Y> wce=ng-91u|?PY*Sd?czcmMcs[@ zmY> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s(Y> wce=ng-91u|?P|{ςY@:y[s( Q#:ip*>4Bݶwk4뛫䛅ֿrҲxfSvO3+QGy۝g<W<@9|Px;sw烾(<@ggf'S;{VZ=ť%-`d`z bZqv6aQoB %=#Ԙ.+ CPTAt沚/<@9|Px;sw烾(<@tԖ)eyN=Kzi_wj:ڦڦnfp˅Pݎ@*i)P I$I'~HU7tla %N QBI Mm۩I%[ѐ~rpG(R2 a烾(<@CwfКz]pfjvC8$)HJRBR*IE9I"BUdvIj̫JufUj-<}Nn L>%;L+BGl@X-ysMװl-Z<ݻlmx@wFi׻֨Mw$ d(H$TmE]]rQz# {l]Dkv[wlZy)[Ҡ"psv:Kj"trY4]I;F0:̸:ڂA#*XOx;swכ}o9á]jN8'烾(<@9|Px;sw烾(<@9|Px;sw烾(<@9|Px;sw烾(<@9|Px;sw烾(<@9|Px;sw烾(<@9|Px;sw烾(<@9|Px;sw烾( N661^P1f1-. $} 2(@Yۨřm̶%EfsE SZp@({ƀ烾(<@9|Px;sw烾(<@9|Px;sw烾(<@9|Px;sw烾( j;iRPdd=cPYuImon+FGN  o-ww+V#p Zq'8X]p{WR% #׼`)[Oכz܂sfL1WXq%ZCh8(P´6uiMots ʤK$Z;e{hWAڞ6I/HkzqGc2vU![@ޓp$n^)'/+w|'쵼]L靸w;P-P{ZS|S.3XiA'u)'ri46I-Όن<'-zUCVh RHyBH aYQ 7fO,R~ rzk"dҐi:vfNKSؐxHiB2bJ{bNY_zo" _/|95DY6uv}yuiSn.KҦ6$GqjN -M;o.vJ=;D¹AѺN2gJj~bb,r ŠT1IZ)>)eiHuT\Ґj-Mȸ%! ro&p`4T{Aԛ]ڲW9.Kd}^v9h7-+n "9ȲeGy d V+zO7Q^U%Ri;$E6vK+O:d-{MFn@x3<CKKMnD N=ys8ϿJ HG7z 3&;vo:!Vc 5ɒjB\Sm\e1\/, c[]]ٖ-slN>mB\mOǎ $am'N2&>~`Fe&چVu?nSm[]6܋3ovG9!2G8JI@J;BJ[7 wyq2ɽu5o{%5ThN֟DgVYl\osmD,^%'%#$amN>ڷf2FbidѬo_+u.KѤ6o@\q*)T@d-MȤr Y;rSp~MwӂՌ]oWWTT=c3iR=7R[*u:JY/@%E'}h6.tZfId\-~i޹|x;鎽(zv.ӫԖ!U=|+mVʓV'%$WoMfas/ZKy'i,ƞjy!Z#c TPɺସppRRWQ}J/qpr޺ݳw!=VsV vm"%>kQza/!jK _Iワxg\[^o,mC 7땱:3 MTwPLl9-!ǐA*;B(%tTf}~rxꖹԽ?!WEԛ +M\`F\\TEb@R!+JQV =q]}}yh]kռ>;W[gݶ[gifԥ5w6Ԕq_d3X<ŽWvhZo1Va T!$\xuB0^/Ay-B8T$Vn/TO?M)h*S'$!H<}NW9um+q4 z4]]ɓr2b*7S2ʞ PWin5=i{_l$%+wuL'TL٭V A!V\&-cKDhfH2@ZYmJ27鼚%~˧'|Ւ}2nꕝ;]i4,4XQLz U!TWږ8KXAwޔVv[w)UKXGuƝ"gsIc\,`ۡ](se5-Ps%Iqx.}>qχgՖ[\rMnNWT 7n.͞cwoOCK(y)R\t H*jnmgn}6k2n?9ךeXk^Ӑ6~avTj¦u2Bwkjpܔ-:Mxz1hksztƖ2o x_.SZ{h"d6)>Ը,GQ8e h4MGmo>z;5|Ԛr||5%౥90JeӜpIo#Z I@)ɥ [s__ȝS{Ji=owj>dI^v<2ԤDUB)|%;>|+-xv%xv&>ճRr?T[4vnqv&48㑂&:7;Zڎ󓶝\o~]Yߩn%-ѴKaePVUڶI$-+II~}Ҋ4-Wr%{߻Gj1eB[X2SivogS:ra Imm8Tj8=v|W_2ގ _T66qV븼Zٯֹq3#hJuM%Hqm$A2q%4Ԝ_ xÞxTy4x3@9x)r9D{@v9y4{h;]:2-kLerjBʈo; {۾>7VKNƑ=IRwmh$>Q pԚ*KoӣtC%5'8+k+t[FYys8X׻:3Mman,#)mnTq9nݸ&eZk7EC5U-HõjX2mJv9#S) qR23nnwqꁸCkԳgg 夅CX*#DG:B@9{vQoz멕%~$Rݷj+v7h 5O d{\F1q)n%ShekᓞQ݂{YuY.lt-~VO>h]TRU}Жh֯nsnb<{,.!/yhIVxU R*_e+NVkye8-ggu\_vS.[]7Y;`r˲!RSB7"#%7nbnv;.9[/WS^7M ԻV; [U! eGjwUi- +2-KeluL\#FK+u2%׍kרRE7܍RNQ{H,;a$xI9$ĒI$$O^O9ʬE FwcFͨFsN*8=qoc$6 u8yʃ~՚.4UXثO{u6oZ^..q?q?&S ˒1G5";硷 HJʸDn L1_j;I^MRyƦ}O+T}n+[COVl+QN-W ws uEH)%a3h,<5˸?^sJ^kC!3Ь+<03@d[~2:-AK@7VI$#C dYRq9%I^Ri%?HCml;dҬ۬n?0\ɼ:-e)EB?*);\x게)+nuk\,*խRN۩enK. S֚SX1l14^QN4icۊiWJwRJII#ΜQ/{W^нO=ܵiս&9%% C )s Jp;2[+rwwma:xt4wwI&確z%3NGӚWZm-烅 J)XYiq(T>i_b64uAs"ϸqBvbm$dP댬"9N4:_&mZ1ѻM'nLa=Mm|wrŧGTFkAÊB6(qJ $;c½%Zy藼(ɠN;#nU6tі5Cɫqn8&\VV7P^[:*Iߖ֒s-Mk}_k %%XlX=ͦj^ueéd v-e0ٙz"RBWͣ}JM{۹'=rE]y&^b%YO".A8nc+a&p p-k'$tiu&\eJ٬M7oy6wojkK#TI>y5~߾KlSiM_MgZ&Hct+r"aǐrK}8 =I[7M+u췚෗ ms9t eF.WnxvE E" k!ARx] I]/ϩ&㞅3I>j=7$ꌻ뎕I'Pt躳xS #ɐ_1yRRb8ˆH݆5k]qtbKr⵼d/'Ur5@GkGf7tlJDpV9f֢d#w|)A)]_%NZ[ܳXߏٵx[Ez$kj* ɳNii-2ۨxJ6Cĩ@[n(n6Sy;[k5{d\)٣7E7{L)ͩܦK.aI I)XI$¯ݓ"Ƚ3Vy4M-+cN۽tCg[Caɕ;ϱ*CCmwW]kMUwgkVww^ٖz>ݬnǛܝI|s-rL5pH52-qmm3K#PlI O7syXIjr]vӗVv]]. ܶiLm`0.@\[[N+x j6_ugV_K޳Yvhw%[7TRd-W&\P `R7e&U.ҿ&Vl|]#`ձ~DMjgް`=1u?)(_R ԔUrzV˯5ή[ϣ.|VZ[sG^XŲŧYWnu2But"-Rl)`g(RQ$J>9sxr_pzoTMٮYjebѤ_C&$lLR;yʃ[iQWgTR¸'%Tʤd#+_j.6=nZ/5Wf6#˪RSٌ07@jE[c('V~ǮܩgnjѺFԺjcG҈O e\Ȋ oK8d9$og\|s"Ja4"BV;էu=Ip|R䁜o$daUX^fyS6xZ;i5g4 7ܗluoA|")i_%";RBAͳ_+p*>!:NDfdZmZs-vq[>ÙJV9((ݫ]i]7-zYs^RNv%[sX-b TF܈ө[ *VJH%jޮ-2,wzu\Nw;sNn|Jt! D{vݿk+Ϧ쵽'o"kouqos!R֖j;Bpīېxpl+wG#}؎Ԏ0|bE.TxR6CN%-E+s=0*ږ?=ZN[Ǵe\Nۧ3*U]n_"K̖_AaJiš|p+$_7|.wo4Ա==hкv 6D$CXm$`)jRzIneܬ Inyf<\tj!Y_LM bZAR~tv2qA ԭjiBSX$҅]}MZ+4noN eF6}uqǸ.zlu >RTYhck6~Y,"MOΐBS(\rg{9.g]o/u/Ko>"kq\wyʥ7\A J-PJ17K7ܟkwӺcר]l.r ^&g}!+Ñ^id(Q"<>,-^Z-(*J\1nʆe2p;[ǍU[;[K]&Iێ}7}n~=="սV&VXy,7rs* xo)P J'+qv}4u[:thNhbk6(#>cf"Am˫vC,ǶnzI%|RܰP:ĽRIk`j USvInDz4P|n5ҕ>+y N::%967h,۞6l+@ 3NV,ݟjE-sNraASN2FRԫ/;-cv( nM˅Gw'Np8rZ`,N3d|)wef-츷z۞sfQ19EA^NYEmk{ؽ]vi%ȑgeI=ַ^qeW)o&ŶT뎩לx*!) )=ۖ~ ww|c&R%1K/)Iu-NUJ*˧7~6+/[^ :;Z,6٭}hj,27w1헼nY>x^??N u70rL'mRc8-Ƌd()MRO gfw\~>Wm1P -E*Pܻ\b>um%a[႒8Uny 3gKkKf΅s$Sj*킲rMNm2-؝=s‡y,)_W na;*gܗbק.V;0zsۤ gAL%/GJ!mklj-~Ŗs^ gcIlK1X-S oG'gv󵺗romwY玭*9玀6țK]}]UB3o[lҤ4$A̅F0۾uO{3lb6NåZW+Fw겭]rO*;8Nr_^[mHY7X-4`5nr$M)NH PRpR H 44T5SޑiCcL9~樟s͕v,<vKsT`螗M٧t\ۓ]WM؞E=iZ{wEr=wt|o&r2R'uфIWӣ߯xJhw>knEs9,rP-N-ƔQR.%d4ve.OvJKTvo؉-_Z #B%v4HBZ}')PR$ܷu-I( nZyl#_n\k')*rcoC₷wO4J9l"d-!%aPJ@)#kNpUu߮Z+'bZ WIX ߜue:9"Z`G[-[c7Vvr]߷Km=WPU=NVaN)ڗ;;IYINRecny <GT/kED&e\ ҅%G ޞ2,\0Qn]'M噤m#O_mfN2 +u+B@:z] ]83癱[R)SV>wfb 3/=<--vr8UM r!zԆ۷ym>]|FmOqY1g(I`bՔwWG7ww|rٲjݡR*^ϓ=iZmiV8K- 5{W)Ki+/t{Кv?# Hv \ˤnt; q疴Ӏ)-$r {y6{>Ekݸ06Q)\A_lN;V0'6$Vߋe[Kؑ!Y? (Gjȴu2c[d1\EPqIA9U+W,W+j$M[+Uk{[- N]`\ Ľ m\e&;&9}%!M`n-C_ۊ-[-n](m;h6țdɓ8IJurG{}Opځl%n̲]9u;gKSGkG,L\pryw!DqNF8 RQJqU\l)/Y4v]kgB=.).T*d9'2B7 ;x 8[k߷7ĭ{-:ڛgz#Q.Z}B-YH7 PvYXqB\KeD|r5T=Z;ir-%Ѳ(޳sTww%J.SR\b9k{<թ$]>:__ib^i-"Ֆӌ[`,Gi}IBR@n)KWQ'W6jIhR \,'Ϡ+t)$<ZMYN-J.Vu=kK$ 3Z~2;2Osm %_TOknMw=EGJ[mgfռ}ƩS޿ߞ f#DQ[QQ _n@qE) u9ʔ흳zr8}mwiE}>1/?dMKg:ΝU\tͭw[(ЄH^QMnNJ\^HJ 02[ii;&v6>)sVv;qXޘ؎%YX>.u ̞ Zyd3nu9P{E֎S#v>8I =4i+wKNZ߼2#NhHJR@x n[/GLqhE_a>C!I)%#* Qdpfj5a*%Z+A^6c lb*r_9O)VBRS)GHQPPI*i҅ӊKM\mBfiq.UXX[E ZPT8(tSXsӼBmrmHq6H-[iuRzRGTwGv}U.zN.ȥȕ2İMi0BUnO(c'mʛt;fzדXͅ qQ":7mԉ;ݣSOwhmUo]y)i-!), 'xn[S NNVˡWm>ԫN0|۶ ~:>?$-%*I# 7ӲZa9mEJzeHqO;lGB:Tr{JI N)ZVY(ͷiee~\uq .%%JR AJI ^玀s:NB߯^RKWlJkw[ݵ3#eELij! h,A öVQ]ʹ(V714gXuZBغ|lV&[u%BE{k-ob+pZӅQ&n~1vbb-(RpPV[-3!}Y떙;2v̡ErDTRYIt-(BTRHB05g{Bmgܬ |XXn$(!#Pi)JGpWJNM$GcxңxId K=:ږۉqGZ)RCTuڭ3zCtAJeV = Kd;x^cG~{n̴mM[⛼v@NNq$Jt`gUyt{}-O.nhxǴ@b#$-\.:JZ?TYEEh]͹qzNöybjSP4(`yښ*J7Jy <rI.Ue5)ۣ="SabK7q'!II/bA6y+dJ[܃g|g%F=I\uTy%!%8N8迊Yg$YEmw)ZH!IP%@((`AVۉuEy&tm:R,K-JĂtq|ϝ"46S[)()%#= Tcڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jC[ǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~4o֠?; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-A[cڃ?ķPǵo֠j߭@; %Zv=<K~{Px'jO-ӺVPݻms] lDh6]L{9?404:c qj۔${We~.+s> \M.wq/V]mArBw[J0RSAVHH^5tFqy5~BMv;^+F' j-RwZS a-=7Vo]}q_u߬jj m~wu)+EE.==dӶݚbl5unnB3mn)M2 Jr@+5QYvdnlɪf5 +/Zړ~i[ȩ۰u{D8S7jjŻs@(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(P @(PzN۵*$2@;$$+sǽR/ ^6 7ݤvѯ,6hjGk=ݸ k[?r?7=/ޗČmṶYump'Q 8ڇŽF-nS .r}_Gm[(yKuuiuCʾ^˄8g:@;q-mP#%o>kTd7npx&U|V -Nz_/zQhMJ<3ÿ^)0cjЧy{mzί,NYe@(P @(P @(P @(P @(P @(P @(P @(P @(ٛL7TB_t3؍.jq\^JhqۤU6mIwfUn̫Uh}CŕQzhUr\HQܝK ,)|'u$) EZZEYnےYNeMw_ycԇdթ5Vv~X.Pӻl9CpԤҠE7jɡQ&Lu4u3:'jyo_؀̀4k{MT) Fiʃݱ Y\~g aeV!IiD{OՖݞ&utTD!S$ʩ$7dprxvՎ{wtꖞ󵷵͎QA'HrY6_*8)RRGoZhnQqOzSO:9=Dz2&i˖J'F۴yR#-) fH;o򛤤鬎Mm[ջ1E(${v U]\Lku7hZͧmPƒ*`َVڛq{@^kmg qo -ڣf R\5jTi7mrė=h7 G@5mrqQQ}K5|JO~O/̠+~Kح6- <]2L jvC%^;`TqRz;]ueov{[[BpGҏum}O8ݡ0Ic'x넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CP}]_cezRIH>>:鼙Ǜ2oseח(%{實|юX޶8RJBDQ{{xWXZ8ҕ nزU#a.WV8ZJ yw^\=w%][{t-h({;]nn˶*Z4n6*@yqltqk.adDWvzxڒ1UJ:tzDv[ݞ$Z$ri-o<8[VBBR OVmU8(tm)6l-X(WrR6ܤ{׊IbmY%A.k:ݿr.,ի{`p1iέeN-UtҲ#vHR:c/87Zf{;USg['ҫ5=2+m'u<(b @ qI\&˅ Te(mʥ)YhEWnʤ;ͤ%.w@{宏];n:KE*aDe$n|,w6"=>}%KK\$gZ|uO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!\$gZuO~!9kye`PGJ_,O8# d$jxx8WW/$] Jd_\Lt++ɒ~s~%r2OpĮ_&I?W@9¿|'8WW/$] Jd_\Lt++ɒ~s~%r2OpĮ_&I?W@9¿|'8WW/$] Jd_\Lt++ɒ~s~%r2OpĮ_&I?W@9¿|'8WW/$] Jd_\Lt++ɒ~s~%r2OpĮ_&I?W@9¿|'8WW/$] Jd_\Lt++ɒ~s~%r2OpĮ_&I?W@9¿|'vBؘY!%O4qo)gp}5Oh(4}P @(P @(P @(P @(P @(P @(P @(P @(P ]Tk}w-NN&q٘lm,dUv渫#M5~WGSvkhxYAukKgR'POF9򽹲z42HurkvmF;i#KAmOkt-/?! 6ʕ{Qӽ:r%/9ǂg:c_[Z]CoT}h%+NJ)PI8krO\FJ@(P @(P @(P @(P @( ֹ__@sK )8$=@pl+:B.csE kz(CUiAͣ~sEPIº(z(V͟8Pѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@sŃ R*8,=@%A˪mV8ysz(kz(5ώ=5=Eͣ~sEc}@~sX@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8Pmz(6ώ=Fͣ~sEѿ9Ǣshߜ@9o|q7>8P`yԶJ ptP*WwAQJQq~M)ܿb$ٶuޡڅZXOFx3|=qCLRBRHw=R B^;vo]F{8%:MޡOXnw}jC >b%\XKiGg$[NmZȫϹ.ŧqּul*%[[-CJ\_GT ┯,yw9@ :7H-~znwMszݜ6mm<}nt.V_}o%o+uX''i.I.<|<9(@(P @(P @(P @(P @( ֹ__\@G&fO~Q&{wE;!P:8tMy'SQo>[+ p╖oh7NcpZϹ!yeL:ꊔiF:؊wݵos'f&眮NѶW}[Fs%,EiԪk'>x}+TOVCc`q)P  |;bcpz7Kkfw,e'N|QyXNw}lx֭w[i{dIx$ÇOI$j_RЏmlT=]o^g:ug['vKvʣ(9<$$ty."wܵC]=*5fv[$l||9=_= |/s߅{z~@9=_= |/s߅{z~@9=_= |/s߅{z~@9=_= ?4 pᚣԪ<|N4ݿ^AdDs #[Ke.F0}rŲYee`dwS)>-^|[-nm>IK֐})gtMUN^LŊkjCL fmhs AiJbػ&VnjUY[[r6ן^Nu 8Va!Ӳaȷu 2q8(-M`)9iɥŷﵺz"bZ$׿GW KGT6{EAtGumymR|R0 ~5g}:5kz+i^/~IΩk[EN\RP!BT܀FCpJ7'χ-ղvO]zjzUOgOYvV\iזJZR8Sm`oU8&e&w>6WIYɭWףWT6ךIڡhvʹ[]vPbo5p25*|eAng\ 'ޞ]̤I-gj ~@9=_= |/s߅{z~@9=_= |/s߅{z~@9=_= |/s߅{z~@9=_= |z&|/J\q!( =9-7w@z3$R 8~4!RA9Hq>*8oJ\q!( =9-7w@z3~@9=_= |/s߅{z~@9=_= |/s߅{z~@9=_= |/s߅{z~@9=_=&h2J3]j k‹1N<}n[j=<8$Z{dMvgn,v[ZZ-t^{4mGn>DZժ "3 KKr)@a Ty*Kh]7J-Ny'ϰ^mf:5KZ~Vtjۣ Ԇ o9ӽ;ݽRv?UԷ+vE۹woRw=Zٜ~[~/ /4GZ. _Є4RSHSxd\֫I+fJ~/{V"icLs%|hKn-)<%NI.- , o|,}t=Q:qz*3Q'ASV#Q)1VlM 9q[ۼz5fnpWK[aS׭bcIoH[햆t:6㨃\91n, J.)m$Oj7{'_p 6v"lW7Ed_[`GeLjKt/ e@ w[i..Kcno},2R! QdýknPf3a(RBj%(IAV+y|}d];&m<|h|/s߅{z~@9=_= |/s߅{z~@9=_= |/s߅{z~@9=_= |/s߅{zf<~znw Mq~M)ܿb@b}XPlbgٝT[Kv+ R;q{ܠ?o:8Sg*R$Ov]޴%K B'Eg$-(qOIZ ڦ#K= 1ߙ&M䦡d,v+!9+P JUwy>%l6 ԵTƖF͞.Ȱ0E4Q>d$ĥ+XgSWIzIxfM E;ݪNߺimu ZoJ*=K*@bk-ԸB# Yi-]íh^v.SoV+qzeU>#rӐgvy1nңȗAeXdPP)8>webr3\c՛Y+fOU۞Z5acRK)_m* _(EZWt㤝E~{~3e/nVǵ>;iםz&T}GD\Bξ2KnR) $ NI7Kn{W1&Z;p~ȯۺm|}AFN3Jo /2u27˜@j%[;>7}'xovf]o^YɧOY"K|8VՈ[< ␄ne*lˎ NMBmozK_+SZO4|3Pk\Y;iZҕswۊԂB?U*57%o%{H'8vK>Rf{^XLē>IJpRʫXҖJ/Iԩf.U;Z:lZrwYcGwkjjIN[7Qdo(7VSNNϓ}Vy_.֗;]6]W7tVMqșN,[4"nXͽ$$AN jV.ڋ.^|۩T-ޞ˥f;zf67IzJ]1uN9IC[?me* KNVzr[ͶׅM!+zJ_غvz-Yrћgg7\i~kۂƊK[B_y@۠89(C7~:~ZS/ޖ/v񷍯f ,IWK%K*J@{RnF* S&8n>Йn\\֊u/_m]Aiz. .K/#RXiH#M[).th%ui^G m mb.tp.٘vc8ȝ+O JBZi$oOώ첶ed[ۮ~tncZ-wXܜzTwچTYfK8"Jyt]t^F2VG9V1t:_JҖАRc5r{kV}_̵Ugb鱝xUr-"GS3P"9mԘIR+UI%:ƅݶO$t֎ Mܨ!])[Š֭)ܸIQɧxir۷-zzzzzzzzzzzzzzzz?Q5MtnP@9  \8ӺzBF3@~sMSi#(g;%EDGv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gC+Zn}F:5[n2Qu4VڐZ)9$Kij"[;ވZ{g,Nnq=s2TOK)ַ\ W]ŶsM`Uuۊˍ)Sys{{{\q#ۅ١]o{?cieSwILNj{y@$;o;ߍjϽd7U%؝׎dի@i$\VAcވp%x(ਃ8Jl'tp'm9[,t]/[7Yfggv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=gv{=T.Bh#h MF?vP ;^Zio$Ŕȅ)`RN;$p8?7X*.] K2P2;=mqŲ|ԩmIum,/IRIJV n'uF(Z:.\ZKn"Km(+u! <;mӎ{42)w-=2vɩ6b6ˮVx9V7p(l-.%)(qPe42+xo&^qrYp]9F(-Ⱥk_+>g[-n,^Z,Zh"lE/lMoDbje;̔!HoTvQww:>֓MIG+ۯ5gѷѻ<1Dw ʵb8٦Ygy[-Ppi͙&k+|=Ԑ4Պyzf]6X˶!ʜ2Z@[%xOl7wiկe I~+aJv׷Kuk Ŏa+u 8+*$% h%m>[%Y-kyH#7-)zVOMXӵ؂mZ_)ArnpO{].z_56#juu >T1-a#\[ "JMБ9u]ܸn}u]q(iG;=vsHQm>2YY]Ѹ:ӐXՑ[O{迎fֵڶ>V~yo@e]A&,uJBVl3j*Z[ݷ̺Ьyhܽ,l_F\έ[Ñr_0V_B*JFqHBrKQJOo.P(P @(P @Vޖ'( ?'4f~Л4LkgmV\m. ڒ!Xp)iҖ(IA6M<\rl49WR[~w'*[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/o+j%l]úB =? [~G_zmO[~CzmO[~CzmO[~D`vJRu[ZA{#OO_=hߑ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/op~CE*~>w'z=hߐ h/'iT?p[~Co-|P/o-w_c;nzϵe"-0+RP'9A 5+RtTaiGvafI,|ib0Ό^niϻ4v'GWu0&G@96"=Z-amMVs omDz[o#ՠh0&G@96"=Z-amMVs omDz[o#ՠh0&G@96"=Z-amMVs omDz[o#ՠh0&G@96"=Z-amMVs omDz[o#ՠh0&G@96"=Z-amMVs omDz[o#ՠh0&G@96"=Z-amMVs omDz[o#ՠh0&G@96"=Z-amMVs omDz[o#ՠh0&G@96"=Z-amMVs omDz[o#ՠh0&G@96"=Z-amMVs omDz[o#ՠh0&G@|o#|m;Bh#h MF?vNzu.꒽jY[u-v۝R- u w9Bk+RY*ޚ˕:/SAeor#Y{n XWw ^.KIivrtA;Lu3ٲf10Kn(NQ RpVQ֑I{v~YKy.Mx_"lW ٵmҗŀܻ*÷egJCVR<'"/U[r,MI6J ._ohMQ55=B[rl)֝l%AnPl(eꑇkw{zxs4]uQMFI wڗſ{ˤg;@^'l5R:5uҾxkV~7v}jP @(P @(P @(P @(Ps/IﶿI ٹv&Fu?;Um?!v_OhI%rŎis~O{çΚI|tvoeSb)lpFaq. J-y)l6H13T*T=TKv[+`iQM湾\q>YrěۯCqEs[Tkzɷx qOTsnH>:ۮN'K.] yrSm% JOJdIx,<+9+iԡm+[.ۙ%۪+lZghQ0e.ۗvR:sX{7ȸXf4F/ItB 8HI>x@lw|IERyey(ҚvKy8&j{VsLOXNzKz=|.X&9m¨2B sp'"2xU"RQڒ3[9p}F|uv#9h'U]IDٴK<:y'!N.Dل--ڂG$>50䷸g}~'kN>4#,KYs@BjiP[,,8pns+Pd9O8ӧ\اX- AC0 'H54\=Ը>/% :oeJI̊ @J[!Sɴ-a9 'lڸjSbs,mnR;; fؾVco#D2|uݜaDHe/QSd?K+-V T [攣FM?ԂKgnYZlGuj([3)}771=Yw?}i6q:1D FzE`Bv7Fqw4#'\sE:XuG%\L;j-8]g{|T dԮ4)ղIݲf){'I1-E}Wt]e 7؁5`8?]&Ob6͸p;Sjx 6eZ[pmoFI;Qiw $ׂH2x8@dx pǡ\@9ώvo5:1Brp2IE]j &a+hb$dqs! :mv-.o@9ώ6)=ZrƫB[79n)B<%9 piQ@ J pha7'|VWJo⡁EJZ[Y7J׶VM.:WRĵkdqq:}۬Cc qD/e`*tu9(4ԚJet;_;hsfm\v.*Rԣ(oI*7\H{B yR|{bRJO訹`0)+kq $Lώ̃9s@9ώs>:|t9s@9ώs>:|t9s2;1<ڏu[*{]oǹ7@s\4t/JӴ)oOF;4c Zu &a\XeN! gGZJKRg XFW>~l<=ZUhJd|c9ONXN2~R>T>:Bs({V=-lTZIVP|q;ʝxKNĆhKah xZ) TE')_GN.)iKBJ4P*dk$h\]l*-׹7~ρl\Lֺ\ϞMIDy PZ zH-X vƲ[ݙ-M8OxWov&:#JsvHήU_,(DT{!IMI z3*'zpˤim|]n+Ψ% ɩGx=$~[෣ڑ% ώ|tfQts$eJi{9T^0ߙvq+DxYZu+qGy9Tppnت "{FUC0[4W-3ka%Dh9֑.9ώsj-JnH})eEJk~u*NMFT8WPK/ak@@9ώ55\bm\5rz"N1՟mj/Ma}t)xՓjFJiv"&\ݎJ-8^xgG*ӽUNoI`#RV.dz|u:Cs!UDmW.YYs I9ǣ]}U(vG#3nwR=<*~1k'lS)KΩZJ${KIZv"pTq ,xjY6OG4>sK9s@9ώs>:|t9s@9ώs>:|t9s@9ώs>:|t9s@9ώs>:|t9s@9ώs>:#ܣnhP} #?|I%4WXy_Qnm3Q wt"4یp!)hYm%Ih8Vԟw]}|9d*z.I =jMj~]>\njc5=H%Ѐ+AV7TUV Q]RIIZ#6i@v+VuEդ8z~ h[]ۓ! \^]J ot[wd\߯kg #J{tJ;d%ñۢD b<\Hp,$\EoۡrKnh-lk S+TzP*hsFK! t`v U[!QkZ+Ne%BP|Z}Miބ1Ψ%YM*rmS2-pa\PJHr^ <8VOjR=@(P @(P @(P @(P @( ֹ__@c[{7-U ?~qL[Ϋ#䱄{ʓe+nKgүkSIu[Wu֐p7P2) VU$Il5<^&4MB<[V#ҵ*uԚE1,ҜB\\zRh%#(m%[mPDTfr5lcHM=ɾ*2JRT(JVJ}|7)PQviO=z3s;o]kKA׶iYٺNHC{y?R;P^NP R\jkB#kS%{~|m<9y&`DT@r|f-N'9uKu:frғj`ʣLyRJHϴKӥD-lCq<\cyԐ! @q: ]II;8<kr0m߂4eR)5kW[C}A/r{?Xgk.g[#:\_nNTy-I[ ! y-t rz@%< w͚h;t{BW`hizItNq BVi=h{N*R:,>ΡVEwǂgrTE_.2V\ru5 JZ tXfµHTY UЃ3ǽ'K NIUMNd-|}ZxQjI} ubi[H[2\ūyY})cJiq5-EQrUUO7̖ļD`4=k%Tz[iR[Q+pH\#}[ɔTS)! |t#%e+eew-m_ZEv.HDfe=*Ib,tiZHPR%T]WfIBM95ݭhtlJVtn^n?oGRSmKM+- p('gdCtfy5v_'݋o-]fj+lvz~̣4GajqZJ# $qƣflJY7u܉}1؜E)Uh'~ 6|[$єdjZvFiG4r⧤0$ns=qa(e }ɩE,?U]RM]9'{-[wfޜaVrbPQ$>ܮX ̯pquS~ʿ]8}omZ59UITK}rwz{R2 W3-n +*V䒍Ve=;NUظgùvnͻ^Ǧ4sOҋ:=YQ>n'Zdj1)t1 `讏gpx-Jx+=ѝճhm- !IWQwm+~,yR:m>uvZnjI(m;FA߮h}0לͥwrÉLm\~?b[hRܖ+yд|n +]<֏Bb@^k{\ʉN0OOrcӹ=xXA"sstnmS}Qdbk2pu `ɷĐQ"DP򂒴t7pAÿR.:WMSrI+3m,s#*ˣF:.5~Ro+gˉGGؗtD\-In%)CPz*}?[F-{{Zk~ZX_aS4%='m{YSazY DfC(8Ym#dw ny8>8WzQodk*~Q煅 ع]8ho:=G:WINܥ7 t) R@Κ+XIһG/1W)6yO@c WdUUSQqyͱTaN#-RuHԻ%ke$g7z˕ FYeK[.q8J[O'N;2LxW&Vn.;r|v+UpΏ5&t)IWލn>=.]sjĽ%%̞5ةBx\]W&԰s߭R1ڝ騺枑 k]ڍAHO tpzؖnٻ%4igO (^NM^̉yKY`!%9gJT^7nA< ̧bƲ݋vv󝕼CZ~P`n;ee4I(v%oӒr|02\T7q2ښRgش*V'k4cVXSy)(/ѭks=hKuh` JIKT7TzBA `kpqemqTݛ+,_;!pG@RupL0D4 6R;`8@Jh7/رFжlٞog2\Œ%>1à55>1C,)'ő( Ǫl2v%ܔ*!BVRj+i Gh݃(v0IVˇkit&E/Jo E+%jpjp ܯ.z'96KXJ?Fьa%oϦ=},{]϶GLS(ZY⢕)Q!G'am *>6>iRX(BݔVIJM.I,f۵}Ki+.:Sj&Eɋ{<^D:d4IQJRMuiݴ+oDտly6eضؗ;%*܂2q=#(8$`ݮxOTWɕ!/.7XO*͹*# KBT; ~*QkدИouڇS{jRTHxU_ONT[<뮃s[m 8Sd( P$ܷz/tz^;6ol轤 9wjo-@̗P8 (Esl+taK)F+z[~ s+,^֍P @(P @(P @(k-NP}Oh4;G]V ~e~$4֌DEw7RWzcHna鮿aY ~Ў/SGojǗ:5.1"FzSO\ y5cWCnqz9mT_Bnn0솠:I!֡ p~:^5$ zЕK6գVڲZ-M];⩂sulvpͮhQ,vfTwcw71ʤh= 6OE66$\pmuY:1vli 1z8 R6  =Tsse-ti=F՛$xΆ1.m%9ऒWk8᱉N2I?ӝviZ96Q+կQʡnaRnN %Եn138F ALQ5~6lEYiiЄ`>rnOQTc9f.1M*@aJ L%@<8wr'6^9afS쳎cKjS4\(Z6[:˲e0dd a] 9k >+v[%L~ʕH(59+].M\7'؏&J-%+w}'~4Sti<Η` իN%=<\)#hڋ*LYGiTuhLI)QAi'C/ ',G K<>_>։j8LvL`;ۛ;$8s۷cluYKH IJ)#UNWɞ`i E-ZFYbd&ggu55y9JV%;Jr[|~{;4jTnO'"wbm\^tcYisP?h+VIZU 2R;m<PKxp|\AcbEǷ^*m2NIGHBZV҉z5zxR=Mr f pxJ4D;TFY:mndq[퓕[aT Rqw^m;ZiKiFmT58k]4[ u[i:Ac m|%+,Shy66?9ʲgܝӁhӡn);rzkLwKa lF1\>JnQݾ6 96ԝ[5])'pK!$Mm]jެJ_os} X)s 'vVlPz ݙXE|x|~˩U9+Ve:"T6uXdxݷuYO:y'n"o{u=OF;aƄoos5RuchY[`tK:BBr{5=[kq4~©.+O9J[ 7)٫u%f-#Ks wXynB8ee$$<\ylߤ\\sKUL WicÌ)yM┥(sǠb1U򗭖ziB)Yr1MM=Sf=^Fvdчq/ҟvdo+T<ű{_5U{gTP'_ݷv;d ,#e4}l0](@IY¢ Mw:M{}*qN4IwZQο4h:uY@9fs_ο4h:uY@9fs_ο4h ՟.6[Yry=.H{[U=[UʓEn4֌DEw7RWzvڗݐ]{twj؈Sl&i'%#HChHJR:$ο5B;hiCp@392@PxO~p'ihȍx'.ߔ0:d)ZA¥ulRl+o"gBhۣg2-,8K@#=&;ԑg94w:֎uY>SNӋm qTڔ($H=F͙.u;]8ЖP:9qݩ:JGwd5gSQj95&_sp=F]͕_Pd`4|eK&)Yo2x6P򒅀x1ÆkzmoE!-,HlGu<[mh$8rP' \xU\䭺Ja%.j[0[63swy*8_ZфFSI7~ rd:xsA׷G2d,2,(:'ʘٴaTWM{cWX.ҋM.RR"EJ(yj+#)*bH)Jme5g죗jο4h:uY@9fs_ο4h:uY@9fs_ο4h:uY@9fs_ο4h:uY@9fs_ο4h:uY@9frGIMh~O4}4#r;@Dm bͦj]% *lg/B|C@^,Hz d6`ha tOI3MInn+r|POsy ZA<P'x3;#v<ͧ[[>='ғG*u ]hes=2rt*Ӟc,>b'Z24տ.POSgQSv>0@cx-kqJa'"aԫVo$ۤK eKff ozRY+Y$7vwVњTQfz3QYLy)-8ݷZp g))M^qVJf /h뎘X.op_rN: pH-rr]/SoǚNڵ($ݼU;O:zjnC9&m u+j7(WS)Nc{wy0B4o~j$oyu Z:sJfVhl-6֥C_ a!aF#KX IR_ͧEZ˿YpZdmޙ}'}ңE\ϳzj7ۚ7nS2"#I}-8%/j͔wi[fH6֗5MFéq$GN:dY=ĚEKb׼RQo '.BP @(P @(P @(P @Vޖ'( ?'4f~>5t8:c]V .I_c{k&_c{h Ah UK'T9ͽ7Q%NR=''W96#EYx[2ٽ&&EK%Աmi`tatG.cr߾cT*TgMߛ!IKIqDڰxϸ{jPv}߻Wsڽ֛7mn}g@Mu7|ƀ631&K}6IFԦ jc6u=~Mv~ٌar_hog,HE%KڨtojB{WMҫZzDy!5_ݚMdro~sg`:߾c@:߾c@:߾c@:߾c@:߾c@:߾c@q=t's-ɬ8ont(dp#@r7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀu7|ƀm V~2SB?ćյɨwP㛤J̿ErZo~o~.ߦ留6R ]W)*YTr:x>:DwG}/,3j0ߧ-s&S{To~GPR9'GBZU|wW#\m4?|r9E0@T*~7|ƀu7|ƀo}U6)k}1}1}1}1?:~cUE,޾ jBSyJAu+)2yV)e{}1}1}1}1?:~c@:GXo~o~ҹ;j!r[;N[qpޤ=' G7^7]m{: Ōw֭ǵ1Y|l-NJQ$oqP0j1N%l޾ ]qje#;Bh#h |7ͤ}r.oaJpxNvɈ/YwCV7t<'7#5&l..STBd+Q8U9a*bu^^x2 ~~zx; 6RދIri=YBu/NP P= NN{Gs~𸟤ETu/i,|ֿs}ZpgQ\̗qBSKhʊP*V02+v;kr/ȗum!LS<ڀRVaI ifZG^D[s$$!CΧq8tp[$^k]<%7:uRő~uS0ҕIBJ()?9\֛uDV5&U(SK\JRW6 A#&u{X5Ea$/Uе;޿<'7Q4O h_ <'7xNo/@:t8_u9pMs|~4O h_ <'7xNo/@:t8_u9pMs|~4O h_ <'7xNo/@:t8_u9pMs|~4O h_ Юs&Ό2H@YPK@q4m~s?_ O__/!6^ė+&P @(P @(P @(P @(P @(P @(P @(P @(P @( O;,ھkcښ>Y}W(MtP @(P @(P @(P @(P @(P @(P @(P @(P @(e4~}~O4}4WP~GGʠ2InCZhd8stxsZzkf:3dyg;ù@W6iut3$dAg+C:9ṕtg[3is'-2ۅȤ7%Ң8-EAGt|j{ҟ 3ߝ~monvhԺr:$Il14_!"DbQGҤ:Ӆx2છѶw٤>3[ ї\qUy,(XK,!ƕe*9J dɴK4U/%Zw-zkˉ5 ;W3g*%uc}!9 #u)_VkMu7Zo]T~D+e{3l\$:rԩl8nnRelr%N@-םVvygk:ysڸ隺\/r٥vSqˮ[ӪEXTV$n0a$wNJM&ۻ1(%ث P @(P @(P @(rIUm_@S?j )M#%6 @(P @(P @(P @(P @(P @(P @(P @(P @(P ?!6ďlz/dP @(P @(P @(P @(P @(P @(P @(P @(P @(A '~Mq~Mku%W9qZHSA"/37ՠftzؾߎVmexNICHYahċT;dXl!Ia %iJGVJ*BF8Y.G/}T{b_3~:=Z_kfoG@=h}/37ՠftzؾߎV{b_3~:=Z_kfoG@=h}/37ՠftzؾߎV{b_3~:=Z_kfoG@=h}/37ՠftzؾߎV{b_3~:=Z_kfoG@=h}/37ՠftzؾߎV{b_3~:=Z2e";ZuI#q#@|~@vnpG)Kqڟ5]^&erؠP @( f:3S4cth\8oYptuaܵ^ioӷYO0 @K`03怯YUmvt+Jbu҇8VTNe`{Śxٮ4d뽷hzywp~T䁎W{ NuPJI:Y8n -FnL=qC^С!84?Z|%f(HIS1\.IJ r@k; Fk=m@_.f<޸POjN3ק-m{q{KM#zC`,7Upxgvl;b-i^ԗg?uX dpw3@}_aحjM꫍oCrm+*% p3P dd@u) A;cgz2zx` lhqʱ~%Y4V-R\w O6U`rjPRUÂG N/=A0wr7ĬLc=< CNiKҭKvuJ޻R}'b&ɘEjL]j1H[o3z BxF=@s{ NuPE4}ZB .58FF9$t m/ch׺gDwd\:JPǓ#6~α6}aa5Xsuכ9+:gkEqd'3zSP=`N1@sZ%=SCQ $'8{gks>W{ NuPaδ_J59֟+@|#bJ֣uˆ!&07.haδ_JIj-=d|qơ%I*@;=Gd3KH9۾,N2^ͺ]wRLO@@ذglo[G2E*1tt'459֟+@=:(Z|%\ymw,o.PWqu8(RVFh >>v7*{M#5ދr'@(P zvƶ6V@Dh܃IeJY&O_ӝiks>W{ NuPaδ_J59֟+@=:(Z|%O_ӝiks>W{ NuPaδ_J59֟+@=:(Z|%O_ӝiks>W{ NuPaδ_J59֟+@=:(Z|%O_ӝiks>W{ NuPaδ_J59֟+@=:(Z|%O_ӝiks>W{ NuPaδ_J59֟+@=:(Z|%O_ӝiks>W{ NuPaδ_J59֟+@=:(Z|%O_ӝiks>W{ NuPaδ_J59֟+@=:(Z|%O_ӝiks>W{ NuPaδ_J59֟+@uvWu]˥\e]dr52TO#/y4P} #?|I%4WXy@(P @(P @(P @(P @(P ~9@W?A;7?#Ouo ~eI{BlP @(C:F&}Bڣ_ph$(GiZCBeutɉ\e(hw^r7f%əiܦB}sw za:Exb>7;cEO,_&c 8 w:7uU$GfGpTIowR@^dM^!i˷ԧ:BKdw2rH9tl,h ?[鈫hS! I I$9㎊ˆֺR_\Ű}ԟ򔸓$-8؀?kb2{߂"I}0w1hƽD8Vf%wHr!L%w:A3;ojixܣW谢ُCirp _󑌅d` 띭Z7d fO]&2Yʔ*<mggzܹ}gc//Am## Rk*M7zHV{uU"J^fR 2ʐGq#u>ɉu5%F4mIc JUI<}2q@shڱe\*ԘHPSq́c9F(ƽx:M0-Ĩ\Xrč3ú:h 5IZoj[ݭSW:T~+ۧ'K:Mkk6NZu.IEL9(o{ug*Y c>!fAnnmy%j6ҹpto2] ct}uuSSZzAe򷖉(ZnJx?(O%deMŇz"B]0dd\N=HglA[;r*l)J֥LJlF <( ,g dB6Q{>ky. OoP]\hJ$gtjh{RsF)ː7Ik!ZAOl3Ă( v52.+8JCmR  Ǿ;Ff.4%m!m70v1RA A#6ҞC;g\7Iگ{#87!v#N"ˆݚb4Iגn7={@(hi4Z?!6ďlz/dP @( D+KfwU @(P @(P @(P @(P @(P @(P @(P @(3/T@F_Gh ߇@G~>@Jh7/رP @(P @(P @(P @(P @(k\nKr~@vnpG)Kqڟ5]^&erؠPtH],/i@3.۳ZKO4RHZ dqcq^;ݞ]x\5HL ˓Ue[%*cy 9GحSųjoW"rD3 Z] &@eCdz;i6]JҭqǯuDUF,`nwp8qJZ[@h ]"Hj+rz+-K kG  f;4[Z>_/OH%p+.874J}-M@&Kn*QPqypJ8=$Y:44LZbU\Ԗ[چˊWlH p2h ]K6˵-m+EܶK Ӹ-x[zF0ԺK{F@;j.${` cKFatSȴV9E ;z81dhTV))LnV@ጒh b4MH8BNS.{7ir\Wi`?"[˶-.kqp:pvC$ɔR@vݭvH; NחvHB0'u\=@_͏ke5;@N(_ˁ4նsӊkvDLԤN; {^s@LM68&Lvk=ʩ*^JNkûI$4kEh[&,iB09U=rp+(@f{Qszw(:ЎQ61ۄ%eH#WO@iz GJoVI.ðA  q4mn:Se{}P~#ӚS8qؤc8d /Ru=%L8`[+9{[/Ưmp^HyMw p f,}t=}j:(Eq-qAQ8$(`( d#[ QQ Hd'4bηŹ1Ff[l-$pxp ;W V򬗸'tqH$AI!I 0ǵ@b,t%KMBFji/|֒YzAlxn!^=%CYJHSv4㱂%N'dd 5jhڱKKdm՛R mwzFE;iHҖVonY |4{ed8~Ӟ85z{h0O3ej -ێO)g"iwV}yqY)S48lA94u2PefY\^LpBs$q?HY~ne%3;wR::p3(Ѳ7ISW9[|x$g00 -:U\ 8qiE@q#8OVq Y+u5孡ı7v%Hr 5ۣ@QJW<ڏx$Qƒ $

    v7*{M#5ދr'@(P :ٟ@hP @(P @(P @(P @(P @(P @(P @(P @( D+KfwUAw?>G+,nP @(P @(P @(P @(P @(Z?z[+kh Rv} WW~?y ~$\6(P  !'[.٭_e=E2Ro}D)$ `yΘ\6 kRDT2R*N7Ip{ W\ިh-TΫnfJVʼR,rI;=ǔ HHm:2Ԩ{QIΧnK 5#uVT=c''4:_i[Q1ʂ$ xB## Khulh{=q:%m1bjTW{Pp%Y g8 MmeQӬ[!YrndQˆH#9n{_k~m[,6D+{Bʖ@ĝӵ'5nm)_nU ['x++'n@Z5oVPP9)SHI FRO{ӜPôVԳRb]\5>Yl<N H9@~)A *Q H$(Cu7@T+ɭtQPHr! #t; g83@d@i}i& lavxunU= Awrŗ2fS]m'p0H'*6';iWK|Ĺ,<-8w8ní#log:No|90xѓ3Ǡ h7G\gEeWٌ2Wc= `4to }|U6&Gma }iI)@Q2@4+P2eq&ƕl IA8n<( /=-Z[Yc2mq0u7\^V7=:8P{=϶V -zuvz2"=JHTvFSӎ8l@R*G-VBCCw IWv sFwFX@4g+fӥ1.V7O|$@GȸGEjKqcKMv88XHU $]Zfܥ)*qJ=w1 mwV۶O=駭؂po\•ࠃ2hIi.OXm˼eGlj1%=7I &@hpjC$Ɣ^R7k @e\ ooS'ux$8NH#@ ƍ7?>Q}f|HˬMrrp 4˓V+11HiOnq3~@u􃷷͹Fh4 ZJ=wO%ȁaILqƓɗ2R7Gqh V3.mVc^. yjj(]PuI Pd=v(3/T@b}Tqg;]^=&̓EBlPeV5 7Z94J`UP;?חjMg;t'l!ӯ<[RRPxqQj길w)Ua)&8i9RЇy5acz_oWmz0^4oKqfA5 m␭ɉl!=m/iSzqvs{#;<x[P @(P @(P @(P @(P @(P @(P @(P @gZ#__3 A&?P}&_n_ch uP @(P @(P @(P @(P @( ֹ__@S?j )M#%6 @(PQZs,,ƶ\QqPJJP94kV(gwt AY#P$dd(Mh]l {&nF~cAe :p{%4ٱkŲ.P[lڀ)x`y( V46DrW(-7sۮY!N(YC))AZ0;^>TVz1[E-j7T pIȠ96q4kT0nw$Y}J^QP”H( E jXm 5s;ƀmYv׫_T;6J23;O#5=e:2M\s$`;]儀(w?\^ˢt+Ė%ܛ,ATPmE'I;}zcji%JQ (p㧆ppqwjRP\FطHԓA햼 $` o+FUͲriHq !Jq oN0aBVzSqVȱ!)(J)ʲj8%==$ ͞]-N&FarIګ ;zpz*l-%%HWqC9˘vЗ;M{M- ]q%9=8 cWj+ntd۲mO9 0QӾJ!<JI_LZMݺ$ .^C2BJP$' 4j2C%+(]vyST֢$'x N2|M $kuT NIKw$n)ԣmOGjs8'-~NVbe泬"//i%#- BcW )NBFHNMamQ&qfsihhIÁozaZy19/bbIk8\t(pH}M>zzpKbuoC)Fs;Rr8P6Ftի!Zm.KYd-t'uDpI㻊bMS78欌 Nv/o7Pz#Ti+>m(yzGA% :Y.6ě|a29|hΥji(!KUGjqĎx"]#iousRYFNp#:m̉Y!%H$ I_ iDR!@sڤ $oV+1]?*tQ:4tUCFrSp3@( ƍuX%Y&36rrヂ8z:v]q7OĎ (u┒*Sݵ~MbwNK+.—Aj`i jkHfh~\6I @(Rn6nw"K!mˉ)[kIR@VȴΏܹUțx-Nr ^uk}*BHJB}psPhUT5G&(- 9R@/BYvl~ ;7"B~\ɒ_ykqn j$% HR??hP @(P @(P @(P @(P @(P @(P @(PֈWE#/y4P} #?|I%4WXy@(P @(P @(P @(P @(P ~9@W?A;7?#Ouo ~eI{BlP @(U,Q~ ް}mwy>IKwr ]ٮl&]GA ]rgmE-ӊ#JUʼHq{k6ZI]磟g9ir,KjVTPNjp"\xJ!%ERJ(Jn8$"Q0,ڂ-UjbhUX:ܗ =1q@MuK#e)q6:OǏNN3jם zXBv-)m6HHB F@K|k[֫cB.ZS1 Blcdl?|xާs;e{!HQ>$'Ccv-JOR-ȐtJ/A  wpR0w@V6Qg{tVܵ0Pq`(Iv lRRTwZ5cs! ) Q@ !M+{ᜀwm\l4-Oe֒=ֽ,`!ܙordZJxx{@Il^F6M%}nLN $czAϱriE X+B}ÿN(vspް@:CK 0eQCmu+Y:{l֮VӶ]?2p\yJ)n珶vzgR 6=ɘϦK(|d! A ѥ鮟ɚRpv=b;te T&\k2WeNg@yI2Eٖsv9 .\A/E!Dc{t* {P@|{[6uTq/-9E JyHT#{ Zk]%ܕiX֖Nr̐T* Y@Y.:NTmҤós,Hm+pjĎ;OhTj6 ćgt)FH@GhR^viJk|\;% @@dOF~âuckq˫q\)JB9U-@cu$Nh ösM5%DI;93hZVӽVF$nVGJ U؜><2EF?Nj T̘W<^NⷊxG-Mh;mSӝ (Diĭg JA<w^7w;MOk1XQa``+9ONh {gV.Zi=#m(\_B H {Pi&,[f5& %cx8rw96Y6o C9Wj;%f 7 QTp@G`;{n|U]dDKl;'{@BmVjmv7*{M#5ދr'@(P :ٟ@hP @(P @(P @(P @(P @(Nwa:sVw*HH=$UiO#J9$W=t=Vh~O~e)Czm7x?߲!r.'⤁YiiUi*inW}LVɶ(P @(P @gZ#__3 A&?P}&_n_ch uP @(P @(P @(P @(P @( ֹ__@S?j )M#%6 @(PU.6=oܞo:LU8{,ڊ6ltNb #'pq4kUnF#W96V7@TV BrR1Ï2;>o)u0BxwYn;Hi%! H( lgY46u4.2PXPs8M~T)e)}㕸@yGq@v( viKfgiԪJOyKP*<-0lz~g]vk3q4#8<3 T@eoinC0ps{ IYonPw"8 OAH4{w1ю(M/Q[. fa/2IPII)NrƀeW;eoޓ{90V:SEt{]5:O^!l]]#%jh'uI=p# v[xzMލŖ A ##g[Jmnǩt{"awqpNOBƀMmєS "4Ԕ, 7z>>"Z&?4mX֥n_e߲8 Pu` ; .-3B9܌mE*(ZJU:8@f[VV;hpAձeIw( r>v7*{M#5ދr'@(P :ٟ@hP @(P @(P @(P @(P @(Ck?]GG+9!@^v7EH?$wk:@(P @(P :ٟ@he4~}~O4}4#r;@[P @(P @(P @(P @(P @Vޖ'( ?'4f~Կ_nCU0_OBlq/qW(M@(G^WfӚd˻IiY7,TH%H&J0HLfT` 4WgNGmV'W33n#OƑ$I&nF5{%rK;Jm-{ ^;S_M SuK>o*# !)IIJH;gV@V+=Th&9%'$Cm9tXg2B{Ux\..-$?6*!COaW>4QAγ+Zrmk}RJ#{9iXXrzWKiv:ڜRsU܌t+mVѭ7e'Ibh. ;*18x(nӶQ|w+kS޹;{q bVx%G{ڌI )Pt^Mp)R%<(!CۀuDgMcdonvWMkm*l$ܽ;*AHHHR(.(ڋݣŻXvtuV;2dZ BANJm#Y괌[?liym-THRN1@g*ǻg5u[v%NdnݦItJh Zvԫ7smn͛ȧCY^2xtq=ފڷɣqL"D2aErI=+`!kjjjwl(锶l[^@h6Bpᓑ d7]PL[nr :4KXʉ@3>P;J [ kSV3.[,xKÅҤ#^HF 6]1kmֈfXiI O(b3=]~+ͣLȃgֺ?:3Rqiz@JY;*8 3ZWZn:vocy$:1p(SƴZgHvy%KI9@jkNt5u+UZ*#)G@Puv:QOfi 7/ R |P=At9 Et .p\4t'BII3}~KӋnbawklf.T;JچߡlT~ޝnJIl T13 4]?tmQv2%GRN'eիSjH6SJJ:#6AKyAYNC#$AȠ,g3yםNƦiEb!a-d'ڥ*Sݷ}-pt 5M"-ĭuԔӼGl2h G_ӚNY'غ8 jHSrqpJ1@gT]>m @ ZiB~ARl{`:FBI kVح]e}ͨTK΄''ܠ zݴXXsX9ZIHmŁXӑ9F ]{Fa>l䴘sڴp'۶O(jN醯aF`SѻBeHRzaGu$T@I(6KFͧʶ—#v;fQIsdΤZPh5bdbZ@0GEuH)H:Ovז ҳ{ۺvO%qe5F2:@) 怄Fmб:Snm6b-Ȍq^@QPaefN^Z*JZ6cy<@3h[coT]iSڒܮM2z+H*/98`N6YxԗLKGeZj`P*$9(ݾm0Z 5/}xxO@G{gm[͚"*)y”2;^I YmK&kIB]P p3ƀl:7$E7Tܬ'K:'!d9tݠ'45~+JI\iQlKoVpIxd MKt'HLop6s`)`` p:=Uij'PXNfat7 Jle$mRL)Z:I$@ѱ.~QKh›Cosd@+ttГe.eOtZeMr?;P94#$9 ;Nٕlj"j(`2o 4Dp1I\%yԏ mKR%Pxn#x 揓mnK+w 9 $~z: KhkEDt( leEE-%H@t`5Q\,1n,x&OnTGGFFzE1@( D+KfwUUvY}}W4Oq 5$}`?@P$ o}{yd}d:۞wr*1ǕvE؝hh)cL- ;01ʂMy`X f%;izp,mƮvdpd6天jFP>` tP Ftskz[Re3[dY+Aq8cu=m%Ms]dm@gs9!'xw@hP @(P @(P @(P @(P @( g2+G=KڏmIHuBP @(P @(u??hh ;Bh#h MF?vP @(P @(P @(P @(P @(k-NP}Oh4?;S~`z?^lPP @(\\vxӬ[ְmEqI(`)(ɠ;:e mSlڹ%qAlI_!\U= t ۆل ^6"j땱۳ښIVNhVpHJ1ōWGb ژe$-[ÄYNI˶َHB"9d6JSʷHn:SѦ6}fڏQ_mܷ=f+F7*W ~4Fё۶wR>*`m$$rܞx{a(Ar&M" uK޴ -]7`'44:Qڕ w7[|ġhuH$ # $-oy_T׬ȱ%,[C͙lV OAO$i֟K&O;+m(-G|= .[Pz\3jnl`tpIFhXV:vtƓ(BSތn8,Z\[6 9yVE{TRr?JOEYۖ FӓoEEN421ɅwwxM׷gFOYUtbüw}!iN8aGt@AnjMQu-I߯tڮheDR8OjTT7Ւ8!$m'gj鋵XHhz2x8ڀ8*q$dL/Uu1Zzgډ78 mr{u( !H 㔤pNt[ZTǻlcݮ<==!%ـFxfh-.ŽbJfC#d 7Pk-+*2RxQO@88){i [Wdѵź+ [e*w R8HYOu퟿ %fYjͩ_Z]mӡG{>E:QGLyݷJq_ONx)jQ*H `: "{Gg!# (mA $r,p6IXwP[&dgB *GOi{qZ%=N1ڀ~֑7 l mݝkۅZ`BjDrCm8%tnqצvkuj}]W"6 \H, G8wkӶobNW8O%t@H R wM캲V۴eܩJƍ!7 r{]H۪ݺb:%n8mg]]?:()ːe)m *8HK:RZm[BӖFKIqR d[N =1ݙPN(/JZdq7I@s{$^ٖlkir$t1ܸ<ŠHVJ0v7*{M#5ދr'@(P :ٟ@hP @(P @(P @(P @(P @(g!jU'E-pa@LiOEVCwu2RǪOx3?j0?ھSCײGjt@(P @(u??hh ;Bh#h MF?vP @(P @(P @(P @(P @(k-NP}Oh4?;S~`z?^lPP @(][BO^Z5Yre8r0xtpտj3ZְFt;-=>7 i;.% Q%*-d9࠾>ڀYֵfkbmM%:7 ( 3bvu&5561FE߂aq=R7Rr YUkO۳QԨM:Тy$nqh I#hGr٭V=8*yEʩ(% x.'"smsȒݽ:#ya=̶ٞy7M ^(m@<[LgFA eJH vI::܅_ِ[&R#ղI]9@8#gRnĵj^Ϲf=+; d8FH&/rtu eFV-;d 4L-3-55dm;Z4쒄r0 V8 X6qjo_u)CBP (8d( n{V\~m|$f%1nZ0xdw;< ezbWkMր˺vSh)TخchI4wH/r;J9 QqիV =n%yzkImՄEjOlH92aD$)< z2;AtOv.6ޥz;0ֹ-d wxnႮbm$SnSQpI@C=e_X7ذ@bPVÿ́:A*MFwm͗rs/Ąwyg1ttqQ( 6dN{P_ EÕ'{Wv׶yhM.vtN :sT3ќrxq$]Ū7W#$[hr$dVI4UYt s/ONj6"-NÜT" 0;VqĚqІBB[C` p]FՎk#vt\y 2\^A1wO@w\,&A2Mmp$EZ₠qh 9Zs:"mJ].6)Is'H({>]@O۵,e޽Y#? d+v3Z);c{G&6/V׉DI twV#w#,g̱[ߺD \])h FG{@DIg6!)%\q70Sn1Ě[LlQ^,RqT!BB@9Q@P3.ɵ e>n#w6˩m'$h }DSm]+fHn3pn*Q) e \9⑓@l:M-gN!sEwwIܠ%ظE"DvdK6R$ddq@v( >԰<ւ%ٵw1֘RI+xdtpe0%ZM-=5)) YH2A84@b}Tqg;]^=&̓EBl[cQ9+4bJڔ\arlV{ c6];̐e-Ũ JA$!>[DbsW^ Kqȷʎ;Hm N< EJz녩6x̘: 2)-$2ZJ&$vE.-βyTI KI1H!6!@- *J VtG򿴿guP-P @(P @(P @(P @(P Y_^rfЗA)'=?=b79. V*'l1S~'iÿYPN|[_>}Me>WU?>_idjrMon3RiiXu %dtw+OZDf\xrq[6%«8q@LiOEVCwu2RǪOx3?j0?ھSCײGjt@(P @(u??hh ;Bh#h MF?vP @(P @(P @(P @(P @(k-NP}Oh4?;S~`z?^lPP @(JmZ/H[dys-G+F 3M[=m$XuLTQBJ ଆ0HIS'<{b޶7qӬz#DnQtrm n8ѳi*(j">.y.vBBFRNOpgdzYܮi7 Vr.^#-W KFR2,(aD'l}M*ᬭ*CmWp}г7ARTI'W=H64`Ie/qi e*Ml},ciFuU`rǶZH(uIKhd]H!_ TTIH́`[+S_Ѫ!DY43Tܜ+ps87S(SS͝z72J%GoKe@ ׀47Qzl lm!O#sH>5#i:n8Wb@ӑmB\m˸'=H]Ow&nR:1ešR A@MBU]fjO$ `;RTp@Rt*2ef.`YnpV>Ԟ ^lSLce:%* oo-a;  MqbG˪kN!GA7w8p6SP4mi6 t 7I~!|m$)jʈ௶P:GLAm՗MҘ:C HX) !*'}@d*CDl[b[]1ܵ,)64G%#*HsU6Wo[8H,r'CjuEEv$pGI%!aQR$vxT(A^>'ln ۴w-d*RI†A9 w+gukՆ g‹[y=h;:+S$:ȶUђ8V,,)yjnm/A)nzONl߫VFK}倥-q!C n'GhuS3v},J6,'EWlNk!OÜ 66Bs禱m<6V6.ЩQ{N[ǦwgbmKn" ;ǶN|e)#9-TSy0I taܹױV5f]Ӓ41bMA0%5Q$dUÎGZx lYjTEMݮq_^줈rВV% #'+I U66}[ܣo8ΩncdrB&xP$9-0ܖ^V 8;Ϣj+su늜?*I0w@@Uv'. \5e$!-8^Sd B3Np!X ,5B!)bI;Bj6թK~t |k[{(0C3@l13rLHi)up8{98Pٳ5[::NHJ  gwٷTUUnЗh8oSLȐҰud ꕄH*F( GSlu6Ю ǰZb̨EN)hXNiIqg)Ix [u uhXT,V`j_+&:@("A%qB(Q-F"dgJ\nMYUѥwjX^Ӷ[So~l koK.-!jNFB%9YКOZ[շC~Çn%l{ ICa-(u`P'W&[3X*ɰ.By+Ioi HKV2h ;fg婹kuD+4]jS,$! -J( B {P>t-mG 9vʹ=\qwM @(P @(P @(P @(P @(3/C؆z;0 ƔRV#)Y۟9W4{X۟9P_R]bǕu%4 FA8<@?ZoTKCF( )]?htN׮]XQ@iOgTW8*cHMNX(#j:AvަҷR#{eiъp#qB1pg>̚cT_t=5'ȧtH/*w-\)sp'ɣwo?T:-%p8IP @gZ#__3 A&?P}&_n_ch uP @(P @(P @(P @(P @( ֹ__@S?j )M#%6 @(([\֗})LwIzj]ɕ),!\m mKq HZ2':{{etXr@&YV8Kkwl6yĴ 3$0MͶ̫3ØJeФmڌ_oLjm"zu] y) mɀM;IRZ 6H5 9j+IB\xVqzJ= mN ^2P$sg#{P= mi{6.ID-t/c9z #/jCa5e?so쫪sʡ%$7HZhPtNcvjewqS3L!8 n$q+Am~VSA[l rS25+PJ{b $$p)4oMswezfw[Y1‰e|QBB GPڕϴpikdM.9VRW;aF;xdWTEX]1b!k?%]!!+G0s W7KڭvZ~1H|Ia).$ p{kN[ IU+wzyǩdsy.$#]9e<4Ӄpn9n3wIfxʒ˧\sf3O-eU-JFx,8(u۱>ϔ*|yMݏe:+K* nDUd$(秅fxhͮ:mn'RY$K-m~̌/go_'rmlA)AA;(NeW;o-j^iMn&pfqԶ#*QOrwrr 詼v{4<ɏ-N0xۖ.ql%i=O?qRa#~(iR`dupҥCUGSoMz{1͙mwL+R[J#_$XIq;䔜d'֦ [֑?^ڕmZ]fo%nzK(uN55ɸaN`gW{Y*4ImnLShpRP$pQH@\U٭UhX 8_b:DI (䑍h 3J^m 74YܵXW[\;r DJU9rrI8R5u\Wx3+u򤐐AyT>(u??hh ;Bh#h MF?vP @(P @(P @(P @(P @(k-NP}Oh4?;S~`z?^lPP  Yh>u⇛v3ڂO4FpH+sv 詺E3Ɲ 2yd6y8qkS\w}ajPQ .Ѷ jUnYWp\qa8HAH' bl}kܮ\[ێdg}IwБwxbk-l,`ڞ/j*7㸞yFRT2Aһlz-(6;m]5+re SJRrʚ1<䩱5gsW=xc/F]4ڐ0rS6@w8IT( "˰MbͬnN̺ItL:ZbT`9r78g!Dmiqv ܋u!kj<"H+ %=@4 B-A77ێRaE ng %d.d6mm. e7Ut(q?hFHKsw8GA9T8,QzFQvܔkHXB%XH [6!hk [ 5&J@Xh JYlں>zScGM1V,!E-'YǶvIvG4UDESZS #3Pbjz4՚j.nLRT@Ud( ;|{@o\ۺ'rJ׻ȡ* v<΅X:h;.ťuKeJT-%m+)F AYm#F[+ﶫ[rP^(dc}@Re#pPlD%z~._Ja-%,xOl8 IGF#@l u%eMʛn +8!JJhvZEYSaEHSWxtvZvnTr}JӞnw`TˇaMmM3 =GRJ[3WIJq$fl.<m'lqZY_;rc窛ih9>ٰE{,9w/9@WhW^{+D[l2Aޤrmz<͛;i7B+KW,% (JxkG*Wgեո nƴ\OC&Yi=hwM6N-$F;ZЫp^w:ǧ<=X**˧ۃ-{..|P yHRBRj#0rqYbj+j\?դY=g~#$3"mKB׻ 9IQY)OoH⬥6 7b9KՍ]^f[=ɱ}ji5VXfBe[۹Y$)XH9P8p|R!6-:ǣ-K!%4$U')PcS\~i+ecYSȸPq}# )<0lzN-ؓpEL SPw0hHh= t-kyʕI)=h _oeKI\uϑp e{drePR{gdm/;̝Eymm&L )[N *폵NrFH_[:˴>sպ;rZ<%EKp1( 3SFՎ]8]#nڔ\Flxn2( 2wH FFh f\1]|lfj#p[ 9)\pBS Ot}? |u]MӌL~mc%ATs}M>E+79iRqגü{)5PHC9)nIJ9%g= ;vhٖU:.+yGO@r_5Mvmp-Md)8HOHJNXH( 7f: I>2Hd2{Dn p'4w[Yj;H=G#ZTRU(#q *&jFjԷ7 W)l ) V O2xw Vcm{֯%"5)!QǂwpM+tD-S-h)3 $a]0Fh ݂٩`Wk|[2ǖ]AP9u@bŴJj m5ZyFO@$3@mq3:𵪭twuCx%H*QNA'9{u[ZZ4mZE09N계%9 '$ x3>bYvŨ5WY=zI0XBX60F!}&7˕8~BuVp  q=$:vmY523뭱 A㞌s@Bhi4Z?!6ďlz/d%j;۞RS^CܮnTC i*;$u??hP @(P @(P @(P @(!u~rKM/$'a).ҍ|M*SRI7cf_*ON٠<7}J}6Fw']c4Aq\G xgkSNL,֡ OMj{!CHO(k?zfNtPGթ}{e~g*lP :ٟ@he4~}~O4}4#r;@[P @(P @(P @(P @(P @Vޖ'( ?'4f~Կ_nCU0_OBlq/qW(M@(6՚|t5Fo݊YS}_ R!* ~-{:S0͵"+m'}J; "ߩ-[m'V\ >}Lwp*8I* 9·Gt 69gf7i:ĩoOAb+8^wGlwxeDd);c#8UN6++c$qI +HI^~4ݣeI^G󂻇`@TvEV6DnxSwu8ڷ^8#4nv96do*s\ ӋN7`0 x0}$74mi6 {Fȴ<ڜuR 7B2Rm#DL[UL4 iejYl+ tN&lRh/%Ito'Np19w43kcv״=9ߤaI* B@7uͺto[LĩhHzFU6 rI;=?:5/\^:*eRldՎPll AnW+S*amZeW4^K󜜤$bV[=x#S.ΖKӶf!VT)*(BŽV@w9u)FͲ%8QikZ:U@aA)m 2}DG%j"J{w:+ݝևѕuWQcJݺx,ͩi 6lˍ:.m zzk6.mc[b_fƴ'>=Vɝy1bZ GFx '/Tamur̥&9u)j{$P>\;ի To5WeНhq^~Ii c pRRtwNp{2j)oZM]k7kVݕZ4b/&)UBo$jbXB'l#E9]Vμ>4C:sULL=n0--8N g#fUUo2ys-<>qVYu]pS.oIkyz0YMzeq g‰H*8XPP$ONgcvXy/kiqFe[jloN@az_3OY2AZ[$!vhׁ\})$p 7tXW3/T@b}Tqg;]^=&̓EBlnv6/,A2\θmBE )T-|-[$O]#f;6j-ɄfBA 'xsܠ!{GtocPX'BNT͒‘fBoYi w)sXzeKnj" [r;mBqऩ*NRsd:׬W1Vwɽ"\ Z9pN¶u~MU@Jh7/رP @(P @(P @(P @(P @(k\nKr~@vnpG)Kqڟ5]^&erؠP @()m/kf~3,eη;=I r2_*NF;5mv%Z5M47-3탋; cgJ89jPơlUOc6ݽl.`VT[<1 GΉ _nQe7a [a.R@#O_m{ΛMrZ{ z#XR 1;9L@ݧi[cڜ5,\]J`*::PWTwGNh hwٲZ%w)Wq0'; J7)kliF[5[+[iemmIR*Km[XJ=( cUocCQqDinZݸ!n!%N%'U쏩dSnH[BSw z֌e%K$ [Uk5tDK4kqtuo9oeN JwdL[5Νڴ(ZQ2ֻm<8Z@ c*ẬX2d7饡:蔕;Hčnw@_uE^BW07 א6 rF@Xo#gzTع{GXCNH%\( HKcmh~ cޣKJAZ, J776ȻIeӗ=^  lx8Č5G(ǬuJԮZϓwYu1m9E7c|CKkiD﫶JrzpHsZ8K7Ghc#w>׎2( j6[s3$[%BxP)ͫj}[G۠Ez_c"Q(ۻ@H omm@mtΙ*6};VXWn.>bKR2kt ӓpOkMٝ,M'I3nDv}a2PRHJp8t`ќPV{"[JTGRպIJ(8i{7UsÏb$CG*ӣ*z! 8JS*QM_ݣ]4,ͦE\mb6Zm±t) \h |;OJa yАdwcTlϲL-ڝde7pl+S %7oC Mr^3'`PPXHP2 m&NkIpNı ҥA + %@ǰ+njlJ!֠N '`JAm6[]uu qAJ$@vwtݶqֱBcGuG}ӸmE=8Q+tw4mNߪw4[li6i*ZQ1@7Uۃ@jTmW^Kmɲ-X 2pI`%A+oIJ[8f:NѮ458l]oS~AI; P]5&6i:Elg8jÇm4ƕњ_LI2-@lmz" JJZ:RwRIlN;jvld iij뚂 XH$#*pI01YK.5W/QƥQZT$7\K$Pr@{L[mV}?ocim Qxw9{)@gZ#__3 >v7*{M#5ދr'@(f˴Q}ӺojʷӮI|-FVLӌ ~f٭4Oޢ&}3"e-TFAh ޘ%JHr\K*edy=Ok6O@(hi4Z2?h>@@N?'К>ASE~~ŏ-P @(P @(P @(P @(P @(+ZKw_~m~s?_ O__/!6^ė+&27d_%BlfHfTUGMZS%myhS!V└ JP @xϪ٥ѦȄ[b#4oG1 /@P08{mjKm)\Gw#9 =*Y/oEXJiEmkfSB J+RHQ trvR4~5Zt&sSDUUe0ʲqǡD`LmSjݠ߯X=_bu=θξ Ij 3@lORj[gZr K ws%'"tԴcEfq;qX1* SIO '؀83v{5u.oy%PmjɡG#xdԝvR]>(qs}*s )HP$4SGo=ݰDrC4JP J' * +RIh_Fd"*{9( D Ӧ5r)&cܦ\qjo)㼞oҚopܟ xi(%׊T08vP( {.GjTE[JF8e F] lyu} ˓%tHP; 2)J4M5sYXB@*Hqn~rP:jyM :$,)uAFhn;NʽGvIM6Cf%(ZwT Z;e#=[4 #]#wx+S9 9BFqק JFyѦRd\IQ87Ie*oz}ksM8b4 J8p'ګ8AdC;m 2%l)֕n`v7*{M#5ދr'@(}Yiͫ^4$_oZId֍ޕEe*J ]L+FHȠPֈWEP @(P @(P @(P @(Ӽ~ jcQZNPj/lJIaG9@PMqZoTNjכ~M9/v-p(dg% '鼟:[v%?EH|C?E<>cG4kY2\X]YCIŒԞf>s/ccvhlə!y!rBJ֔O UzÈПLMjJ.TPyK@e $PZi[~sȇ;ȴwˎ4XR .jֻV;W9Pm{l\JC\j–7wx5۪5ܭv\CV2J%ov dnҐGnٷtѠ$;=-!F9qH'RhKF0쎻̄ǛRQA NVGP՛;ڎ㳇j+ nRbҒS7B1tP7SmEn[ΟױD,*<Z^JCfeҺfzvVgNE ۥjRPK-)5Ԥ;6e'JR jq; sh޴>ڞ'N¹4:0NPo9ڤ3đi{BN6 l$i huvwCa!G8*j5vDYL:V1۠AhAW jGBJԕ>ۥPhsGD6!!@%%Ect#4yg3QimGܗv7I_7qJVFF@MAmIJNzZvx1ޔ}[^ϺRlOQeEOq.!$=kjPsJCYIjkۯ+疤U7@hɏ!/IY`/ʒN;)ެ4R͚kXE{}EBu)@*9U;ccrɧul6yǻ5JX`ib3MQ}zߐLE** Kʉl JwGEaJtg]矴TXgq݂oҢ6Iu+9P< `cwX$l~W:)4&yb-Q!T.m w|5z.=҆Uܵ\]vf]o !iFSa$ɒ 2zQh6<8` $rN$@dZҖ=l,[5oU\Lv)n)@FT[r6γ]An6eIR -AP/Vzxr&$pPY -^xcպn3(9WUQHxMjeqlf|ڛphXi)*$2NrhO;Mm׫=w{b̮V',!I)VJPY}W(MtP7J4κ'Q\"t?im}{qp$5o%(X[n d-[`{"P @gZ#__3 P @(P @(P @(P @(NuLi[˨;D'; vAI-UCEz"DzO>CvpNVLgO8)UIP ]%bOeSy<HT0PMqZoTNj

    s/cR$_R 7\GK 8BF KՅSriul)Z^i+x!>=3~FUH`]?4[{lq&N̯kcV9n=ci4I܌w(g8Һ;;gV-u*q)VRցQq)R8 ' c]Ji :YGO9qn+y[hRN aAHN8AZ6s/P^)|4RTv!ÝRNA"pB飵Nn[&Tr)$$t} mj\@ƲFmDQJ6b%w+l( nM m2RfCBQB)+#9q)1R/eGҺzPfdׂsj9< )W ٽb i۫A8) ʏon`TCov-n~lWN]LӤ#.!<@ֶ5 n54}_qBW,>ݺ$'8)X-8u.iW\U$]m K(RGwQ0ea;#6Q$MJ4k紐u.km H }0JIp5kwGM;_!WжU'R|>yLdP.\I0q=UI%;i%oKمZRÝ=}GqYʝ4v},U EKYt,}Au+( ֳDpP';^!祮;IK#=G֎F,&*qi/Wp;\ӗKew;R᠐B@5x_^O$R<7֬T=4[^Z"*J;e%Dc=m`].63bʲ-hjBTV1GWl0 N nklC9c1uF2R7b%D%*;%'ɑ-6-?~w=ad[. +j7B[N<`Y}Qn6.RlJhՇY=p'2[uk)ڻM]śk+d0Crn⑼Tr{9VxR0xi}3qǶɼ=`N]x8@3JǻlKkq II -nvpӊדMu73' LIAHڒ7pX= kQ}j&]Gt(^U'w!]dEy3}We3R P\VMRBrB<I$@vH87[nlƔylU@h(j\z%FcP`܂Ե)9l9SJm7S\4Sw"< _iRFR18~vɵ`IuK->c,)q NΙRlzA}[pC JpsqMF,{IcMF՗VhM}|nFBm=' "!jz~U[d))$)$xP4d>6Le;0Nz:s4~Uo6zs#m>,ђ OOI1mozZs 9j')S@VHoHt -'4D^oL5f&TS7VF8nnŊR "Ά$<1k&ٟ@h'A_m_C 1SBmMpy>_+&:@(.\[,ĺi ֫R1 SȔl q\찅`';ٶ.'b:PXW^mI=yʔ!{mU]6ޯsR"*mwY}tW~*]H–nN)LЀ ^pvZ[[\'^Vo~̇.9(c";ZZR 5=CrY/RV$Bumo'|7% oJHou[A{&m#i.jket߷rr{;U9;@(P @(P @(P @(P YXg.y({U~Y)Cg?GN(~V#'Ām_#P ɠ =@_(; ks W뼘Q.5(zh;+<j?a'X?o~jkfg_5ʛ'δG򿴿guP-A '~Mq~M)ܿb@(P @(P @(P @(P @(Ps/IﶿI ٹ/jۯu{LS/\GKmbP @(<-M>u%Z};G0V"6x*A;k-:o-Z.9W AovAÌ!IXnT0 Lb뇃ѭ^}6EE@-uIPov#vl{a^) v8$ڷJ@R9-4Tm fjn&ȴM͊{e3I 2 `tVõtg-Z5jcRÊSd(H*=M譖^mc0-FISԗpn~6tSuuZGCRZ5$!# I$q@Dm26ak^lTꑋbds,P @gݒht 5nشQsue&zTsvYPI!@; Amhp#i^PGkrJnHu\ZHJʔwqQ6]76ݠd^.o]yWiVm@ Iz1@Uv[M}M^g[<8s*u- ,~4.-+V(ĊIKvCjHZP9 7FwU7aZ*؞[Ih͒(<3gG^>*Mlb֕ AiX$3H@Za֔Ұiln8)^Q^p4ε"ܶiꛖ\kHk[a)SQ# PRᰞ#4ۭYgknXU\̺)E҉lqp g@ɵͶڥTZn}J ĮXN'x+Gz[zirs͜X~#Ļ@OIԫ ؎)iچOvdP@r)I;7RI918%Yz' LEzZfݮEOc=O:kRź4Vr}:Ib4RۧQ9(HNqPL 9'*2\G)xiFХ⭕q\aP4f lk_$%nH’Vq|G:{h*2W՞~R#di>=t[Lu82u.Np2$V(K#x؊5+߿Env-ثc# <w*RlJj[IϷoӒjlW$D*Hi\p'HxnMfAbVц*ۅZ˃dov]$kbcc@$!+RR*˃WC ueiq6%I+~Cc<ǤtR;:TTV~'u߁wr InVm&[q+v[)Kxu!IH)Q8 4[R'$BZozvm͜Œ$`SuI{R8vsZs3bSV䄭[ gmbugάaնېVS`!! =Ä' d c i:mu M#K"P7*TON;PPkZh}::ͣuYRyG1ѼJǧ9՚9={rf'ؑ2 e]<20INӺ]/ķocA&GHxOJ [ IZ'y|:s"TZ BT0 |g9ɠ;C2ʣ ܔRo=Gûў0պ9w=?43(¼[}E^BxBOH MgFN4\4̽%Gŀ|iwާlEW__~tpq A#s4$H @~mók*.:Jݤ6ʝSA)$xgR;@jCOMw'ƶTT RJTӃ03Mu1}i-I򣣛4{Pg'NM}{jM{Ӯ)tP I8{O"G򿴿guP-P}eWu{LoTS\?Gk6C NP ƻoٮ;v~!:Ŧ몬-S!6o:Sh)a+PtePtݢ hZG:MGBP) B HZYFӜ6 ]qe yTԍ=Jmg8X x͆@% ?i4Z@(P @(P @(P @(SտԐfx#?{~oC՟v 6.H)RH 8*˩?U d։ [C88{X?@Y9nEbI]E\~>G'ݾ9#ߧң}hol@T)";hX,NVi8@(, y4P6KmR\ AFENWdgWYVOMf|a[J#GzSQ>Qމ%|EKCJoqG=ں5\8?+v6 Po>Y1g:n%I\&7wwoq+u{ؑ⣆޺.~<'HY#H~<'azRn֌H^f3 !+vuMklJĦҵmδG򿴿guP-A '~MphACq^ejHJox O\}j?:*kh Rv} WW~?y ~$\6(P Sy 0?!4EM0`cH;ؠ,UO3p֚^o9[&Ҏnn)p4kqHPe}@֫UU\niisgo+֒`B`X>қh;n{|-Ym5!*Im.%ࠓǀJrrRlǘ54̅SJ68whYuE m[vwłݘC}rb1J@?mڒHK P=);`KM(oC^,\-֐D%rrsED9,;$$8 @vxo̸e9jHl"`%,Hᓒ8P6cpĝdӚ~d=%v%\%csx+=$p=A\A)w!vƠbUZ ڴ?u9# m[-Lkik^}R\n[e%ʔ$/r NI{bpTkkS]۷PCXX+* 8R9v6uW"havqTnNlF^,ľ A H[X@;C+*$@Ik;< {%ԑ졍bI!l)e##h.%aޛ -2.w(t, ,);3 hZPjU*v4yHBS)ĂzkYFGQ'WZqi6'F[}2!H.=r{=8J?FNj8MZWaʺo^~Ԓ$$=2JI9'9%\NO|ҫWy\F9YrT/VItz+EIEK+2Fwj)qR"7-e8J ؅HWfZ3vݕ}>ZJU!JᶓG<@w5%ۊvܡaZVmyà:jo <.i ) $`É9ԅ:PP/Ygl{s=k#g;NTFg-oA%a;р#ٴ:| rS7dO=Kڅcqu4bރ9NO)V9s,H<헪'On}*,˾ /+IQeYI#&њl%36{Rqǒ O)Cy@㤩YN@6]{:@SSn^MU=. nw7A'i}Gr6gV)š!!)Qy {^&hnջ4i:|Z43N ( z( z6g5ܴIjmAn*XZ)N._Esd[UxMYQlxN8@t5Ƥһ4Q`!) [ʒ( {P{٦k[d0ȖZ@Q8yD4:˳mO4vƱO6]´rA=S-7gNi'潦\By]rKq-o|qħ9.{UM0.X< Ob6s3@t4>֞ڞզ$jn')($p =ppAq@sli:N2˜")t xO d哭6\*.Qnt{t( ) +%@z :Gc6\"{nyEG$032Ty\H^Iwm)qYC )C# F78PFDu[1%A)O/;} c8@( ݓjNТij6THsqhq€6ki Kw-S.L͔6]!nnv 벐 r av:j]mv_} vE!8l%@{[-+0!=@A Z5 _/Yf&tRIo2O::7ikcV_iqi){|+xPmM,KMNuTē2Rqbp IBRUUӒ( RE-MbC]S(w%w0$aX<0@ W&l֥M {񏦀s=@9cp~1`Ǹ?h0c}41>M {񏦀s=@9cp~1KV;Sd;!‘q{[DyR"ﲽrn)HW P|p~1`Ǹ?h0c}41>M {񏦀s=@9cp~1`Ǹ?h0c}41>M {񏦀s=@9cp~1`Ǹ?h0c}41>rMs?_ O__/!6^ė+&P @x+Aگc JL6LZBJNH|44w-;QsB@Ԛ6V7:(^$c )Ǐp$[eS[nΏ 2:#?Q7GI'p!6z߸mi۔Y"蠙VZ }O6>;9tJ_r"JJ&Sm 8 z+EfMкvᢠ¸̊b %EA›p-a$ @&״۞s}MaD@#8 5X{$Di;uZ[JH RR@ ϺI)Z'mZ gO6mN/ AIpUA)@$U@OHjM^D3޶\S6B7[IἜmuoUfTŦtf9 xemJm w;yxc¨J/Z黅0g4Zw^?";c1Lt+n4>!Ms"[hQ>w]ΝM7unU4wU峝Ք=3_+LHZVu-e;ਥpq8@L[ єIl 8ɠ;vW#_jT[JZ;0JI# #0hpPPͲS?K[PKc'qPc6ecl}JVi(Rxd/)H1(#dzѺ]::_SjR\RYPJ]ӓ3N1@w}}WkUi8q. fdn8q@m)Ԧpr#YTV(opgWP}35mIPDv1niO3lpsm^%ܙ!Fuܨ?ݠ*ˌd3.Ї о9$oR=pof1ɦܩm !/:8 eYR$GfK/?n)F@PFG4}HY7knf$S>RxwI=&msi)b6SV'BČɠ0M3&ǵXXuL԰q[lW Vg]T">6.Ƌw{,Y!FThBWs MPkmfdXْࢩQsdv wFh m/T@b}Tqg;]^=&̓EBlƷHmo\j B.QVŲ.[uNFTkx=]TkqHR=HȎuim$KYHI'(}%8Y3-Xﶢ2@FnZ᩠];3M۝,MG8 ܡǂ{d1u]Z;kqJJ&%"K )8P A)$A/T@(P @(P @(P @(P$~>5reV^({U~Y)Cg?GN({aXkfȍAWSP @IXo=c_y2Ap{d/$:57[.or音Mmad/sP @ltݩ%t[/K*\u??hh ;Bh m;wYh+aз w~~48(DD2C9p$UrW*KlGJmMCUK;Uݾ]%('cl@OA9dTޛR7ZOsjn5t}OPF-yT-wn%k#P1V ߫+A2k_k4C{>ή5Ze7(ܒʞ;w% HpG@]7)?V-㡥l[nvR6餵/v+^q*JRkIVH)8Ɵ~tݳB3ѡn=24q5KI$N E M6nw5=X٣h7LF}J$0U IP]ީ5i閔G%(Ir wa9Xm~jz9).RYRH%8Ui͙$^Qdͫ^-+-v NIr 3@_Mo7)oqɮł)Xs}!Jۭ͂2I#D^>n;R_/X4ڡi|nT=TĄb]ފj]c-h57nu ̃(7TM4mZ:٦6AC1):l%E?ٞ3+ֶ}IBz*RߚX&9[R T2qr ";z,Ęs1pWRq Xim( gm+}JS02 '3yoM9ufE3thJڭ dH2$Y`ay75Fu1պ؀v =[bC&ɩLHbp$#;@bXq#nzQnr=*ryH T-v_t.qzh$A[QwuD ] 60q@CZَ޲Jg"J&g!u(%K9_ ^4Z۾lfvZ>S]by9'Suct炉:]oLLRbA}l@VRBN@fZmor$iAUÔ ` 'uY3ݩQ{_k4k0 )vͥp‘g#x2h@kˎW;0\Sl2dtdws^u}Ժ^iAdnC|- YGF Rps#\u%hp=4 x]-2WܦŶjb=ց&s:7]hgAV$0m&uVom-5,٤t%]ܑ24Z4n^߾wެK )% VjӶ%Eteކ,8i*Qw'=&/YǪNZ뙹0&2kBiJO{W=ii;Vf"O#\T\ R-[JH4XE8tɸh9ֆߍ7($ [en5YKlYn<eKRrJA_ү6h4^u:[Ⱦrc-)!*J[^F]/-KwԍOkZn[+Z[JxGڎ֭]O&uM5D#åIJAos0@y6VԺrkfv U4( % c+'4GkzbQ,ٝ- p-$n APhumՠb*m4!ʣ|IGgPi[K}AZSV8% >@( nŶH:ԇu1ˇ0@@^ ^";fwͩ̆e- *H$ќ1kb'*t㩫*Te8j/jۧ.Le!c:t]2MXGm:{Jq9[RposzJEjY Nhꐴ}%ČzGAN5iTU`Gv}ԛJz.д[R]wspGRUd% }Zd|9`:t^-wT١>ڄMr Vw@r7:MNYMX`Bܽ * l$oF .wkfwܸ`E{:Vq' ChM1.mՈKO< F򔣀NFzRucMDɈMSVpSx#{q2}\_ZrMRl)|+ iM:k)wFo*4GՎ)n'ZB%$Ą{bxmzֽd́Z~jeNpJzNwFG/:풅Ũ&;oGq-\MB2ȠCt`+$ iv )rB<;$8,ͬmz<;'RJ*toorDTN3ӎ-ɳڡ@CHDV^V4D+KfwUUvY}}W4Oq 5$}`?@P$<vڶܭUٻ[-ϙGؗ%k<GoCݹˆv34LxK2PJHCPchUt}OCԎt0z09֌86 %)(bޯq6ƧnEw 2n%8Kn3a\LmXNQ\9Щ(Y6t=5'ȬOb~ɩ=?E>[zFGqT^7i+V;]]:x;6v*@yjռUl.R6}lv9v?,v?,}hBSp1\k]9_O+q䅖8t[be`P @( anO=ĺ΋euKbδG򿴿guP-A '~Mm穦ѷ] zr=љvL*e.(I#{8ƭQ[oKīoշ(G*j]o:Jfѐ`CAZ:K***Y*%Y9zQn/xn-(=ig Q4jy)LoqdqjXS)Rqe̫x ݩK{FǷıFt4ݿlUmOMNun5 ޞˮAy棼ڐK{odq9mjK5WCvZ>)ۧS&vnat3&mu5/8ܖnZCSjmħuJ VBE~_elMxtkn:rwb*öoӤw-oM]R9 ;wVY7u~mYAŪT|ҳ$s:'NVaz6=S7&lPf3aZd7AP 5mBTx~xK_Zvԭy.Fj+,LVY2bCKA g{XZ xʤrs.Z~|M+=C닜Xݺ$Fz'e!keVxziQ7Ξ&'4FuT]qDFm]rC"!!¢Sq;JJ sѿǮ"jyuSiX8JS:RۼEGq|cV^n[8^}aܮ0#"4xC,2jR0psFç> M.Yt# u ݧ9WMQte R.&֩wal49a| 2N|ܢI.J;idOuԲY^~\mmvK~^5)dH Rqt ܥnwlRZZ˞ϫzNe.k>v[R$1ȭr )D-nGyDj{a51{$ѧ*sb_?+_u"ucz}ifnwQ.!P!_B B hē@FI}YMh9]u)FoV>ud^<)hy<_y)]EgCV@FJwHFN='"+c?I7T65e]OQgvZıʔԄ8npkhS5귩<zXRKo&QjK~ j8,`%ԣҒ`x=FKo>b' Rwm/"IF~lKN^xr$zI0dHfY~A y S 8hklCu @R%-t:[O#;5KEvc~Ю9L D8_nr$#@@|i][4ď ŗoMxzh f/5~NmzKJl ˉ#;²N8"uֵKͿݹa&)7a *$P{4Z^t j-bMFB[VAm]'#w2ާ[m2.w; ~+ʊqHCa; $\N-%˒M)ԲJ{@Dh=O#YiX7Vyo@[ŠxF@EZٟ@h'A_m_C 1SBmMpy>_+&:@(PֈWEP @(P @(P ӟ+k>8G~&3Q^<<K!#y#e?OgiIqg)Ix {Rokqg~S[XZT;s#sLM7O06Lݡ_#j[IQR q\2 E3erbZn\2nJl%@Ooq'M[?.V&O,lKeq* peA4vd73-7rXRŽrkWNUNuhx{?iu$O^lxbGZS۾2,rd s]<8W 5eYה>\0;KNxmO/ynrKt,'F@'HcJ'VP*6\vaTq6Y8d{'>:lE[ޡjK$xAsq*RXIHPxfq5K[PqΧ<%W*WqviVM3Ņ[C7K9l?nNǎQ99DrIH ²w㓞^Q*{QM8NBӞ'〣ќ=h+cO 7~HVG;fQTǁw A v- ,Sm\zlN '%cG_y\"ۚ}irKa$BRwRrph 4m\'ܙ-Zuܨ䌀?ݠ*Ohж5Egf\P!+ G*Ã##tI=l&}? pGekp?rt8TrF@m1n{ɠMe/FJH3$gPVsG-lTJZ]j-Z7;!T<8c^gWZ.Yr}zl^]Ң#Rpd@#reQx+SɧvE¸&O@]6k c 옩Rd6'u@%99 c'sX火hmQx Zm^Og/*HJdʄc?8{SV5\\aa^$!$!Q#P!.{e;bAOwtWCmnD`-”N견<83cchw;-}1bԂTVTr d(d>+F419Y!L q muCYn`]̪.Smc-!h9ۥ*4KalR=k NTJ?;sIl ozIYWKSM).2Ce++ID-Cvjύ^εps (JZ'{}\x`ui|-,vIG5ܠP cP5&Vߧ(G[Ƹ?.X1!!NVOp@gZ#__3 >v7*{M#5ދr'@(P :ٟ@hP @(P @(P @yVs\g}oD{x +CO;p~2 <;hGfug2Ejt ";ī+Wsi.Vy{I.Vz`[gnF:`b9EB Fx(tg!C2{#hY5v9kIdáEӛ62-}%ٍn!SQeᥝLiߒNnY I.8!I J:}JBzHP4m5%u+>ڮ]{Eհ$oyĨکҢPH'ST4z ݵs$[a>һKܹsF2wA"]N-;Υto ʟ%%đڟlD 7$ZvqOooc+:Kh;:=n(rkjV# B}geݹ;xU+s}̸jmils,-2pMv[+[rn:Д4ˮ)GJI%z>mV\=ɝ ӬU\_f#d!e/6f'7_5u\5Mjݻt&=4eS^,NaдHj;I*S[B@;tDv"ȵ,֖Xev~\YM"ʮy on)!Iq RN3|>{.2pҰ4[ 5eii9k2PT;JH);UZZ|{ mCZ#>Btb2jeOQ o׻bwݽaWj:vn7wOFmqܤڕnUD RHJ9DA(ݫqM'gYkikݶ2`L: L~H)K@JBJ y­\|{S.y4tut΀sLĶr4(}%e)[<юo$HGv5!TΗV9fz"FE]s2 dHHBL(۹7؝sˬgYw}GSzeZEͩjeǃl-rDGbH}6lVVjI]wkdγZֹvC>Dk{[]4WPے2QNBR3UQ_{:( |}ꅵkMOW-%QxMc $⬋Rv˭s.jGꝴϼzݻ \d!GqTۄgtx_7-vMo .bê\օ&EEF{n RR&ooӺex?׾ȖuCiFn,+}\%Tg:2_Sd[psgH kkqW]:;^ݎB[f+NryDF㬺ۊm\O\m-  5^ Mu5uiꮻ'P @(:/ҿdT.  m~s?_ O__/!6^ė+&P @fRl{~ն&SFv]u儡%J'r{jrL[̵K:KEIZst #SùU\K6z%]Cܣ%Er=7H*7[Z#c;QU:J,zw;=OBȉcNE{M)r )J,M]lB5#'Nj.ɼھ\GhKf0"dsadք()Gsr=уCt~UlTwoz/˟,5~ۭZr J#TM-?V8(qjN*q; ٸ(ӍYnek읲]FzKNk#{mdɕ wx f30-iO gڤD]shͮm8%+xI  ^ u.64O66[TEGT6^(JUث8 rVPiۮ"$ #%+yg[ I,7)z{ij5!+yVH A` tiM=im[ exbI'$@aPcl{809qrD0(!#T$@iE{P[DjBc>G N8>6$ .8ɑ HigtN#8*:{H]WYuFӌOjf<$4҈i;E'F- ˗ִZbIV Q4oF.띥K%LK =I8Wj BZnݤ,P-onxI$r{23@7FV'q@yb6Mw$ٝ"TN)iRÈPjxxNumŏIZc$Nua*AY*o( d-dN ^s%)ђBӪJB)[0Io+HHPB\px ON*Ϫ4K$(e=qS]PH)l8dv캮B_뵥P wyxeC:Pi;^b~Ւv bТAA^#>ДE)1#Fjidv} cN\fy׏ NNp2IOMUvY}}W4Oq 5$}`?@P$֖hl^! PF0FIFwW/[;l:@yIe2A’J\A-@Vu@6Β7{k/q _~TiW&⧰nDX-:hl&f[ciہ]aC.vdHu<3!`Ano{UTto[SEu.#":2Fh BP @(P @(P>vϫIBIx( PE>JDpXCw{u!9&qtf ϭN'ߴ{>a0go۠3R9ܗ_VW'8vPiUyP @MT+C`U@Q;f)BJQvhOJ'ϼ7䧜> o?wGxy*sy6'Tɧɵa(P @( mad7Z9@(6m~ ~?:-%j.M:ٟ@he4~}~O4}4Vڭ~\Yꓤu$Y\\x<i 5!/J)lDd"u}OOZpNHB{T! %)h r:rȤƴ2S["+C+x!*J;Qq%8M@m,X7j;۟woa*bN4Zvo? RT JI5?-4l9:[ s (g$+ԡBUj+oKmQg SZRsGdY 7=)]: Z5Sȱ-r;yLRQ<-OU$rw]Ydvw[DZ֛mn.mWK#ӫdj>r) Cs^L(#qC9wU\-滷rƷ$nJ7~ґ:5Su4emشN1q $BY [H-#MA-=Hˋ[fW=+x##i4RЋVEͫ6\0mVueJ-g.-;}n;[&4Jx_.v5~+?dO]kMܻ[V'Hk[&um歽[9?ieg[\I[d+K3yu1yn*-JJJBVSN+Q;HRvr]O7rk]Q'.|m^pFG@qnL8EiN*%Cq RR Km+!⣄l>]Eq'u4\ aw=Keǰ7O M5)$bT]֟ZU!n- ,Bѕ8֥wВ] %cSnәKԝP5ґ.Idwm-!wc%c 4nxR{j+uݥ1VKWKY&ͺ*_饮uuu;vfwn3.wkII_ZJFz)=6kR:Se@8 RZ-#+R9Ʃ?ђҽYig$L~NֺЋ^ؾ s-V߬o5ˍ;i[,9V 4hBQj0ogټE3M;+u݉4WQXq7\݆ҷyyQn 4I|xdobugnWe~n=\;]IHhfۮ;<Ҷ)UusޔA;bl%E.JĞvVzddy痊Vϲ$V= x"<3!^H,0yo SΗ%%4i~]fwf )JwZve:65>"v6I,& f)\myV[aA8@^w#m8+kK'n)Snֺ?[KM3mi^Mbkb޼g8y(l(|b&wVK.^Yw `N-v=I&x9D{Kq Am` ĥsQMnUߍKT뾭n LzݳmlOkgmź=ZYQ&_-ԡMے90q ʔk'tүâ[ZI4ۡۏfwo?\&kܜe\ΕRAO^bYvT<ZSjUrIzenΧ{= 6j ~ztVͳP4%)zdզJ‰S.NRYe#9JYt;Xb./.ת%F/}oO[wm> _k- "$F(^Clcuה=^=ݔz]KmCқSٟRfz& DQ!/>(JA.}Iۺ˿xԓkm_>"4il;LH-rmZ]{Pm.̗9-H(:HM9ŬO. Y?nyc^9WnZ-ve6n=2oi~}[52v%dHa.6 nrNT2)@R{vm9KKm~SP @(t/4 v&_@S?j )M#%6 @(Pil"պ6q\%F77Rq`c8 qkwZHiS$*%&4Qd,*Q)$6=!OϾJ1ZOWlH{`u@};mnXSu- \m<:ʲH #s>ţ˶{P^S-Y3=ڸ&-<ʔ|+% YD ~u5-ZJ8*%uRJN0@%&ئɴ{j,޸oإVTJ[ZͦRRUgр=vK~*3nT kf׎4ބQ=)k)[VBxsܮ2z^#iukM-՛CZ:ii3^a JJ ՐwwljY<ƿ8b SO-mL鍅k l3io֧g*]DJ]#(a(*[K#: ]nSVyi kӧK ={٫wY6qMd!kg7y9)W׀T8#$TZn4%-<춢Amd)07l@Hd=EM`^&ݣ[!ǺMJS*kL%/>0``q%@cQfԲlϾl6-}[Q H9&ՠD{HW{rYq++pl-AGw9D%TANƠ@뮶~,HVFduHK{s$Б@}uS=xvצJ3o0ys''%$n `'#ɵ]nl^uȡDuE0!()};Yb+YJҳ.&#Fb$pv7*{M#5ދr'@( 5D_O"y PRTwV3pp@4wGl//D!s6Luc! kJ\tT QYXBRms-%W;8j{[~1Dw%KI0c,%I '9 u;FgQ=2Q\ 1Pw)CI%'$TIQ$ mlUTkt(#o[Ui-&:ph BP @(P @(<~M] 0!ˎ;6)u#g4+מ Ӿjg^x3N_@cTښ} L*#$qH;E~\g}o|#Uz p=JeT>; riݥǠtUmSg_%{Z}6>^?M>OuWOŧj{?|?O*οErTP @(P @(P UUHt]XH=h4!v9ԄT"|ͻ Czks~}˹<%9jw.sƝn6̍orN8<: UI=tSqkX櫌bٶ&tS;K_ؗYT6( D+KfwUAw?>G[WtI SǷNN6)+'Cyҥ Ԩ8- %CqTjJJs7NZɧOG=T(P @(P @(P @(P r+K @B/ ?A;7?#Ouo ~eI{BlP @(u>ԵQmЭN!1ی-qjZF;Q84!ކĩDrRW(r0{x2vVQB,DFR`p@Xu'Qjp^^ ؛[oB%.)n0 Hi ρ;JR@)ԪD3l0Gp/4vq=%5%Icɼ\cs2-)(+&vàtu;ʑr'%Hg نꝿ\#uӺ&4$g Q*$Z7HJ)ZHSHv8ӀVRN ܊aҨE"0xkUTm_>}fgmm=O I,CAU)a!<3ps5 R|؂ۘ:8ZQM]]]ƃԃnMy2RO!-I 9 '5++Ar&-kAW&RtK6, oN=pAA vxYRH HR!'EZoZshBҥ[Z}@sbI)c8?hiu!i BJA6{$ i--$[̆nߚBhV겣px`oV}vD%%Bѓpqn}@Y@QVN85P軽O]zDk">,#9E߶'rQ[nvxl15<l-#*hhi4Z?!6ďlz/dP @( D+KfwU @(P @( hZ[NN0ږkc͞.z `wf=z-z/jԳc`crr:Z^"#t@f+H8*SF^]4Z=վ~[?~>Gz:nX4ɮoruf7>k P>k9+|M@~r|Cy4Gnܟ%X,{wQYΌHnߟkB;:j];}6v$SehK֩Fi֤Z|.3}nMKeM(ahoWW})I_^ K-j;1J}^ŞԺHۥrJG/x' =3YF-Zkuc%+-tn33]u@m*à[Wl#Qu7!'x}JBUk~S[SK޿z* dw5Dk=m6mݻ5+&Z"csPOy Rk%/~\[ۻr]}zq-;85ش t]1zFuu2ۘBm %h( QRBqlmSA+ >kf$ŹuuOU^~Nخ3LGTu[qk]*yHJRR|e^du'G__Fd=qɣt<{!qK2L%o)tŹ(Hm WJtVInk{u5uvW&z[ٝ):GnhڟKgU&zǏqW%NB߃)-:nMA)K vmۆn&Kx_ޮy]g׺5i8LǓ!eC)xm .#u.- $dgJO&ݸ7tM- ;2OT]wi.4+cio\(-AmÔIqQ90]7 &FMm%e~hG̹F1OWnzw(u=NZGV}t:CcGhF'8[YO#qPi䓴]jmɔna aV0T씜~,"n>R&%-kNs\@}C\ts75qp(KW(ZYt;Ӻ%RkX~Ջ4BTVuJl$anR)*Hi- J?k%}_%vё%kN=IxݒgsY3y.s{ͅGm2c#yJQI+$EY^:zGvK{KC~Ta?Һ_͛fs+$GZb\\:\)[ubRQZ&ZRs)JeP @(t/4 v&_@S?j )M#%6 @(n5:b\UD@TJ8!# =ھ84Jveޞf:&Td/:]šTvBӼ4PJS)wSz<) S'PV[ZAJ;J۽cLz2ozP+%uEG;hڒ@ym[i@NMܚo]=K .oGjhR;^Ƀ{emhiXA8F=ǍA.f՘vhYe;lBocNA$e9(e~ꕅ,^0I1ܩII˙;CiK>ު-[pE>,萆BIo;c1S@POUm{d܏ S L+e򲄭J* )#nF$ M=&ލhEEQ^C`%*P rNM[zss띞Nn=3۵Omlۣ$%+JQ2xW53ZUkiMIsKJ3.!KxvlqOm@MLhmm+Sl#qwwc6# d {xq'$ .5F٥j>J Ɍ[-RFAlOrcl\/dR+'2Iр1m:F5O1 BFQ eWb+Qk[ BĦ- xFGy$s w/٬ޞeXbbFu2\OAV8{l e Ond+#H;h9RR!Dg zG"t[n snoiֵ<eI Rۂxڦt/:hwטR!-9[!h#+ < -?;UZy;e!1kq$pጫq@NNW[N7af cS ㄫyўקcngؼvolۅv܋tS%ŞL) Za!c95M,>#+ߝ^70]lWX{.!*Iye]R 8?ձvY-OsnlJrvίKr?,b3eyM1O,fc|']̪o&|Sn[JXQɾ}LQo}+}ha#rIsbV̅'jym~f-o;ZYuaiH TI+#Χ)NNrwl6l a/'?rx;e<˪w֬2\JPzA'߮66u*6{ysT٦8Q6.㨹RpRP% Ip䄍'8㊙pZ4ܯM1|%՚nWӆ]&p+c Wm zxw&prULI75xz'Z]BVA]yڔҷZ4fߛcД@$dvIwk:4 y-kiԔ7'v4W^fjrgzSEΆ:#.` Y{a9 =4"d@rSV@8H$t8zk Oīehi|[+%vQ^ݵUz2ȋu9yˠ( *=yV{_vZ)~a*JCP-(JGjHn׿I"rۡof[;{]ٌE! Rh nr iww./.Ţ%e^}y~z~E۬^RNM[.qQe,W]U}ΩzLI[}ĸD[q)yKjnF9ws5WenKera8_,V+Ud֨N4q5:y< *I$K%(ojKU֝&Ŵii 3oo;Ke x)/YYxi^v"lScOceCS&%G}rN,Ѹ(Ir,$.ÞVȶM3EBNY}`$͍) }.%Aaro9HQ7Yur*NdݏJLa]%l0]hBaFp\mZ'pqm{:"Ԓ\~{yVtxvxuK$){Ğ8dmY+.]?9+/{jRM.tT7$#'w-ѐ0:xq9hUwsg]nH6ivH~f\*,W\pRKIC(ICV[eeZ譝mn+R²VN[N!D[le;Z,,jqqBѼq>>>>>ǡHmEԔ/83@v]eӳ4?'4f~Կ_nCU0_OBlq/qW(M@(t`[jd:cs8"2$>͵{G_1l)md5PIQ5oc)!*O98 69$ܵڤr?` $[;$CEZݮɁiX,.Dl/ *PABH{`;X -z농"fg5pqk'ZP\$ dd,{[mOnս267Jʔ9D8s?=$h]Q.1Co|Il%ԕP*=) 0Ev4MŨݺʱ,K'mK[%+!€R$ ;8<˩6yvse`R􋊛[aЃʳj<RMuuJ@I;RwN2H*IZ ͊b1󘙹>&lwj~qRHʊPNU =iorl;usVн67@ ۴8 FX􆌇:r-5=ߐp%딲 ڥ$o`&Μj+I\b'Bn,یM+{ÍL3- kR71%NF;EZ+# Z7RZ;'mPɳ*tfS7RY@)b<pxb/;hx;> }Rf$wEyn4Å'u#$pqW'mPtސfK!w27HQWF4ݦ5}T@%[nNֽRR2@II4ڒZM%I ԁ=6u^)ޯ2ߚ;%`*R񂕥cOsI h1 ћ51G-ʴ˘4:JF3;ɩtߨ޴:ͷZg%@a*9pl H]tYʏq\ˣlv:I8$0IicKlVdX]lvt%8H[KB HROvG&ͪlYC-!%GH3.%xIj-H'.$dd2@\pw( ΙRv [6PS#u@Mqx)98,7HIRֳ$v61=OHę r m/#$Btff͖"\Ng牯S{4ZeZnCh0BR0x9mc xe+# XUFex X_-Z;vNlxTI$Ƽ(M-]&lº@Qe2?H'#WSGu׼j;'yΥ͚R"Ћ\/Iz~T۝<3v Wݼv vZn1ڶ]M1y5%YRO+;#[[XdqjAV{~K]'Xެ..Az}P RpNϮ惡֬b\IVRO N֡Z/D?np$nRqh bP iRZܰ\!Ʒ4ӍݞF#J*}q>+F419Y!L q fδG򿴿guP-P}eWu{LoTS\?Gk6C NP @Q:θ.tuHA[H[m)An$JFFBO@Wq]--rMaKqf-o|ݦ*O$vBs@4nږxV]hWx:R;>(*DeÍomT`ئ޴%˕Q黛9z#zB[m]e- RlJV?cٺiIpӷ=/ ܎ݜ<s pP @(ٵ[kP4l=<;-xfNzhe *88MOGé{ʸ`t gb?[P @(P @(P @(PGS?Z0Nwl}[8( anO=ĺ΋euKbδG򿴿guP-A '~Mq~MN(uO0S:釲(&ZPq-F98x^X].GZzmnP ED W&prRq@XJ PLar},T0 w8 WvFbOj./"5s6c(_[z#`:^"M"9 -KJl? Iq̝YE;m?t( EjؠFrRcAS! 䔤x mEť@;^kۈy_bKKwX5,>TlϻSܒ%Ea#\:C*_?=MuxiB v&<^x;5H>9fvޝ5̛%9dy K, ,]+c*j8|k?J?=:hnDiTbZrʤн-+ws;W[_F19֛ݯHMtR҇$7a*.EBde6[Yސql_u\njʤ+#sÀ4Ug=Lκ;)t# X);9NR4Sj;Nɇg&8x I}ͮo7)ͻ2NCul6hG@'4`$ܞf:$2ARH!)3P%Te*B' Wӎ8@w:Y;s.a% J:#xtgUP[GQ}d"G[POH$ChR۬NF)=3PmP}eWu{LoTS\?Gk6C NP @|<$4B\ii)R2H#( ٖPıK$J}JS*y %X)Z(6='Di:E8j^Vw0P+sM>GX-0lv܌ ldGa[I$/T@(6! 1<ڠ4Ғ8x_wDxQf6x_@yj6kwDibnn9J$-Cpws3 @FιAm(B&q[Eayےxx`/]KE_=2T{3~uzoi֩b2cLU *H~vֿ%dI@s ؝]7w 0t;9@fr:l=F6cx'X) k8R[8(D(uVhj\%O$R Ԑdp ׶ T!)JҗV0XR-%*VO87ߜMPi+um!2QЗBtnnݱ.66M 62HmJD>Un'q|Ấ4߮6vw>ި̍19El{)7q#* -I <=+fLmFvmmY/Ͷ_N BIJR8w'!$$:httɗnw[ i璎n՜%!wW3i`CgNivqNX.TN t o[!C7W۪a7Mafsv II{P @Z/Q48 p]w݆!\S€:u]] E^.HL!sp7@>rp{Ǹ@Gƕno*f'iP'9xd @U.7(S3w˸%s"( 7H8u}GF"qf̗pⳄcپ iDSp{TぁӚRmŤk)Zi; p()8 NsQ+gBj( iߍ NNRH+('=d9܌Q$7sYBA$d `ۀ "عՓQmSwvҷN,ql d9@p6Qw{:=m6H&*XZԴ8@H@^u H] 7IxFGG|€gc[C]}mnޫJ]mISx`NxI1@O} muL7R($5-E9/v%3( 4&i XuI:Gx`9e[~X)g"4n3DF]d Bh 8sI,oum "Ӹ)%B'?knmӚJ:b{<{kQǹg&cF-MVjvSZur-\c%kmE컙t N2 izCm:fΦ ~[2'KkT@w8(O@4ړH)$gP{m_"S0m92$(%wQ$ g@b j[֋le]a\y6ЕNxiX ]u,˽\nc2JԬnےIFxyx4)wRC\d:2AP4]csP?vOs]HV<7ƒd@)Fms,Vgy椻m;iCl%*;ӪaGn6G!g}T ́w ‰! I$ tkVÐԸ  @V4xoZHHe V7Z0Opq Tr73MiK-l.FnέgEFP@œ8{pJ1@$O&qʕyB{{=xbٟ@h'A_m_C 1SBmMpy>_+&:@(PֈWEP @xi'?PgP  Q\V}HC njczi5OT-o{_YXH@(P @(P @(P @(P @($&0Nsl}ğ^';'ΎOvOU6 JzU^yLjy U@iTu5m}.#ٟؾr7%h ;Bh#hB!i s@~BrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrBrB{WcN:ڊ嶕%@ ATvv6qS MFVkHIUo/g֠߭ ꫞%wB/E0mV/1QﶿI ٹ/jۯu{LS/\GKmbP @(</Zr.?gM:$c@8'`~;+cHJ˥9xI08ANj_oז]Q2bMan4^V W tRRTs3d~mMH4K1ȒBZKgy >7h שk.ZIPj;}v|UMiu @,-G{q**ǀFu-b6쐉mɒCM{'9:h wd)>>R<,&͋eemw/tӎNAR@mѳQ5-EMEn$34CE\oV>uTJrᵰ!HII'H8V( WmU׈q&:d[[' $@vtڵ&:MslPY KsUg4c@ԶK\M)ߵP9֕U8N88VǷ\U1mDPZo PRq'p@vV&쫎jJ%/GI*JH=@T.,P:oM]Q쉔~ 11#G}q#9Ń &aǎl4vBG:jS oK]7&ڜt [RTN Հ.@2APb;Jy”Nx@yc{jQܶ4]b&*5o4m'O ( &W=Y>Vͱ1[6K`% QG8্oI2o~.nmrZYt(p89پ]zn-ʐKz*I ')Fh bMalEȎVo2c)a  5;j;na-M $O*;ջ}?W_فr1ry6▱qzOɷm!Z^4}s$\wu9FjI#[vؘG vt̼[1@Vx`}< jJ)N7w8p&ZfJbOHSY<0 H&n'6GKnZ9HSg+ 8 pw€]uKΤUhiζ\Š!eңґО$#a.I@}PPBJBRI={PըmȄ /JOw 4cgP5؎DZ%7.$xnVd+'s4wU;Khg*zM!%\mkJI9^ wExԗ.QzvO\\ 3E)-)$I==3+]L[aqdPV[$)H( c)NiwonYj2`4{T8~ӎ8L)ZR;[ KmґРr8<P_-ZOʲ^lA $$0R@g@6Nд&|r"4q9V&7e og ?hѪVcJۖc ;rJI 9MCeq]ugm-!,"ҷr1PGnRG(GGllNH f̹'k\VBVRw;G}J9@[ŏr.$ĆmL}TA NU ;<ſL\48P딯]>WrQRsLͷBэGisۚdx_GlG [Fx~ P @(P @(P @(P @(P @(P)١%ݐ{NJ۞m^t׻ã#M{O;cXfUi[.(=[) =#v5QEڢ{! GE\bzo@jJhw澓B$N:2^Et;;'GM?}~z r2?h>@@N?'К>A[P$ڒz TA 4mKVwR 8 e%>R1-M'iVXڞvMhIp^?T%/kK@)s76XT NP(viWԢjZ|N ՅKUD6r.oM/U72-DDA88 xU%xFrkK|]6-_q,zcMUEEQDHӉgy)SBv8GKWӿd^w|5-ZjOUk Ζ^_ld X wsq5b縺^^>%P @(P @(P @(P @(zIǀnTK mʔ{W}B܅ն)Pl4ݬ}+>Ǟ%*f}t  MSUngy(-k0vnpG)Kqڟ5]^&erؠP @( +֗Y.ȎcRR  QdsgE-1w9,ł)P;l?kj/ VTFݎc3 8ƀ;:ֈ;Q*/( ha]#%]w=:(l^j~ۓȶwҭ(O p ˺6ǭʁ>4'elq/-HJ0s=8:\GOqU G(Վ:Ve'<?Ll^{kb627"he Zg;6ZJU >@G6_HCmMB)[r"&rJҕ H9Kڦɵ֞mCf5hBV/+;t81@Eo2m3nnKӱ^uP+ '=h 6ٳ]ieAf6v4d! u}o'TjSMU F99hm|0GPkc]cQ;ۦqv0XarWX^'GӍMnivM.0ee @s;*$+*'=6O &v466&ÎTUOo.\cniu7ͭs׉ 19D#_֠*:jHo\g_Y͙Sv)2o`|e@[/G"{ }a$>ۺp(mi !mbN')ĉ ZZn- +8 Nh GauiV9Bm+]dK»k8eIY/Mu@8P51jF9?@p{Dtڒs?#:h n$zorNYKϯl  P;;6Q0f4vM]vqU93NЙ3i2F9?@CjjwY/{A;ƅRh >ҧQ:^XٱHzoǵbAឍь@^/a j՛h̕V:jl8tE5F9?@Wl.2*S,iY)Og@wn. zԸ {e,gzxxBVZh\7t4XLsŞkcr Z㑡6{@,M rfjVCckj,94F^c xmSNvv:qqn^n粝Yzz us#.FÃh$r ZiΧWS@ڌK̎C eJ!A$#*{xr Z_{$6r Zv*#'"i)5`T#?P-?=[b-4^QeחMDh걼07@H666at@t[3 %m^U&YcI핞8?_aPNmR֞pcͅGVw]kcr Z~ P/P\o `"2[KH(H GZ?!6ďlz/d}O]A*-($S* KKp%YQ#3͌DaZӰ7?G˱"ؠ9dXn.LliICB*RӼ)b:,6g i{M`?E]>M`?E]>M+rɀ淺6\\8)|^ڵЅf4-gsB8z+Щt_Yqп=ފ} Hˏw?q'STG\{1g4/x>7B>9{.kgrs.~b+ZˡR[;Ȃc+c>Hz^?g@\M[>G{on;䧡z[I {"]˓[PiE\0sڲo| >OKˏw?q'STG\{3-'[ՖNM9Ez7>{p=Z>__= ~^>Ok֧z[H.W駡z[Ah. .wW>~m&mJJkڬ?OC?gjz>H|?7޾= ~^>Ok֧z[?{p=Z}=n~πjz>H|?7޾Ϩ62˫g.mrQ ei Wq$짡z[M{^=zgۇz>_s|?/n'S~o|z@|׭OC?g{^=zgۇz>_s|?/n'S~o| PSƖnSCr\k8Rvz[N{^=zgۇz>_s|?/n'S~o|z@|׭OC?g{^=zgۇz>_s|?/n'S~o| >SڱG“|5_~o|'{p=Z}=n~πjz>H|?7޾= ~^>Ok֧z[?{p=Z}=n~πjz>H|?7޾c]gqooӍ}|>_s| = ~^>Ok֧z[?{p=Z}=n~πjz>H|?7޾= ~^>Ok֧z[r{j B74!io.:^O9Zz:75}G:fmB˝Uγ_cI[EZ*OSԹeoo{+ {e]m-'I9krЎM#43V`A&?P}&#?q⢀6XquLI)K8##1V6N̪i4k;^vttgRJ[mV5, qU'K3ܩ^q3At%(ܜe9Shv5|M)oJm>CZ/uN^cG(u-Ex))VFGRsM7םre!7m]}Uj*KRߟ2G$i BG%cMŒ~c/UiQ@(P @(P @(P @(P]],XFFZm*\qDr.0X\L.x;om'U_K>mGk!֫v}6kt掁owq9,AFFV+Y$(9c1ӕj+YFKrW};Bx\#FO;RrK\[z_g7s; |6t>:Z*#q1V\m~厄@S?j )M#%6 @(P @(P @(9#kgw}pnՐvrk,M2u[$!sya4MkZ;oOFcu on!$} BSJHP @(?e{"z,\XR$uҥ6^u2 nʜ oW~պVELmY.[Kn9K*7^[iit(}h@oP @(:dzR0Ub^]T;|12pI BIKyŖє ~+6Fݬ--;=g8B\J׻ɩGj^N:@(P @WvD5BzwV(#wTE\^wH1+~ֵMQڴ.W>~vm 76KhGXYp8+ʀBXخ=oFn%(IK:ۊin4 D%@o+wP @(VjeSYVc:ݧ nrECцHkt eֺN=!ј:jCI)Bгn-FAR(1>j`i jkHfh~\6I @(P @(j1vt& ja\izr5PuquMɈݙwq;9s:@(i Gնg%8C¤ۧJ;h Б{$` nڷJȹɃ"˫%"5kb)mg2}ie\+m-. P @(ۯٿS񞵛{$qo ;t}'=M{2v[1 ugpm9@hP @( SJLjQZե/*ib [(S2k*P=ߵ&ݾ媣skJ䊢*#DLGaDqmj( ^(^ΗjͶkvAK2cKJE#\kE}sZ9n뒜8<@FSV 5Su)Wk\YqY*WX S@(3m-C4K?rbܠ%Ne sxw( _N²uFRl3zIL~*Lt,)K\6!i z&P @(mQ-9mljs4ePp6҃3Ct$p9gVڼ53EݤmCbh n-rCT PAw?>GGAI$g<` @vQuQ§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛P^*|ٿE§͛PC:Qos/̹KzkeqҀ+PH-Tp ?7 L*n0e{M+坒8LO7^x{m5k|SQ>CRăp5ռ%e'򛽒NZahwP2xف[Hhk;ky+}ģ}Ũ-ABQ8J 'h1>j`i jkHfh~\6I @(P @(I:jtɓK|KkK>Ժa7H8@[hP y4*9cCynorhS%E^3` @(ꭖik}^o6LZ )aCy! 8ZN2R˳-)54Clƒzh}iq9W(J @(+dUZ^mMMnnJ$%< (o$!`g ZIJTAq@rX6a|cǼ>To8ZjQ߁T6zvމk+~V/ngjw;!/!`R}abF~Ty(̓IG}S};6'V/ngiO?ԟt{9X~ퟭ(P @(ﶿI ٹ/jۯu{LS/\GKmbP @(P @(P @(P @(P @(uG&^vnCpKF#DҝyUԡBA*R$I EP @(P @(P @(P @(1>j`i jkHfh~\6I @(P @(P @(P @(P @(<4u˷+WiGr`C0SvyPsIy(,na qH`mƚ2~ SmTCxT_䕕)@\nid\X-F 0ۥnv>2LHFi&Rmjw*Rm ~9 EuKMdF6S3uS/)e$i0PP @(P @(P @(P @(h ;Bh#h-A) aMk@9{x=4aMk@9{x=4zn.:f&{UWGj{/򺯛KJ.W[ڔSB('yE! *xydž|һnWe9 / tpNP#^?عBum[W)s#6*e )ME8xZ׼dD|-:NI)i+5fwqZD+۞>2<'!xm(e;:0qçg{F\8J.;Mgy;E+7g`+BPZi4UK$}R̽#pj$saX~;<ҳ8AIRXGOMԤMMp~BNOMg'#K(ioJ?dūb*/?iބB mj556kcS,in}I[eD% ᨮ䢺K-'۷@#lZgNdD*jolY'aM@ HeJ V%<{z|>rhٻnCW. FsQ1%8l$ $FmRZwvmsknc}yXK"6-VWVݎuj vSGa )* )V=|5Z|}#geU/qM$+ucxYآwW8_77T*vh;}u@ {=7kDi۰IS3dpSgJ^Ӣ d]IwUΕ[S i_1"ZPՕ{ [`.+ ; Cݷ+eKuIݹuqwUvD-VqLk˖8mFtqhK/.V%QfY*->ڭ^s6eXͅԆ^a<"hCY% ڬ N3u+];*%J]wn==9QZ$8[}c q#Z^U3֖{P77_7W]_Ԧ]? HhVZRu4MQ)E٭mR%:#(%+Q TTƥtl]%;_vݫxuvkKW4 J! MgЕ(%ƜmrU#wW*CMjeP8R{BW=_c0s5? ~zh=_c0s5? ~zhFC(ZV;9>h}Oh4{Cn7h0V--ʒSG 350_OBlė4%Ϛ&Dzy^@>k֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZk֠_zKRZ_zKRZk֠_zKRZk֠_zKRZk֠_zKRZGjqP @(UܧJ˱ `R|8*qjI/+aMxztd;TKnH)"9&!M䓝oiVrR{q{T4շr#7IB:(oJ۶j;w̦kJח纇KQH7RIʝuEDxS7cgQtӻm1䢒.o!ԫ+*i$'|AZ~+U?Z5[Bƣ݂iغ,xj_a}=4pEx!Box`)]mhPA†JKUɯ6f㳋Z.OS -~o5;н¾g"B)-!0@vZtud]}ϯ6C+tԑd{O=meJ9W\2ܗ&J"๩@B B7 I6۶qnIݻqOWRIx( Szgj=Sf;n!3 6m L'*QZޭZKؑF~Iw=m͞T?ooTj+k{=b6Kn$h@y]Bۈ#BVywe%w>J&Xf&@蔈$s]6wрF(芏r7t9o/*"Ɩ[`& T$-䶉V{P ^HPR1ݿF^9%/Sj{;ϥ2,:`w;O]4濺bD!/pT!ԨYBx*T:B_EWPM^ ^_ "Fn.KH-0RlK CjIR)HUzRIwe̶׊%o}Al2CӠye&1I!J\uX} OjJRR2H%'W$r~;ݺ4ݟh:К{J×2|K,-ʸ:l )Q Y'7vaVUwU;N+Mګy`aq @(P AE!@sC~@vnpG1}ܹݥ8η+o]V ~e~$ZvŢX,sٶn e)ա1by %o:$, ,+ Jͼs|򽉩z\Rvϵfwnwn5bFU*$dsh{)dp;qVgoʵf%Ocj{6mQguM7(Z!ƚ+j.Gq{^ޤ}d=;E׾Ųi^ٵu=T֧j>-O:-$gyJrWdZNݗrW{< k][u,41 mN(7%Qp3m}fas"h,Kdhҩi9%rQJ%dFjӿ>h|5>-Q:5AU8χ#=$Fۭl,$zr7/wy.R2'uԺI}݄imԇ(SkBgx)aAV2In_쟴o^>7TkF[nE艬NulOn R[ZV*)6ݻrg<.g_?9W*gыyR[޺w;1nadӪ mku-{PE^j׿Cӽgnr/['{[kعXYv9q׷{%ELtǂZL9'ppn+s _V_ø+H7XܸMf%q ~1vBaDw2T$M.wW꫿ȍ/TcKòěVȱ$:!!AYq-p 3qXEJ+K+x6{fN>$%HjO%cZ-[EԥvSݦ斉nދ| %W4U5] R2nl6.3f%ُKm# {ҔovQݛwlybI{W~WYr$+t %sY:(TFtZ&"R0!`Zڊoӿwq;`5:mAɜmDv֤6o-@$o'',ݾ~s+f͈TN\n* %-9 &0ɖ9nI%ea). k e.4|i1zf){l4v0Ɇܑ w{a~q&an*V??7"nV*=+]\ !1,)?7%}[Q JISYѵ~W[K?R}R:wOCYihqaWC{yP}ҌVm%@ Idϫ&ֽmdžel۷W^i=;~^G}'W7ٺڊ[JT")o﭅%iO)v뽺5)ukxK_>V|_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_= lTȓ`ֲTp5 ?H]o![ Ϣbh_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=_>@:zυ|/u| |_=;8!-)@'~Mq~M֠:WlWPJ@(P;jK%(PXfc u]Å36/T%]LZ,Dw+AIri5a`5V=??md^vYil~W0^vYil~W0^vYil~W0^;#,&cڿO #Vl4`6JP@H )JrrdJ)($}u?MZTu?Mt{?^~:4uzh[c'|#ng@:Wu?Mt{?^~:4uzh[G9a4 >P @v }P"8fSq^T!a|3tPi뫍Lן)% Yd}@b.w gX\讧o]qt5˲KNX7ObRB^)i.%BJ R1˅7+˯Oe؈[1Lk<欐tޣM~TuN[qIw< Po;;u45>ʵs"6St#}(IZI˛F#$WN^ۖEjsMloCWn C) 1*S҃O q,[s;CZ#%eY>*w|o:7mCmj6zGŨCeŴ9&RK@-\R8E>~)~EWv;Zi{MmAH+C^m)P)VVwF J ]x)/&]/[+~qD-srnvYBHWڃ* 㒋_߼ {nugu$;v>啧WYšn8*Rʲ@5,ybN+urkZ썊Xmq7fQgp;)BKYQM+_;^ݿ Kƴ4E??2dJKK ƜKyQJr+k]OׯljgKW~jZm%ռPۼ%!)`Qek[?n:;{47Psz{OnEe ͸CC iҦLXSi8% Tun|i=6f_.>iQb;.1 % KJPJ}>:qv)har|sh)zCJmK-;u$(TRTQVB2*w5MMN[缐0.qogrc Z ~vF:kfUT:[o51ns)yjYLzK+|?T<9*{T!oR{dN;ߏȹݫu_iϚImDbDuByVql<㍨cy[_ RoQdø`UK2qSϹZb)/R-mРw A{^e}E2yv/idɭ#w wDDG)梢((˭ ( ,$)I9%8ɾ;{_LiFګxiAuoMqu-鳗bG!L'` rGjq~Ԡϵ-&~t+U-3*Co*;GVŠ2K'~~t[hN)IXڳ~Gnv״vs@y,wyNhy|d,e*rj;Y۹&{^:#nKzQW8Wc;jzJ2M#NۛmyQ)a (R 9],]JQE^`1a ‹jm{u߫FmTY9O4n3ɔdu%[z *Zq-ߧ2Z-M(+&%kUVˇ-pcPCT8 !B2Q$9<O puVn9)=)ԅQ{m%ҵ] ,YhZn й}oHS2y>Pw8)VWݩ]ů#möj }kZ/l.-1u,UdMr < KJ<ԥZ>e,& bi'f_^zZg\0"d?B[Ki|G-\ڜ,fVVnܶ6ЅXP+sWKU6{!%PpA*o$cƪtywkq-[ IKw-Nl]qD&)-Q%[@; k1eJrMTF寕j5]ˆҭ-Ɵq:49q[` dg50)v})Z.1e('}عiurr6d #2Jo+7{^*ШՒg>Ǒm]ʥKr*Uj#\"jFcvuHBc,!PHI )^sozܻ/l)M+вoɽԅNJiW)/0cD']-%o RB8"JQ{3zBxQiJQVe|ͥ{-Il\2vK nK\}2|\z+3RVKb\۲Qogq3T4vpC3"ݤ2nkn6n0x`*p?s}{nURMkVy,ϫ3ufyqt2$nT8N@$gVQe+1/[تN*ϟ.lY9N=*TT7O"k*`~VCSkZ^7/ΔpJ.6c v\ȋnDTJP[xBpUIYaIQdK}ͭ aul%n+jF%TA[e}R!Ѹ#3%rӳ^[e8XNX⓳GJi, QP%N2x&.9Ni5><9)]5Z'(-ۋZسG.C}lE8r8',eAT՝ٯw\JdcZT'oKYw< mm1,O]$nU$VM B'R.+-۷l/Y74 өU+-@|#|=wxk*RO4r4=Xetߛ_K{հG @v }P"9?A;7?#Oòu&>Cpduj*ZRdおhIFkF)$aV_Ȉ05fNwyW@+we)(I7)]Ym)л?e>d+ 醖zpO[g= _\WFgSVBsd9A®>m(.|O.@A"3lGvxԻNHϮ֭u/S&]8oL4ӄ%j}j?0Q]^2bʿ$3>Z ?zg׌#\WFgSVB}*(}j?0Q]^2se_c]O[g= _.lL|3lGvx>͕~If}u>m(.|Gع1Ϯ֭u/6U&>Qն~aл?ebʿ$3>Z ?zg׌#\WFgSVB}*(}j?0Q]^2se_c]O[g= _.lL|3lGvx>͕~If}u>m(.|Gع1Ϯ֭u/6U&>Qն~aл?ebʿ$3>Z ?zg׌#\WFgSVB}*(}j?0Q]^2se_c]O[g= _.lL|3lGvx>͕~If}u>m(.|Gع1Ϯ֭u/6U&>Qն~aл?ebʿ$3>Z ?zg׌#\WFgSVB}*(}j?0Q]^2se_c]O[g= _.lL|3lGvx>͕~If}u>m(.|Gع1Ϯ֭u/6U&>Qն~aл?ebʿ$3>Z ?zg׌#\WFgSVB}*(}j?0Q]^2se_c]O[g= _.lL|3lGvx>͕~If}u>m(.|Gع1Ϯ֭u/6U&>Qն~aл?ebʿ$3>Z ?zg׌#\WFgSVB}*(}j?0Q]^2se_c]O[g= _.lL|3lGvx>͕~If}u>m(.|Gع1Ϯ֭u/6U&>Qն~aл?ebʿ$3>Z ?zg׌#\WFgSVB}*(}j?0Q]^2se_c]O[g= _.lL|3lGvx>͕~If}u>m(.|Gع1Ϯ֭u/6U&>Qն~aл?ebʿ$3>Z ?zg׌#\WFgSVB}*(}j?0Q]^2se_c]O[g= _.lL|3lGvx>͕~If}u>m(.|Gع1Ϯ֭u/6U&>Qն~aл?ebʿ$3>Z ?zg׌'J*ut;/ =<@[ĕvD;n'J?#Кq~M֠:WlWPJ@( 6ً:hC!īz!a.J%wGI?Ut: 6֖<$䚗%x.>R ]!mG8XLRO0e6a%$5=ZTU/9RN{Xerwߌ'efoe2 g~6).n!1؄ځ#H<;jJ2NDQԏ^<90w\fk-U*A$%VtXa%?9o\F)vu:g8SmVc1ډ<R Դ␠gw*6W8R9l\]pu:RTVQݲ])YvjWgkfP{* 2`Zo*m[)g*HQ) qiJMgmԕݳ\sk<0mZ$Pܥ$)zu)+J͖KΆޡMzO'c:b2JGCB8M#e%&˱Ko#6t7eZ&<5cr]<I',$㜚.[tܝΥAW+AgR|ଡ଼gWEg7szclNijh8֒RA 3,=HUi[]-+>M {O,2b!-IVMsυh]qZ3_yа|!ĖJz0G-5$\/5}^OKJ*rjߍOv*ӄRO\ս<ծt4Ji!G{VڐüD2F|b9wFʔJ7@6<š*mXLD'tO&#OI&傒{ GWIPq5m:Rͻ-odrmpM Kv:"*0~oҕ[KmN-GN eE)N̓өVUɶv(apIIA,bEWvg2m7{Pf6苤 ($\IeGmMJq8;)S)I)P'8!$kTP~ie)9pkKOm0*ڞiA.:N2VjO=U^YTcmr<>RZPqP -p-iZVNOTіk^ ֵ{_Q%,O yƀJl9Ow>$=!9<5$Y4cy(}KUu)sۯaZi8_6癅^qnn}{qL޽=ܭ{ۍ5U󿅻ny@j[N\i}䨗/y]0_qo r+. *$mn֖W;gh:z_. 5o[ujѡc۔iM ŵCEN`Cm \u)VK͔RKJj7e:Yųm|+UMj{cJ 6Fm%.!OH HPR^C䝦p}9_9;ZY靭2mLleP-Hj ZJ vhTȎyLt^ˊRI$ֿׂ2O[4ʯS^˾j9)%·FqDF \S"B*K RxVN>}k|X̃x<y@9玀s:ٯЯ: $8{s@3@3@3@3@3@|JIHVuMI[Svާ@:OuzM#>h?G}4h]9Ar?Svާ@:OuzM#>h?G}4h]9Ar?Svާ@:OuzM#>h?G}4##$2ny(]n 38 ZV29?hP4?'4f~hOrFJ8;)P !.`thwĒēݠ!X##@~r)̎pE                                 >9$~^#MNqJsmBz(0IWUƒwQo?tE8%96@YHҽRGo?tEz(5+@9$~^f)#JPk7IWsYHҽRGo?tEz(5+@9$~^f)#JPk7IWsYHҽRGo?tEz(5+@9$~^f)#JPk7IWsYHҽRGo?tEz(5+@~<SJH>r#/=W1樒b$KJPoYz~Ԯ?5y5z(Bi #hҸfB€P<-!HS%$pZZ-P,fXɊ~*V*t$'V-.b\Gھ"ޤs^ D%ki ̀RBʂL~-[6i{':rՙeYMVĵ)1H %AIz>v>+Y]|.Tp\6egV]A3ZZش\y2 ! 䆊Hp^mp˴l]#i3Қ51CD<\oc{zMIm擽]-4kir;7n{Ly]Jl0R#佌ʹn*gRI2-YCH<; bMƎ%-\K6xJ"vy[y/ky#󹦵$FܘFt*v $+ 0M:ZMJ/yYMat9&Mr]OyzB|=fnKygxk1rE5߹Ouoܧ@:oF7Sf#)P[(Y}c~>k1rE5߹Ouoܧ@:oF7Sf#)P[( ]f.ZC(0w[HHxS]/t7FT]n  S3M)[O(TirTRRMjٸ2B/k\!#Iz1@D|?H$(h}Oh4[<9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[WsyU{Uzh9oǕ^[W\0h<z(<z(<z(%=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=r=Z%sͲ[i pq"]p~Vv]p~Vv]p~VځۋO[dvܣEIt4TtGU،))vDVԣHvy]E4w<z+sQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQsQW%JA>o> #hҸfB€P7s lF?4tI-B,smMIx m+PH@w+_}2ثyZnnt֜_zmnsUPr(B$ptdViF./$au_mZ:h.TMjezcaQVۨJC[#tP:tקzˣDڶZ,NzBHw+xq5LfhGN':jJYNZ5glVk;kM,kЏdZ/GI 4K[oCeV4\ԽZm*qO#J,S䎺k{jn%ԟY2:-+4ҕs܃87F_Lmbn845y8筟SޡV~noɿWK3l (0[SL*|t+y([A I)rԩJ3jOu~Qo\Z$^XeKۊq[ipO{~M> 66}Jc&RHNJ_iI `\mG=d9C}8u=4|2B[5ںKc^6OtTIU$l":mEdv=b~/`w#и.ŕ ^(P @(P @(3}?Kitj -9"9y9t /ڥ{X rjsg2P:pzH%nJOn @v }P"9?A;7?#wOy+?ER.Dn5_3nɇ7x{Sdm^?mQ_!)Be Z҅Hm+JNWpҔi x8jjUj]CҝM;% (#|Bw7OU2H8HҜm7]ֶ2n# ~2nǾ;Ǡf2RG7 7 {xXr@X6wIRCe*tv4dM.˚:ߞNW鵻m:5MLWv {7P5#f!- 6߳ e8ۉu#tsŗHFL5#s Y%=*h ֙ܧ~, }M+.7Ï4^u:s*+jy3fDD46NH*B8)NJ6pmm"mz*s\c) L^r#=WOg"Jfc#))'{Xi}VƦ-9י $dO<ù[IF '+a`12m(x-RXW KPľnl'TJH€#VJs' BTμu9׎j]Wxqѧx}Kp(m9)0H8TJ&(Z(e}2zԀVѯ:VW '%ZH#w88ߩRQAINIk. |Dc:rvV7|Y;iPahPRNAМ>׎sӹ\^LwZ~48cF!i moniwyx9$\6Z޼].S[니$3k9j瑽ShSЈu 4c]n̺]rZtr= $Vzym:s6{rK\oLu6YZ0=o&*3]EYor4r+T!ufF+ap3+=NM<^&8ZN:5%(SܔGTP$pI$(ɣ|eZ2GڕOOm'[BV^;4=WI|^;kac( *FJ5]6J$:MS5v9qHh/ڠ ej28wI5Byv-a uy~ S/U#~ԤcF>fuԞ\ڔȷV-w̨6ԕ}(RO #*߁!R*Fk|TAr(9?/ډڙ()ɭp*= nl::QsLr(9b6ާ1]k~~/BEt>.xy].e>@@UuVfsDM ӓ`tmlBU'hj*4y@vΠeDG@jNAԢܧ䭢9GTi(ӧ-SYȒKlI\N1OPr(͚eN4D[s#ʚ@u5Re)4y CRxF3~G7_*<ٿEQ(e 6o@;(Ty~7Rɻj9uN,BDvJn [ 885$Yg5!:>O'i<=LV_?E_>QZfg]nk^nxv];q}ZϮ>oxVs뷇n?O@9÷'ՠۏ}vh>xv];q}ZϮ>oxVs뷇n?O@9÷'ՠۏ}vh>xvKHPP>A)G;€jk?6e -8w;tOuS^K%-qHIN@@v }P"9?A;7?#Q6 0 @הcE"e湍5.ijhD:РRB%$gtg8\z"^&t/&å"n-|ؕr-A^$AFT 8 5#uiKyo\V2S-{%olz^K㌧ZYl^:C=VP$!ԥF8wrqRiNP$ѻi W.r<}`6"c)Hha83TN]ORni.u#I vЗ[~/T\ \}B=E$ NRBq/dzc.#W[RB7hg|#|P[ UhV LI 8P1Ѽ( :Kݎ]rXp'_>wxmclv)s9HmG2RR"@iA+ %)FIrqiTMT*ύLN**f i=- m{ d㣁AAdܥ^7FJiw+Zr9 V^89:RWz۟>6v.%;iS +'ݫGӣ'&Wz8#u֋#K^:Tqkc:d)Q l!+ɲwTIn涶*suG?RqO(o4s0n*i$nk".%isKm+':#peW) pRa6 Ips{xtcD%(JU+YSQNtW!1HBkdE>n.a.6)(T|:ީ5+|:ެͳݜ=ʂXAB çRXjRZ6hbpq8j-)J,Jd;&}AL) >1T:k{ip& Re_lѬ vdxk=8s%vy3A|E&ڵ'-rv]-+] ZZV$7xVhTno>FW(*%Suod]<: Hd1G*Z'=tPXZ48EJk7؉mL]l+NNWkmVfs|Eu!9-|++ueJ1M% -;!XiTBp( N:1g{8zL3%(mmR'˓xoiZ.lJq$) oeA$xfFpu[77ÙSNv_ ۠3naEh-ԃ|y?8WMFP>/|6au8T#?8$*(t3e mxI0;M.ltˉM&+6cbv@9!Α#Gi':=Ώ~sߠ:=Ώ~sߠ:=Ώ~sߠ:=Ώ~sߠ:=Ώ~sߠ:=Ώ~sߠ:=Ώ~sߠ:=Ώ~sߠ:=Ώ~sߠ:=Ώ~2:h ^'slgiTuRԎTͻ^Cs;xtb^:뻺MHӭqk?iVYb˟/$c5K@9׎SΖG70~:dFS* R G]18ݏoڹ׎μtR!#˺Fk$*Jq-TO32M82jS۸ic^ Ggaj8_:2^zO kg]e0]R eE寵$aJ# [_ʲEFgpsջ>Q)μuHK聂e.:7zV|BgOҡ1%RYa@tp6r$6(Kmt% ` yIɹ=Y:^:u@9׎s^:μtx:u@9׎s^:μtx:u@hz[UBߠ>Mи?P}&P+k+(cNd\`^7o=bΟSmnwy9)ʏq&7ۑV'"2GQK}Erfx·?-qڒ%I')wo;.mu+.XY-1mԖ j:Xw%BeKVJVgte'+ۃxY7oAGvڶB\[\(tۇ폹%n$ʤa '5qݛq]?y57O7ՋDͣU?w{Ywa2;iu! Bww׫oMdRk|o;Fzӛ.Y:g[m$Dt(aQ$ ~Zv|sLRtrϻF)4v:8Tc e5Z($V#jవ*Td?eM%ؾS譯[yٟ~_)SCӻ3T^>}]߳zwf}ʚKñ|O[yNϿCSIxv/)wk~!ݙ{*i/E>o=;>eM%ؾSݭ}]߳zwf}ʚKñ|O[yNϿEg 6rpnlhHKC BOs;=ώ;U7mJۤצ-1țl`A$xA5onI^ݷKJ^k=V@'R@Ev_f>S@;/xE)ټ"ϔgheo47Y},M|v_f>S@;/xE)ټ"ϔghվ⛉)֑Rޠ;>?~@HPkh =\!-\,:iRts ǀfE]|~V뺆 GZ?2_,~wG]脿E +@9W_3_s ǀfE]|~/^<u5(0x kPax+@9W_3_s ǀfE]|~/^<u5(0x kPax+@9W_3_s ǀfE]|~/^<u5(0x kPax+@9W_3_s ǀfE]|~^|u7=0x kPax+@9W_3_s ǀfE]|~/^<u5(0x kPax+@9W_3_s ǀfE]|~/^<u5(0x kPax+@9W_3_s ǀfE]|~/^<u5(0x kPax+@9W_3_s ǀfE]|~/^<u5(0x kPax+@9W_3_s ǀfE]|~/`W_3_j;{\znBVQ- F!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}jF!}j+d/8;a+Zx/b-kM"־kb-kM%fٮrܥ*䶻^:OYCLhZ:,[^ +V^R#Vx>T#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>#Vx>0e[`3sb%(C-Dg!W}4ŵh=Xٟp^_!C[3+^>}bߴHz f}zKQ|O[[AlϸCدIx /ik~!-{/EM>mo$=>b%(C-Dg!W}4ŵh=Xٟp6gٵmȉbܾnOjIBHN=ʎm^?wUok{ `nNsZXZ˛%7}+RA O qf4-ayz2 TT^ZNHJw7ܠ#{x9!Yϐ,ghbo34 7؅ B|v!fs>C@;x9!Yϐ,ghbo34jckqND P*HGzP|?H$(h}Oh4oߐi"P @(P @(P @(P @(P @(P @(P @(P @(P @(e5P @(P @(P @(P @(P @(P @(P @(P @(P @(!>A&\~>@u5RP @(P @(P @(@VBB@}P @(P @(b( m~s?_ c/&hP @(P @(P @(P @(P @(P @(P @(P @(P @(`?@$P @C,Ÿ_-%Y-; :K>G__>ϭ@=E}j쏤*,(3Pd}%QeAZ{#/ʋ/ I~TY~Pg֠K>G__>ϭ@=E}j쏤*,(3Pd}%QeAZ{#/ʋ/ I~TY~Pg֠K>G__>ϭ@=E}j쏤*,(3Pd}%QeAZ{#/ʋ/ I~TY~Pg֠K>G__>ϭ@=E}j쏤*,(3Pd}%QeAZ{#/ʋ/ I~TY~Pg֠K>G__>ϭ@=E}j쏤*,(3Pd}%QeAZ{#/ʋ/ >?hϭ@~{#/ʋ/ I~TY~Pg֠K>G__>ϭ@=E}j쏤*,(3Pd}%QeAZ{#/ʋ/ I~TY~Pg֠K>G__>ϭ@=E}j쏤*,(3Pd}%QeAZ{#/ʋ/ I~TY~Pg֠K>G__>ϭ@4:Z4m6RVz4 HA&~G4BA@q_H A"s~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Es~(P7Ed1{9hh}Oh4oߐi"P @(P @(P @(P @( gjmQ\Z!d=J B )IJPT))H$@]h T ;ӮN. g.Et%( P @(P @(P @(P @(P @(k'@(P &V~&\]@+#*I~K~OgՠK^{>8__hƒϫ@=4佗}Zq%'c%/e=V{i/{/I~K~OgՠK^{>8__hƒϫ@=4佗}Zq%'c%/e=V{i/{/I~K~OgՠK^{>8__hƒϫ@=4佗}Zq%'c%/e=V{i/{/I~K~OgՠK^{>8__hƒϫ@=4佗}Zq%'c%/e=V{i/{/I~K~OgՠK^{>8__hƒϫ@=4佗}Zq%'c%/e=VԛkƑ;8__hƒϫ@=4佗}Zq%'c%/e=V{i/{/I~K~OgՠK^{>8__hƒϫ@W(2ѦlZmrT $#,7ޖT_),t95#Кo> #hP @(P @(P @(P @(P @(9?A;7?#9~BfP @(P @(P @(P @(+Zh6}XE맕y1aŠvT*rBzw^-|kEMm9?ӛFm'LUr-N2OGT#\o1D$ .Lf+6 JHl˅%qH[O-'C1@v(P @(P @(P @(P @(e5P @(hi4Z@(P @(P @(}l=5},a<@,2:+Ѥ4ۍe+I "5kh<6[k8NIQ9 #hP @(P @(P @(P @(PiJ h8S)9P @sC~@vnpG,s~OP @(P @(P @(P @aqdo7LkEwCaD#[)Ciʖ'Bj/ZgݵÌPl/Qax<ǔHB-U._ ?pPuHim:R,d(GtPdަ-\~#AQ[tdlqym^%|TP @(P @(P @(P @(}PtP @( wGnW5VŮj#ە.i_px4g`ڻQ4֭ti܋z?͹PeyeBl%)KB4ou62nz^A]o9o#+1yo@5@(P @(P @(=HYU/\2eGN'h @(P @(œIqmFAB:T7xx"Ph]WaK&b.HOYYIeH¸'xcT*sܶH찞MaV#ZRWJwՑm^BZ]N4ҜbJF2HR'՝-.jimPTf0'u7p owxYJ%.Xޠ$(P in۷ji\ 8V3@():czWV\/7 \IJ9I #8$, $]5capc⊔$ @W6jT*8O4/:M 7Bh4uo:cS:H%J8M[)('1rj+Vy'WUu㤵mmj:Mn-ߔZ78to}U~ݮuҺcNj -ԗCv\-,irbv KJ\lip'龦moՏ͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]3͏S6͙]]PJKeeB Xy)BU$d"~TPwnXEKM$ 8w:q'\Akۭ^)h* 9@fV_vJthUݽ@=eo~d/{Fj~BG "_@X ( @(P @(P @(P @((DszBT ZꅪaiL) .D0B3eIxLz5&y-rm;ɋUFAFJFZQ@g}QzڇZͣ )@(뭏h)5F^0?Ɋ*9Ə @TA:iڎ̄٩W c$x)#рYsGj nֱ?Dܒ̕# im($q]{v}S[==ug_hJeK@j(Z]BVC!I9w@( jΌR nk7$ < .>5|}7Fm3`a t$op{RP @(P @(P @(}PtP @( v˼JNtyJsxX q.>O.)= ѦEٴ< dTGaDE(@ $ē@SG򿴿guP-P @(P @(P @(P v1nɪ\ji֛ c#8Ns@(AwZ]53w{g[Mn@Y0 ㌒q@w.E8VwO\ʊIIkFѬZpuw4AUX‚֕/g g<8Rʪ92TrgS\m1MK&`8e1-A̺+ʰ̥/%!ĒBGk W$[b7lX`NP9@@)Bkef׬]!l$bXRW( NN1)D dPjZp3}˜NIKTPq9 PwcZsihѦs#\e%@$H)Qǀ=cFݵ5}Adf45muÆSpN01ݡ9ӊKϹ2Ct&.PC_!4KB V*Iˣ'Gu_PeNWVwOXQ|4gKi;Oj(8ԥ9rWQi%v[]2M_İWK=5RjμnMH 0UJ(*q *$ƲI~Kvi_"RЕ Hm6-zYگmC wݚmoxGk ැG­y+[ϫ2e~9Ҷ]q9rHї-:㨦3yQdC*;.[QRO2 4uY-lweWP+\6߶[.6ngB(32!* ZC;@=E-ZIw5wӜr,qJi]/Lzaޮwk0{Dɵn픣5b;-ép/y{r#3-!9oANݚ:ZV1?{.`.rZS队{EC_a}3c]v=kl?#Ob~]}kǭ~ z)O˽w!pE>w\?ݮ;61?{.>܇cֿ=f'epvzGL.rZS队{EC_a}3c]v=kl?#Ob~]}kǭ~ z)O˽w!pE>w\?ݮ;61?{.>܇cֿ=f'epvzGL.rZS队{EC_a}3c]v=kl?#Ob~]}kǭ~ z)O˽w!pE>w\?ݮ;61?{.>܇cֿ=f'epv曵8ڐmRYH#U/՝Iw Af\;Qה)Բ qdqCd:Daij͔7NRC>I98?gbu'PDU݊蟊@:Vv([~*fci q fC~@vnpG,s~OP @(P @(P @(6˳ZGFfK!ڣ)o)PCc폩 J0@HlfP~nzCA4s9Ă0867eBT( 3mCtpfP @(:;\+b1>z4n'Gj4m}fR tlB{CYҶӶ?ogivYw*:]ejPӠ]mY}-ti ׌ ɢvvrݥuUPCFY#J]NP22$ @(P @(P @(P @(k'@(P :ٟ@hP @(P @(P @CDW,4PU#p s@v,%ZVSg.2w z ! 9rrI4PfW;szvԲ;,>]q $9!X1@~/ӥ6w.]RR #x l6-=pґz$)eڌwHhB{f0a?9mumVߣf6l!@Rр3Wiͨ4MC⧻ գ!n)dەo7$ݻrmThǜoJJ[(#B"cZ*7Tv.dr7 ~\wZFg,rxnq)we?i;Gmq) @$c>Z'pk'W(tFf[ lHW$[ +łZ\D6l¥6Vzq盎uii$kYRI 9rs**I'8'P:,pDBKQvpIEgP TUyQঀԏBhI~@t.?|I:P @(P @(P @(P @(P @(_@X ( @(P @(P @(P @( [yrt.Ϲ o2`֐d$)P$ Fvwjٵ"T/w 9Y)*.D07BR8(B!)H@( 7/[P]?;@\4%(P @(ε_S5M香Ԃ'އ1 H}Տ!@6G4sr,'Z4^$wL'9!wt)Umj{7MiRt7fSF#%-rw}T3P^YCVqmRǍ P @(P @(P @(e5P @(hi4Z@(P @(P @(P @(d˼S\%+ YJRTrR^nY[_Ru 5 NwIJxom&dWR*uطLH n2kI,ҡtΥ⮏Fh`pG QǗ_+xfzwKhmEkZ1*BS0qX&8,Tԯ9VnˢgW-O1~l*T\LKQԧlq{,Ab=Y%ݵCňSltX>YlP8-)N^uj*q\Y+uը-Œ(I'(wU._ ?pPP @(6uik[[GZ"pe-Gx~t2j+W$]mқ%@R7D'$ h l{ӳصJGpyAI* ;$(n==2}U=LXu6[qr$X Cj#uD#)/;-oVluEDI mCX$JH9` m_X6;r]`RK# *^98EZsP/;>6Ѷ8W]J H^3j'}A`: !*%*T穰m.Zn'NːE6SQwO, $)$mQ!wپa۞'j黅ͽom fT@BSם.)!!;zP @(P @(P @(`?@$PvnKpHALu9æn7 e)w<<8 -!H]ʹYH.Yt8ɠ,Nخ7eGpDrP%w w8O4_H5{cLۛoF|K@Kv!/pq&(Y8Ļ|!3Y@;*HPg?h Ң߲zèbiR$Ι1!EĒפ ;eJfl7Ԇh_Y,)5⚂&rN3IH4=XtXܘ(C'ٯϳ{ѨҳhwZy^@8 '@V7u:Y]a)3# c))N7S!9V{ YctCrZd$p -Bޭ{)SWFDnoܜ'm€ʵU|%P7v'u)${rԔһ6&*ɫl65]V-9-Bpr9OG^e*mCR[eVCN# }Sc=۞,rO$ iOJ сp=B89UYͩ q15)]]]{'LXKH(Sw[I =dНR\?8UVsrQZk/gkiAЖԦR˯{U(tq ԮOzG0TF}*>xZX4?-H[1-Ή.Hr @>C$!O9mf2ֳb뇮Q$e("bpg%* Db-:dC;ZP(Zϖ=W,hlwofjpR'5!( !$$t !F*GrG WkZ;f@Z'g +[>Yiă,wyYvo`8r,[!i}5cCQ%#-+XKxR nn#w rpqPP @(P @(P @tpG,s~OP @(P @(P @(M=Le 6} Fk.ޞ"[@1>ЂIwyigLZtfc[ڭ F@$h J@(3mCtpfP @B]Ft~7.\٣9\mäuf+˪̷)}ɯ!@@ 9@lZlţ᫣A\c\!`raG)9>:ZwR[ZuΔ@b:Nn4RFs@[uũLiUm(ÜVӭ#93ӄd$ez^~66 j˼;ԋ9|cF;I!' $(*xé-p⻮9e+tg; gytQLƥ,#@owvz޷G#Zu{4vһ,׭QorXkrwW0T>dpY$ 讒TQ-s8K$ݙmstONеc\6-.;*J502!)&I%ddI%d^6'F5ҙe0oGQV)!I xnHk4*@kP @(P @(P @(k'@(]KzNeo.-iE6Y(Nyt'u/˲smqqmT|+x6)'}IR c)wiIpW=Q .ܾ]1̲s$P @(P @(٧aۥ-Lv)qYBJʎ$(VlzJ9J)2. hP@qXp4բ-EF,7R?ǎNOMߠ*)ٝ%z\LA,*;#+m$e* clh [USYtԈwҠ(qRxv8Uʬc7mZߞh.΃m{ jit6"6(`tp1RTy&+γVmۭjM_\&jAqY"LgA. )XaY$o iߝ[?T0MzZ~^]ޏ::y˯Cm-a tWk}٘OFDƉԚQ]KZYr5/esZI*R3ڜ18&&chkh~NӪ͇%Šn#'( .s\~'NGJ;ώI %nEwWh5xcSBȳ#g)Ӻ =zPΙ8K%=󻃂x rMB޿Ւ=ElJDaEի ; O `uzi]jY/X%Kg!$$[&/wxZTz.4[8 no)Nx&^jQ)4-z2 :]!c suݾTu$l2GmÎpc-Wmm Sչek\vh,hD-N II !)X%Y؂#b*:<9r;-0T3JͶsi+o}nbu3_tB_S#u`Sgu_1 TT^dXHY @RR2p8{z*w[/e;&GY?mv PGnK|۫d2 'u'xn7GE(*jjImJꎋ_8lضOOn+_nsgSk(n%$*R3>8*ՋlO`c)~k^mn˝=uwԗmenڊ4C-7g{d"cqcu)(F@8͚жnf[ TCɼ^rjڮOu= Kd|Mmeh2퉷p8!j!)$,(y8Gt}R'6u'7 uYZA(Ct{$ 6t6vBiJ@0pp[;\ūj%\j/kw)n8[@[Qua+Ay{ QJGuCxT(o.Nح67eGln)%73w9M!@EGG髥\-ijBJ:NкݴM#lv[V$G|G h ?t孛նjHSt V2F( =*S0b&Cf;(.8I(Զ_fvn}M`A4shUJKMc!>A&Vͮf~,likbd-B4P2) D(Yʶ%X*=88nSK͕nodcPm3qj G98fn.cYZw"8ӗ7T՛QnU6^&mEjZL86 Q#] 2Mj¸jzjY-۽WѫsKT4u"j볿UIJ+l o;N٨t`4xaFuik}YF|UmVYMoˑG\~q_<U6aZզd٭YK d& SiqCKJQbm'69oxY_V%~{ݻɻUcmz4Ej{zih lȸGmǚ˲[*w(u%IZmO/# oƧnx[kc>m\ tb"mH*So,-$X@'I9u瑒YCyf׭ hijA=26ZmKq(G>R7Qy$)N71fZWuw.+*c%ꧢ]VսXvf=.eٲvLe$rb:wVPptdUei&ӓ׉ΜgN֗vhkE8w=]}djkd#~KimO)l p$RxlDQi,Z+tg˖Zou˯Ϗ3$G^n oRi\AS+T{)i\xhc"JudW_;J&}omEuWZߧ טw2H+ɓ궿t + _VVu{?oVZ39V;N[dVV %WMs\$0 (l QyIZ7R:NoQoᣵ劚&ӿ\H~vyj+UN4s÷7+HqdoƷ* gI)Ծq1+Nڴt⻏S2@(P @(P @(P @tpG,s~OP @(P @(P @qQXqC,RI=êIInڶD~5)Z{0 =\6+}-h0c3 fmJ);P @( 7/[P]?;@\4%(΢^Jp l%P'ܦ{:h _T_UEc0؀6%r\O5C[JNꃤd8Ѵtc)F*mEzM$KsVDKѭ캲5✞9JwNzF3+jӒmKCzєa:M9ih9i;Ųm^DDTlGh}"F"L$xLZY32XXq(Z: JNAT?7#T8\$UXyʒY]aܷO]˕{j%K)8Y${PxKSYm ~=_e[PqM]nLԞVHtq[#kT%Rֻo 6m\[}TFݙVq=T]ld7l'(ⰱVOzY[.\34 5dm9Znu/0;{|2HHm*3/0%ZM6~|nz&t6w A^!hi:`ACShT熏yҺs/RXmLavCmcQм)JN)j7j'JR߭@(P @(P @(}PtP @(3/T@(P @(* '@~HYhVnL}2YC!'8WF:x^שaMG*|bf #L*"@!iRsğk"-ycʾX%W&8H8I$P _œIz@@&dhIcB=vȠ,‭lөlq1]ۖZ 'p8t2@lt.L*J}-qwGz M@jnzSzzbBs2)9JҮZ7=CfQr٪UTtt[Ʃӑkm)llgq@x#-Vy ,"8jO#.Ǵ>zbicM-!js -Meg.` 6vJm- جr;1`;]U5T6$jH7%! JF on͚β6-U2l\8lm)*) !@8&߳k.>`U< [*AI MkR7WJQ5JZڽ\]0uL(@ҥdqbTޙZ?Lt՗sJ'$h#t F1QXUVlDmUcn6=7c>Yf{ M99R2J;UteJ kg:Kjm`GE:Ubk-6sDJ{up7@'-XqtjUi3XLjC[F_9ͮOS>rtk0!3);Z֒Ok0kn%N9TEZ/-{u(vݵG֚t}&VY) %AQvs$)&='6i؉CݒW~ƒgꆋvm?g*j\TܕLK7$naXI$t֭TkKv'6­JK|W͆dԝN dؿ\b4h *VTo'T3$47|Vrۦ>nFF>~,uSK ZBx{U)j_B p$֌ҧIF{H*u0Zv.9Y;p\2t gocj] jn N , O=QJkWl*\IY_.Ͽ=ӵΝq 5ԉ.<\;xRRUW;XOfy;_M,OEAߴ)\uzr+I ?\VV['=5-Rj\ib„5%}vO-Kд @!\TR{n8 N R')Fn+gktٱj6qݽ;ݳmvw6yn+*)JGiröN3SgnW'_7vж䈅I\hRx%JHR{ [gU.wYp4Kq^u9* լk׻|m*r4y([JR@#[kdSpw&m-!(BBRxU3JIRJ@'Py4B]ii)Z2;4}{tA ƊmdpdO@(\?y?R઀l<꨿S@XsjG4$?P} #LGU4;c\Z [A|zF \MTHFbcL9'5Keb:q,gm֗r\$c9UEjK Ͷ*"TGc2P:PN1p(,+fv Q`YŃj]"!ķ5͛nDw͝7wH4JO[+?oEݏSfpo}n=넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPI>넟CPfC{8(c44oߐi"P @(P @(P @tW~MgG!2Ku-2IJZ֢R$p2&l7R@h{kBӓS2o9KP n1y%j}_5i1-ۻr@A2< mf@qJIY 8 d{ l:SkR-w]u{R*$%AIP8p@IZcjی{Qm4-2 -|t7,GI6,W1HzEe=MϣjJ{6˭FGHqe!XP=Pz@( ͧg6j]m%opJ|1,jڶ[dNWnV;yBF{|(L4PlOKS^(mj_+rkpph :AdFnzsvJ U,5$ǜ/Tԗ :`iu6VಸkPW1&X|S +ZNo6-tV hLpR#AԀITiI4,RTviu[*FؖT4vbLq\ pԤeX:T+4sx\vI6Ryuvߺ;zX\l'XhoyKy/$p$cC"G썑G euMv\8u:~6r"MHm2y6D7S=Ndם?<6~.6mY^ã*V7rnMoȻ Km߅|>_tqbn`jYo>[KR_ sۨ62BszOGӕY%zVX*Uj۷ieu 7mx)@>FR*!Y' ' վ]ƝΫgl6T+b[wvӝ-;M]D/Ya f+i 6PҜevk FZٛ:IK9]UVVf:_iu[_ֈѦ=60ף:@ WZT`PAWi\:1ݺ}M] *-=Cb;sKW&!kf6 'BIwѱ{7iV]ut%v"+n9FNꐮ)Ixڏmz%H4Svj(WPoj wiݵ[QB\ҤA<=ʐ*p6isі? 8ʴSZ>S1?Us٘Oe( *&*49?A;7?#9~BfP @(P @(P @E=9qWJ N8V3uVq<,{(Vt%C=( iu柅H(w6-2nZyZS.e{ ;@6;azU8+0OotђS~ZO1{XFܴg`VwӼ {# yd @(PTo^Vtvi3hCg@JP ۾mvKt_4đή7 pPr%$)ʏwx l1rj1WlwT?T-;qm[r6@ eԃIwIԩ9's,f+.|瞆V&ߎRNkV{ӎKM0%:vk8ѝ mHJ_jR⹹z|::~wz;])=| ۅZ}T{J[mu},@Hum l471STIer-J2M2aHjk NV##(-k :N&.?N<^[\䂇pfPO2÷K[sO.Sl8⳺#@HPebI R.JL M/(cp`<1'[}iJq4~Ӧou'Q65̶@DE$gH'{n9({&{IzN۠KlKCΔ$ VPxP]5S*|-Ba Z1Мew[G; Sl\1'=Q41nlL^oCpRmV۬W>.}ʥӬMlˣqڑR2Ӽ0F.Z]14=imBqnܫ3^.JJ@'% IaeF6=`ܥ}QNI+immߙ!uPӗW4qm۔ n; NI'$)D :ޭEkM$\gwwI]&NЭta[ozR\UTRxtǝll0ERmm3+q0,4ڂ8NTPٮuJo.D>I6t$ق㉘mL@JINqē8dX -;~gSTsV垫ߵ]ge]pQJ *Q8:I<:krxS,/^bc$. %u+|⋄Pzke 6}d% $wOkb%Z;=Uq ct"ʚx%B AÇuҒ= JXjXo^m\qkhmhA'yiiѧYZd* r@q6A `nsǎjL0ߞ쟉CopT~uw_Ҳ6!iݭ6EL `zМ7黗a|$ա,]I^b4ngkBLÜy_0[ PWNOBO~ yYr^ӿ;iFڒ.l~()\e wtn{p %Y sڄ[W5! it}MQh뤴C`oeaX QRҕCu@ pr܋#.+Švi_ص3_rq݋DZ=.5nF^}ī{y!@!)V{G_6 ѓɤl%UTf T75!7i־  I%h#{w̙Üv#|і b&зgʅ8ݶ2RTOyE@vw-*t6P-hu7YGg>׺0gizb߬B)t3.cICOⓜP-y[tbmEgL\$ ' qs@[ _YӡBE2qy+q({m<_U@6qiT_),t95#Кo> #hL져% $ QWzId@ii-[:,;&%ʃ69NP˥)Q%Po殳\ׁmٳᩮ:w=kn26yxi%N{)JTgڀI*Ӌk\ME!TR]wjlŋ=l)دm>uĶ9کJD*䕸uu-m$C}vXcmkDM/hk{~0CH*#dUqU2@( -,IP qAH)Ch5v{`sT#kFZF {nyI>c]f3@]RӺ xݦ*K3<d}Oj;7?#9~BfP @(P @(P @Aӫ7R+t%+/aYcP W2! p;})-qBsh WcfߣuuG6ItquI[OJuRI KH;ājooDY:BnJ[WN;@QGSh)Z_jLgu{'T> gEMm5ӛGN<aS̴~lOh{cWij;fq^&U@Ef\),̈B}ZOAJ~)vnJ eN*[v&6<]KȎPP @(c@)ZDO,"!7TS822 1cZQ}o~6Aу$>׺xt @U']);q2\S njS^wx$eG=:8R!l=CjiB?@Rv}-)뎞G&2mrVRsIvoiΊ&g-!jt'<x`d v S(o"[5*$9 } +#t*P )#8J'q)RC(-Ӭv&KvSӧ^2d|s_jt@(>iB\mi)R2H#( wS \ t}EnNѓ];Iid,x;_Ѩ=c6y)-$g6yw]Si'L?$'9 ئ܍Dx2uwW`U._ ?pP>Uδcfڣ?!-N+}Ĩo(' 2N(kQ6[]&8|$ HG=1nz|ZNWnvةfj$\2%%6LFy)%wN-rvzbmJJRR-qoݣmxbӔMپMqm\O(|XkXirkԴ>9*k ԕ*_%JOژ%V[7~φDZȖ-n4)Rqђ{WgQu$|'jaST >e4ݞA"cV[m2DaeFBwpH=,5FMgᝥA|oSCz֛nlnY%ơ%9B<e8N8lUG(<Ҵ"yY'6 э[nqnHd )]H O{RQI#jM՜KViS2{k4*@kP @(P @(P @(k'@(˵zֱiu"Y];Cmҁ;ps@Jg/f25[dtB1Є(ZBxE [D&v <r1ZKhɎ$PP @(H@gLlmٔ5 \ Kߦe*g{=#@_;fdHTi(rB]P8 8 ^Я=4ߧd6%A%r1$nw\P! e#&DvQmHc(( 8g"q&]~%ANRN@ m.ɰSo\y;5-` 8jIE9>Z4^iCY4ihlGLX ]-Ku+;0zu(Z[[gQUܔ?/Xmz{Uci@e^tمL{n2,-51T )D%m7FSmΨe[~DMlQ$~ pgzfjݮbMaL-B< O g>oڒPDw}a |q@$'lOIƫonѼᄒw}fk[ųu"tTk7WB\9`.fy4!AD#$oH 麔'Gx*Sv]cqS3BܭpfmapZ}`-H(t繍1RTqו Ƿ? :{uikL R^,jqݲB ny6EeXKgllϤNHlPS.;)VR&+iVIh6kmOmfL)hq%jI+R<{PSJWxzni]GXnOn.0yǔe!H A=OJ*N\2FZwJzI="dz*J)<=>Z`hRyJ6z%Zv#smr$8㻉)K؎wbEf'ed^IY{drn88 T^V6v_ɬ]һzi,s;r9SXs]TiK1W\Uwi:'mp$ Y- kma~l{@-@Y&y^Uy6GWo:y^sRpiы^ΡUvgoˣl Zw߁hbjʝl,vﺛQg"S:LćJ{ FA ԍ\%XEO`v]`dWwϧDJD2 Q)ZRxdg]7vŵ-}*Kz$c(ayC)uu$'=Ps'!っ,Zۆm[+l+οj{=Ȗ#CLF.+aN^\{XJ='=NRO2JRP|~t&oFop=bji^HPQʲ23Ÿv-`q89[S]M荍Xd}e\j($- m88J89OܬIEYdųuZ+t ikuIEէАq@I8ՍHkm fH?՗n>FW (T)a!ɂwR '4Z5d:ֳͨi~kn#I9+SPJ)(R$$ǎr8UҫNvNCg4Z4ܢ >Pͣ'U+ ?/4?~@BM?#Кq~M֠P @(q}qA\@AA ׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7zT~׫߅O7?z}dz(^/>lE^52~L˔VPqej @ʎ'צ?SӥMQWSi|]G 3fQzmfvt%q*UPrR]pf_ڷ^BzSvYaD+] 6R-N0O|gט1qu+MwhӍJъ]_f~7_@L @(P @(P @( hvM3yvs}3i{vCaHK.'I($F@i=a XGʊ,Y-<ÅJT:8KOpA L \,'h\((1Χ'򜯵ỹ @(뭏荦)5N^?J&9Gƅ1VnؾNmTIJa6+|"3rO;I)z8 {w?QnAa+m[Juht;ф gvB_8To(Bwv7a7WӧAomo=Qϙ? :VIuzT7Nq`cR"^STjvqVu{U:.cu}'Ul]ؘ(`pf>-EjQតF>=uPJ;vo;_\{N$Qc9?&aZeumxT\y: *)ʥ)hm_fХRQM)f|%"[Qu=t2Ǝ75yӥ $ϜaN9YIr ϭgNj-T(C aWӦQIE\Bsqo>1 ^϶ O]6͡JUj4WNgn:[7?Ӯҩ*mǥI)< oR'x1U(AE秙iQmPtT9Kw PJo k(á%;P'83@wh =;_N1xKQv\JGOp+U4U6F,!e/>ܓ[) +Wq5S 7<ϧVo]Q;eu{o;un"Zy)R (g'&B6mn& y|+' (8Q*t.C}a!ZX#BlwH8)Oj3>5VMߪ(W- WrlMٰ_:ѻq+z@ Uʛs~|,e8ajns㏇^+q;+¥CPb0Vxt[K`( $ #h:Vպ򩉷"T) (E}.'u`)$ÈO!aLL dwp89h9!Fnu)+JVRH C tPӢeue4K| RuR@)qe$I$t wdvzæN]=l9=냜%owwq5V(mc׸ل+)ȷXF YKta'AU4W2ZfȐ(ĶQ^ #[g_+:O\c{yj]lg+Ke⑺ cUz 5$ {v@; {v@; {v@; {v@; {v@; {v@FjM7h\u!ƒ,aN%'GA4R_[Ir)$J C.y*(<5?G[Z[GXi F%a*uiVq@@~ڋ~.J1@Ve#g@L1jM]-=$8:q1Z*$8OGIRd +-go> ˊ!Gt[nJPCi.pNzG~G kOZ9*j8Bq P3t2eU%]pB ǁ:b67vsn vZyEo%_bww٬E_䑽ГglLT IYY6;ʵG 4dt*WYm(SY7$ڿ +gv[!*O w%Ep@㽂dd{H^Oi.$S;S@ @Kk5+9'&K ;aw{P fwNmJ*pY?R!JbTڳ2<8 Kk[i[!X j q8I'<2@%+4q53qm{Ep֫ŦBȇ{%+VJI $5mݑj[DmOMu"*5TMe@l'BۤtBiK$^gm[hTNJh7t6~.+Iq7]ghx ( i{ |Rr6)N%H ah SH1wss"]DUuj8FV.FGq ~ٵ5:hA:$TeIy,!AkN( (;[&4P, huە֨vKq'8HHWl j7;n4>)L.}@a+9)b3f;n}kq W&ԂFeӃ=ޠ2te4}V.Ijw)ؐ嬔+G9#!$‡FlCRf:JZ7\- VB7 eD ӯ-I7l]JJV,~vaimP~Ӌ6-,Qm)oWnHXc|thnM)vhmEK+;mx{#3UӪnYwPKB[ :li'NF۸\f ѤG>6A6M}29&iBw7{r7Q~g;Bwdy|<1ݙ"6c1gٯv̰OaHNc8@RT2UFcc >Kru/Zy1祃 [\m;de@' };Dw6 .VR.ϵ'\"mC;sG&w7Ds 8;kV]f!v6 ui-Fݻ?n^CFfҶcx&sAf]y3HC]pL T<]v;e9(E]] %S٘8P뤸BlWÈ*X՞t'pHrRp]]frֶc0Y8çqt(*v2aөxjI[y&]iisɁkbqؒS[;);$$w)_6*Ae7;+kugr6B* IwPJ.ѣҰ%V Vegne؆#HJ Rprpz)M[Y.G=r۸|F7%lkzy{ek`Ty~dȊUk^wυ!+d8 =#g9WL1/ z>IUlJۄJCi8IT8wSɕJXmi}Yo5Xe6(unhW'ma-N 1c*֡>F;^0+$|}iCR\m'm,8 (`88'.ivytJ}M]gZr9itm9HP 8I ^`mvj([8m w<㊐BFPN{ ]co;%2ʾHKbTȷVU urH22th#=N;Z"+!-o$7%{€wKFѵά%CIU#x ˳Mwպm7 ޞ{LMSG1}Q[Vp"ͣ'U+ ?/4?~@BM?#Кq~M FFjEikwur\ZPešow{rpXRVRgm{; W仚Ki&Tf«NX/+B2CAptIFJS^ m[ڟfjLìt; ;<@(0$aU}'mӒ<=(EOIkM# 5Wim_1q[]hr(ᢑ <%ءrF7WkNgY=66tOOAS!ҠFi)8-xsn ]r JZzVϺjwbZQ]u|Z̋ɫjݽ:2sp7 r/6uJN{HH6H7uUHZZ6N +[QaRbRS8pUH2$|Ry6휙RI}些ky.|7;.Itx1t Cj/-+)!i8Q~WfSZfH?ՓolDꃶklڂv/7 fJX@S96R䄸PLt6Ҏ.KBO&ܗf콯}QZrZg۽bt|\}ݞj-6;6-Rmo*;Hyh8t6 ߾USIvݷKM_߲.6hO?Y=t{cDb bڢ_RRw]w$dNԗ9g+7+= GܻxuU€P @( ߵGd*I̙BtڿY:s$_o8T 7)Z[ZiRێ@BH$"mIq2KDޝtm)2N&n5i3s}&!1i%6BZVmSRQN?J %kSF)zOHz쵽W\4ekʓ6]pN8($mlTVzMʜ[佇 ac>gk_@X ( @(P @(P @( cqn7hJ/3ʤ3r[.@(PoZFե/ws=GiZպmN{l€fzھԚvaK~[NZ܊dJJS# 0(/˓ʒwBGp Edz;/ N SnV@mhYs%GV0Wi]Zs\ ^e$%bqJ ~jdύs/T6CBԾwk#[Vimˣ+%pr0RrkqҊ=sɟ&hy;^i6I[,yקc9j[y[[=1'qYGJM#ݗC 4Y[^oʸyeI#*w+[g tTԮܬVKJŃtɊBJvKX>-O[;<܂CK/aH5W)Tn1}?ͧe4;)74L-͐_S$׺R@Q'ۅxɧ;&)֥[}?~㖹ضLUJAkZ=lKL)Wo&JT0Jt深TJvl}?(yf˲=AjlVfܛC!ԩP (YS`g+bjOV{Sl׳ KqL"U1ʹ@ÅEIHO&[5"3ȎBu!i=P @doi_U(P @(P @(PC NP @a]Qmr׋ջME@2a͖׸%y)q JHKAJ7H D^ \]CO T!`{4PֈWEP p 1㬥q-!@]3HF^ ,sڊRBP+*9 U1@NJF^ n4$`;vt=\Lw p)ON8yX ~aZR=GSҧS#CD -y1-%;)@yHuVJqu;Vj_ jWFWl{RdǤֶOV(ӐŹMrdӪP<NH98k=:TFc00UBH]4v*]GZݰ%Ѻ|H><'!Av'o4GԲ`)VN( ぎpIABmoWڴ;Lm$5{94{^F"1nGC,9GC#a7NZ+ti^cvvK\VXaos )VGIzGV.yO6.`]:ݝB  p9*HKG΄j[պUcb&8vZ|<1A=9#3fǑl;hZ3Q]tOX&җ̆ o %GFMnٵ05-<ֶ#Zd\I9#xJ༐s[hCJ|cC|\zrq+6bD jʎU1; Z9)(лC5Dݥ\](!ӈCxF8fuiNjk-v2λ{qeK7&F0RFٍF0lkjݱ:!]$iq ?kmIGA{C'zC IT^vnQ^;`kPVm؏Oٻ7gUʥD׭.)υVXhm/xPZj)}+~$ ppxM`Z4LJ;UN9lLJFY!eqP8) |}%պפ4݅=qqVGJT8v(g#=l1E Z+Ɋl$*{*[.é\Cgַ}1YuXp+eWhVPV7w+TPs"bn\\]RUip[y9RQ%H+>"tj)bg(Ig&&jTrQl]v&i ֠Ԛ+R,[RԴ;#wwy rg9ԌD'Xl6 )aE8xS&utrC/EBR17J8 :u#MWŵm沺Ef@PV d8wN8= dCk{˂v~Po4n뛵{ϗvhaոH`1]{F}>Q7w| >VGvd[y R1OMmѪh]7a 7))jnͺҒz8L9vT:ھAce5QVדUHBB#aq#PجBΚ q1E YU}>K7FBJSѸ8tg{Ɋ^L㶅*XUEO))_y[%$|2,;(*tT^#ڻ*Xg*q։Y}G(tD'j(Nu/(tTLJ%I++'ρ46 q+V[JY&ՒY+sj? +xv TN|| $YJ3fv7m*KvVl![N\HWN3YE[ѝJNi.6^ Qj)I!uU7boԄ(I}ZvYGl:>JR/w-]C^\3{ڏENBUn=f3 =ݝtǎ**T*3W(;'[  D7GVV-9]ӲBYPk8tGuhWژ;JRys<ށpȑ2,4a#RKN7vH$t]ZS-Z3ҚV.e#\@/IB^y u1RHJВ2?\b==5{{:GbIm/ ƋafPBFB- JwF;n== jRda:[3fӗ4 3Rœ 6U.<4c8S #b;r-\ay35Y!0' qx̄7SZVY5-ǁrR-! e|8$ k4v5ضkmn]7SM%$G{8ÙEBh՝[~GM9- EGDžcє,93h ]kd[ŮlަBbֆJm[ܲ8b@|7= '?6[VSsDkv@r8x%on)*$+1wi~u_^Rn5ͭ8܁Z A vrn\-}WG>.7BkmIyV*1dnjo'xRxRjnpo.2ZL[CI'tB1r~dN &▷z>?%*;q?+GPtPa}.7Ĕ KOT9m1hx,{++&f}  F 4ƎNZ9Z j8VH5GJtc_ nR^VYgrOjZ㉔e5k8\e zxynJֹv} +*H tw*N[>oA)y[TPrWYNU-%+A’dRHu&-[)-%VuDxNqY-,v}zkI'r{ܗsxPv=n*mwv<#jA5N24~QݝZ4+[;YH"h ,eBZ.޸- nt!CPXVIH*8މњ7Pmզ]F™!˶2]11(_&J1whJ-Y4o[j;fS>=%-EJ)^el"r?.*Tdw%כKh^AtMŵhr_e3cReOXNG$7ӐH4ͩm;5s>y%n>_YӡBE2qy+q({סƸZEJb=eH^XX<{(FIE&Ee!>A&\~>@uP $WgDfTWnO@J#?#|uzՠ^FFhb37W@;:Z،|goVv#?#|uzՠ^FFhb37W@;:Z،|goVv#?#|uzՠ^FFhb37W@;:Z،|goVv#?#|uzՠ^FFhb37W@;:Z،|mX+jR\H(YNq:+y)yo${]_]yK+NjܝTofč[XqyUt?f|E{ (w+煭*5aJ5(}OkX;7?#9~BfP @(P @(P~jF~;bV +䒦\ #)@_6tڤʡ_Iz$e[%mVȠsD-G` ZZMkd]LiZ-y8ZH*I%-ض{tXDhQZC 0[m %)Ŷh=m5mgIRGSu6D'|n{*)HPfݶiَ?d(iQGimڋJqsyE{jW؞,'J*€Ixɐ qEjHJA'<TUB]=Sgi F-&ӽˮϝ\կ=ZV̭,?ǫh~.) 5(SޱQWWI?H-reùcXsV{ep(:NL$1}*j9>6\-~v}bdøo% co)+@ tAҩp;R;+9g'6xE`z)nS˖#1jRɀTP0#x3[TnWIl(`Fq>%YlG1y7 d[O*44p8qs pT⢸/,eyג=9l{0MBObdl.L4[[wFV Sz8欍xIFJRWHajhdX<-J܃ n]k+ecܩyUI)K{{Dw tX6d'\rbSl6Bp%(Dv{PayG%ek.HӷڢnB%U;ZBGpH @(ݿYWXP @(P @(P @FX>_( :@(;NWiR5 >Z u<  (hoAP٤A/gUXЏ]ݤIHÛõ$w#yZb!NV@* $(9,kKYteN@Z*-ADv*QRP2II4;D+KfwU @(?A4=Hz[jdtoBK\PC2BI<25|4۠Fy,4/}j eJ&Ptru5F׹ KNQ8LtI }7p@FѬk+p >RTz =uӖdec1tj4՟U%uf 5֍7|巿Te@ẤÀ$tc:L]6E]1yvK}$@Po+*,뵪p3.}jr d <{@KA *Q H$ `sIj''-%EqNdHO:7xavhxZU:9._'*@h!Coc-s-#[ҍD.t!-2ҕo}g$g'tbsvyuޮjhtf`%x@Nl2EՓڇSeyǕnj%5)hXNw&xA3k4T-gy]4.Ų-8Jw@UPgO xkm<⭕˅GpZP$8М2r{A7E"lF[e(dc'dIժ);jSK2uڇgU[-J2"ξX䕸AQ^ , {<#iʕw5'c&Fq5R\JZ]+C\Z!JOO[0VSWI 8So$\v}'n;;ٶJ؟%@)EBU8c ;4Om]un4B&?JҢPp#{I'yT^ֳ-u-Rw4];RyN3!J‰)VBTHq@UVhX RVIF*;gb 㝕IםxALkqg6i .%0!J PJxQԉκӦ1r5cr RFT@Vx7OA08DrM}'ͩ2-J#lnɄ.<KpOt(kAt-u>SQG X\,7$y[vZu",> A@)*m 㼑O|*ЩjOUg`9GzOk{5ї sq)QHJ95F6K=R5nov,}Y;TTڄ{cOGJ1yI *R,mSYxwp+z1\}璗&i(I\}cg>'gvLf %G.@Bj0>,|l'KrV#|aIb)yki[Mt}vNb>ͺv}k! !}X<``1rY_*Х&J]+OjJ!!V!@?O"P' ڨqjnS`O eV{|Kvϵ؅wlleo Yy /p-'{tR;Ej u󹯲6v՛ iݵn<&gz.MFasWK)@ux‚vZ7t nxн^4l}Pcn)LR܉%?l^'x5*tv46͞_\Ӗ7 $+@N2 * c'6L\ZޔD%4 ?p2 {8@JjM5ki6w8VP9A@uƇ{;TkX,еc<'h ;TnZEr2LDAG '6j P lK0.h  HA&~G4BA:M@L&jZQa\;RwA8j-]E&nv PvVaPVBd1آ ɍYRt{ڿrmmdd_ΗOUm=jMeu,G[%HIR a: nPnIgi5. :mb\}-pOhFɶjS5*3^hTPZSڠ :uL<4YK;au Rҵ!xJE(nMȥe^[|>(޿cdpmu%3.$<@p IJT)B]]Wm{Kw>4P @(P @(P @(PԚlۅwhnԭIyw px\Z+Sr3늽ytӥQo^gGUU:Թ/qNRր1a*ekcJյ}כO+>+k[1[Uc)ԏI kB:ٽ};(mZ#vuth}Ok'4oߐi"P @(P @('QtVV}nE؟)C}u1XJTW*!;ڕ$ >{}?-C$\ݖM4ܶRYe’A^@p=E  ciJj.اtT]wY PzYG7n>s*CTTHN)JHQQ폹8sP\.^Fr2B}QVOҒS#F9*ˁc1i[y.aܫkMcZמ28O (./K ; -D2EsX/Gˡ7fGzildcWƥQO8ͱ"o(;R3<[7 eFM5{RԷHpq=ќ-hΕ|OM/k)RUtk{;8$%_TR[S s[x|:qC !48VARqCG I7ǁq n7gMesL؟SR][meVkk? q'5 Ǩ`4q 5 EgKGT6ٮЧ-4ޘI~Aey+G A)!YOҝ1i>hں])ˈTkf8TgyEt@lZ+N鐥ro-;.: IH'ɠ,`ƹEr4J}hP@r i B`P=5Wwm5@(P @(P @(e5Pu۾Nnݎ&ɸTCRHaEE2@nJZy'0^oi͡N dm a(H'3/T@(Z4-M`ӯm:QN(x)GA<}jƊ^û#E 鶖JW!VFOg&vMm˷mUMm)}U(FQ; Mo?k[ԓ/)h[s\ kG C%@7tQ Skl٭o2bre Y!Y{SE1j7zf M[]%KR{r/ HWB4F [QRȞ8P՜ǩ'i.(rKF0}X#jFރ.`yxS8޼\ݹ/]۠>Q%·Kg)$pSJO8jfT)Fe856Iu:v%sGY%"S%OGK}I%D`EH{\KzcfM߮'ۃ!8^%Moa<7𱔧tWdv.6xo(۝_AIM^^'Ǽi9m-S7iD;ۣ81XŇ{]rUHӵe7Blǂ 6,k BTNEBlJuWp79|sWYqW ?.JԒIxGQo8I9цW)J1I}=Y-PcTΓk\8fR BQ:@EvTkҪ+8mMQt}:3ܭ!L:uK{0Ow=R"˃S=^ݹϨYtDPLt4-Q!J{:G ~-pn"B٧PFvȬ5JdRpmڞ<+jj^k$Am]pMֵYo7;P!&=Bպb[KY֓onq)J@rNqq<1NQ?~M(OvAkOߧ@; J'Z%-~hd D}4 _~>Q?~M(OvAkOߧ@; J'Z%-~hd D}4 _~>Q?~M(OvAkOߧ@; J'Z%-~hd D}4 _~>Q?~M(ObkM{y7KL}  B }s]-kF*}kVikTϙq،V lb+PT 5{l]5_jW%C yjWl>HV%kEiKi^Oclj =/7I;վ q1lhfpaq& [Z P 8P=|zXB[rvkGl|O% }Bei(ﶿI sٹ?P4P @(P @(:-iNhv[D;HXaQ^@?oJY*RBI<9)* :Sܺ٪apkB7{3Ӛ-e:~[۸&LAǜ@hw 4ilBYh82XCŠ\H8A)V3ҒA cc׽SZ5+)O#AeӾBYuc% zP+ZPI6՝MU1i+녺SN#x0N;RڵQKDv% @8 3WRi;Yk~9g ?dF-xq9&< b(_C$XiB4(%b1δNk_W;sK6*Ү*J~|UfޔL?&4鶞M]jQZNNwJPPN2 VJ΢LڞOIW'xws\|+3ۮ¹J&PС'$z s[ι8[^Pq/a79.04] m-% R`*I sbl %NRfۜ%^"V\s̉cjճPY:HH# RR;ݩ: =k@g+{ ԋi-67; t݁ ˆ3jKB9 $vºsܠ5@(ҿԫmP @(P @(P @(#,yP @(9g:uKҴ&M̅`Ҩ ]x׶~;i2=[2(Pzȶ(" ['' bMg\+ nӚ]ynwJp =*bMwI0XBZRJPspzz  tQҜ{ qsC[ӦE='*Qj Hvc@SuZgv/!.6 W@Vx9'WfԧozHDEȪ iYX O34lbU5/[u\]p G~ZcEjk~\u=rȐgK[l-SH;$@Gikmh[ nۢ\4՞dW#BRd!jJ $'t:{:G;7uˑlW]+4R(nFcVi6f CQIR{N<3@S÷}!vePVlIPR[By1$al |-t*+/8G#N;٭..Rm*[rpJ[WZ&7}rK Kh <+qh9’zA*,$:(0;Ӽc48nW%\/ f:qNON7@t08VukZ<ΏjWe8VRwmϳC0dqGxQ5X&naQF֏ijkvrsa.sqt= :3ƺ% t)Éb22Ym%˻_GB/(WN訪2Eo;Gjb# X*nͫF龗GP?=ib+c iԝyoﴯkZˡ$ e!Hm䖉r{ÿ`jo ]gA* twn7˰"keﴩ|XO)bSЫ98.z&uۅY NA>.iaU|96><:kyd5췏 ܘ4MZ._ZSLh乌%}ޑ5P'r~NS*'ݴ@%$'' '$됣ToKv}{FT񎫌t{g/j4xӦ.\Bi3QRuY*TT5$8 liI;[4kVoO[%&U̗cBq\`uM@$`f|RѲ+hEXNm ? :毆mM)pN2ItT]t߮쎩sl?"c)*@-(r(6;aa] }]mNΝ:^fA<w0H@J:sgHh'DbLyWZ(eRa|!$ (VFζ\t|&5귧0iMdħCeMtEI[`rm[}jQuxZ=:{Z^v8 h4,9I>.ؽj vEC J o%7@@w*T]Oǵׯ\&%nG3h.3ጂEi+O̽ji7ֹ'qą Ti<|g(;7N/K  HA&~G4BA@(P+#FRhAZ<''(>c¥y?*W9c¥y?*W9c¥y?*W9c¥y?*W9c¥y?*W9c¥yho9oyd a  8%*,V x '|O6ߵ|M܆}UE;l ;GDT:72FPִI ^`@9 z^`@9 z^`@9 z^`@9 z^`@9 z^`@~&3 *I-' C)'A9,X[\pPcyq(pᾶ/ڷur+v~`|*7urgj97=Sny*B#'߇_@X ( @(P @(P @`;n&>zkrz$VD(l߂7O/x))t0-OV6f-w4qn[(m*–9ISHH[2@jl1sDGwz#9ʒ+BPpxBleD@H1DxNm) JR;*!Ҋ={ØҶX-;\%ﹽ7V( P?TVnL4gB5BH!!g3@.$޹5 veǐR,G'9'8~p|b*BIۙ>Oy9*Nw>ƱS)8BW=[bc* 1injjzac9T%ΟexR{ku5?jdd`Vtgׅeq行CHchQQmy=tnW&pVw[!ĔOM+cI)'vp썋^{:Py=wwӭNBmMY_pP/^.>(TTɐ%ҮJsNOaXb[+ts]<:sۉC]KAJH~R/ iԚʪa╛s,=~Ժ>٥y}I02?@I!R8fFk# E,LqN[\6nǞ~6XogFŒiBW0SXHcImcqN<#Yclz+Oo< mՎAi$܅ݕ:[6tcYM6m^ ״5w]QaCq';X3Z 2#}P:LΤN\LՓ9_i$r^5S1KmPy̭Vm%KZ8I  %36y6J @PG򿴿guP-P @( i;2k=EbYL`:wؒ  V.w>*Y ^&a8m`IJ1Q6yZH[Ɯ[EHqm#t}$d sk{@fR%oKV II$˴m1Zb";҈u˘ QH NMX}RN׺멣IDV[B&RP8pTs+v+duEޥڦjFȸ%'qg(V1uX%Y&36rrヂ8&`6,zz֮odžpd9H* jUF난0!8XFxct|@Q-PvKao[GaETug{' h{̶fZZ‚JɳZ~tĎxZ*q QRVOy>;1۵H9oR! >Hu mJmW(ΊKt]l32 nB];ZI⮜8*'vx [v㥞|`DkeVFJe!JBUFFIˣR+K$w>QyGۘH<*hw+]θP&:ݲnf2nQ1[|$):1GXk-MY{zΧey/A~I{+nvEa;~-R_Qʚ𧁝: ͽh, ?>}1M%a™(9;l FF8"4)yKb1pqYٴy;gXK45$<ج$gJ}TMk$Ï"MsEUձ_FN*׶yNn~]cx|߆j&#LX?Sp9ӺۡK%@ AIx8bŪꥯ6nOgg}ǷL>4Ώ.;U9R%!^-!DI iJ WmXl`)'Ytws{p-擺8 J[ zMnǂ<[qWJ%o͊G\p#{2;8]*c+'4NݘꪖprGj`5סG\Ht%8 z8gzj^X-$Gm(yzGA% :@( ̍Z%m.q/hf dŝ&kR`:ZSV0R'EQf:w.5,0.1~9tAl) U&]e?/׆e?_*mSi*ź!ٟ7ͻEéguEn+mqJ;nJ+_ȮI6ޟ{ n.RJȰʴ21h[Z JVy+ B [V^nY.Rto4UmjMKLlD FsS 1IR7n}O[Cp{7ܯIlQz7]ߵL 71HO"_t*C#u+;)ͯgsպr%ynW%g~*:Cƙ.Z=e]#Ane9S,m.!(w[ͤYe-i.,oo6IjkX}*3po:V[} %A H HRslڦy5v+sVq'k]_tM?>ènP-눹.JRy(9$rX8ZY7ivwcN$K5b/PZV?"Lo;Sk+^CpMș8k(:ը) [Zsԕci8\TM{}헖_GfJɶb>Zmrd-$IAfUn%u$&#(t{YhޛRus& 3ZFy)qjurC %)J-~uY҉,݅ڜxgY0J B$rZn3RP8h;$j֢G gՙlsmW^V2IPk*#7Q'GQRCz r[f$8HBBTlwK=z=ϟCLFn+\z.S$unFҷF􊚹_-^y mCn*JrPlTPe>[;m5+k-]fv6ӭZ.imML$! !N󮣛((ڹFqmYV_KOG;*nrߑ~ ެk彽oo~+up.{gk_k[ڻ+cRmkuT"KC%OuKm3)Gڂ%A7.Ps]ztZ|V˽;[ՠP @( ߵGiR2_h i=/W'QHfydD.*sB) ix! 'R26O\]kcKUwH^YrL)Is}2ڂ]uE hmD%j^B(’{ϯßvMxq_-r"0]LSD@\mi J \N̵4 fc*IX}Oh4oߐi"P @(P @(+Gj6H]T[LlJjP쁩Xܝڣ\j6T}{) QYJ˩dq(%Y$ 1  2͞%iCJ?SS;^Kxڵ$D% dz:b,1-8w*~ 4Rx`x m+qqmbGMK)I$4o8OW U:5|:1v,WxTgu OfГuw;1'Pxfө^{ԁc U%8gk竿5ɜfSiNJGmǎMnfʪrHanXzrݲI^l粶VSdr+I%s=rC*Siox' TwTc.'[)=:ۗwon;]$_!K DTf~~Vϡc1wmc7,4PN2;8 @91 u}A p9!)ix IPHqAkeg[hU$si@zP @doi_U(P @(P @(PC NP @(CۖFe̵O( V^ml'mJl%moQiStښdײۖ+{p0\LU8X+yRO( ZА;{MӻHL;vTfn EKX$d||8@YMZ%]. To#x`q$Q^vM<23Ү㜊@g--^w{M{2- Wnwxx@HH-l˪n*ܛM\gd(&KxXHwA8g@b7[An异Z}h4%C3==1`ouŐV{w*$ dI8S^]q欍BL[qҠ [{z0A0k7="kҷ[ PpNR8 SV]u+hM_uMUԈsvCdN|y6&8}i R wx#ϵ Nϙksl{-D5J{RFsqg+Rʇ}kQl2SerH$稂bÍgF mR4ks@w[*Nw@W6e m u>w~|tE3%ӭq8f$-]ƙyjBN)/}$oIl wռIF446T-H1ݷ˳IS{.()'eղ|w٦ЮW2:1/uդrJHꔕ8@5]tQl6LIխnY_ާFYv}mz\>Ŝ[B˛m/.Ԍ !$'f9.QikeVsk#J6͢Deߋ TYmkХrc *2e{m,YA"Bi|%Ġ#m+I'Qu7<"Y+'~^׷E\ї8SGi[|[d:12O|EZ %n*%&h勍l^MQ'1ҤKZjSZ4> _ѻ/{EʱJbd(tɌ!.B P1A g%FYGSmbKA;yG7IW,)=G w\[]ӥAgIOm(q8.6Vokr|-9#u+its۷+ J򔆦>j2''q? 4#Rו6m_U?6r׭JӊkbT0 J7puYށYSSE񒡉Zy~DDl-r * Σ:Zp&C^/W+.de+9@$wI?{+Ԝך~G3Ke0yl[%m{m)r7eA+e|\nʕH?:0{; 5\-ӝ,oɏ<6A )@Tm0\SDd`+f7Kbhxc)RTwGBҬ9 viq&Aa&IAw3QpۑydƆ)wmLJ+ ,i~$ޫuOqyp[>ӂD@&G&hIwr@=.dJ{Y8ͽ1e;nk$8 wI uillmө 疷'5}k{&6NP:V:R$qnp/ѭxMIM~[}7 ٗE xGO( {Jڎn23-vLCJoE+u΍KmrPLKjERXCo,cr19<Ҽt>Vmx>Mgwr-jbZ^EIe"2RI9'{*A% 2ym;DFSsԩW SҢqPNϮ^Dr^(eI(NY$cl+ЫF;|͈i e;l:$iiם0BKAґ$OH5HN3Wv# [ ?7^./iws苖Vin`#ʒ*XmSt^ݗ*nm]<񒲢H$md} k=v-Iq֧ԺHp8)F7xfMiR2mFZi)u, qڀm D*Ԅ=Fs)|tAR.07wr{#Hs5#ITӖ)ѣNBnRTs3fh6iެ}PgڷxtAPͣ'U+ ?/4?~@BM?#Кq~MBVwj-=1sgf鮼 ً V._4sNdM)Er=mC+)(!dI e&*qWٶ/ޚ`q{tE|61ϠU@V$:B€?3Svݓ8_~-.y+ɵ}ͼ]RlbeSj;.,66+(nU{ G]jcԗt Ku9m":ᬭ9/O56gOK}n3%FRڳT,qR;$wz)5QT\7inݏlI[gڦD酅ˈnN4)(%A ,G,-m^o>J*ˣٟ p%f^\]Oi?˶__!C>e;u %lIx[°Ai>Y4wvGtޘhs]MKlAIu+. I)S|,m5˫PӧBVמKLgRNԖ[n=e.OEαn4K%`yUZˋiUߞ-J[HJJ}o{z[jݣ]o hZ-gqۋmVPfCn6-y$QN7e}^j%u+x}I9 J!jhZViE~V"-lCJr@J5oNn]M7ߏywQdvMԍ Ƙd@Ԗ'Ή4\i nM[ޭVS%ekCHege{nXv=:6w=%j&P*}sQqm6HrGp朧;g/{۵W6ac^z{/ЍJï~ՏȠ)iG)9W-]?5UU=]d=(pۙFi_;ZX:iykJum<ʔ܃.@zx5[bgr)H$cP_lX\.oY++$6pd:IY+;o%~ٮߠҺikeGIѽu4\nNݼZm&enO%ceqlGI;toi}$캋mu?ҽn땯{{=N릶yY:l6 i3sHC2QT Bwwрy UkTkz.e~wa6vϲr;]tu:GwHHjJYZ[)RҦjJF7;jI:v=2KͨnuMt֫`mfue D@R$2.,p˩I9){Vy)eVkjzqM2]%}dcӭQrG5tK wGzokռt-~u?vqԬoݠKm]SΑN7 PS-թJ+S%wBЃ_r6yP]j-1q&zv̵^õ Y?UzIob#)ʋS;Zswo 'u|o츗I$%ޜVρkkv4IZRZo1ot=L-%rBK- )%I­r*1Y8ݽE}%k^8o4?euzfzzfٶKS$!* qPG#*4&5'qOT70|.3)%ﶿI ٹ?P4P @(P @(ĶnM5m"YDj\wb]DVED;*) #R8Jw{Y_bl唷%JQV! 9LF!QOl]lCU+kFA$JZbgk+Ra=^-弸u Ex{#-zL-Zφ)7|gzZU's,E[T%tܤV6}M(#캓zѼmխքm)RRi*5c5千xUJ3(O[>ȋGB6~ȯфe=kZi٩'eCKylh R;5yrӂ;/ic‹jSkGv\Z}v-,ZGKiJ]%JJ(+IUv.adR_<96y:N4ս6>S[ y^؞9G '.!(~VuvwVЖ_a(TKB0sȥ XN2 `]-XZ\Zv#fi=#k6~bS-1@*V7Roj',f~Ӭ ڶagBBb-ġAC휘PHHqDU.RueRMo36A-+xef#q|B8) H$%NFt1h:5pLG  f*w}adhWH!di\k#'uZ[wيp[Y'9TCKmsuNsi1FV%*)P@WaV .6'{*Gq}4PiMhKim! \@ 4w" gzSѾ{Pzo=rK|'ό v6nT'm:޴nJ[WV;@XOvf][h bP @(P @(P @(`?@$P @gZ#__3 P @~)!c RE17yGI `Pc[=QvPC<چG0h ^Է֖]^s- 9O@^gtU h{@{2b,vnm\7gAKL$eDg%lHm]Iy#|MNO ( 4m_4~4֦i<0 uYBN:AJ"1RQl}Cm+[+sɖ\b hzjT\mƵ̳1qO!8$`vGK~*\CGFm_~tm I$y7w:2p2*Xiz@M{=VA!)K'xws81@Ts:whM)m}r!R<RE%bNez\y*yIo0w='PͧlBmIf]}yŔr 7kZpḵtI<{( chzkOd/g.ܘ*t8#m$NJrNI: AMIaqU6rGSBa>S /X9+6~MmVCxxkZz[ S=89%v9~v:i O$ Rugtϩ@:wgԠyY3P,(^nL5ʸ;!6H )H= ԙ5Ըl H=:}|oV{/jߢCj$fbNC $wPgkpv;!JPm(m@IRugtϩ@:wgԠyY3P,(^nL7 ?}Jכ>R㎺ΩPRP@p۟Z#4Yp9,qkpCDt םAݯuP~7k=hyOw(Eaiyĺ: Y$U@w?A;7?#9~BfP @(P @(P @u[P%fRq!/6)$H#"Ph 7_z7 | ;f-]!Pwex$n:Ku]m0K(IHWY$/t$g m [c)`n܌/7U~b{[뿣ͣ-Zr FM'{ȉZCh8 Xla*GvjwVYr-oov*#*g ӨIO+:KՎu^'QIVHR8H2kD0v'9)o -qJU QXST!w~K֩j3ݲ->grä7g¢i[جPQϝk۳MR1M&HqФ(=Uj3zxخBӻ/uRd˹λ:ͲKjZre(8 I'P9(ю хOTȵ.,:1%nIHSde Kx+#>Ӷ#* tg-_d;귙=}rq=ᓐ9"u:h߯7hzw{U@BJx|Ǘa=MKQp& &!S'x- $ѼsUta*3vVmˑ5zg^/.ˡI R8'Vsà<+[ Y8Țۻkn٧q? jktyp_J HT@uo9I 8>{7b]buT)y4)ZT3%A){i^tKsN]qKAk=*! N:h @( .n,oV*yeܶ^ob+cJ:#jfZA-z2orML'ƹ*hO#Tl(૖7%@{"Kioh -?՛Bv 54wl?< (w4'JR߭@(P @(P @(}PtP @(3/T@V4n3DF]d Bh 8sI,P+ŖoBqқ6FRx@ve!BBP % p"f5Cڍs)<;u6;zxܠ*SJ4~avmAq SJR'R;8 v MQ} y H.FE{]K 1B9N =)dёqh[:ٶ4ze%vJꖠ0I%JNGN*;\XHP/zL`Rٶî-O{g F-PGmM-aad@qs54ӕYƜv]ln=Uzi^cLzU YZvN}#N5ibaZ[LNϤTi6][:[ כLr!FyNR@#9򢎦]Hh6ɺ^$&jy/p8Rpw s-AձaԺv}JTvIXgw!$rGOJ{Z'i_hG֌*ĨWEQ_(.n-J8VNN 7MZmM si[nPy?*(Q zuj¥Fgq8G:R×W;fvk|C=d1Z缦YSjwwui%%Y*&bhgċjuhM(]ߊMߝ?4[z.uaE.T7.(qmDg7!KWm Z n;> MENdݓVw}ذ{G#fV+L)T-[G,wq; STӏk1xj9nvze~K{3D{R9Mɷ҆ ~8dU'FQmR]je՛K؛#MG{{'CqjB@PRHBbS9_p%yG+>]k"J}k~; }@/*QBH;+1Hg^ݡk lGirRR]QBI ӓ/ωlO*["RV%ýMi$GR*ˋjA·rFMhԌ0T4vJӣ+tjG %w4Ius7CpI:kBksυz4f>:XkmwiY-pܮeBIiIQR\1PxzUi&y]vl[RueB2HmwF1=m@&1rzÑ)PG$JHq@qjk tcv6yLڮj8Sh!;CY <( vlaC&HFwr:rU]d6(84mY>q9+>vhϵu'\p )Dzw8TFN:kGv 4rUHN347*cJOCJP毧Jnt5_6Զʔx):&[WIF+WG8w3{FjJ.+3v'QTݵǗI+yMԭr[LĎҔId`pʔ{}o9g#Wg,&UךRvjO+]}I}jyc--yP%ʊF8wTF8I {zڤrdžOݩI_NIB7ozwoftr:J7[[Kǵ,=z$Οl+F;O7ٟb۵Z{~w+sdiP QpdP2rFy_][fI-Z>{ObXvUMPj}IwQ\%&²()47) JddvA*;*lv@FJRV@˾^wp%ηKB N8Ƞ$56.`]:ݝB  p9*Hg+:hvAK3 c]/[<'0 @(Pͣ'U+ ?/4?~@BM?#Кq~M֠P @(P @(P @(P @(P @(4?'4f~7_@L @(P @(P @(?^-\-δZ݊|.s{|^Gw5Jlȣ爓PM+#( u#'$¸|F*'eٛ6HSM?+(J}zrUeM^W歫yn d8ϩcZU( zjMrwקr[S2sB980 qP{kԂUN]# L& +ȫri6O-M`$ɳB5Y<3쥄NVi+5I9f?TyЂW!JY PJJrv4ԕ)Ό*&Ͻ=m%>BL-9U+ Os'Q7=e )$9q35\MH w-˩O7$A*´iaTzt:lvآ.T {I %V4%DB#ΤizGϸ\%lmUJgS-\[sq ZYJ)ۭ d)$;*UY^ lNϒx> hsSj;snUJeJT dӂ@ wIk1d|VeG45 -R4hhBxqd M^ݓl81e^ꗺѺMKdݮuIm;nёV*kvK#=0ZR3.]Od Mਖ਼B^u)P)!D +:08{_HwwՓt[w> w('9b:WdjnϏo|9=Is!j$@$ā@t굯T@(PMZ5u] l|aW}BE?٧4 wD9 T.X;%/C@ĥwh "s5QkmEjquĸLdHHi.HlzP @(P @(P @(`?@$P @gZ#__3 P21+d#R;j*VJWoDTZ;.kĴwWxgU 6^7ДT֗l6]C{pTw9DSĎ#;,lAZtGNBI)KR 3k*ǯvr7r0v\HvDRRq$4qo3e!jڻFX + ڮZXXq#Rv2Gdvee9%K XieaH'$"ƺڃBKvlYw*Z}A#(<{FppLHlD]zr ˔ۀ% nF@AB.K>)pa.H8AHlc;:sW,Fޝ x6s3I[kیM`n{`:Hq7h>MSmI$e1뎡cvV9KHe,%jKX+ڕo;UVaJtic2ʅ<^JK& 'l^5}M sA {`Aأo^FնAEqnt&/a:wH6MR& ) ' BNd4]4UzWq:T[iJlwОP2Sړ h)sMtNӗk0/*nrdᒀp wfhtovj;k `\mլ=eFJ1Gg6%FUId,Oٺѣ4vYЭi6D7vܢrm(rr[ cA;sg:[if7(*rY}2 xJBR%g`V[^ٮYJ~'nxVHV RKa(J=wv*#vp8ةm'.--;MzW8Ph4PָqJrB*k[S5S/z_pLCoy(ÎJOI'M@Kηd{璸:KzWl=uzs\.*3MPJT;G{Qdr{key$J#)jVrC.J ʥ%]3QLEJu/%퓄(&VZkځZGݴqd9tvc:[PppJ7FBI5קut|R%v}}_YL36mnJ0+*RRBwO*4~Lʖ gןt -.(R/4q.%@#K@ wL :rOʕ-k7{qwlj9ftÚq7[z.!R\⴩ N7{).?!/'ZR{nHXǎjoN'?Tp)꼯߫aci]؜ܔLB%1 # #= \+٥ȋo72Q*+IDv.ాbiJ}xYX[|9b8J[8ChJS[n5kn}DZK Оݣ%!ŸBVӞ gj0VD zq5ZҼ`nck-n04uuJiO&e QBH9e'y:NQbͻQ/m[#D [+Q-IBɌn`dE-u'Rrn]\!.=Knݺ)^ܻGWr)e<27@p5$TxӦ_LVKt]%w4SrPˡJ@JOV 5:O)jy0ӟxvW;V4UO[~E&01 }Ig' P088R'@œ8.ITw1ۿ 8Ƞ'.\k2з[!*) $P :S8lL:Km ݌rh@8QDUw5mtG[nm3i(($8ܧ{F.pyd<:jQAm9(pFn_ rVo' q%{5縸sol=XZw΅Y}yqMK\:_`籶CSRS+IE_-+{6":Z4ut(X5ᷩWM,}#KjSqy_y)nyS[NT㦶>cX:ʣ3_>$+) }L02[ m $$@a @Ip(y/cCh7hN&$o5%@M%qpq!CoSxZr H1:~y]u{VbÀ j?kZxNF0qsf#Xiů.`YІn[+NAᔁ[X[ʳODڮi{8elxK )Xlc.Sj:4n65+#V {ai[˰eC$xХ+Ojrx`ap8zݮn2OsWEq=M` ^ص,w%bAG)KIYRww8&c R.5VNɝ|JĐM=pRE@gj5:};2ݕfICQOF7i-npRzA ukw{ڮҤۦǟzڞZ+qՑ3@j0c'[8IC8J815n,*Xlb2ChqTսa6n%BI QF S-~u>ܸR(pw1nP0+Zj jj7m-z&mY/{.dd8rj-nxAUJE@\ߔQ\w'gzݳ =^ujRbn;m-* da('a}b>&"znViYkm~u|vSv ; #y*Op^kݤmj`VbԨzepS@d(ਧy); _5>NpFM<˶PBuK{cZ7Ӂ8v}Бhcϰ*M:2;cw[Jz2f+:RQНjy'Wz֑ڶZn$H_!d%ҕpa#''f1"BV_dji'oLaxn) ,-!!+#<lN/5Rqjek؂kXuCs7 -H*K )o6㸠@´TrIo&|UJw޶I[jX6M\mg)ڤ0R9$e)'8镣Z5骑ёKgx+Ѧ+[+uIgbJR.qHRp`-Ex`fݧۛ+ʝ$\JIV̞"Un='OdAԾ[W]6ɚFf1RB&yd#P .@`k{ۓλ)F2!"^BT7Rq6-6}4c. E:춖 92$KR0HJtRSTJ|TmM;K\4~}1i \P֔2JԬ9RdVtjy;x}Y^ȋ+02Rw 㦶3cj}0'PfHa87Ԅo-kNjkzͦ4ijO 88'㎜`c3dLSe.P<3@@IѲMJ/1 Q +%qED9{TfRBR HG'JNoIȇB9KIpp_8cCogBykӔUqYQ'8 pJ)q}^Č&[ta~^fpu{jz*tm D jdI# UEJ|~j\ +=AKǝݵjXceW[-NZ8t6 ϮuMBZJ#qv?)7J+ޚӊk^;r}u.oڧJٖӱЭRBL!ĸ9'iJ^Xc-ŵ;eӝ,s{q}9e{ Oh =P @(P @sC~@vnpG,s~OP @(P @(~;R-<l(q!@p>:rubZǪ:t=3r$$CNn>V"<)j.Z 7&4<$VWHW)7F"QJߛx,L0Ie'Sc6&JK8s:>$pzUjVɬퟃ^:v6k߅i /!ܜFaTuodi흭*X`USc~;[²IukWޕUCrI((,sFخu-5xril]^Q8ZhxVظYӭu,{X:^ؑy ]00 HR7%DgwN'?ú߲ݿmӡY:RݵOm51.(!G@== Up[{N/)16޷o EjlM=wKAެجsgJH 9p12'ZtV^j> IN}YiBPj$@eC'6ovwfШkKm$tnTzp,[]Nu#r)ޛ89y)<9MNCPny:Z+{nIw{m+x7wph }]E\&[kwk{gP AQ #zZBmro @|8ɠ&(? OrֿP%P #Vj>ӳoVL4%p2BRJ(H*R@3{mR.tW&Ǣ䁾Vۈ x~(do< @(P @(P @(}PtP @( дT=h=C.ɋsCPKC(RT r OA?y=Kv1TV1\iKHwrBR 6QF޶[FLud P @~($p=Zpu뗛j!a@aE=㜁zBUU'É[Gm`1 R^HT懞KFh댙VD0\l˗ս Th‱\wO֚-rI7 \ãzPq`׶5;'lVae^㻜&}3bT9}d:{pM9(0 7J{z[GFՌNM.&"VA n7%́c;HN;7Dk[TKB.K+q6C!#FO^ЍYS^oƉ뽦_S]26vu▷+<E%QRB V~DmV=)tӝj脴tBG-UĀ8 ۆu>+JnB溇Jq$O@#t9b-]*㮣ohֺkm}/Iҵm9c:t΋xHd]4~y15& -,gjA %!vq5 FWD41N[pf֜~MۦE0*Z-ĵԩgwB*hZa aeO-9'!E){^GAʝMح l'cpjY=: KMKBL3o^^ʪFɐJ ǎRgRnrwkW"CnL@>a dBO[ʈ= +v2TK. miBߔ|S1x⠶]KߠO"%S+[U$|՚ JU@\4ԙk֎˴J=.=p`VAyrP6u>K;d֊˂K+<Y"e!Mgc(B8Ĝ8 djFWNv̶Z7>Kas7v/sv[V @BH;qVUCEf|Е}bZw+'.=vɖytq'88:q¢&\h_OFk<\۴:eZ:ą!hO'| -N4w',[:fdmYbQHB @IKn RZ*$i}u[emK~NVt}+Y'12Q[H]e ڝԄhi!M(MZӯEZٛOz=ԛ '#Yd}$w}L$B 6Qĵ+_{cثK LuZswnww~^7ڋ$;"oʃJ{4zisdEۘCrKK$H0w})JBI+x;7[n7[r%P$4Ν!7SJ/F4Ҏ51pVR䭘VJ*&[=emz׶owh^{ V7prGtSS[ǜⶮ.^IO$=aMg |M % [s|[(BR[ho+uJ$=B%)yg 6n'8p$8--PYOGr(O쭋b:,!-4Rէ; o(*cHSNA܍X8iVvD^>mst ъ4ѭ8J&)vaΨq˓|t-H2xU,44!!)$ :,[.qM(nPּ+ĕǺ+f[b4l3o:Znc.;۪Wx;4-&kw܏zt FA@Hu9ek_F P @( mi/Xbs_́$9-@-3zkKCo$WӞѬWn͞vynYh{z ^jy7JμE̿?'v:TNrpjh2vm]aSo3ʲ. s$35Uy#T*ch @0_5mHI)*<{\_čH[f[[PR”01SbΤ)7cs c9[Sm|6)c  @ xgRWFΔ&֩=YkzJ%1˪Vvޗ- վI RJ0*Xpg/ApgR,M4!J(=;mx($ 6oh~^LJۑj#ZL-'VA ѫYSu+h T6{4m;=CZv4uZRyNra\@d$*InSͽ{HcdS4(;lRꛞYl-(B92wT Q8 Ztt~:p4~73;|huZښ1clNk [iY'J[āz]yjD&g2[-!PJo愒(\ehsDcޠHe}+})QPI Tҁ n1ERƥMܙKhƭU '*qc?aC$ˎg]Ŕt (cvC}O!>ݧpEnOv7id?'@;2| P> (cvC}4[<.rGacyP @(P @(4?'4f~7_@L @(P @(P @( mnJcOtkMNߐ9G7x%tmctpw@z eq#Om}r] CPWqfnZ۵K3Xۻ1529hC^H Rq_KCey hQli*7U rmi#q;}$VI$f%[[tɵn H ==;Ǐ :tV]7߹YD%hZCӐH=}2rud+ԲelE;*RT@s*T6e֊;TII<<Eb*Oz9L&Ю]۽wzfjVіdyYM)d(g˚ݡMӦ-N_kbqIdr[@_INhe&˫V82p08P @(Qnoh S^_Vtj(Sh[Hlrۑq涫%!ɷ)APH*RR%JW.ͮoj4}x{(SpRl] ֡*H[ @P @(P @(P @(`?@$P @gZ#__3 vuoВGV;NKnx3l)ךI^ODNlaV%r%;K$磢ӭ a͟4 ef#Xim%ū-DBSOZKo `c4P lAtћͥոjhPK<@t V0Nh =gOv!\E+97rI M ^6Ѥ_݆iK w1s(tӖW o^[M?)vx( a]-yfL0ږd$8JpQVg:ohZU-f&.:PNpRSʸ`AiM/dgЧ,W)T ڌqI?fYl=KftEFh>vN]aJj[%k%yedn%_j#=@V[4i 1^J#m$;9Gx:jfݚ:͗*(*Q|:KՕvޖN[mzz,ތͲ4N.JWUHW(Ɩirv[+BJ5&dlsevrGceu<֝1 pwP(s[,5*ڜ -Q(y$Xvv`ӶKmkJDnVjsÀPGz ]1[5I[>6Te>]W(O%Y;g>NkR.k fy7P !*9 PjGP|Ci%EIJ@H%8ѐ+R!PK+t'dKiwckwdUֹw%4\|H%[QxUIկ='w\>e(=6啳PWLURy0XJxr]}HcL[}xDDJIQNx! 9%Y$Km͏Kf(ɴGV ;pf#lۑ]@%AC<8('=ޏbF6Rѣ5Q6[ypY;GQ\v,O m7o)8;) FzEdUj{pe60*'jWii.]joZv_շ\˜3 S)Ghx  Ӛ9S _{z^dM$*mZ7?0P$)MNV͵Vm}F6)pJUG\x۬;K+i9  lկFDX%ȲZz{]%$$a#h%'8ޠ#!T"jmX)} P@CE9%I gڨnx:=HlUI^<'ÏY[mE_mZO^4$g]m1L. ;^A[ClMu2h3.*WDJIHNqQ$|HG+j>wm0BA V`p+O YfΗb앴79Z1hZsoVK}(lȋ?Q:m- $p w8gMeWCzƆt7O}'V?>5,K|hڕ j*r79{lc<g"K=̺k%dwIzm[wA8Y)'$C8#hݕ3##4J -(JI ؠP @(m<_U@6qiT_),t95#Кo> #hP @(P @(P @(P @(P @(9?A;7?#9~BfP @(P @(P @( mzN=sv.QpS4V^*uA 'w9GOrҬùYwʿ4),6%07d-6$pi)h{ лSrtݾ.+ar]Yd#8)]/ Ի?>"Ρ4!VTx g jսJAdmzP:2H 0wAP:{Oz**Rên敯|ڷ^_6+1cJS e+Jqr*Lmݝ:wWlscSb$[j8XRni)N N򱽒s ]V.ۻ%r68ѓPTP @("G)N_zZѪDm#it¶ۭj=_uMOp!8 uՐCIq-ŶfiU'7uBM 1 s8MK,O@(P @(P @(P @(`?@$P @gZ#__3 ]LM- t * ;"֧`HcbaJ֬gOlÙ]Y8(u01k_ t[mLmS Eg59a@( ĝe)Bpr#,ޒVG&UopO z ]Qt#GVLEqʀ8qp@hr5\]84lX/\jq8 5 =Q{XnxJ[JT]QZm($xbUkklrd{ OQ]kq[m9-!.;p&Դ}Lۮ;SI zzfUZGioUȓ1Z&٣(nD7Ic5FU2S:Pfkp hfݫFnMrYsuJ!x'))+ܭZ5(Kr\_&rbo=[4UX ZJcIj3S#q!ÂU\֛'˶]`5"'H5hvkhu(! VgXI*#^+F9&D1UӃJ)uQWup'R2k8p=WGX0nmIX^;jbD*b)},&'>wtFͪU턶 ShS!K$q@ukݫ}u8Qab׭g{7˿MQ$<#p$`w-؛b"Y>'x8[OVcڏ[\XݔWg¦J-0!%)**9=$+xMj vxZu*n.6rn|ƠIQRGA5-¹_Ϋ#zeVn3 )ʜ hSvY#s1q\< 5tV )IZS8=8Nt|ܣ-+BZVxV;-Q7%4;x-<IX##@$'n(ӢG7R3iQs'-5~)o}.]rr7Ү)mEH]*S}7 2h4Y~7\_^#u(kF" isBL&rTŹ?#o`7wwp|=p>Or:ݐk-iHw=yRe?)i5oYsx (PJTSɴiOg63|YnIUjX ImCF]9l+'5>ѭɿj rnБ-F>Etyf{U|<R*MLv#7<<޼o͢ob ȏm3$'0VF j:y>'z5P @(P TUyQঀԏBhI~@t.?|I:P @(P @(P @(P @(P @(_@X ( @(P @(P @("6lZ9ljB\JK9ma;O+@( cFjm _K]zۄXFl6).rR( qwh}MzmT["r [ Iq(5NBaNu ܋m",Te<5HPC\ [xWlhk%ׯϘEXKESh-FBT:FT8qp2ΧۮU{;?yΞU͘l:qx; %A},Ƽd봽?xf<ӝ"Gj#+}#{*[F;2-Ĺ+yE/y @p'Ԡ( {VM:`ϠrgOHPGvPƶgV\vr K5n{ࡸۧ@h:XVuT9؊ aU07I<4SPH6acqy@Xm:x(vK5osM9$opAq}PݢnVm?v^.oJ>l!hs ZwIZJJGFΞN:_'QsJhٕ+ƟTY:rTl Q-(%AIEcGv ߔ\L"eZt/m&õ 4huzB)Tf8Q)J 8'J?vw@6ǘ%0JP:U0wG⬔#5%tU;IJ;ͱlݍܓfZŹoqؑ)\%[RT3q⴨): _{ye.3Bi+DmucF]\ᶭE}8ɸp;;QO{L@ڢci˵XBڐ*V$` |54wٶȈ\/N)%@cAQMdL.<vg~YkMgx>gcY% {,pX+"AvrB HսgޜygOn.2q wJAf㈧(iUqTIֽ߯vKuj7$]~֧[S+KH89#8gZx|,忣V:=W/(Jr+ͩfC}*og'C z#vߖѠ*{WD%P[ S!N +u W QRr)I>VjJwYM[SW>(0aq#X~EWʌRzviK(ˡ7dp:_'0q*4eK{ߩ[hc7yݥgˏAUzl-:YC 1WuV$^۽VHEcpwcĀ;r ?j֣U^c=Kf*uliKzܞwds1%89:klWY ' m,f*ޝm;PJq8ψTSnYOEO9f ;1%i(W) o6aM!ËBTJT( zۢRng]f q y@wP :I$[wivزyGay!>/%d98; !NnzvsSiMI[%:M:yRR<7Sw  LFu_`Z4\"\) Im2_@J`@A @;rӺ^~pUpa,LCXB0cHq*T(i$-Zce%::cIJJK@bN{M4i(tPtP @E3wC{@S굯T@fZhY;W!W  iV2\uId)| mg;l!k>9usPrmAX% )CiB-4P @(P @(P @(P @FX>_( :@(Zm0lvd(l&Q,8%Xi ChB@ JR i/"1IeO) p'㻎@(׃ol#ÌF* 0BA'$pI4bP @ycZjGn=߲ﰧP5%3!< ?2]hκsHJHW*S==<lvSbr5ŎO],ȏuvAܸj(<7N@xgNI:$kt25s2,Õx! 9YF@'7m-]u̼Om7mu})(pׯIֆdGgy4ȭ]_WBԪC!͒sh)Ypejp%\0;jÅdԶťw^Yˆկ-C5ۯ,rCZRgw*V⽷r]o.GO4y%1 D2v*RRp]Sn*8yciWL{{9][xsp7n-Dq. +%HNNʷEi`Q{N S╴鷇iWW}i-w]Cs7#rJ$a#pdqs8Riw8g%zW/Ǩy!j5 .J^\5q$wDÕRlJ `#>AX6^)MY[hlF4p;NWmsԼj]6}-6{i:^g+98TpeyYHywJlPIG" F‹Rw* ROҀ;&˧Y;]"A4|,Axp"u3Qڦ=j]Bҙw _.wu]*@Ai imR^LXn*.ps/8APWm6]l^mwRUT&CJ #$h[YY)O7>?{;6^pP'`v$`TP @(Vb'F`C7ɒ=^.=,P TUyQঀԏBhI~@t.?|I:P @(P @(P @(P @(P @(_@X ( @(P @(P @(P @(unY񌋔7b‴h ;0(.[W ni!r[wwQ % vmZKjF6RLv-MȄĭ(R@ n@(_3.hqw6uHe҂`%ey4PMۥ5E>5nd+M"u}/eAQ(mm8 +H$ @5 ijܹ\]QO-!q.o'Ӎ ʆX[nlj^З 7rZ&adUfu[Hm0p^m4jxISJyE ;ph -"G)N_zZѪ;R۾W ";_uo&X,0B(d(-ЯM 7ӭYqdo)>ﺢVVTJO@XhP @(P @(P @(PC NP @(&֙j 귭z0&ڤLeքs8#;l( 2wm]kovY}zH=#(<dcހ>}zH>h#(]gͤZjd+<iL$ IWCԂ??ZgSN.0ZY+a|JzC-'+|bzH.ORooJygxOՎymG^ F3i.Sv<î閜uKK\ʛ$`U?fX3L5*:TҟRNFAsSi 7m!)BfH p}?{2GPKS.̮*̴6#+hL3t}En%N(%D/4ZWRfTlG)IZ['I;$w;+H鮮K5xvKNS lKԡY-& -! S::C@vL26@ґ!o1yw >:rS=^~5O~C,WJHihKr7 Sq9mdnSEkؐ8ǽ$}ez~?fH*_SvYZ ޒH8cހ>}zH=#(<dcހ>˥^60+klҭO:E01!? ߱@xY@>ǽ$}e?{2GP@xY@>ǽ$}e?{2GP@xY@>ǽ$}e?{2GP3IYt8W&qkfIfV; e5=#(<d>uVӺu.4 9JAdⴖ"m-4z$9sjG4$?P} :Gj@(P @(P @(P @(P @(P @sC~@vnpG,s~OP @(P @(P @(\6?gWdiV1"jhB-*RΙteMێheff]D6ө$! S%D{%eTgz:ݧ-pijnۓrڤLhAi4m@BQggnj-)+Yȶjs[ńzLHPn6F @@\lR߿(7 $G)s$MX<([FS|*Zd4 B?pkmG6܆\-^R TRq@X20x#Oٜ!PO\.7ifjDQ*[4TmBS#KNnn)W6*qjkMVQ)wpĜN̵h vAf%1%iH33zRh$q)=vek3uyȾ"rSq ,V j_( :@(P @(P @(P @(P @((;h֓Eg["OZ.|!LPq xJT`-0幤'XQ9gdXYRfCCW`8I*%iIp! Jغ}WKOʕ V[>*:r[֔(+{RĒԻԭ>m=k[{G/ShDR!IJqdd#%[r} waz&:'Utr^*)T/6=c@(P @(P @(P @(PԏBhI~@t.?|I:P @(P @(P @(P @(P @(_@Kjt6%֔ӁhXT8H":`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-PX< mF|ѿE54o@:`-Pf`ܶ4o@^N,utrH s=8do|x_@=o7v}Fݾ<_۷Nj;v~{#Gn/do|x_@=o7v}Fݾ<_۷Nj;v~{#Gn/do|x_@=o7v}Fݾ<_۷Nj;v~{#Gn/do|x_@=o7v}Fݾ<_۷Nj;v~{#Gn/do|x_@=o7v}Fݾ<_۷Nj;v~{#Gn/do|x_@=o7v}Fݾ<_۷Nj;v~{#Gn/do|x_@=o7v}Fݾ<_۷Nj;v~{#Gn/do|x_@=o7v}Fݾ<_۷Nj;v~{#Gn/do|x_@=o7v}Fݾ<_۷Nj;v~{#Gn/dok J 8y'wӌ GsM7Bh4ZP @(P @(P @(P @(P @(Pkh -bJ%~;&:_> |T uP|*υ@:_> |T uP|*υ@:_> |T uP|*υ@:_> |T uP|*υ@:_> |T uP|*υ@:_> |T uP|*υ@:_> |T uP|*υ@:_> |T uP|*υ@:_> |T uP|*υ@:!H+qŜ%)$@^]˘ܐ8%q̯EԚ \V7yfŞD8!U1գJG$lNiMYt|ﭭγɸ w;|/`u3SRݕz@|$DlKM crjyw$VJҒSuڃS_#kMQ J,2"GT7iMp lĥ;edRFO;N9t~$xO>mR\WI*[mK۹ʌښV02N)%[aG{9uw[yvӵ~MVڶ=tfD9C1 XPl؝kI5~o[W_ZfqB{Z'"~Sͽ rTdJjQB]IT7MmrR~ /\ޙ4wDNv|*4 CuY%16|wV .Oo-}o I{e*KGA ]ݖ\sIʿAlz;E% 6C(U5KE씚~pS|c'ڣuϰ7h-Udeta\Nn{m+Fi(C%=Ť)H nrV}jYskJ] v65, -F*u|Zm?56#v=Mv߭W4J-rԥg<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<^Way^sO@=xY1>g<뱝M|yU %H68[hg'th|:RU!8V0E]jTj .4%Zb9iN'u U( E껠efusn򕣙StHDN.SwVxwM#}=~rzڻY6u̥nC : n@8N,=N6')L0Ά_h-)HZU\*{1[ߢݟ)w S]ROSfjj|G/q&yyQɥob]>n9$Y_VEq MJT A }^uV}[^髆#F\Cd6krG(J|KHxI$V^Jn愷4)rPk+*RFN88MU~գ)oeQ͋Lvoj,ܳ+kx\aAVw ->ooW9t/}OIMS;BFB<*%I-(P{~!@(Ƒ{Z4*VgJ^#$I#9hie\1^t5ɹo=s}eէ?r#=Mu.z& òJ^q }CJ;BxZixiÑVj;Qtk6FC NREejhn]^ACKYk*7/{s I~J\=w[YѰۻEChfHuomXZ[%do_^%VK:Q;|}obĶd2ۍ$ۜiN% Qi${ۓ׼ڪ3l]T)7!IKδ{wռNJ\ >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@; > >rׯ@T:^{;<)L٩u9^7Z@ᆲHI )P#H3&,DFpb0q7+䕅8 Oy`_fMи?P}&P @(P @(P @(P @(P @(P m~s?_ o=4P @(P @(P @(P @(P @(P @(P @(P @(P @(8>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>3-Q@9>[a(Ƿ>m:|t1XTZe*(@NI'~o>I-N pM>!4sޜrN{ӟIzs9N|C@9'=ψh$9 >!4sޜrN{ӟIzs9N|C@9'=ψh$9 >!4sޜrN{ӟIzs9N|C@9'=ψh$9 >!4sޜrN{ӟIzs9N|C@9'=ψh$9 >!4sޜrN{ӟIzs9N|C@9'=ψh$9 >!4b в$'9b縠Ь!%j>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>T?r>87ɩ 8@`@YݙJL>Qe."CBwIPH=@(P @(P @(P @(P @(P @(l|e 6^4y$) RHxglueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.11/image_rgb.jpg0000644000175000017500000171347213502206677023470 0ustar noahfxnoahfxJFIF88C     C    j !1A"Qa2q#BW8RSVbr $'345Usu%C&DTet67EGcfdL!1AQaq"2T#5BU6R$3bFr4CESD% ?[f&1UzF!3Z:JD<5$|p^`{hV{s O>6Oߎ?o~Z"!^\u'PO݅وQOD 7f-1d7߆h?o悖j KGң'HJ `-TY>dԕU*#q^+UTM7e8?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B>2O|d7߁?o~#$GI#}F!'B_!H噙r= ^ VpEGGduh@55=v ^ش_DӦ#:ğ^feVf,ى,f'$I8+a`<}YEC3}LniC")r{)Ucn־='dߟ2?]@aPhkѸ6z=+˃IQ* F7Wn7+ր RM{S4PI&ܣU25akz{||mu7\nM]OQMS!jb#繺?Krl`IBy؃+fu! vB}Dw8"~"Uu-\3q†He}= ܓ?icc}|J_NtCF7#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#_ar%=c4<٤Nh4JU\JOi}˶kJL_*#1t/e_^oIW|4PA DBĵEl6{Z~ޥ'f>h.>y}ՋD1>\>\|LJ'j1S+ϧu R|0sts` vExs ]2HZr/9<º\X.!2zlAS5DD74*е ڙpG|<9\>3ڛ",PU3̊UU&:^C<ŀIcxȫ|;&bϲ `EM@*50C}7l{IG\0L{^ qFCRq&Tx9dȳ $6X[|<,O0-܀l 3$izFgîE겎j ӈh2I2ZӵB"MER52|UͱӟNVN=NggI5&ᬢl®:3>HB\%+x>=xVO^]=#Kr)_f1 AF1VFSR1P ea|r|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G#=!|G{B>#|G(ψ}ڌ(sZXfQ* HS[i\kZ{jQja@36`y) \nnj_&F7gx C ,Kii=鷮7#]ǽJx3R d#ע~#~|T$4ӶՔ`禰4ʶqeVĚ&@<AG7{Oϱ vcF䝀ezҬBU 躈o~l~vUl &KMR r`_-6 :@VE>ON~XK6GXt77 wRpWqZx^j68V QLz* %eScR qRaG/ p>S["F` #>r>}w ejM~\;_EI$fh-|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0!^@Cg\c0CyiCa\ЋzuS#z(gQ:ex BޑhGs N)xt+eA<*ԀE%촚mȀ`$ۑa=f8.yZ7S<}{x{q9c(a2>d.7`v}-J3:G H<,501:?'?PPMpEMUM|^c|S K+530b.H8zR˹O)UTG`B>#|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0!G`B>#|F#0! 7}p! U VU5D%!سiM!J {9| j*xj~rXa%0TAa4CDR<Ԩ\u&뎕k TJ,n,qSefuӨ7t99LS$ˑ D$@klaRE <3%TnGU1yf}p!GY&[tiC|Hڕg@QZ*UiLNgw`x%387!ʓQTԵ+T  n@UsR`-Z 9-Ds#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\\>0SY?f W<#he$۝ʛߩmqs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p!\G8B9>qs#}p! >7qMEǙ 咉"; *hl};oe2x%W8mjl?Vڷ_߮c7'?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_MWɟhMfzMsט?=b?bQ{ks#V/n~9寈g3؏XŻ翖!??=b?bGZ`0ws?[{ks#V/nr>Zaۙ_G_[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n~վ*զsۙ_G_|UiϿMg3؏XŻVӟVng±w#W>7?=b?bG[N}Zo0{; -܏ڷ_߮`0ws?[o-9i\a+{r?j~Zs~#V/n CO58O4NJ-zkA[4 [k]v)Qöx8FU/7'%NxH>Q)S`_]* B> zB6a <M,1)y$wQ@${ ` >!\3gY[JGԺٔ=-pp*}d :U#B9f\EsLIp"nEX}+Tc>4N:ӈ2]sSSU_h=Bv?߹gؿ,OOQz#9=\wo)=B}KB&"l$é?XE |Bs!|G?3(" <脛Y `p རĭT.z|pA n Qs!|G?VȨ?m\Ku()d|1hV||rG?s!|G?myuϙUңSPp/;,sZEqb9*T[ j:Dwܓ `B9v# d-Xp!t>p!|G?s!|G?s!|P9?ykJOaUmԆ$Nc_"\Ms_r|ff^Š,64{6wdk<&1}GIG|K1X+g9 q2̔AW2$V*T[R\>VQ8eB!]ڽda; W'd>+po"Xi ҊEꮧb>pA:px/4!Y^;KB9^/,\|zS-&]EKC`v7Xܮ[rgxyM^IG+OB'ɋ$ '3i8v`e0-bsDjV4Uڝ}8>Ǐ5C uG!J?|_V?䨼8132Z8R -iXHk 5vUIĞi\W5Z"J{0(dcHaC$l7:^A&Ʒi› K>v[ nrW^kEJ潱 j. mJ|a4|6nƲ[ud,n$ߦ7(i v83\887Jx-UHH*O[cUB/ ..fjinMe=NJa݆~R}DUgs&!!:'m⣋1rO^=58#9-2:/7qo |SU,|$XǕw6:X_+b;Gs쩙{*ryAf=WJL?hb\A 'OuV8[$Bq Znwc?4_V>;x^x^!qiü%WdDoDRPCIN˨YA7>,kr=9ۙN3Ƕ̲Zjj'\$Ej LK0PWI#}cbPv+4hxp_|x';Kp\Znp0L#5<ܐw6E>¾L ~Bg?p6E啙¶NMDpeso`J&[؍ӧٸ.*Y0JQxnN8ZZ#nQ|mi0c?s"Xj|#SxT Q|-|7UK>!5C WYPe,`' #?4lU#?v3j8x`14a5[Q]{]@E~a隵iѩ毣LC5Z]h#B9^#BTmpOqvof&%oalkT85m4pT߬|$8CCų;g=-b(^$5k7<{{_ ip0/b"r n;Gvyae2献LIlLnFTs_\57 Ո2d_7csMn1|(;Y[?Ǒ^'?sG?sT(̲3x!6%$ ,X\F%ʸ2N<8ˢxZy'YmϜv#b:[>Sqf]ò'U)1Unzn`(W~׈B9^#Ba% ugP)rfB($w6\YNlGI6Uأv7{3x7 $^'G k^u n2~K's1ո/7dZ56>ʛ)j,ri[iSa>P,oצa{X^h<>9ب'֤:SpEK!LQwFK_=je9ΕpJfU+$Ug~`PW0+//;ޜGώDAĜ%Wg ݉\LjRj\Bn9?~%C6opy| }|*e Wb@Q y^!xׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁxׁ P(?E1Yylp>Q~'i`8٪Y4wB*X6a{/JdMeTԵĚZo!?rNےwz詡iHaK0UUIH`+HȲWp3 UTqb)?>:R|)`-xs|xP(ҊkȦy$*<̪Mk( >^ߚ* TYm~M-⚒Z,‰ 9X6MztG߁gdmp#}/xD,R_IB0$ Д ؑpn `_A]} w0 ` jPk5s*춅f4l\*.X8'@¥lIcD/BxV$nqBշ8L10~;o6k<_O!Omj\UiGG ,#AYaF7cQj#Z&Ȁ|vLڎE;?:7a>ئ 0b -?1}aHj,0!x;;N'j%tk"q 0<8|1vyRp/qeTY>[IOm=Le`ʮFX!8ٵ齮tXׂIWļ1Oz&J%ڧH1fݎ1 נ>=yN%.z B6>Vhq]D6% V A0F>np_Zs4WHB9O VQ&KFOHV'g>=o^Na(܏%i<b9F>nlXʯUS6Af/mך|G$*3j^ZSZ"]zaj{-&61+? '_|_Qc͖g_Vԧ1AvE<;茭57~k7 ;DgU?N +R+UV1ygvvm6$Xg浭0-|ic/pItQf9|DLE:"D9F˚uӨ>`xbq^PTK7v>nJl+Yb%Lu}cF1`3 Q>2gz(9~*o|RpngNU *&7fmV. U%MsQOµT1$.m`[RrڠUkT4C+8̩ldZZP`컁vPv?xʟtז-l1mhw䠲?3!2YF>LI'2-azMObk^$|.l|2ZY1'm7o_^ؽNЉ?5KL w6ib=1^Ds!\IR@/[@#Wp}>ǣ5:AE`v3.(⿄ΟsJZ)(ƒy:@ [HG{R&Ɛd.v~CKa<38 :8~+DR:ױ{')"Ok'f^e\4d4ƫ|}G?8.2飧f,I*FQW!'e[Ʌiq&aSq̩wuI6 $]dmI)Yc@t n  !| _Ң Vfikȱrn #ƤP$'XXƧmgd;hl8x'k^?3WMዩXT~1sa oHiQ dْѼ˔<:$faڧ`80۸iMsr] ?1[ g3{k!(l "5ɛ'|v2Y0JQqڢ(vf &}o_:ڰt4P?E\#=|'C5j?Z1#Ur2mfO>,\+\C}~oψ*W^)98t+sSx| j?Ps!G?s!G?s!G?s!G?s!G?s!G?s!G?s!G?s!G?s!G?s!G?s!G?s!G?s!G?s!qA1=?5>}(`.F!uډejyD1B#g5$\\_<"NəOErҬ}ݭ_tmRKUՁ؂QH1p߃|-jܚV薙je2&] N!k[Yo`"&5ҥJ4;OMK3du’KG际O-c,FqM6lWGEzƾ&xS"Z8>$I'Ǩ<zji}Bư I( ož,R xds@S.u:MF>5WΰتqYL<(::ʣ(ۇ0u;%evԦ5 5*0nd2p *R6#&d U 0U/Pd^=^gY$qSUG$| He=|ygRӰݣOujy㭗=h `h?hԒD5*f>r_xhY\WƏx%hdFZ3z:ͅ4bf9}^ص_1mTS<.F}כ<"/xW4i2dqQaG׍UwŠఴ]>E<3_IE >URjWj+*^uX,ڴvn]GM[kb; ,ff˰x] xGL|KjHULKeQ-1}]Q>S?|_|ⷅ{V2jc){.bzr~沱 0g>qn̼{r׵ ?wePwo&>p.&⌟$3:F)-mIaΘ .{O)'^pVe&N7j<2yi)Yv 0Ǖb~a?Ox8+?Dxo#E[Jf-<ϋx:!$ڱ"+ JMԠl{&v."3L k(ġ- 2E81yT&&OLhjRImWsemip/qf9>)jI2C|7hoM=-{_#?1o1b"~2kWo[܏f9'HyLYXx 4o 'q_LRr&;{.7ZlfS#;_s!Gg&]9TnmIu lnb=z󬳅ck4,:Œ0 /~Ǫj=E^L8}W'_COT2dK  mͨihVF'I>cX;Rb1$Ci ؓL>[9Zs0:[-{\!wol<3#Ҫo|{vs^$O ڣ`BvK+booF&W~*rYl-~;v?D>:r^Sxv?bGw363O.gVW (U{}I=Evbw2܈@`MO9揄G(j<.ZmMv'[ox~b1KL&x^I:;3x ֑7, RSsSǬ+紾|By84_IĮQ7͸0jn'Nc¹;OBCRn\Y ]zOV\=K]^`@jj YקTW> b ^ _Ϝ~Sf5Yj--qE53{ %QەR]#p"s`i;O S+k 7.qÅxsV  (mZfJ?C *_B { ݸicǯbv# ~iҦO_<'Qs*<誢UU4b5Gpc ݦp)b"f'bE:خ32&H!sW?+'42:Ic˲̙т5dMٜPj)n\O,]^V<*߅_m5I<%9w%s{۝ل` H؍6^2^ÔKYQ$;0-T U>Ni1۠%~g*:OI&JT2x;IQ0jdo:c'P57ߐ]{4o wK\$|ErlvW+w,)#^}~+j$•/Cԙx=MÝ`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v# x,?Lx5|#梸n S 6ͨ864GHk$68 )M$J݆_U#8O )r-ei*w6RM/F,?/WW9~/ O;˼P~"&S qO,VF;\zm{2C^>$#tA d$ UfY,tԩ(*$q8be˯+*IykA״ ihJaNT^Tg&:q,-+ Yר>Cp{fMGAqR.Ř/];s;ZLh8@05~<(B9_ɮLү?ฒec-NP!M٢'m6ߧ[c`{L0wuW;g:k=B@+^!V"MP䃻}da\GhҤ,n=8|08 >.h2l3ymsFvð<^"\ 5kT9~.W㏂*WG2q "T˒7]t~giӝ4^Cn 0}3DFH;2Hǰf2vܯUf27Q/qOV |"+fs *zvY8bmfbq/i? gY̑VQy{H=XoM{p-ཏ i|EF*G} pdl3o.120hꌦ Fl~ZhITvAٲUj9Xjgj8EpGF_I%y݉ňf/M8Ue&H G;Wa݊~żN?Waدz8h'YgpI]n]9=dW1SPCpA*6$mTik8%HcAWhB9>"MQe9Y|G$# v%v x珏3dT++/Fqn ց~jKE~Q.8!|>JhM_P>_yxs!E kP5af+|'KlGN SU2ǐOʟxJ>W.q7p&cZkXTI)]ǩc1H0U(+xI&멛ſƻDi]ީ3KR'_.e4\1Pel"!{~jQI2WZ4 ?.>i44RU5 R)mN7rx9X?&TB\]"f7^P1\0':K~v#`B9v#`B9v#`B9v# y#BNv#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v#`B9v.wͫd {^k'l|eB +j(%5=`,oo.Q] _zL.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.} O-pgYX}~ O\2c<Gp?!({?~ O>N9H 1^'߰]72.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.}޸.} e:-\:j2R٭{?CT[ŏo.Q^ǃ^F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!y{9GF?vW?xXle8x5鑁`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#ݴcgyc+w[ŏo.Q^ǃ^F#`B0!F,]hʢBuD| isW]k4<):R+aq2bBGÃCU}~ĉrn _T5Cr6: Ybm Yrj.x/yqqоcDw( z*nI'>pAJBqQv'8KY879R{cR2^x ``/Z7q DYo6yL.MjcWT|.s ߃󀠂_Rێ$eu0-# A;UOO羧K +oG7\A觾Y7@M:j6 sWi-(i=Vd8:ve<ܝ.9 q { \io$x~pnG𸜎ALXz > %s.H _8Ҩ.Z}wO)d4H$]^ Tŷm?P8mH,355!5 L#qWsrF_B1iHGxZ*+=| i^g t]B.8 ?!:.g#Υ5'U R=cw0YB<I3v*xK7I0;2"gspGx!cQ?$GgĀBG7 n`zyvxNO Gqc /Lg(H&pOO[,p(iU  G3B|pm~ A `\r]9a[(^ f"fET)PIPa*z3ړ=!.EOCg )exɌd?Swh24 kvA(1!`? 1Q祇P8G7\FsV#!>2C8K7\QVm?z/9\C~?txo/E|)3x 1Ĭ!G:y뺩\A<}ɱ>^@I_@OوDALjӄsq=OO*6\='ۄspwU?>=RMo) s"$`#)m!z]$8zYCY})p! vA$4`xFx<~υš"@U, > !sgk SVC6O?8Wi uP?.N|28TrL3҅_6'{.v9df1:sR6 :2Cqv7#~p1aO%Ӂmd_p֚93^#ZȐ\\y~1XQs;񗌈DM rAK[)?aPfe%YƘqdEت0 pnH Du D=_G7#T,_O\A~q.!\aHs]C|Qsgz?UWFڬ|x3 bI3~"3f&+mjOvVTYhO!!A9\^A+_&<ψ8C8dD3x}/J̳gш:3HZOB<gQ f=pf_E6zMa9_O\K` C6A6DΎoosrPc6a~,#g9hO~qaė4%r:-_ 3dsR?W`+9gv뫄3o$,A|?z\v.$.K5+K&[SEĹ5q e>q5PG,ki&5ςU/#?QEn?gq!sqO˷I_87Xwߧ-)暑O m+}J$,dWбY8;FpY{[Q/;eS,Um]G+(uJMc%sl :2ROGlF3f B-K \loox"՟g^U7R^bRx'k[i$J&}Ƈ7Ҹa"AXGe6N"nwO. ZcWU$6IWp ;U/x=:R@U<ܩ) *{sI_Ee-wTK. Κ7N),w.piG{LzV)^' VF##vŁ"@G}OaKI{_99wkek">ԹHx8UN͘?v9 6+ x RMoft@i8W6DRMɥ܁ HB=0 W ɫ?3vυ6z긹s4JcD/ԴF׷[b.7Ằ\f-+\^"رҶ,\Xm%[W૱Ua k7$}Oa굟xg@.Pop:t\FG@L;|=<n!^2')hx_4ҩ"&PA n2:Ox!Y_ ¹?ΜEI-+U+pluMk7>kH{\`|~vfbzK2o?x4G9U^͉V 8sI %n_xx͙R\ur?KIs{ AJ6BMm< Q^72B B|ooog/(G /ɧ73?Q~M?)̿WIh9A~B^1LÉ0n,R~$hj˅4S#EPM6(&_D#C|G)̿W' 7ɠCCCB|G /ɣ7(3/!' 7ɣ7(3/!/ /ɧ73_OoPf_BO_GoPf_B__Oog/!(&_BCo@e0S4I"B ?1{-cL[(Q/MLF#`B0!F#Iߣ_/8Q1irf |nQcPJ6Rh+N;uŴ)ԤϋmՋ\n`34t',H EmÌ$UA1R74ZceXv 6#:X+  *w.R7.{t=0qT5_+Oi3]YQisȳ9jo0Dy?Y}h$]ĎIgIi} @Yo\^߾67  }6-K 4IKFY1-AHAquohySFe˹r&X= ؀6#JjxLQ&0zrɃ+m`g6:w qՎ&BZf6k > tPuCT3OWG&Q%D~j6_%콁a NI P@%s*WN%`1#GF~߻9i#}xh$J!:$k}$i-;C@RHب.sLIT+,PԱ>cc~G̏7gtv*GA]Nj㒥U]ZG7"C팪(--BaէS׆Z'iCWF֌J .,]6Nu&ޚxEծ띶EUT"P+;_[!=;͵}M]݋xe>w&^M4q6#E6@7LRCqk䌄ióMc4jjzVw^\ƺ[RMͬOMn~p) \O?)^<`ˁ3(CWIdΡ\(ӨwlphWPdE v[•E|#MN)5:Xq2AM噞 yڪ ʛYB6r6&LRUpp$OΑOi$.=BcASΚR/VUn:]4\Vh{)Cn!9~eF,5K+*2+_c{}]oջ|Yj;q?E5م8RSRFv@ .^'P296>(a$O^)g(rgk< qد5 /^bµ\Se9(Vu$ITm턟TYQ 彎]yKYDuĉF)*).I`{2y:XݶdaA,Ѣ1)Q{eu qۍF2i M,Q"juMHuGh-vfXnWHe?~8Zta?_|EAO eM4jU,+X3NcolOgtT#{DB|U`DdܓOg:{Q;ou`8:HrT 8>[M1ESSZfu{xv2Zv窤ֳUK s 4E,luWp嶥5 Z>~K|'f4jsf$)pm_E^ı?n11a|Pn<9ZujTkX5K&W$l1[荷#g._m*=;X~ͳ2s T`O@~[b=ld(EAUZQk-4nLϏL>-hLK6m@)=ڇ8uHI1u.cmyu(F*2T״usppu9UJ)¶$-tOnl s`S$Y5#̼E W1PTU;F־iT\^3]ĆW%!P)sZzC$qB. 6`H*̉mvRʂfX奥xDQlQsa`ԗ_A9- M$L|{orInpQb}@L X@*C/{KP2rĞ_49L`Fx&@=l7s His?+;1fHͬA6naw}ޑ0c^w7< M5A<?U~߾3qtHڌui.qinO^Kx\C³ Ž*Q< -o} cOqfBHp:j18TBx{x~~6}$1 d/ٺ~lWUͪNcRtYz89(`/Z}%7*&@= 13VĚSC6U^ֵu!U t>*Ht)fm7 xd270{vlGŊfFhs'W2]S1^VVPA"{m뽾Pb?2#Y>Z]z)n(ezNRZ vN5zMr0XÝc 8+=A~NU_Њ&DD8&ŸEͧ? T=fYljUB%$9jSVʭˌևPX1wfͱ/'\E:ׂJ*jJ%J#T~Aυ1~Έj`ͦzS8IFXܛ[<"=u)UWTIR;*#̎Ma1j[$b;/[,}6wqZhRt@$wK@=x ['}>F<:RT8/2?x#91ILEO$),NޗR[2uX6x~p'\q6EgVRrB70e]' SjՄ+c!]znKd=UGE%jW`W9ʎaF:b-}1Z[JW؟K߭icxo<_3p5d m s`1hH $ FU4枧z~ŌdJwY_I 1"v񅈠MrZo p墥w1"H5I?mӫSm@uי\?: 9'0QLw رbb)5Ìc~kvr֋^]H#d4R%J&,n.7ϥB*āokƢ0?7s #d}b cqfYmc>[wW17Hie sէLiRO&*NgRʢ9PQ3oaIm6cuꡘIDҡ)J*qQ3pTl{Zm:HbOx M%ˣ)IY7s__C* Upw3>һn $z}.i2'v)!oRQ`H_Щc25<<YskӑM^CZ2O7-Z#k-ñ߶.TuV>]N2~L`߿:wu?~ kO­YfY \JAHK뺰$b5ͨcR}FHTd4륚 *m6l=/F22zQl^[i9C J$ }:='ׯ:ƻ,:Ō&҈T!6-l O~t|,qkiןJjR~]p\2 4jƽX jUW3j[!rH9Ovf,Y|7^h9)cWkt_; L\e $ޘݢJmcMׇRbisUjA<`,A>#ri3YWF"m$=Az!!ZƉy hid?S5@5-*fw]`xuhqMUG=mc5{,Xy7Zcov@.Tf4\CϕG%J#/} ak \MxV=Oʲ˩ꢍsIJʦ6{IkC@t2 #tSDue?yMfKEřE] 67cbVnoߏK@tZH0T [ okLoj83 o2,YMCLc@d&6)#?nϯ޻ao S`eU帮$jѤ$O8QrV%m7_%0o;*>p*Z1+nc,N`C8_, 0bu :mఞT7Rkܑ}=?F43H7!sqLҘ*#aP#Pֲ6*Af1s˨B?c}uOa0n- dw6aPfsEj =DavPm{_2/ܣ,D~0]uFTvk뷏VPc55YC*j rv9:DRg2W@L9k Qդm%9.H>> vKBt5 v^X/O֦nx.@4:I5k (]?Z# w]EȪ(}nj@V(?VUA7ųL 3$hůse0Ϣ V/ksJRM J >s5hu;취4AU7nθz*Yf+R:"hWi3#iu$>:I4~)d\AY}.zȝ,56.U)Rv;O)段wfx6>Jf\4F*?i6Dek׾-fRTltk n>l)3'zHb%u#H˽7EN<+V[Mᯯlgދ5TaTm̒Hy6W =fH0t؞)*a2ZɎDZ:g$С= V6c ΃O>dٙo\ҶqU #$nݷۊhN.u:+*VkWrTҸf0J1Ӱ |1X[@7~I0GA.cY$5@F޽uv55CíBMw N$<.Uqo+b[|hq`L3$ú>]&"@QydȲԪ iU {M^6oH -:[tiꦖ ЬD,s#0'n7X] MA o R^N ifM /ͨKQp ,Q$'۪BjSOS= jiDuv#Ћ7m#P8N9*Ⱚ1dz}M`< GڞAu 8o7JD*Y4#Au!|ߨ;zc5ԟNMA2uQ6N{if[ c bu0=8x?szT@"3,*uA4PS%3$=cS{ili;~9 _9a)%^^=v>A^{^gei0:$qD2tz$FPVWcB ȫM6D4q5S$HLa-ULF?sS*W,UEVb@]!l- .:z )U5YY>&צ;r_ |xh:sM;ޛlk/0-> DRvg E&'dNI.}LґW%"3;HK([t~1j]X&if2Ae)3Fxr*AQM$;CP$աf]ŽF58@؛s2R[KTI0dmD23\UpUUg;hb=lxէDoljmpڎ"LsVΡ)Rkfrg+hZ>Z6y|aKfѬ~$3 #LnTa>9NqC pxl ɑze!t~3QkAVd $h74)EbF?90u$ԙ:4d`[oò=T)R9r& 1Hnn}^5}V岖M3+J I6,7=qEf5/x@iSOW=ETF$.1N 7; X"Oŗ9~I"SMriA1=.Opo/l~I1U2٩ }T]~ߧbH$S#ȚyW=R$*0+v z }G ᦾ^ᴾ |<SUf",fslE}ߛ3h5vi~")<د>eTima/qˋDtG15Ώe[..aڿ?N>I\Te̩H`%VkǨk\9,ef R8RMP &ֿ2q.Vu}/ sPzEx)g>}|B55stm, 6@.xݸgkpv'_հć[ 9O`>Hy\k$oa}F)ͤ ^ ws,@;Q{?9ߦ: CF>$%,mhjd9\tқMm\̌-)R o1s<3J(@ŘP:`l_L7Kj:U]j.ׯX+sL1yFTcdr'nLfbש5ꑔ~=^y-5 (^d/4̴<nU0ܴHרtj4jӢK )=׹m\/V!J檡I,i:p{}د*_Ued4D*c ,.{޾1( Ao5KR 6믒e9 ~]E$vۮ.Csr@˙x&,`lAE(b-k}[I"ӧ]yP?zq(~m'$9`Bsc?4?nKx1=0?q xk#`B0!F#R7Eo K_HFONd}JHؔ'70`?!a3!tO{?ɠ!ӕjHpT-a,v*o~?> V9sBZ6gB1[ByIHp׹u ]QbG4Iz9m _,lDUi),.cO:*P|ɥFb@k.&שUHQ.ALT5 3(&sN XIC,:&Y^n uV?eMGփ1;歙NAĴe9}Bl²(RHzv}.apۃu ZjECL%U.yqI8*;nN^p*,N@ׂEDLjbU:zue4R*/Of]nz/4 .ccIlg}tR;u Oqg9r4V RGdBGϬ\v; S+42FLiiZ}^c ǭ&dpNWei fSDzA"$;N:ZE̲MŮ-m!j~u ^U/>1&yql t-7W }9RtFQ ~FyTeffX~fԨA7mzOwea_}T zE6HYsӿvJmxT~r9n pqє G lzJ)Z4RtC؁-(QuLx4Ԩχ?%k,:r7x!2P.RV s{x#X0 qm-Q5x? U&)%~B M Җ*˼,68n/H4)my# 4pw-G蚿e~w%|:Ɔ𮼣o9a{N5mcZ]Zd9n5Yl?֪$JHQKM#:وnw6 lz sihѣ-\43t1}{cDb+5NhJ/†hj )$4Ś3taEq؃s0 QjZA&[̻#kc%}J&]YxVU1Uotw' χW>wM9y[SP]AFsD:NHz mmk,OyIߚ,]76_'L-$_% AO t*jcsQK޽q\K1`t??jNQ<"1"o4:rWvjFzekDڢ-C,,-Pin==qm Z{/p GL1z×AONTђҪ`q8WSMϐ[K mO;|jᢪ"9F[ }"/1a9mjMQ9DZ⚷ E4,˦=w̤qOsHE~ˑk)ii(ꡎS]̛t޷Ǐ:iZ*'4%gJ:Ja-4BBJTnw[|yU:Tk4:nWթZ[$i6 g>(ZXV`@_)]Ω7vߦ1 V*\<ݡHS-0uT͝i=N\R,o8MJnXA5ptgAnn6TBᚍ0Fe0VYe77ka[+$1~LL]+Ejtmm}l8o)9X#FŊ1Vz va>gvμ{jvjOr8j벚x }`*?BܡЃ^0fTPS1l y.KjTfPl.' )FbVZ4s}*oӯ;S n<_B4:?Z9rĴJ@l$׀RRn@츷qW䙐h#JUU+!6WWׄYQIb-2KK2PpGK<TU#: UF)6ANS˕qN\ޟ 'a.@ p# qmoy-h3tn,. NjO?UBX` AZ-.uV7 HӚV{io Sdn}KQ ;ՏXy0+mŪk 0}|x-F p7,u-CSE΃]qIʇX >u"\:>^(sjlY2dE"Vڎaar40qh%fTŌًw-WQ׻K&)^fn~M7k|.kQ%DsJvYl؀Ow>KH Hb(8Rn&shbc=0ķ \ b150oӧU3Տ4)ѥjB+J&`N\A;w3ًCqW#vl-/֋"$kk.o^ZL~]u[cR<7m[ya21y%v[ك߮3k]5S/xӳF??˫3Yb &Cvc;ǣk1|p,"XFI+,%**,j*rH u*d!ְ4o%=eF*irA?[?L&bH^>qp;~js.B TyocMs|p">ODs `\$-F2!H:UXk \r4LiNjg0֊V᥉jhfbk߮; 깩/$jSVf0_ fh^Ko . ,D)7"[[ssS7u QT԰Gwuhd Mԏm1*Qixz*ǸPszAST#zt:Zf;îc3{iblr= 9`P@2ɦDU`1t1MMY8dG r Ƚ=Wmw n50bzʳ,޼If g6*l v;~E S[Bv16-u{#y\Ye5]5?2e'kqW^fHu%JeF02LwJnbMKEm/tx;rR’ M+FHbKTic;fEo-vuG5Χq~ qlK[IJsa2G,BkEuf)ڮ}fU@_<*(ϊ5K0X~47`E.Ƶ'sGmc[3bn:\)<,$w'1ˑY]AR-MiL)RZDh fo RMYR$T, *p;ncA-iV-R>˲Q-fR'*I`}~7Q8v(pnQ 0ف6q[ }aM+58(Au}W G ecrW*|oSyEМ\Jw`V(n mb(4Rə1h9Oب`GƎ?2 /SD|fGO|@Dm"uV`Xڕc5ɼ6mاEX í|q["[_^JG PCTP=,utfˤ|rU&'U^[cVi`(IFR#]76.1`Ip]<nn}SQC3Ȇ4 A?==J<`pb9CU{/6旵 9K@qZښS*j6H`@Q=6*[zX 0x{*tfڪI.tC$ *v$y\O_jw-*GˉX#$) !J@6+X׶;JC-!L#=?N% "76DqXsQ"l opaRX(O\sd߈RPpoSe` V>QkYNf%rPyg6~pB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`BsJZ \⶚0)+2E$b "wQs05Ŷ;>Q氟f^8uˠ riUjiJcrVEɷk'վF6Vp'g5ys%E45pO,ǚXY k cV-QMj4\ƢHXO>H0Ө祷B۪7ͫAHMMmmې m,E-"+^X+WD#BX@/[cu|MiqiSB/ DqoU晵^e-tmQ"Nb.-Ѻ m&p(Zـ H'[({ NG ee *aXg; NXpɫ{XXiļ}-E&cY,ԙO=l5JTH˩ bA~b:`FQe4MEq,E4sPxߖ P1 mng HYQ& ~]OYsNJ<*4)cxWMPo{[JFԠM3Wt88_5qpSԚj;#!DcrHxn/"' 9MČ,c.)2koL lgPe;lI SMě|{.K? PeMUOM`m+v0O^gHkm Zմi1I2 zX⨞($nenSrN߾2%JqkO> Jdb`A<4Mf_:iZY^Yȱ6<#l(6x >;x_JDr^%|B*5ԯ2PU~0o:؞8dsa7\-5jYI&l=<^^RG5n=,hvP6mW´%unu!Lg:G渷gfS.SR2SL'ZXժ/q鏲lv*:OxD/8pThJ/:sL f?;EGNz~{QթV=locבt.Rd^GEP|њ)y$Fo*# h^e"s[% ꢎZfFUCf>Ŷߟ`iSj$xĵ'QLtY$ȕRJ67bFڷ TV  TU*3JV0[A =O/Kւ@_Kb/7Nj׉E#]EK%93W '0P*ky|p(`\/^HvD S*4I͚_$(ꄝfPz`⫴|48W& ԜMiyf0hc{ϗ018|uZsH~&'TRh4d Oq[gey}BAU1-T lqGVwp2@}t+Gh'4|g $g3erU;E}~US2 zxvQVȯ72Ĭ *Wɯĩ$.۷pyZ-6?]uzt(yEE !Pk cOVKE+mW7Wnq/)ZSO;.{1tp*"Km =xp0[F眊Jh,Ae۰}o}>WSUVc[M LS Bod>e6_p$mK[iՋ-g Z)3 EUΔHu+}KkX 0V! >'׉+St&ay?ꚖIlWgg-mƦK Tuh/[״iH&G{mC̶?i8mVqny/>?_ Ri *#rl@s|Aú'~M.*e0ä @v\/Z-y6忄{iZjj@tKSkM,C{ Ih~ _?swhE;ʼfZhge& B}f=񀣕ZINn :G_Ii}G-BݔgҽcTO4"DWBv ;6Zfo}b?d%F\4Hd̛~c7S|id8F:"p/'*GxCUeQO<|j#,]op텞9E'__HLR^/^E@Bj ߥ˩aZ\'BA'K_$ ueE<JT{Kl {}X *'5ȸ5/RuٜY"V;|䝪>7t+ZRM,0y\tX EaqՄ45]Vf9i}os:?8;'M4ZHT1me-2ISI2u p'9I U1% ;lu|]ZHh>[o8$E*6Y#2*o"SPUt6HCTuFqѬ|&6f#$5u nl |l4idK;8 Rw{:Uh݄@W?]e,1Su)C\mu{yыv.[L]o Ujtu_=4D*m: hcm0DIs+xmF8c9V&ZmoLk[kmpqA[#ߍaFuVuV:̝i`؁e:0"k@ 90Pg8RtsgQd Lg[*TZܛ]"-+ei1"A̦ u]ev"ERImD7%F-nyT nHqԡ0^tU)1(im-)gal-hY큔k0 9'h",nbh3ُ MWyI+dqE,Fk8{U~S$u/țijz؎ݷSs5phO>'R59g(Y"+<'PM ;؆ŸBɨyhO+5KsDZw*x꤂Qiab]el) 'I6HXnΨ׶i{ƷXctē"[pA9P#Ҋ5F*k''MHV*]X|QIy*dӬ'Bz(&mn1:iDr I5;ۦvHyӣ/Pw:)\Ƭ$]V_Jue4M=x^KWTna=jM \.ǭL)μkMp/6^Y<])+37Fi˂D[LHs'`*v`;o4Q ݁l-\>4(bMk`B>x$jZZ:r }k⪭c*OŮ+0̲ҢPLkK͖,apSnV;]_u1ߣ,y#.w$ Ϧx I{RimrEҩPǿ\ 7B\aXuGzt! 0'{(& $`wuji-ӁrW,fO0.F//g?h13?CV?t͏C0]lG720!F#`B0!Zk?'c~_+|O b*yrI. ہqkw^1xNL1y-C={$xyjo +J!NRHM„ 1n3N[XMa .&gR%57r䔓AC<@UWUR#|/RGSٝmX~Glm&n y BűW9NlT[Vq=4hc- \\,Mۦ5*p Og~UBbRxEV-K2r'n+xJuU,%Q$D p=Mŀ~yO=@~7#iHņmWjVYm #t4Hq ͙ӼوD4| Oq_Į.<"sujK ju'540B_gYn;l;[5Nq)טZirjoNV[ܐlX>qN' HpqjA1+VI&a'SE>Eɑ1q J%;n/{[u4 }&H7 2+˖et `Q0E;)]FCoϠqůgXtz<=WSNI]2)ʧo UU=Lj=HM̈~2F@EϱÈւ ̩ g6RJyš }Bͻ߽³&nI妛)fgE,ҮCq]U YiP\9KMC{^LG1U [\!00Kl$'*H$MzYs8낪qDrduR-BjEX؃pV=}=6ڭ4sp Qº7UB^ ,kyȑB֪~mvv f{6mhwbTh:3U#t(1]lu؍Dcclzu膴ap>5w?!#: SF# +j6g:m*NJjI1Mk &o\%j9]fj=VHomn [_k /aqnfz xG^j:TαS4q[(U(7X,ӊr9^kԮؚqO+QI%Dژ"z_]Ћc7j9w@2#)lM WUm6 .:@Ʒ꠳<<1$4ofY 'X덼iФcyx*q{OD湝2E p}Lhn=\vWPvL]P!:P>+^AN!OTj#cԋۮ"hE낢G*<'Ku駯׮ػnW8 9;\1%]4MTSCM#iقkv t!%;=shOF[t1n7e3YRPOZIXniZ n[ k50XӈQVL A GqWRYBo6ZtXMY;(g>`Gtî67T]c>&#{ Mxk&TTTMM^*J%^3k"S$DЧ(Z)JBn;?;xehuHׂs,$L4u u$߾ON\ELAurF9 y.q7QN# E*!K\vd-MkMmX{'/2y1|,Q Íy@%I =qF3 b2. uSЮ7sLXk)鲮i)IJ(4iPB9l|'V}rDP` :/M5=uU IЈuo`5BALK>t>hc Gd?u?$)iZȑ ?WέR6 6 HL[kRIܸ6%z$Zg ^OOޟLmk`HfXn"'SׯV^:˨%yℴ(ĀŜrM{mlacB*Zj2C>KSmP+4:Mü8O/,ͪjiYVhNjב%7lMb,vێk;P6B> >g׆ښ3WgYnV \d=[isQk'uZð>7ylkZUH(qso. )0w ͻP uօ%vb8̒yRED+0_( k ؍l :]&`G s#s%E2f) Ԣ 舾x-էK'[kBd &1 fפ a,Ԯ{%N!$ɥ3AQ RW K:-I~g1ia5c<`h +&Xh];ccrv{U 6+i&5)6Օ8c,ɳ(#zFp#6K[}0׮( eAl Nug:ϦǼX{(tSEѭi{7ÝٴN0u]6D"p\撊y4 | ROpz;vJ!FDͿQe(]汤uHcX!d#Hǡ$ abRg"'n#ծ t oEUiYf̻Eqc݄1SLJN{ 9<tƒB~is%}=>vx~a hA_aMz$5k݆ۏj5<@U6 +?9\ڦfJGr>69i;_^Co]*r|%NBk5 P-UfD'KREiY_L!hX&v&a")5NQ#avlC22I3 mA$[yGBO{ϴw-177:۠15v<ǥRy2ԎRш!F̠nz().5ad Z,JlKwQ6:D .m:nh>J.TX*喭+4m1FT5FS9_j<+-*Id뤴et B~4^=:Xڎa .u~e-g]U<} Uv&t|tu~Sk[KSmʯThmPIR& Mz ;[ag)a6*[_2xietOQ8HM0-kh*5-f$5SmjϓI0<7g~$e5YuDPT$ H̀6#5;'i{C'ʸrj%X ͬFحV8Lp-ݶ]<*leNPj*$v/ZQ,]Z4 n4rT@#53="" Icf}eG*8ਮ]w ZIm+yJψ$,[dTPIg +}EMOJCi}?5Ӫd FD*dk7;n}ô?/MJ[ϽN*&]fQe46Da^Cu!Ui 7^ku\pṏIdSEU-gñC53jY ﱵ-9Nwת\iq6Mj'RCaJ4i;.{ 6cZS,t@+7Շ^Qu0ژnI-;_ljM̃j9n^U}eof+yjfF7 Kw;^2PsyLx;O-u<,S%UCZ571"'ɶ64oifUcL8 ZxC"9`uQ ze[d(N ҡQ鶺d"` v$yTY<➖9!MT9f]W&b;6ؾegḯqӒ}'ӱq7əVR9C̏2^ǥ}:X-St]JFkUYݑunַ ݻ|lٛ :Gbgg$7=vaVDƛNR_)L97/ IϦ"9 Ic\jur4iį3crNa[:y,ߋ^2|lJ:gXWQ[%A{Ł @/|r (w)yTK`捈HOlJ4، 6YB74J{{`S)7=N%F6*j.p nlud]ʑ w?ġ-o.R\mD]s~`AQq~uG$Q$S${=q]~m'$Np!#ǻ?[,6? OkDuB<`B0!F#`B0!iT0!|=.6Ü&q%6MK0D'"̬,/mkcl0դѠׯE*2W5ME'<^etjDQfR)%))_!e'W)&cQE-W qJL8NlF"m*H= S,@ i*t) kc 蹍":z'iЫVho9\A\rEJwMV:>KfѪiIR3OꙞ!Kk ^nmT2V 11xuɲ,<Y4E@ұݣ `GUuB!:\8mdɩݽ:<߈᪮̹?2ӕW庀6*(l l/ԚsuR;ѽLΡTؤh8\ٵM|_3 TfR"f}GRaV3:]In+\IQWB\-m2':ؗRÌA"-bE$kq+Z1;k9XcQ{SmQVvnfuս4<*K0,A:[cm#|]5`KG.xrҁ$HY`{akψ-n>mA_a8Mu4BjyH!~0؎5 * qVcU&Y4&UK*iB;.JWQq^qs3ӜM'5G8hh$Ufm3 JJHET[M õфm6Hq7Y5uTQq?OEϫZƦjyࠚ]tU,C(yY.zll7޽2p-m( f~rxyk sG~]ɞ 窡!I JFQ`6]ߦ.~ԆVYY3Q6#^ZZ}GoZߌln9i1k}{=(|\SU1bMvH7?W|$6_m&52IkKmd%]l;HN/}jgQu*.cl(+aƥ ەn\`+V?\K|)f90i]@Hf]fnZ^aNL{X2j`cۚ7˒f c+H'YK%H'42<5޺{*2Y# m}_qcKm 8mHU?SET=T^tWGnc}P}5 y,n*(0`ߘ: xzL>ij('YY4F*SH+)Vܰ iÔjUb ?䒪aŠJwQ (yz@)s:|;< mM+[/@} T$80H&: ŭR.} 3Z9zʖ`!!K6q{ ͇nv)jw9Y ]pPs,*Fؼ]N=ǿ|M6^]ga؝ˊ PyyɨzCEӘ>J,|ʗAGw=m23QQSlBsA$vD۠bb$<'vHڪ8)LLO-/px/\紐~vXfNeDW $PuZ)*L>LhZ%f )CemiEuCfxR֥u]E-TUEUUQa@;nPTmzvu fy3"  ) Qc>\ Jl?0 f֏E3T%V{ S{zvu^uU8O8,s/2\aK)d2)ƻ6pfLcun:)q;ܫYyW MUYOi==$0!rj-)7ǘKYĖɋ< !^߀EP3 複3-Io R}Z(ҠYѧ3t1-yuCJARǗDmpjT$~[MmE< +)1\èQ'.3KZZ y}S팁f`Jp>yss 2.E: |;234H{no)yC MҝQQb6}G4#}|G;?IvY*Q'n[8e{Lr_T#:nz+B0[H=;aQ?ĝ6R4edf;˓7";X'ueܰ :Acj_iyrOtM!Sw,V̥&_MB1ON:[aqG{.Jbo\a9nN_Iiˆyz-H&)t*6@LSӥdAss~pnpJͤ2i6ndzui&rmdmd.-_tz:*Sd*(PUIO+ 4-rC ԌSK&mVԤhye2q oolC_1*.ֆǚqIYWXSŘt>I鿭h4}l}XVco" [$kً):,y$(e%*co*ְ-J;mG. S`~WU9%aRէ(I {;u8)QW0r,-*wkG9;4O-ΖZQէBߪ$SO I2„Vcn>N [Ppӽ\Ɵ(+hi3J FXD֒%w(tB_PaMEWQ%mdH+4;٘O'M]CzQR/|:-P5];Ri(d,nJǶ8p (I#k tvq}tܒ1ߦ5's+ؕ:,OAPR({*a.~UǢю^q,Xkq#DX}XOsĢ7l a $(6$6BSk mqn` m(G{[/uH aT?iE?ٴ J9`Bsc?4?nKx1=0?q xk#`B0!F#R7? x=g5 eHdI6v4݉z 1x^uxUr+&^ ^]Hdv:vc/Dw6BM+.t3[],oecheYYi,j)5,Rz [a/{wI᭾hOQcKk̸qmtrI[X#)N FC!w$;)a*![.lh:ck.xԎTQ V"pNFok^*bSc֫Da[R5D7" .0ͥ/pw;-:@I#M p 6M= *LH(#R:AT2n^< XDtT)T6^H(i&R3OE ۥok[>sM51e-_Es̪l>Q <[IN+$,ME֔I]X1D:"1/]~~P8Jm>DxrL23J4R$.e+Dĉ GBR2,z/A{[fqmsݔO T6mQMmlrTl`4,w:}wYT0ߊL5Yi⬰U Ihnm}'v}!GVۉ֫tA󼱠<"9ZB2Φ?W$Bh6mZa׏D\ x~j`{Ni >eDU4f̰M)du:qO=ǒJ0[0msV!NTo^ɃMՐ=u=}1ZE7<+H{Lʫ[-yJ~}($ 7nIh9n7nB-ּV]^-q%k@&:w4V0E*\RWB{ܒ-oaziceA㧶S!P5B&2^Zt?ݎi.#X xr8f՛=zb]bcknKc7_@cksێQr]x0CDsSæM/VaXϑrG6Zɚ NJU J+F5,ǝ@>eWg4q-Ck=- Ҫ$R`j"ulVX6߯%lTTu4k *cbK!m;u; [8\0}Hz*TcrtXzq]7}IZbF G[.:lwGId릻.`Y"ʻ(pU;ta@ x5 95X1EO%YR&}+i4:o|4)U2lN{5$IerdUzLFRJݭ:{ vu, `As#Ԫ"^挤@Uoiԏr8 Xo}q0:xttJHkc[x45SJO*ke*( \5gQkkU( Cs`|Y_C$1I(`V2B1OշL7C<|KuzUe[Hi\Y{v"KiOOQ2zyNsK.eTiriJ0eZlvV W)ivZ᝭i֒T S ARŻt'S.-B< IfaWщIۥܛa k\-ZCˉ$lfPOTB gCbQ~N7UWsO=9Nk"Q rM[-FSAM#4FaV"6']H%Sxze`eWn8ZD1D쫥 >'I\ӒX*;uc E=` lYXX-mkZ[g4N[J\56q3ϒ̳*)rPr{jezr}m787ߢa_C~m-> K!r3iQp:^ƽԝ-4t42:m*5lT` }\MWTD :Z(7(%$z~ۍOwoMP6NQdd+! o+jke9ie }L& eT]ǢB(;|TA{-u|E EdZL&8 )+N<`|#A׺u0QZ^Ĕs@maL kmlAЏ\Ё ~|Raa׮!B- x٭X[1* !b.MϯlJ/[n{zF$btk`&beq #).5oR%)m'mn\{P !# ŶB[.b@ۉ L )EwĕdvBo Vy=W;/&Yͤ`\'8^^vcݟgU~o?'`F!zo zd`B0!F#`B0!O6m ɞeY儓af:lB{+W~XM;kg=|Aĸ]-0)&@d!̫`6c04$Vkn-r.p"| _smpl|l {kkx,S^>qU h4uttHV%؛u ;ԋ $c\3{xE> -w:am}fSCl"H*Gܛ~c)h  m NI"om)<{ F[ku@j v¥2 [.Iif*r v]6ަƝ*n&d[gEoۂAGCKXHkY8wO@2w þKm8 -,s:?) yI;}•;1Excr\<˪ xp7hB07#̥tv}WPd6< 5Mb)mJqLh JTY$33,( WMۖw#b\ZJ(K6L`}<@"8}y"jj+5D*cUCEu,VJ^֥!C1o P5>2{j-WP5k; |U'?jG<aYSy9(^`]  cok N87hO? _WG\\')s2JcꓖBTHH1߮=g0{@. #h4XS~qhV̧+QeT*3O I^7˟M֪"[` wYKq=~S弨kjˬ$nln)Opw^0)uPۭw|ԩq#ޫQUVe2M0~ܳr JDGFíɹIQ?*6-oCB@tԙh%b,@ܛ^ěmGE:΃I{k%g9Ӌm:pYK" Rl\}EUXN*{#'UMw8"g|6OUuivJ!$ǽc0{\iD/c^?STO:fBi̶@Tyhki*EXkE\;^%ZhV1J edY+[O|ڠ)0^[V[TGn#ĝl-_^|R8fE3OVVEF2;;nJrC\\lg`ͭ4ܘ~zU,8*1ʢv2Ld)^bJĞqc3 pN˅v_3.lY7X$:Wg[8+O59ܟw&vtCq.mLA뭁Z]L\Fuą(ON=XFdBGs?^->e7:I>\צ_A䒾gJN\"l/|s$_59FWT -]DTVR.NXۮ5m0JA42]Qx-4fsd*&}6Qe#_.CQ+HK:ɠ_I oՊaHN]ODTr' [.Ty 7}Z5i>o૯uH+fP<@&.@#*kr4ޛ4EEAMsWp9O#1ņ~j0(@ H;{ XRqU7 SUf0OCWL *+U&:WS(6jR}R ,u3#UIh6<>S1WLR;B׷=&SQcM8KV LՒ9`l1`omzb(2"CN (IhM^lyڭm}<=Dd DéY4kUB)E7kn1Q<ZX,;;A=5M8UYdpw PW5qm),\gxau9#Rm'&xߜ '8qS4%)MM2̰tTgt:aaB*rχzm>'S^,䴑+N?R<"|zu2#x~/֔` e@;dž)6~J%-v.ڇw;1k 73Ljo샨ktJQ*=9F'Mw'){ڍsq}4>͔iz*WdT'(g Avo1L,'Q#_eK>U/_9<TE(xi%[4RNc;ENL4O⣪}An8stW(("2Tj5W6/P/ j:Gb*; c3fC`I"?zJ47Kן`=~,˚*Z Hc!'Tc*ɶ=4n$>:@l7heCD.^z(|WC]3ʱT3,d8R-$ۯNÀH3jH3~)W._$)HyYvg>v`H$76^ Zcq4d2:cW)+⩎U!;@6*z_{`qsC~ۭWLCn~?*hk) 3\Mb=L̚Wנ|.j,2Ũ3pFn+7LdcЫ_; OFywGq衳-)j L`ZO[zuŌzB`3{rUgɨa4IeKb=7>=ᡀG5cZ_(gjXIct #""؏zfiU#_2pUiAT42O RUPf1i=Hfܯ@IPZsۖ+벍R{jQMHrVQӾ0D\o/~:I/ _)x.\µy0M3DdRun?Pcb®XsWgЖǎ~$<\9|U<0NlBLT(kHd75om6)]Y-T$rCnKkkN[|z6勗%<<*}!m,EkikI~&u{iEi9)dw (2BӾo|` ߧL [FlYn1)kt/SSmc;HHBV:H SZjIq(",fO0*ӜF//g?h13?CV?t͏C0]lG720!F#`B0!Zk?'c~_"OK4h*Ku$ [G^N.}:fjm.4Í}X*7;XGddu59mt9.Ql}qUZxʕA4/"h5PUr:A% RK -Ң;nO5(a/6èI9nK՜_ #FIo"F*  ncL}t:\.:iq]*(&̕|bUX#rZ`v[)ʦ@7SnKAdDٍ%&wF𘲷 ZJYWQ^(}Y u8gkopWq5>k] 1V؛\[!G`Q,}ˏ^&~&gi@x8N8hr:j Py]4Zn'Ky$p<@ۚǍ2׆*ycUeZH3!'XEd5O2< 5ai ! ~QbMpa buq#bZs1^%l0s|7dxcvR@wP,kk[κ TN[> 1ɨ4I0X6P> ZD2'P!ku~gYiVRQ,ZKZo{nZ!xLYl gHtga!bFRJ5m&dǂmL]xi 2٠I-}GQmU,V4ufX47JTč"KmV d)\nP͚GA-JT,3Das?O|iU4>) Op Q mZX .iU %s\7_ХMm]8*\h*X"rڍ|coGq~Wup3sj8& TBz}[ 9Ie -t%DCr#~)@kE\i&.VfƮPB"0#+}-7E\;q8J/myJvg6H<.xI .4%yaF!66b{cqkڎc[-a)4bx)1*ZɠFES@5_ӢnR20卢g'R@&b8t}}ӽh*iC,DWH"[X#N m/L=4iYƳVѥurTjZ`Ur^a6uэaM7D gmP%UFZu?Jۛo7 G" .<ƾz6cohM|75#=E=!g;34fmQH 6*oG<_TB1yqDդhʲ("m\vZ%oRʑe9u7F9#s2ZŔ*9: pW>%L'uv3I$TM+L"!8 rW3zU,"1q:@so[c蚠$D>uJ0nvǸ"㭱8[] j(5TxYkiϷ^oo|1XMLÇdT*S#M;1n7;opO݆!UFcl0E[w!S-*ʥM.湲4jgQ*LRD뻪A#|)Y4|MGˊas^Dĭ{d@|yp rG/B&a6V}RjjUlOQM@B뷐RH{ps|)Ѥ $O0*ccolWL m bL3"0񲂯J0^a /-@&vwX} VPhk;ǩJZjioHi7fy-4znم*5."PHZh᪒?,\3}~z\BILikZfH%yr0sdLVx!Bts deG8٘`ft<3n)3RMb-,=u?KG;L{zշ,9(38jgX) }t$6X^ָ8KJ)67^.~x2JJfjC9a}8,'Y&uP x`50N{bdi/n5g4q +gT(@ ` |k0Еbyn*TKO)*I@k>ݾS_(ULG(a]ڭC/UeB Td;} VW\g*]k`kQ)-.pQJ$\(rw ze@+3#D/%keTNB*eWi~rג KPdpQQRE e(a !c=>o 1;o檪(ӭZL1*Z^2Ė>;_ [HFXBo˔Jebe;; 76~fc}LYZSG|zJf܃$T= IRڋlm,nl6*IyF ߒt!\I*)! SBYi)k6`b-,AlL_J>'Wii 2Q&[XAW1E-qΦbDo?m6TssTmp/3pn)\MD*A;G3+aݹ[|U,+;'Ea8$eyY3k7; c=KY&H)*r`oݱrԒ/XׂG>mfMW#gTpEQ:$:̾[f{tAsG_vmV|'ʟ33)b`1}x9M]MUV' DN! g+6#ag֢DQRRCMEOyj>$ ;bHc.~Ahm:[Zh"%#}ĤlA`oMد O]%lQӮbs$uBbM;ueS9ί" Vdi*:mǡj0r*E03a,2̠ELXEu~I0g?=8u3.}dT԰C8ѱM@\iIw*0F&|0>wY/>4$PbNMarUxMJ&+̤ Y<&Zz=-yTMHQ(͈a}w=hug]U?IMǞpmG^Zz 055YMLkן+H6\FF%w7U[v%B{^vĢaJ^se}FgW==TL4&mTr CA>$X"4dPfK2\ c\.\OpϝNG҈DY;;霖]dHe8Ά͈ןI ?;x/'qgg<_Y<َg[W L L"YXmǦ)kt A@)6'eK\!>HSAQn(CkAjY`xQkA{[ߩ^M{ZێFmíbFcdZ[ X q) $he{ʕ }QuTĤ%D}0(Y/]u H:Y${'oCxB,'526 Бqlek؄sX; m}^o4@q0vF[5_%iPg6~VB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`B wMJIM"_p^ۓn<>)7X^=^iwYSսGO#"F,6Qv, 7X"4RI"ᘢ{lw܎hN%cP6| tSxki.ٜ׺aU8O?n/Dge.Jwm>1%{F[6E]f-FY7}$ 6ۍǮZKv l"moFUpm5 d$|uf6'~R,_t)vT:Gt<;@hNW9:z\,1hڙu IV=518\G\skÅCḧ́ˏ4y&apLoWE:ǭUY^T}#{GClҭxAh3vci<3\ʆЮ_K- fr2SLc!7]هCp4pWƳY @e# h4|8R(hp*rBtQNgǻ`qleO3 eEK-@t!j;2AXI>k9,P_gyV>88Jjr\:(Pĕ6 X|im7Tcb` 0OLEnsX IeG:}}M=SuWU Xi౅lI)$1G] G#qsH+xzU]]|VU6PrTDMB²5j1³E@2INz ó$tpI-oxU:9fһQ Ic'N(`Lf$}MpmQaи;<γ<+j vtW nZ;Il9 ^Vi:q}H Mj<ғ*z:TvJ_+>ש nn1 S{rZv^O$ZY$*Qι54HYƧ@DzSDf'_ > Nλ,Zo9y  D-`qGJ""&c]Rk`g8M5J- .Jyvecb/{Z+U&Lͯr6m4NYkyJd%ԶQ$:[ƥl#-KsqK1+VaGGWS Ҽe|Hu659S=,c%NM@hl1̤RX '$OQ$e MTB$/zc$ZsJTv61k NS+LDO)a.TM#fe4JK7WT۳^s2;>*U(I$Njb* Џ{lQϖ̨HIj:Bob,:[ {jwbAY]eU]jiNf.&~ero7#]qnMFd)Jiɷ,,5 ~le-x," 7/>04C! * )?iT⨢^z+jMu/--mӮŽkj<7}5f3=5+h8NTk`$Hv[uxF)ٺ9L,:YEPdK;[tjn+rO `mEJu/s{nuVّ/TOWf!@)`ͻ[S}.1pxQN`{m~Jt ,$k&~Â'17SU|=N`Nkz EsOVLap-W̸ƪZJ|E:Zy\$*b6X\8|SLr-*ok.Z[MNgҴ2ZKfϣQ,DMZokCDSJ9(R(PZ$8էD2W $X:A|9s4VhIZK 0mާi2RZ*TFD(]O} 1s_NO/U-|Nh3h"#qSg͊Sm:blA[84/I-d9c$|ΊZzQIpUV#쿮<;F'0 t' *5/d9*A"g֦ۨaGP6~9Y=o^!h8Ū")rN~5&bY+Z6:T|yGSL*ay\2 [F0'}¥s]G{4ŠPv"nQkzw}zD tUP;5}YyH7RL7mfcܤ;VҤDZ̶鱸&Y}Q)5M[5%RI%=ZX_o"RwPUt|J"PY20@EE~8a/ԟ=)c,>oRB[o[c^2rQpHQV_D KEH6۸8^y 0;u|gJt%ȁ,bHPTvX[:7ǂ#kJZZsh*fe 5a߮ic_$hf<, {+gg1x1[΅!UJ ^ e*j;|?;!_NQ)C4E\VtW]cYrk-KmGS.2iG|9Oɒ$P,;c69 &66넋U"d9X`*4ȅ^B5X@[=j"-&A7&v8p^C‡=Z@uԭ-(d,ZIq7pCE[.O3;Q`osfu$$u:ZV'Ğ(h{ żr8 W`J esy.\4D瘾%vC""F.FI2kSZ"8Zeot}X$mQ%XyQIh*H k^ᶱ_qM¤tyU[]<LZ]^ߋGX,C)5e־= ZNPNPP]pXO3LTTNOõS X胾БݷCLİVQcoum^$9]as,VSN~c1]tON"#Q7">JN&KMW2+Ds,q4a`PUZknSvi41=sY-%'6US-E<7Eeٴz}1N;KCnlS{ S9WUOMWK%%Q$Q[%V\\}c'[R~6A_h~>!đdA,U :`b},{ӦڌPx^`ϡJg{n?$A)uhF3{^H,=z]R* =4)QU#52UFgZS>4Bmjmkmȵ8{38pgTi1o5YiX6; &$ 1h>IK#2m rTWQ{ŮqkrU;-t]782,'jdwfRG^U+ *U^LdNc V3_EeGJ*Zɳ2wJYr]TSȸm|zƱBCl6&fVE<] -ycxY'PS ̨j$ԕip-*$?]N1-ZGLGP0 [Q"4<ƵM?"lN۳ -08zXzOhw"-87YsÄx'KeM]OASJV,%`4{j ůNA =Ē$x:inW>WUTUTkڈ=m]aH DŽ\<@W@xr28HQ%ErM⚠; _FS*jZzyA^VŹ~,~oHWYuTrQΠ+(ccrY,6q|y6Alii!i4p m{y擁|Ted((Xn NbF_湣,t&Y=[g,R*S>%l7H5;؋%ü6uMJ`atDoi8fw hXJJSkv CdU ;_5V "o)N( ,w$i x5"rYΤCwN(r9KOSKJ1ĝ؍s|+SM-|Vle&<.Dwy$T2,eb6;m6^ƫ2>f8O]l`ں)(hegI'ko}=G\ecÉ3âfSN.`w6<#M10ȬEaݭ/ E9Sd&~CM- JG.' anx~\A'N&UUdA2/p #8lf+iPINrU%L-Jix`@qmHՋCu$Iӏ>I\h&8J/q5ds 7(V*xB*bn{@߾O(T\3O VbC4qy1]Z'ƋQԁׂ8aJeB y+,IF}g낡uJQ3^> _=R(y e`fBKopLe8oo4LƼre{Ocex@6F,NA;\C1LT`bU2zcM+2ܱVb odz!\, JsGWFed]-?P˜zj𵃡EEe1#)Qaq lz^8w1i/+M U-d4CK3sHyǟ^> 1/kIC˂GWgDUʍ7m֒q >hj:** ʰΧB: 66}:a䫨OI\j.LNٵ_qo8էDmI-) @hy#XTg4b,J#yia{~۾ Sgz~p-kiΞzT+)4 m-n,O G#URXN8ejdsSҸB'j28YJV;ĻZCal7lH"v6Maʤ.z:jULђ&=A\:VźkIa__ `b-K['e/-$_XS0aЋmmzo|z$^<::tp4Eh ʜuO͑VMl tt~ߗ b'M9_qSpUf?_ڭ&&jI%.X[bv2p>ӥ㚮LZ䧧+;1,GPUC ;u⛧.EN)" :LjTmE$ =Z-ˇ4jU$ r2i+9`nl. MN%Gom'4˅oXMD"'5 R4.u;Xvq">Siz +?x$%je0ru#苟@p)okߗ5@Ě? r" g\2f2Ā,wp} B090PboyZ2hT08/Jfhx2x.2˄U!xݖ2t.Bnzz[hgvLNJkɨ\ѯȮ Թ)YYSGQKIN@c/;Mr&pÜi:\Cx3˸F9z-\#0BAK[\,fVSy ξ1YY=z pN['K4."; ,႒Y}/f3O쳨JEU|i|XҞ:)\LrIrĨSNn1+McYZlVn4_';̸7ƲPM5e2,ȯKO0̯:Fៗ{zҩ䔝LY78&/d)[c|H:l 3@Hr}plvnzӦ%BK)RZ%F |B9,@q(kQF #>)-P{5BC{%㿶9BŔd,oPvnJbw#?_a* zޘ$[c%vt~ n4'AؔB [9Rku8D\T|ѨXR.B&Ӿ zywm`Ai?'Zsm=^hXchb{[& `צF#`B0!F# Mgoс ㏃<$%tɠ)(qN]I<|/7Y]nzPŘg3dиJ ةe;mL%n%F?`U'Sk+NWI;eQ Ɔ2ܱ+w4X澍74hUtZFvϻCT궁\CxD'@g<)<;PGA  [|f> PTh_Ca'MoOZKvm!ߍ lm`FPӲhkx.]e,Hvcz졇{/`sG{iQ5*'a)J)/2N nEmTMc1-*8R%]ly }OaO)K) H%s!#IBv=lGwMoFs2()1aΐYus*<mcVD³7*`Ai ÷NahKhN].7,240J)ˆHߧ|lxxuRU1I8s >\R)$E ^o.s?q:-|+,~@1s?A8\fdE4x˝-lVí9_EGP2DμLZm- EΧ.xT%%KeDTw7 "סmҕ;b :7L28vЧ?cձ򠹺īU~Fi49FNT d dHBB5+0qo]|ۅֆaI}O x_ : 6Y n\ \/N|`kd!;1<$d/4-SBJIgy`2v7ĸa`XnynkYs?yUUH AǗ-N :縹  #X9XV5sbD\I/kﲆiU Aە!' ?nw YyuΫMyP]U^pїihE~6PU~HM2-<!|asl^34kUU`rCE3k[N6@׊f]F@m9VO rQ5nD(̲&z3 gL#S˩#/"EIg3E l=ȍ^aXKH.Θj6f^v{ޤbMfY 2QPFUCK>ͭnŠլi%-TH媖ᒊXd k,Zis$q6i)Ƙ=ZGU s!Z2 .qTy_eyxh- GUVA{;d&@^NO4MQ]"&rM n6 `1bJ#%McI$1 ok4wTMP|aQff5MXOJDΣp:m5$)UqL&J 'V oc_za|;f dA;ۈhi(ͲlZTѬSRlA[]Únt8H*;Sr^&̸s}cv: VNjUM涋u>Q*~v Q-c[go$|Ԋ=\9u vuI }7AopU32H44.n,F[+5*Ҿ"9ʨSvv*P/əe52 fV2MS,'rcpWxs` ^{bWHgSф0sJl%㡩*r%Td[;z wÚ/gcM|Ijj(䠗V:O^uǨZ0&8e~9uGΠz52MY))dc.mn ea>W[緟MEPZU$Gn)ai~&]YY)(I>H v}mjNs[ O]M㢕xYjGNN0"[ n^QӬ[T j`X [m7Ϊ3x_od6>V̻(4q̥fm+9KoA507%MCQh6UL?t1tSTHr稒9F$COQaKF<|-oS`&ߟO0Y,qj,P7{[Tw&0ǰ7].[#q~Sƣbzv&*. + vk{*9x33N;֌g.x7ق9}d-K͎/qrs6Nki{ǤEl=0w䢼[| \4#j<|$U1o;u2gNwĀO",'I%'.GKZdH"8mĉYKIWx1UQ.nPG{%&pMfOxov~`is e yw**). TȜ.ډ.@apM:d"f^cJgi$͓( sp96IͶ_5 ŵyO˖Y zXۮ [.AoycIhMip`/q_y3:+9I( RlyZj<4ԨЭ?'iU 9ݑ F͋jjVz0JO"tǪ4^DM)hQɑRzjnWZtU WQ $t=l߾m#2TUrp(M{I-%0NS2ŏV/G]0 [_EB%'QIPߠmƐMUPAf£-iJJ%@_uiUXw̅W7-NMX\t̀K\? GL8妊(tEI7FIgȹ.7'K -Cg1EUA у-o.0h:tD|c5!ljwY$9F)̉i( T5d78c*Iotl5iPA@BfWr;[[RŤ^5^lVy[@jŋf"voaP!B4uayi笔Hhůa}CC~L3ck XMx! F.̒47MEpw 1Gti߁C]Ĝ@hYXb1?A"\z-Ol2\8sޏÌOt@IR-ѺRq0KSj[+0|_hJQ`&,]fI͊n˚~C=Bgu&KfOí͸MG~aSւ'-k~m8~AC/aXʍsFaM9qϬ,r,XeR;r))epYmR tkzՍ2gYxE5Iss^W>&: ʦPU in%tܝ i3sYX{e&ہUj^S/s}Sғb@0C|Q >%[TR9O+n& ׹ s+x"\Su+hj Y6&pCt7l2]%`=\#h28&1@ 'qyv`8k֩-fG=!̄9f7x͘ YFćn~)xyXh⺩ˎ]jtj :[yZIӠs 5X F)3`N@z0o=Ռsj 7,j9*@܃︹뎝W,>W ؏g_5ET `t^}EMᮿ>UTfa.s+ļ|͓嬙o K\ 7U ϫeL,4>ݰ p.GQ4 +fKs@G`eBw9[y'Qd?HwL,eBAQssqkS|7DG2#M:&SsuF>|[[1c_?'+>#əꫡyle ,DG7[aUzk[BURsYTQ V5_6U4 댼JM@O9*<N`1vgzMu'SsM-4sjMͩ ;:@%o3-lm슅ʩME3^lӐ6qM* ]j*GȖeF2j@x{9{,wٚk#{Mi᯽)~ZrەnX\>2MFn:^-  yeUmMLtܐNzWtpuf6\w165汪W{p[8jjMPj#c7!ؓk@qRTӣiׄwtJM<:ZjkΒ!(YoqƛQ08 $:u~rrVy:vxFT".諩D ?5*(1@* f"|}vfT7u/IqSQջ--xf6G \TQSCCQtdyy4d(uiQa{cTmLUZ$'IzP$ @Nsy)jgIQ Q+S7Ԭ-pINl`e2ږ _L.jnfTԥ$,w^6"t4* h9,vatDI|47ݻyvWOO WfG!0X#ӣ\wT @ynma\埊<=54 ]m~Wn`H:뺡*ixZ(i1j"DB./OˊLr ]AgPMʚ9dZ) n5W7BA0g}GңW(7R(+xG"$kDbEsg6${U(>d]H* yM3̪嫬vIdrI%Na + hzJXu{`H[ -qq(z/[B]mT$'n:}]SZq+Pg6~pB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`Bffpdr1"(Q!PDIamTwOsI@e OyZk>w*$ i'd opHՋ)] LNŚDU%M k6PtΪ^rL\)ѭ" a3Fh_dٜz 죫(&blHWWQ[mksD'S־ Œ(S-R.tM OrG⟢İJ)Z]0&ukon[[+Ut?O;l.D4\UXϥWpʷ; l12<u$G2Y%,){y$4nyz"nw|.u>{i䨬,: {s#NfXn3T0|P rzjM?f:.FYsQ*u,6YӚǎ%jW/Zc>nK&k1=)o@50"R9Ί#;j j!G YO@hU9 o3`b=숇w9tt,SS&/&dc;؝zl2?9^[]U OSV3H.ylNq{<~FN{FEϣVkUN2<ʫ%" U.j X Bu˪%^H 61TssZ^LZ&0V*$y 6"V {2U zXd~sL8$iˮ*$y&JQH,N\+ mjaa-?M,6U He 1ҧ+chу^=-6SfEM2qr_I:y, b:Xk۔;m4kN&yHa,9lѴhGrI7nwX:$SL=uYҫ֤R WFmEk1]iu8gY.h%>|zgZ3Ի(cԀ2f:}5)R pR-RU-5/:8LC" @ArMNg#j&%WI ]m:7FM~m8˛nhlL<ηnmo_M1m5hk[+i/H3iNBv5m  [2wM*.q/U3` no]U8ac%']5tf[E59AWUPʼnR鵎oqO5 4 Z-QsG評-U<kgmsLhaSi4کIi+*YBZ,a+Ve bw*T$L.FPS*H&+Y~ʨ'_nc!\T 7i L[㗁^zH(ԁd[S\;UgCNf 2Tlms{ŕFIGQ M˭q{g\fW Pyb6Ǘ>pqJ\}mA]r^*Wi`u1nE$߶7~d_OtQRy:/#|bLڷ*U2XɆtetͦGK^:\KwlP ګ_che3L  nT@-cɽ:۵͌4\aQse UR ʱ!m^`T훸H2x%.Q6NatSc 4 +D)G;b/{k P5kq$KRU(I!K=-kv~<5Led㒜K#Uu*m^qFs[:K< {HPdSr-,=}~UZJq.( 2a< E9n5upq4תU3ʨ3.bih]Нh3q p7 yܭAv'*.}zz@ p֪9Tft'%:eJ7[7Npؖ`9sFoK&qhh::|̆YsPI:m|"}\E!I1c*[ۦʧqaK=-D<&WـVR^ی0acQ4 F%IeFUbH:0q`Is O2WDҦd;j;^]H':x863Y`^8D5@&l;u&gˮe ⣿OkBVӳFNnyXi܃qx Y*j4l`zKk5 {aE!ZXq pz(V*y_Eg+8Gɲ\M/Y1[no{ka^sc>GKM1WFVR"J~=%_b@eCq1*ÜEg 4UZH/ )٘FڴfGpHY~^/fs>etu3p]U"J\@НCaQ)ms>5j9MdLtX@$O]Z&OZ(mP4*58{3*WHF%J9/Z)09ԨDQG]RQMYTFBTm ׹6F>^Dr??eW̯/ˍG PSuRD]cP@@A>kv=.|, Os1zFiP;cQb";i㪣3Gzze(! sm}[ [o;/-VzqYpWEWQK^W2fX}h &+_n=}8;[]7y] U>y܆3c^T!i04/%*EVcIM$bn)5YD줻`&O;ZqWK2u렼k`p>5Y `_[ƍ#0i *FݜkA82l sPӰbq-DU}.^{S3/sLZ"KaLw(ǕB[RsiP7Ǫ% zBG[D%_9@C($jZzj w8S66P t(Y ADJH#~؝\4Pu D'm)uJ6'"o{`Z*@HN\JR~nQ oqn54G]\ꎆҩ r/0b/1+'\N@o#E). ׁBTJu$X@$ ۮ!)6'D%Ib,IRϽ,#`:W,fO0.F//g?h13?CV?t͏C0]lG720!F#`B0!Zk?'c~_!< ٓ^m iO"cj##p@4^b'ܶcY Sߵ88ǗSNa$q^Y2,/j誥ZX1V%.@UlMG~kr@  xM?TGzhgҞP^MĶ2ڎhfxD2rSw'YSAǐ[WfYl@Qq;|-Z{5Ý8N ӞᨸqEP9+G(j[i* ɾv|[p_-6V#;iEu q<WU;JQpmk$}6h=0pEpsd!MѓU5/[V ӏT>Kķo%xْTpT_9x,cZ _|}Bu?%@c}|] #)boikyPxT>OY$UAQ%d# ̣P';t&'sCSzb@@mU7!~ Em<& rI t _Yf)_OF>1/w%w J O&Q=JN]5`NEJK|*8֦uikADFх"{Wc~تU5q˂@6  W._KAN+3VT¥ڴjM|C &FwU 92$t)#AP-c'ǼcAϱ+$TU,"!k뿯Ȩ[ 6Su&_H0cc􋟴F \eNbR5-2=(l! H_ОgMK9bcwLdqc*&h M(lcʊUdDpЁi`4)4-:*uq;+eURH EUEbH6,=F O5nfrĀ^K+I*:qlxdn,if(M#~{_jo29vrs` #S̸WNQLavQ:j Z=- *@p}+S~z.7߯\3jjydMZ\@Ksɣ[.N,ʬ `ˈw2 :9@cnϧW=pM;M "*t\]YUCBUiGg{v*䵙N%&id]O\#}S6b݈nTu6Նaǹl0so8`8ˏ_%a*9h'3P771Qem3虥e#UY,0 J#@[x* _qVovɉ7:%ǴQI>O "ꕉGr &q!iMTsfpF52LsV\xz:)#ȥDBf_ok۽C@p߭WFx^)&>))@ie<(ȭ Av,MCA$LySfxõ5y=}+H2not2577b\z*N`-^#sQ=uwVVu0 w%}3')GqrwgvuǙe-U0q /q6uK]LX}*kV).z$JFի|8Tls*VۯǿK}uY)4ѱg#"𻨛6,5MxʖXcMEE3ȤtǓm|H,^ ܴj; tuvPDejLU73''sN#J!ˤfT+,MA8>IV}I5mE mMrJǥ޾q7j5>pt:Oo3ʪ(䨥dow6H 6sC&52ekfwcU[m}1xpIիYU<~:VyufhU].oqYauR]~uif 3N"l-H}H@":x ,JkE6XYzG&w!ΥyukMSV\lPkEQƕ'r4ihuaqu|y/"34~:"HkpXܒnNa@ Q~ a l.q*yosIQⲰ=!MZOTJBXwĨJ6[[~`I,n5,787X ۧىF+ُ)+lOĨXRsbW,fO1* ?1{-cL[(Q/MLF#`B0!F#IߣwQxS_fUd=RUF{}7JNA ϮEbϲ93ꬻ5SAU}7Wb:iQ^獄<ѩO/lTgy",4,z@ JMI$juV}J`NI4B"SLM[.LLqx&)A6V+"c5 WCnKCVO_2XaS Nǚe@k|t5];((<̥0 C`kXj@f$VuL?ms .M:ar>s*Y-ujFաںwlnhAq8ErjU쿇x',dXSmAK b"S"㟂MY&efTtmPX-2' MR!Dؔmf/aoߎ*ßO([.};0A\2:zUnVE^FXmWcFwqU5RHcdy(h2օFV:\C~LzxfeyZ,3-YULM#Q}a ocS,%J䆨KJp`͏Ռ\I.i1k,,2yDMٝ]L0ŗ%/l[HS..2u*k{1Y8L0+m2-#k ߩŌkZ${IlѢ4 ^w)wK$XZV$g\0fd* 70K3uO;bb;2tVr5...Fb:=j4˘1q!?/U Sĵyʵ$4GHJXҠǶ~W63hD{}~JmfM#qݐΕUj0RnwuNfZl x|R_jbfL:W̲Ȼَdllm|-SL ع&WP葋FF=uYu^NK20uݶ$N} xq'NU?EU$TUIG!B/&\jR-m~a4N,(AWYa&FbX[mߘb6s5.HcYO&1ĦEgXakϯ7>JW`,[_ӯ\V`Ԃ\k0< L-j|ƍ*(Fmܰ=NWdh~-kH}S_EEe|E%$dGTU? kb IUxQ/dRq ,MĠPUe2KxekG[^V5Wkyo_Qͦڃϟoy= )%j4Jb5as[3?zn7UCFdYD󴲵@7ӹ@MA3Uv;.Fiş~ Q'40IlH zb*`#|L;)ALk$M#U1XYX5͉3UT`ZV7XO;ϫZKH[nu9dd9M%>Q]M}7[Dbv駵hrG]7]Kky-GpIKKBcVieY~z+c;se40iNɟ?u*)jĒE;LR}kt}QQ)05W> =JxZzZY)X YV@>{_ JG6zֆS} aj0Nήa}uEL]C}=`lQxt(FD: asJ^IְNg5 \siK7tw1]"UC+O2RUA.`&1 Nu:luQkY*ִSisf+rO7 Ŗ̼F*(U bQAIrCXcHqLDi<, hS iϟU'ӌ:1̪2KSS!ؒORI1mRL'UvazbQWkcp_~V'd])T3`rD3,^`$ƒ."̻wxH- G,j?ʫ4Q; ґ:}X.wkpUEj-ydn ~w^}ϮlJtRMAQ R2z 3:)itkkC)RBSDԭ$}X&ctr}1 ((',O)JKj|J osBsJ7K V6Q{7S߾$"at0 4!> OaP@p) 7N4}onk0)(}1* 1)HQUN$ @: w;sJB2 .' 4H>iXU)Z9/oI_,I ?@ĪӜF//g?h13?CV?t͏C0]lG720!F#`B0!Zk?'c~_&|h<jly(h㕦of;aS_ 2kӆM Ϋb94QQC5$3Lʿ1u: P~?Ѡ^g7/"S%e~Q$C]`1^0[p \I&.?cWŚ fQ0Xq~Kc;4Kw>d Mٴ+5sF*Hpe7%o~*VǚO)buuN$| (1{eYQO*&eh)$V:۠7žW:\|<U*ʚec0k`K^Aܶz WT4>A/8OUN5g˛pYM4لO M>Hʀu,nn ""P#;-̛#>XTڟO.vYh#῅["ŕ&IkYubhvM'U<#agMߢYqFekg9D 4FA,ڭvd,T6].~^Z)͇_$R o[\q@k@- rJ|)p7 ˓qzXyf5}eP3_8j1f:H: -7 sf|]ޣJhr)rhQʗ `X&ՈT>k A:1Tyb5 @9,n,@n=)9>[j[tᴰpM~a80z|.lSϙu*IC9cqAM>^yAir*e<.wԪztw NT~z؃OžLANbl9qx*Tsٔn#8+hޞGaGQr%Z\6= qiFP+hU3v?:KCIXža2@UΖ22 ٻ[cԨ.7֑. $ j^?n$m&FA:mM Ļ'3<fmWJ, lv;k^8.UeХ*Z\‡hb7>jB3:K1H{nmmE*RF:#mSjUj?#):͓Svcjee+ `Zokvv]*`2 q)f"zO3J2 U=IjUM=B:j[KkX8F:OS-)EzyCVЛH.+= _u덓R|0ږӦ)5>12u KpT\FkZ0׍fةG~#pc##oK3V,'h?43l6hg 4foQ؎u'}aSĶ65(ɼ<| -+ըfҦv:z auwT$@ s04&[$ɢ$DȆNQ$0^7Uc\<Z9N!ڎs5Jjj $ETqc~x4=d6-azIgKeVKXF;1M7y6(1e;xE1UiJI`1,[R0)S:ECxVxqiknajbk QH]J@h.֚>_n7ƷxS4&K`py0O?a< SLƍR~RH;۾09 _ik<5;qyôYz cĢX#id嶰.{/b0Ľp6u(QJX-׊X*#䁁5ZIA c{Z pb@;NR8|0'^7Js:ژiu^i5\ug*e&YIT2g=HFеkȲ#HfmkbK#{FZhj²Vt4"& ۓoRGل0E͇ '6AAOUQQ΂X$]ry${SStIW0}`)'yTa=nM ܑ;je7Ta5`h7񱿚N$JxΣ7QDo`7 iV;c0up+JV#INkZ*ZiLD)p}}/*V7E/fD~]|VΆ<+zħX5e[ܰE IGQR\FA?B,9j3|*,3,NHGK}z1/y:$O<-*a >FUFV|0ڂ"ȾyҮ,0ٿig}sŗC-E< h.cv +UXn@tjsARsǮ.EESAI.c 5? 2UwFl EZ22D5;˼.S0Ʋ$ S,AX W5/Em4AVYA~e-%=fHP [Vl Ggp4<-*m5# @y㡁#m<X8m'xW(J-,33¦Jl16hct I2Thlu$\\S[ά(2IzӉP>e]3*fPsq*XNH(孥2UiK^K; N&O+EbwoN?_p> ZHS9lm`7&n;o}׹)ihuva|"wHm51uk~on0}e\s|L""ycGq}G8+F8H*YAS*O9(>FSm>4R͏݈S ܷ]`H>P`BOoAE`5*R\vʅ\ 㣢7YMacBb7B[\ (Cn%%C Fߟbq' ]w8)bv\%i~ܜO @J{8H|F"4zji؎ɸtl! owm踿!Jv\̥6!uS4S1* BeOIZsm=^hXchb{[& `צF#`B0!F# Mgoс ɉ#8#B˗T:L`NuFs]^f-tθJ$j\k99YN t ]\MRʂX6d.*_O.j:y$k# ̌*f(>]7m}N= i8tJS^#R7 "$F) 6؋޷?e Ӯo_N")8)#bdmVC[r(=ٍ+9|IqvmI6r70ShRK*mG`A ΫMdp" j25#Ǣù)Ol0R|8uM柚Ie7;l\-0gJW wsYd$6kX>3{']o$h꒫1)Ss< C M kc:+Pc̋r3n<60MG[hֶ4Q mau F -y#fDw?\S1♣4u5Q0#?GQϺܒ|UٍA<ˬK^ eϪղ= J+r]뷹ǠeF=IUi)3 ҹzSJa f,~\yh%SxRr]Bb{m~]|'E.䵠NW TPTqkg(F--!bq'ZF #v ipTi^qՀ5UPibMk Vpo+s)@s0Ⱦp2"`1m9~K ~gTqu Ty=8 dW"PCm59\Mӷ9iHO232TVj6{tNP܂C701U1u4nh aaho / [%l\AF* ݕ+}EI.a)؏ɝ,ѡJ~./m"gYXh2S 1}D=e*Lp^v_$JVN+Rѵ7/CƷ}Gspzu~QR@gL)U0PYGK>VP;|%S֓Mt[כzj~Z*DiueߠR:ʅ`y,6 DN!qMtl {hS4iѦ*\K꺣<- jLoΥ "6P;nl6E꾋h?Okǂge@/גaę,XΔi׶ðcmSݳ]9 YEut5 4﭂klX1^S-kNe2GLHCM!Ҝ @5^2|PntZBMz^:8z]$ژ [U"R҇Tdܪ*}>1gxZr)f^m|_Lo'Td04ófl7?p?.R@'D`YI6!6{X{ LP+U֪j)j ,!}z~o+gIMY4sCSg,_UM|7U$4Z+(s*CY$Te%8~~(bxk F!F;Jhp,lEb_V \jE \2&j)HRI ~]$o.n Ӯ)j4jӪpgqfaT@e91cjm!%ncRMkfhef6.7mECPuk`fTMmd2SK",A|,s <8k|v,*,v_)%zmN%報8f][ni ]#u`ܕ=FpɒH@:+Vb핼[<%նMNCsz hF 375\vSb>زOη%SQ=U>[=8dI#avqLzu0hNu/-،TӚfHUTKR R 7TlI {\}L–*uԸY,(GaUm^%#04I3$cN.߂jKDqUY.vigj:S~ D]wݵoH 8fP(1$9nY6FNیYQ5~:.)ei-"j~: -WA}}N2@Zn\[c|j -IYƻVi2ΒdM3*$me9XolC^35&?CVHq8'b)j?x 9+bvbՆ y[fxvr #@=LX6®W50DpUZȤ3U#a"y5D D|CMYS?5`n%hfiƣCjҖXrXH8ԫY 'z)g̦V+,Ip⚏*wxq yk?`aVsY&$B'v˱21O]+}n;@/6vuxUBd^z|nY S3[uOK-=* [Xav~AQQ|ebin}"Qsu> G?Ufjf*1}s,TR{et 6Jg㢶bx:#Ys"];#"6j6K cvWw+cSmVW٥MIbxi#J𑦋Ylr=PEGfU? l}'m#Tk*9fmL<2ʫx%,B2yL}&ʌ۞-F;Oq?.]<;k@}n?gͩ? _(8UY°3m[ܛ2a:;ɽՏp&EƶUOlsR>E7l 'phbwB%\,sZgUv1׳V+`[P{aZ.Yq["F9m>JF*鹡^!;^߾k7Mpy1AT|g#s9/i)9S2I7{߮$e*a'>:)թd~B=LYu(bP=ZemTqʘŬVF[HJ%-.kZþ.ֶ&PDK)АX~Jn^lJj8#t$  M @ vt`RPk( Dn~ֶ9A{LtJD6 `AJ-|Biv!Bl>(M86Zjl)eA~2m'$9`Bsc?4?nKx1=0?q xk#`B0!F#R7_ HJ= ġ7Olm7u 4ȏ=WiHp6WLɨ,/$G0ݛCS`|T5i#_>+Nk7xշuj=N4hPnI1V\\Im6lҲ HQWAU}Iq{ p*WqSM}nct:ܔJ<-/pf.cEMTIDM3r"PtT~.1U*NUw~j'՜q)hyy9-p GUغ$|?_5M|(2e:8:34CBOo{,}:(NV(R8veC, צ镙Tf>>w/ Mg{ 2j8 G "Vb &uӟ%S,N[IMR)W]D HCi$iOS~xs.(x65Uc2u*f?K{+Qp:&3uڼ/ᡜrW-d|MKQNao11}%#ba7ছ .F~{G7VSPª&1TU 1`A %h$]Z410UQ'H{Liդw;m]@s4[h= ͘gF~-m 7e}QêΛ^P[ D2 񷈜WrgOK 9b, =R4]g8R#U(S 5w8.6JQM#fTMGPe'Qh%ïu\? =x?>1Y3o,nMm{ca>.P f; bJwCHUnk nq^bd܈箼G趡@Mƺ&&"䲝2H׍V?;n=2X)m$j9BkײlSEN#TiDJ!mcq{mv|:*]>KU%:e6ǿ,VC^Γmu\SmC4A50jU-+ɧؓkt_[ ےMcy9ZlFR -uC:{BZSK\ܟc;c]K4CL8ɊEԾCM[IO"YCGAt'' M^4dwSaz)ÆkJUZtqu;H iKt>9p/1?4/W+aQ*KD+>DcڵZwf5\-q[Ч<=Zx~*:!H 2;)+О ۚ",C\EpKSP-u:"ڥWXk,;؍3b_,G>><\>*z8asbu@GEf`X[}+~Ӫ ]9zBa&H-.%Z#VUI7GmasY nea ИH\ckz[oyu\ 92=1-0Ru}Op fuJ)E8y@6؂7qZk;Nܭ:ΊQ.qCԹN]Pc#V3ϑBq(b`XTymJTr8[~k+*]6K1dMYt_Qc}f 6Ŵ;C*7Cr7;{(M>ZuR9$g|ڮ(QY j ^ֹ齭bY}ɡLN WWUbzzD4m_mi.\L24.L:G77u'<\wvKAw_KS@ӕprƗ; QW-֌+bHVJ3TXY[0ۥLv2S , վaϏj|Ȧ_WM"Ie&jgY Buv*s2@[5M\9sM89x/(ʸvDEJE$m +Yvf87Oi\>&dJ꙯u$k*i%@7Yk_q|uLSs%Np8gb Z4zG12 nɣP|׵{im6dx48fk:ZjcD=Kv`}׍ L:p{OND PBbn:_GmZpzGTp H*%~|jD!a`A^g7JiqoִTH 7iy%]f04l'i''/`,ıߩSM̓_*e[$¬g:L{hւY"í ]E\|D#8 S009M;:1r^eRLvLj3?̈́ JB$sƁYXma[eU2lfD"XE ?BlER{߶+yhݪ$łsbzz6U6Xq, Zu`ak5z:92d,@ı6T>~@t4νuUctGOMbX7slkQg{C#P xjq53LPYrZu=s4B:Xӷ`wt*M2f];H#f%L*mNu`|- +t9_/dVR)'Hi{kaZay殁=_5敪42H@ 1{ݹ*y|3jQ$Ka}[R0Oׯ Ν]'7h4XZ{ zKxSeG,[-0H"G6n\]M 9NVg姜*jaYTn?Ous V'Z1;’>}0,CCim60E~}>zzz8D_/O3 ('q Žq;JgsHs\îRê7z棾T^,:ZisĪn,]v$l<#qnel00gKj8Z 2H 1rŝؒO8Qĩǩ,#s4Y+XX#I P-퀒a0P.å:#[HRK7hCQ],0 ϒ5/͞aEss_/R:#X{ ݢL򏗿ǯHu] xfL0%WCAr2L2]2\P a0+c0#U\7FK`{+܀q  R-W7+NF]In|O n/9Qb,*yR(%GPé  4AApnadO б }q([׈`SEk Չ\es$ Ӧ"o*tAA:C~}5yFH@7=1 tHĝLHPPEbH! /~'DJV rxpJPyg6~pB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`BSxD*'Wz :v1x8R1k}Ly,,F.sF/5LZq}T: ~B5>UL2SE .QO1Vt틨{$c&^W\bQ7|#r5 u k|^}dBv?ZX INp&⺶EAU$&[ZŻ{NjL-~IùE 2SicD[Rd ܕ mʵ_R-\ )d/3RI֦cC(`f8kF z$vvNa-p ӯR~H$iu9@pOעNWy%vTlQI`>\N[c# :k &g|@Kh}n^{)"8x78h+2'ˢXx dPobډ]u1NIbxuk6ɢ$ LYDĝo/Rm񽞝Gg`>*9YT.gIBꌱK,㷮9e2'|1Y]36P]ܝ!tb@qEɴ[(-lu./ /SUSD3H#Jf$*@Bw>kǐ8[O#IwuF)s y sPUD= p;N$ X]ylA ^vAS!@&m ɷJ4v*5U*bUocE\iTe3PJi<+Obz׍MKH%f;]GP-|2 sbuw s(+h6yKs#:ӧ9{[M#( L,ɒ:DwYZFzI&`n1.CAuVfv:lUA LhDU0ueUo* {ل7:( {*H~T\$j`ԡAjd<ȓ+ p '-Q S ɾvQXq\X*[4T` M#:;5jn,ĀعDy*>ddA$92cvQ n C0S'u/!H|TP"RZLNzwBvNkYk)sgiOE~f|&*$X\+۪u:}t1ݐf *+zDp(kd;`L1[ӆE8\%FUl]Qr9Y52.`Tp Do9?2gvW SLH,E* rF(˚)nBMݸW)|;H2"v;d8cRt,k3q%^#Ya̫޺ JZy㧖QP-Tƛd~xYt]LeT?KXPM{L-wz6"m{ u3gwUHi늀9^u\c!L5CUmLY∦iV-^qjvP?.:?dyLu++T1MU, y{[P3n"jf,ǭϧu&X@0SXCI7l:tjx{2-Ti`ٜeQSPB(faMS h#^0nXzJXzT}uj  NTC,Jj\>۱#;L԰&5jf"ND82D󱒬+DV~ؑQGt0'|Pr.)l,)V:U]ԊX]|zbp5h<1&/"0t߁8[ULQx0JefR slZrOA}tX̑G=e\Gy XEmsq5]u=AYiI`#Lj\knTmnam<`eϛCQ/͑*,5+aNw߯Uw|`1#L)k~~QPgJz S8*Y 3O(m@ ocՓT*\ʖ3`xmJ: bT/{ [P7Pzr\vV`yV ?1; mmSu6I"NX"eITBTINPV  $&rNa.c87lVK>rOdd$T -׫0R3˚,k>\֬HtIsw=Hliφ?3k=~E&VYEHS-kZۋ$î CN]U#sgv,XoױJ.>k!apulSOQ˚UG]=Dr|$Z3/6GȹK1!CNc߆-h=z*GVXuBC #fzǿLkd9)O?xY\G9XDH42[f0u#ӭ@k@XI[)55npQv |:Em0EF%f.TZhStc`ɶ:v~Q#:,mɫVlF$1M{t}ȃ9k%_C`mEE&cՕY}@5 dw*=k؊Sj2t6t۪Sc9[%_ Qּ0 XHVX Am*r釋]F2DhwY{jwX3#oGO,T$ps#vMNy'WHhhFf z r4ȌQZLK-AbKs`l]}) 3:$5Vx# +Muwa.>~ g,˛Q4~+燍~&n{Yh:-*}l;p[E|>egݘjW XܬHڭB% LLBPܞ5S0uY2X%U`,k\՘jv27|8փ4))# 7[}XȄ;u?fy|/TG$>6aj`פwH*_5_]_v!J܃mXz?]_pgqxYfR"ˡ'i PK_~ e d$;i~I3݄ae:T3; lVI]eCmX}T,FQ|{ <~ׂ8 ɳ1(s3%ku\Z+u 2pGB0!AfyÙ7tYw,uZRƄݴ0!LVONx!d~ +T-dϖҤ`jk_a1]I?yNvVsV2]uQ?5Vm!zbÇn@:TI+H.Z^gU#ykۙ5n`@ ?> !XĮ&qSoŜTXT he)j2/0+0C|9Lk]Q +>}}]LPԤ`11!7Ot;YۨHk@h7MUiZXc ["I/,w[?DSljӊ$2 %M.z&Q*#y)ƫ?byh3pձ Դ*DV9,NcYӱ7ةyh/[{U2YʃqU*lI#O_L jL&1=\|9# smvYPXCe\/fd:IJ2Gܐc0le*5*Kk6]!#8f;harE;Z\,g*^mգ×"&BrMz1$#| ECLG Yt&3\裸Ӈx3l&,Ju";^ޘ%:8+rmeV-n*12A1f0ֶ Φ7ŸK؆S7QqTRUu+IiԢUV"}rTmڈ߮W h'Ԩ+.OQ:L.ɱKA^5D#ZʈȿI S_,[\w\4o*#Eh֏bƋQGbv2拉1"eTp`̮y#&dⓚaFnf3Ow4ˏ ]Sl;*aR1gMEI` c`==\sr2 ˕Vzz4нC*QHDj"5ҡ` =#Tw?-}$4ՋE'/1lA$l|OŇaۆ΃[k`ωU?O mUQM .Xh$=|K&WT>..Ò"׃`L5j9ʸDثä'QQMEXXB `/!p_A+'g&rDx~#EF̆ TD.xǕu ɒxXvBt:d ob-vc7SnO6`Ҙ@ÖыlOIn:mO9:Oav r}wLE$9z2B")fr... _kdےf 9&\_Q2TW %!h1ֻۦ4{> A$ep\F&,yugyE fY]"Ƶd ͕]IZn. xRnG5l uT:~;L ,p-[;@yJ$"նN*X6v=jhY/fՙtg2e52;82,BAomb@aM˪#Z>2zy#o-T;ߡ}%`VeFPlk T׮N^v6:5 a?Lxr*f-j~jܟS,8!& vAf6z oiHb-N vc}e!P*pNY=Mo1Ӝ@~;y!Rm읜^V"3}"b okLZ4̩)eZU%4 l<굱,ķi<hY i{ nϋ32o+1V$ێNAi90) C CR]al^%ӯETQ I{qv atږ<Ӯ13E4$x:q{bſ}jFq$՜?Γ H {cc ߅IL5vY^4^!)*ᆠJL=.$mo|r(=\uUVs`7 MW[O-\͚2jJiV9- A7 u*RCoikis)0$Ζ;#˗/4@e[(1ؚX|4VL򞊭xp_Ga]:K"(e \7]pc0 T֪{4m[[J+Y oB:m.$"md5t8E`/Rx$ݍ (a=JHշLB4HM젬XtJT Tr5TQTGNUNηz]AxR(FtAAa~o I$[WĩQsuF@ ؉F$OlNX˰/"쑷a /lB?[ؐ )k0)Hw_Ż Y6!PQ# ^`AJw=>+W\N _OAh]Lqv8 R!TM 5H1[@]\dQZ,AHrpJLv;툅+[P.(8 mmN6@z`D%B@\|!k_BAOIZsm=^hXchb{[& `צF#`B0!s89N"˸(x|.Qz| &c 2zǍ^VK^@q^ dyigXr =Uxt)7,]r,!jϛiHcUo\^ Iyx>=lϊ+h2{5 􄿘FS4t Bog KMxqQKNS~ϻk!udJUJY\Y*ȀA }cPդ V{a-#E7eYe(Vf2HIv^v%'|-n?O,-JY:,0èP- }].9Fy~˒E(t]Sn q]t%<9KTn .j{ull`!Gm6!ym8P@3WNJg7:sU+C̎^˰[~(uӯe`ZoPsT,M%) f?`4\<,,EMICS4AabėmFvŸp4cx=4-uu,E/2R`UI\mؗRZD)28YscWetRA "s]y1QeRm7pL_.^r~Zti窘9Y\!{Tv?Y=N5 eU؀N<(9M$mwTq{]5Eu<|8ʪ.~>]X ":{iLSlHD+k't^}0WFA-lr)U\UqQHSkٌj"}Z4r \&噭Px`$XO$` m:kKo45æ.|_J٦gr3%Dh$ [ $b2G.tM:\n ȳܷGUP"m~o:Z~jLhju=M;5jcstCdy^YAOϚӚOYB'hђW E^݆a"o{!X+B.=%:"ě )~׉"=|F49qӆZ3Wt Xaf O8M=,F9#]h mZ EFg ,*X唟9fSrA©am^_0Rq u&@/e:5(< ɛq%%<%6FYǮGUqÝSΓ6 8]s 9dR\ 72Kl/֫\F:#Da\-LFb#P#IbҪڙiU xt\e] ufb#@e)̬T-1B63\nN6_[~MbtO5׷StLLV䟈2踊#9vY_G8i5X8VEo0,Ew”9ղ@2L.j*3s*dD ,5T#k_se ;RgQ¹ԁ.5\{뵵#/ix<< 顿$xtV!N>_^rnS)$ )3o2eJڬJZzCb"ͺ2뇛}&>[@5泪\AtIpl(4%4*,~>^c7G)Kj% gɏISs4Y !@THT#͸׹7%, Ssx2A&q}c<:BPICP4UTA"իO]]-bA1I`:dL rbI樋]](ߩ*k2Aן\ n˙4iE@AaQ,tumZDmW6[cq`pLO^ G.F`!V6X kCq;?4on1:[4WU}^vMF߿B0ȬʅB-W4?.ASM[Ң ُU?m^!9I vTsUy|HѿM;q녾"<>%29}g~j||S++!UU-ӭ~QlRGW$q3[a vq鄙E=ujUKtSREW soϋFQI$1:ҘyF[@{קrN9Dg.gLѪr:My H44K!n<om\5;R5G&-1pL$YuVr+\D<Ť*~)cnrǼƊn& ۸ǨH mFY\Js9SV$_(m~%^5SFYm|[GV#a R%@ m(Ї]h(&nh"Av ςtA܏\ !I*R7$vz|/_kbnbP9%]&M~":߿l@R{tSb#T6(;'@Iq*9%-~ .EBk !NۥفG$1vĄl d,n"B ؉$cuR!MXUP:o)n m5[A'N%A?ٴ  ?1{-cL[(Q/MLF#`B0!F7qE4WGSQ\QWSѰhP2ROey'5zMNaMB. 5;+< tչ-d\?YS&WPq(!uUxk^4eUV'y~[]]VV咴55+K##FtbJؐt'Iߣȏ*𯁪79db)}1KL\l-Ө cp1cYG[Y 8=&z1Ac=EjY*  "}0g`*!ձ:puYgߋ?EpdgtUQSLGB UխoN~; K])pq\NU*a)v#XQy!㗈$S$-<_5S3ڥ[Y.& &I'xaR9jg2nȲ\jaIUmcfB4 ׵,F0cMC"#PA uZ?yZ^1[4p$j-<5_U6=>TV~!aa IٞaK8(&e)kVRkFE1$(0>QsfI˗rKV@l5/S).W sC1.rݗ^ۂy!p1P k|9B xrQDbg.]e}893-hL~˶1sQAKPIצ7F߾  u`HBd^8dG{moAAvw4ଖԆEl02*գ E>F A;uuldyM`U O j!7TU5#YHn/_Nk.4<>X6W4k  sa CBs\<=5yp8w؈È݇}i[w;w bjdXѝКUUAI)*G"X3'boD5)T3lޱ(eU9ܥu$[-{|tƚy]:姿WS&k6꺪Z. 6$,7o-u6YVrבf ̣c!QPR6ZlRx)&KZDjNK5sʠe!iz}WtSy8>_T9 (\cUFؼ $Qm}J,:#I?]W%?ATѴZ5;⺔ىkUӸ1eGPp{ ՛3i+ycG-)1);;&> $,S] [J3֜ǣ$,,[zjwB}ʤ<q)hzgQe9 gy%D]D/˘PUn~kcgMiԃ+r$뷇8rQK[ED sv`.wmzZVbЧWtm~+%I)tO|\lw7y鵇)D[KmX\ #۟@GBr;' #{i*2j(^drDZ鸰=v=7|30zLƾ~J<9NJʈA_su{Z2&Gtꢩ69biU~btQk@mo[@/#K a/jq=WSd+6XCLUr#S cmVK1G+NQoRVc_%Yisβ9W1* %$&H iۯLYD@ۨ⢡hO\ԥuCTVVԔPrWPb"vkk1~iTea*4{Z^^^3j Hӟ^N]͙R^8c+ܛc~|Rd}Sݜ6g%u79 !c769$Gq4;P8_h=&oЯ?#t#9͖>-# O kol 4Cl ;oPG9]~JPo8YؘD쐋 B-c%Bw8JR3h,) I}J=B_s[b|%do!L#kĀ6({ w-Ĩ H Uݎ # sUk)~@ Z% GolT98cvrQS"Q=q(/rO\F:E_KJD\9)F}q;n-)HAA1Xߟ->oQ-lB6P?t?ٴ J9`Bsc?4?nKx1=0?q xk#`B0!F#R7lf.!G 8AM;nV\lR$+Tro벻-UT@d?v5d Xe,cpO׎E4VrjZU03+ I6öW0 u:|R}L *J3.z\[cq鍚uP%Nt4:@)vcZs%<Q]ȷJ}7'm>iesM`2UiZT(̫1Q~›B9qpE?9B~;*^[4rrM؟5ol-R*i O]}-6^ Ť66`C7=Nʺ/v%I1WӉ"IGKW&`XIM,^hX} }\Ns2m$xGs7q yRUrxHg čn QVra>\9R*6htVIo!\a{ئ i#eQfyK)c 7*E6`/0f2낉A 8fc hXGT}xͧ=0wpS0ٜK ΍EEšT,|ޠ_fɓ:SDrjbz&=o'-55oLKLr]2D3fM6_"Xͥd ,k]C$nwI,щ)|kenfJnO~fScTn.aKsR WO'|g%P gVxO' .դtm%dN i۱?}.g՛^kDGb *nĦ pOy(BlV:YuX-8*xWkbx@uE0g 9n{5Uds*jq|]HT uUl23[=@^ Jt205tR|%) YDDNH؂X|gVn#YN@g[|~GeRW͔QAR3RHVV}#$G3u #i_x.h$yTcT1Dl=/sT61xf-棜7m]3IOyeY7Fv6=*WqloMjFsO]~W->^(yĆe7!Ra{ !W_)m;}Fo&Ņܪ*bmoe%u7 qzICG*;R ovX줎x#ث Oz|j d O&R.BV tӨ}{_XA*gi<dRHs22%x*s|ԖIYeQNZo:[ItiqӞohUx8̹r~=aawbhJo 7sEf_GpQԹS$ZPH }-@,ljuveBw™ܙ4z"Ox}\kw\گ˛{ThõrOLd-NĂNmn)iaDŽK؏;̥%[qfհӤc7[Jhv]]|h'OPy=%4u!v+xc2 bw'a{m:lFZ)믢ϊTdOMSfuB2h(ք1 `qT8i [yBM8* r͝FJx`fE1n.v8>7q 'A>*89hɘ[h8 ,Ϋ(i#0؋JYRP>ԉK!p;սTC8b)"ˡr[+Q 7 UW).JZ=~jƇ9]8cO]@3abhRݖk,CB1rji8g64NZHJo#H7ą;bӨǴIe\'KT|_*VHPQ;j\>$nB2Z 8va`;! ـ LG4BBkӾ ۈSⱶ~ 57TU=! XbwqѶ ݭhXތs4܍:)ԠO!H$2ߧx#v(Y!7]sXQ7Ev$Y l/b ]*zz%sSmrXsS؎ޘ'q! v)]n!u^h^P}oE)m{mB!!aӾ%N".!BJLbɲaKgʩT" c8k~|70$(@fP#(yP@;߷鉦METlp6]Sϗg_=<գ.R(X\op ck`+%7[+ëٛf٪j[0YInhR 0 ky~yj.÷עJlftϖ4I5JG-yCcv -zLӏ_~.TfmŰqvo)itB"P:-܃snm-4zϲ˯@>wӝd%̦9W@7qo Ӫ NAal*yrI \Ѷ޷ U-y =oEcQ0c 7E u5:b4Vi,'h[ܘ6 GIPβf!}A@vHi)ѣP}Y-o7c+M7 Q9+#.& :t&j6Pa_]1ۯ@WzR2nW}j&UN*Qod-#v>^}9;CH,RךnMbΡivqapXUVAp3ޑ [/TR0| Ԩ-t:mq'9OQPq$0OKF*j!t1Ӷ/84I RJ'x_r.mW][Qƿ܃:tT q w.lSCO  /H\(3Lͼ+eڟ_r5<ΐ(B5=o뾖#k^ MC /k9sD mz_SAK,lSMgUo|3K⧕玝[梡9ܵTx>2Ehfy$:KiG," _wW1 [nJC 5isbmQiXpn!zx#cP胷׈SyT0nYe'` TX})l {(Jl z`S n7,Xz _?]ShJma|BdЍi@++o_|B!Ű"md7Ĩ\ۧLJCnJE݈Qb5{bPJM>qv@y{rFp~Jmb2lNYç݈SFm6T%o@Rb/ՉD(K=q '% :Dp"7 T?$?}N%$"o\*7[v: vA}Vӈ =3xx{"N 4S4(. ,Jm{ؕZ^^vcݟgU~o?'`F!zo zd`B0!F#`B0!OPEŰ?2|m6S GZU$Kr c)v]gWu\p]k^x]B&Ex'Ytnlv75>[>2/V5W:.r)z!۫+ZIP[eEizh?u4>N# Bf6\$㦻Y0ABR3OQ unk%DalhsMpAq<K&Y3%dQ71 @Ei8>fMHl)c}m>%:1ǩhBDo m]^mJ 0t}*r闙RF&@mKw'6ƍ8nPM3Tml&zA_c4guZ qeYuT nטk׻1OklBƴUЃ巾ˀZkF Q~{p;u8-{XX"mnI Pet4eꬎ6 N[[R#]v&,=O]EFdQX74_-}l+P8wܰ\Nsʃ$W++ \ 5Ol=Z3u揆o}MTFMy-c{DÁ?53nJby0 # ( o؍GK‚^-\ g4uPesefms| @RH0+YMJ_[[)zY8o4JxiPm8e8?h$mc.mGӖOƶ XT Q;XI MFn?OmϫĂ6]Ay4y]|F4LEHQXorU[ui֧RuYZũI 1-, YJn [0;E]U t:xQm6AuQ]ni%`fR/uPRt\Xa:u{ W-m1ϴ~\f AI h$F1C[[jXcS2f^^cZzU9${Y: >ñf:tV S!ۦ`Gp}CPNTEaaOI<vF~kܐv `n6ֺ5Qh~c/{)eѫQ;f^&(|_F2 :{ O\٨ j!10Xv@LG݁$ gV!eEE 8-I=j LdTk3):N3 : y^|;FWg$Gv5)]ME°R͘!CJ*A ܌LXźaD#}X8t^p(0Od$^GrE$ GSq{Lڴ qgnKtۛ)`͒`ij 1#h*[bKsݞB I̞>bIlcMge;$*ݍb1p? 7ekaÂەeMMHR1 JwRt]V '~0&˩]#X>|RUU{}~EpQƶAG@cۯdL-)tuE79idr#}lak# C*4뒪P"z/ 3_n43.6[/]5M+8|UsGy\ ɡ pu\VbvlT' OGPY+YM;)K ,Ik`)LM|U`]DW:5:IKiBv=ǾpqF' >KOu-_4uѡXV;'oOOw.$@P)7{`|t>[,GT)uPZ۠7_;t6R|=406~† z+V^*qM4uSCAGL4Gф+pn\M{׏_&В7*:ȫU4ӷ3^M oyq2 *8)XNA$E<B7* &+aaNU0 :ܣr gWTU$Q+ ؋{QA-]5k#|7MĻFɖ>YK:sέ=~}uJsA4fy+>+SMKZ˾VvOV-QNI]K |uLL"BI#JP\:0(t4"~i G4ޡy'3*w,$'QIQ22 pB&>~υc_wMȎ-Oz _(h(h` #P$} SէLTݨ{xbZ8J$JYI2^Xumr@Ԍ-Z#?+q}󊺪)D+,T Gf$k3\4ӂCֆmHXfYgԮ]^򉕪 Hd:j=GS6+ds̼YT=7S?_ (kjs*iCFSFϺ`ER`1 ęq媾hp &gSf?8EKj+|4LA t6SbО~1A7^%*4y`VRC"/mƎ>ٛ:_ h( )XBMZClJ?5 Rs0( /~v FA&).ZjKb FFkb A>BKtY~lB6XX m(&BlB\TAl~D T\ TJ[7S mBCmﵰrJ䩸9R۹\[!Nn4ۮ&X,[},X}jlo3}XBQlB1TN!m)${W)mT {aPli/P9Eb5FX#{,E=v E1D_t\j?#b_,.bW #ݴcgyc+w[ŏo.Q^ǃ^F#`B0!F-51F/R13Ē=,FoNĹu7 Gyle<\v7/O.\FdnJ\z}RMe&_U7nokJZ7O(EӊC]30Ψ2uw,+ IUMoU,ۧM;RAP'jZ@s帵i|=cn*A1⪼I妨W0ȟ0e,v n4pcC<\<ܾ>3."a f ݢ,Hm\}ZJx3Hp8^|p qVE&_@))2: c;o\ۛ9 8p*D[rdAjJgeRR;v8`\hs=ʆ;22.aNZw#}`uTK^ zR\|7c0 =O`EYXcj 3:3:z߶:cz( EL*3# H(k)au4Ax`"R2;s)B%ŋz][@bvSXWK $9opbR0So/}& eߗmsEL 4 a4 iw˜֪;N>.LkFZť\a/-NeKEj *fbjKEҨ /~\a4hY[ I7h]TkeW2HcI͍ŁRl]cl{iu<.$xZ:)G;_pIK,r FBV}1:^EvSdžsxt>-"KīXه^_z U58F`kFBQfs+)A@[aK,v<dLyp] <;2<¯*h*@o|Rj4j724 xNRt_n6"95-PexxTttN='Rk~!ak4 ui[Hd| ŚҬuqcT-,ɃbymQ]6ZkdD?yu=N.=޴Fi@x%]HMտ3r꒎嗯@t +s7k}MMǗ,ZIGU{>eGLGtb!7Hkcu7($4^ sRXWGw&O0yj#9D(hȲ-[OOφ IuQtYN?*:J*H Gyư"$߸m'd@l/2q ͇5*,Rl:%%-s*ԧU҆`m>clysK *IIpY_Uu%ٳFs@B)nǸ';Jo_{E74?EWD..b,: _M)N,2첪U9YJ4b`<޸*%keEip.i[5GxV4,$R1oo#8s;e~;\=cKKREe7 :PvfRmVx],.>✦5$QFGk:T,{]KͷT 5*c{k6*,eXSd1T}@a_<537hGUxQ[͗֠]GMAi@)R`J|UY|J'jUdM;{Ŕٺ?Ec˦p˔OCTҪPO [ =cY:1h6(2܁ȧI֩X10REN*2Ko.mb NkiqS(mS90@=@na/ (!K̎0G؊Dha .'6L.KUK<6VWH 6l,E\b-ÚФss7Ymq靗lsbeK@$CTHK mͶbI1kD]=ZzvwBd5l:QVs4VҪvRTQ1% !7ҦH lo2ථvc.h=|=hxk2~+䄳ZC5Ǧv+ IGZ~[䙿ÍLtZtSt^8q5XIT̻@`{R2 ^:7(ǃH' Mrʛ0 +e􏩿mm Ix|6)DANͬ4I{_R:cxm>㌎0̩ijR8 -_(ܖ2]CLⵘH59x¼넪%%'rTTǪ̻DGf}~%Qm:m ?f5c\heHUJjƵ1܅!NoajMtGqz =W :U1eH#no6}-dbO/?Ecp270ʮ fNlW,D4 -A&w p{.IAR2ςG =Uځm؟-1]W7T:dpת«)??t2\˄(dѤ*0.GҦK簈vJ:2GUHs2^\)A:H?K}XZj?WqX0`jZLҳ-jMPT ҕ",\X}Fl WTWM5bβr[&\:,pO^ﲻ28urU\NXӅrڊ~e2}}}W= 8C7x]t35e/5?Ay3 d+ә!ۦ,ezFo^ lpis?5_a%&@BR,N!HZׁ ,OlB&F'UN [j`JKoC KjBO +\`9=f3)v5P{b oq {vBPO48-.{b|Ϧ%@Ymk!uh]Ek*,7 R/7;5F},q! 6Qzw8|6AYn~B'EaJ $o4X1;ymPnK (2zF]WA_+7<=lm^0g2I ǨuL,ܒatn[9kB 4*+&{X=3Ԓn 9B}ŕSejj#a^X +>"IkqKM0`KdeYdMS]Ns9`$؛jÍCs-Rf,KĎϩr:,<'CقU[/h4,EfGklr\2~={)l*'3z9:$Jjqv1 I'VA[3?iy hS#Q=XH!?2xxdMŹ&wI9>g 9%;v=OC㚮u:&t ĚTfA%2 8^ ҢϽOA{{ljKrey)*㞶䘘,`lm{wn!8ayo jjaNxCh><oov\e `p?gX+&`K%~N+pV0D{&_PhiЪL$ibƆaf؏9T`-[=GV1 UǾK{[˭p*Oi1̄ MS=1Y-̨51quY$]}벂0+Zp߄YoCT\mi`,&|ziIJi;!$ wns{5l3=$!6ԩ^蝯멽qČU!ܝZϝfjG8]+ :, Gd0?KI#ZpHFzjT2M5'آUp[ ql(긅(2:|5 F*H/m;iS{,7/:v;*Tbf.ۥŭo;=~"_D//z6ƻiN\LL=C\n~`Ȩ[al5xd"ڈ=1&8m1!gO52I;H߄Pw}f&k\[3 7 Ik m4jK,n^5-qr8+zkj"Fg&BP-f(;6m{kQHvsɳ*"I)gh t\t6?0$5T+΀4z}xU` r8f8)L)W7rw*_k lnc' @SNDj_A֞Bj QI޸xkpÜ'Sf&T)XԀ83%oe!qgd),r,-f*/v %ӣT{{"n7-)sҁI&Vt;îA՗@N^k^aWffjyf]EorIAhdC[` ,h*:h^~#q'mUt`%'cR9_1 *xd}÷&rg\sH\Gx(xBL5%̈́KAQ&2$lԌA % TDO\R:dl6 XYFI;mϙ?EZ)1P,Q[pd8yi~&X W949oTUifN*n唲k:}jՀ><XaTˊx8"ر$C{wbN?E[ZO .3ׅR-^Q5e Ә5IPAP [lF5bL=uIנ \),8Q g`-э#'%^Y)2bX0e$엶.| 56XnU<Ηr/MLճƲbڃ_nƆvAϪAx`qu쓀3xuٜOW kv-.o`5&~\փj:@}c3vC>UG21YѨ,̊HD@;ؐz)0pTpӭ캫S+M:`in!*3jy)ij o[$E|_Xkiw;auG_t&N\qyx2z+%1,(ܓΌ -sq~Abk4m9껥TwyΎ<7(qu]@%[B[z'Ԡ@u,|<9h\Os'88wS;JAME%ELQ"XKo am$LUk66 ἃ(h|z*&t},qӷ}g:%kg!w Ψx~TQB(ihc4݁ l}0&v1"']cB zeuҥ}MLŸZ&f[p)`{/Xf')M+2H=:+3o6 劚ZdcN#]wg"CK l5˳9Xy C^G m5/_!N$w8ҝԝ(}J"Qz_Z~P0)#[G| }'u0l~ 4A'`1(Y7koBm6uX_lA;$b7͈+뎔_Kc1(qFHRx Jm h)v8J>`FJ-s|B%:bQ]*Z#pol})|DBo0"Nf%B!N:̀nRK:[{ġ(^1IET-5GQQAOIZsm=^hXchb{[& `צF#`B0!F# Mgoс 烜if' j'eI4@ݒXqq+*`Idר^gZgQLϟHѼ)@֑_PAm^nxi#H#<^rAC[V^Ji5l(T ][~6"?[*<بjh)Zu`[{=4nd0\s*()yR&`i4ܕ`M foTRiO{BҦqB,b(\fJ{37ӧ/uC_upܼ3]7>T+.|ukɾznn"Tbxf~jUʕ( bmJͩok[\mf|U8K)ښ(). 'JͿC-9y&y F$S:=:_lh6z*4IL4~JS3= q +Xy B) .G&$-,5&xXk~eT0Jj7InpLddo]z*s8 -41<4 "^ ),KONQ:.ӯǛ%Fhkiّm@I#p{_ To|2zY.̅C8&D ] w 8[+ Q~[GMneQ{s  :i(€ʄT5$9 K\@[u(MEX:o,7H*n.p 0tӢkP3(lKSڶp wبժ$+#'΢]-{^1`۟WVNu) qz.ˮE`a⺒֜HsxPә[r8餉vmrYPXj'GשRD~"N`tWBg1_U+^JƦ(s(oWnPXaa}ͻc睥4Pa4IHv[xjX~ΨAp㦒meGٷE/1c>ar$j@Tkwh0vDKOaT~QeNdUAy(kc!H%H ?lI˭]z@i?l2m~{tQk/ 331_m{~`mu'Tv|ոcQiے(6$dh!@lױNe_f6i)}2jec$35tA=uԩqclFlC{aΚX9*s^dK<I[\[á8FjV b7UXgS$E!a`B{c>2bqa %L)DQzc-r<Z;U#\)+Z`*e֚U8?"Ee}8#hR*ىi,o%su\Ž>3Uk}}qkl. 8GzpR%!M$cfd@@'I[ov Sm#Rj|%Ujk[uybM,3]2Bl׷F eG_rc],%? JTf֠+7r zbG9W|9 ,)Ht+r-~}6Xg5JEf üMÐe-fI9lk轘u\'f%nIfaV3l)&)Fb.2}qs4_x?洚;sXꠖIp` {F˗ T "aꅥ?{6 }{) /[+b)&Vq+\ݔ=jK|DiB|ڨ3$ja(me$YCon6YhV ひ*Xrf ;C%j5LkǙI9'mtﲪ&s?+Ս %aHdSkG,.=Ew&}5"T=$:jq̶U3@و߯M_Q"ީ-9fm6d#\&*G76 lheKEeEU=pI)v4Xm j}k|]ډk1QYnԿ"a|< NIQZY9IPW هKїon.%#U{.6в8&y YRE+ BH]U{w늜4V pw˦ KK/װ~mԋ^ص*4>֘0n*M湎|ACKG["kl meU3MpӯЫ"~UYҬ-DV4&ıA>u,loQ Be|U6G{lwlFآvb[zPQ:C299Nm;SXfZEOR8Q`ty.sp `3KJ8vq[-ӧr3Lf]JeDx2CH*5)u º@ŏMCe1{.u) ,WVf&UQ tQXPuyM~amW^\Ǘ5EGADg|9%e^U@T#tXnE™15^ EIQOUG͗:82O#UCe©PE\nkj23 [n63h$q I5T#HWŋ!,lZo50~V]OCmV\ $zga '<E`Yk n@?qWC-ٞI}49X+(8 Br7`xeSּ4 \5bzͤXaTD\ă]{o zd58󟧯pӂ6f/Sp-d(Q: ͇mRÊUPj7'(g]h:L2QԢ&.C 춹ȸW^X$sRX3ci$<6գRBAUB0V,xUոsǾ$sLËj* jdbemdVl1#02QqVP CF1E*\QAdI ;Xmco106[nIX[o OZnpvEM<5E&WcCԶaR!Rl#୺Y2zu)̿1cp3S sc Ųk[#]%!B-1@9 onmG`BABSzfmLC7(;ۮ:(#nnǥĩ ' K{ jX׾&Tk1+K'bT,ceC#D{\B7X7SdvQPnmPyv)X87 vJ&|P#T=*R'|@- )H-q*BĂX.%Jͅck#cSH}Qd71 JC:(àlAm@m|B5C\6!M؄L 1*E!7'Ӷe YE○!O5mK&ʃAOIZsm=^hXchb{[& `צF#`B0!F# Mgoс 䏃Px9%%) #c[n/kߩ?f=䑧y_ ȥ~ "#勫1nO݁U{95_x ti z늘͸'n\!CgU$ -v m|dR^1t8jcVh??(\fzjjԓ:eV?ck'~0;3_%%2VEA E,biܛߢ}:il9NI'6OUJ)])Dr%yN$ve њH1Hqt-9lff0Ӡ"*u:Aىhm~+O4N'51M MT0Ed7U:ى_V؃)5dG I#GTʥ PGPv..*''KD u ξkvGeNIjuŹ FO]L#RLy ŕr:^ ߒ֜'yN' F*̭ \^}Dn!4`T:_eeZx汰kZ=~챖="YԄ^k@PP)t,@übw : ]8 <-0ZBu |ث-<߆[!0ͳg@`QZ0sqΌйhGYlU1MH^ K]m|+5C:d5;ǒ3A%nqO( C)Rɒ *t۰kE":-0omzy8}ŗ;_O97ECVAO[9H*n,oeWAƛ@iXdw]O$K3^<>\ri-FgR&M*YMApI 1$r"5)j7+R$fcigk<1+y"JnXܭaI_*ŵ+4î a䄓b#m?뎩Q˓',+ 4N+ukob:X;I/־TqhO@i/>*XX,a}$l:󕡆D̪*D?1؋pacPewyziM=oa˜xkg993#5jv[(mKM$ƕ #` =uR*>`uMe;+-gO|JJ^Olwsa5`t}sԁ4365M6ou%>*= j !깺Z6᧘j,} sNhg'Cţdr|5\VB/v84kwfF>ij5;7Rq7$leG'gi`$E^sw\gTO}H;'\U$G\PqPf2L#%,pQbCfGHgwx®sҭ fALY;;b;8VhskA1:Y"7>+9kf*""npsRp48RʁI  eiX$RPdP%Z6-@},SmGrl=Y3]pR XcR@c`m܋7 sݸH6gwc"řAY|4t`w#/uYnmp=4 U\;xrU(+)Y4uXm >`NP4)8Km:/Tg>'eEG5ˠJiRۭ~۸ 4qo0XӒ_UǸ-&Zj$)ERr#{m;ݪühmx(Եw{a's'#ukSe/ø/=\YHc =5iզU1Iqkk"U.pYf NL:Tͭ;ioދcsAՄ謜n!ԄQpXfKLDGl#gk)'ԋnq3)"%Pd9}viYOQ!GX!h ʉbؑ_HS.qkEeN$uĔgPYvW-VatZ]$%&keTRqZ/m&٢ 낎sӁ*<|94YskZbq& ۮ>WC]՗9]|;M4G0!&JWDAu-fCڔBsd6V0{ sL1O)h3jqP0 +v-~@2$Z.H}:)$U IE$S+nbͽaVL $If$ üCÇf2j'ʌf)4[AuU -߬Vaq:^8/J[CWNkm9x~50I"h(dI'V iq,oNWOK%75 >/\>\'6Z0,U~\ı!6ؑ`ǡ^FtllwlajSnlxqqLL3\(SOeXZ{mغqtt+ UU&y_REQTY/ 9GGa`èlmq kV E򽹮)M36ܗ#ᨤ5FP]uu &ΰ[Z塢/$|+'7JZʫ$G4SLZLd'-P+/U]mo#q]GQ&՞&䒓W-1C J=qn8w\j̖)Drc7:KmKuxl9xGh撒dF lE< GŁkMG}uASo=J|=nOOh( I!œrYWCykT-TCXT]9_ Y̕UF ߷-i8mG5bcY.[Åט|Y𦛂V4UM\uRmGd#a}v}qWji68:ǮU<9⟍mrL(yhԍdՇk(UiC[S_@8#PQYMJ(A"c1s3W_N}TGZ| ).,i)ڛ(k`"'0DR:(':nVZHW{_L<3঳YS4LiEy n,_6R4I/X]V|E$hhXnGkn5 vX˯!i .>t,1 -mMA壣9!Pr@ Lj:fg`9|/!V%A߇ϊGƙm U %>];4M6ZNq0_S쇽-z/iک0t',|B?}=q;(n1؁bPo\JokbPx%_-U V[|J6Jn(bBRFUM#(1 BF6݉7@ȿہmq6XZAE?ٴ  ?1{-cL[(Q/MLF#`B0!F#Iߣʯ~xk=E',I`e-&0z`C!^v>RW:uS>2OOH%Jt [kNb2D^iaΓx+ g<3gPx+Pʉz8V\ۏ5uPSpsF+J=VNST.bkKQQ`v KKVA%oF618s9n5EyʍVu Z8l)r↾#,bTFm/{LfaM#5T%tEvuY&k 0O{- 5'A1ϑb U%yΙgA,z)`7#`.} IU>T9cMft0TKTK#uSF%r>YS>(cm1 O#:[Rgeb^Y*)*M[+j7rH oUQ>4Hӗʿ- --qOG'6%愉U wOq0n'?-8hLшV \+'#Z z,sJZXȍ8#I:G`& z5T~G:?T"(ikOA6?1ngkkǏ$KJA]a/9S17׾䎸,@mrjyd8PT'nM =l:{ /流CY5g<'*鸵)CEQN`QcI'L}5W{Vt0:UX =!.jQQ-޸\č_kS'@/E1B T"HCA;<Pꅑ_C'p;̓u:,H냚'f F[t؁kuĔcpx#śSXl=DQAe0n&X1l0he=`S:|H,H@[baN}7B;P_$)E~BB6P~uwzbJ)HrFH/|@-VE,)#bPYg6~U8^^vcݟgU~o?'`F!zo zd`B0!F#`B0!ON/[N UEPe5v k* u H!]ϗUb5]3;ST QЕnZW`EJ=IbָXdx;F+8S)J%UR&v/np5AJ=pSKܸ!JXѐJuQXXtJT~EZ+|!FgW^"eiYS}6~hv66eChBѝ"CWJXiVTlYoed m*Zq_${Oڷ#,l,M;Kѭm__߮΂IkGhAd'ʥj(ri:~qU)a m 0 kMc!mn1ˋqxeHjfERrX,>auYբ\u%JeI1Բ+ѣ uInæ,j٘ CouC+0E.ORYj\cM؂Xu܌*ƒm13 fpK"UD҆ #l\`1 fzʇR,*<:fX:>[/sr;F5VFRg%38@ n)!l$54":h v;aQ}f (r|=#:"Z*ꥎ:cDl;jWx4qxSlTl/.YAWI=<Шfcv}7![e`fW{c᥆SU( `x甹Svzi IB]za`$FSM.0?Kߏ1ѬU &Ǯ.qu¼Gf2˔C_^iL`:'HVkmac)үXN%HN3 mse37\΋6A2f*{ta?xiӘ6k\eYJjg1`@Onah"󯭶ZĀm %w~;w5\p9%U%KUr``ۈcÿԬd:+t>.-QjX loss{g?3缀cas;rUQ*u0I=-d$7Su.th}B Ng=EFjX鯂j*=3!z[GuZŭ釪PljlvׇEE UL'fpa|YCGOAQ1-**kը-u:LqCan(~:R26F?Ci3TkBSɅ@T^)6Pچ@g;@jU 898U)৖R(D ̜j`Ќq\ժkqAYXTP2j|8FqFJR07Ŷ}DYF:R9㎽xYWVrM*dmO^)C-$m9R|7B$T L+]p`X"{!.R# (77\pVpsyzo•bת,U Vd,@7:=qlAXRxca$粔Σrʛ9"DDoH"ܒ|CqNx]sV+OSESQTM!KL4] 2[{QWN.{~&Gԅ١Psttίm9u{Seḙ-2Sv.I@zlzaխsTףE\vA&eU2x3,7,YF^d`MqGk ioχY&'~#Tx0Iʸe{<` YD 6qm^e9os.}j?|!/e痙XO1 t1bHrFs`/WQi7=ls_E¥Qß/->YO=Q9uBֶSY#`Rq樇-tFƛ^-×q-}"%*"y30RK7Ƕ]Ku* #uS;V- 0G'^r4|-_WTtVQ52ML7`! d3*eN@4C^O22\檆[s)d=7K`^,q7]#TR?ƴK:i×4Rޑcc毢I0BqY&M<0TSA;èO*2!X9-LH@㖎6,P0jXJ9 oU_?u-34Y>g$ VPM_o42 mfӪ* \x5|j(F,A u qZ W^:bN" i5ΐ[[Mͻ f/ZF:l7 iaa5_޴/KGU%<<3D$E*;A6Za_L yEjXmaPǿ\B淦"7G$0!"8 7Y VwU7_@q Rk`Io0`PegBnGlNd#)X‹o6ua '](%M( ʋJB< "4u2lm MknO݈RlHD|߾!Bö Tmq5vRx} lJEFM|D.2w8 u]#G4J4Tֿ\ND!X%zyMDFi?'i#ݴcgyc+w[ŏo.Q^ǃ^F#`B0!F-51F/'3gr>]N9рX'nV}_%G]L/I ٬M eO)`êYXaפּV'c ~,~8.P1*//O“gP@'-m! a)\SF~++Oz[ *S}ñ.H۔FaSSsyؓ`Xܑ.3OT$@- @`u#]r̬ CΆ ):U[,kA}R9&xY]_f2z&%#OHϧe{ ~KƷW4p\ˌhj!%=cEƛߨ74*wh1LJ^qd:m3I4SZȓH.4}acD4qHմFOO G}r,=˫ېzucj~:&i~RSw?*UDT"ACCN3|m-1"-ߚl.`u5qnFGHSƓZ bCPz~ z>s˙fEu4|F45(yj`$wU*kaŔְ{29lgiiLUP{X7cFs;( -䕦 V3, LM>%Ftgو8EXq>{e-׮%3d٤c+̪3\I>Uаw-p661ؗDSp:8xYݵIQT, -E{lůckm?9y{ p}gOSTDe-u)M 3&kmG.&WH')WGKQU4a*[ьzxmcZme`%D<̃amP}X >V8h> T aϗ\+&koSRGM\:Zԕ>p{d$sG-=~JJ*#Gw'P!ۦH"o목iq49_ K5>f`@R xcX`R 7)+V/ַT孒RINgng꽍;mۡbhZ\ RU28 L!Mzu_sk={2Zaed憀KKK3Nb2TIi;`l6\E`i^xe}Ur/)V =IfV"%=t[D|qzxbMye3X#5ck |Pz+i|=MŲ#My F]Ty\ӴRzlFKa{߽pɛ̃9|chEP32efT١xI2D%ڙP^}qZ>i^oâOdyMpdx"yrkGi峇&1(Yfs뀼>6 JzF N kY.GT+;(kc٘S%3h$k+WY ΐ~pT{8Z9se}pҷ0ŭmx SVvV}H2j-&|pYvqSG5Dphf`-L-rak_s5abԤŹkQ\ SD|:iR5,nnWĹ {qEڪ5yޚ$dI C0'ԩS!q%i4SNZ/꬜Yg4Ykᙙ֩Yr[ a&W@mF{>seS5]}D8)榐4&lIb@9RN ny*MVq œpO pfKU,r*V!WP]kw]팊y9[+F:̃oO8^uͳ *4* dTLʍ߯\{F)-sjz+\顽>%hHBXXz=a颥û{ 4y^2:$Fv#9NS}|T˫)Jrra)B3^Trb8n7ڄR!+A5eAS:)*zz$aUԒױMFȟ> +w N>yfQGY.]SZ͂FgӨ-aNYyt2m4}X^˞Kgek/[Xe * S&Z̪HiaO%@x* I&KXpPM 5NKɪ@J5TXǜZqV!xa>7EqZ4KE4ebn\ 57n7:1kVeqS) ;滭LPfsMyQcFR:&sav["gӸ/qCZMÒ\*pg!CM,-!u:=LZ1>` 5c;X*qv9dCEqO_7S"O"CV/.<œ*(*\Dlp:bMAU7-TVȤ+JzgCO*C+b#l@Mvaa+'VΏ4-̎ySNI鰽EA,xfs1!*2hi^.Wuza-hvP |+[2#w39ftY$lC<3*F\l#~okb&~[ʮjS׉'م$笴0̑Z(G-uDKv꠴sN^˴D"͠iQ|ƮiRk|z Lȏ Sk8\%Ĺl~|:ܧ+)hrJOR2F ?qa*[e|5ENSF4(*ҶK0bۨ>ٜ>]{i31[1M HKHI]T4=/juwFg;x /ec+3if\k#ă6YU:mGJwm߭UkO#RQ򃇊g-BBnok׾>IsAXtd+POO˨IɁj%lG4]J-cPF6;7D.?<;e9F\d<3*k-&M.4閸YPxS_d9U~#hjU/_Z"/.iZ*5ĵPpp.MSY?i kh"IC"`lwlm .T:xu"kFnV|w e'6a%2Q|\\߽ۏMM0fO Po)MJTBFR@~tf(;$Ao^逡.I'#tlQ|M%[!R *%f=zM#oA?~v[hhھhԳ$$M33b/J =:->rLXW% .:<FI0le{)<bGb1*,0+qq=!+Xܓ}(JolG$!l6B]]AaoπSp+aTh-F"Q-ccPV Dd6@HU6_AH1;!d߈R*n/P??ٴ J9`Bsc?4?nKx1=0?q xk#`B0!F#R7{)< MS1Vka[ navyK~6V̢.ˡjj5hXRZA#s`>YMN;ȍ|' h б3jJ΅ew.A߹Ԓ;g߯-Y_>VLׄKrުyx>l, 7l:S-yjz}w\9dsTGWZze9rvMolrsp^جts̋v#K%>S4k0-6_mM\%g\f ,~GEYMb*H8}xeŵcc괺m)CY4)BKO3HPdru]_qcE&Ҥ{GUSc&sJDEX$JI(@؋a L݇iFx< ᪦U͚*4.luXTڜO.3 xܦ.0ZJɑPӪ<7m V9hɦr4{ yh!wm}? US4zZ"`WH*X܃5|4&,]phڔR5mV &{=g62*{IU24Y.v@=zuF]9i>\6S#fnˍ  so{ZX*p4f#/4s_*Wh(+ nOF*;v]7Y#@ohʆ<8 *jUW䗕7Zֶ218xH5 ($w,20ZVY$(it뺛_Q6{6[*S1h4vTc^.!=$艚kd7q8bYpxXwJCY߆8|JqJU 5-H0NBI"l/ ]TAB$!Z|STqyKF8bHgZHnEIX)S,]o fQ6M 1&QPLrK'ͪ@Xb2.O ;_UYMT*oh'x^->,uu1)*2W 2Zb='.iմ("nkj0ƻq] $d8>8ɹRXzrX Qu=M0neY' .  N2>DJ!-NAfXHk{ zwcr},H^[YA^ii6+1|2Y"Weztـ ڍ%\#Yr#ri(;SFC\^we K^<Ϥ>qUΨ4@q^{,|Cmt=FK, n'+!ZYFcYJ\<9(ZƧ C+GwA늘RN8lͬUOU TQA .*q &\}@ }o^S9,wQÜ2Gr%pF2PEk~\6CH:r] 2*k2f|JYe5aor-&XRysL!\\3HREK6}pRMs\A综 E \$ᲈ''i#M϶;{fjo"yCC*?n RG%BoJ mr >eC$=4Zf RGyT6N K"5(ż5$ݭt^$ 11J3AqqǘqiZ7iqGt˘oϫۃe@뚊A0HbhSIrڲeP]Mwe' mo7檭X8: U[N%=1KUbnHKfƣsםDzϧU9<寺|a_9kk#S,gQ|W4.oo[RoOK:mePI:N4qmWȓTŏ"Ol2l~i]S9|Ҹ?{IcQb)$ 'dY_YurQcBUn,I@s-[SLͿ~/g5CùM u&['&.GɉAmcpls.5uZqkZ4~kuԹ,!VY)@,˹[-|4:\s69ϤȲ崮fv+7EuS.aHH$`*ՇUIMU5fWR$jV ۨMWhaYO4Ӿ$v,GQ[TJcNX/k !K܌U%4YyT)s<|H9&rZh S%M.BIrr ' caW+dhbW.L,*umz蚘aɘq]Mg~\{}GZ18\๚5 |$0ͳc)hҪ9u>ƀ6vyRL@#y@׍~]fyG KGGR+740 wROlqS^ u659כ_e\lR1Y_ęUTLpVC+ lSTlWjn;֮i$)IHTadwCX ^wFm{nc򅤪V?f `Ude; 4<(f''_E춰nm"R[`)JA -|Bű(JBAwĝkRI>(F$b%L,| |Awc:)J $`BN#PӸPL-vRJ9$$t삔 )v)6St${bT%k؁uމ/m0.|P/ RVv ;mĠ q nsqoؑ()[[Zî%)~U\_R.o|*o!A*buB$oP}p!Sd* Dkb [H=vꄑخ *$4F@OI%VB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`Bᠧ_µrؒy.EA_q0mJ ikX[E"˫,AY-Dok\Զ#uFR|S;-esCII$m0ǨUn wv~.)kE `ʹ7 aqHĕFUyEn(mk0;>7>UciQz%=L,I=ӨzX+OK]aZHq3/,yu]BO(ɳ.3>nT%N>[XnI? %)XzRq Qpt.keϘ]K6Ic$E&4Rq IJwvW:z1(r45"$Yy(*!~gtmn?U|R3 9ꬤ氂=eD(ϳ*^9(%\܂H; cRA-SSR f7S:Y짛LY@D5(PD`n$ S19Dt4SV|&rKeX}׷^F*b㒤W%\AO^tx&3H u1U;'b<3t79 8*[cknfd-IUUuoN!&~a&"7]8\=W t|_0,ycKWVI"7^mz 6QtPUTF#f`51U_.}q4)U2&%I"Hv+"ܝ䛓Ӹ_0cqZ#T򡒔9Սͷ[{[uIp#܎Z!)V:X*Qi[ͩHV䞀{(Ԙ,ZL(G)s[o.fp s9q\omBl{cSN;CUӚn<}swP(᭣4g,TIi|gV!\Ñ]o4(~%GcEÊFc~1m~"eO2=ʳW.dX zkq=#[D4=hsQT2|9=៉yDOY`ZkmNz;0loL+ӨnjE-sq|N<,'QW2@յPRbIySkؖ,Q=ةTo tTѬƸ+&Y)\J)jRUƀޒDr -uRծ7uE@UspIi6*1W)cXΔDoRrUo܃.{[d&W[^UoŘ\&uus@UpF6=7E<3?H?.*|Ijk hBT.?HY$0=Tb}wOƇKk'ufyC`hƳ* n ؑpú ?he7f.xD-&eI_;+ifCL儡l Tַ8ReIP\hY؊L>uZ"ʦYL)Rɻ0bd["ۋkcGjA_MyT$|χ E ^Z5iwx_{.XIVu6]O[s `G鵭>>jQNi6D炷>;?K{\^w~H-NN~pM9V`@[ L#FJg߯DJtmeeMT+ ʱ]-t0Zܮw kO :E ~kBfldHF$: Nd3cR3Np{eԙ ȩY 9cxac[7xyw?){Zt:U<2BSH7RN(I-=Bu 7R<@]Sa_W5e[FU aF ͈ⳄFJr 񟚖c*թ5D8GVizzjD#ZPA7[I䭦Z`Lξ9fc7ߪ-[”X \#8L*6j>nVVH$Vϥߊ# !V 5uƧۆ3,3:I#c MnX Gat3Hf_)Ǟvw+\^eKfxՕQ2~T Y㘄nc&_+X}=]8`hȟes8+J*|/ϨYc:w:ZT s5 1$D*Sm%ˌWZ(xΣ. l Q 0{ikv5k=/%<^T4DbzY:1 E*G ]pͫkPuuwݳRz$6^t/qdzx@iٙ#׹USdzZjS(xRN ${_b4O$DY*BrB1=w4I+3.>dkkNdwhn:}Z >`,%2FRlm;,^z7U贼7|{QiF(єfeC-bcphR8>*{JLx6$[**A-pNt1ߟ{sLz ⩨u =4TyQ g<6}O)-vW':+畩jJl)V2YVM \W9% 8 iE)t8u?>Kqo\CY2O%+Ąʖ'ؐN6^ . t.YĜ?[^Y^w)(nn;cAPAm=a./ JnlfdMHz[  r7^00h)rJ^*ɫ :uX z'C\4?Z8livۅЩrj Y*rg IL@ Ec0Ts4sPu3y}EIM̕I4t:pnIۡ]uZ@R. vk*Wxk&a9R)Xka﵆@Wi? 8<Os82LNӴ`AFt؃"aOg4K1`ezDW򬚓/gjdyLߌ ;ǚNFDO?|GG|~Q o Po9BL)$3\[Av܌;$<~^eͪ[S/e!$_ &fP')v>Px*ᖷ#.X<*47U]*YFŇ[ج 37UkA* jeZ_xF."ႲZC'-+akt}LO3Y_?5:>ϳJZ\1x!2pv}1˚5)YETRK<,dE*e ;e;,.o| MIJ7FS'u>ġe^$moD6}9V&PoMJ =-lE I\X1:@Pza M-D,Tjcn؝'e*V=EbT,mJxRBS(Xr|Nd:B_m۾#um%[J> ԝ=1:w_B; XX_e%r>i&TrJkz#(r7z4DY):!p#TscBZ EQ4%rW,fO0.F//g?h13?CV?t͏C0]lG720!F#`B0!Zk?'c~_!|`~zD`4v7>ʼnVbidƦx %fWz<%h2F1Noߠ܌l2C$+1`Bˍ%rOtm:~ j#R W׾84Dw\gŽ)ϸ45u86}EImc8gT3P!N3N? WxјEg#T4"#WS ( S+!sXtx4n*8~*h`Y^Ib?BM-u:&6UK`|3f15JH@媣.m}qχ.Ӊ1ύ+(_E|5Si^9eH.='nۍE̪Xv6l<+Jv*G)\ƖQOEH.tFa6է=:l5Totڌ5+̪)V`Cp$:ksjO%/NkyZ79HughD%Hu,z..bO <:is% UUpҊIll8e*:7t憦je!1)! 3o׾aJx)89tL S!.hfƱF@ %pT}xbNiE H>hD%̽Y+{^q =`̐C~ڏ pN3/̩Dkw"m7g,A6X帵׳T=/22eWM_4AkedLwƐMHPp;jv,5 $/᪤gR ;~kbژ:M+vl&*iRP  :t҃RmoNC,lAZQL0idZm}L_[V>*ϲɸ%fX2y1F7 Xz]&N~j$6Qu$EȈ0Wp6:l-oc1~?D²4̬P`By{! TSK#]'QRu oqkW,z.o0VMQ7߸f*ASxeBfGjyhzzZyeo{XsMCn|3*˦I1m-x1G0[O)g^6K>[CMU[l*^u!\oܔ >3 YFث)E6pVQv뽷- Ȑ*dmlIF•$D`6m=>uy1 S dkk [iopY}I>eS sIE WITsrE|M"95|ڶ*}6yR5b =>j ޮrDl9;!IqZmҤ2JWX5>Ĭ ` f# Rk1-R`p'gU;4uNpҖ&3{pEp˻.}!,1nO;Fd`#媴a,%~˹T~eP+ F+EFXkhcs7-gpp-UVJeT6dFVVҭkt?n:8AEn2ࡦȅlT$XDR-`7YHߜ*@[y)8xr1-=b#Gi v vk+,ӴYE 5B!$pF YsqqRp˔~FE=deeFDh"+om32waM|AQ͘G軧E}9-4`PdJBY*$%ͬ ƽ6)*+^y5dXisJ8&Ŀ(wmG\Hզ-7⼏ _c5MM\Fݐ5ɴn,/Ntk."NJUFx )Htab6#l*}"DFΉG^ycUY>ck]Yn}zLF%=w]/u `'_/G{y#U9I™_ $4zj*uok{dܩ  6`S)8Xo|@ ' dt7)Ak[E]+ԓYeR'{^ĨJ@}pl_{|BK>v*H6q+!,Xn?F$" :lK E&_3tہFo&V y܌bskQ(\vby(E!O5IN% %-sBF:l6q"JvLB,:OĮJAeOIsm=^hXchb{[& `צF#`B0!F# Mgoс ׃5O"i-T/I$tƵѬ_N+[Ǯ+-\yJ$Ҿ]bG[tc+[n*46VV:ld+3U#[slPm▛]L=6QUgyS\ <044L]jQ~r}feڟs|-i2Cigm$ǩٍJmg;Gp͕F@ 0w*P6Vx8sj:ZZW@MUŏFB:k t0ϓa<9Q0Y]|4FʢDؘɉ))>cNn"w#IA?m/ReNR3!H w7@p29Lc]VT P7<$-#IO\wM.q?UۜwN)^+)c'Mw;“U XK8fW8NpfMEQDg[7:,aoFoQ=jvR r9CHu+^0o0oĝ'TYe5R,QihW@e Vۨ>`)sݮ۬2 92,ֺjRя @ Z}Tj8Pc߄LWd s*i&rue+knw.#*G5L.X-׿|X* sLׄ'MM P"BKtbޱ:/'_m/2ӿ_5'\AJ %>udTj$Rd$ ݈aRo4Rv;22좞Z+,kؑ$u.F 51i4%{v*+dqגQoh~aUL `6! oAԍ~;+stnBLPR]%;/Ϩ~>?9_Ì{.fs֊z|P K&U$s݆Q&Duݔ 9M%Wf5X?7*w4jQMN&wPQG ʏ6ӍK $ZIV8pM嗠 w=wnyuRDsCDhD7N`zǯ{bmUOt+UM\G( lc cۈ}"pq /tʟθUzl.LgG87V=WV XW&I\mUR{NCs*zyKȡ0@.A#G6s'L&_O^hѤ#bI;ٺ o|WspX wE ^5=eTMK:h%*0BX_U[Qc*j]mÇ`dk=kNTlY_)76ƃQ5&~% I )"@)#8jnvmO`>[JĮaH-&a1ƅUuyXpq]LF[tۈ։NA꩹Xn~()RDR2 \at l40K*UU der-q8fPI >WQ]"/.(CKO^uM\NBzj:2:<׬gtX$uYxB.򺜺1g[}"}@SLkH%N0Oy %E>m# ,C_.H6a~[ QmzE*7㭁%QgyXGϥz|=|dkeVB;#4q,r>^\RzC") 9ciZ:JU2 `œr+\kV*5MW )* aG̋QJ$Gu[i`un˚yU=]+2 暦`a;"]AP{+a,j#*{1FT ubBCXc DD\M*w 2Z4muf$`m=0gs$ –LYҌd4iUUB)SHnA"ƛ CCA.?ux_ks93-⼣07↗6Rf0{nčnoJrƀ^JWMCp+Dȼve" vSM Կ'T<7r]P64K?F%D ؂8Q݉k @,bģP_eThzSJ%D Fċp yr\EdG5ދĐ@'ae#Ϳ h"}7Jw'7O[b7G%6\Jmtܝ ab3\wGEzrT! FqRߥň5׉BUJWRzbaSBVMTP[zbn)]1 C}ֶDֱ mJ G5>M۝!rn$ v#MPTYbmE&UV",fO0*ӜF//g?h13?CV?t͏C0]lG720!F#`B0!Zk?'c~_ 453ER(TbDw GӨ E>r*8gtm$QSLi٤/ ,tpP9cl>P~$( HAs@. RH֪3L(4Jga&/lQQpy:UsD|MyQ]dBK/kfr *z:7Y֢&F ,d0RHqN=B^ŭ9$\oB"3$O/HPpncY!A ݷalq5qtis6ű wxb)N잆-HҦ2̂|ߡ] l:ߣYzq݅M5q;>+^i|˾zJxgtGDDmjX =Vm&.__!⅓|4JĠX EGuMO-0S(3K$,v/_k)׷S[갻3z)'aSGKVWG uQKн*mW*'M&TK^\̤[)3=D@E 6\sߨ&kkR-+xf " ki6$5uUN;+DedYFq__$)d_5-m8>sSN+P+y"A`.SJM:'>muJßTvim5ZVgo!,I` a)Z zj7TD7XUPֺsO(ۨL9P9yIQRȒbokm,=1u\@{iFxY1GJՕK8I`i DzAq~I\_9zhnl}8h&9u M4Cqu*fn9mΓꦅmB QE$2iD2K2,ISbíƢ: ^7Y|BM{+b sdڄT'g .10nw*;_64\y_Y? Z651ETE*9N-b㧧W&yIpT(Zpo!Ik\ok eV,'ڤLzWi"i&ӢdBtVob uG@uj0QFO"+ 茷۶4i7'~*Tƃ[)3,Kw{vuA1*7۠tL,cT}FfO]qZx{1( ZGzij 2$rnC,qOw4>~OԦr'h~ljJ9i)C_~`c^W:⶛]kO{Թ2Z)VN`}E٥qmx,lsB Tj)x(2e fA4׺\(J8ZtPYc6r`2'_ROװ_Nͤϊ4&1WN; 4HI"$Bǐ.7rYNiv~tTޙ ԛf,v뱺@sm_Ba3~[2W*)5ou `Gsr6udX4':qʖ9)婨B0JhP-5K_c{onNhǘ$eNlPWWʫ*,Ed: =765ZIi9aO59.U Rf E +/.m`°`=6 ] ᏎuO?4pC%?C$u5*u2Z暑0 eXu`.76!p+Iu}bnq_Nӻxoy9uCʦWUr]:Zm,aq<+ VBM'KҔQUTȳ$2k*bm|zxOQk( P!!} \]uuQX{0;}!"PQ1Rt0b zȫזenFq} 4.qj(&AFQSBS3k 0!'U, [ [| uAo@&J_\BK* VU) ^㠉Ia~+B(Knޝ1&]#y!dBRm`!broq|Jʑ+f$J%d!O5[),G\B)k LL<iGN ):B@eOI%TB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`B/P x (5Y^[M-Nk6ǽ[Mձdd[ #m"va}^諆oنOTAZUV3>[6;ZŸ|u V)$PNS Tt' Ϛ򞢥)9 jDQ0$ Qv8i: 4DL<駕'VI Vۡۇm:.u|Mmˆ9Z *jvE:J%^܀Fcc!o2xT*8^1#udC~G/-,m 5Z"8.? 6.GoUEV~d#8f'~}4iUO0~WIbE =zcd-QvKمtYWY-T݋w n;<.QI#}^ kFaiې oj\G1x;6zzV{]j[ PGcjXCnB|s\ 6#:hjZԠnGc2_:ӧw10v ]2G:\K8=rܫZ8J*R73(=ANpWOƹ&]3K[UYѶm{a{_˜M,6c.v\&vL~ 1+)yƪebXċZX_94%OVf3r(1 nEAF+T n. kXϯ)> q[Aϲ0r7<&QMSI tSP%/s#6v=ɉ~/bB!ZO}z{OԲh e=I!T CP 61Y.lǩl^35[*i8R˃0 S03UiKkK(| mE{KGae žTCeQM=5RUP[HZP> zCio好ufv@gS|CU#!4ÒũA==:nS.lby}TS}M%F]Q2NDFfCf['}/B"t#_yk A,:}LKJRi[Qx6箵RZ SfMI³HEf1@UU $}Fs4:6m&\D]hjGEBYYT(76 >x+a#ZALqbZ{;lq& I]h2-QzPX\cq~u.gQW4G# dއQᥭyei3L :Z|ҾFh*")UC\1.F: iY݈}FEQ54Ieqfx $(.5*Ay 9p{l}} Ú뎵ixdUKIK*D\ orwl=gh;)1'h᧶?5<= ˡLԿCBG5:^]qӹz|h4"N]0VIq?5и2;ê jI9E0%Rzu#3nj&_ko)uibUESNyST6IP"?O(&e7=Te^N{}T5=}% 5s_,܅ANlE?@=\׊sid~ 37ډ&PYN $LCCo4̨eiP%@] 0<G)р-)G-JӴD:z%w5 oYKLug#5EQ]+DR@K%K @В(m}3TG50:d"3ջJf0 gtǒA7<*i㩊銷0Gpo1}1OC,IU8Zs]o 3 4s5BZܽfDQqbڔ>uc9AS[+EI]RK nv [ljS{F j-=5-55fAF $k1KPwP.E'u*46r;aiftz}SEʪjXTФU4Rd,1%C0FWLS4Sy Ǖ'1j*jVˢK?! ׈P]TgE> q DBbuK1zo C᧚ 3ZxwhU2̕VLlїUCa{5cN|}4XG.>H宣!EGZv`l@-[]pGSZT\>u=K‘A]l%HZ#yu#Ϭ3iiW4p hY 3;{ o0;C>ʊeVFlRrGE2.KSMyh{Km+dy4y$m[*)RK7bNp݄eMS$im~wN2|hf._C<0ZL*2H+pe!H XӠ{Djv& T x6")!fu.zMV25S1+t⭙0*8(2) Nಁ~q,c~gt 8?5-SoC- (#=qv Ql-שQщo~Me>_ ptC 8gNԡMn ,pdvG*x6J..:S=m@d [۶%oM)=t+i?'Zsm=^hXchb{[& `צF#`B0!F# Mgoс w4x;fwB=Xa7$@MېFܓag05Wi3'U-Wu x*3گJSTTqFi$Q-CT(ràlH᱘vvm3]ߚ(urѾe' qϣ;=9S % 5XEk@[vٗTUɚ-Q+`sos>((j)O"0*#*+c;.}լ*xu2-39xƿr7 q]A.ϡ-p v6F|yi\%k(i+9Zw<7ܮ}mF7?Lk⬩IAq)+!Ŕuf=;1WLN/ ]2$XxVrSBiJm n ÿ3M9늩6cU]U"GrWQ)%&ֽ쫪68e X6=RWvj%?"440GUR1&~srz%].2IeAp% tfA54mb@= 8a
ʿ l_~u2!P5i-z!}ny&T™ӟW &Aga-=Cԕ(완}\cU䭙VU2Hy.S!!ljU)9F?u¾@Ȳf=A%}KsGk@u7]8̜@Nm|׮s"ӈ^L3,2 Zi-UAhHBJaSeM@ro<#FH@(O =NWPY0RlBI,3c=Y,wtʯĪ2 2T$WR,b]'4`A*eFyQWS$YM8ƗAH @sM9oEǪkCS >VRjs %e7H鿠^ǐFᡊfG5"A6lzliƏyIIqҼRX%p4on¥yWV(֖xL]έ@[_\clMՂk{5gzbMR $I̦ϥ5{-՛Rp7oӣ-`^"\we/p]Wp 3UC77]S*=M~I\F3k8E9χ5k,SƲEWN{`6+M"Lsj i^=f|$2k#`ģ!Ү67 lgFIF󷂦 pvb;T}e;MeS v SRs =pPuJ4wGBcK~X9ӭ2&Nh˩$(-`ZiW3 k k8(ܾ3=\Hgf0b ^TGeU96e $miHR[ jEFN^ Ss Fx%:H@! 3mw]STmwZt}9ly,fz+3  ݰkH /R›j,V3qH Ȋ,ְڔC3<4񝾨1̪\ţ2>#L-h/h9; p6z!OuļGG4FבUjXd)Z:rwp&kUNPvk7a{_S%c9Oc~*QE<%Ҫ;rhr]LAѤ T.AtNGO7daͬo衲J l ژ{Xa|*.GĹ!:*#e/ц J-ko >I-tc=:A-"v#ne3&iP3}UO.Xn{5@ qG G)Zb߽LE64δQQhh^.:gTSANlB,C,ݬ@1-7uO)DžKQ>o4|#eˑ,pE$) q 7#qQ‹Ow7c>X^՛$x> qaYUWYj$G~\JY5ءe& PSLᄢͫ)s:ezFA(2è`,oסMsXQet?/P/M>e[Jꍕ $ ۮ(UO5]LuKt\WkcT+&pq(JHLBc~%1)rlv)bN=1"5BnqS؅+bq(.tX%E2-e,p ,X\۩ȅ%f.x0bfNlJ4؂ =valL(({n}pNB~?\ř ӵBg4jy rn.,{;PtR9U^ \~3̩(\;ZWR-~L EW ?QgϚUWrفx*fq"9.]&iXCTLbtE7!EMq WAfo,, wHw666A[nF!,o1:!)] D^$;B@#]~o'{`A\Gm.Z}x6Kؒ1ēv'%Lb!Rwp!. ـĨ䀶[5u'[tO,Xe o%" \~~ d7Se0(/YͤbUi#ݴcgyc+w[ŏo.Q^ǃ^F#`B0!F-51F/~ fp24rG=,z$Zm=J KXK /lTF-.l XonAO{jy=R{ZYu"x鹩44;Tn) +H9IW*9us+!Nڍ_L3@P8b$Hۂ QEX Rs#PUz\ vc=moqi (PROEtPJbt:Ѓ=u-|kP9s3BeMQRIFb1Dǭz Sf{G'LUk33rQΜF3DEDGA.:iצ4`V!9\3I1``u{ y %'\l&.%&8Viɠ#e@b@'b*ܕຘ,$I:˳-eUYXT@ɸ蛘wMxҤ BkZB[-mhƱAiI3+A`APrR/B4Ixmїm=`{rs<$1ݺHZJM:W ɮvvcm硵Ľԁ?{q#Y q4DlgCgW-uHs)l$R"vb&tgiCˆCM E[$sij$2F`"/eH :ۂS0uV\1++:!̱[qb{^4Tդ׌oqTyR_%%mSA\/MD&ԑ L:9u%)O Q$L†ACdye .-`ױ"ō{CìgkG=}[ݸ|=:WR)B*0V"uحnpov ׊fS7&sr/rE_005s@:y -Y\kRSP$rCkulX:t~2]%I"ʤB ASԛ|.=*Ui#_^\Q?_9>O̳ 2Z4b;}[U/#@U+ zF4Q %8j7kF& : ~pXXTLj:}B\_pgVh mcP}Tnnc]qJM6˭4c]B2PӘ2H3Xm0G5NM#݃e9diHP$r 6 `S4v뭗@i]]Aq-d-XK8`*i؍MϿ]'Y9-=:tM͉&zb*RkK7T% "xPUkio0]<|CMG,?u*]iMY X~A4;yIa _od2 cT9q:% գU5UQI0 Os)[kIjW+Nie'HD6=0)7&i=QYY&xӇ vBUջ(=wgjx F 4_Le2Sխ+j# /mqh .kz"Ae[K3eImI.ly]@cت{ƾr`rzj>oGr#>Z,|M*6{ M*5|a*_*ACfcIVZ 2" ^T/ZZVuZI0>- Nw;W)k~WS}&*nrY+Ӗ, Htc(;tvU$W=jQ&IF(LL撡\$U6\nt mkmb}Ir6%L:4,nI7RIMJh[aqpvO>J&v5둜 m}c\\6 px-#ꫵgH}U4J#i#m`$\,sBZU3+UEO4-< Cq틟9Y!e`ȪxKN}̹vH-eH1bZ4lc`oa07LۅOᯖ xsŠRce FØ PBK89|'!iZ [KfT/Zϣ E~X@ڍ`[bs8W3xJxlӢUvS)f6H/C6׫-,v]"s_et3^{vbJCrHW e\v*LVzjx#\%r{mEJAu^}P9 nsќ筎HcBQo*{^idr_õCM9IKSH7f&M@军6鄛^lt]Yi૑Rp47]b$-nƷi5eɎ*0(1uMxp5 V*Q1J*F^o)knOuǭ%kڊXi$mrHvE b5絾93ߢ(eͬM抙Fn @'y<){EIr'^8ɒNYC1%f.X[Χ@qviM4O"\~L/qNGP ̖3$.Ԣ6{\i h^A&eZje,gJ$ r,.:X_5q,|_}LrjX4}1]4dil{~%-J1p3Z\@ovuQgjV`j*dRD@c߽g7gsL-5Q=p "YjzUZ>sAe6Zc^ btlV=%BǩQc7<-Đ#y\N&yc׵l)y"B; U*#PĘď~b~ubmHp\?QfzX)[ bg}׶:-"cC7U ISPG-|2=0֚IX)"ctč]zDܤB-b;}c5ע JΗ4HRi'r/sߡ' U) -=X98[8ϊ~#X+39ES/ArA=^~ Dؕ[N 7xQdY{W 5LiD,t{\O[ql$9ׇ̧ N:94:i>FpG[K;5᯼JX4$mO4G 5"r#1-݇ck_|<8S1a+3GL2 #:Z!{[NǮ{rCj\V3uTr%B2PT07n7;{NRAo8tB@T.$Y[T +\lNaMkixʇ88=O2*2KOJٖ1K!. |gAwrAUMG:xFtvmԛr>|^28rV>(Ͳ*DAi`WdGɹ`nMnFزL= ˉnZ%9!h. ,N p <ЋMkIlA]Io"HS%Hi}AMR]ώ2/^-KE!#SaSͷPAf/p`ȓ)s%x(NFiԤ{k Pox4>Nߗ :GMMjѬc{axfIcm!X!&)][\-t8g^n{6|S Mj<ﯰa;SJR u؋c'* ܘK;8d9EKKhV1|CNGBsI"Zn)jn W0s6nuhͦu5CS LPQL;+l$kr,,n-n li"|jr_ܪtZjR(@bJ MhnBƞS/,:jf#yéR49|yD6TgX]_cD1q}5D"?cm5y>eSSV,+*`$mvөMK@q'3+jhMSca3LJ^jTcnx+NuYg~?+&LP5dI*0Š./iofǚ<[06I3Dيτ0TDRE9{ w;5*0@3Ziӯ\WW)|:ȀIKj |uΪRIT>KHcL7yL?EgyF,嬍I&9 eá4ga2 d,*Lƣ\>c._4vkmkm՜ ho1.[}W#g^+9|0'!⾛u|%<;~~;MJ0ko{زE˫2A'ES55TBd-nM 6'&S]|uyJBBZʈVX4[16䝾rHJ.||@ΣY&Ymb|q$huZm0\;+;'8/+ Ugŀau; q6s0WƊܶwHH Hܜ U a!ybyQꥁCr)5Ȱ#c+Upb:x=*WQǖ,5K ʖ E\1Ja왵)Nw7^YHہh-$I`5c^- ؐVmiQ m68:)^o *Z"3:<4s:̱3 +^详()ԧ4 yIuϩU|A55!u!Ȫo9ʫb1VQJtebOΩ`;d7tt$n8&,[i鵺4ʅ)wd.EnM^g)KyDk_?j`Uc1 Os튚moPZ6߾.*ՈCmQ+2캎"DRB%nAfko-ߦ8i((؞JPl;C#k{R555XV% z{Q)/ oLlcDlN!#\N%F^>8.,"eZU*Q!BCp׉R-Qa(%FN"nޘ9dE B蓽N%r_~ttF%=G_|ADBN-o|VIp Og6~U'8^^vcݟgU~o?'`F!zo zd`B0!^qIqW|YAe9KQ,e: P+6@f" d g 8;!i*2EiQifդ!zs`B0!iT0!|"ql\WFqőQՑ1HǚJ/NIrHRcjshn+ngQEM%+X/R[`F=#SIhZ&m>BI~k"ʧEQo:D7Y0.t7R]YY^ W K6=c~ ,Hj3*V]Ik@ lzk-H]UͰ輱AVeOKS jI") pVhp^O8|,ɳ*SjVWAT,[rnqj%!`Fs+ǖɑD+Ueg`.,55[`0gT 8^oײ9 >m$4لpNAβ;ҷØ *aT}:iK&gSieL( ŗ;8Я >=wfO5iTA2dct= y@4v1Sei6>BF4j&5=T-a}mp ť`@7jN瞞_ͨV ̒I B~N5p575@oSo_6*ȁ,DhGqyS~'jNSXsO)bYFvY8@:[\󈙕]Q F rO_KKv@\@ i%BNO_ 9T6vCu ZJ lZIg`&\+ku?ftW^ #!U, ~|.潢gUk&DYgO,/1f/(% "lPMb_Y5K)4wF(];v;>Gxt,cܲ˘,4k Y`imw8'4@VU'5I-7 |t9 褨*Y 4~6S{1 ,hSHd.eee@s,.0iuԝN[G.(굋?elLf9-.a;)?: Hyի 9N %*cl?9-ַ+0c tتjjXLW4fWZx,˳ e9tOh)IH:ͤdg:8^&>)$DkU-EK4Q%!ۭR yo5fZ5 e0]R6Ƴυ/^X!Yv{p9up36$-iIMa`\  n*AsIx(QahOSPuw{ؽ$~G U\*4o4?ɲ|ޗ4dHfYiU,#S7M|z<5fN#^Z;V$ :-\c'5USOMPҼb)FV@uw3@u~Ee,vUꦫɍ1,}):xG!c|D)1oO  2܂~jjd24@bp<$ ֺVJM8}1Qm׎KA2TNVVAK'ż޸w*)?Y>3#u!xe1XEǡ, mk'T% PqVLӉ9:| =efb ܑmɿktǝP4A;}4LH#dBۛ|GPԱC PN@{}alë@ a"Ѳ~լZe<cyd!x2)gmE9K#~ xF%?@jbg[L&Ịm=5G%/< |;aEU\8VYCV겋:cUmL:TPY"!\~NYpNFCM4ǔ/#k_PuߪhZf/8zQWS!]|N^UIJ11UR)q:>Ci\RܒV8w nm4HtLJ&it~} "MkUUEJm(oS?vSrqETP ɖS瘖bN^ְ s|Y\纼rW,.W SpH{o^j[M}:؛uAR5we9K oܭĐ|qH렟(=SsK6]?8sך:-V4YUF܂{g*TSh`mV~>)oӂdjJXI96"1JB"4i P=E]L,;zH^mӊzE4zCR/v$)slDVY]%<$~fQw|FG]jy?\zgQZ܁CFC؏ fR}H-Uw"3I K$[mʼn!>ݱF׺e"5%^i=4Avi :(14Y$:SH@=}G^LRdׂ<HdNuo{k\rMPNSjs`.A w1H lׂ̑[x{=%0@X2,6=@w;P4<0S[>6lvm1VWY>V8vqQ 9cݗw m?czKqmTGf24' [-zm{i$m#%^PS< ;T`ۯ_Oa/ioO:\oz/1Τ[F;X=/Lu#* | Yzjs\CU t0RluX{c&zt)eS3n>*t)jس *a8M$EPFĕ/rGC+R I4䲠lOVf3A!XY` ե1WhSs>79a |gO;&IÉM,{]򋑹(VeA|1{AoeɭMHvִ~{]T%19%X̼]kZӵP䑷L16;G/J)>fXX. ss~\u?V*-%K5sXL,SJl7:Zf z@ a欥Y4xnnhҿ/ E1@ H&1 ~I.ϗ5 Q"9j3h&jXgXGSt ݬ^8ne </5;ID!Y2~8/'͘KFDvb?LVHw,Gq'2~,H驠dH:ݺfRf7]^2cHN# ! O"H33Eͅ販b&/mG\>"}EKS-0IvwY⦎ ˬP0r b~} a.=o XХN}F7] H0 ܨ u mKn,^S* 2˩"]8Y,fm9>bmМWNw[6뇢q䛝W{:.+ͳYʉ&U [wrzRH sX⤛W̸k1yN]QX"[u$źJCٍ4 H/{(@$J_l SxfQHG6x@[i&Hjxx[߿yȗ!(rʙ&ZFR@:cQmEvZtU|1~5Ȥji*T4 M$\N|vfDr*Ű6✏4sGYFY*TfR:dCNv]+\UqUm:mu+|[CNwSWWU$T53#Ӱ\LT9 n*-6C%D b JEd\TtM,iIi $U[ |]췿)z<>ݘL ?)#jhdL&ZJ 2y -;GIVi^ K\{{bo5;bJР -냒 Fz;DP?bO݁@E> e+)"V˷'Th]-m 4Y {Ӧ _a@Y%3t60) 6 /=hU6鎡BV/M)A(+i?'Zsm=^hXchb{[& `צF#>PRF_y5<9m>cRUH APH y&k8\)”)K'g'q]]&cHCOGHڢVeP" F#R7H[Ž #ՔR nBۯF=z#mc]u2}N eٞ]U^$yM Z'DfK.8T`|]EKL$geUT+;KUZAZmrM8Njt )ue8VA.q1nʲ):ڜGE* k FK`VK^π^V_7u,=;<<#? Ǎ>_gGTfsO$C8Hڔqk2ĀxJ~zEYe P枠4륕 ۑpodv{{O~i3Ziui&Q2rIM' V²@L]3M!_}նxjg(]@D#T4t~ ?.2K}9*{8lfGGT`Mǭ,w; b6/[r_oM Ϊh n[Q*5# bI3rEsnF94 %\ I-GO\~u~Lj1k}TIm.2 j)Wy)eWm"X5..0Tk(d#_+p\vî]Oi4᪄<ǎ2f}ҁMc,L i`cއ":NB*RBc֡ó:uĵ8>K'XBgv/P OrGNdOL]T#-*7n|v{ǟ{YΖnQ%)" Aʪ%hʤR,eb.;mF"ƚ|:}V=Z~#QeXX_I` v6 5N6%ΨF3lGrX-9r6ʹK30pζ<3O%mt(1$&8*Hs"-|4uoJWkq K$l+΢)Z@tX>' {zV19o6X.mQRV!% #pv_TaGy衤Z넴Ҧ1v `{ ԏu<.G ~{,X&-'Anv-ی`9 -T dޫVgE=&T3- u,6=zU/UokE5dR%~c8BeiV6|\m5no%/K]R3Tу<Π)2)Rz c՚/[䦝"u19X'v%`^m$/ǥ8CNWص\9-EFfF>k]=] TnB)B }mǡ4X0zם$믒wSef!y_TӦclb'h_MPUv [;gaO%͕+ͳsQլU|V*!2T{Sͅ\{4츣UjnxƝ|8V㧧*"8)v]@k}T4#ׅͶQ[ B;5$O Xr0ҲT+pY倵U#AWKAN!&#sCm6-[1iaۙ8O ܺzRfTP@D{o܋_51$guC|yDKGb0tU%e;}NM9OycJ1qR4kJb[\nz[88ԭW9I#E)Iz:Zʬ'*jE$Xj"_ Xǵ6t-j VnocL26HՁ"Ӓ=t4T%UlVUӰqyd1x?EB㷆>JZH Ht&Qk;{Smk榲RQR}nGt C{^;qs}ɶ4(muӟG˫3Hn @*Eck܎^ Nꖻ6\+eV; Pι[oyW6_}--Zգ%y k;涢-S;q;륢c\9#i?@ dk ŏ lO\v߭G9?PU5c+h\6 w[m^/5IsIlO.m׀I6I5ti&5xŊnj.f.qFi]S1WuiQkfQڬ$aS  QKV*slʯ4U&y(&Sm?imcihʎO\v'Ay_ee6]X-SI CSZր٭n2tQ}:[|u.o?hPY9J en+tCOdyW߄5b}9tr4+&zX7oeG\aE|K`i ]f{=ɾ1 P@7% _&#E$n^ eZS;ɤ*Ze$`$K+} <^Ψx}/n3Q]?/~3ϗ;VLºzg _c1Z@e VcIr^,׭8oƿP_I##u![u;(۽ZPHP؅) 1(:jrJ 9,o l '_S@b>`DJQB6ؕ Ev1\).v@R.mp( moρB A T A$&ocY!HoӉSvA.`G4S64;`"ģ@`0#(EN DX@ !,fO1* ?1{-cL[(Q/MLF# ?)o ;zL?o82hhxz8: T0$ Chݘ=]x׈԰M>˚SMfA=}@@H,鲖X1`B0!OR/9&OSH2“fQ"Ҭc1ݎ]^˶yJ;߉E|'?ZI'xQDF0艨>k0,G>?[%jPu8ܸk5̝+!Xd&W j&kjM}WK!?D^Mfաr+3HVVn&ˇ4^<(jjR6KiY wmn]ϡl|o4}-yJI5 @bx#vu_x?-ck8Orʣ.2I76Q\549Ԙl?3ngU%Je%Uzܢ ȹ$}Ω9Nڝ}bhf#M|eF og>R^ϷLwMsK2jʪ %.nRT9VA>Z[Q7橨Vjk*&&\ Sc'=8i `2(cP!<a*u @$j=A*a᱈o( AL03)VqfI G+.\u*HOYN>wFW]>liTOJ$Ikz[|^ʭuH 0BΟ,dI);G[/ Ӯ7( n0&Dd1:(&\_k_Tu\v2V)X=Iԃ_ZmSSYKP)^jcWC&ɱKnP",?=߯`'0l)| >kH%dً#)aM7 ǕM6x ޥN~.<<*K7^4t45T4TZ(5D#3/P^G}F=gdfiۂRҵL˥dCcC|5[uu*IˢTv))IF odžwzӎK4j iY]zK ufAU[o>*`^$WVe Nߩ'+KV=' B1ݻ]yqii`d`}m7qT5~w|GD,4&JZ(Y"M-/\; &g4WZKZqv_}gKY W Ͳᯬ$9duenm/_WZM~^jOWI1\wP?ô_RCɏWD ^b8iEɗpdqR汋#ԼV!$nwcr@& _xh'xWMq?i֮՗+KvP ~ }'?#nRtCB *"ͥFb&jSŀK1$\0{b xv+._<֦sj*:-ݬ:q,_U)H}%NYI#^iJ HLj!7ؕ76~ъR0]:?]N[[3-.cAgVS$ULV"k7뵍_Nu4hӁ~)!\ 2# ~vĖ A4L*b.yҫ曩۵{kX;U|բj$/oEVX{)StjSLGSIQ,ئEw'Er6cq{Ǿ-&5y@&1̌r$1R̒A0ԧS(M+b]ФU>zJuuKbe+cWJ3H.;ka4[qof$oV^+ʥXl&DЉ9f_`@F5JC']Uhk^H Zn7]_t$9~zQ6PCrE7߭µav22J)+DL9uGd ]Ckn8ǧZ)lg TQZ2BjO(_8 ,Dē9ֵ|=U{\ysS8`Z㲀.w=ad*\,E9U7 c8 k|>Hg;IȪfii%M',-*5*O^R@lx oH3ꊉ)Lrjǭ]A[}zAq~*Z;0@%ok+CY :sNK!I ×O>g9lY!Ҝ+A76[ **.vufKQȠB_PqWVk+#0[;|Q{s]K^x2^:ɨ z|'X6C eY$K9>7e9jq"|A ab\J +R1 ]NRE_Ĺ&WSKW]U"1!bK &.vQ +&|H*bde?1JɄ;\ A7 UMw7[,؇tͅ O~R[p!zR)qwoC76IU]C1kݩFh߳dBxrnn#ȼP㜓5|%AO.fcMKQYu[tU˜EPqٌPJBl^Nj|)񇅇7_E5VG' 9 -?F*ܨj5B7>xsDwqKմ55>gV@`BRVx}C/)H+G5-IBT~njc|#ros^褩Jʺ]* ' FԠ(C=fyE8d_3"93**37~%(蒦B%uWR,6}[0L NΧZNHwS%gԠf70,j]䒽lE7<{)0Хs/1Ĭ\0!nӵ@Q{co:|kRI'_򗋙㈪[&^"ꤣI% 4vMD nr/a4 u_|Uy:i\ER4Z2[v_a.1ez3(FjᩗTparE@oc1U`-JZuiO7JR4I]ĜB .p, b A&X?Km)RTM9iiKaN鍗%]C ($KY`m6AJ5ym\Fk dLE]|y%R"K_[}0uo%k K8,LSVEQ2:TjrM,@M튫U84 j/%VFFFFC$Ř3JC祺[I=}du69wC 0`,-k V$T37<sK$hfkl*Uem̤lOnk VxzuM̏m}-o TsS%%Ԧgyt7e[rHHŲMcYL~5]zoҬ%|Ć(MUE-9+r&C([ jlR٫9M珡$0r3WgUDD6l%lX *_D:ny޵UU K=9>Y~lL?DbA]׀+/Y"SӶiIUJO%I&<VwD`O]O.o&>u_&OtMV.Bzo 3{xy8]jZvFښZې@޶1Z"7]pp?œ'p9XMGK:^Yf>C˂U+L\[ˏEB>|zمBFP3at鶸£ Vװ,zBys]8Q޸&UTbC hź)rZ-|ma\Ɗ;y=ce5S5m+*x*뫪IbY 0a,͔\ƱCfr_uYK]H<%wL+Le9pb4\0á{bkRk>)1 nʱWguG%4kT,4B`ӔBr ůmk}[[{T,h~LyFm,Foԋt͐迿ʖ@Z)jJn@Xߧ1fR -^k9U?'rm~}&Bo^*8i+"4Ȑo蠍ݻwYu cUC "ǭ| < Z[d^ߦ}:m(2D&fuSTĠ!mJn$,M8 |]|` $oӰt֒b$)> وCNʎL8'1ߗGe˜Nϭx ?ˑJ 0ڙiG;% u_{ql=WIIn/54#oM:E42`۷S "Ƥߏ_E,hYj-vNnh-kdi6' [sz2,lflUFZN`,Vm߯lj>rƳ7!k>Ho>9-Irh5 ;)yQu[o=ݘ s Df"xv֊ZQ mOT,)`qktùÈx]<7 94hɑ>OD5D0tQhTF~= ,9gu.opaCCGEAW=x15w)DMt:mk3f!y2?Od 8+☚b u,<2Fe/6+`EF*f]gQ7 l+)!ڱm;v] amb XL rV?m? A] SOSE) !Mpd7SJ9kZ֓i[Pt dG^GYq nuԉ:D ŪBopԠ[lcXƋ(&TTJTi߿ՎPą|x, If"IA6-_k X.F7\*885ZZ$O+]maXmeT:bx$ǡx^S]LPQTWJde*t$"SY 9ctI  GbCYO*UZyv6另~<9xxy&C;GSQXCNٔC1~U *qu5g+NᬱbI`SL.sebeԌbvrbTy5u<,PSxv=71#l"ptOKd8|USpIomjTBܮQ:Dk#ď5 VJ;(7u>Ev 6Kp7"7Y)ۦ(Ik?N3d Q`zD!^P'!i$Ą,fO0*F//g?h13?CV?t͏C0]lG720!F*O$qRüKE t t:$b*w> .} 8W!jڞFFZS5Y: Za >/%OML<#̹>q_GVO Jm0Ԭn~6=0!Wa:iӆ|HJ,/,1Q_ER_TjKYF :Eؠ* űehʁ& \])k;`Bnj@8Ū$l Ų:\zƂe*QH$F7nye_CMZedo U1TǛ|n_!:,:4pޱ{So㯥ӈ1n#ʠ*ʊ֬2$YZ$Z./װ}^ʭ{j@1k^DNޒKgYptqP2Lš3rՉ4~L?zӒ곈uFI2)%z4O%߯M+PK$"6YKpu O*$l&B@'Ou&25PUPTs갑2Y+~9J>h]! UӨGc0iK^؞8YP|aAQOCT?%Bw=1;-DB&]*&(ΏEܧ,mé>.;Q }U$1+f1H-ʔJ)cU;u1^a3`]yi;K?5i j\ZJJ(2b[Y܍K$fcp1s B!Đ'Q0q㧈yFeƕyeLupiTMȌ!Is{Ķ] &0vL{OP3XzdY咜bQ̢6!\)[ |rUƌk۸JFe-F~r)s[O$9\ۭ?nhdPMd:M[/44|AexzO^|(c#zn9[rKS*PW0-f~={0ü~CV\ohҖ]aN \ZH |6^NVlkW#)hW|U>a+iIC;,l; RD'tj{m&*M=d:'l˶1F.<A >p93oE DBD1IPwհ$sZ[M&srH"EZW3kiwǩc{ƺ'uFE_]5]+xiRӿu;\/͒JZhoQ4J.<`Yu:kd \qG)֤Zuc\#%E5]SI&`cRY t 7kU|e E:nai6⵩a9Ϳ<9ֻ2eS 8fg@cܭaƎb_hx8\˗$Ski#C56n;Ɯ}fvit4eIp IT$z jSfSMH\i=J߈'O/LgU PPRUjcy5)' wrqe&3Q<\4g|7BQfSI=L4P*z @V'sqdM /nS -wLZXN 5:EI"0=K3)9~ɊMYh1ˠ~(JϘSGH۫i m(Y5)n-|_ 94`$RFmt}Фo=z *-hqS4lei2+2Lwr5mݠh_iɼ jF}Ks!PtX&nw E*@ab)YD^>f2 N`U7RlS \Ȏ1{ ۦh. Bkн !Y(WqAs\Ml4KuluZ}FjQ T4mԆ}Vۧ|h3K udcI$F% "SejmsdTM:yl=6x%=CH,̑#5FD0q*#/"O\7]LӂYD˧?U<^h_G.M_samD cȌci q Ĉw4Ts@BO) Zo/}km\h|iƱ v;yu+y<t\fBmɿnKBBP_Lק\L-qA$Ġ] eTRJ-TU$ [f%QV( >1upf40IYȒ݀AZ }-V2꘭D.R9yW<ڤe1%.{* HW|(GŠQ&eetT*shjc;xє-1qT{s {+SYO?8:o@\!1勇ks~c1-8')T2W'y 1:䞦\u;;%'b$0)Y;t:b7Q!:H|_tQ[Ik1(*P Tpo٬yfIfٌKG K#諾8}FS9~lFrdj6$_Nokn q#K ea}+/QT1ҡir 6jWHOEMS|'\+9ULQa5,,w?aJ Ф_E$uuJ5_0Z%V-]{ˊ<gK$g1":s$qn^Ȩn ȼc&EP-~kGth$!O-^,pȨֽ`N2c֫+SRN{"g_.am:ZNai渿YM=>]A ETJH{)׳Dy'A жaUSd8!!/_<ϫWRIʺ>6-3JIHR* z`r1Tk}}K֤e#m$99HVdB+K}(m>c.a kN.+gTM-j*x[41m%&˩O9m1b%~4Y 3;@\'q@HMjfUW3ȍ[%.2wcٕUY,U$!V2m~Wᚖ`n^ E^8sr\j_55-lm%R| K8):*$EZ/ыnnpi)v͂uW~rq4չ}:;Tsl } t7acqF-fxDQ%hŞQfL5%PLޒeq%L4($4t7;O3b<-=vMԫ ed4+e3 4aYJզ:B8z* BxN@H3;3"'_#6hPncbFtۂZ|l%D5Qjd+&?12uEu:6ߡOY;gE0IdR ^Ӧ;<Rq'N*1gzTs$Z@f #ۑpp4 kJ]4Ng6*^HuTab^等6(eP:sZgC?~\֊>aRB_A$=H83a.\+Aqdԕr MGP|= HIVm7ΞãR\I©|(2Wt5>.ͧLQ2v!zP5p๥5cWI ?cMLt+q`ڴl{Ϭ4jH1EPZ|GYŠ_3 o2Jzw&Nds$:k,~Z@{2z:|Y2ڎlU~&p4b: {cAa6niˊKMTV<4)+ D DY,qP4$ ōB,qkzzYຌ\7glY{iRقok _ P96i-}U+)JСN>kJ$&RF)d$goPOka|`Ӧ]xpB 03iPb.Fd@+woEmM5OGp~&H IdW">n07r rs InDQ q:\EfI~8._T؃n}rL`h))fz~s]h{N ƃ+S-Sl D{g..;D:QڜJWi8w$'H waug|#UkasՔ-aL"h^F{wǐup5^$8Ik,J,w|jR4|}Jkj{+U9Ē7$B@n8U[#/ךZӑCF\ĢA cnh|KMr!L2ʾ|*LD&fYJM<\;bDOw?=yN2s58^b#ژj]$jG2 S(!utذ=?1N,+" t0{ \3HFG+BHACz5_E:jjX#Pӿ9\ .ߑKfU*2Q:%= kApGc[%W&$㕹%SLz.:a[.rs,)aij=\8_/zE}Wfvi2XnS/ʣñft:y[iӭ-`Iܦ1É/K\W9 qa54VHiEksbf @kEѭH6\.6i<8A&/uwe9&-r4N렻DHKpv+$6>Sּ׎~N~p[Yq5eE-A^b"Y5Ik/erzcT{hhu貫?apwmr |p#\OLs^CinbjWݼ.0|܃;q,߃jW&0)E-vk0c u7Sqc,pbpM5Դ|U Dc! 6_T0IJbzd.e{Cùd Hf)4033Le ,va ?VxByrUqWù^eҺI$AH:ت 0?-9^aSG\!#C FOkK^mksfʧeUKMWB8X$fuKې6aiEWbTv'l]UΈ+GmۨĨJSHX#m5*ܩ#F ߧ׉BQ{o 8bFVC{ B%#n@=N'K_ɖn5Ʌ~cVfTC3M^Cbnz_O Zlg#Üy''|p]~}A3_."JBS[EɹWql5Ze n!ŚRSia2H!%e?1!լ ;~έEw~C1c6[(>RuM59r#Mz4^ 6l,Ϡy okf6^^5(pWpL|u*HpF;0v :{D|nN8l:@"*r~L: Lě:+x͸/l D|sZ}UedM2t\[|ZFyS?k)!G6= 1ׇO%i,}mlva7879dYf4S#&DM!O0q[Um晧P0_{.G3>1ghEl/%Vs ޸{S=Ž4UQrt񡪯e'WS|J؛;I~[(()i!B=O_TkMPo82$@8 dv'|HBQw1 :$a}(ZݱuklNP${}x,)!ݬu}W7HE~lLd,7(7mJ&UX%IZW,fO1* ?1{-cL[(Q/MLF#`B0!F#Iߣȿ扼!ƐLYL%@Q;[vLUQeXWtU2Fh"*xؔ- c0؛Z{(ܟm9[Hp3שSQI(:MS$` $֝ g=:afGXM&wOHԙ%v{_P0,ĈAʉ yI:lKEMI>XyE:h#ׂ|x5,~%P #^GL/3h>Ǘk_-B p.YUd5RjbRKcb]H{CE?WDInKK\op7ߩ٠^Zm+Oy#,ꖚzX0bj v;_[H^8|yuC'\Sk4#1&6tcZy3n6d?(<[(fZ`{Al9&:QᎺRf2u TS :v{kaws˟%Ӯ9<3nspM^)лZ[(]lk$U8}/X"3+8gv󦿘oNSƚv- &gzfNXakGRIի/YUqVF0/me=@~|s_k\XEX=EHN3:T (gpG*YUԛH؍?xjԚrV$q|G%NaOON[U^+jc$jH}[G4 a<qt@U|J*#5

    @oro٫ZNW $)d?%!U_IC bvQa |y\3|Zp#URc$ сlzZRx%܍U0jIњ$l9ar6MŖmwA!*gY,r IRC"+cmͭbpgRIJg箼!t.cP^YZYjj56bA+mE\?{7Z3RU<3#X^YTXIv6݌o{uN :I$=Na d ^#0 p~jT`/Q_ I ++{*KMkjtY#`77 ﷯B6ōƽnրnVֆyXsb,w}m >><: aH-qX49ib)Pm8Z_MMg-Vq]>#re<9Ltb9,a筍t1NE*y5-Df_MVV g,A\ɨYB=7Sx,cb;#}Eg6P4rWAW! ,  (9[YTSvn,IďlpCH0zDð,Wzl/ [:we$T5L;v[_A/ۥ? [x%\D4hK-nGEINa˕1i$vof \X,q,mBHede&g]I6aCIQTq9KQ`IuQI,.<ǺjBy+u!xfE*Uޣ'aY$X'ҍIU,tF66hӏ%*5ȎkͿ+ /h򺜲3f1L2+\0aM 40liK.د0d٭VIRf4)&JՃ)}1uYӡ^h2:ڦ3Vi1J45wQ|#Y`4KH;]|H'Β.EKHvWH "ʯ)6o`9R $^u策wwjT#4Aw蹯4MN-l#EA]u}!EmOǤX5]P|s##I6MB1ğ^P6i4j3Lg TWQQ憪WY\m|-Vs[3m^Fk; P%SCR崰Zl}>/kiEsK[ӫWώ4|9sYLZt opIca麮oirNd, _kZu?_\ 5{ BXnn%J]ͭw!#n([;(]G4Q oI ^psUT͗S3KhNabž,&JFGZb-^kEU~U:d4UK@z L{w1kk569-Ͱʽ&ϸ/*$ʮZZ  ;+Upokէiul96o4B:\@ ~l I`BA뀡bHo m`vTo\@(Ik&ˎB6*V*ultT (=6()vXC"6Gkۨ"R\^އDl ?~؝-FPߦd|BR:['BUH߯)qnBJwu)Qm`AE6RV {-5SJ?p,M79VqCLsTIIJ3m=^hXchb{[& `צF#`B0!F# Mgoс σM&1UNj-n@ױ6}9d.40H`xsp:`tÁ=E>^kKh枒S->֘"ub̯rK]kUdl} Di~\MUn_QQQ#9( Lml1Nj;3iy n۫ pt/VQyu(Qk(F{[21RDq_Moj9U⿔S;%ͩ5.f׫1˄RC3L4l7 1Zt?^h:W*r/|zܳ05f$R H@mzm X*wr>yuTtJ>4wn }(:ERPuӔiC4=,SP5T|5;Jl }ncpZ Ǣ8o8pԩ8kڞR~w'}"o/Tj^z~E+JUq -o}Zf Tәy;T[֟o1KLIQ+JeH:[[{ tWvy3&6Q5Z}0)7V9VUM["%dQ®Z$C:t_F/m&զs486Kk)]O+6XdjjYup{ m`j <5V(h[McG))\i5h Ļy?QeHQȓ!Qc/XeӳuT -3)+J:ijϠ)e5-ԙd"@!y{hi#r$A5+erYeyX/!t }qV%k\8Zq,JiH5<7'Ϩ>:I,Ԫ {} nM9Uf! 4*9*.U"si*:[ )`vd!OCmŸkI~T 6{+~W5 '::cXX ۭj#]Ei3˨W7bJkڦa*qkܠOQp;{4> W<ܾzz+R=HΫeo1_!Ϥ ٕ3n*_N* ~|jiBbi6@]ONbuY{.n\ cUjVKň-cۡ7ۦ/cWxПsM@iipij;g-iftP&Ie>enqO(6)sbamAUl2_+WeefIuz^-T9R$,@8Iy\wzְd,)ʞ HV,%dv}VQ:aXJ8xy.bho5<|TBeZhM_^nM*lle]YK\6էf*>m]Tcˤf]V{@>q @L|F- OhdM幍Uܴ,ĂlUz*0&XZlϗVI{|=8D0l{oML+~:L5@Uf:%ƘUmF^e575ÏVOx.O6pVܞ!<ޝ6 d̲))ұuS(Urt }@f?4ΑĝN!@ѫ,PO)ObwmcEHط]JqiWl/4Y:rLlovM68rb]bk<ܶZ*tSQsn\{uX8m\5? [,j(h\Z{Ө!7go;~HҨIMԷθxzJ\cˣ@5&4p'/#u.Xgx̡Y``.BƐ M-a:LxuI#+I衁$(Mΰt.k2ĝv/j飂0k*Ml5XnSh구d TDI,m^‘ɲ [)IAIiP+_`p[ :Kgk^XO]YNeɐEV+UA*IOH [$i {aj/e&ԦG5X|1obII+fQ2HbE,Ud lu07DVD9}Z-A8φ|9{Ʌ<]59IgzMҮ!'&8o83X^j aW`|]3c Hq |XjzL=ezLⲒ9+2E !id;؀,{mlm`e 󽡉vs;rY5`FZ-n)%!7{}x4A nln4D$X[ě @JĀm)F%sq-0a~07x[~eO%YOKj캍bYn;݁V#ݴcgyc+w[ŏo.Q^ǃ^F#`B0!F-51F/>TM7U!(EhX}W~,zk \C+@+L"+(cb@$z|cWeS4#ۭ Oh|=9JA42iSPP tqXJ-<gV<%H7RUFHtĹMlQbH]y7;ƼuF$ah[oQWIdc 3ڕ%EMibl^_hkiu{*EAiT8n,,#:JՔ<4ب'Sicq{08ng嚀}x%&r^xOq>Gj)(Eik ($j%AFk溬{a?ENw8O4'*-ʑA"lAlؐm^a>7Y]󫴇[qT"$t<.hYWfv*jx[jzs{f}1ytE u}4t')EQ_P C񣇨-df>=xSSSu(T7&w)E; ='CxL{zEq?os]lr4gCsc@~)k1t궸&Xf'㪁swڦDF '"[6-nr-Re^Rk_r~1%@졭 i -Ʀ!VGo{bӯ`Mөj?FӖ]LJ,tTo^} Ɩ.ŐRd$7{]z|\#Af47}UkZ{֫Op6_đU%dfl 4(`EM->=;jɸ"y8g?2+j$3J fkXmllfjEϸ;䕫TSx}! oܜSnc_NC$JQ$tYwʡГ%lZ~ʔC*n幝NcGBD,Llyؑ\ AĹb$9^ B̒#S8wJJXK(|gѥY~h߄ d !iHUJmpc!c6xuE>ʹW^;n t?]nqZ-<4-R*.ajCN$eQc*\(f`7:eVj<~`7F\P$:!C}GQFt 4X)\lԍFH׮;ukO]k[}*[;HdOu'WQԂ;a\3 6d=(v5~D(⌳5=ebSBfhR"ܨBà]7o55*?`HncӞ=>4cTE+VKEV8[ [Q˲6SNb~w\6J QA#N8"Bw Rqwx:yM8tC-GwFd ~){w`{U˯t*K8rtF20n:}p +2`tZ7/ljfXe[ V;c;9ay.l{]g9O.SI 9(XeHƕ@5;.*5ok&pMOLS&̲z狖™Brckkؓ)(%Lo]2^'Mx):Lƫ82:~k|؂W;Cb;ϠEZ^ }4rO Vg'*aZ0Q(2l/4q Mr}_(n+,S<m#'c ;440|!s'v˻'{/F_~!%؄,RmJT}`()4)n@(kؑ5Bcj5BHTEvK|3zAFF4lA}9żf-wm7W7p -͞Hޗ-+^"t m<#a.ybLe¶xfORJjsxd)LQH (?ptmX\e'aK0 Ԙ8TQճ621w,۱;SB֎)/qX ܒ1 KiAwT.![WJ=L [Pk<߆$~Pٌrc'J;/2+F{'/)$K 54[d.ChӶm$NbN[h!uwaceKiRY>rc릙lq؋z'?i ˱uFn.\uj`;04MsjJ x&c/%91,"0=C#n>ia*XhauO5*s.vP u1==F`Ԩ1uP@í+SS÷_ǭkT^*tOK4GR&@ wp;cMgEX~xtlMle;}(=,,/u?wL'b>Vb憭/%-/*VJ(Kno+T\ 5:, O4~RHBrw#=7-G0C,ePi}*,OcK*0ZjQ5DQTDD@fm)L^z\M("R&&q"4'3$%![x{ ҎZm1ƱmͭC07$tWSaq kI\3*3 ;2ҡ`Sr7FXxhE=d:AN_?"*%Br[ UN"1*G$)T0hR$QuL5 |}[ijs-MAJԥL_]=\8n:^\(4iQ&J(m6} "Ua2Mێ߯h>Ϩנr>˲j̦#/& U ~yJPEr["U,Ƌ֋l~KfKg"ЉIK\X~_;@,vzxZ|C-=q7>q|)4^F$i o5(uzS]s.`jHnT (QKo{m 5f5=kՈax´i; 4k7c4*ӷ\@ 7(Nl"̳Zz1QeU#&F 1X-sH$|$FCNZ~ULҕBYWsx.]g.o]rRSPÙ$3n3mf[ ZIl$)[p&@7& >NO *p $,K6۹ -'`qqyu% ՠ3Rˤq6UnlF-gR*d(tUU/XtyOsco . y~qRx0ѩ]B$zP9d cbUmkyVE,KX-ŕW@SS@m8C4Gss̐ʤ:iAcBe-{9N!][KUD9o0UG"ӭǤ~!=7HϕssyʽeyIX8XǓ`0"ĩ"1 נYIn= yNa 8Y'ͿO-}zLuJ]Zj#yJ$m7ۯO\\5ٝouQ!i *T:THlX0,W :+Lu>W5ۍgO4U4msaԂ~8skDo}ˉjxs(Lr2ȳMa~Xu5NCZ$苦Syi{:Щ=-e(I:q#3( .5t8Ú&:f`Ұ$R{_ØLCq 1LL'q+^ӦT@;H`}pBZq .^)Aeo6`PRw>aJ@K\ IH_Uo )r U)uie$FđL@16+eX4T15*jSS#M,ݘܓN_8zn1V 'L\ssem]o*XmBAw]퉕&-mBV;<*.Mvĕ A눕)E9cNÝGP1.5X1W K,;z: $dn31ÛAs/x%LΝy.9{CFUW@%XE*}X)ǚōI_/Z3s͢"HX 1 l>z-v⨶=PLyc m" \R u(AužN-VK_T$t?>'E 46#t$ۉ P [8#D0#A߮+ @7zo֥wJި}q‘ǡkJKcm)J {_ !"~ؒg(H^˂_a6;b%&Xd^##Gsj&Yt?i?'Zsm=^hXchb{[& `צF#`B0!F# Mgoс _a)a$Ac{kb[\TgqCo6RkR4%kT9mvg<5;«ehYؓkIs[[d}X-pd2er,@*R(SnWE ֪Y:i5%*3Rg}MHc ,ujzo{mt;ӑin:~&j06rjQSW2ֺ$O`B*j٬.@m8-ôu;tTֿ9G^7l!2%E%G̵f0_[5&\V=79415Keδt^zD eWV!:rؠ9VA\[> ]`4/haØIy#utjgHh:xZ߾:k{> '_]4Y.?#"BƦ6f6;0$O}v\&41Vb*ENO/Wv {\6@ }w,s_|}Xuۘg3fXPҠko Kɝn hwwjIbF3Ly&eQ`k3\ZWbgIUT; #Hp#nln bL[yy Hox XUK5;(#@upT_ͻ{xk~UQ4Z.q[A (a)ThKD: n3FS9I\?I]Ll1@uE0,6߽mkcI1K)8o0-3RRQ+HG'.  66c{'kt%:H`O燋#f§i$!ҢzvI FVw$l5 YMA__M(t5+gS&aY42h6ZkB5lnTﱽoGDT4!LC0~^LST Vѭe؝r{^UfAU NL!=D|EZ c (붒Mz؜P|+Uli\k(*<$IiHs%z ֩Q;iȍ3m-y=ZG;qYl}1Bj Z-tjia'&M>-p.bR-gp|9Xn0- %[xWF@kfLtT%16>3BPUyX -o)'Q D ].Z~s$w _3?ZL߇#Sc~wy}v^?t=Y n^*V N߯leb0Ҫ$ԋ{K ^|VLssXvw~Dq0p.iyWIKBzi&gMAP  H$bFPq+kLfc3f~W q[.SAjyTE!bv*P]@Hp>͛yZ'[C}f_*EjNPFɨ?Fo(&nAPq{D\۔&T" ~`KRuuV@ *$ u#T[&㍼c2(:&&jI[W4F7r5̫Qk^̧(u:t\O[ed+QP'Ĉ$iaYvҳrZ.@[͠(ԊT0\|yY1ZE9t,3k~C|◧-$Uje 1P΄H^v*P[鄉)HYTpA㩽m]zAq^T.OR- 2+SLrh vq?^k33:rhAlͼzePTFymmr$N)ӂV?-.x9byD⭮2/â"bBF={mI7JDmn2m&U]%LK]L㴱> pzu83 m Gs~^fFuZ؏A |7.K6;:,0uךTP5Cf$Ncl,E:kJM@SgO̤I.KT?OC&m";1G.6cb1]:u+ BF:Ƀ0UgTU$q]cހFmrִ\ia7?5ffQ^4sarXUîQ q4($Ir!8ZlB#JfPyaf[XZđR5y?%+4=v>j!cRl$l>5^fv>?%.ht^L5 $x~cDK(3)}; {D7OYCtis$T8r|(+ޘvISۦߡ(8ˈIܞIgjR[Q ʒf7z4Fp|\ImQT99I#*ʅu@ vv7 ,h/']UCK"jtAofi4;Ij<*8iIda!co%Z]g-1qQՒY<1!ZTmձ$^çՇ7LZzxc_ Ϋs QqnS{ʗV~;9Y&]Af hy-9S|(X 4?Uk29{oRSd4T4o[+54p \YN kE\ξ]tR4 1RmבM$*h86IZI$rv]GqvRu\`:'5yy2T3W5uPlICAxl9$L9*[%]toMLYc?$:uc`,#\cK$ɤeEE2TE þuMO]5SG׵.S5MlAePl-{;Nt]\jUu-DP;x'XU9Cb?uͨޝVQ? YmǿS<8{ UIMTu.H*? ),ąm^jDwTPpg\j 8f';K\dV"ߊf" Ml|Wv;5Ld^uԪwd6\,MJHβ9Cb/-ͮcC]kh_%As ⩲P/L#-:wc1npúvb W"+, +{{_9{ ]N@uNik䯫cADg:=\Ks5SkjH a$|:T"D O#[šj:[2m8 CJ_yobGPm(|NT[UVqh,4T)aQhAKuIHKY-FcW:%gk`n {^{fdx9cU@4ϖ^*Z.N\_aG{ZuItrHhञ%TsB2fqf4c5V'6L(ǣlyfkK#:Y_=[R;Õ՘5sj}c˨:XdwTVc*}wO ovLAiyD< Sc:%x'9}x.x}!h)Ԯ;ۣSfM{חf1cw<.#(n&qQ0UIQ0!XP@;rCkTq6hu*ANpv]%{E]VeTL~@6osu8A}Iv]L7.1THOYФ0f2ѭn \r-`N4TZڎs!YXЛ);5#$"Rk 6=j ~sp/em:t|.iR QJbVbZE-/ b :O $ଥT( ,ڳ.SGZPXϩ3M>-/lꝺ5ͼqeݘoA[KCEx8>.|6ŭR*XbI1b4:vUYA]QeFu_`3@07Á5V!=y)n.<=U4Թ\US00Im:dȾY~\uZ늯U6}5jղԈJCrFEmwe">l<pM8UzϤ &dj]򌲓9t̤O8Aͬme*PzkŵPMXrlNv~W)5hܮ:ʻ+,$LVK`Xa}_`A1?^MP|EW!3~ksI%@3liS{NÊ^ iUM=@bz9_Ʈ6Ȣ/^_U<713uVp*u B|~_H69W^o*rmMdtA$_áC< }ƛq]|U6u7<ߐjaoZl;j*R)MAgv*밸%}q k^NREu3,LQL,B(d̤ߍ{(׭qsML=0 cUPtsI˺AHBgF,?Eqp{n,tCe͵=tn4U,qȰ$}e;IPݙ-I\%Pr: MMePF1cWt]Ltnm_}ily1]nZȟ+x'̖'vxrzh, E؋JnVic!Fnuax(ᕔث֛]RjFzbO$, {b>B_ŷ! !@.O)l(zw3ٟpyu jC Lֹg͹-̪dWJXݱ cBDZB[GMHu}VY:OGQ5$H@+t=׉VfWLcF@]LI-p;\02Hu6Q6{Mާ&R +/ncAGBrK#j}w; asJ RWf"%Ssl-˪hrf"(X6U1%|SL2ECW3#]Yvd 7!u_*?=E=/=~4#:SkkC0)Ych ؎UzzRiapy J0z):;2erRbeFtH:y"vS2\^E7[*$蝗/_O_S4.W5@IN@+sSmUzS^uPh_،?]b~J7H> d,Y6Yݏ"R(mnR؅$,[y8rAl"lGBG\JFk\ 51(ᭈBApLD%A`N% ߭`F}[5Yfo|$`aI;, B<aj4'E}?ٴ J9`Bsc?4?nKx1=0?q xk#`B0!F#R7[#x)tA@卂o]-.픩tMr :!h-O3_ '4I]'!񪋉ZښXy?8BTCncb"EӼ'44!q\0e@ #fyvz8ݡcʎu'~aXIsޖVL+h᪨/ uT*/UܛRI'\m:o/$ۗ׭RY8mGe|Y_Ld#-HۏE&8x{S/1438f1 "#nk=@{uŢxtbM 3I*yzw 3یuW8޽l67TϏsZ,%iC|qi`7 a{-'" uE6lw+NK¹5 YT$SBDU܂Kf P$Μ\s3kqRbl/I,)*1X)^;oq0;̭1Ejw La}E6'>Zs9S,yDE0DIbEnmߦC$ΡK , BQ-^Xήc!52 Y*Bmbq4bU!0aCÝzPJUzpc郯$Asz+g/T܁O4TTГju,3NMiSvDi²W|TxYҕ2@vk #pzT~"֩,S4m4B^^Ɨ ̽SEMR#\)NOoc;EG/kRiT@J2 [ͩ1q3S!_Q~^ OsidftY]uwPwW- @EaCL)ij5􀾝=~.Լ< hnyg複J;9D%-]/ Q)9ksc>Z qK}'VSϝHzzya4|a\oV's*UTAbva4wmi.΁)`k\vţ,9bS̀OI2YWC1hR)Χ,nIaӮ bV㡷[Mؤ2uQI[ + Ӿ |`PԢH`]O[i5qhN8/l,YD'{k^L^辠ךƒ <8~SYfaVi)\ZQS,eRFdɹ m]5*ꂣ?$Nr8*iJz"A¶|o`ag9T|0fVM F6nmaԝ; o$Qsk8x~iBnq=na515hҝfhLj`Y\ I #HuNW >e[SQ2\\+Ν[oSxz!'M@h#0's3x:/?xǙrqc:uLpS>[EhseFr=v6$UdnP=ad:tP8z] L)QisHS}&/44 iΧ`uz9u~kyfk I~X7 c8ARQwT q$>_v=p-jM50Gl̉:) ?%PmkV/ۯ=S-p-t8O͕2Ir\?k.pJ'U(ewZhda9:A&6;m92O긨fmkVjGG$/NS2,7[ ieo6m֞:^tI~  rOY㊋7>ty2Xmd> c5z\E8s\2?^gyFQBc:%PysrXR5 ҥUZ?nGp0[{ 6@Xb

    V(OPƗ;b4Öӱ:[=~K-U%lڕdE@`8e2bXpx')=oi׺d\?UPӽ<#kuǦk4)b Lv^V;%裥뙕?ltL(o{QSȑb`V766q(p!e؅2pEt#ą +iJ.ظ6B^RY%BlRe7Q !fAH,HbvRQ7=1_l(Sq~ ^mt9}B>eOI%VB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`B jRp}^f5+S򩑇2]H,0 6Qqsk-<<^^OzӺq٬Ԭ&2nv p64@ۗZ.q>)VguQ tt\[س]1k7UEƼ@M u{spnT4S;9l2̋QK16*@ op a!Аc`&ȨDNukĆ&K-{Y|M? GVkrv5Yj"Ř)#=hvK 8tThS|t<**Z/bάZI m:l3f`fOpod5.qkTq%K qptb۾;Ha#@xRRAi7aW !R,.IEKf9cak*,@[kz.J,袡'cH 2),:v-h/!]0γj#$BiX*9m 6ol0QUL 'E0ޡLad"7] \4]߽iu (RTNjtipv;z`ee],2IcAQ&HmXZ ֮{c iv?IV# KέTJԍY]쉯@B-;֮܀ciiq {|AS$PA\LeJheKbv9_ 7-Q6?=R&g1ʬ+2buaq~^\KxH3駂S ;A̞(M$&df+! t:mpn o8SgP-omubE5;R3i@f lM><2LHn5wqo6ɲ>i3Le99CT 䎽ER 5]TxLԦ~!ßcG)8\E c*p@'( o6\~-H12ED`Bp5]MyU4 roK9noQQ=|,<lc#;_R%J:ּ')3wfEiB݊-rT}@0Uposn:WhkZ[{N+vv0駗R0{t]^>6U\e;X.-#g)9%zY%TF-l xKsQn609j4x\)Z$pbxMox Kn\`cew#IRծYg$Ih+fp_k< ICDQ-iRorׅaʝY^ꭩ \v:ے/Ts{1179C\|tķf5iRn=w[XfPdA}y䩪l^AESa[L9'^6ӆq.keh_^), Kv߮T~PKp\i⒤և㺉y#,A-%!=o277MV,U@I77 dÙc-Nz~hZERSѩ6rv[܋ ui;Lbm#Fhls%ٚ}YAiw<ۊoz&H>Bqu较-bv6Lଖfh],EP0龠}=C:Ul_%ML+ ʪ.2cjEh%,VەP=:]IctD3XȳO"aSklK>7Ls]+6ÝT)hTGΌYX1!:v|QqIjK_n [>jG_388X9>FI啂3 1[OBWhaJ$g|q=To|Z3Ycs +6&=B;zw ZV6UoOf19,DNH]% oarBR!MWނL7z$iR6T5.AtW=`L__65M@o Ξ 3S5 FTer|,BXH_p*v?A:p-=uo%A_7 pʬE`NI#gi?it<ֶ: 3z*\ˆB܀.\Xr>k4eLN-3>"r:8)V̋v\aLL5T5ņf{^ٶoS4tPVήrpLT:—:7kH{ݜ3rj2{KNu &2Iˣӣ9 u_pX)"zu95 1<eͅztj:'N_GKJ:R5ruxy)Tevjx :|8#ڡ'peb k _(򋛏L2*mY.eZ Jhr HkQ,mU2@a2Y}"ޫK𢣮dbTPI=fk^X=H|>%BW|[eRǖVh7MDjk?r nWS5>_B\l]v+V0Ex%]~wȒ-W"!XݔY=1~~ϼCWoSV )4-(IsCL1 {E>vU,{6$sQ={{b{1ޟ-^Sd,;\Iۊ MB󉑳2@$i3 UGWJ&* kr2ڃC_7VfPs _Q[XԙA]'w?>K9gCO5~_SGB牐H=TGLTk @[[E$=H R ba?{93YSZj#XH?CCQԩKF^GdYbeIS :Y<`Te:lH'p-{᷺JSJh`AXA_8dmFخWq4PJRFSȈH ~#$4sm׋xISd2=,T F@ ΥMKF_Ko5 -t8:ye))J OFШZ˨)J۶'ͥmwv}^桖/Uixqoy73'*姩3Y ǣjb$x B[װ7Yc_lD!lcR.I` b%X#0,HD%kjv%%P Qd!y1 4w8;UnpK`P/^ D dU)[b;@BB[q Bĝ@D_T؍ % :#nԳ䟷E,fO1ҩ9`Bsc?4?nKx1=0?q xk#`B0!F#R7Il-;}d[wǤIǝ׷T$Uz k|_Pw[8(IqQR/4GP;4h@@b+X:*w ee, pcI%aK!= e 18We lj)h)5&f@@'WKbYs/׊kPmF=rΓlYVzEV%fL=xwK1(}\ OAYX.Kl|ƿ4GT-vpjMST(ܐE-3O\s{ 5{[iZHQ9$ ->c<}WdL4)->SBVhPIRjz{ڎ!7t/` 2&i(** M:+p/ӷmX , oUa̐5]bL/˫ИBmkfIh&n.5W3Rctx맶Lϊc,E4d--+>IwDu$Y`n@V{\1Qp2ltlJWpaq BCEd7t=1Ø^I$k*ܐ 4Te^kTCX⪥y&tTє=2^ |0_fHym{uXn>C\_<Րi׫ܫ>C-MSVQ"{M°TYIXf4_,)dA Eӭk*YͶ?5XYMʜST)R!JM[b{iýRψQI3HқZzm%`$5ry6ItT= [XYpe@$ħ5u|VU]ϔK题=Oc|QO-3㎟Uq*̳75=XiX#:% ymOlLQDx .*Dۢc\8 8C+pm,Ca=,WXF//{_8K?1-V3jT>GW :$K]70b-~F FS| mm\oM3`nIَi ҥ.Uɒ0G+#2ɺ00qLƼLu%}Vi)zp?der,3y6[XǷ.apGWTS@ e'IߠxZ,<k6s;6mc妖0AT {,I/ fUsM*D JZ*T6qJPUf9@4"}-bT F]6K=H,;tV SUq!M@@7|Vwf > en\؀?>h1 KҬMU:ENb0UHmL-W 5>.Z +MQ/SU_jy_2nltbV7$|å%F4xח❧[w^kThY ݅`/XY[I[t94#$JkLlEx_~')׈|F.Ȳ 2҂JI)9+Xctl[%_TmFLFᯱ't7ZT4p::Qm D퉩^3^ I'+]dE2THrO~;Q -M<)uFO-LLM@Y$tRFAǥpvT:q~Y)ꦞiGѩR,>=cm5(+XS.Gď~ݤPj6n3GV %sruo{\?%l]Z +;Lf 0 kbiA<#C_6*1bg SDUpG;E#b-V,ep*0mw\K]CfO9eAQ(/ck(:I=5C.fY0AWM3x1ŗW)*o}{@{ouNrʩ3fU,̋LcVs_Usf.-cb/VXbSh@'TZRHb}@'T{{ur1͟>Iܻ01ҰHwkQpudW2y):B<\/]m(Q C zuVG!!7f" G+_ li0lf 223#rn@ى'N]F Nu䴩" UxeHmg>B~u [SMj4HQpc^tTskE!v n L6~&|2͐4Q۠4Ǵ -}tbpʶ93\7|#%)rjUvT lnJFDsVHaϾM @o`6K` q%îJ^#֮*9Q-H ujƋD:ƙDX_VѠY_Eu*]$tAq#鲨 $'9mISߏx7jz¢.#yY؍cN,֝@ ʯXlZaAQ @C$e6` uj֪X< noF}WVEWYY=mDO^VQnbOO++F- qN82zܞf,P$aHԤ!#=k\믪NRqp&4Ǯ* ~}`RNؑ13 )Qu-\E]™&ktQUYE \bPZ!zQ xQlH)9(g'XG`.K?qKO/=ad+ǵpfUMK$JU7RE[.-q+~uQno"Ds:re7w|CUK*!n8;rz7֞6`;{7:Ma1s^*FnǏ8*')Z(5 ;nwkըS/t7@Ҿ'Ms~&*lPI]CSt/¶ >R.FD0w)4dR"3p uV?[HCITh>J\SIugd%GXUH5k "][[q,: s0N6 ͜dNHb-N(w u şh`ql鯟^ܙIsI8xsL:Wh`ƥ!p{ {dPC}{ v [鉄Y#zzd#?87Kqss[ W PcBmNؕ+!t>*<GM#ԦH#c҉HYk`NF`Pӱ C?Ĕ,SFbP'T}! &߉aFUkwq!A_̳I ?@Np!#ǻ?[,6? OkDuB<`B0!F#`B0!iT0!|{T'\Ŀu] )'{ߩ[sPh'u)UyUidC8o܈Qs`*A4`Ob蠄J6Qb-w ޸䆑%D+\\bnv(X[w#d˟.̙ EA!*} )nZz&XЪ[ezi*}K(!F&m#Ŷr ypp!YYĔJE.adR)ʶp;[=maO5<@.2 ,&mvfmq-þ IiWS5ŕdTxBVMYVP}Cpsoo](uSFG_Unx!ҟ.DVpƧ iofaKg^:쨩VjqSÛUQS!.׉we,#N뇨vQ'vC̵y2C V2Yc*uh ]ZAD|A wrk2,r~-.@\[PQ^kP&Z5D->4wf õ&rCtWNzEH sȍmʑnOϬtgvU`H8A'V̪'c&s ͷ3C'1Pǝi֮-Hb̬{؛AK94ƶyn\*8&vr\ZM;;kbĕq)ب7~k'quK %y,s\%ѷ~e F\FUWSO3-4y$ۥŮ%lo.1CT4rMM^vﷹ۶/axlnAtSȪ"6ubm%/9J>*j,ّr畖VuӦ䑸 u8 s&\¥(\6^"zjڌi \*[[6\k43oeRΥO<3XZB/FA6U3ieqr |3:Uqek%ۃsu\SO㷮ޅVJ2TS8%jPf0/vì C=}(*~'5թ)X۟{ T)ShhҺe tslK٘ sU@S|BT1Q79 ?Z:mbPrVm#jy!V 2VH{cT߁\Tlq *vXO΍6Un}^ӍWMa!pjUFpK5mr;s6?un"o]2IKkn{c`I77eI?+-'*u7+v=>aU9O-&֫IXl7QB^bb _e7 tDZ96Nh2¿6!˩i%y@ sbnoo@q5,76{'cq*38̣x#ʥ%ZFGY.{XNAH"ޅ^иW*>1| 0ʳ|5gZm2fobXu_; oQU*$Gޑ> K۝7n;]Dx?2sŚ`tJc]X./kܖ x敥O>*hgj~EZX&i%5DR&~x纃LtV+D+EL3p7X{i.aedaM:mgk {ł-ͦ9q>k˒߄p sV+-,S0T ՞Xy-Hxt:ʢ۳܀zofVY5PMV:Kzz/,m͙*ki n./|@6slFg$'-C#_`H M*(>Q|"9M m  Rk)֌IJ(EzfZD<}m>,⬵o7 5$Lr+bb|$_&mDq|C#!7]e/047".( )_9+IEK*<,Hi x]3.#_#l ,'RLt(- zaJ4}V6~pZjs+i^ΖnBX\sq=u UkL̡j-hy%%@]Xnc\ :1:NkEUa҄JC]Is{{qbs~.˕QtU-5.-`Ii\+l0TN$k.JoR@أ+,;lA1Mݤ`{pG[[}Lhu p>bSєdu."T̀@=~"dr/&V=5O$bY6ը#Dyx:l8p1ݤ’~߽ØLS5ldbB<Jn]%o*dF5-0>FXbLTgfs<=h3|3Mm1 G$L\^Ѻ[sd IUGkP2bm%M8W9fAlBv=:ߐMY|2T.5&d~gDu_cWP \ X~+Waq@$U+ lϻ"@O iH&@xpkixR󌋄Sĉ].aR@Yg"Ǫĸ:=S<麫P.-,ZFK}3GC NKp ppĆqwF߈NJcKBL#vX] <ʁrm:&2;?T^=W-/ÆZk7ZjvT)9v6,}J`8).2:`lTArb]LwLB4ó)?HsBR w#e(7(1^&J9,m`F zcSm`vQ:B6;Tv.E`]l յbT_I ?@ĪӜF//g?h13?CV?t͏C0]lG720!F#`B0!Zk?'c~_ |Nkr{-lzzw Z_YÙ.萅F?oFuLT|Du[\;SНj M\ZD /^׸?8َ b5Oj)&檞[F]`rvjGu互'.]0SV˔FY0n% v22 ņm{H>?A鲮|%r HF ^=?Na]/ $n].c%-3W$\*5mmu'9ķ.3 *L%z쭋eoBYPUU$H4OJ:H{bŵ)H L8+]n`2dy1SƬƃ=,F $tjittTUE+Қ2. cb}-ݱ$IZ.TeUM(,VŘ6i |'))SA_M*Җ}2XhC` =NnL4|hsZxrzyrJx1@)@YTl,wbAkHEOxg]fuZF5ږVi%:>=JlkRkoO|,Ḙi b4mo 1Op\L& X _0*+m4b<΢qAuڏ1(mlӎ?1j|LE Y@AuG |+6Z5 ߍ/rW玎Hg)#U)[1@9A(;xsSOVQISUPC*4eKNzkg>"ݣliiNrY>KGEE *u=ddgeX;}ܹV/I/}+ PT ON뽇NF]8ϲTn4o%BdH`N.t2Ұ롮0E9Yeޖ1iERn!b/|&5oaOm D<=s#z %`)3 A]}xMmh}7iЈg+Iak׭NOhbc+8eJ- s,f&y9$~z|:\;"ZVSb8/ED &|}VkkǺ5uhy/R#I AbmͶof@2zITt"!>2񼚕dg `~\}1ipQ~JkƗtҪX7VTK"cۨ`?z*!:"{s o&(}`+p;ljYټ <eT4}ta߮;UUٳ8Aۭ5鱭˔s췏%ȪELT5LAe󮳤l'ژwU]=:+AH'UcVo :1Uic~Ji89MAJW2 䲂EZ$_n-Z jJshNd-%Lj (fӣVu'Pne rio&EqC5@G_娶K^FCK|[A` U+e5n]%DLYIE*n,n6#cOa`-p _y%[ =Sr|f^cY囋jeۮbEg ߉A*e/6K tKast7 2R,]b-cmPp^ZגZ: rY`ѕ{H=znom? 0zH\sF.+i Dj.Y[~ڃmic>࢙^b#hG"RGk_P7#uռ]L0kʡJΘ"@JBMXakI׉"s)=TpO|M*TKQ-U* ,u ]Gp7Ъ]vjwLUw3~\&gNhn9|= =52}%lE&uCHI7'QΫI@.@Epzk5'Iz9Dۼt]E%<6HV@*)6H]`~}+&ƃ&= fa]_Q6A,RI5`_uԢmchEץ݉p3ilR,֖5˅l a-S7)t-6]!"e!Xmap 7:vq˄l9S曀&g|g%0b~m0kZ*C [BVJtsH%[`&=i0"96i`..03Dj2CE2TLkQƎ Wb+R[4 Xs6ws:O¥+yn[&k!LEsjD:[mo E-Г3 I}|+85AxO |<%yK/V4ӊ JIUzm}[b˶W p3N2\ J"u!"Zbێ G;cESd4+R*hE<:X;&݅i7KK3~ 0,Y,Uh<־]/c07NhVy?f?R9nWPcihe$]IR  !kJe,z&JEMB8;;H)k8J(lTe^,6ivFCl5̹'>I煙kTU+0CbHXkخ":e"8%+6ֻ+ʧ2xY2J ,)eYd(n4pKV^i w<?PW9Q[U&r#1f$-ݍhi!tMTqkIh4U4JismdD*ච,,Fm^/Zqϟ^k&\ӯuʻ5jY̼_72j4b-; $S6<2E$p g&yCORYI9p$_s[9< 5*u?r*?ʗ-Yޢu2sG:sqfo_V<9i zSFN\x~Q^Q+S+>n*"atI9`Aeֽo@]lqV ̾5f|OQ\-+;SUѣ.ĴpěuC~ԫ9@ M@'K̿xhd9YI71TPڬ7a\Uͭ:qdZBH:cpe |.o@%cKS5*kȥJۯ[l7M`y{:E]Vo@MeCajQ˛2M`tvk;$t E .Tƾ3s<xYBeCKeMOTKr B6'Ƈvm r 8Wk🢽TIt@HX#-\ʠ%Ii,s9YIv%}6#0Rh6"l,uYy#4ԵSK2HRY܆Q`J1oRF}Z9~*b_iÇ9MK`kתX8*,A1D`S!fmi]= mm.{QYGWI]GTUlZy2`,AKmvkCA&Gf1ͨ@6^^GG\zpSg}$lvaT{Wxk֫PdUTF{p-nukw]flkq3cN X1 E?<]8ĨY!gUKmJM P{\G2؛b.-.vJq"`Sn@kuB^/`F!;^x"TURڒb:?>eOI%TB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`B"O.QOʠM)GEGc۞+b[] D o?W4=d]7U˹-4YP;nOSƍncY^( JδG1[gO1հ`:錮źf ;ZLx˦:uV_@h\XYPSSb+UÃ[S;i[R$ X3QIE HLnkv9``9o$^EO3Vj 3-ѵ=@9=96GJV3ig?{p-BʮȮ@;5Wvag lw b]U,oӃ{;1 lYxs'yĨk6㷩uZ`U-6+.}jآeb MU;__t(դ܆>7e<Ȫf9P[T@v-9j4;8h֎I!Yc,#XK{=31OgNiPjb9Q[̷m LJkLt*թ@{hc_TD:ʌP$.Tz ZĎ ASHSdikAOSf%J.4wA/}INPWHS7(h*h*|ٿ}}Uqp=Q05cpuÕHbJ*ibF؋ColiV*1\< )-:i}TmKHѴsSV|qS$Z7ѾG;/RuL(NGVNHqZ35<ުJW U*)*:v4X^ď3 *uPF0Ym6 A<UXP9ʣG3"Wa^zeu;0H1kleÄzhap}n9vH$="i7 m~Xl RI x(l&IS#1]GHҿ|^0pO\ cW>Ē;Rc{lt۾x6*6:/kt: +,θf8Zى+[ؒl.wf5M9l[2|L: eK*E;X=:[>s27HW~:)/MZfex}!sj51$hBN?Ǖ-lPM"!%Ua<NSHb+8Rm6*{7-qD=al6[2gqs/xaUM7,Wu0 {_y.8Aax'O5)슳U'3jTi?@W#ÕSHf!ff r{ð8D.U 3~ XSOO嘥-e[w&~ H}֒{&Sqo&@u3&X%E!Ѹ?uKc^{kWƻ^~cѨ;.eeιda$Q:Y 2HwG=FGCg)fgSN/rz&VTB4-#,Cs/KcO hwubA1d9?EmMM,7Ŧ -a؁.Xn á6kTk.yn 4ϊ{Vj)^),MrDFۛ6ߠŔn=u}AP,4,I L&; =XmթUZztT.:ieiNrǡ{u#tÔR±rcKō(yg5 GLikAlW+v:k@8:jS.4M[54򄧌*p _WǑ>uִ.|;)vI͎6W(-ZՆ͇pU[w>- NޡSK\vj#>:9viӡiA{>xi7)weYߛ#J66ݻm 9/jkgl#&DԪ e66#i(P-0đ-rG}7ۯCCux.p͗\FLm:ʬݭnRz<+5yɕPE@o_aE&ˉ k-wULՋ̆!.2y+uxѥRYc{^=1uJ5珅nQL˫*y",pwhB?JӰO5@G ;p+G1TZ jdx+HU&m6ı+h#YmJUi*gH/2k)gc1yvUTEA=/!XHt&Žym7C[Zj]TYSFGKTn\'X~RD(kF.jďOI%B0ndmz+[iutb_Z-42 I9E:GRn< bOxNvm*-eE-=kdvuA5 XXou@:@k52]]tdT|)f9&v-$4ʜ)㨵6nqNu`Y:[×5L,}낌n!jMLke  }!Y sk_{aucIZ{Fq֪PgtU/QȺ2yt_R-%a=n9G엯X\ů cXp)2OES'*[6:؎ pJ3|OvQ_l޾:?Zy楎hc7Fa~5vK.m)p/f;ҽmԽ%U<) Xh{t*"uFy2܊ ],xU5)UUPmw¸ݳnx V{V 5!'Iy4r MCT0>"om}; \s?<)~TpNIJGdEi 55Y`>Ay~5m˂5wǠ p1 VbI;m;+%o &3j k9p$ZLr,xqzت[_{^s讼weWՔ9eAN& M(3:ڕHu;mmYڪ|r0/T}="H2s$䶢P]78PZ;yYNA>Kǟی:~fDJmAՔFN{Dp76-pSf%. qsu2poE͚X"[̶"8 ^=B1]{zhR)"][~c_u{ d CٟIOWx^\;m#aw" qex.3i)R "jWhۿPE\({CD6^FDp9CM򩫵罾fl$H rUfo嫗UWVRB44[7WpWuىCUY=YEҼY\Dù=O]ѐU1DI kw "\I%tI+3YQIEFa3m3p\^~P6Zxˇ l' )TEN j::O|̆ZppmV4aJ\#a@crN୊cF#y6.7-5t^a\-vS0)uR{mj٪`=iÚUu'7ojj.yTQEJЁ1s7OLrHS*(ɠ*ں0*^YХXt큮%TTJ[z_J<0!kO]B==1T,oT %ocV&.W,fO1* ?1{-cL[(Q/MLF#`B0!F#Iߣ~FEAǤ촙:YyJjīU0EKؐB؛ ҤLFJt[7ؐVW}R*47KtۥI4!u-F6o;k[r e%X*ݩ6ֳ+(M:ZYfdR7p5lUTp$õm(I)7\`U3$ IsNk2C7xG&{U] "< #r^)`nuY{ Dd*"} {j[wL70ORF` & JSU]u0ok_q 6Mmg ú}== Sż9]orV刿c9ԋ ~(Ӎ#ejRbuାeQɗEʆIY%q(g_6cskۮ<⃪=i|}h[upJ }nB7-6E*.[yޑs5ǺSy _Y9e$ pWRAu(ELsr>29.ֆ3:W,r&i$w%bAS-&\? *oma\MUdqQWQ>W]FJTf-{B,'xht2.uc7 5rםkjãkm. R4_*Ѻ }q)G35m߮:5Kp){2>t1JR=FPH Aۧpq}:y J*b3IS DDroߑ`6ۧrl=n$Ω6iwŧҟ7Wxթ K}7MܝR΢ܞMfm2V/ȪicIb#pspG5hLm?$"O#dS5LSZX창QQ4U"GVUx"n9L)l})-k^bmk uJ83(m8MC*71??e]Π [HO$? }!oS6n~dzC \S̱(XK+GYL.տ}^ֱqU֒_qJ L|-E="FKƱvmw2*b*›HHԧHԤj8[@v&};WMKxI`4-opuZH07L7[R뤔C\.=4L2bzƾF̈*'U4ǜk)7]\[cC@)-|a$4(38X&V2AVokVf/h+ ܑD_g*\J2H(W䅚q &k~#N`lBCs`Mf#-=i¶:]L;hʎ 1@zszhT|"Z)$oKjm@m{ 1=?A7JVka*xµ5 bI*uMt}bz@FM^@$4WfSSLXX.h'U4QYt4i_34@;.xYqsRTkb E Mt،u%ERְЯv rSYRY TXlC2,v' aX2-:k}VLK8[+䇑9lXïf֚_ ;xX 3n-Is_bBߗ+.^B供78lna'g=:)U.-&l!ֳ?.q|.8<9fQUKV:p4ʪ.ZmwW> IE/Y-M"41Ӱ*P:rUwY64]U2s4r3krkuNm}i\U]~ KGlj"!z dyjӞ\WEF+`3S0'?5E61/%|+VbfWb쀒UFn=d h\GB v>c$år'r1ʅ:ACIMTbhBLvܱg|#[ܐ2;jqI|"⬷!ZꥦfTuWeVA]چ::1\A-} m%A+YnV#Q*PNoO\9 c #ZXoL( Fؘփ!Pn,fO1* ?1{-cL[(Q/MLF#`B0!F#Iߣ  Iei*unYE&a,k v^^2j=OU"UhO,-{k|X6'YRdX]‪yP 6qOt2r΃ k)lzM0JJMt*k@7;oa'fg^eKq^ib֥IF6:liۯ}2ҭDk EVՋ<|z#Ǘv%ܵ^{(KeQH?ٍ;eTưVYQwMka0t)f9APW=<U?iɽropK;=0wM+24IKreԧ%?O۠:A"UQP50Vet# tYkF7uCY>sWm9>jüaQO5$UU4emOOSϠjS[y /rwpզoᩃxSGp+0Rڬ/mb~t_E%UULԒʀIIg#QS\iZj"VQ6+}x0Ϧ)u:cCIסUIM?͝9.oR@M;b"U yZG TSGU  R4B IqG8h}}S?5Tp4Grӆ3}t`DTk<qX[(6Z; nG_$!_X)aev!k[mpm|U.UG[u8h/ XOZkrEM!XrHWQŔU:LD}V "HĈ$7#{>4+zIف豩dm/!!ub46m/{tbp4;)cxd'ӖNS#SKi*I(y$spk\u^X؅vyi2$&zJ5$LJ:ES1L/p up4(6\uD)sZ>e%bc^bRTMT=,ܗհ-칣IL>F)W)ɐGPf7c}=+}1El>FNςqgAxWLԲLn0 m}w=0g!v"HX:ΓP-0I"&tS#JgR N!pk5^ɉ3G5$D21E7$H=Om*66󴄭lK\r0fer^7 CI$+nuƝ7R{&~DV<"&fTVA`ܐaվL2 vcHM@]hQ9]$5"AEحɽņ.w=0 A9Z,io˭̰TXgƪv[穖w;qM*~0*~-Jj >&5Fh弐yH$AMao c x1 Q*X #}`$0X|]ր}Nސ LKV(˻߹Fæ!Ź ]u rO}@:5 :yYs3FkfT5YsM7Du,|n,$Sx ZOt. >wWw?'ɪxƊ,iyE;I#NG|o(PGR+u@:iQAir.Gٮ{_)Lrj"j]:+n/8n2S+EeTfC,Jn~J;u:<чmJB([M:^ \h%¾p :7S?$K$Af%{[qh%|6RP2]WmHȚ'*J#ܩI8i7&Sp麨<9XCI2qA:Fcة$]k`l-MCz&gSQ8SJ/dCMFVX $H =HԨ9N}q\xo5ϳ!'5γs*3Q2Ƈ\]%=K\S!ʕ]9v)|hsl=zz<2H!$E9GXIT"P<+H+0 lF<9ZIme0x;k*I{ᾟE^l]trIJK7,Fۋ67},h9o=5kqY9];o^ PSj,0N^h M&s|âJ`4qud(`0ZTU,\6PYԃ3h5$l x\s~3N+TH#Ys4ALs֏!X:ik*4 c IOc8b7I>> ̧'p}xEБӠ*D߾a-pqf|R6?.*R</XW.FO!é `qv}2߅@9!nOU44Ԕ*Q\ iQ`׎XFO\]:riM|#Q1 XeM\֥2_ 63לU>_F&7B4׮̸c'L\cq-Y"h"2\-bqt-[w.R%*-ߙǁDitR])zyȒ4#KyVڶk' pMEQƯXs7 =W")/cz[qQaX=OI%VB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`B\kR Yxd(X(,Fkcc6༽wUw]YJ~dTS \ۯ7 R>=O%=8/~@}Dehw^iuΜiQ@K$Ƨ@0ƨ#oƶ9{˪O5$s4kE!/vĻUMlm 3(ƪMNđ !̫FW7,(|Zs0EJBm \w$"@}4\,Rqkh1Mۦ,ePKu[*a*Yl#秠ki XRhgVVaUMoˇ^߻i3vUd5N6I@1>P.0f8جФ.o4{W: x)UTt ŴJH|'R5],MgHi*cuwf,Oox }2"V``RMǰ89W2U'jZ*$ͨT] ٮC_\ǝ&9p;D_6O->a<|d4},LP6*6ȿ6͙Kd$+>euJյPr&Y2.݀[n S_ԀN7=C yl4.n Ц(r69|g0sn8(ϳl4|A:H)g \uM#`)kN.ܠ5Lܭo>YaL($ۥ㊹~UDtCIXYLlM!5KB8C4LՏO<4R-)nTXyOE43]YXT"с+m9ʧU7E!S%fB,E]m^en 0d*> @$dΖ3Nm*áw? #Am āaU+uʐn ֮U; 8g ޞ4:CYZHIR@5ugK8 ?˙jZ+H3Oڭ.&E]l5J/ #ÚGU@ 43QU3Rt~ }M )glVT2MYl Isn {'MּeUFXdm(|gU}&(N3bT ӋNi$c}FzneLuF6L%h)Ā~[_kkǟ n3d>q[6 `u,j!IcQ_'}b.5#n<#^Ky9eXfK._KAm=4j>^ˢ ێlʘSK3M.$}(\_+){k_݇eJ04QgE׽REJ`S5cg6_ M:qhP@Al2MgWi|^z+QJ*`xk+ggV1C$k$/ysi4@ˀ@h!*I<8 륊5#Nof.t8#OzWPL'˸+el.h]2F؏R(w0S[n5\Pimgǹ]Vh'Q,CFꪤ@-e30H@ 3iN EU*955oSC+9Rm:5w67a~Fw'} LrM#FW,('OS;#%x̨k_*⊚Afvi4-e$;1>Ϙ6,$p^8ւV*j3|~b1H4WU¨:}:-)04 JNÜ\]M.e7NTF8TG_<:(:,$Uʭ&ٍ%=pk]B̺EʎLiSk63˒M&zSS4hZo{_݋]C3b"F׺T4 T_z岰oﵽ6TC^?pA  VTeuF)d k#)fx$C#%FГ}z̥ـ˩\2./~nq]1qD"Hr^)ZI}iTLGyRM7Ǐ8H"ĉRb F-MQ{c~J%$<:xƹA x_?G?JhҀE^chW=q11Iߊq^q `aiy4%/D 8J]-ty8 ĘV *ms^blL6y]Sjh'LAp} :.缶F>ڕ_DPK : V}; yń x_z@9OZ+cҴ|:[Н?~*akf[ϯq{ _ٔvi[J0v]aI6cNb$ >j_3^HX*+eva5*Oگ2AX=H3QiysPUEYI2#ne6e6uf8>}y&R- nz:54T(im Z`>c8B_ e(+gX={./zax3؛{|RƴʂyWUt,"ub6'bOcSo'*ቦ KE[*$C0L=DDXXp{MxiKbLjR=7-]}+V֤1CXyEev.Os EߖZ%Rf9nsG5>UycUWF̭CK)nO=rD65b<"sʊ+ZV:E 9y,=uJm谪18{/rGI]=gv2䅞*VzH5E+(No-T^餙%e!9aQDdkiAН{cx- ]SǺ9ݯ`q%Aɇ&M2fV H,rlN/MZ~ ίQݣ̭qj%l9=1KKUeJيߩ8>q҉'CxtLwC˔[3*ijݪhV70^ዀXR!츰bxWyۇE*3HPDżNuY,khFcK4@:RҬb0IK,)[U~a&:H=mSQjJBՑL7EMn bXnM}=f";i᪯ZfydIt auz,KL*5'J:Rew`.;uXS&Dxh~[E!Ǐ- D|eH8c?TT_%:8cLPy/)RFUH-b[H[l-Zv"xOA;_IJ9]%BGQM IBAuakMQr7`G Ni Q59iN (Qa'ƶ|tx$\? Q<Xd y1p,IׯیA\N.xt"MjT8eH ېo ;hQlGR iYJk8D$؝ 49ĦI/Nh姠RܐHM~cuLJQ`:rQr.ITd(:jN7\:V(C/mc.tkguIU3TRESF9쒙7)n 72Z:nqvOSQ~XE5HͲ3yWh]qHO[@X|{1L"kxi2Ew|&GVܻ-jIMJW1Aw/|Z懊:F|  by[ᥠ5#HbΫ?{q)b{igOUuZF~J52+]2"ymzI4\C)"ʳJ :8)Ksc@ѫnBؐvz'iIwbSTI1<9oQZT5;SE#3@J6čd/R.=6CH`sL_sWCĴbj TS9)% }u&aI 5ߐW|HaH#m&O^)`ϯh[9LC4_Q7 9ˁ1.<.Ӯˍ҂c` s!H.ox[H٧pe:H RYX{-QO5Tck]Jø#$ x+%r~ 4\SW7"F~gai/sIܝׂWkOuzoƮr (aFܮi@6 uB0"'sٴVf_9_:-k^Zmq(!6Ģaqw]k{u,;oA J1 RC_\؅7S'Og6~U8^^vcݟgU~o?'`F!zo zd`B0!F#`B0!OIxK׌.SHFǭދ|?ī;EgԘK ".;A \hj dfPLkJak6}ZC!ۮT95\'nɍT $h2* `l[s:tckaKp/UV| p**"eP@[m\KM&'e2 "Z*J*YRYuk&Z'Pz6L\ZYk7#ӧe&Sl0Z~=λCS#Q4+k5nV溢Zg^qurE&EWd>'Gѿ|`5F!zыn{zYr0$_D*hPqo]-6u7s* 7"R.Gdb$Ok*F 6h, J 2Sᥞbi=mođbĉT?8BE]YF<\oܝ؋wMdEAXWEpTTپeI G[S R0eU rs9-l mBE4eL)*2\"<4JZOS$cn|D6ڔ,n;2C۶ykeOu)TJi_W-8O2,ڙTc"s9y4Kng>]I!R8x㣄-@l|uㄩ}_#.]jo 3R) 4uX1},NǶhԢ3$z =\!nEZX dԖ׹Ƕ*{iyPk,s G(!?a"?\\8jxa8X>[XkE" 6ӯQ*>+i)#b< &\UI[NW=4VW5,J$*mL[I#`6˜mvw,I=Ә\s4kx≯kyʬR5(aՉaǜFdkYozXIsv| (ZN5jJih(kwY ۇZ0H<#{M{*<4gUo fgYt3Fңrt{[gfThfl*ji(,CA]}7=CS-y28p{7Q㧡BA׳[}'}&(9o7^Pe1>BeXC=:%v}z[|%1;AFG}m:=ߏZy0s7 OrlNlz[^$*CIEjri- k {߷/,sZ8.s5O*HZ!$ܸȸ+~ㆩ33RAS7QUW,) k*yI=- /h1LoɌyNaPHLPq6 7kHa)Q~S9bU6QRMd44{C)؛X&B v kQF8(0$ghvLmyKGX?: i[@'}]6vzn]9up}V_rOL%ne,ܱbJK[Ʋ)ԞI3ײj>x҈OXb; 6&Q7bkG4u蹣@fg_KhrSQ 2Yud?N~)bl2RiOYO*zZ# hxF]&UTF`4"܀54鶣?fR֌-u -Dp;be񱸽ڤC1iXdv*aPӈ JY$$U#p>qlÅ@dFeMd8SSMILVC;wks@Kd צ>ɚ/;Po⏛=ly 2 [7PunȦyj)<#Z<֓',jJ%y*cePF*\ #}^H 0/活?YS|uMcl;8J|J5.`RGu95׽ٸ}:.28G>kj֚d y+EGfY/Tgtԃ(ZҶBU_{ZS &Dz/&H\g.5:9!K[v4B y3e3\U4pz\v#:]eQ?)܏i"AF`r]@fݵ)$p,;Q-.*4껕'͒K=]=bJŌ(kRzf/t OebC@ڈ"!?H{uƆ(7:o,QsYCV <ޢ bhK ^vc'SJ]HotQ2">qע~1p}?pVLa_F0F^]>_QwaTC%U+8ӆ8AjF P .}`5 [߁Siw/kn-?~r@jLxU{Q5Bpq{j;AtYR((5c1 766$l6:촹~wٞi(ՈNr@ĀPH'V0lM T >B qVq2yY̢Ko{-sn XM~yEcpZ礕+>ޚ25+. kq ߟ^/IɆAڧ]Qf֊q}l;X:dE^H e Eѡuf%(Mt=6 0kO s_T۫ 2/ vZYLT2&ܐm~}WҩUdT^m:؀v7߾!},FJ˛a.M  ]_R5K1JB;|\AُCA3d 4psk{RA0VunYXsqNTd+%5ʩi5oS_;(}$ cI0#.ck^?.s:ih Yl0 ak&XYE6WbiP N׃dFuiYҚw, $kV;5ȫUKb3(s>+:J t|yfM(itHTmC#ߚ'>'/ZS4D!E=eDՅ*th'E,"<76I;ʀ%寎~ !H[nOMmw8(ۗ4S$?U\H'LyG@ ُk:{.W6jf peL: u7U'Pcw!#w;`?=|iԬ{NSkgUx\Zg5=sMwX:2EfA ø;"^js>4z^GZ)jE"Uu\:`A.c,c]>VN9‰ ּWh"i$$z\^!fuSN |4QA_TTλmbT ;Tfdq! &=µS5_f\]V%rKŘj+<à63EUATHC} vw m-:UGgYMLCSPO\C*mE :Us출5:߯U)'˽74T;1좵LhXwrv zZ*6dX*)auo5x+(('JE#({7QpK':呰ؗU%M|XiS(`Ǡ$c84f&ʨu#I4q!]ǔ wO]7Hӏg<7\E3TYKHYME5@}LYJjںuHH~n髉k^ӢFY?U'L*;dtGN . q+ IN~d.MZt|7[s_D4lTgu4:Wi\ 6 ˧k } Sbf0e!jl3՞6:Q80 Si"G ml.~# :9iy k .Ooa#Tq+v_(EO %0a7TAS"DCmwcؠ0ai>?qP&(!ȈTstUhbN~FT-LJ:U]icKa`omb>mL=1&'b'X뮛O'zx%&AQ+SfytfIZ*WRa}Dl 'ns< C՗up8]Sx'3'fTe >2򲀏,bF(hef\9?W<ޏO[4,B>pnv6kcbgeL')2]y=sqTW;,@x e,1WT# jFQ 齭7ק%]_X ?\"O),- 7@:u'8wf"n&nGu0* JYE"2U20WlkX:Dz GSՕ#&A 4\+~lc˞5? a&nu닝IsѻX`pk]PLs8%qRHh$u3X~oLt LEc^*¥'O;=༷1s0Eajb.&zd8~[TkkDuՔiPM;UԪfZtrnnlʻ۾^61 _/u3=S*j`0I2SGN5^P H{$fI{-{ǮKeT\o uP'8 b$w\A<wSpH:}z:;'>=:WbAklzv鳒.\6*ZhnV]:bKX[>tDricY H벣nSom]<m)mk-L#SO=M+"[Kv-{[a>τ|J.jL4!t eձ"ԍb}Ɋf6k_DtE:1¨EabnHPگb ԎpI:/eR NM82ͨ2`4$jX@IN;FZ.Y9{e #O2p&i0[]:ghdj%N@:>;o)`x++L(҈* ;zfbCXyہb)1 O?En5G4c0♷Du+Q5|1rU3Qu &]]'pKOB<%iᱝPڂz+Eng]촍a;SYl$I-% ^Vc=W5GTAO]I9P˫H;2+kV11m@0}>\Ik,xˠ82S[RԊZHco#{a\el԰mKbFԪ`pxˍJtG_q?em=M\BZ2HUI}#s{tڌ{O.>}ǃW,J1E[\F"ޝqZێQ+p_>x 1UL&\U %:w`;,~#eljR $1ZP+.RE{L0ˍ>ߟ}R_ }CRQ+:E6պa:H43[ȾL6\_qA(d @"^p҆^}^Ղ_d:Σ[} 큔fvP(gYg0USgѪ ,$2%7S{q{0p m"Ru@h/7:^$(·8*X'2I!Y.Skvf' q׾ˬ|-ȩ\ {T2Q߮lhRÊH>7 dv =9O1|I= }x}!lFieY>^k`lhFzԠ1 _MIFQ*2f3=uU+OQ'ͰP#Co6km(ˇchȾA|SIuj9qcEz j?h^iޢF z{9_] pi:\F-ؗW n71 sMU"C$eA})~oo{cGۮ: xE{x&1-]>]F@$6ߦa0ܢtNb\dEt-`ܥJ7e*F<m;2͸s<^jT;ɥUU[pMɾtў0K70) ha")f".G|qJxR-"Dz[uQi :ZD,52vc*; u|S.lǍiWyYS LN$o":_6[c e 6Alu76-9.~?4qEa̢4Bꐝ) wofcg-@4jh驙H7RU1єX*\dB k=23o6.$6r .wPAlOZkҖ8*{nL.~WNz)(1pKJ@&ʢ~GIIex#ַgf;5ɛvʒSUN9UTbut.l)fsc(qXNx'q^s@u- `H 0gyjg tl.3uW)ȼY4 `;^Fxj!Mgr#SsGK XrI;D`C̺;:m x'VœP׭UZqTS0âi4$[v lk{42>6uۏas)->GR-}}2$M.LN}]ڵkenQ@R,wh9q8C:0,lGm>yDL2 q SINBak\wM@T2yzϕ Wfxk*iy3cz]30ܒT7܎TUE痷1᮪\U1_ASC5i$(99K=,pȽXm QŘI)Ɯf if4Z`"؊51DglBw3ȚsM"6fHnFD~IdF"xkH|1WpSTeѫiH r H;tGNAmAg2 9x3%:$<-U,WOpU0i.Yp; Ϳ, |) 1"aG9 pH k<ǜlasmĶܟ\h&V'M'LB!";c kSr4E}!lqc*F,mЃkɿр(:J=OI%TB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`B  :\*c ߵQI4_:J=M!DUI%&{o^yΓf[863;^`lT7s{Tph{R#4.(-,)OVܹi=$ԁq>ޓU"WূޥDlCrō7ͱk9l P L´SW<k?Wuѽ#U x[ͫ))irfB\{{/} }:{] #H$ƘC)*>ۡKHd5QfT;,'{vsi-ya,bȟ=2R=L1sΐonK<kbOp]^Y%TuA/Z7e"nbWVݔ_(#3qT5)0BZfWx`6[䴬Ci㠍ٙU#Q`=1ULˏZ|Ú-UTdɕe '$u.ֱ\z:±˸kcUrJpV YmGN~[c^ݿ8\SZvhqlJZ}Xd&^*n=w8bǃUEUΒFDkF\]]?UNW >j/mղ,;a+kc!Ŧ.\'4|CQKQh!Y./v?fZ:LPFꆷp.'.wx;w.=/[P?IV\˫ Hb:T o׸޺e[[l5{ s[*`Ӭ7F{h 7(&YY _w5!LZ:Z 1媅J\UXapa$y.0l+S-M2yP؃QW'k/eia^+f)i8ZW1O)ؐgBGm؞ĜyFc8>dovxׅ=Ym554aiQb6 }.{0Xwq:NAp.%M T jz-"3)R,:]3Di׍Xsˉ z(j,EӷU}pRiZk?5K50"J([vm" q^"33 ?Uu7hSg˪r:~ZD@WjWk/TpkAl3zYxvc 1$kFѬ1;dJO< BE*ũ;Oo%P4 R3iOi0a{C7v늰2di^4ӺNYI-o-ئdžQ^j6fr`xXPH49Va/b:tp|}W~-1-hєmB?*ڍiW5M\eJŊoR2VR#km% 0ZYlm=D )f(jӻC}>:0KH2omth\]b4MmX!h3Y{\{[SmU }{pZleְ;*;MJatR:v:,4vIy-.E_uiH0::囝+X-=m=#UůO2Qז:sυFW Qko=] gOfy`1AA4l²uT l _{_ VpפtԈ˪teB5phj$hQ)]Hǯǥ+F z wDۇeUuZ~*zpAM,1ΌƽjAh[cL." (Է 1*zmr-v/>B{(,$t, K4m@/5\XBGC[8^;p]$1U0ɩyzؒ}Ǯ 8"YZ3/dhu"A%3k!n, sӦ{nnci֩LRq;Eo-p$dŶ_sR\rX37k9f ;I8[= SzjI7GMQ#V,5m6k>۟ku-H4CZj,_,s|0,|j@7l1Vبُ4n]Zyr,7˚y1<ʄ ͻo%Fs@xh/|Rd'9E9"9$5O~`:9 ,? 'rN)Xke~y1U4ҙ3R d {]lTuX$r(iT2`; kfΙ< ߗԩq饯3-%](qn^*8c-?+^DJ%$\A„>pAi~cܕ  YZeBfCA &\d>f .&in K=,u=IɼhBPmIYͭX D1 C-/ِ&BUWeFgI-\BdZIEW3Yǔi1pkn/> Nm[U5, <9 AsuZ팪=T+2x yM c;#>hY˧RjVsqI%Dd>oH.!G1?ʽIaqM;zN*r^|RCL2O HB@mv;^QiKc3ƾ^T΂tuqVcJC1.H,w?K~E0aϏ$鹭%>KPx}44{F\J mNVO(bbAI"H>Nd4^dWpjbe "r #]9aX[\#q.e&4UdcoQTZm[#4aXSƦ൮ ul^_TxSf|?rĹA  q s\X5ֱa^\#S(h´gTYT9\)Vg)>m̀XV#Nz*SkͿu'|d9FIɘA]E%T֔5X"V27$re&5۝"8Z$r-^[H鎨fkdq$.|,A@yus9Z7 Bp>SHlkg[HdyiBd$6H翯^g ɓ uAceQ:‰yu3D[Ps#XD=xn(&vQf&%C׶2䶉\εH%`[scS;hMՠ@kGIGNb$#;u+:(VFF,@>DIl)~`%edI.?Am=*0 k`G*U<Q1VbĒzc3:.ƘWfDۣ]Imb0Qs*+u73+=oPPO?Kr4+{r;cn5wĉ kDZULL5:D?a)LªC@vSg!2 c|>*u&E:O9H\+?0I6\TYHЕԯ- AF׽qV u[\inO egv?]U~I 2Q{ {[tYL<qqhߑ[%|Ӛ: {$~*Fo#r֑7'ِ=Fixg>(Iگľ9s>ERFu2^?,ʸv.cbCWsnV^-"$1ΛBҨIT%v!Lˇ熞ȆkAecl1樓 kgyi"Ҥ41JUܰ]&LiVAKy2ҺO g~Sym|Q_:'cbk3,ZaB?6*f̣*iPmf{S@>c{+}~w߲GYD,S'7b2j9ηޱma.rE1Uԫ=ibI#nĝ:(^^9R^}hs,)Ics{ Zk7 M,fO0*F//g?h13?CV?t͏C0]lG720!F#`B0!Zk?'c~_#|J 8:NrZ$P \\u5id"Gvgx{l:xLCVyK>eӨ .[ZqYC[# 81(>lSq7'-Y3i9˥X e'Kv5Ex&oˣ_9ugNUkI_!d%obE:g=3R>[ߌ+ඦJ^Ҳ*+3 * "KSUkm=s{n+ KΙ|Y^/&0uLyle3:列ͨ }nnʧZ*`S0kJ<$6؛7l;M#JA5LS+Aҏ<05MD9a@!N޸`1y^Oa8$I_>(+gf/,Ud=%oJ@A;03]qp| &jyya-"n/o|nciey/u1SDյT$i9 u7Eg4 pW klzZ?@4A>ad q/@ ψqM[4P5Yʪ@=Rup[(9r< \'qP nFfw\.t#$0S (7Dw\yAEf>ZAڠH*6DG<,o׾)s)1>es[e'TR.jj>ZtYH* ^ͦ;|ewLk &m:jTXUHD;;|=2AoxEay-Y3PA+ ҁ *K; EF-$ )B]O2ܱ96R45Ճ͕fIX/ߋ[ٛpLUE;1SO+w 7T2mQHJ[2/Kʌ xl}AVAWx7DnJHM,k .I q.;)jS9\4Uw䦬Hっm%ֵN_ۆy&i裪sjB9@ н-cn~"?PA6TVJ]B_aLK Nf^Ibn{P{#+YRTq#HCGo}߷3+bip 3OğFyev0P5GR 4?Uܐ6qJ=w n Ƌ+RiDf=Sڼ,F1lEY,{E)T-vX>d:zJz*!y%#=ۡ⚸jms>s晤 >Enl&"-2y4Z6ZkR ݛS.>I3 n>yR(aq(gH{yJHӹmaWu(-~{}AkSɫxbPk/nླྀppc@뺴RvZ+,Z@˗S9Knz{'8*33odAP^fwU!QIKLʰ4:@`o!Qp V{~I< L2O䣚TԵY)`PAnOA}ED"Lc}Tre1ě^X]>mتx6Z(E\Ԛ^iFBJ]W;5R5fe$omhL}.p{QAS euBV:@-b:oal6ƷrM m9\ ⊭'U_V@ ۧqWFF x'EZLqTiD/bnH [ n=A鹮i{)dtӯIpl(kj X]ъ \߯jÞ$0q񱙀g{P@GdSǗO"2@u ~Gq#ǫxŸ34f0r"AfUP-pav)PD9+؈U.=xbEy ^h$OT"`I>[aMR) א4W|e5+ Q|MĈe˶OJԢ.7*k:i:֦(If~S: }& dDƦbZTYfSjVnZ!+j R}@qKE )jx3i;S1`@ [inA7,}K6UY"G̑9fIn/LhM|l3e|:jd4Zʻ)nډ?Wl)OZug>^}s [®y,40HiKro{{/9i>q ƱkfMOe_G8U^ wf.+cXUEıMɵTI@P 4Ulz гj>{ |1A/,u.|8*2\Q7-yWՀMRRkQ6Baͦ;~dٜT)J UZ>zͪ#Xe>2f>絔Y\pnm U>0d-Nk>,qkn7D]2g˲s4Nu5V$\mpAk$"6VoSgRa)4y1M-aFly~1ME+";lKf=6n48Xr?[3R%2Nf#{l{ַ Pk)<6 O-Tpb|3x+'8RKL0-#_IltlZa?EaM߫SfT1g4Ɔ^@e z6i V&3c5Bi޶W SB8 &: 6 ~HO)ʲȏ@=cU#Ѐv689pG! mzBX!5gC]M'~sn[R)\< yWCHeIFSX.XX6+mA$=>z)l"z RI&9]n4|p{`\75&.K\)Y>^%3 ZL'w!%vrlH6P/`b^GoAc[#܋E4SG nSE`6M'G+UoLעTm^jK8YMXB~mb\փDp Z5YeEkT0Xta&HMSK{@hSCSpA1ͥZYaR0kJl/LAƇ[uo5wb PT FD=}3&\/粩فO^OJƘpSabuP4STY F_~+77YR$m>eMl/\:}*d ଒} M;QSڀO[_d3WIejr-eԂi=;VG).%2Js1\n9yEꠛj >y[_XRb|٠d2;D¶EynK] H44ܶXɸ˹`w7U:,A:~1ɩ68j[RaXm{ܟG궖^$'ȥ1T~\h08I1Y[S:FcjYWI˰RFKB't{<9Lޞ*aH1kZotk~._<]RlIT[fTI$RݚY6 7rocmG//ꌭiKG -$,4R-.ַac7*9EB47xXSijy;jY܁}FF9f@qZ 92*h 4X0$XXnp':2L>niWTD;ؼ߅h(hN]\,oEKJn%fq'f< PjEN(4 P-bMTC893}2.m֩&[)˨ĩ ?M6`pwqGN!6lȚ*yijeNnpEg;'51ih E!8uV R%j"jHl ˽F$|=n- R`ulκIr%uҒX۶7hS4;Q:,%b]Y7[jkNe[Kη0ȿYnY}[{|3PU`JM^LPƠ$P|=~nlՓfUQ>CKMWD$܍׶4*wkFzodR˝xg98k*9n U]dA"0ۮ(ϱs2:MZ&y릥r[m`G}s ;ƈo湪K9}p#HdXG6[~p T+ ̖RicDZߧy6WLkNhz;* HK`oᣦ؁! nOk+Ō#sLmsI)]t;_ aQ=3agknWmqMqAdCCX "FߚLU:BJ9ma=~Sqt#ָEӪƪB:4Ƅ [o7a[T]6i.i'wZt`[lon6TkA&H}r~UJ %xd=( -oi\I J-SK5B ͯs߷O>6{Stjj/b5LlEm zsI0|emru{DwqH'8竩YQ{,doKq)7,.l\I4^o:6 ^P qp|56cE,⭡eWf=DF "Am,k1/ s/[,\ĈLmo_{7 MߠuR<2oo/5zr/eh d{؟NPm"sx cŽStT6oԭb92q<%D`4mssQpokEQ;y9u߂3gɳ)ZI#U`@ssr߯C1M[rBg7W.(β\ÔT~.b)"먋l-l.{>:TĒISd7+ckNZN*o6RFdHLn5N/צꆖ@ vޜ~Re7tM`2̰FHa,o[ b\kd@!X1-.s6QjuR5 dt6M}ؖUI`o>_u& KφF ^QUUUԶW*7=NWSv-J{\S]n&NFt[ } 1 EhbXI҇B:^ŮnoiRC=&̟,wϓmAHd-F%`Pqr_V0)8LutGQINfydt.V h$c e-mA][9j7Ͼtykmկw8ۗEA,I3y$\1T\w{?^*6&GM =u}T5\OC#I`N^V,vS#$XrYvg"7.gFs6uTVǙ|1RdYs `WPD|7v8ϣH-hb Heum>YKKuIw13hg[׳u궓Mg:PͅEmxB"0G M5k 6<= ėb Xf@RL']GcAW ,ʨiRMBTT*4{4b1xfh:SpYo$ƳuMŴmeLPOVIurڬ-\>Pg CíWZ!tܫ_c㞝3G0蛎mØ!y}ʧ&⏀%5D2;9JUnHmžNB`okCq^Gp\WU<=U4_=)(51`Ƚ:uwמYZ{ĸjO6PBCEQ)o5ـ!\APQfAma r@ĝTn yz1;!i]4s[oOyg6~U'8^^vcݟgU~o?'`F!zo zd`B0!F#`B0!O?8C'+_MI+%qS㲷9mN9"*$zm6kwcY]-Ae2"@r1I8UY!cvP tle+X-Dh"!rKmn x)n&'LiT>RL ;nZD)\)AC%E tUN| G2͡}v=b]AyIfieTP+SN)^:`j܀w/ o& ;Dbq}J~_]úz@K h=q5e85<)xUl3.<-6M󁢖0ܛok^ƿ 2Bs~2Z\80&|"jf(TrHPժH=])L:#u=t\ :*x!UZT)y4~(MGLx<{׺DzG-aԍqU6S6i>:JjҢ1Ne&( |zl+W$Pv<DӥSE (!p:@-H"d"e7(x0<15rzJ|KC缪`sm(E%91Rd$ڬ.tk"C]!=,zJ'Zf/OX*[5/6Ԩڌi pSn"sLҜ2cr%} i܋ZSI}}ӚfSjyR<*#-QEb`? ߍkZK"s 56#f jGhZ9%&.P114[l8ΑJ@jE!E7ƍ&4R%:/F9%W]a~|2ۥwOslΒ jwFdUiI ݍl=/t4 7C f0zUG%c۱8Ѧ˦ gh;L%n$somG__q:'d++ۨC(l$)v;͑ҠciyXF%v`2u]ȌCTʲNS=7=^McIkv?5Kˀ0!o@CߖQ׉0S>7M4RU:Kr@bM]K6ߦ1},=.5! j#u5D@$XU[Pq$]Z^i W^!f#- P6#rO^ش^H3y*vrN iO6$046@~ƪq2udu"1cql7KI緇^6ӺeZ*YꝮHJ{a ]7Ҫӣ SFj::KftfiAAO#TR_*VHQ'wX@UK)5ΩV2[K/d@$~oY赕 LDMҸjMXvx6<'KڬҥjE;:ڥ-}_Psq{ x 6.8_<׬DK3(+8zx򌖹 34sFv]q:XB؂\l_1]R $--bdZ&jWM( mAIƕZ$yҳYK3^&8ࠣj_HmW (;`IQ4=t$s5Y=5e3Mv:zZU=HuU-q-ll)gZT2,aU( 4ܑ9:t$ǂ{(HY3-Є^Ukz[OzrsϚEoci5+Je5P9ҼîwmzbZNYÚn4Y6+-\b7?߆0y]́ o:˗4K*%,B78z6PՕVSQISQYWV7+ u|:bTR4Cm~<46u{pmO,GsSV}3,u:$K|8+ݕYTEԕ7XoҺ@vls1b ߊ kA[j閝ۚ܄VapvܑqMRLnÐW8aKT85m \mxJyWjIlDvX aۭԒ`I:&O5:"ۧK܌Z\ WT&B~Do~a+ȋ&!uM.UM*5Rf ]UA9ث@r $b3zhf1#e}.U%Nu,GyC%^:nZm+NKjxyԜ.+,jE=DpJ^&3]L9dc_s۶; p+E* HzmۨŽ:8*!Ԋ"{ts6]aL!P "&A*Ю9e6V3OTXˣ^t򷛧ɱnjgxN^bt^Zʕ^&e U D23*G.aN^۾6'U_2&LeM[EڴVb6>1M̒iچ$Bt#nN@8~ASK GT3,4eS)pr !eI X jrGH&$kS[e^˔xW{}VU,0dWI^x^õ(Ssg k|tjjHgYݘG~+uV7Neb U_۽ۘ~Tsd}.95'VeQy$M(&ޗū*_"ьѧN| os~KYhӫ֘hD" >o0#_Q)T9ma.>i[B=dqƲMb#Bt, :\4ָOǜ$Qyss yUN0ޕ!8c`w;":n2+071γx'aR39{c%iQ4Gz6Ez(#fO?ugsUʨ)k$UpG*#g8u @}p!j`XKp-"O /ܰ$ӟ182i̭Mh:,ϊXCMiǚM{:6`~Y*XBab;JzeϪo}ym.8 OrZ)'t0Dl i@z졯FiCCN.MZH*Zi2jkjV;Qnzۏ}|$(O)&=y5y=U-*H&@[9C6z)͌׵!Yj n$37.K_eGum}'܃|9uKE{mE&O-dOCD)HC+,N\ܒsp0p]UyJwASIjb鋱v\H>]nu+Kcӌʪ&VG@\Hsr7<<.3\̋3eNA@2ggG(.I Eq1TQxITsHPGƐIP쬥[pun_8OZ=5RxT}LuTEjREJaum_[?IGOsJhH(i@1l/р>]ob½hχlaj8&QUGI9c% 67b >c0l}kFX1"cNTQyc^ěn2 yTj'0hi%J];=ڕ^}HCۑ6:(\SU(@mNnϭR:-9TKBƎyNq|qUh!w{1|fЮ1W'xNL(:jSƨ UwͤlV^ǻ޴ y-%GwU|&9C2s B9GK*omni-,ŷ;}sqj8usX=JU> oRg_Ah5̏}D({mr./qb1J.y%-mmǯ5\$JmQ(1 4`X·uYym{mȷ3EO"JI%f,*>VU {L/_UEFٺEO =iWFuLHY 7oT7HtS ʫ9? 0nC cG\8f1W(c qi@x!TCƇeQJ,ύj6qMZZX+?:$39;V5LCig`*euתZ޲ Z"sWBI݉mME.(Ի\>( չ]'etEFdzdT/a]anlTs_>?zu)*(1rPXr-x=6ZE}QTSk^_%@וhuH"*#HmЪփߑ>i Z:sjjYvRETɷ|naY2Ixtz SIK S4b~68f7<xPPe=+>*3j #b/G[S66;lCOևp㽆&:I)^5Q!WvVA`=:t7=Hi%(n~lraY ͻMq,UyU[?Dˈ 䑋8~oL]G)!jjLq5kmP-ko uHBqG5\G6;^m|YUeAV O, JIXGKܛfddYtfK*)牜'WSoGbqUFw2`{sWS u+M6R9fV=M/`,vxAu>j̼#Y}W Ml,4OGŮV斻.WAª $(aFqǍ7O)9ԁid}RLBm-N0:qe*{8 JZeD(n@u3DG>D^}-leYdU٥%\K4O!(Y lSjgi[o_T,-әLk2SYYY?܃PS )9\qd]T򪤰zH4d7bMyH5_xI7NӠZ_r~Q1S.c-H`ei!@ulUIո$b+V/| ߮~'Ezry Ե{FJ+ KKJxrI>0 &e=,yyFu}6cﺛtR&ko)Z"K>6Xժǒ㦑ׂӧ7PKUP9N"i O6 b>|#*#^"ȸ/7*s*PIE֓Hx_Jmf&}R47pHnWv&ZgʅKBLqRJl$Sa}@c= t;S.zeL B@y ܋c=n@:>uNu bQBb9.%V-ϖ&]Q"$\kq׭(iE3V*DiEU>aTR$ F-oхX^ڧ7P eXAMHWUlE3Jz\@.Y TDv7ءhy) ąE :70s[aSiVR| j(xRI k;+v ]7Yb@7 k&ŠQs;*IESBŕZ+)$Rk0{p݇q}3?=W5h<8 ˦Ͳ᪦9i&vf}GC5k_"<6&Vi9_/ ؚVL#]xʏ%P*1^ Eu:9H -sEf*G>2;kݼڭq7;ngR59AepQQtmJ,~w\tv]$Kț>38{7Jk~R]Z:0+as0OZNY'L')əanZJY{\Ap:T)HmYɩdi5sʬ?A)tSֺoՠꌬ7e8ͪC:~[Y6)jzɄ ;XmV Hc/M4K k[-<"2Tj$yop nkz[sT.cxT2?_h4RGX6҃~R'0ϚϥI~b5ⳚyޅIh)vKk-p{/s~9mdѫ9ohv!91m>{^x&Y<rԬDT# X-VNDO\=yiը&X<%ƙWUd* e!ᯤv0T갇4AoηZr 4 d ⦳2IUV.q cSEm_[:$1oZPgu6n>kԪR?HXͦP8'4K[C_. P"*OĐ6{;)w4":7M?Xrbnqeq!XHQH(X$oc-rp,3{;Q.1km̟V* X ҕRaEzN7(m8k9\ *gP#w ]k뎱i<\a)I[W3 x , X _irw۞鱗<8\!955=^e?,Ia@*5uX'0gMF.&d됷3ŭx8i2sܲZjZwgVIe"-ag5*S㦢te*àNxh"gNB.-mE-{|SNqIX_>EHd 7Ǡoik_US6.CIroalS;KdM ʏksGUʪ<ȗ@؃kۦ4@hu΃#,*P*'Wm[/չtɦ q8Q E7^s.Jj;2TC4l@MQۿ92[UlG߃ 2,2oGvĶbD"oO$٥]Cr5v"Q$ R9BRFBJ g^UXQ"{_M}ePse5Q䖠lDLJ(Ne|V(wm&&+]U-d_[X\wzbLS!_^jLŔ"!4>>?N6qISq/UA#Օ ,\Va[ 3K@M*npNr668+Qx-536mF CA%>{<)`Ť@Ka|?Y xq\}ikQaj*%J³AksMX14*UӮZnCÔ5y.],D-1$ ak63i9$2?k{ghɮ%֏F-\2c ?ú0ME{.|KI|-0PpSK@v6aq q7 |98aÚ\x< kjr4r[#qT0!|iW!|bem%.,X6=oq, ҽer9j5+d*ͮ7~mU; :1rs3:*^nd0WX Jǚ4~ zRK me+~jJ`,moP͎ؑ ި{bOU L05K$gljijI 4\z}ON6 !Eف NI媞FuSQ/-ŏ(?Iznv{+R4 SI89>#hJ)ytGxNFe(ts~ z;梻H}ِvOUY)zI9.C*j[ȵ'Ya9P WM2MTT4$+yArͺ9 @4\ݯ"y[oC?_Jxi΄%QT[UCtUVuͤX.hxQAssX=/9F>M$|c)t-lRk;fCxmB$CfQyҩKk kqF)L ᱟ%41)x):fb45_[";\m6"ֳ*42cclv-<'}OEGƏTFԖb|FP[bEN!wׇ[ӄEc?R9kWĴt5uLfXGM X2Y!wa0f'4I,'OEUP}bUSqRz#ذkl?}|j&Auo%UЋ'SS2cYvSnI0r~|V yۇ?iʪ`"+^Qax:)0$mypiVZ(a idpv'6!f$c:𸾑ԧX9\ÆsQO&^Ԩve"6WbK0XБӱlaeV-u߯ SSUOTXܬsnv|cQ_O֪Ny,ԙRHDhJY-AA$zX]Ӫ fYd,/*e'0HWU& >mz5O13>+8_MrF]g5Ga%i`%_>nC' /iXSC5[*tQ^,~j#k`4۠7߆5=cU[TI~_.z<9cB$H7yVHmbz^{e[M,'3 4 r ${w Ak'N ').] *jUZ u2U=Tw>uԮT0NyU &6vO#k~_EA6[ASVeKK[L3qLn{<vsX]-(Ds[ e=*rRxf _k_DU:050"|oOWOV~[҇lLz\QƜ;Vgqi:;QkI+AC7% x J+F,|Zۯl5K HQ=->* 0%ɖmPq fsMGIIZiNmFfFE»i66c^j$Iao KIkY_7~4Ezszig;Z}ko-,pyMZDXmqU4uVA$:J˒eiE å*-n,GVLat.ldʥ+30)' [}D([ bi c2XTawLNv⧦w"cGuJ:N0ϻ9^ {*8kAf9 $52ȡ)ԑ#y?m'3K9-e+>[6Hj2-1/%՝iFN3Ij:F;φ:ڃq)t;05K#A9h}وqUMJ"/~i-f od2r)#T֝徛ܑר$c*Ҧa0bfHӇJf@?fjT ]:MQ˳/qJeflAMUH$#DCK $?~YUhhI⡹ $=U5 %HTbIPA27UjE?>DUbXŨ5BmPtZָEoog:\O%2 *,׸ō]m<z '頎^+cBs<4Z?mFP67ilbנʅ$昋ߔxi iUGJUIjpt SkPOq}.ľ]K oTL6}d[J{6mM_O cȨbznEǾ/Q7b"wl__)1&xx%U)󺂱 P:jG`Eu0 Is+ߞ%AVyl]]*,L!) m6<|,+hYfSc [u_6NS&7M="GLúKYi (|exLӮ$,%UEVr j&b(a5P[MJ*?!.YaZMP ##RTpT[qөM"؏IXYSN׊[_ʰ6X~HccN}1ȂkrK/srU3F);yU$  6M*TlqX-FW6D5B_P 3O$ A]qOɁpNIKt_ OA-0*nt`rdNnpoccNi t1h`[crܾ**!xҪ6!U tE ==9d-AHg3] JhN_) x@ Las[\\mm/kZ]v _&2<莨؟8MX *2juRce_-%6IW$h^1܏{c/ u8&5&z2,%z:jVѼL 'o}15TJNVHBwamߥӋ{B*iV2ʑ{bH c ,)gE,$S^ùKw NuZ\ZU2 4#`[qs\L+9TK4UƮt:c}>Q)Ui9s"C#A۞oώCE\̱i!Lu 5~OuŰlH] 5xh ⊕[Qܮ7l:j9g-%EQ6bH{ܛV5lMEE H.DrNB~4j ߘvd%%IQG>]OJU `H/qooB-궓YV9E>SqwwWQE_%4vy[moLUDX\]vU #lM l8hTeFmK #`Aԓ}mcXstܠ"VJTVL=8VJE׶a*LgÄV}\HyeQdE&oئ>w;6ǿ.O+D&h2 +A\N;~ԫMxG^ `9A+K4)apAے,z֧n;x/rfySśKa hK GA';@Us?1 je43-=V{ b]͏uKI͝y=͖*+jcx+Jf(PI{\? K o64gV0*4J4O([) T\@&rZKfo,@ƪCEZ8"}:{wŴNHU:(Jk&p5jE Q׾5p`"+FA1ԋ-yffjVŘ)Q`Mc>Zb#D:\WpF暝$kM 0.>\1Bz !iH"Z^׹u>*tX9~˞KMgp#ex?ٴ J9`Bsc?4?nKx1=0?q xk#`B0!^/x*-Ϥx/*Uk#dy'5@I4) ''8.N|º jysZ?  `Smkr%XB-'TCp[Oy=cFS*ЊRI3j*0 -q ?qg3qO|49.`pG=$9u/:RMR:u9- W4"q;)V7k`Bir ۆꫠgHՑEe$؂;ŕ]!Nf 2?_x[P! 1_4tZM}{aYE9Fm] eސ,d[Ek`!TJjR)]I* w!u._Ԗ_,h!d_T*U̕IR9"mν,V9`$䜂וW3ڇDů#WMἦQ0]Z6E>)Sd>\-ggt-;|KV5dQ5Y ZElq'M MI#$a{Zb$‡i YĴ?ԍUk]>MIn'|X"5|7IV ar7+R"L+d60 3-LG2 lmJ-w`TIPߨƞغ `g.Ҧf[NiWRBGH-zu"wX9HӇR;N$-V=DEX0+m!EPO؆S o6uu`vS#tOB+#BYI^V6/+wI"]$U\TDfA;|rke-ss $i9mT+RU"xTHLm!i) m'P |:*e htT^[|_SB֕$0m;m9"[UkS#7CM D 3yǠYcZ`,1q-YQL9]+Y$ -k zv8MVa{$S# $qŔYE ,d_\cv[i/ƶN}9.\U,%l %,VPJo~5 e6VTҙWG eT/piEti[}Sx*gI\"2u>0Q!I~-?42T$6uUv ÒirCCn3l*juku*w"  PN?X6]}0֞V_)qUJ I4I'OݫCq\8e3bxMm;{Q@ ɕۨ\՞?ZHec} @`'X?lwSE,qQN7"1C%Alַ9ٵ}<᪆[[fQ 0$lYe%SUބ bT-L3Z|3\>#(!AiT_`mq nn&#b汓>9@|O:C!Fɰ`F׹s jcԎ_6N +(*-ؐ7߾/bC])TVŠ$}ϚjL%VYI5\\lk`<+7Ix' ՚]f_V2`ܰ[3i:l:ܯ1!'d3';N#"≲%ajn/)$ Os;l? ks6B!vRH,nXYmӮi[AWKB^Yb{M|lv& 6 sߝCq56*nQٛ%wdJFuWOnC2nc|Jf8898m|QEgfehIUmh #J^c<4i`CEy&MQV:(8$ͳ.''-xl,.%Gnmlj|96&O)S9y_Q6s]RΆ-#6V y»gTԈg|U˔ xkMU MTNG.f lz,y{it'-Ϙd:uAeV9jRXQ:t",OXÃ\ܴ]NZ`g75̺I]m}vÔK [eEc؆@QT&_͓TzEFyf@7,z;b0x˺ p"7ࣩc*9Ɵ"<'kߦ9ҟgogK2  ?|X~|m\9ϺoQZ]1I6 Zݬ~Î4Am&ʪkQ͒.o^u478^`옦47h|U.R4,3Nگ<߈keuC F{*dC"HexK\27qCpp/͚Űh+&IE_VTSO8-Yciֆ J듔s?ERj:4#0-n=:c ib) \8 *o[p+ͼ!򤫛iiy)ZbRn/tX:$UX"*1U(-U+M~Qq/ /My.TeuVC$9TXCг07`ǀ5iT9DAlԾH bV̵gY$=5_)!-Y/}@ Joc0Z1k"iNL*Dy?(I؛_\Q#\odPb qY|Êys$B#QGDevyYDwb%7UÒ M@|x;;;[7R6KqNI*F*+rFR #'\6M\sN!<* '(EO\*}4[_Vh1&[Ԧ㻧Ğ|zz5(S"@='wV@¬0.jaPg 7= E7tsmEv4U, חs!B\#y=-͌|U0lF?uS<+˚U$uTtD)rŘ-ZֿC|!hG=i'7xˊZD[ |syjDUC-Pײj]qZ)գ]i66HOO}F ]+>' f-m4ET^M€66"^sk\>m)q{+n-x8QERf-*3S,%lGpq]I2tjΡp $Z:Eq.Qː_SbhSc&]W*Z,:u:C;3R v#bkgҥqstHzWT÷Cjlڏ0hgXx6bh:qScʓuKsnQ[DJ :mDŮ.lid\ w|e;& vTdQ+)e7ix+5wʑ*8z;RY{7,R}ȵf N{q G캟Qʂ gTe#VXAl$V>3pZ*µ+"@0r~Kz7*d8j/eE҇PlOS׿MMUJK1 lJef1҃V 2Z My >gJ!6ˡw6[/A=#!t]86<ΘALg"\˜.d% k>ltdU J3̢Dy&O*,1Pn~YUd&'0~ʐSBنkU?-ZCv-Ϧ:y4Yqv*]`:#fxY3UEӪ,3IODDᕘ{B&0ѧ@>*G1x2ئUSW I=m`{bl *@f/KKYM_qiDLE5ܑw鈠NZؑ 朴\zZe*Q*k"`TX[cB o"tOUFs()́G}X'm hJ.+E@)"f@PuA]hJyB%GmkA?f!]6"gDY:̗G_FvurÑFU,[DL@D]>a*Q1ٕOȂ/:6}>s5,pnfVZ01Ȅ"[=k#g7I!^8JeUpљ['_^q[۝;r\õXP]eZNsFYI!&uI72}/.\D{̆㞮GE@.;{ o0=3Rty&R*TGhDm}p8;-ꪨΆe0"!&S2#6}vV5|ѕtYE-暪BL@|S qn1t5b 5}:T40Z]9ܫR(Br-`׸>ۛcw)xf#~4Þ3#x8"8&ʳ>2 iR,u&.ms<5 tˢEw8B߉# ys f:NOj-JX): 4۝2+2- (qpllzcQkd.H-7 ԲӪu Ѝ@q~|uc$Uc{=>B:ߞ(|LYx|LshdET#X`t`tmZԩa)QZ\IQ`'6.X7?>0VyRGk%W/2P9%ՀkqkXMr- EJ#NRTG:u u uW@vCT^_]e2*EY̞]D.X\څРF*g,i6֣ZañјW.;3 ,xJu9D% 3\(Sm5NM^H0O"Mx-[4F'}oDoea/-<ƱBbIDm7m]$uV45ΝKSE yO.4nb,:E<-@4Ϲ[ӕm3Q pPf ,$D o-{'6]^,fIse'd8U* +K4` ܭq=$F.汳~<%x %ۮ鍊4FRt7<~{%*8>f`)FaX`A^j=4 5*S3b qI-n4Fzt߮x׿C>jg ܗ(̫*%"eM!# +n7oKb lS.yexÆ qA],]$e0ؒlO@1)|-*eX|’*%WNVXt85i/Ô#=hI83O7r Zà¸E\miQn!ၶ'َ_$8I ʁb{z׾4)s0q2o* q*VDti ,[bCF'Vk.5,FI *3H;Jlc CN[EƱ_\io.?]2Gi:,2.y^'>+]m)AS\vgf~w 6[yyjE iʍ<憯2)!f%$3u\>g{II$hFo9 {,VzcI,E+"mKTw;KouWv~Gv3`j*"K,G(;;-cqUJt`uB5cEAoe*8$Xue;fP'w2N*5 pUn<+!ENҥhxuV80$*̭Q"1k^ʦ.=rgUƶF'tG{o^cp#-A$A5BkݻrW̸fyFWM3 Nr97;={3m5EIE6?%I1B=gJh!ydw \[Ěm7"@KP$bKalppt\et1E˩1VVJOCDDžNT>UA񺻸q9r8ɫGxfPx}'Zfi3]N.EF⧆8jMo}Ś88cY4 q6r*bI%Ou\MV?7ʉ2mls5@zVS7ڬ)x0 B(P%YmݺDŽn ԟbmaWeQc VLZ H$0ٍjMMȦQtuu ]-}Y)?URS(([W2\IUJ6ټNCamV .6qE|s^*#ҥCS"$tA6j *=1 RU2b.)ix 7:dڙX {_/ӮǶZe}mt5G4X R5Ո5"cƸE*̳xܺ!jܵbH te%0#^y7)Te%rp~WM Sd p=aacC)6YQiݗ2- +Eo_M&IO0!o&H\Fc<8,,u711Zt=ˈ3Á+VGI}OV=P)O.׽6Mkw)ѣW3Em1{S&U\)C¹^vj*2SR K> ,[{ֲ7c'hqnm6 ^xME4YMGI%,ySz[w,%Wvtp.žb5<|fz4S5:U!*&s"!1㍔+,Hu|Kid̍`h/ϊXU` Lx˜WȨO)h+jߦ6 BI UH jUC_;F4W;,m$Y؎׊IT}Me4o Yw5<, m9ZX 5W3yZJIcm4U%|Z7GЪ}ׯ]evU;;ikkaE75J(Ԇ|\\{_U _Mpr.GU, ,jD[3ǯM͡N,㯊\-0¦( :űۡEɶ.0u?qRr6p4roouƛ\):͉KgnjEM37Il ll2N6CU/`J^pJm$]mow8Ϣ8VRļ8d&U4͡& =Ͽ >B hMGK/ ;8y:^:}팬Ax|n&OѣH3NDNT p@暆'}LlWό8S#Ȳ꜎!j/U4% ]D/NO'1Ym/鎻ֺ~_@K`j* *5z &Chu߰' c.pOy5=pQG5WG ّh[Ņ^^\X2z Js? LRLz4K 7[1籸wWiD 1~j@|BW@n4 \–r~-7FV*YJ񃀠qT47ꫛKGT9BReTchDuj`.o[+Mi8}k.-i9UJI)a0I==[P IT&mQ|2@or6`: +dL |ֺ%ξq\Ə-Zf}R%UaqіF74:ǍiSA0%+#VRU%#cbuX3*1o"}`oV+-6|ԦwUYŹMUY]Zdՙ1>e%:ZI$\;c464S3$D{C9B*8|Ӿ%*L3Z&گH %$XI7ƞjui x[갫I{̓= 5gf~EYHQeGC$ΝLHf43ٱd_%FZ@ѫ`HUzmeM1g* EΝ-~lkQ?$?#lTJ I">adP @cRƧQ{}vӞvBaWg4?R43"Uy#1.E,=~N .-}f5CCx:Yzf1B]6t&\ܑʔ:&smFҧT:&_K[UT4SE`TQA |eg 2{읩W/k XQPУS@&A=F hi$nZ.zRjF ůcr{%o~[NUf,=*T ́0*Cɶ%_ K5R3z H~Gf :}_awƠǁa#1jA K`@cǶ9CDeIYe3-.bUů|W]̩LaX@uZʌi+)d0JW*s6]MIZKWq-Xﶳ:2 n6iU5Q bWe*@ {lz6Z1pE[Lc˂բ)]WßP0춶 kY~iHKJYX;L1R):Ԫ{ fR0LU!D k=/@}kMJ)9=y8)0=O/Sy<fӉds$@RG#G!b:)[{Hم[82@~7fDiϏA_2OڊVASR>6RNb7f ރs? A3aa"k@".x깗._wU\Li\)Y Hf%au뚏@(eBj6؋ k ]Ny%̫NN]0zVyA51$loYI_+[ ;, _Oۖ<6DA,X`mcQ3$l~F12+y堪h3F ź5[ԁcO Bx[^ןXIrv2@󬈌#dPK1d~H}R8bv㧦KweHiwHUc{"#m#kݍ=U }sÀ^oU&&w*=(TgRrvli}JנisIEqtycT4qR)3 -)ٓ> :[4 m\3Gׇ 80:F_##hC `!jP[rN㡽5(隕BJ!-JL Vq1a".bt,nI' wr^|3ڋ[-> >vhU_u/ˏLl[!r }hw}s;u|$:rZ3ILz*UL3B{(s;H^e:-{7RxX):cU iFpnogK#\EPOpg+RIv*O2u7ouJFߔx_Vvߊ3N*sIS50 bGb ȹ,E9(T.#e *oa_@6L > Um ÖԹHN[ɣNX <yڎsEZ>[Fg tQx3MeճVEOCf0򊀲j[*r3m{4{YfDɀ$A" G}"[q$X2Zl/FZ1M=6]~T6gD֞0ጶ0ΣyQræݏ1UrAm๞UzjjC%eZɖ6ؑt\6`q]jL]/,?U:N(V0"s±skYNn7}pLC8z7aV:dvqR+ZE}{Iq2N4$)rQ؟ӄqjR-~t|RUsܘCFAJsVhfdT5n_QeIM#ȎVRwؐk7 :gbJ ~xOPM: *)4mB#* +qsqo|y^Y ܢ* ։,u8RPHlOMuށQF:o{'O<@|4D":Lb$)?5JK)U^@ vG?i^Hu|5Jd\$u螲>I]q(rXƓM\|4 =/njR=rYغ,F^Λ(|ohu#b. [mNCiyW5qkBc^fX"Msp>'òlxh=3F}<8s,i$TOXZCk߱ۥLs|-ܑy GkXܛ^idZ]J/4QIçlNU[[SХ曞Ɓo7(gwD u}̀?|wMk̑1)v[~puyNAQM%vfF$bje*5…ܸp\PeIE(ڸ?tzqk|)(詣 MpYdWD1@2Kf:nʼn^pQk,[ +RZ!{[I+MG\5+KX J{c= .'N#X3 ]f2Ɓ=Oxs Kj]u4lƳ0U̦D;\%H~w=c?[*CEs?^x®\_YZ-Rl`n6ս½Fm2VH6x LSg%Z8O\5O/m@^!6kDaJLq"71衸qnwy* gN]GAA8JIPMETy/mv v8EpxJ ^ Mfey7;Xcm sxy,a\Nk8)`Ś1Q#G!8Roo0wMJƽa ']7Dc,Ynγ+#2R5ѿ{{otƮ(S "=&71M7fsJ*ǞٔlZ5mEb`$zap1_k " pb $_Uu*Zikn|ϺΨ3LèEkJϘOp0J %>,ڕ%-i:`R~Q Φc^0t[@>|)X\[PdGBBޞE3n  &1O*ռt֐¡& Mɳmk_l/Kiח.pˤ(ʨ[%]M!@ \iۭqlpEO\ӊPQgI9︸;_8i '.U ̴U<ҲG,$<7ث N՚$Mո>lͲƞW r%NA:e$asy5\[v$oU 1P5԰C&nX/c\I/noB)eFEٌS3fV1FUƢ{[c7SMI&iTLZxDH$kqnbR =u_U*ɭ n͂ƿmg9Y=x)Z\(FxwAN SesoU# Mpd4>>-mISz暚e r2Dd/;z ;s l[^0sZ OU u#T(۰;KGåc7:i+7i]A:8 ol05:́ik5+Y%=KV?nQ@dΰu+ ^Q븷mwS1E"?1o|F#W~>.'!Y68bXyD P{v+@1Hu$[iai!}- Bi3Je*|Xi鍺=j{rAIu]L!d&kj!/J2De JMkmpCc5؇GCok᠁z>uվT+>+Lx$A6:w۶2>;D૾0e76c ]s3ә5Ǿ=0yɉ_J噤M Y4mЋ Amgfs)(x~rMy)aShuM vv mCN*`Szl4z(gzD[ vP ߭{FV(T"}<VsE\<3fRU 9 PD,u mܐS`R$iea x ſ)02+Z.JѡnFv߶349- ue𼵣"J<@㾖؍@ۨNđ^YU\H抪H#xb*ǖϮ׿Ca/\t?ٴ  ?1{-cL[(Q/MLF#`B0!F#Iߣ~MSwMYׅ( *Qe]R7'.B2kfc:O\y*o .sP4цYk&$z( l/|l`m9}. U ̢&Ȥ̥S mLZGP}'Ƨz*7)?UACg5]:FYLDa݅;QME۝h߭U=2KsͩZ:vRMc\l!A8hn]##OΉ=EL'u{ZT0wplWt[o.ֻQyNK-M5LpWGm,R6~cQk>hh%Ы7]4ug悩֚Vw"Koޤi3..י4(xѡjm AK׵@ufe0Y^@.{rWAh4\AMDF+m6ӧ|Y\jAdu&A>`?`Ǘ=ΫM":ovd^ct&zsSE1X RĵXwH>"F45_iQלv(3˗4k/Wz5@Yu-,lPp}jMϸNI\C[F lM2Tc Ŕ;[o/l[_Ϭin)QEoB=VXy:m2EPB.m"|_ժStI%7CCO Qj@{%Uۯ@:M #^k=]o2jLj!VTQpA A̪ ^'fL&T5+$\Ų= ٽTuol^j1,E^Rߍ2;pccCeo('4vRia yF*+Am8#-ʑ.~#*Tm*Ŗ Xw> W4Fˈssɫj2Ǎ%ebu:dT4ں/eEAJ\ѥF򻪪FT).: XyeZ^|-GC&]_QΤV*|zo$=u6$v_RʿnۯItu*itygBPH6[ju1Dn7V5ɥ-,Hyy#V.܎ï^^Fv!m:]I }zST "ƒ<!uboǯ84MONSHoA4)ZS:%Tz2I[ 26R P^Qx\Î,Z֌WuQ*`!)Qp#& N/ژcl[q"8ew|SXׯBE}.ok[JI] Nأ ڸJ ^hbj ⏛KNjiX m=m=n{.2AƷ𡡣*-$=t;G$|[WP^ܫ_AK Qh,XJ{I[Y]jZ,K|0Vo&O.3}U8@RnIh(嚢3 T.{iӬ*Ę3yFc>S-GTӶ_KL:4RTkЂnl#YŰSv,IyBwVI<@|Hx'UrU̪VC g{3X (jsݚ$Ƅ`MqeTi@u;s-V4ygTQbmh12F71xbі k\T;{#*`3TAƳpgrv2OPM3I$ T(X؆]kQ*']hefwm`O4:T$HKDl mD[uTjfs z>*2$I; 7Ga>ЦZt!xmv/Gv`N[o2PFPEjb@=IYokFwN Y+j'O髪x/ZjYF%ˆFPRka7Rk 1o&5t&ҫ&^u1$b-lz C m3.Y 4) s SMW%3# *NI4lCK*4gD|ZiJtuѹv= {ߘCHPN[;x"8܀TX\}W9iS1Kѿ' 8H֜R+Kz#06) m{݊BF~1y[vQ껠諩暜;κeTv n^2)]SmGYe.uX_9̊9U$l06k5ϪY//F@^?/2b:ZG!nU&2 rHqB|qR%OQYE-+$FIpUTO.3$`,~AN#ʚX3Ĩ;`IFB݈vgQfF1I3,jYgQ{ZsuJ$ Pꮞ&QKU[E-KaAO",Xҽ5j-`m0uq{G=8Rn][ZLlj4 o5pc̩Cgb,Yd 6ki+s*9 J ]#KNc$@jT -jmPY[ˇET/vh\']dTuÕ 9lm1#F{ 8BۧQFf />mq̄j~iiVD/Ϛ.{ sKWk! O%8(BMơFV`NQl#A& (?cʛsL)z#MGVa*+$d&cfvbaSVxڡ($iamAʐlF~=1p:L\yhvU׫I2_i uH*A, T'Qr`OQG;r2-ԏML5iO)-k,XzFVb m&)my&Su kX(9gcXnv>ҡM9F.~]Y%] 9u*ZY28}:~bvS#q+5Zوmcy FS5D+!1b ǔފ}ɱ⌤>3 yie5M\Y43U&iRs6Ŕ <9kPpu9suaIɯib,?(3wi0`pt @oϚ|K*hFe=FijX'liV5po-8SwS|>o(5pIΥZA@%yر6(AdO9g];)gYƫ:KLa` p}V[/nÇm'4Q4H>Xŵ(E$îSظo $; G>8J2K0XYte]&׹1 L+6&Aa)EULH޾ S3̲ϩ2J/5CT['}@; 8ÆS8~L~s(;NC&e<"n-**i$*iHZGq51Ge Nl3&M,Jl8׿g/riߔ*.ʤ /͆6)kH e,:@ufyD>V-7_غ@-H%nJP{LUcX~.㯽qluISE% bbXuo)0岦GMi8xc"R7XmpS! m7eu>ySOUPLUQ,+ý)k궖F8 &$}UleڪbSʳIp^C(.eP~a9,T9hj*g 1%u(6PIa]qąЫ  e ;SR&EX5tBp6"ק9d3[+ݵDʈl4p"muIuAWJܨ"M8^V27[rH"-ӿ9p$\ir4KSML'@Bkz߮,eAp#UĹ4ZQ+idm7 ~66^ դTmQI5HBlJ*S={,X;~:ĵ܉[Lly b+41NWSU?53km?Uۅ1ollW >+GL7JĐ]XtS3s?0gya٭m뉨I\ iG&~d+8ݯt@/ۮ9T4C0:j4|H`.z.y$15[+) 4"4ce];3K~.n`(dsHGydoqaԛ ymO %Wte|, qcf&SkVBcѝ|pNCL9g׷A.SiϞuB5M(y'{%`0?%4Zպ'~2S]zUxa= 6bWUt6 X9McqeDrQZD/Vc qcagu?>vH/cJS֬44eIKpj!TGptbޗ/9Nl*[!jx%h'5eה ׷{؎%COE'4|,RYf-Y H2Or{2kی|CELPDx<5}USLR.OT$4U_0UI7;\=H)ƿ-Xx ļkSKA 4U?}C^bDiq<-ۙs+^2*G ,b{j"ձ5IIa? 8,!Zәw ob@^p79U~(Nit\7 _\[NĪW`Bsc?4?nKx1=0?q xk#`B0!F#R7O8ͣ9MxJр5J [H a69בuu>4%e+%Pjzth#Rk{_ƣslO" Ll [~qQ/eNqꆀᘪ䂲z z1K=@6̥Ht7; sl pE]ÙlsI|Ջ3?/U$jAٔ+bu-$hxp` L8?^!JsP 0CbA񵉯K {7C3ܩ34.Ve櫌W,,z>֡{F?\eBCT\@@ '}xg4;4kaXVt[w \=\/0\b^.Nij L %V&fI)\p-6*LQT #3k3fQ~ kC" >#`Ö 3 *E") q% ݲJI6OiZ:>Hcpav7#Ӧvfd~u˜LB.iWWOW]=7wX9m b;e: 2O&plmkkCSa4 . Re0dyY DI$#}FwՄxyEa? _e?f]ǯ):K#P _N _đ*3qE<WBL2H[{\3_4E쫔٭vUМJq;HI hذs N1jS@ǐ+ѰO^.M%FYg-ܕ;uAi3 VuY㪣XR HRnKs`ʆ7:~ IiHr?9BԳ@>˨wxz:TE`#{ΊOXXeiIY`9a5_UůpF}B.ǜyX[_ּ"4h$̥6YƒQbU%e|"8`_];$T:qf0>STQ "C _nޑN6>S5;T49nSQ28YTG;+6ߦ6f`.6U#j;FƵ,1l-GSN#6`EiQq"*]=CTȊ?^TRDYsU^Xu^J)3nJ7sq"* 1O"ݛT;u؉j?s e c U4GGP,@Р7F59IUPł9Ix/̯\h%\@,KbwJTM:~"XAx\XrS#C6US[J(i)䩪ܴ\P6,tuđ(N$J\&}k>yS% $SפB-h*{zC ˴Øl}hN@UZf]pm&,ð?^Ze|[|/u^2hZiڲOȑNmѸ7ǝP86FO $ńi+@.lN'ңcy *d@pE_{SOFk!eT ļ+fsIPADu i_P {~X<̲<崐+X%亐}1f#h/ ƣ^*?>+xa3)|j7-h\,@$ߧ!ۈ^)cDO{4 5i]^sHf)P֮{- jcFJQipH̄ub5v{ͥu_޶dd4,X],:iOs/hl x xbʍeL%b!icR[u7lmԪ)a1 W>n!%V e)DCrH!~w U⡜ړ-:Aa}~S^/.a=&b`z7q,aKQJTi!,ˡAI4fD`,0U{l-*5aړi-|ӕҌU+jx;s<ނg#%=-*#1t'a6=e7Ssq$oU ZKv_g fg9*ώJRR[EauN찓-0Et ccFԕ'*XUb=Z4ԝ׸>OfdK;SG%;1*E,noq&5^sq1ZCO@ɮ6M+!tcal8CXoEh 28((*Zx3wiQn.ma{ۆ*YgಐjwT' y]pFri~Z|ռK+K!q($#sû6k6UU"Y>wZpn`~ 6납&jχH4Ӳr AV{bh5y~j>,b*xݚ" ݍq}fFiY IL˨`PP.Hİr"XjH3M*imN,˲/UmӃ1@@kH\+FZYvps/~11RI ,so~|%9sP4HM'hT{4$Ŕ1$I";na;j8 y;K8~vn 9i Y-N&^u݋(Iw/6_-Lm#SnZrg'+&,A1Cw31*CŘWp֒pNbl;sHk1 PByUF `:0Dz}-kZ>hg4LYfNFk]Ocӌ65{m;krsd?_$ŧfqA$1"GPV)G6; wX9:Mj/N+%gʆ( WR%G#SX=4L>F 1$Ŵ#}[L9$4ĥ8g+2<0d4HXLdiT$qNv|fY`l@ l.uOңs 'c"ޞ& Bb!^$['{ۮ w3qmMuNeu,)h,0kٙ`W&N 7y:Aج Mvz"#N\W;yfy2w$n \ nnzch5l4GI u[<2gPfr4eخ667&)ysL8) sR+*b`^]P^v OҴQ,E[6'ɸZr5SJ`u14ztu͵S~թF0I5 RZٲ$)4\ KnibÝL@9CN|2(SgF$ wxꦕ&Ix{Z: Tvit|&]+5m?<SNE {w&5hOxӕ [U&fh ;H7Q [ؼTc tk6uSZxEģ+ X6}9&`foo2S(I`9  ,gnce _{s^Ϻ&OUЧ lˢ̠!j]s./澛arWiMDLJ^*^L8R7'IR%N@򾲨qnwoHH.ƦTz=#/%dyc2FpBԆ{8X>vg R rߡ-f ]3ynVsYOpEKM嶢CHXmޞ^Ay-S}EE@kdcH4\pI2zUL3E9F`,H#I1u2ZGz0%hC':)*iyʾsECyvbEN]ch̏M49_ kXz)ULf:r~Zx#ȦН& e~pT2ňXԫ?X*MM=nfՔi$lBI|f=> %Ӗ|.15;ڥjٳp֧P =,4DzmN+-IͥzJ(b(j&}a"r^-a^.3<Pe]礚!"Gh ,w>PH{)6IuToaIe\yCZ>m˪&&B,zyո[Lm4p 37>CMwy/ .;10e!h ,LPFY(V^ }=.bFO-Gf5]6fD$fi&2Ibo|qQcA$mr.L___{Gz Ih.䇈˞-˪H+""]@/ [6n6Wh$ f˟"0`/ijvUM> LJyeDQb ms~,9wD^cL5ѩq~*zcj+Y= >^ >hӅD1]2JޙHugiA 0z-`=gE p ʮjZfbQ#q̽{N'<m>ëJ דx5GPOVfdI_0C -v<^խM3Ӏ-gχ/4,_]$vἧ4fP=A頊(Ae,kC7UغioFSx;2 |^wb>+#F"KY7Ozвv/Z?_m㌺x/(Je*#&ņƗf9] ,6w xj)jZHS*30I 3ӦK: n5NOnaEȬXT}7&z~|+[ cfFN?aft;hh3I%8a`F؀Hfۿ\+Ļ'0Ѭ]O'.[G VKRR׈MX-ȧnAهN1L˻HMzql[]ߟRqͳ+j\@/M+\t8oK RsRI9xU⪼U)%\75|a[+jȖ8 !xIB ..K\ۂH?-7hx<5Z}'o|!$30)Q4134Kyԭm7au 8Ӫh NF>^ ; X& hTu]5 hy̠]v:m7ʩ#LSy{ FUE83ED E0[ j2|vhD$⩐1Vklwb_ XGx7` <.fA;tRȢJ"r:;:'a)Z>J5f&|{$tQqTCo4/nǵ;9V׼Eƹsx|^JIŭ;m:L[hcَgcQ Ss܅XJ]-V^[ao|+Dh fA9+07qoE $2UG=u^U%iP@ riѧQN5j>P-![XbzD3r#W( ;6n{mePMFm Vd~d5Q̘WAAڴu` [w_b9تig0A"-!mqa)S(5*>@s{nMVC X^gQn1fAeUGTzj-eҊ@an۝ϳ~%.ј x${ʝf&G\SjhH熂: R,ڣ܆bTsqQ*7E }ѷS|!-PXcUNw؝gcAk.s}CΞ]\ 5=_ePКE?1݀̚$i6|!Cpj[Q3E:e"s^LsɳL'r::i zk{p qg%îFМe+kc!w~[;oWiS56ٻVRhqU6qKj4+nxcA u' bs=#o!֊ʭk-4Ȓ0+rcZ*H,S \,-Ic߱"ޭէRH& u$8nN&m<΃#Of9fgLJW6di)Ċ.%9 .$]tX8Dž2u+F0I `c+WyO)u=YV8*f%hADU0F,:Ml KX| ^e4SR$$!YP [mW?4 ȟ31ߚw/ 7'ͩ$$vN&:giRTHW`Cg@0r:C! ,A2Tcj:n4ݡCbo$p^L 7s\6e" <{n@"ݺytc^+?*=vS3ҔСK_q>Ø֘7c447txhƠuT}Kw`q} L&9A gH+׸6Q=םsLm>Z,-Ohxj3ʦgy&w;$Y]Rh弜pS]'zH驋tMJX l>:ĩdTGP/Gcsi8 FoU&ƭ\>|å,T|ڀCu#Ia)5ȝ-pMOfgrnGݖl #<#{&}VCr].IR%d N AA{okn-ar{EZzI\'R>gQka펩=`zk:c)e\ #$K39w@nݬ=1SύM^H~xRY1'3HG; X1SFaLI˶3i*8(*^;->-|T0ߚW ^ڃz9rimŖftT/XV7#b}M7_i$<9? "gXg5L^,4xk\[>+׭:OE40^8V_ g3|G,)T96}6 &3X`Dq6#|ﺾ9 I4Y1%XAֱAljSiLT2*;#/(꼺hr9:gTTGynCZpla#&򕥆 yXsU\D,AR@ޛzaʔÇ8&D\DrۊQ2)rҒOQ%Eµ76lͪʴb&\x&ԩ4Ηn+^4|4E RCi+uCk%g#[y V&0Utx&PӘuEGg&׶{qe `D `@&cύ0q|97YAWf)ޚ^b!6RKlEŮyg5{=r^f VRYX7m==81]h:>?sk,+WR4i,{EbhRp[GU 1QmӚ d+=0DL'3b7;=p|\&-yk\c^,)橨$yhbr O@wRXS,qO7̩i$ZSO76 _I[ܐ9\%zͥJIwR_Ruc5ƫ$h1$tQ؛5M3Rc 7Ԧݛ> f>USüovCj{4V#2)RZnY,Jw *76}L2L[Uț1̂<*ѬocpHaJ"|bM`[괧"ƻLڝ G O9OM%E|O-!w1}M}nO@q=KPPp<~ֶ *bް_$#ʜ%=MsA"TT趂 ƭ7[|eU &q'}7*B:ZL@#Y)^v(5  *8=7E֊g PnuX5FapJQ_T'ZY4niTD'Tb]Q +4eէ`B<`k qغͦB,L<Ԥ׈1WMGO"xٗ2%I'ma#tc$G[kE<ʆ}%ZPYͪkPy*J6Z V(6 S4H ) ׊jhQ9@].Oo.m%ESY@c-Q.XbZr~ZUrفc! C3ȴBr_m&9'uC\AT*.\iu~;a|tg6CY h}9K^Fc,✼&7f擮ԞEjsC z1 B* t+#*Kb ~*ApUf U^Zʐ-O^/iХۚ[% ˤ*@*l*HH/XO)-vd+}*^}M5]88eyUlSMRt}r2l7$26k^\覟@I0hD|>iOeU*XH]=V7EH7.i0|.S4:xji=a1dU+k\Z*LB=eOIZsm=^hXchb{[& `צF#`B0!F# Mgoс 㯄|J)\Z#׮xkRi<Z2g/P`%Zf$VfoΙr+hepʎl;p :ѝMQ&e ΐ/6$_lF)5T\4d̯Q-m:Mk)*ͭJ\$fU9N#ew-͐ 6wlzy)ap"A)Ĕ1WQ;eifF, ,>P3ˎ넫(mTdoF$>϶K]䤲s< Nu8iX[op:}^آaMyos9jh kC`06&LpM: Zf'N ?pUSR(f` * -ck^Zl4q?$Deя3+iUC@ͨܟ^=1}LSptgY.J$C;s۵N&sKbDoXt+?( ⌶<':D+H+*<-k\XUR@K-l3e|(\|-k][.t=B*$:줋ܐTZ6>]R8M` zx,ͨ3qkB5aQISHa`MEajQLtݱ]SLTs1K)@Eyu>Y]"V,U]H:yI'm߱tƶ:,{ r%4!pLJe5:Û; P "A D>Gj8sRnjo0.9 80||Y}?D %`8&b' FRIXiZ=GP͵T(B8~)b K[*P#w\{*NZ1R"$<&9_Fd&pb0uO=1%[u1dK|ָ8v,b6̥ZCKHu:@;-7FjxybV%@1,?DI?X2qy~66d5*fhg+tfyV(cXph;0( wiѤI_{kЫ#7JԙtU$sw=@ؐ-+:,<,3 ZM5&i v~׿ߦ3;w qX e#YGwxeVGpVPͧ]v].7K1%|v 1U#66[[IIq,ӭxd: z&\F2v򓽾}.):=%vkc[5,ªDՔI*!Pnֶ`6$m\֨ʣ!9sI~ $ifN] "Xmo1F4CIo.mrVDRi \\60N+&J ĴMi)f#j*zEd#Rսoo~эkj39\G pM7nz]#MIPj9FuKJ.6a2N`$쵀LBUKMIU=T;jxS\3 @ 6'բ&3p$lA y{q5s,ߧ~Wqq墠a"z#c$-3Ywצy yONq hEʕiʼn7\;J&7c ~iJ!W3IJyYX KHrʡFS{[KX vXos pNυaMrxYsY9X|DgEDСݶ$4߉7:^sWxu=vLgga0,\6 bQ],tٌ]p"x N9]2Ԕ0RB\4.[94$=%phfZh̊c-5ďC=O,Q\mao[EWfu֚ ٕL4xT#sanRFA"fu#Uunh{elY**)*K:m&w[ĪP~vg i8YYC8ꌎ У,^-`#I{SjW}>wɺ})RTh(JFijZ"QM`[nOQaheDI7\T/mhSO;#k|;kGn}X{X}x+>Fb6))*"#t|-JqdT>_ReQXQgֺ`J`?0]m~,⪺A:f92PT F+v~ |mOZC5(fH*==ILYRTEy0<ª6b=bR!ܽ-cV}^Ġ KcL$Jmp^"$Sٮo$ wu88]h3noUc5\xG։UQWfVPW9UƄ0,7}/Jjfq P-U]UTM=Ai*%8;[bI$xYF)ҳ@M%ė;"Se$lX1Jn5@9F3*N0uRI[ |(抟2%%Pzj[, "X6qo?[ktFZeX //unTVKvwAbgOMxQLF1$JN-k94T9)WmP4qӸ+@7鎪MR4\0f0hQ!o[tҷ-1U6oKb7%uIkaZE.D7[]׷LviRt;BQs@W9r\7)e~l" % 7e3*a8wd6G9raĖ+5CDM*c ܐ;k japt0a#`pג&:pP\lZmZutEeh)5m1GWic lzc]OEYSXVT'g4uBVƆӲ% [ۯAl>f$Xe9^u>#;\Q~S{XaZtު9+gXuT؏W݈/"sV uOiI5JG]}}6aM:4d 9& Kw<%-, l#}Dbr7Q܀]M6{\^[O}_T$1GШ[bTydUӨV%}7~k^Z$܍xx)&3kaQCD5fV7$o-21ctD'~RmiF0U yOϭATSTxRLzzva :<'vǢFUpdHS_YIM<.$r"QyWA"FɶqYI-` X,VWAU<(˨I^g"7eTAUGDrKXԁn:AR0i0u+8$ j*9dPȭw@rz3m#ϖ\F#˪ME>cX0Mt  pBH`.{XDwsqsPMYm=MKly] XcwwS u{aRv0:+ۂ8hvNrUÓL.dňb5_au#K3A#ׄ5)Sj5zx^Z59U(#rB[깾*ewYr77 HKU_s*w,7#KS0vhF/~(7a?@ks̍xx*oxikk1e33EU b(R[Dh$t @2qp'OQ@Eo}8'p}6$o1pT 04$Y:m:DcYAbqDצJ߀2M]|LA5ti/qPMey<ɩf.&C#cp, h\%ۙ_ui˩jlIz:k&%%l%m|WNUc) 3clBwﺖZZz,Қ(栬UdlѷKj[ =ecjbEvCۯJ8>(>ZtVNR[TCн@/k[ 㰬7lKݺm%|WcX2Hܐ,-^OܸTnl\ >p-1<5ʪyUtSGFH*' 55:_oM\L4nljYR"8Fc : _&,r }bLHXQ| ,3J LҔ+7NE pXz~UT5lD߅sZ ^k],uS#A;W+D_e7 ]:b;Cfj;&CKҵվ<*" QPLw5 G$58=0o/i2ȳ:z(ܯ4mʼnװ^{Hf:m\/,9CQ wঔyp Qpcp`۰N}mb-=F$:u 3qI._e( ϨlTmI$\6DIf9 ZE{Cj:@Ox[jCEU2TU:Hțns$t5?]$hbu & 3CuCTt[YkK+T+{Xtا1 O^?J{A*ٌcrz@#cm (nl.Lkrf}F/e&,tm5Su JSՌR$6E;X.ă#)7;`&omԅ4Ri?ͤjF$8U|b/{2i뫪p+UU?)(Ye&I ԞnNƔxzhku3I(%29 9kX:{TH|h|z䩸%H T{T"?]p!Ė<1<%UT*2` ݺ7aInvV1CRm[IOYS*/smGo[6 a) lΦl67 ѐCEFW3~GHQoDi$zjY , %OөuFkYM+ˆ4FijsI |#Υyf%{u-,]#hypWy\2"Jy o> e\5dI…7ҧ@׉^rQ 2򸡖 436l lmkaՒꕅ76{#ZdkJeۇ 葞' Nk(+m;ײ5$\$o:&Z {p)nAm<`KVM[d aCF*ni3x+r)2I啚et\U*Ow9`<=%2KM-EL92Reagk1;;{c&!xuӮu Сecjo`ce4$v7 PO_wYW8>ʺت6Jq[UW1H!j yåى'rl\SUدJc;m{ ʉ]-+ծ. nzjxt:wN>_~5駊8Aсr.l6P/Q Q otitʓx3<:2 đ+Q"HRM[נ@ۮ3a;N"BDLκ6 IV&ԁn 㺈Ę*YR+JRf! HFc߰! bsа2u&LλL~,m9O\rʘ87ϸѪ ڨyp$ѣM!u$H 7M5 EZMLn8ʨc!$a`4߶442:i iLj&DX$/!=wO^z톛TCn:/m䚚'M%rܙ1 FlvXS']1IL$=D![*Q7:(:xך2W;(q. 3\3C>dDB"IBI N#h:ٝNNLCNE cA )7)HrE#ډb{Ro al<'SV+Q(.[zXZODR.ϯuzS8 RS,FNmMDd9:t(_{MŽ{E`,r|cC2Pe"7;yov7{aq,} g~%xj%h @0SݯaFʷ: l=De"Է6I.Jz~|q2 JZ߭Tꏆ35qIJ©Yr*9 f  1kX'o1fk`k. v$h㎘u: wW|0%cYHh O=:/77nCZ^W꾝7GutZNIp:W;H[- Ea,p[q)BI 2楦PpɩJbzb'N1{p y|5qTQfyaRiE(auZ 6""Ԧ@hnLAX: W* %©j3jHh.Uܒo6~f-"w$%[.m& 4TES%j1GKƛ-e*$}aXx 0,W.W}r@J_R Ⱦ=lͩ s%\5̒fu]mEm<jKs(~PzckUYN&gxϒ78>}|-*a:ҷ-cKy$_1$ó0 HEk8c|,9>jAML| o}1{Zqt5z<;{_n\~!PC-^G<͚v/3Xfe%bf eSȁ\zǬ-kѧyzߚx5ԒœԬ JE|vk5I`و6F7hdr^>1e35⚌8&˸2w]$5#:wg_5&_ mvuBH9r? G>WaMHR<@$ OMZV^TNX6rCixi(M{mhkR;˟22lڴ.vRn xxCX#Ϩ\g>x3/G2KUUA =T.8_=s:I@N,fO0*ӜF//g?h13?CV?t͏C0]lG720!F#`B0!Zk?'c~_&(<ʦ( N^LAbVPz wZsUf06:3jRy#]ԕ{fdrSJ)-[$60BVy *,Z<9rHӣHFw^US t)IFK$jJ'bQ+Lp԰lunK;k[\uǛOTxptشG'Psu/+驵KM+A_//fm%@ pj=!o츪tzDe f<+._>g}FWM-EPі2R!t^q /s2:5%<M+` ?qšL*@ՁE5dYM4qgpsGs~]LCS!It7:waݗ9Jrn|GjjƦK߅P4`NMaF:-TqVe SGK@b_XI`HhHt8SYR^J9m~~:5b#~zhYALb+ɥ8uco2\wŕ)F{!k,c INP#cQFZwC~-nˉn }퍟:6JduvR)⊢ZX$uDjkT'kĴ9N@LQõq%\xg*(Q'!4I#bzl [^A,OevGI#]Y1 ERffiդbXR{v5+Rd3}<>.L#lZ֊^ZX]bmUYߌ9X%7S̠6jW}8Rg-8 JFh@ji*omlyb8LyM›(1yrp 5{4Qs՝ҺAn :hsAy[s᮷ ZS)bF\H?œ YI4/,e[Vs&\I67#/mv.&6\EP䎈@ٯ&1/[YMz5! ﱼ^ѷ xlM6i7^#򆤬#̎1 M}ʥڰȮG塧㝾NjOr5˫-RQ"3LVR P&,>6/C9 Y5dRSsd9rhx#I=IHe-4TE<* "[Ěf(cM 1COI3&P#%ʍ{I%-J4 6y!i4K!plUwNIzח_ Ƈ59#'152.%ܐov|֏u\TLl+؛1y.e3u&;qEVVTMFN3YQs1;[*蠐ƦZS! ܀>_ťA#8louyHXv/b6?~P+L:Vet,ah1w[[RܱznNkC,uT8ܩN Xh 1/ E߾ h I5#AoI"M7įM`+m:5{ bͅ?Jix/˨X 5M5,l֤n~ E663Z"72]ȍ|?D=Do2ن4)mN_Fif:A>z tA+0*ҰH[=t48¬HqP#GUuI43ʒB'Uqqvƨýn 4dcptU=u={R%`*mEuǑ3\\Ip×[xQ|kC\PH3Iw׆8}UZEeR$ :=k귦PvR})ΎZj^zz}!Y$nQ[m`O0qdLWx+ʲ厂9B>g)X}^eNP,g+Sx7k%Y {u/vƣb HsD-*k3DK!ww Π ppu7[}5h k~#d0gTY5Re2,2\.Vmm[)c;|z$+\=AY /0zMĴ ӂah,ƺibNz-Bfvm1i0Kx^V{5Qͩ ('hTe0԰]k_rWl1\ Zr0 ζ+[RG$KO52C~PRCNU# =5FQ#݋C6[ĜFkCҚ]i'6bZ^^ /؝?(U}MHkuD75IQUuUpFChl ߠ5ˣRf^y4IҪĀNmצ8{j4z~˰'E=$l<%q̎F1lnF(5}|L!87 uU;@|MBYB_osc>HP @cnU5mW"h:5M}\ZCv#q}Ϫ.9f?4ݟvh6W< ^n\T^(#4H ,umŏ_{4M` zgKīCaH.vد<*̤36)s<̪Th3y}wm{)[k6]O(2F|s5W1&JM'CT.GG5TΆ]zXt:MGmڽKQd '6t;? y[1~=}9.7GHĤq037;rO`/ bFp]Xϥ͖vF/#I8Fu%ᯩ̨ډ$G m\[IA[qt`|G7S 54Yā&8CD+% M6eQX^қX`/mDӮq"^Ӯbm,fj2،QͧNÖ,{npz= [MoIyQ{IEYRVafڧ$&ߛ kLFZu&ɩ\9pu|;X3L\4aQAT$ `nP~3{VIq*1H&%h--'4Yн.WA!tJyЈH`:"@G~Y'X`m- if9|5J7coӍ<;0fK9_dik09SÌjխG(ªm*p27V1Q=[Zo-,hp.Lh/ WI .Mk}h-k:x$E`k5߇):BƑ!EH䃨FX6Emkei'Y:2 eQ8Ƽr\k&.yMLgWU%2n5>pT@hOZ'߄UQ4thW)!WRB,7w2湒bNξ|sSֆv#q&P@S9+Z#veulan\= QW^oxU,WkBRƪE@kv"#Y?Mb-0XK(ɛqQ$*%hXei#a{ weW9 f5g)?,ΪY$j UC2|G&De7N /\ jb;_m&mF^xxz-6vPU/j(5UA*X i!;jp\* -`v6$W^u$xEN$d3:ADXI 4Ep2J@2i'roӠ q; !L[LASL@uWEOA5S<)vh}KZu:]`uKi sNs&=±Fn}m{XdFuSMa-ndUK<¨Xʼn$ ^{fQ4)@/nll$#O~:cjPMKƼ<]f"J KR1GMkҮ]NaO,ևU&&ᯞ f4qD}$!ݢ=V@?%pΨ ]A,ZHS.1Y)'2T 3RAMRnq}_c80 'V[Evm: 'Djo&"~lQ@M#ɻ 췰8u֣7M=7mFŎmz 4h)jaZ@T4qk :)J:z?׬wS1F~HWA[ON^{DX>]UXTj:DsL^ VbkؾGbXI.r}|cRP5TM^þRnnA:HC!F@ vrmL3yu3/*(^v XD_ƅ xA3LQ 4s~˳*IXE,) qn[YTKN D YTxǭS\2a^8U8-$ 6[Iaͪ&\O? D2h͂Le)T+:s<Е7a}}*N/H\Ci|gw-9o#N*)cWaMBw/wc>x~V6; )nKL% R"&2J-ۦsiyYh:u,Lm$*gNeh T ߙ CN @H1` RlaXfvP=&wY5+,pMe LT*4-1+ىnvpȧ^!3H k eRlv;UGƋX24dni {r`lMv Ph.3`*TVUgf(X 6{LÈW LjT#a_mULUJhuWӦ9¨qj#2*&:5$$i"Hʓc`#kyRTߟ5RZ7_;~wx'4(sP|#=:5NjKo}W'CW9h0$Σ{\&9lMKΕUnq=yO1WHV (1tc",caϦNJ$k[x X=)4HD2`J(ESHAsi`(2jo-dsYK2:Y#VϘ$aYku lߩtխP15 \\dreTԓG]RK˄ Yn{[RXw?4y7xNJ+i؏RFҌՈ;hcŇK j4d"#M#]MJuMjO,/rX肯.-@iA܎aXsYB@7|>7T{L"׍*Y^86k/u5akrdƾ4/EK{q'1:.2Ȑ`DSLv Cunl:eslҪZ!2J8C_زP ^qu1iN3m;<SZtT6%٦UO-U @ZhFMkܕqŊOF"b8jZqMUHh֦j&2j"ߟ|55;o|WK ޷0^iJΓ72樬<1<1[,`7־&sZ8tOuOr )(H\{TÖQQQJ.!Vv'Uؒm1pl}F `j"@qOE(XV KԢ7qqPl;+bCwX3$&EL%L0Y |]ƞR/}`[Y_TӦ)K ;OKEs"ՍwZ(I6M*"-S!)q[l25ISQUH ndq nwn 8rMR"eY*'B[J{o|:XCI(imQ~;[=-nKx4y<.~NeuHفUH`Abon㬮}fհ\/?UÚ,'C6ʮ",#(gp[b水@#tUz8 1^|YPBB1,eYB!mgݮHl( $dhKG m!m==£lkeeGuhW:Hl}:lѦ\`H;"juQgʩ{i$,) ]楝)"¥AI k7oqb:p9n#}(sN{qK$(tX#I˺kߦ@E&ATǔ4%(,5,mp'Ml0+:(jm5U_~B4*)\P yk @Orʇ utm^'[ǭ:ÞLi[Vhr:mkmf!dS$y.U9SJڬ' &G4~0$^lzEH<4i&.8㆞|槊DSԟ44Szz5}"YR}z9hgCjD#,S 8cNeMői=NI0/)p/|JzWEZQ{2&|m N.qM5u=|OȂ@K !}BW ﰰŶp4 i̼J*l9 =mֽԪYE9!Af38<Ġָ&{;׸)N :~^C 3$5\XmBVJt3^w_;ji)#T1eu"Xt:OI!VB0!y{9GF?vW?xXle8x5鑁`B0!F#`BY?`B౩ \y<@F0P\nl7͎D22ItI~)*i>晥oTU hB:mW$}z@Yqcw3 xSiQQy^̖M*)}knxܟ^hxt '"^ 1,Dc0&ŷ'Q]ʣ mT2I"ۀ:7!ᙅ> {!nGǡ D‘h`jZIkq{cXuS`dm|j3cA^XWf*UJY7(p94 y"nMkg h0>||UQWSetɕ D+I:6c`08 ?gN M0@NHJTϘJl/cXAkײFwo:ds\dXЛ ^X9QTYzUJʪ|v6?I{2T~ ?ͫ/2-k nַN44ԻPX2IKepf?]QI nȡA.@y9 KZ;:YeF{7biCr Sy_MNreD ~l:Y9,PѢ$B5![i"!RI%e +|5Q(WY@Z3GSх"MŦ)$k\-3LoԮ荆>23$#0K[O ߄iJߋE_k QT@106lwUH2k%ѓX`s}-oK!KPPbX(&Ǧ۟OMHc1˨V<,+GwX#y1^MMϷ8yJOT=0q' |3MCYWNZFpFicdq8z *ddkb骲omecXŵ>6;ܩ80 8M2x&L\C`;v^߇۞}Hhtm[Qc4ڕႦ14Q:Fkop۾DTKQH-sW*>*#NJ,9nQџှ|IJV"I'hSUsN3LᚠBR(T$Y`Vʰw PbYKM27 xOG9~rΕD(GÄ@u;Ay umQ4/:wx랂4$#SPIq^*@.{Z"} hԛo : ZHdޡ!llu9Zk9؆ p&$u䶱4 m4o^~`+/H"5ʶżQ{{Ɠ]mrP<&䞞 CO]/Iӊ{lEFPIK e n}0:bS^+3isl'c] 49]MTMGwJ7զa*Z˙ -kmN،m$EKISEPUE SQeY!V!osrTl7Ç4cQ c_O/dHo.#×GmIN\I;u8^'k)1bWZT*n lkf]uD Ma@M`xwTݛChRy^m_T=n[Y.S:QrZM?K\ `EZy؈:.)Pvo__^K|W6Kۖ.mp}M!L4/#K Kh n[_MJ$]SJiI[ mSZ>;*Pn;hx~ש q~XO c}S4sI<8"ZM>a]4pd;\۩N0oy[~z:neA\8򅪜WVVS!84PcI[Q/ 1ӿ]1^G/%(ue 2K4W/bnۡ\`:GW{Zⴘ;l0'|-p8RҠVĀ}@mCg/gv #Uj(gѭḺ jX:X mT떫eCZ-7.]{)ܾHyu&PH6?u-y cC5Bfl+RMOMEZhyZI>bL,51JNdZw1Y@.ѐtRZiէ+p^u%\- R0rG.#I[ēv^4pfvLě^|49 AM9S6CI-o&OhFjM_NL>bHmO2eQ~_Us䡟./E!dYdP0;CߡůfVԖq*)*ex3ICFՒl ',lw!Q mo a2Z%gjYzO6aCh-$oL5s*EIRhHrTM%:=;Xtfn܏}#&64oHx̪F,^qk[As$陉7[aˡN(4PYFKOU0 l"H^Z[HlIۈQqc:L-XgG&`"xߕfrهkmѪ@16'MkC AVC<Rq._dNzH 7+:*ZD"M)4#C`(dPC]W<3B9Tz$MFjoLLnKcHcH=5֎GP &lmϫӈ:Ӈ\7YdYSUGTDzEBlB1A*`*Rqo>@A]0JUQy♲ٙ$Lm]$i-{?k=9`I$,d-k=qrA$ QrhxުH3^Z)H[0ki?HyATqMw']pnp>ehJHfux8ĝ'ṋĹG楎&3ɤJh̋ zB5\#$GSoITxRYd90Fw.nk~kJ6Q9r=bؔ77(H{}{I?ܧL7+<&#`M;{`,lF%TL}ՍM=1NMfo'`mr=1T8Tjh+E ICQX~_s#SԫCÈl'DiMB& 5?R=Tua{C ʎLOWW8(zE[S0dMW|0[L>P `h*&FBLIONj 'L%s13j$)Jn\-f&u䆵ƑnqIXmt: o%lZh%5:JDl&w7߿lh:h%^|b:&$YmJxwREq}I`w t*qOjS)2j%:ama7b<2 hKsdH/19se H)7đRF_R΄f#n3)RjQ3ֱVڇ*KEXOl_я_174)2pv*9:ifYja"4U$(> v0ipll nk5Z>+z̵6SWK-AUdF${AGMћQ:G uNe'MY˛-'!&9oDe[k$_p@ eT|A3"veuek8N7-ɾye啞@)WSm7qhl'9]&sſ1}5O>vnu:+'IUl:>ap0<٦-6XCn$;a%N5:esԐghM296Ae#ekuK7Sl: | ʢie\È޹+ 8`M?DELyяM3f[ğУKay+4٦wURyDfM%$E'8Ŗi?' #ݴcgyc+w[ŏo.Q^ǃ^F#`B0!F-51F/-W|S'1NtV/>G `m{kM#lSFN_{~lzVBq[hG+80ݤ؏B1_΍4O䏅IdX|.P2u$" ,@S"R]{ :td,͸s3hQU0ա؏PsEMPF*(d%,̱UI򒤋b[I-8Ytki=GGZRJ,}Mg4NVp)P *#j:ˇ)#b: k5ڥ9$fߗVM D1˪3Wȡ*kqOnchSkK\Yy{ˣRV,9}NŴ+A7'{-q"USNבThK6ٜة)MXEK,W"O8j?Kuk=X uo<ֈa4GA/5P0M?oONU,^Pal6HA!l,/-}ؐ߅@fc*iDyQp|6T ;IIbIqhZ: sEA {qUEm3.1q;-ϡT}?Vq-eTFS=McY4 Y1>[qg `s7nXg~kpT9|sEg0=L*%a˰m+}'YzadձMxtU,-q9}._^Be4rJts`ܞcКu1L\Ǣ}6VO rh"˳,8#kp^P4ܑZ2}zJvH}GiFKTtҪBC2YR3 +{ۮ$V ̸%i*j[)æMo{Zc؜f%?u6TeǑ;Rxyhht@ H6*7lH/~,R ᵥn7=i6MԌm[fr#"H#(!.< >{mRbs:rUUnswઃ*uY!E(å]F=[82Ȃ9xb+M2Nl<#tgK_'4AYNzb?c̑ǛVhѕ&x nPfP[64ymk:%yAP qmIձU)6s~G&.o[U( $&5jmͶٛ^\[ f9\4HkM&nn>Fs-TJU,9$ߥaNRl5L%ucxЛ7;9{-dq2ՔkH2 TGK[tC1mc~zr2r~Toq bUSFU6!$0G@W^Q|=6q3:r+= @ 9j^e-.ePٛ*J*/}Bޖ'|[G*0~Ѳ=¤qVCo/׍ZtM%g~cR5Y$WFV$`nly`S! n5dxT\T>eT CH[^*Q2"8.L:3NGVH`ʚ;k^ZA0GxyyZ9e~g`a:u/Ns4>ۈ\5߁LMtsyF` THikY@&׶Nvk~}ss-T.[k'SIT (cV 1rH$+-ɱ:[z]YI"4cN&U(yeb;Lغ؀ǵ71vik)1) xŸ:ig2 6 IWe$|uqlaFgt]< J&pNU#emSfs $(M!o>Ng 33n+Xmz3ߖ]2:zw *,V`Nkll{q͔ Aŷ3z7SaWU$N먃z[V㽱=TR K*(R/jjTӄТE){zq_q1uDY: -,*IM Q4m~5EXө ͼk)7O4-.h"F`.l7~qQcgtPgSPD\s/[AZE_9ZJgT#*PԢYď;z|EqlDd~Ik=:G~ۣ=íԯwpB "6ntOk Zأ2xה0G"C, tŘ7 3@LFmyU`bg]'hs|3k% sw;c#EVQmu |%1Yƙ:ˈY2/%zZI.:`KB* Xa|7l٥ X Úu鏈6/TU|2$AD #QMcUVKZ!{]t) MlyzMM_OV8%b P16߶xu&92f$[U|dɜ[u66v\>X/l%t+[P T9\k7c)lf|eeys}:IfU0ʚ"2cDrڗ>&e`O5\i:$z-|@ &U į&Y୆Zأ <̀l.nm½ϱ)AG$eycEx2j`3H)Yp^c.j#Ԃ1O[KCqx?_ԨlK]m&/;]<0IQ5j@u9$ \@ڂu|wf&rq8X\bs1NWSڐJp5Tp=,qv5( ^_wRD;L*㪖Ht6yqƽeR̭&!뢫].m@յrKraQ+8"vvU(ҤTl6bO:]3J ?3jUYL )]х*cqd )៉ DuX32cr$ ol>qT+7n5p:J+"-T`^.~뽯>4h|A֪倓)(ȫaZzdBC" ,.Mk|aENiheF$i<8Z||=>kD"ubߡ+u5䚌Md9OmC[j5iw=ӈrˉjr,*%hĊR$"No|gn9ڗ ow<~iǯU*:ZL~*=u0Eŕ1~; ~G[M'8jlҤʼTc _:(k9b0`5dPQA2ZY##]4qei4)cMuU8viBJ'K.[mlq]?3+d6W\eUѮ^Hd 1 G|rF:DfGYUW\N$(C-4p&pPbݻ-Qe8m`4긍'4iWa\ 6nAWd87'2ԣf)DB;GTWZmolE& ȹV,(i\΢jukZBJ]nlPˣK9/Ge|5wjZbFT6\qci?j`I]s|dCq`g&ZrIN[\*}7 cO.kiNqG}zeVAG2YGB=F=2H,fO1* ?1{-cL[(Q/MLF#`B0!F#Iߣ? &I |Ž\@f ߽Cwlgz FW{[S$#*S#G$~r !v7hYZL*f_+I].-M\s =G@W .x)Kb]QV ("uV`"s;@ bk0@m󬖃622ELuHZi%吷k؎e; V\Lk7䮫=J'^~!2m952Tj$eEAw}1괫@L$K:UNh⑟}'z}`HH>te@.8~+&Ųq %Yʩ\Qh&^Tܒ@?ISkb?f7~n3r~MZάې꫕FIE$eu<,l_s`\=A+ Ƃ,(6xؗM؞ĒN gqy ۩*1pezQX\Õ]yzokܟLpkfj4jKqe m ~.dyG$#4ESXr"J;ߥk*1$xK P:0%K$kWL) cx]8iB4C1oM@lV3UKLD)̌Ş/]ۯoHZ׃Z)Y^ ai"JɨS{.{c95gSx%? lS9u4ZTK,2,$؞e߿,ck uHi,hˠ˳lj`YBqnaaRa2bj''MSE 3JjCS'U=%zzuj)NRmuu7 ׾mvf"y' !U-q&Y孩bal?jQufR@D>mVGEQLhծ5p@{[GIi9 uEۧ_8L* ച>(^絽%SFGo 9afYf271uk[`~iᤙ;P)jj)E1lGk\{|.5EG G~VSgvt;i3J r󌹕NPB#)hPv='}>rp.ghfZtLϟ+>_PG4QN 2Kt.$> ׅ19[ـY \zzV Nx U%I>k,Ҧ&b m~qN;4t9$rtR{Rs&Z1)7_A;+ogt GO& 'QaZ)AEF)ȱ~qt|&:hHqG F$~q;[Ǐv ت`]F-cJ7嶖Ă35Hjc$1/`]ԟ@#;jAmepk߈xCH셎On폧2}N^8wu!G$eQܴ8ܱkxjLGx<q?5-D(bkLkuSMD^pau\jĴyzn\f1V.JcA3{Lym84Q)uVYL%Դ%Z6U1`w-o|QVj,XA-_E}|*R}C@u r5huXcԮ[Xc݉$Wfs6xۂSAyܪ>OT)X!2 F0$ ujצsd4vKRӧE.O4 ͸EqDed)cS TX:#1›3|7]RsC?/f*D<Ȃ> ${uU&cUsn27.4_B0Ė"OB6: =~.bǘ3g jY^%gG,@& 9ָ#HJWRSbHUo42p9wsv=Me[Loo{_k^ッ]|-lʒjJ0 ZVsoL!i6gW“lja'g4pCES\3eM>0$4qT1ɒ$Uph^;!e92 󌓺SLf\wq3M,f&@Zk3:E"T>y5u(ɫwQ ] -a˪"DOLmAfeEA˒Z3ESg{|iݭd&M4UTy% fCi6SՅ Y[\oЫZ&Ud_aoyf/n) }$,rGc&5O&h"+s@54:<=N"^y)0[k_o\,q+!x)5cWWK“P43-g &a|-@iyBk<|7[$' 1R}Oػk,u\T@_goCЎrV$k;v%n=-R\_=W´ZT*TĎC35V]n HYe%"pZ@M{s|GhUq#i+.a)U"M%8 e,:Ǟ8a+1i}Ni֥U$U;7PfܘpF֬?{3ح'Waڇ]sSOKD}VT/c{X_|\iI=*̒9E}!|Y9qKt+R@]*K XɡT2XsLhx#0$Bڞ ,-.SLqR3=Чxah2E+UtqH@Uȷ6-kY-x+)1 q\OUmTO֬EUPQ 1`|poVouMU9*#1%Pӣ yB;㌭0 n %t IMP5sjUG2v$WHQ`Qk:A%MU岴P HA,.v6*2j; fO88C JLTz*Yժ:[gtX%99d82~ۮP[|3H䩥+),ocYpH"/"=H?/4ku79YỦV$jbF7'?pwm3RSmSsR" 2qP koq5L $q ,WQ$P#` F[X_3[ ME 43'.0ċ)=pqh$@@cK>O.Ra\*Y [| gkx8j7DlLqD:_sm'$g'8^^vcݟgU~o?'`F!zo zd`B0!F#`B0!OHxAITʹ4Gu#C^&[&.aVp'[Z<}WCyCRZi⤨ҞVQ[QXjÚDze/4Ap,A%KGVEM*W\23xܮU;I4gt$27BՠUb gCCu(rʹahkE Ch r{^[`H?(I쯛lIq4T17k AV_{cu6U|DܦϝcB9r(dmj`Q~rA=0g:c4V0DYj[S@ ocץwfƄaV W6?&bh+]D} -w[guAY|0%P;ִK(z(}U1%@J'cu E-c9Lo:U1#aaONB#3w lqC%-l-' f.P#mlK+]۟ZϘgYKbRZ6}e4֊A{w?n=DF'p!IN$ui=$Xv8 u==M*㘡mo^2m ߄$zfd:vI)0b 'nj{\G_bc-NT,* q/ ^B*ؑLzVK;A e}4d.8 +=:nǕ٩kdv[qn5jm?,ᜋ3j*3SJ$$@H6b Zpyi n H2ɿ/_ȮcCԸ`򛓥\aoks+1}> tUveW\@luڨn i )rƙTEU:!wٺ-y5G:򷷞l>yi_5fS,<;-7i8 A.<ױ~IΪ /{pJwkdSe d*+/%E$@'am>c fikuZ. 6׏eNe0R^~WEieq|^ XEISQ;o'_e!ԑDdZtXFYz5$nwen"*ӘEbtCo%x4X`k|Tu/5QYdC_7Kn-n.K<7q*5it]KFid)dh:TRorn-wN쮘:㬩l+eU&H +0R@ u$X04%ؙʠH:C|4Ʌ3R;oӮ*l $ʍƧ,Zf#PQME=MoRM㭵'nSc]t *W_XğL70l &];BRoalgMϝjS>;8ie3.|U8h$w \}=-4S͘}>g44iw͡Y?'< e}(7g/|̎qZG˒CLtˉCj՚*)oY-bw 3v齯Ӻ,j7\u6 \3TUcWctUI܃A4R^6 Kovvlkvna.&EζuI͵4V^:2iu6ll<ǖhxϙQ݃ &<ԅkKM,Dnl,Mmc* @ u3}ԅ}>WH>fjsx -_L #SBBXjFض2hߍp sT3ꬦC&[Z rYtl }#p{)T{&/^Q Ϸk༻3?YYO|wli $_9jPl'c.!& oi1~+䕙&z @b_1]FKX_5ij4؍c2 Ǩ3~;(ڜ3JX٤?$]ؕE@,H5*c>>1fzs\ [o2zzRWMt_)Jn4PU0ajdǙV0Nӿ;~ >z ᣝj!]c sp\[oҫ'jSѢ ֵ6f~גm6Q-Axi2vZ祮p}.C"b&l"4pLB\8vX;?hD!U*~\_{Q w6Â@p%*[I-CW-0lˡ4 \}{"=eRϲe Z  lr@7H6:Zŵ !58>""1Ouƨq4;߽#*k N~gY]GC%4;ɨka^{64].'4;uvuzC:ƶrY/2JjJUj: p cӹ:Rb*\iL:~rN L'Ͳ㎲%*,sT {Ka6 Oyݤjt .eu=]mLTGt{{c Q7xl.WKFphuY Oa$Lk+iVQK+ ~]x,Hi-8.EQBYH `Eލ)0$^|FguPj`﯊GZ4@ w'b E ՛E3r@GvmSʲə4K@ S~ XÒkzl5\HuʠX ^8%);t|k"gY&nNt:D'U U*Sг+5WHqTkK}<1Q8ifF 'L Wr#S`m"ǵ3&npZ $,i$M51 |9UGgYO_Dt0q4*$&ÖWr,n6tZLlHx͊zL 2OPrUxIU˃THo'iHC+qȵ/V-iq ]w͋j8)T0O)"ٝR2m_7 ]i䥵(j\榆DETaoLzJ)Va-v`VOP(cӽ!e'{^ èvru9 TA$E%SY`m7nc]ZeX|JōeKTrXTȧ#XZ[P٦Vx4uSq, ̍k(e|7{7 X}0@؀z&"1iҭ.ZfIz^c2@ٿ}lɃsx+A4S/槝ܰ܅#=ej{{ǀTpiniŵU_C~ZI ihsb7s7Ӯsz൨yT))+sl[Qm]X73]/- x>KQS<0՚ud@]ѣ$kv"kqNe3L +xsGo ftMYH֌Y\ШMH|]; *:lwMG,fO1Np!#ǻ?[,6? OkDuB<`B0!F#`B0!iT0!|CL>ls+kI(L*;lF=FLLu].yvOSZ5Y\ feҦđsccwU[X )ۂk[ PBm{\nmꘃ\iuldwb2 WSBd'i7kyA7뱰8N!춂MϏ(8?]uI@ {z9ctsZdRC&ulpUu N7FܱbǾ:Fp)8-`mJsn`kתqWW1Κ1iPIvUQ:p̥ERˉM}}՟rc낅hs|҆xi^TIBܰ $-4IkKEu po79<3aSUhϚ6RA {|cUq$ ngrNXU8yO2+B$,s"* \-[ǢZZ Ü x_RVSPgGP <7n01 nvam4Z F#Uj#j ,ҖM&/3TB6 ?`=V.R(-qUlb!rm1+QϹ&@em06w#]e仧e|OÔYRƀ;EobXk+w̨~jƵ7%˪*!KsaHChpp::jhcko1ǚe1fT=a"I:_1*8]TwfPSrP %$ .K$%!Fupm5Cj6O:+W)5p& 7xBW,2I+sY7qu"5׊Yi $eK C Ql_F/>w:Ϩ֖A Dm4  :| BC1Q4vIm*,PMDt**Uug湦!O+l6 K*Q5;쫩Ԛ>PdDԴ՜dY,t+n[Q]`jJ$d+eTnX*OPEqf䍶`w 5Hw]\f=9UzO _V rG^ht_1~ZY3LKI !uU=|%U㝶O/3Ia;U\k zxc@fHT&,` np<kr[x怃MG6kQOO4a7 B׸kW/x^xez4 ,jT H5$w7ln0ZLYj ]FcC3hEһǩޒd"B plq\7B6{_# ~gkXj/`}y>U 5EH΋m{^bꕻTy Aqs lK DK4lYMƳ#06T!EV>f$yRD}G2;ڄ:iԭXqYˊ8Z<6*A'SZ0\,ωZ|@b{[{ҥ`iȹ  h2I 27;j7c~zS):ƀrW*nk-{汦y>"Yd(4>RSvS9n*!&o_Gщf+MP b+2ԥP4܂ۗRfSV)/T3L42msmaQp@BXa<ӡ3)1SD1sg$jI<7*xAAeF- ۏL`b(aFj;QƟxۭ)jY 2В wt[E'! V$-?vzNLŵӉ[(wl]6NrN8l)S5q^`&ZH[\_LVHU0XX*TÝv7 '*zIg̠nXv9&O~BRkK iA^n\ZCS#*vD쐕 GnX67nǡj0`_7cAǓWSAn,l$[~7 dwxZ5KЭܓ '1p YQ8E=] TdvU\~3 njᱮ j9mNzOv{@LfZh"-(/pba1k6>G%|Cq@ƌtoJ++bf Ee>^ {P=5JW e1)>XG#,ut[;n0:Q5IcΧ4̳ͨd[jװk\jT9mkE3$Q*h0okv"IH+%رZ@\[i\R J(-Ic<ƒk*یb?ʕ#-\ #N=L-zxgE~+s p[•4SU5O:2'ARm\ }Z䳳f@؇CkD)rd$tJ 8u A LN}ec(ze/,$)[w]Mŷ! ğdhT0K\xO%*e0\"Te@z{5h٥ S yk 9j3UܼmrX .:Lz\]B7GL '2rƪ 54A4*K.em6&ċ^Ŭ5kmP" _`Uǻ"IUtrF24j!_TUTqi{H#/kƵܕRT19YkqAiRS3[kN21:k;tq.A.Zd\_m6;nV-TNu6fh~6|gkkm~N @dWTp]| ŔtFW\o&co^ _1̬׈ >Yͤabsm=^hXchb{[& `צF#`B0!F# Mgoс / JtȲzgL1>{+aAtE'Y˪xeJnah_5~_ M77B,8,V*z $h;c sݷ$AR V.UM3Py5So g}US2V& QT6oQ*4I71BNj&]28p9|z- WH !eXA$16߽!MORR,@#ֻ\3шi8llTtr&̩!ɨk(  Y_}V`ECm7;F*edoGOCFƬYgRn !s}kafW]uˮj'U+œuLDLɝm.Vɵ7&c7tZgp-U¦g%SI^Zkuf0♳E]Tͩ9kk+% pA$9(դrw5,A2,8 jMjk]wG>_bY}GbG443 Q9"R|;7(+Ik ՟ r秽6Vc;Zn@v=A K_`4p1/u+O+*4:U.k@SAL$յ; B4vk0s&&IqׂJhT̕fwǷTRLJ2ePP5vߨ^0Apki)z|SDOq|\A:%CV L-TKk\6=jdr~gu$[T_coWX7.n ';]Q&_갹?Y{4Cg}#+@qV^$*1GQ6#y{ 4}lX1`l>N 5FGDVr,3idij%Z8 D#I$Nn8m3fN@Un,':1,ǹӂ%}Jnu k\^f,m6={>TuTQ( Uh-"}|^Pdy?S8"IEFژbO'@ O.z&y ss{NP l=3kS\4%]45G e4J#??.C*>bNǃgkbZ PuhFۿh9IG=AUFK4PNdHP :} V5P  hR ,|<KH>R1{F`5p4L: (3!40>.8J( ʒJ7g;^#_b0SL -еJFy:LÉiSTuiǡ^$Ԥ*W9:;YN'_D)<8x-UrPFFnbR5mpn :thVw|^,- r>U\Ͳl %Z*i ӿ㭆6;)R/xEĥ1T_U7D FԌ- [}xv1OLajSxNr:jy$2^3\ 8WEHq6Osj p&s;H t `8u@"疂;p=8h2qVD]m[pFl7X M7U9xh4V66Qy3Ib@qk}&̶m{xhwJ͏ZsNk(h,*^$Z&'}߱8wt麦X v 3'[8]"V'ˣ&)`mʐo{Nݬ)PR|9EEjk3}u]3+(LG,# % m8 E*A:3y+Vl 8[aq>f, b!fY;tRSR.χzYVZ2@E2ʪ_!L&# rZISmdW]2*fs2'׫pSy[5γJʊr̭r9;{ktķ UYq .k3 F[o 1oH/ -v$D cӚ4۸9ۊ]Tsܻ1,cD?FE64FrWЬ3VN) |ޭDusabV1"X0k|A{[Hz r{_1)_B[8 bG%nzj5Ko:zJά>ȲV9 u5”ΰrfb7S|\TH. {M؆[q3>uP|n5Tk2fԶk`\c/ NGb8  jb*R9 Ϩ,)8&/ɡDYacQHy1RIAm@_4Fq} %ڀ -^MUսdƨER,X_kp:{]N2(jhb#ƺe~cVsd mPLHO9j*ĘJ;Ɲ̀qGyUFWɐN]T -&]SM ;G\D̠)lX}D7ިs5d:'tdT^g5ą= F .3ǚ[L*ښY+ E&J ᰮy?4ZBpcR5e45ЫP.Q.w0#|yR*ɛY@Ў ;抏^Fh1sNx(jg *"Zʕ9rn +;aTIqnr@»pT5(6orN(YDTů}V==/շԯ?]?(}W@ɒr8 G5졇B/c1屍ٵ^F`-W5EoP|}|fLje},P,ǽͽ0dVn;4sRh*iNUOM#h`l{[Pf4(O8vZQV6GlC0'pz\C 4(|,nwYYجJ)KMOJb u#px<{[N\HqVQb(8l|3<0.`㝩K0G7ۍ[S.dּGL,ѵ4GU|}׋Z;;4H##MPdN%杔 QQՌ1̥L. O:=˸VL+iеq9h$_+9RK||Ԫ: 66"}"-^ \lrof+fjdKO/{ 31j > .9HaK,Ӧ__]j!V*` \kSO Ex?+"-E^Wt-WK[--D23Bҩ`k;E*aUJ&^,N uH#rm[qe"I߮j 71D$ԑ9j5eܾtGVLi$i{[|USi;0i>b&ꯗu5ĕ *ʓF̒( IۡlcqЬEz6Э;6Y]g;KEC##;X Gs{zi 0lZ/Yg6~Vsm=^hXchb{[& `צF#`B0!F# \`Ŕ 8+ 2lʨb'F*` {7[4>)`[ ̠|,/ Y$}B4M)N84qpl%Fc;j'Qeb&-໥KQHd? ϔ&GNw~bW.jek0`Ŕ)&bӌZ/੗'[#iܚlšU8Nh0z YT X qPpL:)/}L-<8ii!l#MN\ I ^f=q@18GIrrtSH:`,[InIIq y{legx@-$,h2> Gµ)ԨgxT7IJIJ*< ʅU"PXm1M<*dM#KPPυ _)A^ރqcRpL" *C,캪99q|V[0:_o؜qZJ}2`8E9S{_SB(Y_´)>s ~aUik #XUmÖ=JyIQ[/ξU̕zo727oX(0T|Po c`Xs-F۝=}x터=zT$O3򐪡15:o4 [6ۃ݆,k\hx.Ncȷ ԉxN&3֬Pm7oؐ-S1@}}U̦Q4F}y>D?(Lׅr)ef 3+5 }ݭU28:dD%Umu9,՜C!ʹ$9+C3^3F GxsfVӠ`MDᯒ_W)jk.d%Wg$dM=tEѪH#i$GQ%B=dxK+eQdQpo}Ji7F75_"[|µS`5QTw duds^e\;* i GeSOf6 'x%Og"3ob"[FPg~Ů>rqa;p;6Q!S~z>u:*oYuqf禢(AIo\c1<`] Ϸ䭯&I3EhEf RkΣWcLn}$m&/#,yl`QVoqS\N$J` 2/7=G+#1bf rnz3ic@kǤ68I>B(lj 3u  l{cاWnW6򍏓ϛPEA2߾okc."37J:B2ϊoyt-9em=`ƣk nGnR{$G'o&mM2}76׽#p$`^ȭ|C6q? ۾.qM2m-7 a)pZ_Q򎚏.yd;Vhk( aL=\]3`ޟ;򐦨IS3: 3ezlR4\E6'yp1N$|_ ӟo>X sU͛>K EsKA+HŚcv92ްkZçl. srRy?!$uLiж)EMo=JJ4 w~wjU%OLC/hdgYbcok;a\ꢱßsMԲ*<|RŁ|©~MiexohK$O!;3o 0` BH$ؓ$a Ns,Z%ޜ¹Dž+GBюEiкAI5HdU56o3 Mdꥈ%Hoci70Fm}XV +rx_&Ϗis ኺZjeE+MM# A&޸ nQxWCxB4#[Rj踓\ @$_\I7#WD.Eә~P?ȧ!+<<9Y=nIp1FCU o9>_c|PpPυS[s1oWnSvZO&J&Idַ@خf(˜ru|g˂סWH6㇄yʩi;Ji9cҬؒle <>L_i@h#)&r P55tfza~k1: $crQǐ|36X 4w}l#lS>c#G(PN6 K\.eH4Aɷ/G¶dY9/{nQ @5q&UY-q߮3@S:}%|>Q~yno^جLQ6[G_)J7w<{ܡ텫;25K BN|+;]H~tcmM =NߏDU(6r3H͖g>hY~fǘHRui16^rwUx``nPw-9>D(g*+j*e?:}Gf6+`ir;I Զ6flz~,bSQ0,so eYXWOT71[&|8FwtqG'5Q%͂T&QY8X;i׊a>Z gX6lZױC5`.1*W&b$|:*,1檇ev'bz4Déo^a7X[9XkAhGS!ǯ%RD2QV;aPm?jw8.S~K6|mdͼ'm7Aob Ȃג~E?($ ±ČK۠Q:̞4<,B;H麟: p*`L /f"sFg~- ̆lX[TΤd)&F +w:~>@Cs'W4_ 4 T.fDd,mlf̏0&miynwf{_QMCE߄ԱشT"Ɵ3oa,6.ӰLb3[1"/l>;Z7Ro}kЮp,cD>}pBpo5ÔCk$yjJ=n;{@P-Tr8HU#sf'#li3 ̜#6ԖbHO}](9ZraZW)SOSgRP-Q9Kq>,5ΑL3=H6E:}asO2}%>E_(W;}BqkmoK_Ƙ@0 <٤~E?("D߅D3"mmoU,t$R-[dP'gr>kZ8M ' ęZj~E)Zx rGxq_Kq/l>*S:촟0e="O4M+RȻ+qKXZ;Ӧ;} wʩO-O$DK8L™Ma#f͈5Nmm$9__i $DfiÒYcpɧUė&9ߊ|92i~\MS4^!eB̂W+bZlsiEJU9|e$ۣ?Υ遵hhtW88\h>E(hTͼ(Rfw[vc v Eð4y>ߒ[&HT,zidV]ߟ+:egKEҠU/ Z̯oaxU2fO"FMYbɽN:@P;:3'$GW 3 o!$fDrt[˳@]}DS7OgKUq˖YRPKXU:@$o`ms*RWgEH-zWC0Fi R66kؓ؎ֶ-P9ogi,`|e3 AG7s|TCLSmC+]f$ϑ'(]GJfK; @Տ3N!pU}J'w3o ,Ŵę rH`/`=ƿknR`ħ'DPٟR f^s܋~|r]V̙b) rG?sD~@žX3K\}œi}ǂ!ǯ$L)(Y7PSRS* ĩcbؒ;t h3 Kܛvp&d]7?r+'u%ܹy2vASdv>ߒžA?(C#8.Rz~iT;SC(ŀ&}lvӬf#Mln:]Ϥw(!ߔr1`W+ ?y_n|~Quqxuc^I8xK4_\zQ 5sV;[{ uH]X#Xܡ:U x+<;dex0 4fXulTXI & "#X`B0!y{9GF?vW?xXle8x5鑁`B0!F#`B0!F#`B0!F#`B0!pUʔY-&ceOeDr'XH  cr'~/x¼'χqk.c_5%4Wwi@YXp!q%JPFv`n*UYILiI1142v/I"#`B0!F#`B0!1s)rl 72_>ItMMk L\.G?)Xl v<6HYOYY _CDV`UMGTfll/\B?-|qJegUCQ kJ}0HI`"EN\]/87dɳ^ x4˲Xj0PYm{X$ ̻1ijf4YF0A"o&k`B`B0!F#`B0!F#K.88|^y 4=z_SMD"ǧ~x:-92z̦zjY''28D#.bl!n1 q? 1pvKõ9̙qӵK$zBxUT9,UY~gYYr<QH"Fs |WVs9s,1k[.2l e@E}*)I#2 W\F#`B0!F#`B)W\xoAM]U-QIsQ[ % X8'agxBe<:W$IULʡAk0!tI;]#x+nʸ$0Jh(⾙bC# #p!F#`B0!F# x)3;*2 dޞr,QV>"پIg 88w"7:yU+kP 8"D*%6v#Ĝ"( ./ɳrU$)xe${`B(e_ S5dQK6VTt.kXhC +|@ZN.HԭECĈK ,Bm}TgRxsxKٌOATMhZHWOf/a*G/p}iL|M@e':OG/>$JsxpgTWAde|euAxQG*]0z@8kٲ v7sjm0ϙ 5VԎ`I/HB0!F#`B0!F((s.H\XfR|R@Yu}xx1GgA;|yi, !hVy7.Ȥ߽ͅ!i_(~M'ܦ;YGsby*+ec5\QK],Mȇ5_rhZ<Ưsʪa% +˘4N2_dͫ*ZʪMEut"TB(lS&? C]AOѦQ$CKER"Uyz`B0!F#`B0!Uxx,8c\50̖X!=&:zZB*|2~ #z3.kh<ϯ!POYP&>:S o{ 2+%2<\JLQy1+0r*u& q%7 '.m=3eqɕJzj$`PJ@t(seU)8(s(vY3Yv &JzQdgQ`/{¯LJ%L)ɩ#W"M0byp!uF#`B0!F#HN߉2j.ɩ|v[S ׫f3'*-c0!s:xǁ3ƹ6N7//m$٦(o6P\S'?+l2<%%GAK֬:]'WR@ U%S¯fkl׋2ޒGzO))kuX 'dԕKzfe]SMü1'͂ hi*} z Z/RdqXbC4q`B0!F#`B0!F#ݴcgyc+w[ŏo.Q^ǃ^F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!y{9GF?vW?xXle8x5鑁`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#*/xy93VUD1aܜP\7N&2'"j. ey7VUԳ#<2J+'RBZ1Kr7qVOOgT|/I6zZ9 u];+8 *EvsA]R x0 yXO%l"8'eUyG/qMq=vW$yd"F"EߕGpmpSp.Sx?#)ud$f^F6Wu$*!u2rL‚:x*!mI,l+)A}!9`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F//g?h13?CV?t͏C0]lG720!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B'ÿ<,|0x_LeuٽT < \Gp!GpĶ? .ϧ|e?UNklQ-$&c+#eba mNP|[rB3rb]уFڂZZJ3YQ_зp_G]_$SP#Jm19 UZo5'ƺ(kl0^ [)f"&*l - Mx@z*L29?,%]|,UѰa &x /pIe=dYEdKVio(y)mjToa(Ax%:ځUe}B4k0E3%|yUqWRpWNqQCe4rTS4Z2)ʛ^L|'ᤊR4 ;@_{ W`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!#ǻ?[,6? OkDuB<`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!k)2FRl[0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F#`B0!F//g?h13?CUÑX~Q~'s[5TIǝP;[& `hӞw#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ]#NyzϿeߪ`B4Ǭ].?TO̹! UR4EGO]Yyc*7~i?80]lB:+/J?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ?!:+B>tW|ρ_>#O~|GΟ&KdMmmDG{cܗ4Iy 5C_ ȣu0]lGң|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|#|G!;B>w|O?Q+TܪjHCT~`BxQS~jiTZ|F:P9tRA'p,}1;sP'c0ǩxNxÄ8'wJ:/EeoFfvfa@a`Y$c0ʵiR_GO?nãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}>؟#ݣt_GonãO??v'~HwhW}Q/#?GۻG:~ߒ>?GTKO:?ί_߃Gb~7䏷vu';$}xC5tY':,I΁$@{r=9l|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>tY':,I΁$@{r=9d|O|>t]%W7E 6s8j5AG 3aj`fqѠtT=^4g,x' SIY'Ϣ#U?bk(}Uھj\usȍhڬ͒QM"=;o3{U5]r)^^ޗ3_j7T˦sNǹEǼltɈyNݱ+eO^ʪxH8sFA;M-cwmhFCG;@'1p]noLoi\4EKMgK]+qhl_ׅ\SgLf+LD~׮vIH:l7$Z97oZtХ?Z2M3L~^kp>[V"iԩAteƊ r_vyF9:92ZnGFQ EśMCԗƛ޼;8YV6vɴOoNt?_e>\7zٱiFeTTV鮲tv81GR;Q5d 2EMUv+_wJ -ΔF\e9"w߷y)j:ޟz%L;_ک0ۣiM<GZ,FgOю;xɓہ_+^,Օ-{)Z\rX&Hӷ%zˏ̻M bV!Cov.:s_b161njP8?@P8?@P8?@P8?@P8?@P8?@P8?@P8?@P8?@P8?@P8?@P8?@P8?@P8?@P8?ˇ_@NdMk;PFG5hsh㨔qOͶ̩8;_K3_4Ľ ]N=?䝹5\1v\:W+ aIѩsi+*m<-y2Wg%h?..KVJu(\u-`qVi4CƷ-ukLu_Uqc)5.7M]ho\:wggoG;_7z4^Z5%}"Za"a>E R8ԓڿyx7ɠ]6_#kxZNɖ?1'`;WTTQNuK{f%q=|;> jGSG4Ӥ m4L @7W|vDOg;lu ,|1ۈk\߳O: t8~?:\> Xgy3$Qگ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@vrQڂQ[b+f]lTZC.a{ڱ>T~r1'dH4rhl-;f6.W0|xyx$vμUWUrDz6rω;zdC?, ˇ%?Ϡ~Nݻ릋cGN?\Y%d5^sCfeRWyhm 5nuT`dϦ/6;uOKyw[fde'_W^0 nG)u{c @l7h^ Kmc|ߓwј~ٍpnbXw{ I)ិ [E5㏃ӛ) G6>=c姗6oۭZ\O~ǎa[5.1UO';/n@s-Vd'v۲KF&iߧ} _Q`u&\2R m%X caqe2ٖyuuZ[V#LoMjU`naSmB72:7t׵F˼h&c_ YplkM4 Z^9pw+4-( a?e3rաvrQځ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@vrQځ;P9Gj(@v0n?C`4#T2h71PA|mjZ-Ya^WTmw)m>!3R͎ܽRO1qI>O'Z馒^rJG֎Jtk~jn5~cyr߹G\8@Ϩ5/8cs6`^Cƿ׆qLӨpgho4d`(vlmEm{uJ[7VFdjV^-twjflC]NɘFhw"[U@v6G0_gvGNgw2Cn؏o$d[,]w־>,|(馭:I ;[Ϫ>8pݒ-+5+\(YK`~ 'm$tl[-[}AAA.JZjfG4ugxw4T԰MMC#1 9yGyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyP9GyPo!vg3l)1*9o8^݄_)$sӴX@]jtxy5H?xs:3l9킘_D~ o/Rtwo[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆~ o/O]/k=f5Sc|I}J ^7;⟃KSWK|GY nq?_Rº_׻>zkw86o[w?7ԧ~)1$?tw|qO%)+m{#߆ָp)]U]Z*fh5KS tG[rYYDDOw%#yiiq|Otae_^AקN4$,oiU˶ѻqPC,$1b ^hXTt|Yʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟqՕ5(ѡ!SuߦO_6w#h~?96w#h~?96"8UJ?5$nt'USE0qMWʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:*>tTt|9Sr@OG΁ʟ?:d#/ฑOb</YRq~?2ѵU(?]?꘿L7N9v) 8ޓ9M v;ulFt䳘nzt/ q 7Ul7; 5}u|uB#d 1Wy^bd䏘u_y^=;iӉi9wnvƵ[>vY穯׵GZi <S_r q4L-;1]+5X'w;Nv ]GEFx}wdGS_bopMsUF 85.F|4smZ /Va o_X.rSk^zvl={][m?cAUK~r q+pplٳa#e]zot;CF]Wznl-zOkx4Ꮁ̰W8.4pU`9XukFTY]m{%/9rGʃ@P8llsִj\Nf"7؉ V1`IQGCQ_l7GB[ӧN`}GZLc}(ipx=MZ+i~:MdcҺo^՚͞m-t0/]9@kX29{$Hʩxaq'í},@x^6xhٻ=xOwtUX":SjZkM鎩?h8o.yvqr qdX򎚦Y"FzwSfj/tt]=TPQ5cCYDL)$O{!ì|r qr qr qr qr qr qr qr qr qr qr qr qr q"~d~?b/YRqK?2ѵVq(?]?꘿L7N-3cxq?'r?+{ڼպvۏuQq1ɰuwRڃ.shy:xKfx3q.QՔvR8ͨb=-%}4 qj~=_jc&׬pOL\+Ik4nןNua\7ox]dXCRXZ9k۠Kus}w8;l7ޭi<)x]2̮͋oa*lA`ie{hoC$o#ir2,nVhځ\,&)*Sa8t!t:Ŵ.?[z!uIԕBGS#g889ߦFLǞ:,kEO֭SfQ]l^PGߚ,<*\)w3w1=be/[f2|'W:W\d:m;i㖏7◜}lU%/h͊%T=Eצ2Mls>۳;xjk+ދq +$Um6s;JAGF Ժ>6Cn-'ˋ(o^Ǵ/-2҅W8?Ah,D])hcw1ߜ{7"iQ+Rq!^i+VޞkwW\ꝷ=TW=Ƿumyeň֯u6&TSxKޝҰlRKۯ`ㆮHӼ i繯bah!gmf(T\(#)*$:XyI-\_F x=~1mv/~,ڹxqjڃS+玞s3vô]:t= tzK\ذqXʦƘgN5{w9]t>lg-' eIICNݹǰMI]Ǣ?> \makM&Aoz|q 䧚t}_n40n[ ,4 1*F:vGGl-;~`[?mms܅ e!Ž +]o9voӷo;NKvLSo jj\SD^"'FN7+f>Չ7D/s^b#3*̬; }ӄk)sE=Hs~3k7}mdz<,|8Flq.n^NYO,G]bƙ_jBZ#io7;Zpn¸uZ*Jqy.:9՟ϟ?+jjځ9|fNo nQP;SWE48k$o$k[gh |b-yڳ>o5>7k43<剙۳0fQ8cGm6&;Nr4|:_<1yf|Kͷ^Єm8jv6s.',|?'yW|[6JӤm}7ߚ6 2k ^g57 G"xC's>pd۬=+gc:2qtځg0X^;UC N&9NU;ݩ'xZ4H ߇h몽v{?D"jssyЮ/_iigI]ULu=W& Nq޻XtX0%-bfzo;Cε|[SOE1X߳yt<[JK[vLCj{&)7doOMoq|<#<5o;mϿWW EZ4H: :5*pq_SrIjkHyi{0#Bv7M`}nq:[BCg40vOHZ_|wj>wj WNAWRO= 'uZM_\%;k-ꆙe\O,sYMyĒ '9RKsZ}h:4um_u-11R}?ZQ[fMOk$u[[#'euYca[cJ2XǸh5/ VZh@[ݳ?)f ΥWӎAW_ܤ8R8ˈgӠl41+്1xdb9,W;u\SS:=P9P:֛[b=g:M.q"9s :^KKūԾ o;~;y;AW8^ܤv-'p: ku X//֚c3"0y]|_n8й/ԻP}VC-"w5875+s_,kЩ/'{=M&)WNygn 8,TqQo'@όu\MUÍb9ci-{a 㥒`SxbtKZyމqݫΞqݨwj Ÿ,:-eUauf'"vJ[7q|cgi_Dx48^GOo{ysqu*7 ÚyR֧M1V"b=(13:X⋤4WD "?Tzx+5Zx^ɍ:O}Xdr{W>yCۖ; KpږYMSmulEISܟ8Ykxv<dž}F++G[bm 8eVg~+&DaU)e^,%ijJi4=eةyOF%[, F[mLÕvkx[l;Mt 2fǒ2N?Vz5噾{9g,/ڪ]stM)`APE9y{˅AxE1Mrj֓6"gzK`VY!.3RtڸM6]?^x7~oo_ ?\Gm^ѽ3tLLO0Nf hbNݧs%r}'4M'4ے6{gh[xľxɮ3)r͹c7-ڼځvb@9WHY%$琎?A\(.!wE?N{.c1 3?3YW%Dhx^sjܵӴv?9aǓ4><_=^ondfaXupơy>3oQ\>*"D~CdVE+Q6[b"zhtprQez aS$my\\w-_@34BN˜!a~]i&J"f!ݮX_ne'QøzVbk#6=nj][* m˦y3^]&ZM?5}&_]4OQpŷ꠆]M&|\p^3ͣ^ߦݓ"x1Oh˶Mc;#pKSܦڨyLii<7ruv?){wraE *TRE,@\tJnYpײݸvu<:owj8k[W $:1aAM'[oM-¢8-fA̽O/ŧN9g>ޱz?2*{D69$~čړt8qaV'MEv͞}xƸ'q\]5cxDGdŷ섟WcQD:ݶG>e<վ8{t~iri-''3Lwڼځvkb-aYW|&'^4Zm%k&f+g^ǀq-GkE-kL3a#٧ $fO FpM'fxyX߬O#Mis]o>ԥ ZzQ^IՇ]6iFh˄⳶ިE6TbYmCqu9I{ӈ Y|vӊME/cr6mܮMm~َ96qCNeڋ7[ƃOo3[& /W5ê{e?LVGXmd<~]"LZa AG)th/8NxŗwVڽ'=#UjيWԺFw|+K4L&DŽ:rimkOi7+M})'<==_*.X-=Ddjzz]EztAS|+U2GkkUѷd6ܬ<ܷGsiO\Ǫ{ķٽV⤽?NU({vղog< byomtS?}DsF9nkضcFڜJ":gG2& aӭ a-_| i+1nGkMzxDf?v2Wi_(ۍ* Z&=̦s=h".wlv MQ6 0:m$nәt+liyYm=Y/J{MVh-݉:x9+=GZKnyDGu&=W߈qym5g}(- v(lVTtW={u$ rj[6YeS ##hw=j+i83\oWzG+`tp:exsUƃ]]3:Omg6SS}\,C ^pR1}OʞW]5YmE3;;6~M'u1chPCQ]166\'A--ϩۤDGڸq^qǭ=hq@Zz8ց8qǭ=hq@Zz8ց8qǭ=hq@Zz8ց8qǭ=hq@Zz8ց8qǭ=hq@Zz8ց8qǭ=hq@Zz8ց8qǭ=hqAk(/C_σ1u y[o{a5doOڪǔuL_OS<>yܟBM=_?ܘxn X_u+d^ L"wdg10"rIIUWoC8͚wh:~i5:N4yː6+fhꥒe+Ө?9w?jIV@PSyU+\M%†hYM,t.|/nHuD[-&OO+''F5o'sÎm6b5 5-a^In .ŕqx 9gs*q|t8Z',r`j;Mf o'{wKzI\^l~k=WKǤHgա,P8u,H'#KǍC,i mXMm*4p[QI!/6_SwW[XeyO^siʼoAL)v2ڶ1kOí% ط%/~ۨ/t:W#jv^{',r#k8zhR@?=.q%q[=M.b!V@P8UQ[M-<+ oc4!}yYw9OaC-e%]mһSI=qS&r_׷7fL:Ŏ7vYc(17jy<ҟlvhN{j2NK= G bƨɧe܃Ctl+h8n|CtX%qзyS/gnE6Z)pmc=Aŀ\XnF:ql!_y_u=Nɬʬă]8oGTmgaj㉉,vXo+ {OWw~ {lfCaE\_O]Z-ÜF5ڝ=qrym>yk|ju7ͦƠ3pa] C[ +šAVT\ [Q+97d5@P858NyTϖB%-$np;оG}{L{YmU`htizρ+tck2;R:!!ɧlE \GQ{Gj}~W?HjƠ}ᛋ,T:")`sqlS_6/,,W \'xxדnfujɴ6[>|>h?;no>Xʷ}xܟjqllk4hѠe_Ab|ʨ`5\5U4#vi__ƿd6'#Ơqj[:{ A[IuqnĎvσA_`d0nTvf]_SAf&Jaƪe}Tso nnVs)ţsRrPS_qTWּ:W{V h %ŵ{Gqck.Oj oINOj oIq)-TSEIMC WӍ@P~ q笠q笠q笠q?85@P89@P85@P85@P85@P8rG E+c儓K5F}k13N5|}85̃Dt҃AvqgjڃAZٴ߸v5@P85@P85⫤wcmxѣ]IW/՚鯴OPq^Uc0G+_-}s︟ŗ+pvt5g p ]4'EY5~+Q} opϮg-T.P85@P85@P85 A=HjƠqjƠqjƠqjƠqjƠqjƠqjƠqjƠqjƠqjƠqjƠqjƠqjƠqjƠqjƠqjƠq#meM~>[ؿeGƾ e?jQ~{Sx1~oʝ=P0Nv0et}Fw4Wq?'r?+{x?Y*~n'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏx7ӟ*@X*@X*@X*rDوCZ4x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T($dP;IS#wG@'%OЏ2OJd?B?x?Y*~~T("Z1ۃ3VN8"f`]{WY෕wʏ}FAFɔyGQMTa*ta?\z'z')Wk-7#]gV*>5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ!n~/؏</YQ;e?jQ~{Sx1~oʝ=XrO2~"^hZn@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub nNoq.Ck%=WI M}sdR5כ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇAaku1P[iS3ˇhA9[1㜷WXbLˈk?OToوjN9] ɿIh8shG:.we C}6\ľ⛕탔Vw85=N9a#mC0Y1}>x ݶ]jGK\Ku.K Ko3Fv|Nf-Inj"QsWST ckfdk&Y5#h1][OqܱˇZr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇA^d`#X2Iw̦\S^@/dˡ;~VI,"8Ӷ^o]3G0hkV݇TʼC1'|'Uͬ{!-v>dݺu]'ʲ[ǸtˎckKy??hV:S_cS|VrVe7a=zpUW49+я~Lbglz`?,cPRN4fe8Go ѿV=Γd9|p>e˦Lgsfzfb솃J*\UqNMyY)-0m~0U?S&M;#xsV#K,sAoΥcQ~vH1k_P>e}|Wܚ]'̺/'ڿrk[qwX2|;jt?[g=Řtb|E;]_\̘2%fIL*W#R4]>e皟~O9?nqܱˇQcZ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇ@01pr?[.8Xe֠~lzcZ`?,cP?~\=jqܱˇ@01pr?[.+F6+zOOE<+An+8E]L2(%ELI$qq%[#oFT|k7oOڪǔuL_OV̟עq?'r?+{y۫A; PSoe^y)n$T7j2HM팞wӲ. J[<ݱ؃mu߯Z(RџLUj}D g;!j˷$̲l?|=Զ l5$dڼit,Dy%1#it߲@В4<7.GJQ>w*xKg;fX/iaR- Ţufl3iVlgon00:'3|=Oo[vϥm5%9t [Dp K6>5KXh>y@l}ʃeɦ)S4nW2?JQUCi#OBF95ĵ5c;Gn4s1iG-x@fvYUU au4.AnEAe7?՛&Jho7RrC&_ o"> ig;dEsgk٨8y57UdGI$$$rXySlm]M'HdoH#+ocNLqhUqRCS9X!sNRj,T1Ӽ'Xc<~h9)=; T4w8ujsA໐8M4 w; رsӲ>|J9 )XΝuWS_n7IKzY. wq LmϒTKՖ:xčiЅRd))Zv൭޷wji-3ݲDnd fI'Nk6T^cep8/K<r~\ǔ h;~' ҞXh)ɴюՔ pz=$wy5 ٩\Ɵf7t\WO+Cg@g ~$})(1%W W{TC{ruh޲l8$Or}6C `5ִu9736C|}`6":9Gŧ,PJ!n~/؏</YQ;e?jQ~{Sx1~oʝ=XrO2~"^hZn@@@@@@@@@A\\&#EatOPvӘuAf.W,/:J[Xz65O H湛NvjPZlťUvفT@bӵ·]7:|OMy0W߅]-^^84: "]˜+s 9j^>Z8z!nkiǖf~cFc'" K=WmrZ ]8yI֍F֬Z LSGl\qXNkOEh&J]֍wY$].cx-Vg>E#Prxv^wg 5<ʋpdY޲/C-[.Q?K[f-gCĴu޺N/wF;'{Qµ_Up"('SJWST`3Fr+'h2G/0hYW|itukuVeKLxvk3.j,pP]# \i5퓡!:'KuZYy89p95bė[5Q5nin'pScx]+sL -eF($hŮ:U15QO;}y^vQCokk9=UZpDo]jT?UT5mlRI:s+u3f\c_`tUl{6,Dohg=z$ʼ%p ՘-ht1b92['-}?S:7]G/ck8@x [cAÂmSrM~Ԍ8k֭cYpuƸ c° =L#n[}At_ 8WLhaLY.٪ _|,aU-NAsaf;ߦ5=0|+5:}viApWaSQPRϚ)wS1]gݮ3:΄{64W;wo't}3y񔌴IƶRDO_ZO^ֽj׋Uv~iW$bK9n!5G;4NiVX:fȶMm:(`j׀y½&ϥqܳhk%5k#%Z\ڷAŤo,K=e̗I_k`f,|Dj=U1=qyنb vئjjiUtkugi㰩5ytj:fyk2Gc68H8q76k>IYVHڭif clEuOr NX&B0_}5x-l__Qw #x?FWJ.t9mf82hyoY'n\0D䞭*-cuKhCmVIPswK㴖p[ME=2VuA-I'H!j4tV9[IjƆ3G#x[s9'Z'o<>VѾ́hd clEuOr NX&B0_}5x-l__Qw #x?FWiHy]+YWKvj1Ӳj i8vn*Yڽ-z,ygɛ8c;Usr!Jidnp7,pbv٧$;u-V[SS5-'9CK N~h~Sդo3Kj"e 8{aUpT@gGlzeL0#sQI=)"Xff'wl}b^wJ1EK)ۙi x>-_mndҕΎ*Ef6̩g}$ \?2a}Vj_,,WN sdxyUOǶ\yۧaȜWQu2jb0DPG7vrȶބ;l\*?*E} I}D1R4,AMnZ&h~EZn0XPTx8j㦅j4;K|󦈞S x7K/2Z:Yjq ӯ@F128{+xnx.dzi?lkm;5< :F.yqSIiE<1pp[fxk=pGV'POoj.k( ?I+x{LZ]"}I)󋆅.sn QXimvYS/H&=sjEk^bgɩ_!OkximTtV jafRnkӠ :hqW'4ϛ̨E57Ս|[*9ڎX5jw);USp*a0B#zzy4|Պm=]Qaz;=,nb#0;Ecv\5NeU9ԿKVvF'$˵V;)bw~o?ѻ],նTqD%Cl9zt\WuR4nygtxFk4l6V6GG$ .2NNe3,@˞i~_3̼_!8ؙ[4tC_my 'v0J۷UMNɛN_.~aoE--41m  u1ho5-jŲF3 x6jiqNT]7~%Ⱦ7piןlZ-}̩ɴz:e1b`]-5°dZw t'M1^٦#}v[L*H,qq[eo6o㌗i @4xÓƞwg}Z|6 pv3tMuUFbXiA 4o~5=ޡeU"vm]o t'bwQVՊf=8lm9G~I1oU4ד ;QgUd,6=엺,\KOm!>FuhWxI֔1mOX&V*؆ho奕R3yCtjzNs+>dKO4W<&e:\)]g|a]X- x5/_B-f>P҇KlWZ$ o'iL3Hz W_zdf˫FRwRMMWCNgv:$#F]we{jOn;zjWOzF=޺TV:ْͅ+q5L$'MmUbV"#GW_-.r͕:݆0-LnqI^al]N,UH;!+H>n9+$l5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(8wGt{XKOQ_vhp A+ ?Pkyx䭞˕9S#[ekmazVjdY(r݆#]){MFK@[5Zem:rWO\.pj9MҮDB̍y$tpN\2Z_W̨Κy,=ROY+pm }јC1Md29iٳj%jtwANJLˎOLym?/Yy1^)Wίnz[#! dkI骱g$i);McO?/KV֯<7XQ3%Rwv+j+>̷ˬj*67Dggх!wKcysdi6@N'Qּ䉬[v{zl:{a5*$qֻ)M 26J*x;Mʹ4+Klvu8m#e/g[j1iauz!̱}VN< l|!ަ]Eck^Hh[ؐNNrhgZDς2󼽶<5][+T/I:}b<+7lu˺ʪkO(,Ơu;Wn79+t&2Do92k$ -\̆W0jqK +3hVZ+5T-xdi#~Н5\?p m5^rXjPCCo; -62X>0X|S蜃FӖ?~%d ?Gxu y[o{a?2ѵU(?]?꘿L7N9'㋙?wSDzO_4~W-bV w??>3eA8 bzJ7uN)nSR49 HkcΓhv>NsHf i9_5\lq] 6M?.9|ߒ<˖X? F;P~ {೏b̛`2ykd5Տo;@n иOGbS2[jntr#hj߲P,-o" vw-1ݷahʺ+cd)1k o C  Ę~j3WO.7lk^:< $N׉+$M1=Sr7+<[bdV_vl,w4@5e8ا̥L VɅ/ 2I[MdU<\4!WOSgQ"h5leyG=kKkW7٢q+ \5q:G\3j2/N=|zDm{'viV^/D,yNNApQi?D[#oFT|k7oOڪǔuL_OV̟עq?'r?+{y۫A; PUN<1.˜QQU(ێ#~pݮG<>{T6כּ⫵d:j֏#Z@ 8ڵtז<(+.0AjMp2[R:xd^վPAZ3pgO-/L;>lYٗҽˍ,S:oqz-<\~k\y#%yCQEaa `T:ٮu<-dCMN)c&{Mgyu[NՍK2TYc:s.6d4;Aκ6ϭ[{D=X'iq3Yj|˕McqIkjkyǺ#p<˙3ŇK;m};'i-;^fTYEI[-vCri5Vߜ,|x#nƣ*7<8Wz:HB8ܥb͏KWjkw,w|)*ZA vtQrQrQFQj`11푍{ CH+gLq_ZEOVd~̓3gF5@z ͇Y:t]Emj'|(n|X'jZI,RTj'DZuwNE{G1Z"'Gio\u]•RESN<#G7jF7ȼ<87 9{!nP7qPbxvIP[_cdZU̦ R77g&Վu˭rH6:pq y)LsohhZkt]]MZ4u434-/=kY;-g>-f.̛ͺi--[z$KKH9שu? rG;_iGUhݣ mѴRa᪻wڪc59*j$%Huqǣg!e4Ly/1t 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(:*ۭUnM'GXh' cy|${U_W,{KObcR<+O4ZW6Y KZ튑Zͧha3k57fHٰe^%[+0a;:Geh^QyH=QI"%v֛#UiJZ+ZaITkO):j:s'<> }45p IDhdV}gV3"S{պ<|:m=5##h7ȫ)APVGU#qْ2tsX kM^+ctٖ9Z&f mnp66 yi5>op.nOWKY4fXSR]p%WST$l vKIizJzoJA dS`٪`jjY2qU?w7+ m_5Ǐ/􉔨ۄ,yNNApQi?D[#oFT|k7oOڪǔuL_OV̟עq?'r?+{y۫A; PA$,X@PEU F&ae=Ɍo8Qטg_I;v|pO[<,sip2K'T1F4{4]vigzշd+I{W[ JOU:nWLܓY^3Mqti ;Mp sA 'M7%|i1Y,b1zɺUJٟ(sNs.f' '~Uک.NZ:3lpp$ t ,x#y}DWZ찆˗uQK5ᣝ p;4әyGzLz?e(˵իTCj08bEA@yN%B8Qo3=[lOٷ(/cdc1B A1|-fϙU]Kc/= m'e+ adl?_bf; |K 3Ka8 hI|VN༺皆R+*:803QNlֶNXDk]ʗ/1fZl7o]90Et_UM<٭obgGTr]-5fUP:q2_! Snhٮba,[0eedzag߮mj&٢j1v!H+oY 7z܊f(5VE%3j$p\tzָ0Z";ypaDqŮa#vS{;OQ_bo6oKtjѼp_a_:7_eHdq?C]U~hj"yg[r5Se&Tf<ؤۨnj[H%4C˙z4WZN]oiz5)ס9r^,wk“{"tXK|t&_hO?VmNIˎ* {4G_DcF ]]^%%9%ҖHi%7әSeÓrb5<ԝZ[ˉ!n v\bvFJCkNNΧ 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(08 ɌҮ%[6u ex:~ $d6hvRBm:7TKVxYsl tԭw5mFѳ}cٶ ?st4OgJ+|7xsx&'OYSY3䔴ZC@m&6*Vk=]䮓?g=*chM-e^imϒ'1Ɓ(f zk.kMm痵;W^XmqZmLq'.NjL)+{5V "Ԉ5<#ՃWH. Qɂjn|x+Ie ª|=|]fNKd'B}[ "3jSkxdžzz}*Iq$I:zWRc`F@{Sm],g2K5rZh{y+Fe|^HݳG.y;=,|٠  ڙ3VRmjaO<ZY4.|oʹ0ATwg+,vKpqM]Hfe~Ǩ7!2M""uG~/1ž_vPPL,d6u.UÛV"|҅5mzӴz;\P~s|[} AM~?>4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_Hs$a-#B<}yI<vRbMia%s:P#'P6A-p߸@kji›&R&;&3cr\cy:uzW91,=ezeBH߹t7i̽ Rǎ6=kL̯an,[j؋W\wﬦ~SqmLV1ت7\CX{TKRj$ҝXu>gPw81cZm5"ԛLu䢩dѸ4RbѴ"v%B;D yQ1ue Zhi;%- bV:8i&6[ ?aQ2LiZ>l=QڌR^!ta; |%0F]/?i+؈!xAJSX-w{%ifFc9iss:h3=qFXNQ]ꆲv \P[uQ^eq59LiҼ˔X?PpUU fk^j31R1>Ee;ZGhQkVw5kCeJImYfm"g ~#b/}h|Zr~5~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp' uѠK'azڬ/KhŕuӇNiֺ= ͒#,ij\-hp[hfmHlݓqVY4h3?VyzlgpTd, ߧۨqs AlZKGhi<6񪟔WO=o<%/Q PGR". p:v56X:-.jN$UHMu/!u]04=,<- 4R{gs&Y12P]ꡦJlqD8H=NEn9KcP(7JKad4]˸&f= DR+[U-ڸsN`ҽMR|}LD>3)V {-ZNPc! NhccӿЬc|ĵݫdyY:vIVH";&fgyu2WI eR7)SXѻϻneYƼ{P,rg$rm;wXf̧C)#G̣ۛy&b;e6bSVM,wX57i5P3=tzJͤÛS\NggQ֤2dםe90FnrV/ 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(+gn8;Z1^J"1B5 m z E3f2Z>"Z;^\JTӻyy:Iu]*!Q2j.2yitM;UU2O3;;Ɉ=5~:'9:ՋF~ [,tEDдRdh TL-Ϣ)!ld$SMjTF8䣦$%` yY1OWИuTN k8+3Դ6zj+!t&7Uig9iλ+xߗ惆_%#ö] :Dͭ O<#/>ˋլDygY"eZ) Za:Em.{5xo+<#RXBU4cͼĵw")|,tqI.DF+ۻ^g-M?pݨ 5?~TVz {JLmkAirޔkL>>9õ4oSВ'>c:6J_])Z[7jLq1(,vx+9݆-C5i E6z6e#Cuu\ײX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/ ?ocُG:J(lkvk6a\s]:n}4Z;}PF&|<[wl5E8kYZ;KVgT*` mln[ho,"7Y|fd5F0GlR['<["|nvq5~3Þ0߳>zsBy!p5qaKpaC+Lz9|w-0_P`u[&l5n*j+8%w-8f1cQ-U1ƨWIƾ#\wj4p}>\+dZVJz- 'KT脛R u\Og鑪*I n{P^nuU,lL:tyWwT]urf%ܱ5mIqS -k=ak,et]*#/zn=B]8s9{\[G]WnXOoT9hρ:=l]եje{R2HcTa|-S$]6O_5=vZcYVl{18k@ZoXo o-о.:=oIb{Ln{tqA:?2n>xfzZ8Ayi20|),L4Ihь|wwV*4^Xݝk;,BVef .u욶]<B54U]t-gz׺L,V;#Z7]I  tu}~snjGx9q׉id7zHp%e/9s ~Q|SemԺ>k Ss} 8]S$1>Fm!teXb%zBpV=KEn ѽQ'WiQ1ܮuZ\u-s[$L,K`6":9Gŧ,PJ!n~/؏</YQ;e?jQ~{Sx1~oʝ=XrO2~"^hZn@@@@@@@@@@@@Ad~~|fq@@@@@@@@@@@@@@@@@@@@@@@@@@@@AFC vnT  usC ^nhx1R)'tiެ?c ;'sqB{:aڴ9:>XtזcEb5`ѭ%3YX8n1I5 텑}qBavu}Ln/:(hYcU!{(3&rq1N˙ 95\oh|{S૤:Y$h״As6f\DDŽ`3pyX;i;w<+cqQ;LvJU4(u!5OO9Fwݿwκr'WE-а}MH]4c=iQ ծe(͂ s0)lf'Qbӵaʋk^,6 7]$.{ %S-#9fh.է Sa ˫^JWe 4׮57iok8RlLۇS6IM9`kҺ-#%o2S&gyXzh.2vH #%ce}gD2W)d@#h揣vv;E yj[ fy W=okSx ˜QگhVj"C#^6uי@3۶<~Y-.Tu-}̩C3{5yAT֬fڕo٤לg.KD[avr3}@Et|<5ՆS8v`G1]m5V@Һ"һnCKYӜ3=|p: pͪ|Tr\Vf8Zxbuu]=pDmm*{eF<6Icj$?pH1 xv/ 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ1uziUsm:2JA̶c^ðb]lum-@ stPfJ`6+![taZnW,q>fOJ %Y& ڱqʚ+J Ul |Mqih;'Db=+1g| PM[.Ӂ#VD8Nn,qnLK*hI0-:#CQ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Ap+_jmEkhuo5"o|mvלX͌'ceDv:)4Ӽy5^;ےm*iDo覴VIEU QNp q̯+1k{9k_{7; 3b\{A9m8jtZ2gZ;dRf7e-xn+em2&%#dY“rفr)DuM;j%cKZ7U׆`u?Z)u2-7ZImxD h.U3t=lZt|kVzK{tL&mMP$ NۼyA [3wY\F+yi!dQ>P}ͭ%gC\z:GsSWM lH'p #y4ώas24^`^o"~OSK;ǣ'G/I\A\bt@C%aa-:r<ļ>Om[௵K;7^PœO&>kNTNC̶/bsSܺ(LZ>u['V-b_W֖uKv}_e d_FNMcò eV  Ub_[e3J8;zkjrEy"ӷ-wn@@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wkı2hlpеPGhAE<&2&_U+ns}--ya--d`npyu3#wX&^lT.r@>A\װC؃*vi[bMxۥ)vGON%-ps]+Cyڂ͗ Uin9hrJch])j ޗ wy\\Vgk۫m{6\:[-9Mm&;mWݨS7c1(E-/lK&,9$k9'RCZvЦ|㋈qx|MRܽU' > )65c=5K fvn'B\֜ԘGѷ҅wŸ_U4U!yk֎7@X뎑ZB-3;˭ePn8+#n468::v[#㦨xcCo;E|F$Do;LF8Hw=c~[}֎hgIy?!Wm;L6EiS7mUI}ѦHcdPWEZ3WoYT LCt4UUpF\hf/`9:ѢSx؝}*=&:̽sKR뭶kSTlq> N-jmU4͎-7IU}\k_iU␶w{vi?Q9~7jiԷ%'=?Zi~, ^ D6>V8ѮyARzhb=Dmx;g%-V9\`[4UuVKN6@oKI:uTc5ћInO条L3GM)?Km QKImIq:N&o6 B9x#3 ah%ڎhF:B)80ǒqۚ*? L[7f2[gkjX뫶^>}hO ͳJͼ-rj0_?S[ZL晬y']ßw2~}Ys>n)e%[(nv:=K pAn:kϽ\UQNlVdj⾞#>:W2}ATn>TԎh|zd1oTp[`,6 Zy"+ذ6afJ4Ctԝy;nڋf zk;p<;vkq0-su_s=&Z9geMnZҝ~0^.ǘZ5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(5«Tv(T5SS_|I#e'Ofb6zX_#Vm7h7ͪ􀀀0fvR+Mx:#ia OYpDi<9iIxpen[K53!ifk4޽Zrʆ垮{DD<'h4 ~fXF|gynpeX?%XNj7iAp=~Sg|Ϳ\xQ9]Oif7tۨ K]y5vIƯ=DmF:V*`]h*Q;XNQ&JͩYɔ#+͞#41-p5O;jXoףP4|SS8 b~kOTlԴiil1F9֍I{6lEchy¯&9nXv~eF!H0>W#ByҸfo޳csڌ\] y;&DeOp7Jmj*t!GhA\UEm U"AW'5` pHXc-yi9/|3;MzLZ<4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_ c,6+ֹhehn44}Keh;]4$u+aEqu=ViS?|;{56$Ժ7LSJ"ih ._IJis7رVt͗Y}ּjlP\7ìfoy:sKuP5]&GmMzl[޷GckEMᕖcyvz5#vf4,9q})6]l=o^x;]Ჺ*Ƴ[]^&. ^ͽY+jub<}bØW^2v$;>oʹ%t_.c~ Mh7s0h1̊m!M4kz> zO/ǒ2{R JqO[OM4{o3C?{#UM+ts\Bu'Io̿^{ImPm 8:S5W%N޶Î'xyUDZ1{Ci:i١v|˺\GoXm [ޑzʴDZpڪ/UW,e93c{$d>iok9Y0,#jwdoYiXGXGt8k,} V\U*v*,m3t ̼,dG>v_#QnZ|54W;Ɗ鄨+yXsݬMV鸝tRs q-6[j(|T=3Fά`5 ։פtwVt#mQ,~ъŲp*jnr&o;ch{yrt|+%Zk."yb7XN< m\ dZ*SmAY@^;MAkj9T4_gxY#$nKiU9FjM%|e !qYp;MgEb`ͷ,#.!7c1 ] QR>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/ Y[oLqeD5tR4iӡRirZR2WQ'u܄OG]u$jy՞γvX<\Lϋ>FV+uW9a=+ Mk??Q4z;"[mfLm#\/m涑wIs릊 iR)n[7<d#$A&f-M3_ž"7tlBWgwi&y Gk2)_n6R ̺hAs {H {sb[zU%g!3Yss_6eIm=!},ad)kba%'^VǨWuWSu=K#\I'zt6X7\f/UU*%KY!q,'5зgkqz`5?J͹*yr5;fQ>owJJIѴ:hsc}?̢ܻ#)$F*V=l݆֯Zioʦ>0X|S蜃FӖ?~%d ?Gxu y[o{a?2ѵU(?]?꘿L7N9'㋙?wSDzO_4~W-bV w??>3eA8 oTr]u?ܱe+7V3o/,xYfe}bX.i%CѮ~ݠn+иg wFmDߤv,G<Ę6:{CY9{Ccz6wT[M;"ݿ'8}337\+ݫ ElΨb.qtuXq+6fei7j.*w8er>g .sXc,iߩRrߤC*zWdld[c7 ^/e?w$muJF$eC":1i/>qL7W|{sVze]5sR'}5t&(X%JY3@ Ǝa[L)tIǧYy];**=UAtGxO.:jyΫҶT[y^ Ἣė |pZhuk[<')~WCj;b:4u̮ 72w e]o吰1I!5IS4LڻMp}zӭئՍ,4-lw mSv.g:ї^q䍦ţxqc<-I\$SIM&8iΘN^|sDļ K -V_)!WۮH,=`5V'h"|&!=d.l h;1t7y+ѬOuI9uNtRvd={ϝAjri2F\sն*c+iokj#J>vJ2xEvb#GHߒ]UfM¡[+g3J֓7eKFY葏 1aU=3Ց|ArS9 J&;GTJA!$7I'Փ$cS!ɩ֢8=[{Z묎 fwS}W~[3K fsjtj=wug]Ԋh,|pHv:v.^ۓtq3ڱ_O>W\h[l`}]Xxf4 }eRjuYxx{"!**,̜U[I<u ^㹯p;mfč+Ũ`NJŶz+OI̊^(Cn d|gA7 zqX[cNp.V%e3ROI=%gZ7óۢҞ6s:R3ՎqjN^bqɊ\+Q?T_ȥ;ƀw#͆N;?xm=a˜e;)[4)k\z%p94:=ocYFJ[Quٙ_OrU1V:'i!.nW?KYX捺za&-e3}c ^͎[E, xa:Aԍw85VY][jDr{f(VڛlY5<qdVz6M;eYw`}59C5$!#A>ejFھ+kqVv_p]kWCcqk-Bѩڽ=yɝ5X;oVSVɱY+.ގ[n|emرKg@F#HZj'n>Y+mK- AM|9Hj4'Ag͓QLCv*kN)V0xX[ >y%;}G;`[`ɨ]兯ZF~p 5WL1״4WG_u3]:oؗOgGӿ^jjqdw-oDvFd4ܽGi)H;uv/&~@$I֒x5pYgS\)+[22hvˉպs|.SUZiYK2w7ʷQ E h;r2F3oe8s6VZ44lvۨ]۳LJՂR:@;H%6kş}m FXnC{ #ϘE9ѿʫbIm͢؄g4ȹDI펒&>0X|S蜃FӖ?~%d ?Gxu y[o{a?2ѵU(?]?꘿L7N9'㋙?wSDzO_4~W-bV w??>3eA8 yD%m_{5_cymO6bTUɇ\푏ll:u 4Wh톱 eO?bӪ$Ys]s#$-ɴX`iӏ,pwntV ӖjLCꌠ7;t륡*]p4h֎2?bgSZeᗛ6cmW}S[F$p߽aÎfѴ̲xUj&k]Һ bwCr'!'yR<;WkzVsfbMܥϣ=[7c/fdž.˷\e-0 gl%+W`s`j>sHqc'qq_V՜ckGVX3O-p7I*)"O[gR4un4jIW=TiGfI 9`,nN0:@{Fx+7SD~^^cLB5Q= *ᦊiAKQztX5[>bb s]tm3DtZ5 t ~=-+֑dۤI3~$tzշ"H%vFp>c}jrR~*VZK!:z aZ&w|Ϭ'S]hxYZ.]{aMkq|_gN.Vxn3k׻oN8Ic]ׯKӬObtVfdW]-sT-$UOpdwRzդV"uU,;w-4q2=F0tSNƗ TXRZClu5(QqdYKE4fOmXx"YLi+]r4M ;gMNr|&/lb}&6[~ W'UZJMm`/S 5A.ko=zv&oOvJ Gj])"ajyǴU}/jZ-Yab&6x{U]]=Dp])c;JHGFkAմuUtԕ<qZ9g *`lTϖ*k#MïyW[;l0x7a]R e(NpcFvح&L_v|U[ius`cckF澸pŨMo I5 s3<`vl鮺h菴=Z~{k{h\zɭy2Wv8k5D@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/ ]_/n8d~f2HœZh4W%roIyMg]q%kuT f@ԗuWWSwTM&"y-g&cj; onhVUz4`ji*b[ϳS歺1vk8N'f:p ^̙)pNZ;(YeY'&1W7F5qB}69#Ϳ=Duc|44eu\t6bz+$s67kδN(_95kk׼Gt/<^yjvdWՙ*׳C>Ny3P4i_*oL0:A4w7~tkV\ۧkn;G4Կvt&8fuw9S1Jm{}£5v]fvl[e#]%U[xsݸmiQC,N|]#vm<яQ-yhlڳc ˫~1QX+I WSGa9siqyF^5kjV0mJO5YF5xbTESt7ִ;tMy~ uU^ vW?>boT 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(8k 5T `ٺ5}waVe,ŗl_GV r:@$./qt x]QO_ɂiYʆ6UD=LF49lTZhwCQ_"&'-GPKӇ;Rַh$w/bPvNFv዆n g1Y堂:j*Hk̸|#.-eL)#..V]íI-p{\ǘBMcnU&2mmsGN&-K[!hpi;9#nR۠:=+daN52SSP]?gW[:l=26O۰zD>u;uR8vɈIj%qOO5²Z`|'ecL11H~iFHaq-vt$,=gt/ |w[ XͦsiA\uIɾ앯$ $t5WADV2s[ivvt#i]Wj5F־n׵O4\ƗljJkcf,vtKbݰDţxd:Ly!+ϒr.%l6myi]90FnG ~#b/}h|Zr~5~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp'CWfdQ ;)%RԻ۷Ǥǩt.+lz]<[{TG[匿U@67vO:k܃8rGl#<ࡌķ,/Ycf-:_Bb]o_  a< [ mR'?Ty4% Obvq U..n:C&ߠI|hIӠ-S3/XuԎUNn)`C@wJKOdd)f]oaz$Iq/䠪tRn0!C)ت{Dq1ƍCȗ3xY\1T+j :;w-W۩4rbw2;N]Ǚ07#O#;:oQseە;X.twuΡKD;ciCO9Rs\vLN1FX n @kjnmn{] #̫xk;v {o=!c۳(rEJ:Q -l)31y}=K~\wwPҋsy8ct;ԷyV,噵jGIO4Ox2;7^fm=\hq3u85Wg6-ݢ; 뤽щXM9]u޸-^8ls<N3V"y??Gu0+mvЃrMfm;)hw |m-3vd@y OmkY?ӛcQ Ksh;} u׷_-w:xY~zDrb\uFEUьK79W m6[b=U5+(ɢ:"2O+!sG"g}A @@@@@A:1L Q,qiE綬Y5>V ;dam!'8Uf&g,㱑W,,yNNApQi?D[#oFT|k7oOڪǔuL_OV̟עq?'r?+{y۫A; PjvWL-v.e-klя Տl#%|/Hye? ^)r&njY$ x1@4nyg8b*{R;+lRzlG ݮ;GVDA<=.47gan˷8t-l՚}6KOԉ Э:wdmP AReZE#a1R7޻2뻖|}Mȹ ,w2ՎmF%ՋOW= [Qpڇ9x[;F3kъc.f_7u ) l%Bz|#ƽ/6ݫۧsu~/I-֍Q뮌hfYFkR6GYЙvn2s[pJښqQ,k۽:sL>9Iߙh9+P]ꅧ9jwjçm<2u ڑ4O-q*S^=рsFUpWOƔקʩ4[WRӼGYx^UYF>t1OOWXoTiݭ.q A:Ǚ_Oa3~3RSۇ;{"=Ev!AA_o\IF: t7'_D-v[k:/yت3f! \,Ē9=k1i78pWYW捺"f`̚VܯE d %s]j eK*[=0ԷQy>MjgIDnՓ-.u-Xmvh1s{sfGY3݃Y]!hhlvle 5ށxƭ>żu#b=-+ hz>#vM6գEc\TWk` clEuOr NX&B0_}5x-l__Qw #x?FWU1[iC n*z:o{#:+LY<֖U7W?[&݆]]4Ѹ{J%]Z9$0Hݦ:5Z1互 ~ ;5;c :T]lC9 {;t/s sxrigcf;vFNӻxd-qRtyMyט`r'x۸)r;u>@Uݗ QKDv4Z"9#qkpGB7͙܏˨3W8, X:H$eA90#ι!q[4y=h5wke(fֲF/069)ZӯCy xMUF(1ZF(PLWk{vmTa2[zUL;b۷hsLGcF!ډ [_E&YƝZظ-1j+kұOuړ b 7x~ۈRAvJךyyi .ӎ%mL`36aL[o{]^;L[UQ[֏Q +m/5s.8|%,6j:h[S$b33/-g~z=qL#kLQD`EΊB}<[FX9{ZjZV63Ld=ÀlOzpEDΥ4zVLAԎ~+~m{i37\u@@A-UCXttO$G,'ms4ӱv|# ;fUZO6dy^~[T{IR>^%Zmjc$D=c\AԖEltrVSY(ՔGYrc}>o@@AӚO1g`re'͡e1i]=jzN'y_6B8 vXiLt^ZF 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(4\3}֜Kom\CS f=*VSK~|S][R{ubmTa+yl50.h-f'm-eZ /)]m{Ws6jbjKu*Kjwrc;ai8.pc\ hy #sⴖ׹@p=8LY-Mo:fF1Vz!dZך pSOMPZq.[Z:KTZc=Q˛הVKKFk#N}icIzOX\bKodذ]i t5|Qdxs4>ܗɣӴzzֹ:+F]",UsPݨl0ǃOҺ\間LJ-fk;n2*6i͵f 8/#.o[M]\LM<F7ζ긆mTrۤz\svX ׄ'רk1E5e 5Օ-kikKvH :4:,'oNE6l_|7k{[QsPcAѣȺ:<)H>L^>v̜W_lƭf\c#F:Mkfa\0=JOj7\6%|ZvBz|7&ge%~Ȯ`R՚NCL Z uZ~MiF Rd| #RƎK&|";c:byOV~uU[-:flIӂi[ydL]N\38Bګ(QAiVL;fSy:KDӭe;{Sͬpu[bO8w;e[M2$EnG?6[c|r-oU2ixz}z5]p񶎝UK[zH`6":9Gŧ,PJ!n~/؏</YQ;e?jQ~{Sx1~oʝ=XrO2~"^hZn@@@@@@@@@@@@Ad~~|fq@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AWCMp=uؙ^ 5m hB?Py÷9;^UjðC JmfGk:0(15f%<ʙ] ]YVOeE!k[sFh΅tmiѪu#Mie+&P)gF7L)D]ӶZ=lxMBnwشsNm[$ḐYi7k]C. g+(Kӧjw.S4qxRpdLs<0UUʇk]tbqkCxX~Y\U;^R5S~I\٧mmcP[MˊJFХ冰,TMS4]u^6&ȮO8l_W .v)jbHy]$xq[E>$2G3A @ehu%u-icn&rNbwҝս{}KuVd;lahf]A׵an~JױR64-Uaͽ wu [恳:̑vP; U՟;XiE{smV?elu/sOj$FW-%TSSg7lRkObf1 5UT1iS4;@Ϧ8n)Ў71͏:~[[>PRw(̊I?1wo9Ewhe4Ƣfݩ=Aps;'dLU;U q3ZtpOhH1fǚ9&h` ༼ M}녾{Dubse&Wk@7s:yXm54'}+-l”]OWC#%[fMONfi'~Ո?>4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_ '냭! ;A56MLb,F79]G R'oBQfyLpXe$~ .%4v.>錴s6F;BAiXfM؝ei_1c6|K;{FZvVZxM}*+v C7sK]m!1FKqpj9?jfEL0orS9{۠hy>+0(.\&6|[%Ľ`@@Ac;S-ж,/.tchYQHvéZ*f7D@%|lnK5+3 ZжclVv|F9}Ɯ~^WE& je=#hftG#.xQh؞|W-!xZhB czv@FuON!!\zW~D5k\G;VZxDty]/@LJ7Lgǧ f3Zph* Q0@eZuTYzlh\@@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/  7yj>@@@A@VGc>J K[GNJxX%ff:FnkMTv̡`֒4{#^}:_L{+iPRڪܼxߘwXmK$Fpnjv&z#yؽCN28jz& fHwm;\̋y6 PANx[rr^mCkd[&EmZ.-vM=*}Ef/):R!Y/{-4ib]sZ1 䪑еtiSu)9f{:ʫͭ;غyUZ|ڵJ;FMf.7L˺K'$ucWn5,; lkre`bן 򌒏77-+_4uTVrc^s5si|G=1Z6u:~=6i13^^3oNWկ\44B|?rґrf=;fsb86DMNkZN\磝s<[Ԯ-uѦ;ӯo֝l+~259\p;|}]9ctT+45 hhCۤ>@@@@@@AQR[0Pu:n}t.V,>Up:۽S 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(4Lŷ XmӽRB>;sFliإ1˚;Dֳ5TgN*mue SydKG# ml[4&E[W.7iV|޶! InƧWwZǞG&ݥa2oyf kئG f"{[ m-18k"fռz8=]S` övvyIX{56xbnѶF_mJ62nbCEHI'_9q֝{gfغeCk6x:]F+bd᷊dXGvCHoQۥ\ $h#]?9$FH^[=GG歺GA.%r?0NGW¹ d cZFNuܶmʫ1hbնu7 Og[*YWAT$S3s.&;ó뒱j(RMP+L"WStԹǵkW']utF#oS ͤ4S;owE\)c,{Oh+%-^jMkR_81|qg|]lأjtzywuho~ pxW=b}Mʦ>'1KI8hfeJt]rV1fXg~z.[ynqPwܺfAOWQnI,O#B@;kZm1;yHk`ls{*un[[/3Mvl4q@0{4 >L6椳fOx>8E |l3hmλAǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')WkwˠAKIqW^nh~FN]ނ?wB쀸 v߇㙯tF vk#Gumw `lq杻6×{ͺ[pu]=@al w*)c*g=tە^J?%jr+'Yt|nC SdGe6&nKB,<ֺY^uszch4Xлy,{X-ӱ}q6YWi2Ph5rA(@wI0LFJ簃wJh1昹s\:g^gYG菖gz9jz;!ڻ\%fmFSL*:mPt#|أ$vuW5 QPsHUEfgd|+k.7Öl p9HFF渞}u:vpWx읝xo^ygYۭqj.:xu"hclǠm.J֍Gŧ,PJ!n~/؏</YQ;e?jQ~{Sx1~oʝ=XrO2~"^hZn@@@@@@AV916ڢYdжmGSNHA ?˿kXt>yrҋ T\inXyoo&2TAƹun|-6G/mZ)i+n2R2ytxghg/1m ߬g.O[tu!l;e..gT;ˬ3fZ1]ldͨm-k6$h ;N|!^ x"×L5j`::Fl;I%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ATkb((D\ٜ@W#[S7n}\3Z FFv!c;>\ĝM;KcRѸ̬)33k^t;8ƐZ4Ld: ",dt-|Te?h7]S=8&{ˣ!dFTgEQ֘Qt\7UXOIǧհ[h竪S@$<5$Yk/A6d GpߍWGB5֟2?^/&79X,﷥pdԗ{Tc'p}+ګG[GgxK騙ٯyl"lYݹvۉr~sE&D=zW1 f}0EI-;R4kF^c_^oKvozb#ϖ%2WC({-PZ׻MĹ>݋F6lӾo3qE"b4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_'L[.KaGMJ!d֛N::wBsjbrb#Ztn󳇏LGDx;QqM@KCDtnvлNEg_jQwd ;Һ9М::n!)1h|'MÝlfòcs[9fck1gaݧlE^331\-WiDILsZ&pn^ ZWzD^9 ;ȧlls=C {v_vVR @#-soWCb-sAéF"򑲽'0[wW)׉m1yf ޘ2,9wo3N,[k?bzl-lQ&;Pq,̝q 57 JjYn1QSİu$nQΨthf: /|MnrTco$ rSiY1 KipgcĚ~۴W ѤHoh>;~þp䌓gz& ){.mO Q;KuK(8mV3ٲsr (ߨ5jo5a[0\gJ]`4cA? Mp᜼vmvm*37đ6fjF8vp3qڨ| ikjz;%ʮ`6":9Gŧ,PJ!n~/؏</YQ;e?jQ~{Sx1~oʝ=XrO2~"^hZn@@@@@@@@@@@@Ad~~|fq@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AA8a*|nW72=d] L5y]-fm)spϲTe=N<0h]c76.}>no`OlJKšqEcx]DbHo~Fu\3ίO <]bWPCRj#1Ját1xC ҟ<.eog"hc+:DtZ,\'xb}_6]2Z3/^m[LUOBCXi&ih@19Q+玚gy$nHz79[4y}j89x9Փ^ AXGG~ {eocڃi[m)5h5[X#"v3v@ݦWwa\1|^[]% O994iȶh5e'0p'Gt{5uEg(W݉o'Sj-rrVѹKxs{z§:$u4f6N*->]ĚBZ:|\GxXn5f'vpFpl'|,.y1=˛A A6>[KU'4Yʼn0-i輋.9Œ;=H͎#KSs q}[mکГѦyh߇Lj4Xf{m 6 ߮ jhqǣx'OE;' ώݾ.-/y蒁N@@A4̂2J:Zvɝy+"uo++\0b'B݇c5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(?6FR-VzF=Ka``C$s{VVi#gibۄvX޲5 %֞aOSY-U-cv*"{n!z=3|LZwJ<r2h,x|2Gu:t 0N?P<{:zơrmE#%9)+eL jSv:CwK{% toxJ%b'm!٦aղA ՒڹX31պj7RNִ;gXS$l61n8 Zʹ]aas9 yEfghXDZ՛V5Cj+9&']){I{-mK.H7F㫅%WJ ?mPsw7^eq-wιiD8EfR\ͻmb+4TcѣQvz7Dsڜ&(ϓ yVxr5 Qo;Ʒy=ij_/ѽűGkA:k+tt ˜IsRoJ۬C?3awվMUP_dLkGOoן~oh嘎_hm&XUnr?F[W_nd-VO$Mf:³>7K+Mc*x;OlXZpZ"m +8bIY$<cvFg&m\#>YֻGe}H/sR\0OF/5e;DYze0MpFXf)DRGk{D Z]5gz uUHc{Wbt#O 4h$!|/]5t|q;'[Fѷ~Z:_+*-n(u+u.#Ejⱞ^­{['nTEYĵjGY+fՀ9*4Mv 4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_l4 :lI޳OkKje-44t2# c|n 2Dm؇b]n~{9{jqJ!tl^φVR.Ӟf1%䉓0F BE2|.87paسASlaC9hlA/2yR59i]"lsiR(+JHpqMia-U:J0'/W؝G\oxR06GT!F!i<[HV^eWpZvݏz4=o%np+%]Α ZH?Sf:4Dkm* |-s(Wr5ceَ 4·62Unk4yOsW`I Fw7<XcG#Lf-6:̯ Ecϝ 6gp$.f֝g/i3n[26[5Q[ 7Ne6e"[N6ǒcl.lk]xakQsKqB#N"^ڕXiٯ6mM0G05 k[QL^@A OIyr84T/D òCW5Ŵwn޳;z/&̵F5,6ݔa[R=Si*F>PA5ӛC36PϮՐ?>4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_ ;E~`M`4bhpU&tcY.[ʌ Mb,E2C]0^ Bݧ֗5S4ǓQJi3p{yΆ\VZyeelϙh5lϪ8 DDLv!iyz2ś|ܬԷX4/eLuqf2<-z4gIm^ɈCc>9UmNjlPԘ) $p:u>!8"~k[6Ѿ~1,.Yuf {@C$JAo7usג)*^ӦR ba%=U ׫/VjjUycskUYZLc1Ѷod 06 n[}XhcvIp]鸯5uՏfݽV㟡XCYm5%,qL4-;8΄FEWcy퍧dj-%V3=s?R+\5k8vzF\]ώ՞YD9oޮ]tjսb/KOQ_;]u=E}d0gcбrs}#kfvq+y?m=pqFK_i@(p+gӽBck̺WivW:d:vܹa +;N X oWN"2deWSW3cf@V[a&љh&7Ryv84huXį,?+W˕5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N( G,%f s.*Wwղ7d'MzU[iO1ҏ+N @@@@@@@@@@@@@@@@A6F4#PB u YڭTV;싌=nSYNѺmL=a+['i+[jG3gVfa=|zףmʥ冥r 6M0mrWma1.5/k`s5StJHh`l00GyPi-Â)m3p꺚\YE-Y(6eל5U\G eN}gkme.$SͼO]u]`lԶX"[Tv '7MU/6'R>}O`3Mpb{c\hkecMߨ`wmox^M楻b}Y1>uVH?kT̻Pti^6/0Q2ٞC 4'h7k|6:Z o2٭[MMP&ALG8 6mW:ju۵+Jb[o+5I|6,GKi֢X`M~/^eΊ:wv>u[WKG_[9jU'cgыU/oeGp;~ y:YNjK,OYM8v:KmͷU$d4t]b-s3_b 6l-tcz&NJ;$5/v̥ yM.jZiǍphndFO3."emKeSPQBzJxqDfsZoiexS #8#;K~kύg2ed w:]Dv4O)]^O=Vyq{I'RO9]i3:k|6>Y0Ղ`*m)Hcs\\G\Y>8scu_eal۰E{·X1k+{*9k7{-5}Eo.jh, L"b&eQҲ38HS6s耀?>4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_¾CQ[Y3)it!ѬhOP Dvɝ<71{/t/ ٴO:GOXJLYIjh,WWB'cgKɈ^{'e+-v2^v,cd0h5+iϵ&6ߣT˼LXh.z28bp Z\7u:Rzbݿm1ۺ?Z[W$&g7ešźۨ]ۡCWƷ jd#Nc]5"L0无0[,ԿI \X>}7#A:ө\'ׯU6*6/yj//*n"ȣup=&H9[%Cwj4*dYGJɃGJEu `'8-3,9mM9Q3"hfzk5kYt@lN6}պ4Z_ظap`pUVڧjKIAF|#&1=#ni'OZ@x^pKjW#vLwTђqۚ'-jpfmQyq{D}|z+ZbĹ/rqq-G9=3>P[n3i(+|$6hȇ#N^1ieg>\-tګGI(lSߨh=ت0p?7]|E|e>bòbn |Gkwڸ oṚ׿,rt{,fB?{Uׅ)^NM(n83wκ ~#b/}h|Zr~5~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp'\iia׾:[,>35uch扉W EcڬO[Efj8th7+}gV8niTbe3H*qbvze(!Z1\q;Xj7GqƋ8ƶkNNZO&6>ǖqeGIDA_HOOb⹵9k}-9tեf/8g> m8f#uc;癱8FiMY7nW\Km[pt\Aei6nff! m+ȶh/f CA_0X|S蜃FӖ?~%d ?Gxu y[o{a?2ѵU(?]?꘿L7N9'㋙?wSDzO_4~W-bV w??>3eA8 Xfs f SKm~Ύ0zֻM)Ӛx>6)dkĭЍ׬WoI3vꤰ}G>]M8wolI,U5I qE*fe1Dw7©IsOR5u4B 9jo,D݊@$i] S}cPWȋo:zK9%]*uѵ5uey*'GW~\1z]rvdca M5֜y>zmmy]D$[ eT 첚✎T]#PΦ;h-s3^#W:MIƖoT}K⑳k <Nmɯ۪|vQGzWKql]^(gwNYl͋9ԴEv29j |Jٟ߱cjxYu4{v0㷥Do^bcnȟ 4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_tdUpۆpΘ?LZ*0KLthNZ|3G].ٲNK~Jc,&-8t cia=K6.F_?cF]~7jخu`R;CN~I􍷙XqR3gK7|ySIc_%c*"a;tלkͮ垇 6=2|x1` Rtk{"HeiyNtԷhzUj8X6WГ0|gYs'&4\6(×,-Y9|2q;~߼kt}~\~]i[om 'FX1[sˈ(2Oiϔ=ןNeYxfa{iaWQ}}>&h 5Ѻ2ku9qF+fX:V[%lQ[i*jݷQ4n jOEK"Gc(k31q&챭cuF f_#cZ5%.IUfFm$ts>8!i:ݦ=~0ሎQftAe-T΍vX[ݍ1mNG&׈-ӹVY֌t{\:woD[]F)fw[x^+QXՎp;+PK mR<j-;%s.È\'MQ#v߹̔jyy;>@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/ ^)dd,/,p]ѼsNϓ¢k\.w{cnã*~хqLKSQBēNik[t'L)c2M""DwZzʘ㔾Xv۲㿟NQ^CXԭ1Eb2n&(b۽R+JwbܢpٮGZi(_c%qD~Z>h{#d- Ә?R3[{2NM>˅q?RSݤxeCY-[Ő ԗ47˪ V5w1^S&o/3sg5/9nV7;VB͍4 iS c!U{ͧP=Jڇ:%p;+mpfyba-wJAUFzvTL\yKerM?3,H3/wfԕcd:̤ 3Oz6}w[Co&_Q;={_nο:#U|Ty"ґkp)X51N _E=:q.;wgClzgDyk]D\|Gm-ltn>Y9uXuHi+kKǷf-p-Я[el|VkiihfdQ\nFlk60jK@UWc{!"#yX-ǰ[m!:rzҾ6@c/:5ґ{ږ[ho#I״h4O>埥=X茭]*4^*ëhMF1IT\C]8cO1u'RY,cH#,{c5'izR㟵v:gkJ@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/ 6pYvbj A`֞!z' mSS"c$[mm0=\ a!`;~ tܮcx: Endo$.xLg0&ULĞX-\oZcwϋd۲-nX> 3Ua^C4jxxy w]54w8%Y? a^;ځ:j|~2rG}#g{k)mPRG- hyqШ)KM+;o&U3'-PCHf;Á#xRqd|f)-Yl]_<睂}Vyۗ̏odF+ | k"19= VlIy!k7fT{xfA68 s]5\#xYWGj߃jal0Ǯ4uy\f\ϒr^zʵʭL14ogn]g<ߖا6S.2bkeP:RY] r5ŋ 9&|z3$+[UCKc3f PFyq?6uFm_kł1ՒTB 60v|ko-v,'F;Is-卭db<=M.͒hS;Sk^bm28a'E6Qi䨥HVCP7+&-4[i葧lꊊK5$SDݘᅁ`n fӼl1w%f^Noy,w\wk>nkNⱋL>; vARw9e01ݺ3Ip歉gz@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/ 5d>fmdž[yT:j:;p:O>ˡg&8 H^WkmjxhRhf7FX. $'Y.k_$/2tY%hFmcoC~,/̻93e Yn uks4zhMukx&cz^mSiTw \%U׎M 8Bvx͠5 mk}>M)Ke5\O9hӮdͽp?Nkvo޴ίd+һj,5uJZX ǝĂuuX' -<݋ce;15 jYn8&u5wڙSuˍ]:wk SpUAAL$5GG9sUykGYCy1{w*PIQ$!툟#OY[j^iDg3骞*#4249Fb bbv"<ӊ1u6e$}MLDMIlgOmWqqFZWiFm2qk1M3eֈ̅1 '6b;,GYG;H{hj)sIL=m} =K=ѶA#_2vttڊͰޱ葀<ٺ./̫|:kwo_k:挀Z"6}sF69R5##a^@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')WkI}yvl+øPb^69j.R ]5ۼ r\xnjV,Z?) s1m͗ ήX#;uz6sC`<^cf5zGZ2f>2l} TDC{1E37b*ݱ%9ypn!Rt?`Ԧ(J}4ͷ9*5ZʉpC,s#ugvμ-/]Ϫ<7vuqgU0 ectMoX߱Rmy Ȉ<:W͢F@3`ᭊYc 9Xd0&0Iɭ5. 4y w'$uۡ=,Xl'|Tk F@\ Y7944Jk# ĆNby(bv5:vʎ4;i-5VѴEo߬8kKNξz=W>juȉ V> GI <㦝kȼ#8sӶm3i,VCn 5'UJɪZ$kAF^n+OgjfqԒtW5-kZd0i&xˊFwL[%MDt˫IԒWbcbcJwOd3n`=yLn634ӧ8Z.1e9#Vvt2OWVK3c#vq:u]u6@׭~f:<+咢 5[YI$]v5ŷ^$O`]_+8-={a[;€jt]84n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub* 4EV0ysu;R|U9sv|Z&MN\:he[ᦎp#T{CM0*rKL.\#fps> ,J;'|c6_ MEQ+s@E:k|73&Ydy$8s|1ָd(-3i;S.qTYJmLĽ௉/(joF&/ԝOJ }".:n38~ LWr'QP[VT xp.,mYkWK^v<:ė+5GQV F/a^YVtilG7F."♸:Dvd:h\I[}i.URAW,%MN猭b^&6p]0%#%Ҫ`!h5 5*̹馥dUbB(uv{RW @5;]A*.<כR՝ ;P\4x:}J޳Y,=CaTTշQQJ\-<hL8ծ}iωq~R<2꺚8&1>04IQpLڸ/^w"!ulSZUͼߴe>=yDORZ,ъ=?nlNiP “⺊ڜGSfn ޭ[9+Ѵ'IrDϦzݽH#pw`GJa$t7 M<7.KD`dp]R*,c^4CO2gkVZkn#1V-ң؂Kt+Ҷg PͧG[RYrS)ǃA LkƜ˕Nrmrᚨ\h scsLc`Κi`R4hMWEC MS S󫎽H,;'|c6_ C6x󗗪<_T@j5ߡБMک{cXX^-5k'Xɗ6Z,mp鉢htD7@NME,ڥ"Xnj;1Py7.0FcڊE:,~4vH ;5X{Idm7Azt|xi!NK}&xY`ZyicihxBA?k͋)h7 9x݆qKN}] kCZ[ s(Zͧ-&?&X5xǖwJToc݉j}⎢G6rDFAU$WQY O31>i27/-|8CXp$b>5U5/F: W_h|3#~+feo6ݐ+r93&UKQ]@ Pk? muzy΋:|voZ`:ZH#4 s&fӼmAit3FBѴVÕ|Tfz εاkV}/U5<(9ZGW"ϲ`UC"0X|S蜃FӖ?~%d ?Gxu y[o{a?2ѵU(?]?꘿L7N9'㋙?wSDzO_4~W-bV sq==41yGIPs]+F馞A͆n1q3_$6*@ʇKold$%@@@@@Ad~~|fq@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A]pfN8ʌ*h[Kk\ a44oӢ.wż}.xWnWT_$q+0*EkB֛N։%nbCW.;>U}txߠYTe)۲uS< $n[cwцQh8j Ih+^z$fs-+}EuTώ3:!Ы\S ^vZiĭbCfmxR\ UiPuPxw˛5Xg I%FpcJ7jcwb|d2hyXdޓXԜX9i0 gPa*hh C ZH$\ %3w+L|d MOKm&FjǷvPҭmy^-hU]tяmFt'r{g~Q7KL摅gG8]ұhc wZHю8ݦz4Zbiw߳],X+w )abO+j# f<"5Tks卩G5+)^AxŢewys0M\]}Uxf-'&8oaɻ+k[Eh!ѹGQlS'~W^Xϝc~{➿IZTp{i!TRdT[ެ3:wgFX8}d)3 N77Q$G'<3FY$2hˇrN.aո>JOc;mF{9bc5kuq{7@@AՅ-o7k] ^ޢQz{tO-#y|[8Ue= lom5vxO8u~83ct Db{e9a8bJۤ>Ҹc5iٔsUWM㥡1!ÝpzL_ȵt[I}vJg>H4vfn0X|S蜃FӖ?~%d ?Gxu y[o{a?2ѵU(?]?꘿L7N9'㋙?wSDzO_4~W-bV sA$OB 8AIO>X2[f}mάS&TT94x\nGM` Р23 b_qݮRGn%%$P tv]<f@Af>srG{=FKLpo?Jf{[YU8Rã4a|r19KH] e<ת=;k oy)LJ֗]C] ނIAd~~|fq@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A p Ke$-uΦ:8$f)%tAZzjsr_57[fTq,SUGTڸ` |e: NzBt,83EW|vT$vet؄sB2~sN;j\;+>IJߣDd-jc|qec;-@ .i?G:⵶ls\68f+m?qTUhQ-LbXCYi O"l/5˾^i7سmgzHjKɚqqoniw+36Ҧ[NބU&<['"HC9]tf\;% -p]{Y-jzs^*(mi8T כ]:,랖 cc6[O-Ζcdm/if]7xԬ{ݩSkejY7LնX1ӣ=5uvjXi0uphq@uqO'[ױ3; %זPƆ7BӸj7Nl8y'ݨ֋$j8ontzX/MTӮӚy@$yObS%* :JEr.%5sh-8n-ARtЃ_k9\{oٷ}lZO3#\XՅň*#%͹ntds[-tۖ=i諃1浫\+ > UQhXTWzLvR]4- =W3ExB.l3i!ߐxW_]Qʪ]wݪu-#3ÏdĪpkI'@7Py'¿>kS1.>,9k4GkZvKs_ ^ôu∈Sڦ͒oo[q- ~&'I4+kR(1+|؆|IWA4]a{71t=k:Jαd%} uh|{jtX=gGI֙\<ʖ',OѴHuG:3m~8 CX4 *a-#hkh clEuOr NX&B0_}5x-l__Qw #x?FWTbq}8*ns.šh`{נނCmמbۭ ڻ9Hiax4^8ނȠ Whfsaj̒3CLh9Z|]>l׿ḽuImxy7 J#Cㆌ97o'i׵\p0iuq1m"+>]8ۇByZfb6mYEZ^Z{їލ$xj:z:uNfaRbvxv#P7(=L34F7ei4A\8 f=f&]k$lЗ4]U81nX4\Af~feKx۫/IM+D[ st<]J}&O ;Z~u:@ c\I*tUt|aۄ1ԴyrGdƒ-f&aQn|>\ IVSzFn~. :%t#Lm`'8\Ji\vSKϗ 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ* <yNZWH$rhacXK!.vs 7X1q>9pÕ;" i}<͑dKݡa Pi&.eAS.)4lnFkЃ7A,v8?Gg6#Ngi3N 1T0aTAi Nm5Ԃ͗ >S*1$nEE$n [kHiv5R4=vw痄AjeGM0GGzƋ48W=~ލT;qI`d!t곘v6}#> :uS+1=1b;])`yki~ 53=]j Mu52Rbs[a:/X;.D[%+9V8 {eU `lh^ǀyԂ9]6(Ŗ'_M75U;x!|s5T@>!.Ӝu^p;b}Y0Z*kV;, }EDnsK$o:ӢM2Dz#S}炾'vA8Z۽QeU#%ku \IyݸnP4|GQlx??JM"mky}1էZ꣎q-!&KV)C%t{e1Z+ ,0[q/7kWAatF5ѭ$ytToW^|<=o;^zK9rb#+óFM(sGu'E34o煖 U`U`V]Qn/uBH#,v*\e5EQyFܦ5q1L|oژ{{J͌Ե&7k;OK5Sy5|]:ze W=dxZEِ{1PUSf,stO"9|DQc5vJa geǶ8nW|#wW+]5Z.lyώwmGnDg,n%AtL]緰+&.~X4\l4-që\ v\d[E.\[oNhFek,TalXn4Qv/{Jɨ>IS)HmVң`6":9Gŧ,PJ!n~/؏</YQß%q5*pu-I{mT hX?«Q~{Sx1~I\ig2'1 =ZrO2~"^hZn@@@AR#&%l;M^OJ i;Kt]-lnv0iK}x+e/i#nL%SXk+u5-sC_+[ nxG͗ Cۄ߰++*Ztqi,'Fv}m>:-\GpV|IX1=6Xly6^׾ =ۚyκiJLz.9IOWVYPZtpx!˪EIt[+>gkٮG-3u-?9A{Nmo#)ѽmb3jK6nmg-I͐8llwmi\\4p@Nz.,1"#/֝.<)qpaKcaYMNJN,a/jتE#?c@hmzΚK&쑓SlReE<>);/ao͆xr-Ym_Olˋ*.ky~'R{!\\X 9 Kexk'ba1h[#WIlR$̖Z{8@h <' W|rڛLqjV7[F41y#̨4y#wk׵`g:hdg(ish䞥S3;BWbʫZJldLB:ޯ+:Q n7T,SwLXg^1ߝ7Mwo~f_UY69UộXSޱL+ik \0ƝJQ0^,q/Vh+bPSUI6FeLm;J:qwJ_gk\t7 :H n%tUO;sycM|lq/Cnc1(l񍇲 w,۷|/+s2&gܘuNd^bܧ5"81 NZ+m_]#m [=g_etƒfNREP׊"hFAڷJVn俕H#bj\g7-Iq0pt=ry)84YVy&wc]el2b;9`6ڂ nߠ[,S%c%#vc.q[%jrljt;ZҸZ<ﴙbe- ,yNNApQi?D[#oFT|k7ְ,2 r)zf鸐U(?]?꘿L2ڪt~8u:N'7NSG|r/;ub;'|c6_ zɗw .;=r;n0+Ouϗ%kKx1Xy+/$qS/lSiiwQN{[ĩkk~bPCFx롤FwnZr.%\S_3kh!m&DPSF5ZW} ֏&|ߒ OzA-H Zu;yGKv5] M6F Pb@l B6ܝ˱ǿ$o̭,߁s<KAR؁4K\5]]Kݿy+H23{)<Ǡ&j㉎3:uo a un&|tixM^c:>l+VOآ'OFLWR4ެfy{QmYs~Q16$ƒ:ֹlOݦzxGmG} ɳ3eߌ/1lO-pˆuCشx3l3x9Ee/ jZG:vrZiTe-,n ]r֩0Oxute-,L3;7-5:8y3=г\3~[`-F ikD K$Di:t_xwt#_: 9e&jSm\ќ)^oipԅ&cg5 1|Χ,ha*JjxC1Z`ic]D|zKfNj16>yYnaߔQYh0Q5gy=0_Usx?>4n >-9c?G蚂V@A p~MY෕wʏ}F \6fS݁}_ڛ^IO]8ޓ9MXՈ ό~N("N_Y9u롒:JgM6jtXht[5&aკ[qM}4[d!=|&s:./ޜVW=wzET+hh-lѶ@8jeӝ|j4(+w XzUfj ht=u2綦_.zӒf^!sN/LPζouxZ>$,Gin5:uzCʪX΃BJ"~̻^0ME^ۡx,`-d@iI$,k]TjmoG)lZ9(G#8hw~:mxyoKe+~pThZnŃ.,] ]&{̭ 4|-4߿] uB19m|D>7gul:=7yK6v 9H!y |߫u1˿IF(}vy}(TsZWKK,.c.zV]m] cd[aq=BmḳŦ30hJ\k)dL!HZ`rZm꼵5`[sˌɩ+{J6AU|c:ojo/ ,tm2UFǞvN:OC'㋙?wSDzO_4~W-bV w??>3eA8 ֳ/8j ֳeŇG{^A9񒝰,&9p4[$۩fCo4l85WyxKӖgΉ]-b̭Zր'?PcӇ].vּW;"=Chi{,n3_ auҙLM7CVtÓ%cM#yy90}2jmETRH.` wnԮ׶ϨX Iz}dWoxcvA]TR9w| 4ױam<-4G-vҲY C33Q*m1Zsу ݪn8ic1t줚Y;Rh5)ZmWWf7{cnu,fwNzp5+Ŏmxyޕav:9mG::VX#w>yv^93SQZ"bbgߗ濍.՚ZzJ>ZPe& Ö檆9}6{: ȹfӚ鸱*ŸI]0aF[ 0=PX5 =\W|7SMX~J&|y懛]cJ{%\m ړL}j+dx ݰT1DNv:orQ0`cכȵ_[o4עpAW,Cp)eTksCG⚬8tߦ:]6[fƋZ5Rܩ* 3GQ$,Z՘vKLֶSzqU(W3䙬4k@ߴFN>#-/hrs<,)9PO,m; 2S2-V'" Dcu˕jo1DLǟu'C'5o^ -4tjxQhZf3.:; \>3`0Z 2Yki߲@tst]?͓$oS!XxfpPS *j[wu=!mZL8\iM֙,yNNApQi?D[#oFT|k7oOڪǔuL_OV̟עq?'r?+{y۫A; PhYC&-bkmx8[O6僣S4Lڹ5%q2cdsWjs6=(毆7Ck5:p,22ڨyc'/.q `tG':n=K^u'ii|\*3;4kbj$T\^]+`cy^OMH8wI~I =}LP4tqoxn1{Pe{s:3IkLGH|}5RB&7>-<.؇s-sY,TJZθѼ]SqO8g>3u.tпIa;[E>7S!uDsAiϡS8Qx]} FKGz@_Wt8Ti̓Qtt-:KzJຬZ\l7;ͫqu{*b\QS%v"vØkR 5~9^57ힷ1biҹaӴl i:bWzZ.Z0XkbWޥ6kDwr䉦 ~ufo̲p757 ޴r>k5l8!H: ~JXo]3S$.E84%꾱:T4LLqkލ4rJ iX2.NkLʷ_u}wJCeZD#L̰G淘,X[7PMG>,Zx;Cj| :̱g$_L֍g^51[M9kh|dyԺ_h`8$p|uvUM(=^OŸux87;bb\=PdgV5ԕMaɏh}~Hdlt)XE-UO:('yk t`N9\}:6Y۷1N~XU+ g,iu9zk-)*۝7cI%N5|~^f̘V5{ۄurG) .%jWK~?9#gkΗ*oK[f(􆺚jt_#s?8NpWq>H;%k}.~V'> NɋIE[ss޸Q;+sGo΢lϲw^)+4:ig}΃G/rѷ( UWΖy?FF3"I`6":9Gŧ,PJ!n~/؏</YQ;e?jQ~{Sx1~oʝ=XrO2~"^hZn@@@@@@@@@@@@Ad~~|fq@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AKl;[Qp 9姖< ik9.^{۬jJ7#i4qVPB .k]д0;D|w޳W HzJ<Û#k ^ԅټ-I|%\@IpөgZqbp7,fN?c.j: FQ̛ ;PluۨޥD\|YDL-ƿ@֞V2Ú7ِ9㕄Cbnw %̊Y۝Wյ 'xs JWKsN߻]qdؔ_ 4uO݆[Ik$mu!qS/em5+%6g升SR2W%3@HxMVws6 OuIw~\BF5=iǒţit0^Zl5a2]XC\w9.|||+_̴3~lukփ $3o.b3N_&#Z&*z*jDVڍ7νO\1^X/yޗp7\ɬ)+h;;;d:6\9JvDZmHHJ#j\%8G\l79E[ײA%vJMIJ`I-fVز?ԺYNԒ\J=n"6JSVӊJP\<@CuF@w1익耀?>4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_87 獶ETcԓqnԍӸӷ7Kˤ?;VLq^W gʚL-UV) /ee=hZv\ >r{%S5Kchh+um+MM Ѓ;km 8Ef.CYyV^3ZӠZy*FQX@v=GQ!:-3;OAَ]Ԥm[vFϵ[&vJzw>u)6'KoL ЍuٮoZ4,He%s!ȾL54;OGݑ_mz7kI9i[m SMcXaiy~eFmo+OmQYS 0mx#xQJnd<2 \kM[@"dm<;duԻhn޼SeZs[o8qE;Cp88p ΋.qn <&5a:&2_8f4n8 vMKTUkӢ<|Uڳ 8kzcw}FnEXXU^p=MF3cHk)ڌTtFaa'oSBqFֱh_ ~#b/}h|Zr~5~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp'Y0^n6|MhwixJV7)3fxա"%B쵺ڞWaqPed Iղ;~뻵2Y$ e#xRHԬOXi٢˼ 9ԞXd(@͟jBݏhٌlBhF.(öjѣSh{zþS|OͻRC驸^Hnnn+cn!F p œDbUE\H|ȩ*\&r*{b]͍zt/1LO]cˏ7G ],[$x^ZGbȍpF=S67˜5[ᩑNs?jF8Ś2i<Չm ;1 ~#b/}h|Zr~5~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp'K+5>H;Fe.:|eZLV<;F-qg<Խb鮖6R`A  YA.:sWqiN[WygQy^Ns/lƗwAjwW= 0`wEӇ<_BNzsJ?<= IKnun%5T<S7SJuh&X~y]ɭ7=Lyqp#AץM$d+h{!X -T3WHh[+KuV84iW%g}k*]1<ח׃mZM\qXAݩ]砯f.$Uwf֤z}tj"7f7VU;GCďѤtog&{dзҼexl73h~!]8ѯꝎ2-KGP*<Nyiv/U܍/ݝA}hCF*ڛ,*MUI:F4k@ԒU~XFKzf k$@spZXvWYQW%:8ↆgG 5pT|X~2r?;3]W_j>:ZiY4ΌMcZXa"ҘwJoi"l4Ѷ(o3ZѠ`Zm3i한F耀?>4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_9Ꭶ!Hicp#B X[2} =sі<'UDUTdvNaכt8{+YCrT[=zfOlOP@M>1L[l"Z,v#6+1&:nUamS_K]{eӱ#8ӥgZLm-_Y*lRoیA)Tf$]$LlshȣvYV7 Ь6h?bX;P5nu"7}v&u Mb#!'&kzVVcv~cHןM)1hzBtP 5Q-ݞfؖm깩٤o`p=moMg(n&SZKsj~ݡbj" 5[/F_;!]`[ed>&mm wŗ&JVbQ>,flU}N>}|)*NiglԯgWDZe,.r[k!OI}S=/x.VkdhV8!k%di\k clEuOr NX&B0_}5x-l__Qw #x?FWu*jݏ[IovJaJaz>iu-[k#0R ?B~YrG4 X[#-wfIkƠ76̾׵3/+4#(-KY'3Zt'NWڙ[k~~fv^ ص|1u5RqtVڳ`Hܙ5YWnݽ\t;Vvz$ιZhj:_(l>tG~ϷeShu_nծhG=V Epz[if2`3@H6]I ҍ9|"O_GjǛyўK O 9ivG% SW21Gb2R:HK1S9'G~[vp%Ev?-&U?b:)Yj"ls ps^)X/L=! }K-֮NSXohyk^O.!h&=ړJ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/ ~4S 2NmokOFДN懟y%|4JX v: +_:Fg]u޷GF3u'U';KFbmG~a0>{ahf6qŋ|7 َ:V/WNpRL{e*3gclYUp/t t1Iho1#Vz7sN9>'ò91F8*?x\h]KnB4nkͽ}ow<7dÒ3f鷙s,yNNApQi?D[#oFT|k7oOڪǔuL_OV̟עq?'r?+{y۫A; PSdIRYC岿6VFQ-3ؼ\}guLĻSֽM=Xb͗qZ۵؂68;zX+5MwB B'7^բwn׵[%3_'no],jFY$@+*ZN:yg:͢aZ[,yNNApQi?D[#oFT|k7oOڪǔuL_OV̟עq?'r?+{y۫A; PUOd_ \VJ Zy*]@5k_EWwUQ=Oft 2–cfSJ^wt7 J`)Z\w0 Dz20%q0t= W/%Xzɮ4Kpu^ˏ_>?=<zd8P ƮKQ}dWjjr77s?Cū{igbc\IAq#dn)z8n[XEڹAmce}< ^]uMNWygU,c<8/1\AKmCwϚ6d8o]採ͨKJL} d \ڈ->{pܴfdMDZR8v}4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_bd:9X#pxB`1B`si+uWH]==#I׍8 763luf:i)N<~mJ}&xSf8U)|rU@ɡC^ a cxE|23$E,Ը.( 5U]ljCAJ~WLM{7\Nj-;6[˻Va:L;iM%Rֹ{s9+͚92OT)C_r_f9M--H9e;-vY^y7mf-mV,7u-j8j(Q Co ذҸֵ[yj[uA.{X~s3n#`=4H< { 6r.1n.Mx;1'M~2;hjW.H%"8sj~ MzKbO!.19yv ]+HiU=ѽk\ӨsN>=f>l ~Wk,wpӡР濌Uz,,d:7 6KuVP\mMi-q%A=JpXcgxؔyu[ViW)q~g-Xs#p>t5\y4odI( 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ1x0^_oUBo5.k#R ?0(!O!i_A٠̧`< z0Kո)c}ANfQ;PC<_c-}[엃9UNa:؂F\a|9uamk]n\fk*i#dks\ӡjz\£K wZe l J `y V׈cR}x/-9G,>A3@3ͣvroJe7;`e[aAlXZ %1f3ǣI8WbϨf+v֘k6yڊS)YdysˉԓڽKz Vإ7Bvrw5KSi`t2&mD`C_MQDogK`t&NitBCqY;v",5E'ktS;֜;eg /tuu1O Nsz5NtP8jao獛Vo6{Ј0X|S蜃FӖ?~%d ?Gxu y[o{a?2ѵU(?]?꘿L7N9'㋙?wSDzO_4~W-bV 8F/zganLԻIw2y:S6 ^aAS o9Y{㫗[ؤ .pa >ȜAKX6c+%[ch)#4. jA4 2f}xj:Wy_b&{X ^5utj7J*z{Xec^|'UR>oƒ,-8QSˤd{Tutjԉ$S]+*zsVC#yX!S/t۟ )=G8O[vGYh~Jo$*jfW7l ߲CsJ2UO++8g~%cŘZMEO/+!:d\uZSNw7d<-It Wu'%-ݞxVi<=^zTd-T Gj;NįIFأNH$qle[bc y+HptMcs7( , {|Enkӝ|p7t)3KX g];kyL{}XbRS0`Hr'@y\O5w2I,b,yNNApQi?D[#oFT|k7oOڪǔuL_OV̟עq?'r?+{y۫x}S46 `v|oKaWwf'J$]ڇGӮ]Ucl+;nj*qp2 sq%Oj|uFqik/-l1IGQpWR\LCԾ:RrLXU59Mhwx_yסrn> >=+̲Ւ g/AMکkxN(Ǿۥ L58.lU~ ,milƗ{P=*,3:sJfoo1\0.4:19ݡ;t8 ?[oR==p~jeTuT5Ek^i?$[o#0i4;{J9ߪ4ۧGo**OXaaղvJN6dv?T5W"(k3@::y fq'nز;s$-71Ujqڎ*V=jOrNbeu7-7Ua gG\#́PEŦ#Z4Z MAdVa6ҘIkkˮ~>潦cLm< 1k+\*6jA+aykXEe|˽b,Em%Ғx mO =Nq>JVr⍶X`3gq6HKFL;Kˬd%E} yOyǓ+ho hf5x26 .*̪kul5&>V5]w Shk_oAaRITi]ݭ|un͕SJz'2 [nx&8zk1\h$BeVdwC#;O[2&EC#h4t(;zqzY,977h{f44+9Ɩcҳʍ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk'v8]ǃTκjAtսFlzIPϙ}s.8=l8!xXkj4Z@!׬@=<{m7OY} 7;e 5x{v^ J_Ub:%cGtrS][,Djסת_cKp㤮RCZX rb4R^Bʳ94">328?M5 5_#ZѫXnGDyrÕ@ F9qk5'@󿣠mOǥұᜓ4c2u2JI<@MO?B$Doվt׊آ-w>Gn`lxDں*O4n=f $Mz^L<8ZX1q]Ry7a 4`lְIϦ7wWծV;[Q3生>])Ŗ|֙ qK IQCp*VݶəP#fs{EqWSԱ5j h:u_L}mYZ-jd ᫥jiA1LFЯ;N<36ύ*,V^NSWQN w<ۗmi&6'/$rۆSq}M\yԆUk5)9#a.%%@@@@@@@@@@@@@@@@ApKg) z3gMms)xƜN1>ߝXa nY;:u/*bכw3}EmoSUH2&8]ͻrse<ۢ_SZN'͜]8b ?c[$0ds:G7ǟNV=>?](7ZvWwyq&!6S; 9ڍtzMhiMKԞXVђ/f l2J nl:3N@S^+ۛ&tL/pZXhV{MN8NqJ˚͒w)UXRӍuA ]v1?ڃбǒح01Nq7I[7ŖɚP'C%tXzkV%tt+=%.Y[9cP*tsKƏ/ݡnөRge񷞩tZW7}/WyAk졹nʺ ?c~2ؙ[v+q.,}pv l@/z~ounSd\8lfqJ{{0 j2jmϒSJ㍪fJ48:G*OOmO>+'g>^כ؟).7R]7u;eI!eQYL*?Y"Iu XFmCQZz9\tסSxa +wt[Wd%ϰ"J<(/<7tk;Toy'McmؒI5vͯӾ9t TY%n88M\WO+h.2#gK"o掳T~M\=+Lņl u.cm |Q aaFSӣEb䵼|vuَ{^G48m=fhgUKm ` dVcJ:1l1gK ;M HN~p:|:Yb'+LMoTx =E Z)d1\ԍ塺nԕ]&99ϙ]1nHR%{Kk-K$c*Fm !] 2V6ۢLsu{cy[r-xb g$$I];wQ˪>YWԥiU(uUgBhs\:4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_.}XY8N5u)_%4h]k^?'iNsB—7$sA.^NJxV{/@2=] y"޷ٍ-剮ME9ي3$:ky[mz9;CDhs7E;[$o9 '>ꫡ) _l[K9NhY6(XȵS뫣o{Ny22I驩ct3J⍃W9Āa32QKaTdxh^gy/㱐X>5b:[)$ѹ9-qNlyckre検:3aͼN3hZ39>ξכkUӥQ}z).V\K)H1 K18u.+5o|Q?gYlVYuo hyJڦ8uיx:;O:g,E<υĘn' [K%G H߸ Qǒ>6;QE qA~&f& p4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_j8(5<_7Ҳ8ugNݦy4#*F=F\S-0ԭaѶdV^ej54Ϥc0Y[U֋x|teX { 碢eo'Aƾ v|N 5w1WJ;΢57,JGUKK>3ǰ]; 1}%DV!U$^O\G?6p>]=?t.hoxZyf a e֛fYv O ]#qiUf2|o\t}V5]jb†bסo8Us]ׯm/_)K6S_"7N,lb &[Q_Hqi⓼ZF b )\,O4}lpt^1J?ҐYX0iO* νb}>K׷a|7ClӲu5Aj^o7lEchem[-3:{š:R'xtp4պR45"-6U@@A,'#l{sSQ>5h/`,MN'+ ژƤX$Qxlj_S|1ֻ款{ϳC,#vSk ]>E*<>-vnc*/MDq$=op nj hlS{tݩ2r|Wy]0WK̤N SMc0uk87(c.6ڇNNw8TZYI%N]gO2#-[z|XˋKΜkri~UzDv<>nkPk{OxR]hhP|MzmϕdXS: 4tN44tƃU9x>+߶nXPn%@+I1 SWjoyZꛝ#KEpR ºfpُPӮM! &ox:\;RS^Zy]3K;Ա$H '?&;ǝO-V21,0U]{e8@9c;e.WU #U⸡aXi,3JSM Ih4v Smz_{w!FXT1VCQĐ#g4A2&'iXvW@@@@@@@@@@@@@AD>ֻ%د $4kѽt#EkZs^giz[hf[$I aw>Z5ZoiֱF2 nG36Ο><{s7cvx[58{2v^MsݬȜmz] ]cǹqQtistu:X"-{lckB2OfMQ櫸_%wQKIRYNΨWiu޹-O{m,kV a+>YV-vqth=d&Keb2[! ~#b/}h|Zr~5~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp'2Fɣs$h{4-pE9_*2Jk 1$vN ;lU?Qwl4l l9 'ZJ7> nZaG~捻Vycw$:Xe,Fm˼y6٫-6H0Ўҷ I+"k<7 ޴3xQiXNjmıK`0;tpŻZyN{Kdv2 fw ;Cpvs6&׷qsЮ_/6֬o^X>(BǢo0WYPdx}CGHsgOZicG35F[`Gml*ctnMXÒzlyAaLmi1AQUX;Mqhs.g?lg+c)݇Ś\ hڢOMdq[7&aK;ۭBAQ\|D;kepNEK/5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(5YmXIlt?l7H:݇,mhfҬy V!r{ :U֫xSnLz{r-D6 S;\*ff׬HΥk:[=)u&\nĔe g?\vOW0V^-2ם=4GWU5K=I oyz+ L#{m-5,&R]EF0m>RwF-[luE^s1z=]*Y)J0Bf-<~ǢءFhr;| $ZަIטi6 {u{\7swJx5XBDMb^Vckyu<6m6AݵuKTT]P8yجWhc3#%}ㆃڒ*Y Ky76oy٬g4skHك-<倫=- cqa^fNurX^+eRRŹ9h$nz]H>LD .ښl]"xknH hynF?wVYBͧ*̜mzu~;WAʢ,cv]Q+êŚԴJYaݲYYkrY4S'Q'kw؍ 4TsBvͿK1G#I;5i&.ui;XL}t\vf09ܾhhncjE˺Qr.+qMOEr-ٷV{=ק88ݑ6o^I= | T<",6M֋}5W5YhĴeaN36/CuQ/qm 7E0wPN]˨q|LѴyރK1׬LG3ъ:Lx<^A5<۴Zl?K:O{t8e񍫱,xv{og+;X6ki3릶K襾 ]54b*jxlkFy-3itv ?>4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_ 7ro g^uRX_OUٚ{f;zT>&ҹ#iR_mc}{BOʶqSNm-[Y5 V:Ӷuiϓ]# @~e7^eM,)1 =m5\5SK `eElA{yZwiߓ]OQ%Qah@ư4vVcU~_SqoV`emj[pN]vkm7hc:Jncc(6 l>rwIOD;t-xƦiqm{ mcmyVۉ co/N6ed\d=mƟwKƘ{Z45T'W~ITLs&R clEuOr NX&B0_}5x-l__Qw #x?FW4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_ '03oQSH6]+XcAv)Xtٳ]V.8cUT`}@Qcwؾg奻% AQ_+ ^Xjlg/"fڂ8nݧZpߕW^v<\k~|J0"jQ5sH;7VZjxNaxթT) 5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ ό~N(08  1oۊ7%i阆6Z̼͉!.զq=yBl:lxt9kӼ+M%lu-vOJDp2k+6-|[4|n<#qo\_v>3 u0j"F3 bF5,W.vʖESNZGH;䯎ح5m+8o ?LW_{kAjYF$17v1F/yaYi4C-vgWdZՑQ)ikCZw5Ԟm,T_40YqA ~#b/}h|Zr~5~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp'+զgV331-pGl>LoKƎ\ FU^SUow:h&]ncMzFcXxQjOb5gr7LmU|c:ojo/ SIO]8ޓ9MXՈ+<+ \W{]UK/)#`fl8;nYWl8}O,4TxrΥQc<3^(_̔N[;fH.j״89a LM ]آau!5$%klͼ qR f#Nx)ZmEc-;a,:ͫ O M0԰9N5ĝU鵥 ]ym6 c@kZ:!ڰ} clEuOr NX&B0_}5x-l__Qw #x?FW6G\:5'h59-K#bnw:K-:J:v&go1Z,v.Ķ]hen^.vCʲ;%i"bѼ2k[耀?>4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_ >0vu.V Np;ZzA[qdJvV-Kykd\#K0Rns868OIg}NIɓFUg o]P)(068äBƯ/-v*VJpC0Xd2]v%>۶C[#qtL0SnmmqQ ~#b/}h|Zr~5~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp',Ϭ&45Q*hv@#R:A;T&Lӗdٓ~ǥnt7[|ۧmDTtLviջeB@]jxd4]Qqymw0`6":9Gŧ,PJ!n~/؏</YQ;e?jQ~{Sx1~oʝ=XrO2~"^hZn@@@@@@@@@@@@Ad~~|fq@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Aпe\n Nq~B֓;N,^kE}/;Fkv3l O\+m%WjvCOtzORzzE~Y]̬Kl:%r :QNeŧ Vqe _S(/s\Z9Srm,tuu@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/ 2c7a&%l\q G[NNO},y.[@@@@@@@@@@@@@@@@@@@@@@@Aǟ؋D7MA+ ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk1/ F`sYs(tF#Qƻc{v 9wkԥ|,^n;Q[ㅧ Y8>QڨmVW_+Kt o$vofm;D#fGj ptTR&Qۑ]wvOm ˓%\^%\% ?Gxu y[o{a?2ѵU(?]?꘿L7N9'㋙?wSDzO_4~W-bV w??>3eA8 Pp^nK˸hs`t{qXϦPG6iNg(qx[Vɞ >3]_j{Ce: ',X0]|#NX&~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp',yNNApQi?D[#oFT|k7oOڪǔuL_OV̟עq?'r?+{y۫A; PW*clt^.M|Jc8@$ $v+mٳx$:L˛V+͒Ccפj9-N es;ێ2f ~#b/}h|Zr~5~o+bol3&S61G뷵7SՇ$qs'.uOoI^^vvNlp'[֊Ng%D󒲊ͧhɘՙRRiY8:qpFy[-%cyȵg["ʨeɣ{;ZHݤr@͜GG*Qx1mtԥeL9k:fτp+]8`]4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_ s6n++}b14dr2 i1~vCritZzCLcc+ oNZ6gNSF;y'dhvakdxI3Wv&}&;Z7t=-y9cӗ$csv5ΌGS[{T^v!kbhkm-5b+ULqoUK*)fV jVѴTLu˵ quY#qZ᯺xt xvn12KjighKSõN:Lm1[> \8v ],/l0ݧJqkNڹw 90FLO-ޅ1푍sHs\5A Z>`6":9Gŧ,PJ!n~/؏</YQ;e?jQ~{Sx1~oʝ=XrO2~"^hZn@@@@@@@@@@@@Ad~~|fq@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A8'vur\Ã31L6C]'q]NiѦ0?˲N^Y mp[\oM{u^}qݯ<]UY}/w%ZX"fK[1G;f}U:+Vy|2aڇUY)$qŚzt񵥶炞+ytMHwYycްnlq2{p?>4n >-9c?G蚂V@A p~MY෕wʏ}F܃)U_ڛ~TÒ~8u:N'7NSG|r/;ub;'|c6_ mNVTZ4Jv:VyFγ#'Ѿa|`\u”[dh$_+IW]uVq㪳UN;uT$r2au[55I-P͒o tZ%op cZ]#΀[i;vBjitPx-T +lE &Jj8&g.86Wy|$c [*VE,FnjEe\Gg2G26?M)m;'u+_G[oq6jvu]5*) ab?ߦk[ؿeGƾ AFɔyGQMTa*ta?\z'z')Wk?߉,vpxҲ0v^׵= 6YhmTTvvFbm v2:vhr pݧ2s6E8a_€^ [mNEV8HY; v΃zƘ .egiG :q }ƒG] T5秐IXpPvPctQg'GU4xwZuЃjۢ}0#254h-: |KM=-Ҋ԰1Q *q_P90=)jdmKcNݪ at\|u-ہU%o[KփjP_hYmR<ڊY[,nӟG4PwblJot4vCq p ](:8O:x-8jf*ULOl140-ݠ܃#$v};8ƯmC e<2(5Fݨ4(}5-usVvBZ@;;WlYcSAQsі2M%=lTqmqsVj9I \)+Ta'GQCC|dŤ;AW?]F+ [9lڐm7dtxPNJ23&fˮo_Αah: xaeV 58C/-XJ( !S $MY>n%מ@nN7s8.p]*m7S1ll1qhMz5ApY^-ݰrt4l_ jLJ @;A7մ(VyM&!em1oӦvxAa +ckCahH\@w dg~sTYpvVC(XfkZ ;Xa+>F(4}PT#li5t@i{G+trhtiw=H"{ +. ƞ[f-LAxd%@k݃!,S0Ō58xjmTpd5A0/ ga(a[OW[<-d{i<ďn܃$ ?Gxu y[o{a?2ѵU(?]?꘿L7N9'㋙?wSDzO_4~W-bV A SOG[cf%ŔՕ. [klq7;y`xof7|yc;c)+)oWE9cuІ'CsH;R ,r8. aWRک62C$=.|ߍl YsY;-}=U;qn4Ѻ 42ς񭷄V&kqݎqk਴GNH)X<k3g n^ u{U4.DR8+]GLw^: O+rYh)iGʙ@]@$~ tV%uUevKP׹6@8j@҂6ٰ^3"q^bMv֎iqMx:DPu1#ڶ۱-{mH#ln\wH8Qcuᇟc7Kk8-tCsZ]ohA`PB0_}5x-l__Qw #x?FWу4v>6cMq?'r?+{?ۏT՝Usn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~ۏT[{E_@קּ=" wÞqW;a}HHUsn?*R}l9~O£1Eb:Z)*6z :F4kٮ[ؿeGƾ #x?FW \C@kqӣU]95ٯ1ͧ^M6:BGU>j97 zt՚zOoI^_^vui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;Hui_@ wW;HuiFil6++%΁4zC]5]o0L6iݰK:\G9]⸎;sO]vUq:/^m:[oV3%0|rLmcn7dl(T.'>]74﷧t/ɱvm$uX8 5q&R`mlqia{W鸿pz"{zch5i#cgX|_q^qO;O}+=Z|_x5;gOx+Vƾ{y<>i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[i~ۯw'>>Q[+x秳gZ7\k9rhi#~:<#37nߧji5کY&<_t\՘+,;梺L4<w!L=INJhn#{glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.15/0000755000175000017500000000000013752535025021036 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.15/autolink.png0000644000175000017500000007122413522025220023362 0ustar noahfxnoahfxPNG  IHDRz> IiCCPICC ProfileHWXS[RIhH RK E*I ĐDʲ ]D@]Uwudkc塈.lI]{}|9){ :5<4 OR aMIMc>pF<\ʎP_vQrs@(A! /@zR% &TԸD3ԸReρx/d'@Y,ȣ}bW@,@ q _@ 񘼼YJ CIFL, TT"2Im3U pInLF)B WZ$.&j| a ٬a)5 <*"'!r_S9cBqr 39 Qj̦Xĉ)@'Dx,O>\/L$hpU(1RóSoqPNʧD"k:$MX $^Bǩ bSya, R͏H yټq|" 8  0 dq{_S < Y@\4aՌ^@1"!f?hW-Ty䀇( +T^hԈsͅC9Oj50/KgؒF %FÉ  ;gٞIOJ"ܜ)^"`pM_֌AVO<g&# j9̕ƎJA)(==GX=C\3F:>N =kKlv;a&ŽaXvDGV*'ܵ޵z@X|?,8KTb7ŕǎa_S/(Bh%JE zB0&f`>ҰLL-ʰ kZ|w8g,H< |^Seޏ" gKB"&*;  CxM$D{7|SĹ}Nb7qD"IXT@*%m"%#]"ޒdwr89,!/!W/)[%"̡젴P.Rz(T==5Hͦ.VR/|&kiUjuV;>͉ơM)h+ihi7i/t=F/Oj3js /i?ӡufTйӧKѵtVҽ;sӋ[Gc}~@DInưfp|RiFހkmPnAAxd"j#]Li2sטGbZ>aԥQoF ʌ]5zo231^cd|7q2l2diGG?S4tv63s3&f}L`lG{-b, YlV.uoijifn9heodjkuzV~ I6lm؊l7ڞ}cgob]c{#{}}mCCCGcf'ITtur;ovC;F2vu ۥХXK6}6f\ڸ5ΌD%n-n/ܝW< ==w/e O$o=[=?zy{ɼzmӽk9K ]{_~]s?`?A8aDŽ^@V`z]AAڠ؎l^Y7?|P,4",=L?,)*nUxVx}xg܈㑄Ȩ5׹f\>?{hQ QUQe-I't;6F bb:89nrn$0f&Ix*VC"5Y'yZr]Д)]SM?BI893m`j S{yN+vmfȝqdL=xZ@7&o? zµGk3gd*D}bJ<;2{k؜]9C)yy$ɩY泊fuJҮ| (N9".o.06Ž·g(+q|Σss[Y[<|m  ZZ/,Yس(b9_dWKS,*&RRYoݺ _&^־ce]+?8w \پkՖՒٽVomu5g/[j *WlHݨU]ټfMDUWC՘,yYҖ- [Ͷo}"5Vl'n/pG3?PdgΏ$v>U]WtϪz^Q߻wގClnpiض''?|m>~ `Y#8I՜yhuaG :J=ZrtX}'Ntlur+&j?uo῝<>sl:sׅ6϶{~ݫߎ G/]:q9oWW.\y-ڍӮwx|3? 6v;wM_ v?n~zJV+SϚg~+)=eχ^xir׫Z{=|ޝy?|t{(ohHʓT[ 43½CԩsJT고J @"e ]UO Ј<]E'ۡfZ(<4qL&K{G%jo ~sPz pHYs%%IR$iTXtXML:com.adobe.xmp 244 1800 1 <#|cIDATxǕg7l6 8q ljcfm1ffffffYhY$ lHl Կ5sjZoFt7 nݺuOW_}|/2cdž/0 0 0 Øec|p/;yq_ȑ# 0 0 0p=8Ƿ~> 30 0 0 clX;$}֊ar\9߉|v_%\y}Yw Ύ95waaaF+)24#Fh%OEZF>5* 6, 2$ckρD>E Gԑ,-oQaaaȧC"8׆בaсu9k.yR0bq"a1%秶`؆;27"#iC?8 0Ѓ]ws\# i*|wBϞ=/1&# H_g#!޼9lag7s\# ik0 èA`"ɟONGRevT9z-*NHXl 0 橆Ca;p.#f o|'|ryMCv[Fp93 ?Aqxk{Av/s\# iǹO~>yxoagw /~sWs\# iaF|gC^Dvꩧ;F|^O9|Ixm8oOaC69r-ͱ 0`Nov"@|Qh_=w w6q95Ґ{J 4hP$|nt& 91iH=e :< 4 lLFX8N__;cq4jm6 0[s5}Ðx~,64H$)֡ׄ:Pn=9O%/~aw+=iSBG:|Nm(zXG 0z0x01l2j:м1؎ Haý1)l2YA&42 L!HCZA ̞]<%XLI!HCZgat = Dl]zh{"A!GsmQzK/ISO?6hQ]vYi "DޔqW-24=s_.J9ew vmzwp饗<0z^衇b!ni-RG7c&"ש]ckxW+;q<aɶ \J]v -oٺET$pZk!- j#&'&0V:ۑېBҐ{0l۫-"sViH=[n0 cH$-z.\` b4;,ޑR~mZ*B]üoQҐ{aCď: /[-{Tg 2oq2+EY$>1v~`1DHa$=o$B?H:ku裏ƨ!u_g+'؎kvawdvQ'*$oW I[IyPY^}51-m }O p4B!yHn2{z~oaF=hФ5Y+{\R39ȃyƁUV/{ HCZ< v>|oÊoo\Rý6 0Jـ@[F'>@6 Bmy9H1&♖{'bBv.bq x 'r SFux.R1\s51Gݩ2.3u^bC hK.g;(V䌶q3O9I%_3l\M$;*ĕ-=/ >U0 V40y"?&~3l^$~NĨavk!-{ @&P׶ v{H|*vR^$z@i3Rz++3 d6RL1o_~}|zJ e[C=/8HCZf0 '!|l}}1 D CȰQϷ$AZAzɅx %+zًʄ%D ly#QYCe7mYh1SO=5F rMYe)a@^K_"lag6s\# ik0 è-vb}zs~gs57hqybȢ!DȤh:N#?77AbU_$xKeT6L[J)`l W<7ګk嘿WSFnaɹ'&>Ymll9O[>m șHT_[~z daw{7cq4gaTTY#Mj8^ɧwt]CPH~9"[xi t`[&Fj(]O&sUIԯzwz./{L ̛afVF6j[,b>q5פ0 ØA$Q?s9qkb:/aԎ 0 ØSdQna-!,z0 0'na+p0 0 0 cBÈ#aaa1㡡)aaa3aaa10 0 0 Ä0 0 0 0!4 0 0 0L 0 0 0 B0 0 0 Ä0 0 0 0!4 0 0 0L 0 0 0 B0 0 0 Ä0 0 0 0!4 0 0 0L 0 0 0 B0 0 0 Ä0 0 0 0!4 0 0 0L 0 0 0 B0 0 0 cZ%#GD}U?ȶq$}rQ5XewmSZ<iʐuqҕUKNݹ'ɏ֩؈.-n٬_բ W*<93Վ)A>]v`J-t--aw6gÇðabg2$K`B)tv0aȊ:rP7|37~ꩧ A2ǒ}29/rիWMd2(*r8#ڝwSo}32)xOF6V~m ?jHuA}~.#8ʄ|(駟ߍVh~xWydӻwO?4_*w7?}H .qLzY8/rU;VVW7ިwEuLN{:%ʓD/v傲:+! G)VSNG(ޫcG+MǛ`jO[+F k#Hm [*Euf;E}aڏ:ٹ2ܙڎZŎbgzעyuR\wOLoI'|y晉HwcB؁2xg?YXkj744~8;FJ:rHS4E>cMpv v[TsUa>.ᦛn _u,ܯ:oneuȟ/kWz-mWZږ_UMQ>뮻nLnI?0pwh;u^`rW&=ihKlL.h42 { ~m ^{+裏ƅ{k0~o&w}SOmG+g/8rH& #㖶FtW:sDǀ:S2R]J9tC.gΤk|#lMW_}uz eg-EvJGVEv~wz\sUGdyHRK-o5(y#OqOG[A]zB22ǿ?3I}և[6UnG[훨ݍoG>籿6#R&w|Z}bQ;@ڏ}EKȜ>LetyYvdƯ[E~ Wx뭷‹/a?|xףVZ):tROh%_MAPP(rDL;iwHGugA"o-'sC:"A^%) og#*$m1ڜz'z饗sΉk BEH<4`9&=Sж]غOuՠ=W>kY?ΧrFvЊQn >yq믿>]q*ƓO>91p#N:)lv1Cș1zkϼAuO T*/MEю~.UVYbXɋ2(;5y}xBG}T,o /pSN2E>regyQڵ2:s Pc^("A[[Qߥ!~ti\)Z>XMiYʧȦ'j7/;EU#HtA.ڰ, c NۢTVd/PO?n.S{48EsHGsXF%j= X~LF<%Svo[G;-AdDki$??&7M\`c 2)rS5`}[ve+N[=s9i0noˍT*Gv;XMRҀ|2\jc)#Awm]sM7~:t%tQ_D/,'HW8dSl'7oI !=R:)0E/k aB 1pUyd"$GydXaK,' $VRUc ,N?xQ6  )N6S;%ɜ{w4e6;vtP>ѧ{GLQQ:FPQGV_}X7(,"z%13/ gDq/ѧ-Tl+/VtM Z#0.R\w6yXu,mN +t/Wvۨ_<㜹N҉9z v*ɿ ]T;ȏ{(cv^徔K22.~t-M"~>$c-(v5,ؐ &zz^y5>{Hf3D읷߉Ƚk[>%<+~?b1oR&sY|oB(B'AҡZI@L :h"k8( "ڶ֛oEE``!&A& a8 M\C)P^ZuNW)R'}`!cm

    4neoӐr8:d\SXOS8LڜGF7pC$Z"Q{l:z}%"؈ӈMll~({7Å㠐7^Qz~CO aA@ZdCq!Hp kꚟkqCXG7rYFGcl`^SM/88 %x"???ch{ g -EH yi@gRj31Bf69@Pr-zӯ&k!m;ʇ N6uBg?ZӢ:\6JRD%/Z3O_5.~k_iakt!x5Az|]tߌFYgn~Sub%t'"<}=BǤ}J̼&E~Z>09>G($'~E0 )t0V4X-A *fO#3H ӭ#J P&"YiB H' (-1FI fEq0(/zEP5,TQv)naBY.8Ί1Xh +u&]\i#ʄ79q9GQ=q2ڪ:=DSX=d2e5 YhK!jӽଦ1}p m]D'+t ҥ~$-!/t?q~?e}Jhe#kdDøNB~$MYZU4 YCޞBn4,mZ>RDq/c+_jWGUZWi&NBKK(G?kC fNRWtyC[{2$- ='(E|R8묳I'=R'@+#4VS&ŭRÇUQ8r;O!tȭڦg w3oo)lc9j:2&@7uE׈'mtSzG[&B(92)Ge㧚^Gc[oDZL!<IU#wc;ݸ.Uv {ʍ#=3graQ#vWW2.DVlrDҶ7+ C[HS{xs:>uF–!ꇜDkm{j~L}/3u#&_@Y%-̿-zP:a#z~( ;ÆiE]ȹ'*)Hs䁎ԗ'[z?Ko_""$)3)SߟrqwGàl7!ByBȄ#t $*:k(DB)<ˁ±EU 'j%M=TꄳJ~\GiB:K”2 ԥ(8;(;vѢ"PbVWʡFȑFg) uHe& )-'EQ!̿] cBl?@[#B$A+BZy@Tj/~Sȧmc$Xb+ v0q*1@>Sb= Z&p=@?#/%/ m©3zL8UdHZڬt"ph }H,+=-N3IxɁ!Yq tkԓ"St>YJ_[gcPi\634ъjIzt=?fP4qj\ʡ3F8h2i^ DXp +of~RGmCVP"8'!j2N6v#[SĸБCXĀ,^7ckem=O\eJm,7W"CԻ+:49"]e\@R9oBRW!z9:j~1܏BOpXbYgE=XP?qKBnkKQj fjDxD#t>I "L<:-y/v,2)9ʢg՘#=iAx*ʍ0o23 d\V0D E7RN8!F{!~4ƮEXVP0WW?ez!?! xtBXLN@<^Ƕ"/9 G631-QЋ:ie˵ڧwzN-rQhtU'`AWe69aHWC\j{tDptE; !rNZ۞l/"/Cͫ< X#BJy:-C;Y G@ߘ | zBy;JH[41(ODvcn7e\ʢ]!d.aSRO2Ȗ~s~SڡgMBjK !UN? aP:'I_eșbՆA *e1)2 /1 B H-6 X=-m9 *^N@D)QR)cR `5\+L 0iDۚXoQhcykV)3j{G*"DyMCȈBՉ =TlHYm3`!oày#H$0אIp3&fI}h7mJHr9l`̱RLB^Bm'o7F/%;zDž Y4g P4*w8hzudyeb,L3ԃҗԓoh=iuCf&Vmјaʩ` RmJE2} 0֙ 0rL1tt/}G_aЙdcQc:A\|E!m4q?2Ҋ:Bi#M!}V`{mg77mF1$ǃ"oCCx"CpiKg3Uǂʠ ٴt7@ ǡѪ}]EN"h':."S\cl,AXpvtz46`OpX a c'%'ѷyf@8z^ @(g8i;䔥I~`h2cЇSw0}Gc߹ycY[2iEk1 +Y9Tϱa\N?P&t $uQwE* 8">Ae㧚^2]σE[1?2NR?cCDV }Qt':zFUv~ÎCr!t@ 2i!ީM.](4K",my~+DOZ4(әs?/zuQs%u,6ʛc1«YYf(?[F : gXM mIP; !z &+dG)gŃ62QHV"Q&0V1ZH'uԳb($Q9V!!# <*FVqزH8(4[T&rKVI\SAbf{+5D3!!/mc_}8O?1AO8czb7a!w59rAl<'L!c B^C qxd-Z \@ԑ DgI`9KZn 9#dX!dp2BCDԙ| qSw=~Z}ʛ&4+tcd-pSYg5%db-08LW^>.Zh3$Z !_=1E]B9ӧ/YOL0(1b8G~ N |w{u="2&bcG_A^̱ILLK6L^Q#GmO?bWo3"R-YzA'K[>]EhZ8<"P}(9/gu5ժ/C&GU)dAgOrƈ?!dH߱j-rjVRj__G;EH8Enz>鷜+8LEUD"DzR}O۪V- z\MeNXfCZ-tѼZyZ2ATFS|%O>|8Pjr~eUѭվ2%|d/)e58n?ߧ ewKcJSt~Ly)WkGbu-)/MaEHH,BV\:_CЩLj)'׎3mgGG;Szx^=>`p#V:Eo,[)KZn8ȱNd|՗~89ZR֏ۛtS`TEmX+ϴ]*'}̞Qҏ}4Jґ]-ɢS@n}XhOe]2{V}Ta2.bE^˼q&SM/: ec/l|6Z۴Q6Mi],A}mֹ:Ȣ.(jr@a9XrML&d1ʭH;}ڗ_5v&aaa aaaaBhaaaaaa&aaa aaa aaaaBhaaaaaa&aaa aaaaBhaaaaaa&aaa aaaaBhaaaaaa&aaa aaaaBhaaaaaa&aaa aaaaBhaaaaaa&aaa aaaFaBhaaQ@Øэ cL_M6v'14!4 0 0 cǘQa؈гWSx FjBhaa ֨YMᲧFNz`t8 FJGB8jԨ& yۘaaL]QIG!F5*.||tQ%hYCLۧ+BB 2$ 0 |BEaaL%Q/ xh2<>*{ר5cW _Պݮ6Ӗ66H[i3mg|;~Q kdM èfkE )a'?JtBIducO6za E?q@hmD i;2n!8(ߔ}00|0tЪ [vaUt\݅OѳN6lqaM0njFi+Gi;2@+JmP_SJ}Bzy+pQ]GWGE a $={ jU"QǑ#Gv[9b*]ʿ;m8q7ɩ+NN?^gx&['ynDŽ>vøӵŽO;Nc&1kЖm{]ElsQQ4!Qׯ_ O" ڔVZCW èf|8Y;6N@rO5Uv|⋺sIѣG/úOk"7^p<3fdXԢ/emEggvQ?;GwS:/2]Ox%O ]eXVU¶W^5ĸ]m'ܿuVMZ>^[H[i3mG_a߾}w}7IJ4~hĞd\UTmr/qu4V#ދmo۵=}`BhF6lXxwMȞ{Ⱦp>d|>ixʫ^t88,=X%eOKkW3`O aA ;3Yȧ^):Um~Mq}촭'|^|ź";GT%T<+SJe[oխ]+vyDplX =g$Ö-s"w-fƇ3vVWOEI>6m:]άH^~ˇM6$ ᩧys?vy랢(oV]uհ;*pqB*7`T+ .h裏1jO5RY aO?4ZpN&Hl zr2er__=JY,POEH^ڢZz)N>%2\b%~r>"?_NeuxJ*#-'|ҾH|6-% :[q(d#d9>ýxY9emVH.:ѧy,˻h anָ[F'2\6t^y8FQ[?ae k[ȉ|u]7la \sMڐVeCT5lD^^ʯlU izٷ:ӧHO?L\~uQ]zdž%NƗԇֿ58tTK F^,f /nd2+22xos6%lu.l_em|q}='mՄ0Q8Vg}K/mJ=\hhh<馛@the ]CiH:uy~&o&Z93M7.?m)m'F?Ho> :UB0:"8ؠz !poJB\B8/Åþ}8"&oɥE~K iE =P̃sKqtNH=^{혆FK(ir^:ܫ' |HF=|ܨ:ep8VFuMN/lDO.(/m /G^dTiSlH&aᕭrmIeRT~JD{4t&EB)Ɍ, er` w}TCY}']֘I F:_ '/ڃ<O? K6%J)!\s5 7gy&lw}w$z8mC~ ݻ"D.K^eR&"[vU4vzʩJS9 39iQv )ܣL)Ne/'u.Ȉ[Fֽ6@tpriKX*#q[V·a32^~o}y+[,=0g׾ nvIqpB)m±3*!b$p(h3.4yFm T _}a 7FcW"DDY+.lu] 'yWuY'ᮻ:k<slXnJax`lMLxUW] *nfB0 B<pWUtvV`HKlM<\{駟9h=)g}N"T8}{GlV:묰 #<s=7=^xa%/ra%eE񨣎@ʢ{gE]wޘ>\8P6̗̕vZkbp_m"N8Nd7|[l~gǴߏr#CBzQ~7_Ep}.l4i'GqDY<rGַ~{XzꫯwX>͗׷iсHh~0TNb%G?=DDO<ĨۇrHӟ;]mkv$/,/첱ɋrO98v(c \pA$,tvmtN#OMc?Dą[/?DtP,PD!ȁ!G|,α ʂ;ċzl:nb=XgTj#G%:/$}[wec{!dm:`L1ΒI&}8>#Y7V?4.5?2>}% ij G>##xg6>h /i }1;^4ե͡OF|% "wbshq]Y0#kfywPK0GFsCϧ]\vMmǶN3ΈOdCy Q@"p13Q=qI#Qna!~ob\d1G=1+y睕U#f/ < ? 'u C&|& BB0j%+8K>16[#31?Q( o"np܃YyQšHbұœGOUom<8V- a8QI ($ȈCQ< rI;X /s~ 9Awi7 uL@Ғ?\cqL![A*Q/<o`N.l TĉvB4'dbJ:1AÓO>g',2h!CB"YPi!S 2É[bY4m*g?}%RJ<"BW^qeHxNN{KEɆ,Ch 5M_:M.fmO~ğq7A< 'ȢtP| FW!,F[F?SToWc-ȉq@[B& &\gwAU!_\k }-XW_D{G?Qe/fP~qmeP 2J" 3w=v_}#ynӶ6!,ݧf2@1hd/04DC}AΤĊosvء3ܻkFa` 0@}OLT$='FL D&D "P:bx ݱ"c Bmő cN%_g"Cxpi7 zA >7;p s1Gt$!7~G9ژ"olCELȔ2H",ڂ3J$BG0w jŜM9,"/n)Md-n\Ls=u.KU><|m{Ut D>Md mɉrLtH>G(Dxh/s;YK->0'$Y?P:A ޴ @}sO=NL)!qBXhG~/]N-?2F @"mWI!| G>OGtP2GIbO6!iEI}D{)njC@w܋#7PB(;WJP3.'?jlqGdc ?49Q`УX3@QDw0a32uj+>qV7y% "z;\ȢDŽHvR~F6gm ~FenGeEny%lEscl}Msh6??9(ƌ?m=\!xaԨ#<_ $H'bB!dJ'OY3Vi0.G Ø ]2GR]J5!# +BH BVqe+BMN1*!:8ܛY_l6df>98b!d\ȃy.B$?$QLViN+CFsS13{$ @YډmȐK>}cl=D6ԓ:Afɏ>[DT VQ^"B !D[wE "1܃ʜSSiLHI!!QG ̡܅^2""DdүluSS# !dHo%N"|i;.y2eɄ?*.Ov1!4 cj"r*<'g<X$ۈӶ6*C\D?98~;rplaw* CDNQMrG sΉ[A[B$#qyKo$IDBHGg2 $9-DK %_.--#Iڣ]1=})z/m{yBEB>+%ʖ, aeAȅzC!Cп':,% ) @@ɟ(pNo%?HM$PmGUg_wm+!DP)C[9i?!?.oD\rImy&—2J9-A,# )jUg&'njO=Gt.M;۩EoaK:Yچ#ZIg3xYKVB -'[1s\K[V_ [F+N΀gIO>$>KDӤtCB<Y N#ETN&%d %T+R A YzB+=B׸7ߪrnڅ 2uo>"PęgGY`H/It\[([BiL_Ȅ,^آțk<G.[B>tzi ~6Qꅎz+/2C~("Jy11|>)6?~^{MzDre!FoY=D5-Ŋ7"OH/i!L[<%wa#u\E2հGKx.Jǫ}3RgKX8"KenE1ٹ,5"|3HWo Fd,38{ gx%o5(ĸdFf:%̛m&{Vmm@h#muAնI IT|'0ݲ>i*\Z^[=`ʤ bv0azV ]Bc0+v[2I('-:CDq`jY Z^7כ/{l~i8|A RTgQMIҏ[} 0B=ԑHxxe)=H)]R2~E>Q~BY-&D~,hl"Y<"H_~|E|T5:˖Vt|da.S?Ϡ}0½[ŸYCse,m["&;;{l 3e9fH/4;>$m!1טm ҮsZv~| !ևfe2[z U~=yem;DEhe|L,Z˩쮪 a;Qi(X?NNQJ?^ 5YϏbtvߋ*x{8iGyR${49D~_ƨ!Q&2LĐ6D c,e^v>kGSyy;g_*WVHG5U탲P ɣo;jYS6!D DzVja$%eI, ј)+(]QYeRP6j4=Y *w_1Yc;>L_?ZJwo seDnpH٪p~2ְ{uw<ִOkKg$OpsM6Gv,Y~3sFcyv\N p0}BBeO;BhF-Up4k[RI^䬱O$@9Z? ]IɿeiAiU4P}mx*H@j*|3I2EMVԋ"mʠ0NxޏEh)OFW˱+uZ~,Uuf=NMPhHmScg3(V#_\D+2!zj ';|vng&o mi?۷u+)7sk֫ hMhn~|Cޣ,YB=VrMuF;q0:"lX)pGe7}e1m/Hлi=1:qаBgHQyIL~aj 7if/G.LۑxP`;Nؘ@aaLMKh =5:pŽfpBÎ7wbmg=<:45M>]VBȳ!ٌWY#Z|eaa/QE {n['/x,{A֣Fm:immn͢&_t !C]T0 0 ð_<5D}yHYG lF66H[i N1BE1yacka1CUF Lc‰hm675NDŽ0 0 0 Vux os hmjBhaaT#js|NOM6NgM 0 0 Ø aaa B0 0 0?#G@ 0 0 0fvlCv_͏aaatp@ 7|B0 0 0 p's%ZHaaa6Q8_[@vH0aaatmx cIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.15/complex_links.png0000644000175000017500000015450413522025220024406 0ustar noahfxnoahfxPNG  IHDRR IiCCPICC ProfileHWXS[RIhH RK E*I ĐDʲ ]D@]Uwudkc塈.lI]{}|9){ :5<4 OR aMIMc>pF<\ʎP_vQrs@(A! /@zR% &TԸD3ԸReρx/d'@Y,ȣ}bW@,@ q _@ 񘼼YJ CIFL, TT"2Im3U pInLF)B WZ$.&j| a ٬a)5 <*"'!r_S9cBqr 39 Qj̦Xĉ)@'Dx,O>\/L$hpU(1RóSoqPNʧD"k:$MX $^Bǩ bSya, R͏H yټq|" 8  0 dq{_S < Y@\4aՌ^@1"!f?hW-Ty䀇( +T^hԈsͅC9Oj50/KgؒF %FÉ  ;gٞIOJ"ܜ)^"`pM_֌AVO<g&# j9̕ƎJA)(==GX=C\3F:>N =kKlv;a&ŽaXvDGV*'ܵ޵z@X|?,8KTb7ŕǎa_S/(Bh%JE zB0&f`>ҰLL-ʰ kZ|w8g,H< |^Seޏ" gKB"&*;  CxM$D{7|SĹ}Nb7qD"IXT@*%m"%#]"ޒdwr89,!/!W/)[%"̡젴P.Rz(T==5Hͦ.VR/|&kiUjuV;>͉ơM)h+ihi7i/t=F/Oj3js /i?ӡufTйӧKѵtVҽ;sӋ[Gc}~@DInưfp|RiFހkmPnAAxd"j#]Li2sטGbZ>aԥQoF ʌ]5zo231^cd|7q2l2diGG?S4tv63s3&f}L`lG{-b, YlV.uoijifn9heodjkuzV~ I6lm؊l7ڞ}cgob]c{#{}}mCCCGcf'ITtur;ovC;F2vu ۥХXK6}6f\ڸ5ΌD%n-n/ܝW< ==w/e O$o=[=?zy{ɼzmӽk9K ]{_~]s?`?A8aDŽ^@V`z]AAڠ؎l^Y7?|P,4",=L?,)*nUxVx}xg܈㑄Ȩ5׹f\>?{hQ QUQe-I't;6F bb:89nrn$0f&Ix*VC"5Y'yZr]Д)]SM?BI893m`j S{yN+vmfȝqdL=xZ@7&o? zµGk3gd*D}bJ<;2{k؜]9C)yy$ɩY泊fuJҮ| (N9".o.06Ž·g(+q|Σss[Y[<|m  ZZ/,Yس(b9_dWKS,*&RRYoݺ _&^־ce]+?8w \پkՖՒٽVomu5g/[j *WlHݨU]ټfMDUWC՘,yYҖ- [Ͷo}"5Vl'n/pG3?PdgΏ$v>U]WtϪz^Q߻wގClnpiض''?|m>~ `Y#8I՜yhuaG :J=ZrtX}'Ntlur+&j?uo῝<>sl:sׅ6϶{~ݫߎ G/]:q9oWW.\y-ڍӮwx|3? 6v;wM_ v?n~zJV+SϚg~+)=eχ^xir׫Z{=|ޝy?|t{(ohHʓT[ 43½CԩsJT고J @"e ]UO Ј<]E'ۡfZ(<4qL&K{G%jo ~sPz pHYs%%IR$iTXtXML:com.adobe.xmp 676 1524 1 jʐIDATxTE=wo]׈9+YTPEEQ *I%I($ (Qf$ Iofa9>[nUs~uTU(ȨPSȚ########;FFFFFFFFw 122222222cdddddddp########;FFFFFFFFw 12222222cdda`dthtiR ;y̌u>J&%IgA23ҁ_iµbo7nL roqJ^ʓ=8 V&9VPhv2`5c< `cy6mںu+!r"DZlʹd TMb>|W](++_SzyYz rٵkej\= ؑҦW;|h%:g8;? R="qR3ijOXV"@R|aH;@~_(F|HQ-_~>`ܹ<&p\w >=L!^{mΜ9nR#5ka;ZDδ6ŸHǜQ$Y@I o,^xyܸqpGQP(4!ʯ F9M8Ni)l:uU֚5k-wo%~oso8>~o@~w x8Fk:LbtESLIUm]rZu֏<)pO7?>~;N'O^d_J$IKvt|TH飏>>|/(8D1cv=i*U0lWY, C!DːI>`[ou ˖- -[w}ݺuI;A:|W=饗^'²13t۷KQ\7%1JFy|"{ԨQ*F޽moGidZd4b0Q3 )&XJ>9ZbC=JrM7@pz_M1:tH\Q;̠A@T4Tmcd B% Qr&5k$:-FscTZqMT~R*7wW`M^BcUO* D"?5b\zk֮OIpq$+Nd~u/^j 4/X_?rO>w܉J,j:K ;vyq@>}AʥVN0F;#}{A=5"]7~əƂrnHZ)9v |P(J jC`JQoT\?g̘fؽGmV\+C_￟#,҈iժ{}{T>0xDPb185eH>ڄ~;N&3nE̡4)1|x]2>hٲe8B1l13sAhMx0q} W^}Ul:#<Uʟ ڴiCbxDαtĀfӦMw}'dرc+ʇiF ?0i4R 8VoadlҤLH`stz,-0<3 ҄[#p  0`ԩLp.J>ڵ{'t ^&F̪~5g=܁ L%AК1 tč.\W_MaÆS=&t2կ_>C(+IJ"Q;N.]h"ػ뮻dE@{Œg\p{7 0V".BfGt`Y+ /_tD9%A0YLP2g%LUѱ vaŊ"5q4ӆjuz'Rb_yQZ@ 㧞zj2e@]HR"32 WPAC >R*1W4k 4'AժUh^!G%JRub^:8`۶mPRJɀc s=@!c/;ќe]& !YWzfs~F1 Sh. FR&7jdB x>߾}{p裏&cu9Ea4S>C q&(*gI#=+i:qI~cƸ@F~t5j=묳VXVCn5'@`#Cd(5G04npGK#G@Nc@:K1'C>GIy N[))c Iė/;8Ciiy5(ڙ0 1ɯ<ɜU( NhSiq=bgϞR~Oʐι{;uR%sB [;8D#ݩ v Z^qk3Z S& t*2K,Ly|s\>~->lXA<k(yҥFlj)e)uO;jA8A*D:"Z qDѼm}#XL`:u/(ޯ(ə~ =_ ] njY zTz< %kTL/ƣXӸqc Avh0߈uIi3<9 [x HF3^JpÀ̍4Bd/r-oytA [_~9J),͛7g!?5& bRk"O @"N. K ‘+ыD?@W6䩤L4 5m,B>B Vpc7:ae pAnl2it+|ȋd's|a bGI@@;-@&S N] ֙&ߘla6D[=ڱ94/3i|7w<Ē軃)3bJKڈdUKp? Fsw!B LqD:LVށznڴl*pDZ $F܀a 鮍†? %Q]/"tye ~@6؊S  \)ʯؑP ފwY=QO}cKr׀Ӱ9 9b'CE4+g*q@wqdt]ߙ"mgV #IGLыG4gpg&+Zbv.;H|=Ek R ̉e#ɓи~We>atUNhM!Bŗ_ Uv+HC!s\nڿ=[}#PZy;b,h u#:%f9M19ق2>`lX#%8^^Rqh k|џQ.'F:hp-Y!rs\-Law4QOWtPq1Otx̙%CF b8ha D(<$&p5 D r .mE]]4h7];DMNt8~RpهQG*1B3u&Cf8c9o1D:#[fa Hg-iIHniѤDs48vCs1 ƃTu1,B3L`c=D:SshpgL:?U'sBu;L9˥ :Y'%ެ#vMGba*&V_uG҅b-_k,9,)*Ќ;u!ӂ2}  sP sę\^tׁ6ENmMcbXgT'O2Fgj`eﺬMBҰ \u| }=JNSH􏂍6:D٥l%X@WM/ѷ96]'Xxu !>Q# b ݧj`1o]-{g0oฃP ܹWOo%wע8V M$ qGefViBdb!P߽X`$2.12aNc"09U'i2G%)RTj?dWb IK S8lI#@Lc9iBb$t;FFFFFFFFw 122222222cdddddddp########;FF72:t(p##\bdt(6qY##;7*,E}ܗ-_7}W>7t)A%ڇ=n8]H:ĘK'ޥ;Iyu,pG/QҗEky2ZoU\ͽmt&ܼ̐ ќz.dHP<ثtW *[f|Mم|)fYwnК"*S&^[o.]_5V8S$$/]jIz: sϩ}{Qh?/E )x\b՞W F\jmX&Z6ϟl2EѢED-$?d)Iŋwx.Ю *pN,W?M8=ܥ㣉 x0xh'Yũxyђ%K)A0hk x/7|3!tkaX2c~0`ҤIhSt2;hР#F̝;d2y^xA%LqѬ8ꚵkmƌv1eSrƇ~8p@Of^{84Oz]Ny:o<2 P Pofc9/">JϻI$o!ƎK?c}K[oEn.]`Hn;P&6-A7>w {߼)CQ ȞY .Zˊ_Fhӗ `ӲxbEzѸo,A,_\#$rժU+t@%qr6a VBʼnSEWiD?Sbco-Zo%¤Ws)RKIU*Ȥ?H-@ }qΝ;8d3N"D]vdK`W Nq` "AZC1ß/_O|ҤIxO>8sL"鈰æ8͉QEل(UߧONu\;;F;l9NDƍG#ұ0_'x" buGBu(=#xE"QFe7xovX "㿱@'lY1'N"^Cs,)ܑOHbJ]J?FߪU+mcFE]Bzb cҺu9MGua uԩSR/ C<ĄrWs"DÆ (>,NYPcho &nȐ!k. $x W^N"U)SAc9۷o'@F xt ^Pldپ`=)d:8.5p0  04;;ރB~IIFIE1D,Qړǻ,LEZ_zW,i2*K9|-"kscìZp'?;|/%Z4mzvGe]#c@oiW+ϞufF,{ѝ9}$^Eg6l (~i`-/ˇ^<ҾzM6 a6b?!aSVprB3ܨc~6*,U`Iuj׮AxCTG,Mļoexh-[ +(BJE32 f  x]J\$>)D!/d"Ez{1*HC-[OaG2eAd3+8E5k7]/$9@|}т#Ӡ38`>SGp!\%2p-l3`8+xѻwot?Z%sbF>Z(¨d4:}fw}wyDsկ_zq< ʞ>9eʠG@P~l K20Dছnxꩧ|0[n~D(@;wBxVԂMբkg=ӁQꉌN{^~Zʈ/jGyO iRاz*i(9W&"r) ^R/dK1@`VF })`,Q2L`5"NULz1>$c8Q!뮻$D3jX>hR lvh`+)J O?$XG~Q9_!;4Z8DfHaEvS3 o#k>:\sXó Ϯ3<\}hv5f?4"K̾O6n޴np׵HU~,0 /:պk%+7s'\~rPY;2|)ٳg˒Lb/*RJLx48ar`/uȬ"#b(O?)dƉAϹdҬ*HtEpxQ+WZ @7"IX%Є0"2e@y,ZTC+}.["+3t!%adKJ2W0V{#y&N}YCQi%J %$M@KwHu\+\FLH9$B>V;̪?NԻ(6/B٠Hco#ң-5zNqs4rjߝEeI+{p!wuV?ši174;Aԋ[TcGeL[n1*Zl"dGaIC_6B$;|pf44ظc5VZBu<묳HɇqF _ڙu4u 5%pS [~QLaKim-R,#bQ|;HOa.U_WV.;{H5f?,\kX\#Eۍ < *94zUțoY>zO"E^߫u'\K͚Q4\x_1cYBN D|> uOV$,"X (НJ78xס; {Fa) H$:@ۘ%9uT- p[nAi`PK(5w? )٘SJ*btSht*UkUOӼLgmO>d$ Y)[f#%KDIS006GhkP ɘGl'5L<`gur#s @BD7AeT'C^341|9Sdn+WZ.TG _d~SD[U;O3rHwsw7]@!x `ˡi@Z4ۖO ,`HKw ] :VNB5h=~!N؁*%P0:H}ȁ ]2k>pu45WkT&:3Au|pd%w%`zFkF.)$*V[` & #+#CQ<ALoFp(0O HFQ8 Pڦ? c&NBcYw AGrÑF)Xsȟ?ljbcD]lT9F2JX)o pE'(.3x0%o`Q*|ZseLR0M`+9dŸtMO?pPdKNݔp>xН W>8|p!Rʰmwp{_ =Wd`/RI=Ѫڕbxc֝\S;KC 3a*b0$e%6aC_pǟL'ă cRK]mRfo= J cٱGMGepG+B,Û'@xdPҊZYG!FhY÷&d+ދ $\|Yӈ ~B\F#PZoU ҙHBՐ|>@K4˿lbGeG9_ y `٥ ^w ]tS@3Z3tt}҆c[♐٭%mbNRZ I2H Vbv1@U-0SR `Gl2D+S $d 9Dz)UdːY5!F>mAK'2*z|W>5X k`FJT ]2J5DСn̄."go d)9+--xNHQI/>y-iTOgʵ4D ʧoi)G0HDj.xDQ`)9=E6lVCҌʇlhi@G {abQ$3zr'z#I ԅ1Hy+gd1uWy 4Ev-PS)FS6(.+KyQ*'vlCWy=7;9n1#֪UP#-9 D J(! F=8jf8FE$P!z+DnWa"@;_1zTJecH;BxB;Hh7?U}^YiNKIx [gh>@u+Rgu;[&n+n|UքwqWC阅!rM2H]"2ii$Luk;~K-btD!X5ZN3@/. 5FMA*}A `imB]8-Iɣ=eQWY;RڔP,Q3s).bޔDH]i:_pp&ˎ_w;|G]£gJ2Hr"wlmU'\Srw&'VyyE22SY}'ka0+(X)12D# #UC," #yzD50#AȐ`܋e;r^dŸ >bHdt KGXˀ5]z-eBbaD=ɂuמ20HԊɄfs2:seMDSϹв Ux>0i$j[^ۄŜ U}XVT"5:oL䴨iifd  !M8yRG KhPG@z/+ L\i^DD:EU!XLI;4&!2g:G`Mt5Y%9CiT!#+yaf#gg:eȷ*JVXZX#+4QRk7#G;rsȹ]#^L`{ċTujסl?OLQ(l$3r"[5ΧmHZFDV%Po}TBY9#I& 7lDar`Rҝ:EQtVjHCt&JTS"y(]^F dD\(<1Ԉb UtT5U#Ou@- $@ ПLhfCgieya/6W;/)D9f"+}&V?q^$|Pf&-/N; /2T3&Fjg)l>#Y@94skex$DvmCvX)$2!ҐG|Il^,/"*.cҮf7DnYHCynEGzTH`.(>|-@zveBz%mehN\3{n}A2`XrV.%]Ea5^2J =@+sQ^.f1A9{ m} Ҳ\CEK yqC.U=h阎>ZthuLT!:ew ޘpG^Y&邘@'J({޼' ;YZU;W\VNefYiEb-E2` ;| )iд"Pd`XܑW e/i(\oۯWMrJCwxINErɩ0[,Ctwԯ;#XE2KtG aq$DlGMR({vS,E7)@\P*`mb*f2^qaM3>x+?ӟ GZmþGtNvi:h&Ԙ,< S~ s!!lh8/&ވӫ;h駣acI,|L%ݢxGKty:j\iX–CeY"ݑ@'0bB׽.+,p i;ɴ""1 @5 @r4A$Eگ\e!%Y~S&O]`LpG&rAdq#u>r!hL`Ȥ IzSf⥛)nb 1Ăx DrIf$WXeTp .s# OƉfM߱am<^:J}][qbź@M]?]>jy,~/>۴ݫ3jl]!j0pÉrs]!߽=I~[_h^&oƉ́\mY :(m H@3o m5G|/]E3!]:r`/znqά23Qn^T?W I'M5eݫs+֝Ic2. ܲ$ /$}(sW:I/ ]ÚbdpgN,]S]aQ` Hb֍<;h;E\mUi;ha_swXBV!5֔s[޴9a3,k 30D3eYwҟ`Nͯ xȡU{Gĉn&zq& %2a LPxM:kvQd)"IR/[G N=к-6@(FFjߎq"c43kWj;wbKx+/T{U{%zǴol N< Wk~oJ\|b i&'ObI\9 t(0;g{WitH- NǾ9m -px$Y|B;U,IwpcHH_l,MS'8KN`ٚq*^>Fw7;;ČŚ{G5;w|3/{#o >q-f1j>b.NQAbYw щ/NOKG8WE`J ]YHw@g&-:_^U!6creFuZ1ċ$ JBP$j50H7xW3D)4sMSP;׷B xG7j\S臘ޑS~nTy-P 2ryE6mGK{"4߸&,>,3cTd%it?슓 Pai:\gB^8Hn`%*vkf>҈pq;<9HB.ԉ8.0䐀Htlp 9&#;ڢ#2ПŜ?Q< o~{w>kǼҘ'"C  >۬w#Xb6a.HiO g,=r1GYzXpb1:O]5[tTEhr JwS*vK0g5H_:;ҝpBL|[<~F%&(dse_|{~-Xr\ZzzjuZQa;I4`# n8vLlwg%l؃3v jNuI/$yF2/N;*(*U6:pY XReuBMswѼ ,X4ek֮ذ>3ʿ0ew ܽIwq2%aw!tK]fjLM}hac-{̦*ntu|ɒ^$–)JxǹkXLI42{#ݩ j}U씇|c(pQ!;;ws*F.ns}ը; 8Hc`M (};NGH$5JF>29 >n2< 9士׷no.='(C駟~G,X]iT$ C’l\s1*LY `o[o}sHgk!zjV:Q['3I>~ŲkƲ2 _r&kdw[n B$ڵO2U8P@cx,f%Jp8L?> Sgf׉4C{d_%INMc\1*Rp W_}ړ^z%`B7o/0hٲesСCoNb@'(2 )6(<"&O͌5Jݻw9hM0G1Xnm'jժu}"izG믿'H+** >|+*FuWThciҤItg?@̘1AұG[fpB:w|RcpǨL):}Qʕ/w޶m[l-L{xeCyiwE34hPV̇$+y^yꩧ>7xcI,h/$(+7k֌_^I\YCv=Ȯ~[Z5gyD>S)0V+Ucƌ!Z,]??~<[W^9sL]! iӧ⋼Eo櫯꫒Uի_-E>< I)p9k, CХ~ixd~]XC CㇾE&;.>JӻL;wDZhLxH%T(- '?9$%mq-kcTN=cO'ʞBK/} Ͳej 64۴i# r0va'ςxʕ+|H& .Ml ~&L { |g8}m?RJQnM_ ɹRJ >(O>3 [nIF#LoG),Iԝf!';իWo׮W^yrڪV!;U1*ʼn/9zE12!Oƺ$ jn3d< N:lD~\t-HNȁY3;sʄd ,d+510K+@H7o=9)yJ&<HWeھrQ;({p viŀ FNR FL2峙1@3;vH o1" ABKG2!T't2w{db1d܌.fC#G"%Ȅ0 P5\ĆUGF  b7i҄x׬Y0r@'*ȇ4 $r҆x>_~0bYw -aб(Fʆ.x `@ki&[| tJԭgʲJ ~ s#O!+;;3JFMt0Q9#Jz}**A@ZN\3cT4=toFd>_~N` k@f9Hpז,YSNa*°m޼," PD4X5jO;BLA-qKVXiC1AaCo/SԐ wue?#Biyo߾2 )<#$?<~:0`_%S -Qu t 8-c[K8SI @8FJQI`?ԇ܄KJ=d:}=fXSt$<򂙬X*EPD2RPlL>Gq*m8VZ-`p&ԋ^__2w2O'a Zt"TGz pG֎'x#׬I/^a¸Yѯ_?+3ydxa1 HLAiъOY}ZJFX+滼H8EQf9Dj9=jٲ% saꈭH&$VWuu(un& Ǝ0uj ]3BT@sz.(~@t rˠo' 1RE<*JCE՜& _(KC8l^=8T!=E&Nq*9(ZO&BDI32*鼘e\ˉkw ܑXq2o\r%H 0[ pB*V={$ШQU`Adb XzH8?>29,Dz"Z؂&O̻\pǟS#'+/dN< {9j as1䣒` Ԯu$oqJ̙w Ai`A/~W )Z1q!m>aޢҽC2 @ CHY^f,y'E&^|w[E1XɩX/0?sˇSmrNOc\K5;FE^rt,x<&&3~L,F'h8e>wG(V8ˌ!SxEG'* p?Б<:%H :.]ڹhX<3/MnVI?,ysVxic\1*jpG^IҟQ }Å VTeb[EJ,sҢ;hןݟ.}{o%BN>`h9P>qͮ5*jpNr;AP / Cib1 12*pUEqDůt(Yh;Ƶe#;FFETϞ>Z䣽F%b3x%iD(q*pc\+12cdTTNL&aVI{z3\$eIb"=+χ;Iݾbxewug+Ncew 9n`0# 6 8P?urO9 AB4gpPWx^Yh&'"I.G,p^8_'N;\]]73zx{  12n6Mqnehqv"949xEΠ\xGb N &:̛3= wO<+jrVsy?ϛpܒt4;5;Fw 41?G` `έlD]tE9%Кm۶EG_&ħ~SO=lA?`;5;FFwg}mPfV'OBy`ޏ z<ѲT% %YP_}U*pʋ6 &u gpǸfpQZQF}.s{D.;eV.t>/ɮ!G:]W(CxcG.[tG!ISkw  !Q(Nn12ct 2VY .BfտX h䎆)7}yۀ6-['WpIQڶm[Zȿ+X!S4{c$}#Ш?ͮ Xd728Ę-֬pǤю; Pe֤Fw[ cBDQ;_Ϯ3"zd,P?h?mjvӾژ.#N/(}dq-YfpǨP C#qXdB_Hf@He8(lpaϮ3ٸyӺ|;͛Z;>NjZ6Q`֭XQ3p aR Wr_GS?MIھrQ;ꬬ,#wp ܹsg ט|:/ONw ?4k }dӶhqA?5yMiwT#9@G(O?\6Y4=N#qFբ`Gr^̺cdpߥg[I>;PIg!gH&p8m?./!҃'"=9es2;HQ!f<22\u@=5KOwmk:&\ow/0}SXw9Sџ\[l.$,5PǴ𢆢gG 4O323ܼ%@ȜGd[,N"/y] PsOUx۷owu!G!`"軔mO[\ZuǸkb:w!RSO=@ȴUH}D߽{wί/U'ML$7ȂzF'(}A]vU Kn')inƀ9?7SǤo})\_/mfEn^9 i0*x 3(|ֳbat@Ѭ< -rݒԩSyB_Ɖ蠹s|xċgϖbG>ѽ/P2-p 2q!ѿ`Ǚr@])!]A::хYONHϴC>ZDS;Ƶ\Z_};FE0RWp@I7E]Ľ$'NzfNnhFLf^6{.7O~ *p3C$@D@p_^Ƿy2e@hsp'S_D2Ti&ܩ3$\wSGψ}n=$qaWOrp#;GOJzj["P$J)5ayH2r%K$"Ȱԑ.e?Wu,e-ɡ⹺P/aػNfVy(*TļB1)塚.[Ma(t;ƵF$&;FE\qw d %FYf۷Vfu#/5Æ cm%M 4|H3|\L{dd'|r޽%A0֩SGS1?>hr2?L<~ K LуHUijvG]#Ͼ5=rDHޑDRdTO81:.}_ Y9VBӠ97Q/*mʥfzVfj. YdO! *3fӺI* xߍ\8.MOȖRѱ(|r.߂hbHyӺc\ˉkk]K&[2*Rpe ,b}})̽/wJ Ě67t24Qd wUW&0ߏiܸc̆vK$i~) H9_$Zrի)5R{|ٝ#Gu\3ruH =#EBOT =(+]|wMw8^dPIٶ%U$>Jޅ͜bdN Exgqţ-}' uV*. ]YFiq-'ag1g<\kXjvmGO,(gZlY|ynnAf"İ~͛7QVjU95t 6TL֭҈82$Ýڵk#w92GG}T{-ujJ j7 իӴiS UI&?ܹOdh+F=K."u-l${cJ欴ٙE_~{ !L.:r\%%at!ZPAH+0 6C@PBjYx * 3,60 qh>u#=#N(D,ѯC^ beXqtܙe\K5wǨlw w`UF !(= TR~SR% n1DSY2īv;R#kR.oA:kgϞlRk,=@ľGFSNhݩ+z1X}PhN+:GBt:E-%j19=#ZGhҸ]ԺɊ 3NHýŸ $;6@޳.NjP sHo^;{Y1f` rŧ5)'C:(W|q-k!ۙeTά/f]xᅶ3wfdk`a dK2OD3ȯjժtnV<~h KֱcGu9!Aƒ+Ob054>#8_*!_dvڱGTSwOGCM&j=XKEo27{ǵ-t$;isz41 e[,1 2w܁eFw, ,7j-l@` &v' Oe!=jL1? }MX8'=adn,uEf/?{O3q}w)B ؈ӻk6^' GCc> fl6m*Zy%{wxc?L3ԑ9G>!7x{XZt;Ƶ91*:pGĠ`;).8SsCd)I?_'(3ܝlܹmSxBu3PNٸͫ =;wÂ;5u̚ԨP 뤉uǝP88pzr yǁ $s&9}/om~'κSj;wbKx+/T{U{%zǴol N4mp#q$;_pbXsf?y6oB3Zzo{⭢'nޒ>Y00R\ʚIͺc\3cdp(? p6 -O /$y{ M{G5?wRS)bG7?I^YijtM)tjBe\3cdp`[weۘ]W]_-:0淺w3[^voeu:|*3WnxQaԨQ,a.l8 y&I"sP[;%8d{,Hta<䓺]rtyz#;Fxt 9fqQK/~iV;O>Qv7.(F4ޢo ܸ!3o{_"A$ߥK.U'Ovj7jHD''N <aԡv),l$mg™K\Cx@_r矯 112cT6|a~Wdh.` ~\lgy=+BWpk]h;N+vs+Nc6Gnnj[pɡW2KqHNUy'p;Bm*FsrҲZj#*υt_9PuT]a;5;Fw  I`"CvQgou}$E(KL_'Fd.ޠA/~mY ^J{@XUqS<"+Ss8.CK_x/Hݩɿsn+NY)XOuō}GQ 12w?Ht@֝RJ1MG :T 쵻ѡCuib@1b-NeaBgZWOC. ŋwBr(wމ&F|wu NL2h\ӊ+:x8ub\]-+y->c\3cdpȨiŽ5}ǔ% Jp5EAj,͚5%)>\SyB2n y'/b0O"'UGf/Nr;q3S3nZbJ3ˢO;5;Fw 웝8bό\gGru*3{-Z2kKȸLks?fekeiN+9PlSxh~9KGԶRfM/޲FGfK箻V8A#;FFO-N;nLu&5iJ83 '"0T~ ;\2̡HJ19 1:p;ErwoqlNpR0#;w qh G+׭X낑+׭Znurr!я>E.6Sfp:׈'њkVbmW7|G;m>شlE#;՝9-v,RƸfp:,ăuR$}#Ш?ͮ Xd728Ę-֬&p'QKb;Lrl=IWr#;FI qȯsO,:Dw쌶~=Έʮ@xA7?ɫgO:3kcƺ4;TưK/^qK 8~鼄 W\ B|^ T3\ vqW%ۗ<ݖlo֨QR c|pժ}~ibЎlpaϮ3ٸyӺ;Is Xs>/ꜾIp5uv)~ޫÂ)Y^@a80YWL%iN + ,#;:++H>Ӂgo2o}/hv͡ ./<Ȧmv7~j]7Ǜr8_&l<'N*{JG%΂ӂ%ۆG栶mۺ\,prR:m/~Ӝ9sHbSy]={CU:`+WʼnG{sHP^=s9#yddp{k >?lo^1J}{(|qȄȺs;EK(K\tm 4 yIϊR-[ԅ~yJ(Wԧ24k,2pc% Nh_YInܴ_N'VP Fn 5RҺc\3cdpHf5 O<%qFT-luGoEq+Jb?ib"qINc׮]q]injds!'MϘޗ.]ºSp~;Wxҷ/mpx(60|ϠYFOK'\a#-dСCݺuH(".(@EIvsC\@81h Jlg!KĊL&ޱ#` ,&Kw+ qpCu]Ǵ`4O#e˖.EEO$*Ad(9 l%6yp_x ~ɫ\tXBHEU%s؏&1I(: 3W_}E *l}?5+]N;{kJSk%4 R TpfpeߪU+ɯ8'+)$v^Q'c kXX3':^\O,pǸfpΦMҥK3Y&=wWq*<)!c"x`A8l0ֶJ]r ,ѠAȇwa21a񊒝|%p 5C*Y$1i̊O=T:]~s)ӻo`GJvRW>P; pG}#:YOkiËRQ DMiݸ"3\Ϙ b'E ʕ+Զ6jnq{G3;OBa&_xᅑ#GJԀlÚ6y /J~۫ i o|Mƍ-fhLzvKr;XdC>0EE2yV]d ܩBTH="gw1r]#eG* = SW$7; ƉYR?) غukMıWZ H[лd+nHO zæGoaUkذ!iX+iѢ _~vgNEN HB wkw ,FkV/8 '4 I?'Zv9:<&˗/-AJm޼ ?*V," RW1tp+&@2?.cIxPI'$I;`M0)qΝ}"%;Et\5R)M\]"uS$Ԏpthg#S6gax?w DRO9=1hG Ĥ+JFmS?*À,7.\(&fdDQx>sJ` FQeE! DX̪+z1X}PhN+:GBt:E-%j19 NNsx~>eO$&~Xq /w#q Avto90Ŭߙ܏*ĉh _x R< 1CuѣZj[oS^G{d,a}6K<$LH<-w4>36dl¯|ٓ{ܩ;h駣acI,|t%ݢxGKty:j썝9]Nҽ.2Gw{80 1C%Әb2' ys9wwkw :] >l2U9#ˍZ 3A=2fSYbH'7@ {oiN?ӂ}9K?`j዁Ѓ$-@*Ϻ_w(*/sSmOxG> fl6m*Zy%{wxc?ܜCѺcdpǨȊ YBdHʹ!E\~q/΍ꨬs<]wܙV-[bXN`p'Tu1PW~>D[⚎ON n 9$DmE3s>p07̹#J`N[Y@r.Z"st4L)4?F׉p˓yC(]a32ԒՇH0`twg#ŋ׻.(X !e<ܑyK/L3:ug{:m۶YIeaD]x.3~9UrmU "GmKX n=n##;Fe;? W^VY@6hЀND@ˢG`dTt W )p 7ʺl@"^lbY' OXpopXN- w8CK |.Ց Ss$ӂx X_{|] u&~Vi-!-]W+ʍ 12*t'݆-8\F23+W$M} N)rE&S&cJфOq4F{…Y%kѢߺC+ KH>7҃u~$ *(Uݺj< Ýj&~[ cɦz[ܞ~59ܪ`L_8{q?eg,k:f9kRjx@Jp?# Y&zċtbŊ]tEĐdHź'm.k҉|)@'=盯qa~fpQAGgM;Q7Dp>eGMqV 6ă=Kz&ZK'VĥCKܭfpf^rJ=X97%K pm˜j54ÒCczXP)`]!WexՄ8rj6VLbYXpĸŇ0&DN?\3f32 `)7%[ ;.7#;V(IÝ-zhgGc 12*`NNeco5KijSW',c1QIGv Xx\ 7;FFw rpg##;&  222BRAY8tNNYw Oř{oփ|WK;Ӂw q7qr8 "U[:wTg!]<]X'#s$96Nl2蠝N))E/Z'5{}CwZUl'zQ!aSd' ;p'LԟG}41y.3QhX$%M!I:*:8~+;r8ι8lw'KSYI`p>w.Ĉ%y:S 'r˖s\2!.tHb6mkw]zP2]7 ^6azTÀ9:OB(K` 8D)oqwcaq݁^az?P!g XM4E]"q7 Ҽ\\;CwZqS&^Fp2pn*!D,j z*U8uZ/)7HCkvp,ڍsrW/xw,G~63κ+1cdpNPi?Ok׮]T{@yTD ;Ό_85hРz?nݻ79*Ffv λt'7dtKv#~R$8`]"*ET\ A2ٯ_?'Oyuqh2VZ9SOUi'@NG/s=wI'q43uO݁EaygwN6x`sµ<(HУGSN9Xmڴ(DQ2W"+̄di}{\[ ͋̍ttjcp g@СCm'X4anod6 Lu(z,}+BQX2no_;gݬP/XJ;jڴLVC MF &Tj*&5 P2p>;ݬ;\SOawWa!'5rXP>h1ZC\j&85 &Txq};:^PXvAJ FnC"E28p _^]UV`&bu@*H$l~ժUW`}&w  A58F *tA$%}P~ TJ}Lwub~aVX@2?#bT6:H4ZA#J*s}dd"GyD(ˍ,ũ{w@3wN*PmիW8ݬ;;7YMO\Ye#*k=B/أL;WV/ Vrj:ݑ.P n]nH0 @ꫯ yWVt'/~ë{0y C *kp}S\) g?Q `ڔ5 TN9əgd8!,|q"D572ߺCzV7 fs13 pG#EJ@Z J \1?ڵ+ %z窾|Nرp1 2b/ӋA3?p,ms+藧85KVb~\ TRvXTaO.^XtY ыXwΪZ;w Mʈ}SNwBP-r00)gS (UҎ.]:XK FrM2L2ȂS|Typ]2d2$= Rec` pF&L T֞#oڍnJ/Bzۭ&r@V>XdRO<5cdpgG(+L!N~~;*QELQ@\zUH0y)M `Ctv鱩OqO_81 jo 9Д/IZdi>H[ ͻq".c9n$ ךmG}wF5/p0@qrC;>mRaz,9zD/" n`)uZr:~Q<>YaI_~)I YC⣽ZI'X!i>|y7&2!+I% @U{,RNwt!E$}Fs9B>T+rʉUރ ED{ls|Ie/2cTd@po-- 8#VՇ" ӋK_'ryf|270Påw_!k_7$W_ T<8' uY<@h1Ļ])$E2JCwYq"^r[{̬e##թ`Z4 {DR 11y KFV[dS!0\}Urjxyr:P3Fs* ;iJ~o1cTdo?D8 ^qVewPCf,>ۄ8C\ c&DAʳ N)@- !(6 {eSNBa'k< %ʅ^њG؛B+tt*Rnݺ&aN-U|bȒDi|Tf;)j1YsHZwe֤Fuj<3 Kٳ_@`#RZ@ijDa\-)NV2]Yn23C ç30h͈4<[ȻvŒL|C8S63B%2w?&CIK<[(-: %Yw 1cp(M=toئʟ,SE΀֝wމ"La ? Z 9@’ET:"4X^(9*?u AGcuRti&n`U4p@ب┽C٧$` 7j,:QSk;&k 12J/rU`gk7Q GlpKPbEt5Z0%Xb,>d|I|wɫ=L$=e g#-lZ- wT0vYM u-UU+Wq0^RЛΪ˕CXtaYf672cpǸfps4>NxMf,+xFwǹ,f[' *𐶯Sl'\[$|Gڰ|(gl?6U'K,:5԰`XWP v*˗-RʁsI<56CV$_Ȝxp)5Ӯ6&K`ɍW,^عw 1cp(D;U!~a9:FD̑AH)3GdmritfΊSut\ b> x*/~T~ˬ@:ǟSw|wwujb-c1YcpQ: U;g+p@8UewIk]/Լ`O>P7ic 1cpȨ ##;&k 122cddpd;FFw 1122cddpqg;FFw 12dddpǤc5 ##;Fw 222c##;FFwL1cddp;w 1Yc\3cddp#;F֟ 7c,3cd"(/)#w 1cp!O"J[e1YcpCzqh G+׭X낑+׭Znu=1cp u֬Yxb>E֬]k[{;0/oe+1cdpD:+Xg۶mE7F^7n @8Ec*#Oٲj:awL 9$O? _E֯g]TvQu?24ǟ6y5bi_gfmXapdqQ ueܼXQ֝Eر#G㇏ <ɹ{lpaϮ3ٸy:;wkwUedfl@ C~lٲ!N6mJ07Ʃ"~عsgIb/hv͡ ./<Ȧmv7~j]7Ǜ 1cp`%_|?L?%K|5'ڵk&sΟ7";۷oϦL2y?|D&ׯOm?p8L_oG2(;Np?q={vR{K/ P#Esp瑑1$\wxXm;-{MDŽ+ bA@Ž"1cpQ 6._oV$C= 1&LeQ> \r%*U*V؀n݊Vڌm?hnݺkQGѪIi) [o}'OtޝOP= ))lʍl24})\_/mfEn^9 i0*x 3(|ֳ 1cp(/D7@\?Ѳc)M8"5k6f7w^ɒ%:%^ 2:HAs^B <BBͣeywhG =#oR$!#ԏ>4q,X@ {#;>>u%;x7m4İ߆GG1@[,NX,^򿨬Nl~tf;|G]£g0 ]zEٸ۰ի'\OZ𑝣f1cpgl%lеwu(^;㯼J7,]:,+z꧟~:v_F;_vef Q(fx@%yu'qdD#NJ{y릛nz'XX^ȣ3gt'tR2,Ϛ5@9zwjR M4 D #$z?GvI'+#+e{?x3f& 窫"",ʕ+7hРQF5mCڀK4>z-(=6W%s I@l<F ,fA-&Zb8žD:t_p*a@RnUT`ۻcQqʊi߾=h {W_ܕ;_)=RGΑ:F{lH"'cab*}dp;FFl5˔)(hJ`0 :pN/ .p r0xbG(Nh8Y}O= r9+& 2;HX!jBKҼ^o]>@L("EeLF9A3 @NjAn8Fa"Gԧ+eۮ];W͛4iB/$&L[,Ro߾Zj E Ν}"%;Et\5R)M\]"uS$Ԏpthg#S6g1c\3cdtpgә\1_ R ԋ/(e/ʥ^ X!%p Q?~|Yԩca?.@%PL/}̺bz%!"`h pD]t 0<^wX[tpG,hтE a>DpJv" /4 II 7Ъbp  efj֬IYQe;U{EO2Z:kϼ:9j-)zE(1_4zYeDB-7&1cp/flm<} #2wߔip? B}whv<裸f0[1LjO4~).r'̹G,-Nu)#K+9Uk;:z)-Zz'Bug{Y^^WwL ^ɶP-oD;w 12:8"(56rh&7S4OqMz}%N1MQͼ6m =g4[EC{Oܼ  ' U ̧ k"Ė(waQ^*PC&э;wdcfߣ$z^K/T{F ;&kkw%S nLv*5oFq7L֟W]7cƸfpΡxp{b8ltZ\h _ZSk]aK6>QͷۂE+7n۞kp#AFdg+24w[pɂKS_O?.Zf 31cdpDQ>!m__dh.m6v0;FwLl?Hdp#AFF 12cd"Iy;2kR#AFFw 122dddpd;FFw 1cpQaݖep#AFG:zHR-5w 9D"N Zhr]0rU׭_NU6YcpCY V!Zv_Vmh4rGÔm{cp#AF`m۶zm?4*+x  <1f˪50e1Yc\3cddp ס _E֯g]TvQu?24ǟ6y5bi_gfmXapdqQV]֭My2;-ڱcGAa^ò]gxvoCkȮ14ٵ__odo}quw  geeMYȁoe݂iӦ~c 7⡦?Ν;sɻ‡x;_=$|C5,\_xMۢƅo$o͏71cpQ ??.YdWsb}/]vn" Й;wy 7ɺ}>lJh1C<&|v7xo:uĄab[OWvv6I&+i'1; W>8|p!R2ܶtLR/$ _+2a;w ST̙3O>dP.2d y&7TZM8y衇B f„ 2ѻ \r%*U*V؀n ƪVڧ];4 -ֹsݻ}e˖]tGnL>=؇~H2b.mE`֭[+5h;u_ Wc@z'}ݸYox!j lt4;w 12#D:6֗/ꫯCo_r%Pɟst,;w 12SgYF&MTRL֭ۥ^z:TmWti?̊$i\uY,4իW?ӯ_`~_ve(Tv'u]hn4( +$*xȳZw$_>|>nӁx≽{&cǎ}LDdt2rH3f1{8ļkW^Q͛駟۶m[vmb,X hvJM270rUH#%F}32jzΑʽ#75RȄϣ7<+rKpct;w `"0ξkܸ񩧞Jz\tXk /_ӧ>QE?ϕW^뮻dAj M̻|z EKO>s窫f#/;Hce2dK]s))Ywwnq *oFFpIS`ً lۺ{U1>!nIb# c SHr#{D9c亞GvT)-z2&RH oGw 1cdt`l{W֭rq9um-4;%(9jRL+'O<ࡌ_`(i,8A#Ֆz -N>/ȁ^x!7nMʲ`HHWתUGy'Gu'+^Ąo߾}{axWJEX+V'N|. تlL>s+MС~.f))ZkʮbOh"E.KlE"vD? ޘ9#;>cz袋ЗyP֘XmbhFˬ@MD n[5ŋ?x:X>ŨDX_Eֺ##s1#Eg}6C~Xy:u*" i.8]`I @ 0¥b}+Vv;U{EO2Z:kϼ:9j-)zE(1_4zYeDB-7&1cpQ^,fiu O)̓P0*G?%-f… 1+Wu,լy3 Ŋ%p#Vs2w_Y fao [&xOL6 gϞwq1Le4m/] N)r`h=@E\s y]؁3~!hɊ<jXTBTXtWR<VE":+!f.r[ wn=W P> fl6m*Zy%{wxc?4cp#Au2 Orq',Gns:P i01K:&Q.=Sg~gwnDuF[69mP]VzNٸͫ =;wÂ;1c\3cd"HpDFkIJ"`\ 'Qh!#y::VNJFtG9ݙ?؟Ż?Aȟ؅ݱÑ]&W'mN^wJK︖މ-⭼P^fWh;Wm4T;;wN%312]:49nݡ2 5>";;7Ś{G5;w|3/{#o >q[2c\3cd"hp Ӯ4)I"DT{ M{G5?wRS)bG7?I^YwL S J)j)._o[ݻ?\>rjpdqCĩ6\Nj-]xK+yJyK4LQvF6ʾ~[h ys 12cd"(l<,swEy .Ypi E֬]a}fw[;w 2'ģ mE Br|>kp#AFFe;1cdpDQ!';w 222cR̚D#;wLpOr'05FƵYS(F)3D֤bVEnXVB:)CN֝5k`^ckF˗ϟ?;Ƶe5 # w F˼yQXhkB n0 dd19ˬILfmܸq>`k\sY22*p'hߩk,sY22*p########;FFFFFFFFw IJ@7=Ƭ$kg#5 !b,kddp',0q# 4 SgChkddp'p\v-bҥ?r84Bܱr"/FFwre2/pj84Bܱr"/FFwRX”_h 4Z=FT‘ЪUb^~Я9_gy}.6HwrC? k[yaD? [mٻp\jݺuw:]f74 bBs{5`{`n8l) . 6l,8e)1O@Q323rX̌) bB37=r=dӦMdfx_Z[s/;A7n,M*|kddp'YR@͛7_2dȤI$%Iܽ{vG\$[<2Tua۵k"vJy֯_weί&xyاf&LoG/Θ1֐̯vb,.^ z{$@ ,Ci}ш#B6@ӦK4 &FqLU͋|Ǎ7~ &|W$l`0Olg֭ŋ̙|oHi$m&;)SPҘœBzjԨQ_|qVd4+⋭[F/" +[۷oyN-Nر, üp4'1K*d޲e B&lwnyWd/d)KLhC~C>CzJ C`pVі8/45,ٕb XC7+rB.7>Do)S7{@<ɍ_.sp y.q0"QfTucJ OsOZ$Xj/9PҥK~7|21T/thFFw.eYG\gA!qb,rnASL|TzuLH^{ I|X /Yw,>e.oP()Νˤ|̘1$T]#Ґ ͽ6vi :9~x6y,!!w!-ꫯΉeI3MqZl^WYM 3!m۶7xc"Rt!Ҡ}r ܮ:3%yGPt !7'c G< o 7N>d/gO(opIٷo_d PԅnFgQfҰRCTyy9^aADa_?sLBe˖዆cL IemʪVZ DyJV^]JE2ʃvqaĔ}dl#YMh榇s=!Rp/Sp@&Mf7wL,({N20.$eYN]%^PxL\pQX )!%oذ[`723G$mhu? FVષzZtML>-P81dҩS'}iԑ2ɓ5d:PfbXmԨ$asf9O|20(Br饗qh *+?0 9묳T"S&O!'hI iĐ^_tEYʕW^ 뇵U+4&J+bʕa(F x#^DȐ @Hʻkddpg 6& Wf~-nszm'LaAl!U,QթSx5{TΝ;#"@'x"e޽rkuHƔoG-ޢE VLH-g p(/er%_d^PLdo7iCY_d2 A6'%ZGeJ z衤,˩ yċ`&u |exѲCF+Fɖ߇~BױXPH^|'3vz֕"Z\pGPBAK%uK0!)FJ`W?ab(L(NZys◟o 4"򮲴<˂g15isO>{ XmرcG0EY|![ »[H_ 0qḦvLy2ڪM65IFGMb"?批e f磏>3( R !43 J ӡCb'$ɓy9Hi|xQ5LEʃ9¨+ K( y"鐞$`yK6!Ref&ld! u䋈@yX<@ʄ㕘wZnBs/=dV%4 p>~`_hqNѼr вIYSk聀r4LX(3iŪݘWJ]4+uĺsGP0F ^ᗪB XCMɖYGс\> $"[Js4;~e) J dX`\F2F,o'8 :G OLش"{-CځGj4fp*6uLa3ar]&YlٻpN(GℸAb&aU>3YoҐX`1FO?{łkk rJZp I(с#1bŊ+w bN2_޽ދB^@ |0!%JPy7l;GI@]_+WwhB37= 555mbÛh|ty7y$<BL7`a4)Rw! %2a+"#RAI ,ݘ£V%HIȂnPT xE}g:p96 } 9};$H{ "%,+ Q0P2׉ )FM,A Z*f1O=D,WЪ($mR,.s_%bvfNBD1 &|z0*\ ]NK''Q?WarZ|켙XÌ{9/?㨂ʪm䝝Xsœ9^;IZ&q;izHJ522J82ypI2J{{WTr)O!sVbL$IܿhX6хv@iP9UI S"-mCkXāo1;@N͟yNLߘ6r pyQd7i=`LP N8 `k?]e# GCґX8#wlI%eWn'y д?ʼnw,kddp'pE@M7vK&4ɿ[8lQ8aC#MܵCl N_6r HF DX߰r("/FFwr%%Chfc########;FFFFFFFFw 122222222cddddddddp########;FFFFFFFFw 122222222cdddddddp########;FFFFFFFFw? X*IENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.15/autolink_details.png0000644000175000017500000035637513522025220025104 0ustar noahfxnoahfxPNG  IHDR6LME IiCCPICC ProfileHWXS[RIhH RK E*I ĐDʲ ]D@]Uwudkc塈.lI]{}|9){ :5<4 OR aMIMc>pF<\ʎP_vQrs@(A! /@zR% &TԸD3ԸReρx/d'@Y,ȣ}bW@,@ q _@ 񘼼YJ CIFL, TT"2Im3U pInLF)B WZ$.&j| a ٬a)5 <*"'!r_S9cBqr 39 Qj̦Xĉ)@'Dx,O>\/L$hpU(1RóSoqPNʧD"k:$MX $^Bǩ bSya, R͏H yټq|" 8  0 dq{_S < Y@\4aՌ^@1"!f?hW-Ty䀇( +T^hԈsͅC9Oj50/KgؒF %FÉ  ;gٞIOJ"ܜ)^"`pM_֌AVO<g&# j9̕ƎJA)(==GX=C\3F:>N =kKlv;a&ŽaXvDGV*'ܵ޵z@X|?,8KTb7ŕǎa_S/(Bh%JE zB0&f`>ҰLL-ʰ kZ|w8g,H< |^Seޏ" gKB"&*;  CxM$D{7|SĹ}Nb7qD"IXT@*%m"%#]"ޒdwr89,!/!W/)[%"̡젴P.Rz(T==5Hͦ.VR/|&kiUjuV;>͉ơM)h+ihi7i/t=F/Oj3js /i?ӡufTйӧKѵtVҽ;sӋ[Gc}~@DInưfp|RiFހkmPnAAxd"j#]Li2sטGbZ>aԥQoF ʌ]5zo231^cd|7q2l2diGG?S4tv63s3&f}L`lG{-b, YlV.uoijifn9heodjkuzV~ I6lm؊l7ڞ}cgob]c{#{}}mCCCGcf'ITtur;ovC;F2vu ۥХXK6}6f\ڸ5ΌD%n-n/ܝW< ==w/e O$o=[=?zy{ɼzmӽk9K ]{_~]s?`?A8aDŽ^@V`z]AAڠ؎l^Y7?|P,4",=L?,)*nUxVx}xg܈㑄Ȩ5׹f\>?{hQ QUQe-I't;6F bb:89nrn$0f&Ix*VC"5Y'yZr]Д)]SM?BI893m`j S{yN+vmfȝqdL=xZ@7&o? zµGk3gd*D}bJ<;2{k؜]9C)yy$ɩY泊fuJҮ| (N9".o.06Ž·g(+q|Σss[Y[<|m  ZZ/,Yس(b9_dWKS,*&RRYoݺ _&^־ce]+?8w \پkՖՒٽVomu5g/[j *WlHݨU]ټfMDUWC՘,yYҖ- [Ͷo}"5Vl'n/pG3?PdgΏ$v>U]WtϪz^Q߻wގClnpiض''?|m>~ `Y#8I՜yhuaG :J=ZrtX}'Ntlur+&j?uo῝<>sl:sׅ6϶{~ݫߎ G/]:q9oWW.\y-ڍӮwx|3? 6v;wM_ v?n~zJV+SϚg~+)=eχ^xir׫Z{=|ޝy?|t{(ohHʓT[ 43½CԩsJT고J @"e ]UO Ј<]E'ۡfZ(<4qL&K{G%jo ~sPz pHYs%%IR$iTXtXML:com.adobe.xmp 1644 1800 1 F-HIDATxǕfw͆9Ig;A13333-;昙-$32 $˒Qd1˒~%R{oF#i9gM[njSnG |A-bX,2 W9kJQ9y>0eʔ0i$bX,bC׃ ~ɇ_ oW"bX,bX,*,'Kuĉ$aWϷ"3W/9ޞv‚+ǜbX,bXB8 \B> "Z`M|0~ܸ0n(s.^u&%"8~0f̘(NhA|+a!Lz'?.3: ǜgX,:vbĶI s]O!'c=X,˜/}Д3HM#9Es\# ak5]W^ ?|xp9'*oIaᆗo;?OX-p9iX,3 $_DMT&!}(Tlϒ%ucX,ƎU *l7”SX!|᫝1Fr؂ ><+;kʽǜa[?΍;&=ڰOI+cq0Z,~>S7ߌI~OwuW$L)zùFUy\O:#Fvi\sY4hP8蠃bG}tj*)X]zNSicT~}옱Ns"DII9j&u-wy5n|S:innjZ,E@^(te6st[*+/]ዝ Ǖs\# a=['3¬B*2(CX)kQa⸉7VV/:^',4ǕsV{b)&nDb7*aÆUϱyz6lÈ@ 2$h+qD 28pp燝w9?qkp>Q N1o~muS`eRު;ʲ[n-Vx¦p4^CZw*/D_:jX,JӨŒ22#*є _ne\ lFSRJa% a{c* 2x+r0Y߰ʳ;>WR}oFSR5{,R_D *w}խGHPXcsni~ó>I^'z6, .!_vm87i\}am$MCxrԥKpEUWSvu8H={v)l ]vY8Ccgy|0 HqCLz*sa 6Q~?9|#Frm&l;cxcz?C1.r|Qލ6({_|cbX&" tǮ᫝ET$p\# agt,#Lg32o$09yjp5*|~i27CX޲r[,\ǐ2H[F\!j7LAnHjzbxW\K(N +DRt7ȇT}gǭcFO[YاO8@N;'x"IVc~2BرrEK\wqGXqc`]r%1>h$wnl^{h;3,H</=g/ _vec'Q,2ib+!A[@+agA<c+MK !(oA-&SbX S[!!HV0o$\c7k>C@!/864>~^\pA\[j;#YgU](:ǸB:Z0`@-֐u]7ĭ!:BV5V c=6@Uu.B0+2֋V^ŴX$ϬPB&,E6;bXI R?B&vpy-O2JXI@*+n aGzjOw[F CXX,K}c‹c 2Vˮ>U!9fm̭a%p@\E|3{?l1DHXAc%𼹓BMA_o8#%y%ݻUCb/VNαu׎}{ɨ BZkH a+^}ꫯa)dRsrH$Ga^w|}v/} CX29ȅyƁYV/{ CX\ v# ]&4=rJ_*5{b)"NeT[q H PnC vLe]*gqrg=2tI1-:B`FyىjX95{q (k[hNu0 o C x=lc>"c5Ps\# a{KmX,Rr!?| *M7 +idi%Djz԰"DD/ܱEvXF! %\2#V?./,,V2ͽ|i` +uxGHF,Sf%/}v?A@(3[A!'*I<%3lue,*7-b)&R Խ>uW _UyIs\#L [,>jB4\az95~vPXy/+߇*[CZy^s\# af-RӆaQ9bd,cۣoc iaңO.aUW/Y^&$-\g kx;XXycij<˨o2Kc r~z\ȑ6i]']L!奬믿~| &-)5V+#j13XWƣK/4R&lr%6;+ ŔZj"+m bX&RyX`Tˡ^&zPxg]p9&iAMT8ǵHka!7_ 2la'71F6bXj&IeA &1@x.ʘc $l>n@ 4H2)BF<3#Bd훬Ab˯CԔw$x)Ke6L[JIX__bi418J*J79}{'FN\L3`:yde0D8057=7 D^1FFX,Kqa@?¤v<=NMx7OO׵bj6VL/q-O:-Fj&(k]O&s5 H4Е|z,WM8gX,Ja2}2ZG&:3qA@8bX,KhIbU?wyqkb3;.Y,ޥɕ`X,y] vZY\X,e%ρX,2IK[,2J^nX,bX,%M&LbX,bx4eʔ`X,bX,'M'ObX,bxbBhX,bX,&bX,b1! 4iRxgλ=4yZ{?O. yIw~Cٿ>oVm,ls;9mlgMX,8!d'|>G~fvPKqDy睆(mM>?ZOcnt({˘ߔ]uONoB_H}F8NO{fmΩr൶;6G. 59a:<)igU=N]dtvw-O^[=N"'6ҋ֊wvi7fMk[Qꥵ,:O_o\Oepׇo=s=^^}0`p1DŽ23N^&[ĉuE2RK.$<1ꫯ^xo_w%<#q"v7:wO\s0px;w}@u=\zպO {アxO$ە1:*߭[tPc=6ƥ6Hun(}ZOVhiQ,W#+-OV{diW^y%]zj~;/]C;^^xa$=7tSU:uzJ۱%cg#vuoƱ]Ən҉\juֿOvmSO=UՏZRO/Z+aРA裏nx)_}nImlX~7s }%] mr=ߥMnWVؽFul<R5o_ fƿ}f&7_K[oK}ѷ_|1=py?Nx]k?5!n$~}7455C9$OGGsTg+gX4O8ʄAFNg 1B#'|2p_9*2B뮻[n%@~4'9m #@@CWزI0JoʦuUZ˧Dq};vl A?cU>MEܻK$!Vs&¤V8vPvqJO6R9G;8tN}~cM]駟cc%a@D)џSvk䑺N;UAZ/i=W'+eiY7DA ?Od.m˴}}ѱ)ouCܽze~)(j}WJDo(sLJX \wu1=ܳA~{?ۧO*Wy"!sχ7AD}SQ۩rqWy~B Lj5rlL6h(u"xVUd_VS_IJ?z*ۑTEH:96qPL@7꽨F&L~xwU}l[^i^K7/}!I~zkGL'R;_wv]wq18vmD\j2 6lXR #E~7մE}zwy(לKs)+õtLIwEe,W~rEO]ѧDv!}<~_~ LY}f[Kz+Qi 4ypWGC{*ǚUS)kh84YQ?8#aHӰ("̧*]BxR&Vy >3`tV$tA(ĐWVJ V @Y#+2 O`t ܳυ'x"֗¤ECuB#u<wg^>Sx]t/5!8ѥp 1MmD] :$.P #+Ô2:FH/r-c\=zhV.V#c8ʦ&piWyB5s^S=V0A=Y4tD3P7y9WdWіYjU|XEh"_Jt: ߓG2Y"V{cxl4c]{Of+Ǫ\i^?YXUMMctvҧCܗu'BlӔCvۅ?z^) k-U~~iwEkkZ/E(.ʒ(:\ߧyˢ?B_|_MnweI\'([`n0PW?c9]*ў[:cϴ‹.}W6JjZmhSuiPÆqE N 6_v6Gg l>uRWv QꔼD1pZ~!~ɛ[hW_JדtI>#;tI%7;7q?1X,UAG>qwna숚 Q:7rQ ;/ceIQywUVx91xr| 'D iZխ- &8g3 yV5Pj/L9p6q:'‚3ǀn!>}c!ƞ|R^B$lzΠ#Gy uH^>@qL[:j2M|̀pc{pZrj?FZFvh{ePϩ=%QKNm/i=Q d5l!?hQW]%OǴ~m?SO3Xߎ雔<0oC: a$m'1:+mҿ bt3gZLut4XV IY>FG-nϷr609a +ے8$'or9+rҭXG!9CAqiƏT'?#1U=%5%-\m՚MP ?LQYr%KΖXb؞<pZjbORd>N3^5sr>z/'z~it_NPڝq?E&lRl, HMN?1܇ag8t˨ehLB &B2 "WXWvlRj\e//vბG||<M #E}e܈$+mN;VW OqO5Ah'ڀxq{3) Hj&[xk ԧPRo|=R6ޛf*"8^Ahd: BT4ӆ1N++za V4 3kA' =(iE}8 `䙴-"R :0Y2J8大GFkfG # ju"#9mI2m8]t /B8~8˟@K:4/~v4Pp:.ggQ-00(k&tf0C\x8+ fc>1QN*t=1)Q?w1J':`<™ӶD@п<^PF&v"ڑ4R׷_|":3tZyt<~ P>cGLh)&3Z!duI;ݑ@>>VS=g–BcҌsJ[(l3jt']7~\ z5%Zҋ1#8ZO-`&fv]h*>o~I=s/c6i19Vȴ3F+<66UTu=ȷ?MJR'E gpTwIp\K}8"uC+Y 1 &b7+3 NPhSG}E?_D_2Z̤Z>~&~(uJ]oQ)u5_n?bCh.T'wxÛs0򮭤RBXN4!ԧL}l/c^}OUOPFHTdz+GtFB*Hb0@hA0sJ Оsh3|4i>) [0H-:zv/~d^N@S=@ҥо<}bY!V= @ӈCz+V !'~BʰR8u%gs}0*nG!]NQ>D:q0Ƞw_FDuѤCԘKe]YA@ȋEm@KQ8 g^θlVC$jB?n4~e $YJMCSҎmoJ`ŭЦm (l]iaԪIʛS"olo~ũ mY;A`5O>Ǭ18Kz>^0F|"N48nS.6Y_m=cu,'PeT=J ]q+eqK!Mzq $gt# 'Cz6E/llK'$@?K'ldi|u]oqX?bz&)a؆ϸV>U'ϩ+?;%Ӽ/Q/QamKu %6L&6S]TE6tEj";˸I8|_\?wp^ӏL3ZIWϥ68}zeOWlgmU_ʽ| (&3ueTv3& |9> :yQl:~8YAşrSTW'A4o)Z!T>e38Ͼ4)+/3i}uXBbԮt2^cpafʥ㧯qM)F:Zϒ0FtrYAfurI$e!t ʁe6\q8ker3;x({:!='qS. V-vEPԛ0,|e z;޾Hԙf؇IߊsEt $PoGh/9 ,1ʭBA?Eܣݣ9=~-[7wQę>7|R.1P00+z-Mu -?ʁ1dbgr]Qc4(}IR$ /zQoi@r0ƤNI7_/c :B:Ke5<-NF(g?;wܱ^C_ 7WڇM_嘲i5r9M@cT}O;-'~`OFf0N +6=iGx_!bmu-eܮ*=yA LwL*RfOk&qzWYx#O5m'=wG"GYN#ZF'=CDܴތLˮWTw+z>.s@W.7ZѤ_p?UDN_KnU#TF背j^qaOt&**[KG~79!|~_9Ch_ҡm?٥|}MqYX*W4~[FZc:Hߎ1z.vNSoiSUyVՊkv)xjjAu\C_ti]|[jeyLiR1z~=f ޯg-cҷc~ܧ/Lx[҆4>cmOEޤ/Є0HZ!T-[:zzy¤ t^yGuIqo)mO#T>òNW}$^q|MTMNIGoX-gV߾Jۗ|-y~ 4tuoJNg[I/GFC5hI7i]Ƈc8?I UZ|Lfj_ťtRD[GJ'uU[pi>hSi^󾜶QnziLK WZ(ܖ>iQE)M!nV3x96WdWӼv~Ȟ)TeTy\=km$GW#=n$lq`'m~鶤ҋ>)ZoigF}9vD@qFlrYFe>\ZS eczzuGois}q/|aM_!b )c8UqVg0ԩn!lQz~bR%~4r2tDž~#[F P&=3T4\<ޖMSy* gN f.8qM7Ν; xKtGsԨQ_x8"}*+M_LWjsz$>t^:J:oF8cc>ڢ> K7ܪmd(('P]uQQϻuVZPO5n<#5pIZw#c@YsLS_/shۢ6Oyפ?mQx"'Z3ƷTGZjcMY{5omq|upvOop!=#<зox%׿UV夫EuLD9DD)bR,8I 59ܯtYsݣOweO:5**! θD}4wl 2$jx_2իZ)FTj4͵2]a>Xd]w rKꫯby^/XEvOi)ZyJu+mOtetD~x޽#8'|ߌ_xiQ;OҶU]z[o&9GzhzmH(iW_u?^ӧOS[F j/kOK v s?pe}Ru3lVE:Y/۬,%=\?ɟ3Ci\EZjl\2,iBQM}*ұ| .7\Q^+٥|"mxse,mݪRVޟ؞|7|3z1mƨwܱS/_y_&ɜvکZ&-/yu@ᥫ*'?p7XzE?YM&=H7IAzU6c[FaKVҟ;#W^QlK'g}V}Sm4jcI"F딼k"^GOꪸEZ>M=/wގ|2~S 3⭷ kFTD)9 :GR5tϞ=W\bͶC<+2B?K/ŊacW^y%VN-m+FƵAn!:5 NG!?8#8GD~1=lذ8sOx<+BZ#̲.K~N$vڵz_~hH!O=Tۼ uJ]]s5QW NQ{7u\t'x"\~Qi۔0ڜ0܇stQ:2|)ҝ|@ ^ڼVd˃:(y>/m1Bi~4-Xb mӄ^нZ*GA}]m9IzT;c,u~oYr^N-u89##=ý W\~ECmAoNtaҾBˆeY&O>i[-G9& 9#.,Wm@▮RjC>?4lDOfȑ1~s˱qud@`-NDžtd<خI߳mɌ~KxYZq9ˆ#fEF>얍ScUt2PM3i6;C67wڟOđu)ee4ӶI9S'm`ʼ~I?VT7'tGĕ;!QwWu ]W9/X}6tz XX۳"iexrES RF&blD(?:?Azֳ-=)rb ڷȖ4#Eq4bceWgk_|ԞWOdS[jSt>L_/1<ՇԮQ䏾,nBH8.=H}ԗ% n).ҩ2{\+5PLI 8o4{c SFFijM!3T6[&N:W_}8Ca>ß8l1 A,1/twiAV8f?ϰ袋rnm8cbCC1G?!i<ʀ( 6:?$^dEr3 t >#RZ(qƘSGXjՁZ@'|Rй_G馛>8G=E]g8a~nFA8sb}tǎfCfy81ƒzNt`Ըvׄ-2!b|~_ļnf1te/4W_uuUCY5$NVX!D,m{:1nu+$?{Qh1uǹ{7=S'1Δ6Xy /%_?UXqke'qRo;)TNG'Σ YŖ?GcoT oc.axi3ό'='~Ҙ1cm}cKt<:G#<2iGq;5Uewmm'vX{c1PeŁg/b+1Ϫ wf? Pt%nSCqIo0BE1`'!b_b%b[p>mq'V7XOWgqF3>ީS/"ĩ.?39r&vaf~g8nk~zXNUO_=qc+VZiqc%C_HN8!{oG/±Gj=iZe䘾(Ygj¶XKǶ~.hT~!LuZkzh IB #/ď>b7#8vYSW=+c/aS 6o~qe[O~&9ǐxv)_EGe9:HP1|=35S)k"zE$m/H}('l%a}R믿~ܥ $! 1N;4hڐ|otqNm82p^)NaKu͹\O)Ac9C1D9pphn2Iy0T&J}܊N;FeH/ڡb3K(8 0((q6 呢/#]4:+"t0})ҕ/UD\#s<<ښ0Ȑ5\3{UW|UGJA`"M=rnhC3uN\ԡR&0}nz_feqFHzHl# &6pV0ʬ U+E iSK}2F9Pam%^kO]c`oI:=uC;C"hW=A]N.L̢8;}:`PU6O;om@qڪ+#_ZkUݮĬ4y&JOH!#QHɄ.(;5 N 3zĆf~3!z*c1N+EiP\?ēbz tx7dESBrP8=J:Ǵ v`F\$/ⱽhkgmasL^V*(ǦHOgEkLڰ[nGMI] ;C]b3ҡ(5ٕl#m 8+<1_eUK3cADY$ԅ洏o&Q).8)$8_l>40)n&fol v ^3PM\Lddžo+0CYI/_dOSڷO}3S(^l?zgII8A-t1|_ӱ%בzqX#t#:76Y- `O~T [B}yI#ZQ74AM%7|:6="aEaN}tsύm=4?ֹN. s!E114P"'ؼ.f0<HZAL,i(si4F BԈ3+CC"}tj  tfXYMEN3eG!j}t-"L7?C(cyb_AD1LS9ȗ!%B 7%,qPN<-2{E:RI|fKq€=3PP2t~s4QRG?uf)/:c|T~ 9` C^٩O2F0'ͰBH/8s,+78{bm`@#!zF/%^gY4EBQj*ߵCjż6y`ն:0`66&i_aRkBi/+m_2z<6G=֔Y;TU}^vo[KW>H̺2L$Sה8>_8a4.N0u[/L<+afl#sN;x!|"%T3_VZb&B|sL|8At L_h>7mI}\2~Fxƽ>p18GwT.b ĄxXN/&u%#7zm曉RT6uD!tй?e2.zk4iP;OBlg+B-ǖht|#(Gj `G봟mB3lcM&.RD!R_ _!~Jcɻƃqbq<@z{&wx5K(-) m$Jځ9mZc<[')v&[rG-+?q"wF=7!_/h~¸MHq{?HC51FWJ`!} i0L~GG7;}Q2%mO!!m^6nV7~ZtULh,$.Tl|t8Ib'#Ht4H8jX@mGЧFodR=aS{чkΘtC;h#nB8c!ߚ1qpv>(v.PiG1}6L[I~K)Wٿ.W<-"%tt/=B('LQ*2J'13Pc48fTqӠ1"˯~b:&ÚibD[0yQC `gBS&>. zBʢ<.MiS_(  H%^1įr6f9 &tV`]$+0 J^ypFM[b ?Dc7OߊS 1-Oli"䁼o%olYwucLJ<@_Q#]Y␗@Gϋt-zclrL>hp8a~BL1F KGG[qfx^!G=iVc4ߊhu-WI`+zӠ=_79'?ؼAtF҉?*9AmU6X?fqj^j!>_wu3=lIHi709\yg%:e3-a:V$'$ڧ'J =O8Ec qk髈qWTzVNcVtF?IM˞?:/ڍ-Vۨ@iKe41}}S~U7|Y5q>1oWnyzVǔ</ڦIH?@k|CJmO7[OZJ[=~2Pb/BU"ے|mgrimaE1}FGTGjQJ_Lt^OO.K>~qM@- NIJt`XIG鯹>HoI1B[QnF&gDUo6>Vt0 /o)WkfP:uK&`(~|׫pya@g{9ƀ09208̒h#S+Mfh}4010~MZ zSqӁq5;FglA9(![Q V(S|}34:}߄gf18S3py}o êyrttaF9L1( d?:{!PO]?أUrXM, ,荱o:ArruƁvG#/$='0pyM+NRoL>8cCQtmOq SIW2Ӷ o ~+iYRJ 1 2"/}8<}ڀ02D{jҤ.8p`~z,zꗴߦ: ,zF7lăM/yA2c;E詯ͥLJGIra3 G)'Np(/Jd:{ ]+諶OM7af٢>)[G{sN~ lVke/IKCt}?:h8ʟ-W?mM}Syzv Bߙ[!>Uq>um|V"6fVkjkE]Rh;v~Vo"BĨ{=gO\E!m}4-Ń~mDqC4N:G ұ#zq:hѶW[&[+3&(}9 A\g[O5 fF&atE'TxVZ%CVu[+*S#c^"بQƪ/HolTZwUp6ߦ~SBhGp_`Ah;H-[e-qi`[j26+5Q=o{ae_7Ko/ @^sa0 }fmaݑN:E~ﬨlSjkh?^KK&oWNi}Myw[T Q鷑m4\͡EE#n{Fey M"o߽+k")&&-Ճ{v76~iu~32;?`U1嬨ߖgjLiU"LۨH&X嵑v(ע.+vcQLW݋+֍O۴e-MP3)۵Y!9/5^^ eYk6V٧ɓ 0EZEv]Һ.ԟ45kه\rz^TILGFFOnFi\Ec^򞏯rٙsU?mtl>Eiۨm)rY[#Tߋ̞WQ?^5LǍ[yvV7S?%-w-0I嶵-\AӇ!ӊ @[sYEa%p!_k晸,YRTt$SQZeԪ8 :!;[kۨ \V9~תFuWQPg-u_Yj_r=cߩUF^-ZYVI}E[T\}hK6'SK$2T7Zֳq精Wxv^k SJgqY-iFڦ/i4Zy\ Z]+Ř׈_J *ʈӖէF,R?1QgePϞ45e:_DJYcE ٬QRVpiIg>4Cj;V\'.2wl^xBsl'ڪmEFo4Qko s!ZTE_K{2ebi2;V:,V?M۠-^쁙"Էuۧxbۢ4xx0{6>*lQ!x,!o9p-_ OzEm+ZKRqWrtQ>DD9m-nyʖm-bX,Ni0l*ۖVo_|{ߋkEC`2xk(mkJ郩z"|'[x#I$_2-rs<ʦ_Ax=?TEpidMoL?{Sze[,bX,yBx[+^H.^o+dy o#ky^Lx?!)VLuW\~E7lQV+!ɔJoOx=^Gͫ|ysꍎiΖsa"C'.By/}rWߪ.d%ERX,bX,8!G!fd%m@DcE!|V[-~I7$r8~6ɊpGCC,X$Ǭb~LȔdjpE胥#ȱʤZ(ׇvY-JvOგ2D=}T[%gyf<#2}zܹsR+|߈䌶si]Թo>XK|_*~ԛgQ]c=&E?bX,B+B^{H=HXYbukvi{c+&+T98AVYeJƶG> ᣍ&W\1!Uڵ袋 ¿o<]A^uKqA nWVψ/]u?(+2>" +9éIu 9;cb*_~ӟ4:-eb0uԸ߬:Б>x~g񃚪sn:lhŗ_UC~ahպKƊ-o~X,bX:ؖQHm7ݻwGuTk]ڸ*GB99S+Zl\fe"AD |&`2hVXc '片RHѾwa^+^ TrBȶwk^tAUB!t/! Ċ3~('(yBO 2TGYV6!; "Fmc*$:J"K u_lŚߤA^xYv%\2ê07Z,bX,2JBv bn饗oVxqWg-ą{!9#GlB(vM#m?e[$+O K87a % $/ta&g@~¦5vK!h.FJ^xƒZFCgB A?Vx)T@ZM4GcU rᄰ} s oj<$-/V !l%՛MGVxЧ,XV9!l78^<uNHq]w G= #$Zk >b|S K= 9dti'r^B??~HnӷZ,bX,yxhm=#I>am>}0^,уi cͼ~~RK}PqS2IK+>l1V~ז2P+ з?\bޖlbX,K "a7 Xqr4gꤧ0y\]TZy,uUB]yCV*bK.q&5ɓfHn꧰,&Oj !oKbX,/J)x:֪VYr)fX,bX,&+lb,bX,bZ,bX,ńbX,bX,&bX,b1!X,bX, bX,bXL-bX,ńbX,bX,&bX,b1!X,bX, bX,bXL-bX,bBhX,bX,BbX,bZ,bX,ńbX,bX,&bX,b1!l&NxwbX,b(2e1cƄ#GQFY,bX,KѣG'v\B?~|x'B>}B޽C,bX,e ۷ox#zäI:!dtرa0 0 0 #aԩaIY!R ૯X,bX,y^/œaaattxB-"aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaa&&aaaZ 0 0 0!4!4 0 0 0!4!4 0 0 0!4!4 0 0 0!4!4 0 0 0!4!4 0 0 0!4!4 0 0 0!4!4 0 0 0!4!4 0 0 0!4!4 0 0 0!4!4 0 0 0!4!4 0:&_F0 0LM 0yȟHaaa z):uj9bdק_9W0 0LM 0ߐC•]_{дBShZzT95ka&&a\H?p?W%M2,:bѰp95-.:xIaaBhBhadqfe6 M'7e-V|gŰKN:%-cq0p &a a1AM2%,BӭMa)%/XX|aTq0m%4N0 0!4!4 0;)ϑK JXlL ,"ýA\aaBhBhash{s= Mk5&T%zu`B ϽA\i܆aaBhBhaSs-w M+ N^&,>dPR{e0 0!4!4 09ÇXbĴƴ NҙJ5b؈fia aю-=lRɠ8nqia a M5YuEmA\z aaBhBhaWh;gBS +MD+ !qgaaaZ~WtFep-{hFǸ WaaBhBhaWB^i린fBhڪ)~wX)jaaBhBhaM<%~߇.:MԺUBx4ShnS믿>tM 0 BB0 ണO MWUV Ъg)8N|R?[u^}&a an[|pN-{h%,p/q 6?$2) L 0 BB0 =.v M6ǯ6RHrO.My]b\L&'w)9ʡaaBhBha2[oSO?5UCMaKe,[]2fwa=vqؙa&&aF;!GpqDž=*lipSX~a)+%'/x{(skiZrϖ^?ь#v&a a1 gܹsKvXдZui ?'a Es\# a{HɡaaBhBha32xG.]4#^S?ލ[wд\S95¤d8Rhrhaa z}^xC(H÷a&&aF; )>ɏ Mcp^Ka[BC0 ÄЄ0 Ød0 =itSka&&a !aa RhrhaBhBha !0 ÄЄ0 0ژ ,!0 ÄЄ0 0l2pNBC0 BB0 dda{ &ڪMoop_Y߬w-}S[WsRϊ}n/ a1$iUmז\Y"Ve(#ೂrmia&3MۚGRhrhJRV/ZӧO0aBO,/sx7¨J8xo^z_O81^K׵{/ of [~=z _{O)S4Kl?u/2|g3OY~t>p Fz}e6Mvb]E+G\|A &&al$gRRrxK[Lg^;x'ó>;zri|>駟W^9am-\=ݻwn+Jz'vyc֕~CZ]tpVڹrYf*A+*GRG}8(o}8㫤0O=bْ7xc:thVيfYJ|'a5תƝwYZXmˆ#LM 0 % Qr)G5<wWw^jxV0lذx/ǬLIߎ?#z*#\>/s]w7#o?x±HXO|x5E] c-r߬B~vX\c9&:ۀ|PIzjV8y;蠃1DpemKSDw 棏>> ,^*nyQjkFG+`(qŴ`w={mz^a-3fL5Ia1^uWW$MM 0 6#Mi-\880KPGj`kp9 6 :6,~yfc=SqӎvV$?e}WT'dP+z $~ek2+݀ոLM 0 6#ͤ9_VlchNvu3hDVq!ڊxTW rY|ecH{ْǖTҢ_['Yف 40,7D "\xq[iZ 2j"->/FQ>R!"nSAfQ2%)@d@-ioCt i)O)pAmj+1$+bGl6tHF +ɬwI' ۭbqaYI=c9tUJ7~3mE4/:XEg4c7'ԙJy˨ aa2dpvy9d4UXm+;_M[ӳXhVn䑗VpiڪC x V UCVXa RˊZ%0})<;< L%ZD)q!Gt>"6NV$0ӷ*-‰:6L=FBj+ZuX@BDnI)JSVңҙm_|9-^jK&"4Zc6D}{ߋ\<<_:M;vхUI'o "BIxV!!zi"Uh>d$/,wyt[FM 0 6'Kh;}g5'-tU5r|$VDpwgGйsZkUY#MV}XA6Gr3c$ADrxG|m;Cܢ΋x 'Px/ĆgȽީϳM cEJ!#c=ϑ#Gkl{<|jZBxG;ڥv P[<_I>Q"@i9!C>h '`>xjXcjܒ6uI$ϐVtdS|JGIm;S̻V'MI':0vdBB0 dӝФ(<)ؖosuHik!{[*-6\~Uh. a[ Z!]!7bn!R7=ۗ HC|SNoR[.I◼܄A ܏iW=yH魬OCv[Z=)4'ҟ4n2?mSV2!{תLB@%ճNMUu.=eg al$sA'f&ζ'Yg?$ٖ-l9y+~⃜ĢrH4-_/-lgӲE[BB0 H!4)4>/sV^\+Kej43Shz[Q8sjrkʻ aa2. I1/wV3'Ҝyęas5ӄФ0 0!4!4 0Cd=BB0 ÄЄ0 dpBM 0 BB0 L!4)4 0LM 0Lg3!4)4 0!4!4!4 0Ф0 ÄЄЄ0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 dBB0 BB0 ÓLM 0LM 0L;4Ф0 ÄЄ0 dÒABB0 BB0 JMM 0LM 0L;(4!4)4 0!4!4 0dЄФ0 ÄЄ0 dABB0 BB0 JMM 0LM 0L;0LM 0LM 0LMMM 0LM 0LMMM 0LM 0LM B0 BB0 ABBB0 BB0 ABBB0 BB0 ABBB0 BB0 ABBB0 BB0 ABBB0 BB0 ABBB0 BB0 ABB0 ÄЄ0 dЄФ0 0!4!4 04!4)4 0LM 0LMM 0LMM 0LMM 0LM 0LMMM 0LM 0&&&-#a&&assa44!숤Qg7 ÄЄ0 cAr}@oG8G<}T 0LM 0ڝ}B޽K7om/K8c¥^j2hB8OBtG7dгg ;!}Es0 ÄЄШ?g.:J+.!"󋳋^q5!I!tHԇS]tQc0}2 0!4!4vc99ƳԃxYg>8xrrs_~q{L*bBhB8/BGZ& BߢEE\&&}Ya'O_|15s :q1}1im`=#s֥4?8t5ꫯ^kĠwޞ_Cm5ow9{aRF \k / 'pBRG7[ԉ>micxGhX_ۼ?V}^:83}E;Q6J*Οr)%´DG ̽ a;DA"ˉfPTitٚ=#΢0A F6'hiv #Gi|3c`=BBH8^/Nypy煡C ҥ=-Y9qׇ_?߯UCtg}vii溠ԁQʿ4}7yI㦞h^ep-s7oѣ 3yt=:oᘵtu0uvǫJ/)b34Ml±1kNxk'M[j_s9>9] ~0l!I0aB8qb3_4Z6vG)%!vm2Z?Le@WZky|NA`ߏ~js?+V!waܸqzwy睅$Ⱦ@|By {nq0(N;+b ^ǽV[K.$w}Cl 8n:x:of kQYuUC.Y_|1n1[ou |WA2SHesN4W\lB[Nb!_nmwQ~:`xqlͪmP6?#QwoX%l~RړOVi1cTg3iG !ϐ!Cb<-NЖi+]9ahHYM{ld֣:*~{WWHFm<̪iqmUxM7Wӕ?, Y _zh!!Y?@#SYU իWeWPU@ad?O?D2Ɓ%?cz,# s && !aOƭÒ_2 00v8@8Xwu 07_iF˥]FY,c:ٖq2E曫yĘ^^,eJRK-VY-)#_ʄaxG 6 ,8 pߢ. #曇'x"v1/>SQQ,u@<ڞRcB c 5䳪 . 9_qVU9w'Wۓv q^N{.:"4۾<3(2X``)/<}Ky J[o@c猭832pV>ՕU?NFh[m]s7>熶i/B☲F]sook 8:8xЁC '~R95QwE=3"5}>H_lO[aՊyeq"ib2gX@uϸC],U;8  7ձ㷿m//E16Ycc!RH;&Ȼu:uB$0>B -T}Ad_1_G_jF]Yߓ{IV |]_Єp" fQR`0VȩZ5rJA_%Z̤hd Cp0xcZm+c+B40tQ#Gg ABA9޺U@10=ŌgCԔT[chp'fr0 `Ub eNz̧$oj|0mEisfa)qjjOf^'ee|8X " ښnk\vf$o3kN;XvG{[C!4;xf!3̬MٱDzdьvs&&&!ַq 1,vۯl?c;},@UL v =8yC|/O‹/8o$`F|N!8Hcu j6iWЙz*>"O)ɟ̷ZVms-B={v@_|By w>Y=ߺa` YPxᄉӶ0E"nfb9~4mƆY:<8Zj ]jP}ԩSP^aeKHE^)['DeLقB PݦI:{~9 V¸]E82S;u=ܣE]^y;SމUVYa:;GLh`@}i! 'څ=ksVJm^Siδ?#l=az2 ><9ML)z%`p/[01&rTgf?T:֛tpQoQo۷3vkӖK5cPn,p`˷W_=Y`#}츞+ ?u8h`|fc%6qhL%',~aѤ9|1v愩VsEPY:֧_&rĸcLxcxږgTm W}_y0!4!NHak%1̮Af zsa!XNmaU5a7gUx &f_2W^^!W-# * #3SlY^v2+m){o5֨i :ն AaboW} V3@Jgqd6Vƕb1̈jBʶ}=vc7s``Z[5k,Ͷ af '3^@C]0 4qtzWܺ\s~Kef/:Ke|%+pm:?+O1m5v0rȸ>qV9aTI5 a䉰܃ -̀c0uN ` G=Mh:רSZmZxӍ7Y;B1W[0؞n!x#U/./uBtAWO[3p3Iӥh3=K1 W]" rYaڞ{kzξҮ[ڞV0^}q!}StVV;g'g'ىytGD)M:eUƏWr=9\S8q<ؑbtG>31 m|)6m0q20Ƨ|{&?_.pCꏥ[r!TieRS N`/0%_ЄMZ޷ժeffQj\nF>u@=қmeuT7Ɩ--)0zx /ܗ]~YCyjo5K6Th32\6XOgq@[~a90}ec\ٽ-+[2CGO*Kq-_%\t=a o_`LM[OaBhBhѮ`[~u⛞|Nj)ᥗ^*'NuދA›oÖ!/i=DÆ ƭ|AիW kV3⭸ΪMˉyV[m0bBBB0 ;ɳ.B8Zz-sx t=됧;#?{q{9~mr'Gy$naw _}Xfeno~3:RE8 q7hjj Gydk.p-"T|N:<^{1V-S_=T+C9B aEb>pgF\g}(?uD\ni,~bJ]u4 +*!J]i*-iNqLm1?7_$[omG _kbv9##)<[;/H~еkx A@HOi.l\pc{SϞ=c^:ܒC]wp'F7~g2N;3AԇȪ a1H&s+,#[n ~Ko)r 2HBuYպ@O++ B/wqEGE%^{BG[.(VO#COzl{g©ZnW{u#\#_Zу^y{F›Y/r{.nCnTBW]/Ye[gvBVv}f[Hj)–o$,u H zWא!C"1mtyvQu-Tg}b<0LD}8HuA<뜈$KUVY+JVR>!"-dV\_Y=3!:_ePv]vBvM6nus ]hA!j̢6<6C:fmVKC$tZ].N8@[*NTW4^*BȪ%z!gH&=$"lE/_t]e]@'W{˨ a1I"s l) Lsctgx 5>駟WYszU9-VVPmĩqEǬ"[mUՉ@XM~I `Ր~@x >lqM o1xehWL@l DEr^*rDA$> wћ2ϖULz"iBpYS]"jVZmËqgY%=/aX}f/%I"; V^{*y:d4:蠸 m"JVJJ]2V X"g9x ْ;DE_`k"`evJ=r1S;[Q/d ųzLǛU.YYgSGg{na$e ?Z児A}?f򀭛5xFPضXcjL)%ZD7xvC?TFLK;H,Rjɓ" &3rlBhBhBhaVRքA t (} _+/zN!)z(͒)ѴBVv6@VHDʓtJ:Ex3'/JdG /!964δ?Ê(OSziem 6y>@ A`˯.ڍgB!{ 6z>Hu)}+yHϵKҥ7a%!lod$pm[eō.|6Y-UَZ!O|>땭V^[ZّyFm$-gWSb Inۂ-ɻaBhBhaRVAS#mÙKtV~#e볢2ڧ^+Lz^Z"znBhBha4)l B8ɠI1HlDmęa4)YB8ȠIa aaR8pf&&a&&aцpvA@0 BB0 ØE5pVA@0 BB0 Ø pVA@0 ÄЄ0 Øͤ/}H Mcp^UdxaaF[œ>ixSs"Vd$0 0!4!4 00)S?nư޺녦嚢p9&%m!&a a1IaΝå^H_дzvi ?'a Es\# a{2hhaawqc [EhZBn ˏ]>8eŰ%o/cq0%22hhaaza߮h KX:,;eٰ⣧˘ߕk!,p/qd$0 0!4!4 0R.iצC bFka=M4.uq}38@0 ÄЄ0 hdpMaAKN;UWJXަ7Æa&&a܂ӎ:-4]VBC+E+OY>qRB-a&&aF{ؔS}Xxqg|N03R8ShnS믿nhaa|'{+Wh9̄8j mhaav{h:)|駑![,bX,˼.?"_%SbX,b1bD99ir%{YbX,bt$ i>6 !bX,dNsvA-bX,bBhX,bX,BbX,bZ,bX,ńbX,bX,&bX,b1!X,bX, bX,bXL-bX,bBhX,bX,BbX,bZ,bX, +bX,bXL-bX,bBhX,bX,BbX,bZ,bX,ńbX,bX,&bX,b1!X,bX, bX,bXL-bX,bBhX,bX,BbX,bZ,bX,ńbX,bX,&bX,b1!X,#&Mr=X,s l1!lߝjo70̍؆pNRV7SL ^Cqsr^ȬL M pVۦzXOzw*ֶ;sbB8DŽG5:Z ʎX/~?8wΝBۥ:voD aV9e?> g`Vlg>qƅ7|0aL?5BH JvO;Gge=M81Jgr{h>̪:l$~+OXAt?)stmذaaРAdDkm(42ӈm"aaԨQ1ύopdGpϽ{'}L3h*pwF]SO=ƎlrL/75&%}UWo=[-Tvfȑ;,rE}ѹeg~>׿u WԖE[}'v)?pŲϯ#_N)4/ENYwm^xᅪs<~u}MńK/;V.W_}uu!o`P?OB׮]ç~Z5<8y"k^ :=\ӌqQE__yҹt裏A7d踡/3ό^3@h#j~G^2zn5zrk;/Ǻ9tG]veqr6!|'NXN>R{LL>i;oMZkcwu* UJmbBXQcuMuqqu։y8@1|Yg#<21c8^5a,5m8w~xÀbGӲC'pBD‚w:xG΃>8'ϔΝ='$ /Ǩ{Gqd8묳N2PmY {{VP?3<#Qg8G3Cns9Roy?uV8r7Y  mB9E&phW{'xfCrhO3!ݡ]v@ܗ[nЫW{tjiO{ضt%{.$}5vMQgN<ĨC:?NK|ZYTY(kV\qŸwq\zHi! a拫#4/BxꤓNHVTi}ц'~1~Ԗ.#"X~qPzAq>yRa AϨCƐs=7m1/OM7Wo?A\ڈ+ Gr0zfrI젃yzWû;øwmEBġW$^qmw3%^ T!؄nq N1 r)a'ClxXzE]en2=F'tz'=6:pWێ6dy&p8f_A}M?_h1!l!I 8@e/V3\ Mfci+qq`Q#A MO=؆ 2׿Qb-+3 L"?O1Nj,HuV]ۆ^J4Y18)>8tAOzsd9`9^ ae6TD33S' 6o~EuB:$ٺ1mMj=cH7MtjDLO?կo1ܣљo^zHbqRt!"UW]j c!{'W!~*doq|dr~xT2H?Ȼ&9W\lB︟ZN>d݄RJBt bk K}c ׶fx ;;WvLZw>m.3<6RkoeC:_3s8]0hBT~sŠ,j_=388\i¬H^=$g#<8C݄p&+(g5}b_+|rp~bgai3rppUepGpEp8>*K[HWiїKH!lN,6A{]H W_},:"r6plpunWf-bƚkBJXXfvJ3LBk_j_,<뭷^t!WkFt҈k3ِB&p̴kLH}XiEfӇH/ ⃬*yb$ VqMXVפ/ʽRKEa,#V4{҅q- U0v.i_Wa-5+HB7ѽTq|+rN,e~ҥM;DzB~Zg{=|<&{g싲<=mޚ4aI+8 mQCX&ӢWn(vCsL3_'?,& :j t3Iaeɿ/YJGC"FGeg$%\2^4o萇rHuƉ]ӊq$0L8|_ڂQ C!8;yMd/q 9n++YepIeNtk8\hm2@8} ; `"Hȴj6?mۙU i^vAp.XqVAYki!?ŭ]/Β6+ @S_j;%%i1C: DI(#;9HKߤBs*1C^B{CUwnt4 ϫ; ^k؈VZ) }Q۔B& Z!áZ'~'o6l357t3|{ߋ$1#+<7aamJf a4MlYa[+:.Ɋ|&s39}hbgTpZY }%cP3[袋k'۽^3qD#<߂Y//>-m Vmg3zu9uEMWv)Pl+bX3͐JN^]_֮(gEN*ujLz  1 LԮ8R' `b89SBpN+d;K~5B>bxK^("m=/}RgH-]8r/8$z%n;*zV9!yG&jc:©曛+E!OX}+{U8⤼?Sݢ7*}QGB*_Ž;[\ "~P.e+Z= 5z퍥gy >*V\@g]&Q)߾L|0zы 7FZ!cJ$i62"e}M^ g-&u5= tR=GEca<|mJ◿Ίbp ۫qMρ0SХ[ѹE 0ۏL.!-r5{-\Y1VqN:2m? V" }/"@ N /f>S! OD6v|D> #_>CZf[$&`U[5V+IK L7Ifep-hR$/8,N8+CGzz" 9BNk^.ó~)!M/w Ki:½z0&=.Maׁ+M*sG[H! cU C 8lKr4~^ܦi~eh{Zo-y|-vBR1}'U6 q,[KȟsM߼V|m!k챶V k&!F#v &U -&u`0NgÑ`'m 01Ĭ2oŶH38? zz0+9laGCyFşoAJ7((zBG_LM 8̺\ +311oVQ$^t5|2@T` BӷoIŒ Vΰ7 3^ԟ`{dxY96>8<ϧvņ4#"ߊo!UFڟG`sw?} ] _&/ C2EfpQRSOԥ//䝉v7Fs>Q [)> gK6N+W]FonUڏz|!,qA#Hm[>v?lЖTݑ>rYhS9}My^UV.D]SGeUL/"ߴ;uL1qRftCOk JM}Mo=F==˛ kiJ_B=;"lI'4iM}M?o1!lpuhY$w`$\w4߰f],;捾`0p,sjgOz}HB9a١\g5;HRgUTRAU)ɓ* hg$XDJjJ_SԮLdߤm}?l_ *k?/HԶ)iEeZEmV>i(L¼k9b S6Ia՘P#}A{EDeԘS+Τ jGNEߡTyXTu,͏~*J>/lDj4HSֈm"Lj+< -B3AﲰG8e*yWk]V\ N{mEkE:VuMmFɓf˅JkQSKڿlٵř+uKIދ֫ZvzaƝZzHݴŸWVOeyM4HQleflpqkynco=m2eZ,bX, +bX,bXL-bX,bBhX,bX,BbX,bZ,bX,ńbX,bX,&bX,b1!X,^yb))SVZ,BbWe̘1aԨQ2=:L8ѶbZ} qYfd'/bx,*/RxC>}&MTߎU¼3H#Z, ~yҨޞ2dre:QCiƆqƅ#GzEbT ofxw뒺I'> |'?}Qo"[$cptںlqK CK13=nP~Mm6 ybkџ8Mg?N3Fg+LUƼF-1o_;x;NIajRFڛ~7ã෦[o[~<8/VwגV¤IÖuRu"G,sSC dB5<~{{k6ac;([;3| ,˝c]0aBlSqgXlB#/8.]Gy$toZ QxQGۥ[nsΑ*|ZM87;+4oz_Z(\ˮw^.i>:=뭷^UG(+/өFSz}?ѣ_y{p@w'ר'er,m1xrZfƏ FOoWa릆}n$ty0|tIa4'm!zhl1"= 1+~C AyH :th8 kyzp{aÆi\7ywY~fP(o{)et BHop!=#37_&"ql_~B+8ei)J駟*ԯꜲI'/*qkpʳ^z?qR'|2@%{キZ6@zO/=9馛N;} 7>f+Oq~m>`NN#7M{Tׄω~Ǝ#˶#-TW&O#^Nrs:Swom>&&]Kkҕ4O^Si'U?qs[})okk[oIWS]v%f~D  aC1pSYƜ_p_ݮ<}#ӏ]Ưmp[> cǿ]iƟR?H?Cm"EJ`!{ǢNAD Ŭ3(d+g}vtbX hqsΉ>l$)i!:+`ĽJ+<N+Z=ִE_9{DŽu]7\s5qeՊ뮻.WYl #'VV'*%+)Mϕ'N𪼵GtrviSgkVtH׮Ȯe%[qй5X#ML_lgC!B|hW&~:_JJG}4Jg}]ӊ<vy酼A`vpXa('&`!h'a!$,liVLsz>+͐X^4@e[K'% 2,Sxn6K@h«|j; _MEl {UL&6`hoXl7BVENR2HzlDuHOc^S6sp=~ ^Ҥ>H8F묳N|2.3)}$=s7O=T<>:牃:m-(W6jRznK[Z7#ry'?<ƁH5S=tL< :DDv9O~>&5!,0 nR0B[?\[q+n^ӏ?pyX/WfޫtI;]:.N/qqjˇ:RB;flK ,P]5f AgzH~Cd9r-#c0vm`NܧrJv-sJ .HתzSnnLI4`v(vbbCFw@lĊ(ewff9:9{^{f=%w%{bV5RK-.)uiUVI?|u5mȇ)i^b羞EZo9} Ņoů48ܟk'r7&YLNpM\{l >@wpo׮]KYy$.2 k!ӟBN=zD]vsiZ!y'd֗(#e:# ylH|s`b,0@y@9E]y I# ʆ7hH2Ka*'Om۶e@B@lPڙMQn9 $s1q 0 8o"*4J;qT&Xc'\qmAOB@5yS_@*wa4m3 袋F8rFZyc> Gz'ܙ)t喋2З^:5ui0x' `Qc,e⠬]!#er`,|^} 8GxȜ@&wGK`eg\2AqfGD)sY_flf>qf2~5ΗO{T+S w| NL:>mqPw|٧!=uaEqCo=ueH)BaZkJr LG,!QH Js>7 LV(}( (iq!NH܏diDa;`4- a՘gfc擰y|HXIga;}A\&Y'g(j (k*`Fx,)o<O=Qژg0=);~6loN4MyP"]w]B,|[#/ 'WY@2ͤG (}x wFDAY@; FPpsE'}g -+u8"M?E]{C?鏟0>[ Timv<rMD bB BzbIC' PM?OXhCң_?S^ r=:8X63,ԙt"/i1;W௸c`h/!<ڡcaFlK&8&wcL`@%ϑ,ǴcȵqOoV ;d']ڗ I'AX.ԉѲvզBL0fz3ww߇ֽ2NݡCuG^ tLD.Ϙ.1|?=/;uab3ǀ0Sc#eaF׳ n(I&zFgjՃp2ߘy T|t2 CS5i0kG'E]sAWOV]OTxbӣ/O?+ݿR:i>S*{?is':@ȇH+UcXqIB8r8ssv6yB)bCAAd%)1iC7K\TpbE3_~ x䡙[ HCAkjm,Qn}! Iť E\d8cX>Ic( @np,rA1l0RY[m7.Ay-[p馛;}i迏/wC%7~#r%>'mB8ڕxIAɴ+p嗯._$xy h[޿6ZdE *03jgc?6[S ml9 @rp,@x^K~2DᝐcThWᲲ-B8;DttREBX@RG9 iс)A|||e8F]jՓqž+_9>mVl_Qwd|Z')(vb(P KE!_tⲣ$2Y,&/̐sИd =3AY[ֻ ҡmdFAńA4}ݚ5 ;m.甉JjFFh:ᇫkdd+ fNPte .8_2ȓőW\!ff(.-ȓbVvGeT|(I>f)6MpQ"Y sLBzx((rdք Jk@weC.o0rM(r~ vgJZ+840dmkWD@8yүa# i1Eu" hO99.Z8v)L:1EyT?BPyZɄu^h2!We𣵗[;_ӟ⥡w;*!dMoqxd0WrvaL>'ZCHc ȡ홠 ,k)}P׃ s'"P_7} uβ2%]e}%>c唜;dLan-(q>wd=#Gt>R.˔4y3f׷a=M:&>P^>MZ"ѯ .ґ4ʐ6AAC;NSʉ2`U?-%}iqID)S)dԮ)]Ƭ_l_Y3ǧNRojB^Wߧe7]Q+C%QQ}3ՀisRXh0pYKr8aïo2Xyp@AȍYSL#0X3S$Qn=X YGĀEyE&j4=i,0(m:fTUeʄ %"Aw +Q6 *꧙f5ϤHa!D@ cSd ZW@fQrV+ M EACyw% Bg" `C^[&4CC(ΘX[Dߌ.,|S<8%k"V!v7dX.pE p[FΔ2WP)?Bjr% d%/Im8.Xچ끏 >k)e؇/ȟt&mծ1?\kW,mvl"\wԄ~/zNnʼnM{!_!)3yN8Sy(\Mѧ(/OcV# rA%m{@No)v'rL+Yiƴd&SibUU=[h'b䔸\cyb=J{M:%uWbs˲aq)pܭ /%7.ZJ ć5%̂l(!^i1.3iy䲥7fy*d!N|O\@q]F̷(J'B QZ8~aCF>*ٴ֠+ i͞fLHK5!cB3*Ο=!-S׼''M*֯([V9Nn@ȇF e=C?S38̐W\Қހr8YH@c!E2X L}9 фm9z75K{g6wurkmFN`ud鼼?r)72=c+YNy鷞)]Iy=UH~>ˠ2iegtks,@Lz,U*b[gƩ}*ʬRlbɍ~߽\L}r 4xJ3?2Eˑu҆-yڹLQڵk|Jy/{sz]U7#f-cm!l"P(E(@"fa8ʚeb&X{T1K@)]̀(mb7?(;`Cp1l{zj( )8Rnoz` Ε]/V(k3![Qʗ(s:!mkBPVׯ\z4ɓ~sB> SYk F&=] _?,m 8jzBC#wU2X 5M@`^"3}@9w%&ek;wjMHɤa(71J( !.Z|3cM&Z=S3QC|w,+aHݲ&<3)D3V@)yLW1? Gge<aDP߲龸zVN8eaMRHe++g1Ni>[7*W7{'yZmՎSS<) [";,SX+~ﱖ V&eyՒɆd ܕi7Qm4%鲲`iTQHPIDgF RL Aq[7 )Xj>":oE39X(\ D(qZTZ5 E +`n ;7eiBX,}ĴZE!_󟂒]Q+*gNH+Vz+ĴjQ}LeӑG*7Sf@ceEz ~tx545EZ3 Zq[E2 a~6DK~qd9TrwY1uAxoU=CT|}xPԲfT]KXərE;ڬc l"#@֨rPI+.NP=T_a֑L+"<3uރ@hq,η Y>Z-Z dE":j0ʦ` "e Eeb&#EtD1#.W=E X[AZ8~CXE&!O&sP޵v kFҳ+EC L;i-Zn{Ĵ;퉩OLsW+k͏OL+UQ8?

    ;ղSƔ] womgy(m1-{ΪȀ0:Qc;ⱍ/3(޿/;! +%X?nFErC:b!+-55(RqCg;^Ek=\׿uXt> q dZ|Ò6'%Hpp"N e|J=VQxxR_?d31tRJ'N(UCRo2y;@;cY❔q1iͨ5#Qt2(@J%2 +ۻMU|F<<::+ۓ /&+mK}Ϭ?k* $6&a].n ASf1J{eVZ.z\Du,Smj%E29X31EZ*&OY*Yb}U 0Ye+oi>wJptwMGvi,Cpg]5ZRӐ MqA!\3[{:4Eujߗigz&YÏYC}}e(un[*s,zQ %8HD(_qyHvtΛvҪu\Xs(kPdY6tJK?;B/b֚ Jq(i0O\n::Ʌ.EN*/ 9ւ251utJm*k(sRٵIϯz*~]F*ϏJiSZQ8Vvs"|q2i[͸ /iΨ]F-.t̬ +Y! 9-0H[Nk:g4==knݬM9vBM odebݙBy|Re}#$Ly\֦:F5=im?@e]&*#>~a\a5<(q ѫt1!K.6\ͦ gX]vB-k|Z27Ѥ|$8y5LK>(,ccZW-Xϯc\)"fU\+:V"W*\Sy V ӘjoJZё`#Wcˁ`VY#E촉Ū6NV7==ݟ*kPQʗc[SpW/Ӎ*V9Ry~\J U-[̱O iu `|Z4g/RCM&Hh @ݔMpg1xVL29?L-1YD ' vG&VKc|? ;7^ ep" $$1RIO&^0LP$Ԡ.Pֱ1aژh7ڏttGy1)WD77PmI>L 3q jB m ΀&pY&21@o)ȃE#ښ5jڂxhpUX8Zu\H^.OJ>'%^%X%@۳ay&yEn(VryEN4r/xK[h m&j"Jyxԃ$˒S i;7Ї=!OLX P둛R&^~,_;Uf7PʦƏ`3?+Zqs./+KNZQ(GV\*`T,0s}>WYz6G(꿫(U,]q㛣sE`¦B̕As@n2Z78i|ܥeuMaM9ĕW^αbai@m۶mXt^{ &]K(Gag]@Qu9?QBG8*( 6 W|0sP_A%Pw)'isW3g18`2(@%$%> kYFqB(YcG> 8V !.XD9PZ)p=\sŤ*%6 ,@ &aA1h|WUdX.arב&pVhC6 _MƌV8(/kp#srX(7%^)P~eˁ aCb3m;P  壽fNGƀfiUtBhk,rڂ4F[<5-!i MH`-'5Qv*yys|K&*5E_|ū࣎:*F;h| X Y V]@VKdk@U}xf4 ^uUC+Y=DqvqǨ+9iy`"ot iZi fp&1⮵ѩfrzkIr:eЀd2Mo@h5V2,?Bi$3(+B@,ܑ#-e+`s3VBmƂ2S٤DzE|"&K{ZtK~(+ ϿbM! 6-6(iUqač !CnVRܧ=ɏ5v!m%PO +K7, +:!C,rm0SJXQ޿ܒICSX9.XA:P6hUxIԍs=N) !nԍ 0:eXr%l9Tro,ݦ\.91o+.]iY5A2%.Z]H=vՆ} .|M".xPx9>3j&iJSCR]+L?tD/+F+91iŤi9 LebT2.6eAkőGJ(.}XrpUM7-J51\ģVӤUƀ]Z> 6q=`+!JA] E (J 7> 6\3W+XŔZKRnN2bi J9dFb-em#׼'!Zmp} "ZO]Fad9Z_-ŻX5t#Kr aR!q{Vwyk($ 0ߔC GpX>XqPvT4y:+YXPc1MZV((…wm:4lWгiJlDhڵMuyWH&4mr9~SlUW#VU|-e  r5g]SmˑZY3 X 괏ʨvՙj3ʖ(U;<7:vۙ L pZ~d~_\n}y<[V1Ԕk{}RbU [KF:?U&6f*i-kZe@h6&iܒTLק(dZf7Y]!gֳV5A -vk fـd2`@# vˠDr$\pM d\#a ˙& r-*Ӳ'@ch 8hך v/biZn{zgl6L ٶ5Jel:NZOŦ"lơpCg.__(flR˦oVM0wlc WTvvdP6wH^bX B^WvY~y\H mwP'l0¹lh®s˳V9"/Fd6+iwF}ؘנcKwn-֛O6Pfl6Lc؞Pz㠶9H9bٶml >aul;Gj~vc]vY+;@"7w&/86! rpeGOx u6T@i+) RnU{U,f!x}Uxkd}&K zt?ХGUAڕTVVgHs< ;R7& xF;U ifO Ukt.#FEٱa@h6&EBNBg^vb[z5GF_l8]vMw\(o}X'LbD!8nek1(g:ja$]Ϗ`sO9j>lOCAX8KCI#Rr<n~QO>ta%=' r8R%,(4,urg؊}VpNO8ꮮtIwq\ /GxP8Fb%@yIo9hOyyB~F}t뫮*Sp@8 mQ|CcbbVemGjeHy>Hy`<8?cF+0`T5fdjvBf<@fN;-pnpGn x(dh:XH/!/| 2qH|)멧Yd8P|qp&k$ʿVkPns~j)a9$ݺusN5x@u WY+e9#C9d^~7e<h 05wugt9k]c=6,EG(=#*qi 6C7KYl6LekB9fN:WRhU@ БBGUL,N0`զM(r ŝtu&LuR du5 4 4fBb^dRg߹s|&}]߾}c=.(ոFXOqrM ώ;X#C!^{mG[k꜏e ѥKZbp,ѡC[6 8T4Qo@h"XOX6I|\s8nA`dX1b);Pmݶ.ay'ґK/tl"F0ut:v:Д& Kceh3ښ0jʚBq< qּ\:4Ee!Ôlb-$teŕvjM=; wܲ9}+&iz4cYC!Fndqƅk EK(K&. P@ ,G)#, }O? ,IXXo,G䉕+#,lF,(* Y e!LB`GB7 #!jG7 ?\)7}9Ltc S㏏Xtic 6purUWwL9s@{%ܸRN ՎY)yg;}ba̔ @WZK$m!h2o+mbz*ZVo,Ь!D6i?dkLJw=:ӠЀ!40.-t&声^4 4L",,FT5\(lb̸*J&&QYo5@1BhZ>6ٳgVg&֭I<먣 >>:4,uvl$ab(]\SNn> `, p2a- Bez8f!Qƅ^8S\Nhoak@:h<]+ Hc97qhm@SoDpYu8<'>EẸ i5r_ePaq=2xt6-!m|aY+myT>X%߬{R~u9&3Us4393#͞+BvR`rh4 4L3BEB5A@^jPJ/|t@5};x|vjK@_5$}zǦ!Xx(.< `:I 5`t諱@mz=h %i[֠笳 {!-ڙc,S} jxճWW Z|Gꊅ:.>m.ڔ61Lxu5K;ªi*nj6;.J λik9&(k?ez@ۄ%#F&AlVO7u@wi`0:S# ÖCˠdP :0z>mҝʎ>I'āu"~~Ij׹s4tnr(|% \aR _˧reF,REj>};Ew:`^6RzyytOdL ȅUmgy6,YVoGOMz*|#f[(a;?ٛfg L U ˗]Lˀx%HKinW,gp*o>K|KU]c:hb [|G2/kڵ,Z~kZ ;w٘ JuCӬAB4#4(33+]\Fk)ak:n_En2ۜSy-~t)iOkydj Z[LY=Yf${ܢ!/~, -7(,fql}2gHKҌHgYV-W M&SkfBXf$4":bvb#)XpɊR2YTVpZÖ:DvB rO KlˋK8EZˮ]`6bޕSVO²M1;;<[_{ZL&Blnƀ3SFLq[e6ˠ\꾬7G垎“YͶ(l# A('[#'[Gs1bwH ɰêxCidtĎ gŻV=T8Tq Gڄ%ȡd2 4fQn9N YpXD B((dtJFaආB/6 ((9TSV#FV:=XբB- bqEZҦ"ڀ ‚tzr_;U6ڟ4h;aZL&BlnJ6 2R`3W6!E\:aee?a|(RdFLeɭSʽvAEYVd3,Co%'A5~:mC]Py uO|j=O\:n}:VO>Ö+&ɀl61 J!K nl((5:BQ%L2C\IA%T)(("SFʁeFL \ voTӦ\ @Fcqz+M:pcUOȔ\LF-ZL&BlnBNCG65 Y2P@IHRޱ('5)'-vx\GȃnGx)(RQ̬rh9Qrh@h2 fB[M&ƠXܢ䒦pV[c)St6(Q^vҦ_a#GO^ܯ. KCZױ{=ʅ<7pC_Z1+yp~)e}v#衇E]:m՞y| ale2FUIbAdHַהڀ(̱E_t嗧n)p뮻.%s79p(>g5)ᜍFoFU 4")Ҥ^yQ:tΏLHc<CIxCBdj* Hs=v51Oc!]PN'+u1[Aq :h:C"3d"L .,&o]KW 0I(LT2 (;,hO.\e?fr|h $em}?CꖷBi'GY8ۭ[sӦnnH~c"?Yji.: r0L[S>2ˤ&vu&0{P٧,<@zG]RvdkҾ;oe`l=?E^;ꪫ39蠃О rJ[>3i; }?ϑo6cL<0 C/ |Ȝ, c G)Lz;76 l@_wuC8%_ \mժVX!>}/-)J8/l.褸Gtth|-#nUW]5A!38#!P#i!@|\?3Q -&ɀ8 |YF}4-r\xb=@a7 .}C0kf({(ayvi(X4NP}7 ;BY\SV@lIM}aE@r-gi Պ+Ii#7c7NQ6bB|PX`H[* 8 7򗿤vM+Ŏ;ޏ%:m0#- ]k#Ӓ+w}~ccL0)NxԉwC;,hs@S ׼+(c@ゥߵa#q^" .x믿~||/|(~0 ͯJ1@^8fm vqx(^^0i E}Y.RKG9e";sBEXLzᇭ3-Z M&aS!mPgp5C)p]]G/F|PR9fV(Ŵo1A'|r(Ixj >e>Q/Vr:?+J8k+Br6,ym.c޽õp'z0Rhc(.`x$Oڬm۶qcLwyl`vm5mz녕gܸqÏ>6Zcz#@wet2˄Rf,)eL;ɃwB*5H:>As1Q>!0XESKKA !.ٳgk)`|)wBB@\q> 4(>;vEIӢ.WG&^y9tEԥPVmH=G~)\L0 ·I~׳RGncG@0h@12fP~B! 83(묳N93dHG,yS&|->YFЙUҌ,=O {" [uBˡd2M+  DJ( ?&%XPֹƥO6Y3 Mo= HiQ1^7r*Ri >8e EE1 e.1YJ=Bm fmr`L(.,)rMRFt6)'X u^>3^Dk\OތٸIRnʏYV&l/lIYE;| l1*H!|&L;i#/:ꨐv-tچ2Ƥ$>ԃ6z&yO^PSN9%G`G& A` JYq, LP7d7o,SX?(]0Oǝ[s](6 G;pL4h.]tX֨?1x,z wBXɇw@sNԑ|}ȁdGiD|! /!.d",! kL,BxF;1LġNXqIhc&)+ ="!m]*~V\&huRK"ȯڒ2d!dBP ʳIi!C* Nf<yP޵\q9yLʌ0o#i19R ee-WqwQ+,VBYH::7!..(Hrfj &v.}{ʍw?w|ӷoZN lPךPr_:\C<'_&gtvEfWh[,"<yO5l>Lbks,pI|i !@+E/iΥL}xgS6)!d- r0Ov:0 + ojkzq G͟QҠi|t,\Opw\-xA(̖Q.>aBPdL|xtH-- Z M&P} *)car+1qM0᩿B& {1&j5|І!1eX.Dq< OOIt%DJ1<(66bAaiM%;+` +VڢKe+D218/(r aq eifʩ:&ySV3iά;s;z>Q}'_Nz'iS;w>u$m{+)eb dCd}ER8+AȐvѤB X~|a^#*rPLY'NM9{Cy( )+$Xܣxa2[@ZQn7D ' r`9;S'^Sk²ޘtd,7j?:/fppy,C$^ t뮽.4Hgt҄8:CnneCh94 4LieBb3t vϭxpei<4Ơ6,Jy:Mg*bצgV%g)isSGHCٴ~tI83Zʣ9txXЊTvrk,Y!&iz[fw] lφ)HM8!0up,:B]ta 2H8lώӇ~xe $/JaJM8y`!i> } A5tf svH;DAb(u&Sh)9€ڰzҰo6b !Ę'M2e_쾮(]1Ncm,ZiLmp 9q lIBaCB4+!C9:py}&]v%v \?8Xݭ[8#rWS/ـ\z꩑>g߭{8~Rܴ^-ؤCJ;s}ݣ~y7܉' QKlrX6͸]چc}]0 ".厎0ttt>SciLx:(!Ͳ5~@7-L*oځ9BaC9f ?>p+&uqVniԁgXaxZPmY!Kξn2fIЈ;u:X;e ͆og6#vl5 gc̸qPn$DttAsTuxl7RمPJ#$YQ!u!/ѩrѹ@Z)P-9dOHecm ~N-yk[ѩT֎ ՛pԙcњZaQa˔C) ed2l !cZk˘KG_&3:  At\1f+eyT}@&,V8Ҽ{Һ뮛~IʿG~r]u]ӆn:w>4pA<.lG]tCcM"u~!pVH{WjӦM;Hwi(78cG\2iZ @xk:aÆ,̵a:bx|Ι)cM9>VDsMHXgBdv ?y晪KǍex'"M:~*qYw8_舉rz宂jcRȗP:H)Ӹp/i/6zke^ \z^._k՛r>*x(S6 -[ O(dـ>Cd% tM7N:)ҢϓMn\㊹[D_[y[i^"mՙgL($>.@iI!X$200Ϧ/%=Ƥ=zPal[G. I|Oǒ @ !北K,D0'ciJ!jIqi in}r }b4좌@⚎ |wEѥcAYhH#_k:AhDZtRQHtaK/Ke'<ڵޤAR &DYBaC #LC0L7 }4@Hg,C'R?G_IyGN7F:䙟'UpQ<_=YV(3U ĦbX7dEAd 6 QQ&h+}@ QfĠT0C-[B!&:*3Gyj@IR@"w3Ty::`:Q7Q6IqńNZk(+uV1jbiuԄ6t|I e%BaCQ M&ӬL),u`{~] b*NNgyQvPyakSa—[^fCRGP.{(ΕZz[$>:ip:JRZ0jqǂ#+ (QbuL>(S*B!5k(ԏTMPڱzZF 2j9lrHګ&iBl6 NYy(c}qiܥpf Ö+0 4Lfـp6VĵD-~S5DSFcPNe dXYڎSS<ϖ-K"&ɀl6ˌɃ2h0Lfl@*if*fL&Bln怐!r$y7ژϬxV94 4Lf-CyƌVk4T&)<-CBd@h6-u=38 \w޹ѢU̪3`Ny̹'|^k [C{o:SJpmݖn8#N;vL}crpv-|qp ZL&BlnP$ct'3<3-ri}Qf~PPs%1t[K?S#_vCFg:@1O#38#]|qjeiϋsj-첑rh@h2 fEB-آj}R3|C~)=쳡$?wމ0RCEXhȐ!nH[o5^~帇{qs=7),%XnV[mPEheԫW:Ԗ[li@h9L&Bln9rwL{l5,s(rcS m=[n֓:*"p#NN 'l!J=ʻ^{E6EE ."X&Z .X o@h9L&ifb-[neuGǜ`@.袰*=ʵoӫ(-YgԥKXw^{mX[Fz-\[o>)а{OQ'}ֱN[um8Herh94 4LfE?jTh[5 q׿}vi5N(D3X%k DEZpӮkڭ]Fx8O.\Tn)Xb!J8X rq6)!{o2-Cˡd2 7SN2=WYaqk{fB)(Ѳ$! Ee)>J6Ʀ(RY6f̘:/V)eѣGM5P|Q̵s"w^(J#­ d-*S<8ۍ5lXV$@xĀrh94 4LbJ s:~31Gwps{\cl>qkMz!>uyb7 {ٌuVVp0}-h6 4Պ F il<"DBˡЀd2^@(nkF0*;ij.,c߂}%^!xy{Lޥ^<dC=SV!M̒';\t!@-fs[} 97/W2^fV,m8jԨ蠵OˡЀd2^@*{φiE5gbr~qq=Qwv =ᑞ\Ci3N?#qsΙ\= {:kaS|ږ%vVGZ?w TINrX 9CwEl@*2+RYƸ)z694 4L2 pX{׮nB_?6$<];]v}6!dz<0m6q./G/:Gd!88íKnA5 4[ RJ$bjGxB_xᅸO[b-1PZ(vmI[M}­a %G9 fo*3͊J!**eBˡЀd2yLḵ]F_XڶmG&p#CYszAd±1 ~JM746ck!e^T%Ig J!3N*Ŗl!:_rAm٦E΀R|AtpYkq[__}>;oIq]I6ѧO/pY`5BaCBԚa-8c\dMb ճW4x}zGߏU{1J3$ XIɆ47pC$wU+QeL`2&20jQg5^uUa6tX)S܂!ԌHtrŎS0ZvRH,%"AKU+sMaRcU?>ֲYƲ.s-CBd@X/k !g\AY}òBrOG7{Lnqפ9EA|Sfs+([lEB1|}0uzz7[__ҡ1>E.We\Dl{|x\>?;.w瞡󼘇R+kAI=83蠃BˡЀd26bq(ZfaefWkAef:TPtIM2kPL\+,4Rqc-X.Tmi&΢f^ bf7.h7N{n\ KIai"r&-̚kYP_=Cˡd2 l,(Ֆ%Zy7[) D\xmӱ[ Xp-<7l䨣 Ž+?өSъk6aDX}HOe.°u,*ƺB(E|uG@m9';qi4 Z M&a}a5yc]I^Yy3&[5Vڍ-lnfB,1',#mQ([#c1U{ĕk[g@(-)lqܥKXw^{mX[8b zW`O8eGa45r--v]yu)BˡЀd2yLke15xxx=|q: [+omCE7Q'~˨6=<2 m:S+mՅ٘f ٦eZsHɅP`v /Fa λإ;EF)PUsoZ[ ,:rTt)ה펹<ڵkǕwpvΪ2WK/sΉxXgD Ārh94 4L|u_c5V$e]L^EsaY+\q8ۃbsXHs8F LjrcCb6F38¸:u LYQ6$*:nDB>|hY_gt61/Q.1"ƤjW_i ׯ_8bVіBL_j)9JŵViQ~kec:K.$@w bǝv)6[rh@h2 WbIJ/&[G3D yM,oV:,r, xv>vVL#;AQFqߘ"WYeHK~P9Iږ j! e-:oȑu~199bEb֒^My¡.=xZ-&\Fu0=YCorvla\4walxk׮jԦ5 8zim]X%묳NlbFK6Zx۰JMΔ6qЊ-&CˡdMeY<Xxe?1ٳgXw=b8nbE$Z ;gb-1 I9Zhtm $>SU.,fW]6qYfq8ZA[.X,>KOakCBԚaٱ]F9+]۶mcC4Lb}55krkgc365=5$O6!܍7~J6,6J&iX+)Ny䑥ik}"rHkdgƺƺMk3><-U M&Sk@"lIh+@V<#uwީO>{&i3$ XIƆ446w*V<QζBEE{Ly pec3bv0Ś.fs+Tfz1˳1зMe,&ɀpve!8p`Xt s_X`{`sV:͔@9‚kU>WGPںo0h6@H[3;2: ,vy蠥YUʬ: ׃<^QFg0[eR33i~]'q`4swQ :t$OˡЀd2ַ k;̅{X&ǽbmoL-KW9" !T9i4&}܌!O:~朅r˥}'9Us峱u|p(q'_:ZaD$k B;s\_?pŨeɡ(wZ M&aA!YYy%" 60$Et-Z_7|Oyh$u2;MAshf{+>::cbEbCP9"v9rd5,~ꄥP).츅'Wq8e@؀XM ??;qJ:[-CBd@XPXxfę3M y8 OG|ef 7`nx0V H;Kb|EGkv/W~0~z((ʛsHE\c ;cQ4t=3|HOiUWk)K 5e(Fmd rh@h2 e ]2\5K|^) ;餓bLjrY-s[QwyHIkr++l-L^xՍnjy\i(=%=xQV&ܦn6 -3+rX;P6/ B*TYU!',,4RHX Vmi&֎;` : 6xtƵ:ŝgq)gzsα rh@h2<5ta6t1VrV0 7!~JcGжgyb2<24T>itӖ{Oy\_mCw-T7Q[x0>jb䲣r*.;WlL#FHofs gKExwL{l5,<(rÕT.x:C[nb=9ꨣBY-*w?t)p qU{<a!=|Ա`VsE\J֕Jrh@h2<fXc`Uv+91f(X\@,b~7Yfc*X`&2l8XqGyWx\qd`5f̘a5Ck y|H|,i`/dx#+;aH V @9Qۂ~(3{/z)_e5_cڇp&o`Q*%9ےs"(Ku_Ͷ-ܲcNXFP>Y/!\G\AqǝݒuK.1Es׆:"\Svv@be"x#qP7 Z M&Ǵ危 s=YX  /~&*LNr b2w],mGK ,@#1xrh>y3j"3/c`"xki[vec`X@{aeկb|fIu}]qx'Xx 1޳*aؐu\V<~Wȓ6XD i@!={\SPwڽؖ>@,פ ,+Al3jc)&ZŌ|w\|brdJisW7Gq":Q)Ӄ>ה٘]Fxn$T|udx@"NAGzXs9C=4ⳋ8CmAԝMH׿u0{h$@L2hbSZ)|"28jNX>4\)X s$c4r@Y(o^E\y0;[KY$nCˡdQ{wX.'*x*oivIM~,@qop{R,bo׮]#%VE3,b$a#_s8&KZm۶ ޘe3t7`Q:`d"zr6ʡL,tM"ȸ 0<3IXLyƸD]Cqc{D=A+bd80Jyyfq#@ZT)WW׵kJލI)@̸5\X+::r>rBˡЀd2ַ1!֘kX +Rgrw Ǥ%@ tgd#i@ 0MeZ.2ɸuL|?/s=wILY^V[-`&_yW%~&`ȏԃZGzZR@q1$vLҾY@g^ ,lǤ-m@H@` e'a2mIz5v}c}ۥ/2`%Vk}裏 ) @Ljp%\;&_adnʯw0᠃xa孶pYg վ}VG9`KdzI:+*ڈvT@aM䨫b[n3ȀDc@h@,-3&Cd6m`ų:+j:8.uC~Nx> RwaΝ 5U` ŠK}K/?X T('@VVc;Z>j$q,/M8ͯgpo|fM$Ch^Үb;jbؼ}rP|޶|lc6 -3,CB%2#T0ˇe.fs lE ylHu[|FE+of+OˡeЀd2ֵ5e6 @(嘅͜cqZb`fE܃2h@h2 flx 8.Jw=[D]җݓ |yƖ캔>LŎJvʮTV=xZ-&ɀl6k;P!Y*:tsu׍mvotGTφARXz8[i9 L&Bl@8;*R9f 6+t9O k¿aC(f ?M1GZi9 L&Bl@8;*rcǨ^:6֭[l) q(VLJ}8 MA ίZcE܃2h@h2 fl'e/N:E<7d%ICgqF?yz'/ dE܃2h@h2 f| 8kݻw36w~|͡s}Wdz[n%~w}sΉk[fO?4=cu^z)z(vyZ#F~d@h94 4 4Lfـp6Tsj\_x+<-ABd@h6 "u%W.) {f2 L&Bl@ ,3&Cdf6&CdjUc_|QFPۘ:OM6 B k]SL?_}U5jT:餓,c]uUS4+uf7 Sq'Wc]v6IXGT*aӪ[7 Z M&ӌ_\s}7qO{G:w}wzg#MqHqy>ySĝ#,)CVzFy~_l._Ih'M=<ÇGyFx -+Hwygc| 家)IGMrHMI: Pҟ`>b>\o2g±o^=yZ~4nܸ}SN9޼c@h9L? 0 骫 ~ ƃ>EtwAK/MN]vsmsC8迏Xƞz!>MxK .`믿`r6t=xyǣ GUsϥ., 0 DYIkIZ,R?01vg~qH`{-6 D/c'ePGCM.h\'ma %L+8 3Zko6BAAONDtz-gkPCBdjj"+zJݻwkΞqݭ[t5o 8#0WlA! Mpzr-.Z/}ҩVZi^O?=89#-/1QJ:cGOs;QX V[E#GƳw9O[ѣ| RN *v:쳣}ܿ{u׍ofvE=sy"a}G-2gF-bAt&(-#tt|GnjFn13ćL 3GYb?1:L17>!DgYˡЀd2 @8:;%0РAH~wj>V$aˁN19p(#;WV=~ʲfŽ~!}~7bcldMb|Xau_elkxfz뭣9Aee2 Z M&SPsp`ڧ~:m `UW )8 &.{w i߾}EŒB0kX0>;x5Zg>KuZveW.]xRߎ;q BN=5A }d?V'|2ʸ+7x.кkIqgčK\rtX>3c% *K/N,/rmݢ -&q4~0_Oh66R': fv 0!fwPʙ>~t1T-3C-2Ĭe :P>lϤ/UEAy *sqBh9Lҟ>/7 `>~xVq [$i2Ոq,LXҊB7ʲkir@܀3A=~H<\K/#;JXVD_SףG5`,|?yjĺ8R ~Phmj;1j=`c-CN8!"w1N1J/rV*$Mxaډ ]6Ԁl@HEGDG">2Cks,3K,D3Tl Ĭo@̮1lVղ:@Gg)BZ"Orh@h2Z  n֘N& ]Xdu'Me5/87?s\olM2`Uؐe]FoRc}V5\<~"c{[/K*b9-b eLޏpj}'eԚƾ}Ƹ Yㇻh1a egIk8/bU뮻Jo, 0{ ـEN ~{$lZeU묘"a c#!>n-Pt?p(ioFUE7e/E. -CBr4v,NX`&胱ɒ@abNpX(@L#IYiqP SnYpX L?ȟ$2.eLS_[ (RaUg('|J_{:.W<-CBԲ4|G &?o104u)s \^Pi-/#<(e3aQun_~&GcU.SNyX) y[Y8[<5g`b6 GCaFz,m~ 3,z1XUP{>n u)~K ̐13Y:_q̞yZ M&SLJX-Д^]VbZneeUZaTGqCqT25T|{emؔpuYOV.л2 A=oZa̶Zq7ZCBdj.cl68@sQՎ_]׊_\,[YeyחOˡЀd2yL3fBOˡɀd2yL3fBOˡɀd2yL3ft՛:=xZ-&ɀl66QqGȦ7({V@9jU׬f4@h9lrh@h2 fC3>餓駟~ߧM6$`rH~Zo]tsoIq6JmYN=(g4dק7"TI)Ș1cov2s-W M&l67c@A[lEtVW_=phȑa~66u]NoF՚B#8",4(/b0-2=CQj뮻FڹNXDXr8;^[nIwygU駟pKzk`W+TӥCM7tcWPGD馛k6[+Cˡd2 4fR̆nXU0k~8~c1y7ӏ?[ntᇧ뮻.Og {?~8o+(XPQfC裏ny3<3QQ+S^Rv7|Rn9j .`n?>oC={L-X(sύ{ou%ݻ5cvU-CBd@h6^:']~i} K qQ|'aYYadk9*gzhX+K/4=uveM6r0 :~BJ.0{E>{lLJU ,FQ@xi֪恒r!. P[m>hay}WÖ*&ɀl69 l۶m۪ \ ԕW^9pq;GXbR~GVFZY9M>wy'ԩS:o[<= cp viWD]vMo}>!,/ʶl\:t:y=z5  6,ڷo_&Ѱ `1 694 4Lf!.rN;-r)q{,3(s5WUe},(PoᆸSTplSO{F"  ^ya۷o5/᪇Ÿgmipz'fmuwD,3+ TX::<`@h9lurh@h2 fBXhrm;wZBlfC" |MVH^&oҰbI ֑iVQ "y z7Ϻ,yNl:mՍE馛`'#F^ˀrЀd2fs3q[瞋0Qce^JO=TZj" JpPcЙqlqkq4pP2M<@6@)P\q.KoV>6kmK ꫯ7VDYΔJ'և]x_`?_͛rwD尥ʡdj̀rҧ2ݦ.{0 /J#efs+8HH,6Bƺ%OossN[cӘO͹rÖ*&5Bʦ_097LJwps& s:OZya#o'~00s} VQeYI3htZ+\t+Ŵ囇//$&5B>=8& 7᥂W+xi²kWir5DHTb#=Xv;ks̑6Uc4\9ZҧɞyXNXvএ[6$#?lBe{rKe;8X Y"ıLl6 4y M&Ǵ&V^{gOO?wa||k.@_~Ԧ5<;6l:v옺uqLӳ>}.q8VBmCy#=Jd5A5 4 M<-&B1 y?x!,vtSϞ=G{Ce!Av-Kyp^Kr=C9Zhtm $mY7ٔM V],X2۷o4 4 M<-&B1 N?w|ak۶m:xvE}5V:r\sMrYvۅ{(kY/H:aX(Sl٦ VFu]2p iwLwygBـrh2 4LӦ$صsM6]@{ +:ٻOԧOqO/XgH#26) inWUވ\F9_RϬkꪫj9l>bj6 M<-&B15 K^~!X`'<,+$t|~[oGKpMQWlw}B!q]s5?`@HjݡCjLj͡ـrh4 4Lַ z7<ɲ7f̘Խ{ԳgϰQ=zŐ{=hq=qĊHq;ZC,-Xzĉ -P*B~iGMeY&{oHBـrh4 4L;et +]۶mq@ ų.(7P;r\?rGqDn=5$O6!܍7~J6,6JصkװVR]v%iS#<4mOUCz l@hi9 LV kDc=6dpճW4xGe>S>}bt .u=+#iӟؐ.rWŊG\2z뭷;K]w]իW/bv0Ś.fɃ2h@h2  ci8p`Xt s_X`{`sV:͔@9‚kU>WGPںo0h6բqFַ9/? 58 OG|VMCBˀdjl2PXt,s,y.\s$DZ N*\VVxԝ2H+K;OU9r+8Ohm?.F7\oj<4㏏u([lPZu7Z7Z -&²ss ]`nr+\}67!~J8Ӱ|A my'v <# {F ?ŴeS裏V7ϡܻ[-xeܹsub䲣r*.;WlL#FHofs  -fXc`Uv+ѣG9rd-8x8}!1 ,{W_}u0dP~xO P;W\@KVcƌ%.4lذȇ2sE^!c'y84R١CGX8S.OYtRXN8*0P;q?e>}iKq",맬yAHC8ϐ!CFuꪫ_x:7oKuR.ڊڟI,\pAn (?a|8H[a2JM,f@Zs5Ò˨ k >`ӟayZxJ[nYgۥKmr<ꨣGH|sIzh_n|FG[u衇"_US4y[PvoX[O=n eLwyO`uܸqG;s#;oA}emIc1m߾}9딇 iWBB+&Bˡed2@ؐ˨=;,@ 0JXɰmivur:tho‚8rH W[/=u ,"v8PbU0[,$X>{5BٵkZm۶ ًԙ;cmb@` D\DꃅNzՍr(vmm^{M74,B)y^N Rg3\&@Ȼ8Sqc=c}Q{,@+X-W]uBڕ2.pN;Z7yZ-&%6=58%@J+a&N9ߑ4& pKYgV@E16hfu 0u߬e$~^\B{(+kfyV[mH{}I\pA+ʈϏX7{ G~ܧoqu]imon i ҾY@g^ ,Z?~|#=5VC:upjʺIWlK }H:Jv5 "n2 ZL&S eNxtM_~iJ^Ղka= \GrQӧOhr&w=ma=XviK Xpd5Ï> uGE C5~K.I^\ڰZi&ʯZØ$Ƈ: HIgs`0]tQ۷o~c `K+r>` ʫh7@U^YA^{R:i_ ,vY]@ŗWss 癿b[.PyVMCBˀdjef qv,&APbW>~&n!4/G}i RkjjE1l޾Ei(~YwVo[~>I1Z7Z -&2#T0ˇe.fɃd@h2 t|z&Cd2 4fBOˡɀd2fɃ2h@h2 fـrh L&Bl6 4yZ-&ɀl6Z7Z -&ɀl6Z7 M&ɀl6Z7 M&ɀl6Z7 M&Bl@hEd@h2 4LO fqɀd2?1fB+&Bdrb6VM&B1l6  M&cl6Z i@h2 4LfـJOBdf6V̙`ǻmrغeЀd2fYl|s`_,)&ɀl6[Ǐ~ ίǍg\lieoBᬓY)&ɀl6b x֎cMaRyZg=Ӿs,4 4Lfـp6J7|^y4|O'|<9rdjr8q^Ѐd2fs3t?J!JAҐ!C3{4poֱF@h95L;ހ M&l67C@aÆ<xС``7 ysÙϴ3M M&l6"Q>J(n8qbYǴ7NO+(4 4Lfl@h @{Zg) &3&ɀl6[ (q$=xXOH1x](_}U}L1 :io]N 7E M&l6 Mqj2,3XTw03qˡio}Z'$ M&l67sݿ ;a2 f}0ϲI5&d@h6 g믿>iu]47W{Kon.Y/Js0L2_yW f|2sO?4;_nBaAɀ`6b .ft/Rt.hn9"Bei9 Jˢl6 eL3f᯾*f-8Ga9 ZfsKfYBf-Get{2fsfl6fl6 4fl6fl6fl6 fl6fl@h6fl6fBl6fl6_|ye߅ߥb6YΟ~iOyƳڲly1fB,1f@{}ݗ~L`ښ6=ly1fB,S>zwy<6isފ K˳l6 ͳNQ0{累?8}Gȴ1mMV$]]Z-/fl@h3ǸVf"A[Ywawiyfy+ r~ᇓW~3Y/R^cL4;I1lE|\mOjߟ rȟxF|f*}>wUefTδ1mmE~wټV/ d,/fـc@W _~eojŤQ] K~>v>vׁ"2 8.:zcg4#%]6U=Yg?ˤy,yb<-mnnϧ:]veiС!c)*IM;iȑi=~Ey^VvgDfY( YJ|R5Ks1G:SҀҊ+:ꨪ ꫯzGxo?/3X1(o~O81)%_4W^yPƍP7p<r'FzzNLzuՙ`V$wſ|0 Q!ח\rIr@ |!#2d!ÇǬw]uC%Ǡ+؃bGw1/I?xN;7xǣ>Xk^{ܼQ]w5( 7xc( E~po1|ǧ:n(+u(+?FVn!J&G[wِ<Y^zV[K,DGYf:wNK=z6k#i}p< ;e~_,Wٳg1(cɷ"}d)*M]髹~qbݚl6[+-:1";su6_~v6(!n顔8Q@Qp۔eP.C 3@e:yC~ liذayQvbu7iv%eYk ԃ8%3/?V Y'Q.Ny{r"2]v٥]HF!2 @DJ2/dQ/Uodb4!q,$3@t [.2zQ].d K|!. K\x P Nh7|LpO?Ȅ&ړL2׸ C}>푶o }k.">-@ @dlן]k1Qb@ȃoM#zaY+BzkF|} #Kro,of!X\Vg_b"m&F 2p?n6 ͭB񕫙\u4;Ϡ@ j]w2.C XJB((6fDq{PjM V38#\0bf?pҺj~3cnB<#w>q%N\q3Օ>S[lEEjS ˏIG@Жb9Yhr N-&Cfh J|g߰T7vHEd]C=o27Nl` ?J7'Vm}d ~u `nu ' _[&Uv\;vʉ|Ǔ } 3or?O٩;,@ھˆմ_:nfE* en2FAלaa@IPд.A~e( __fA|„ Ϥf`p@(/ő]FŊb9Q*+, (-(*U1 O#qP(Q)iYQȰ6DJ(d)w$+(Xx׬=2SN`da2!ZDIkdqh] 6 :]2"C?GK (I~=JG+eMA0ѷӆ-w{xkr@=A"% yg|/d@ bK2r@x72IM0)H1ف0*nXM|X`;z[fـRҙP܇ C; ʎPjkZv{E`}E5׎ ƚ' rZDmot뇘uF .F?N(8XY#.]H;v %u/ԑ>#RK-U-?W_~U]7ì3k(+Rh\V$Zw\!Oq VYF8J7TdvٶlTH9HpO P/'8H~^Ux(фo`HuHN 9rf(| x|k{Wnԉ^EWW%O,R|MxSф Š~Q&nd A!uk LBNp]\>\rɺ}M?VwE#8_35p3?ɽy'\emGFdE&C4l'dA2E\HXo\s_;FfE* r752l;(WRP,& .O:Xe3(ڲ^,;iAYH&J o=/'ńe69M 4cL2kC {eGy}6=rLhgk.M= H7dY!șXVo'=ܛ6F zJ";ByV`- 2Mԅ%Zu#C}q4Ųs/x e&bH[P:k|({.E>+O_ŎZ'Xj{ڋΙ|jɀ~>?9/ב-/M@T!{aE=&>*xڜwk՗ };/WdklQy~^A YM&4jű!@l6 4(Ebhӹib9bPQ3J1E17Lx~`%k(*AF<[iL*/@ky:/KeRU&Wj_e,kG)]H+dM2+~CLcdoH0?mPⷡο5MeD쪿,~ \PxnۡAKW,!d,Y"a\mމXmߥ+kmTYT~/ddrWPq+kfـfI(4$h.Vp}5],STS+ZiYmnEeeNS 'ey _*+cmSol_]b 0Ú{f]bK6VkZ}cQjdLk}; żby~l6[+p20[ Dz~lH bj l6(X0h(rlߥl6(ȭJTZbE߅y3<;r 5Zwiyv?n6h<Ǵ1m*07ֆ?줥gfl@hWQΆ4xV{V$]g>íỴ<7fBs̀,y1mnwawiyfyA6|0<[^fـl6fl6fl6fl6 4fl6fl6fl6 fl6fl@h6fl6fBl6fl6fl6fـl6fl6fl6fl6 4fl6fl6fl6 fl6fـl6fl6fl6fl6 4fl6fl6fl6 fl6fl@h6fl6fBl6fl6fl6fـl6fl6fl6fl6 4fl6fl6fl6 fl6fl@h6fl6;Y e?/<Xl6fy֢OOtlcӷ_}V٤-+unc[2 D o7xʯy8oKږ6њfl6Ϟ~i#5*9fyӨ?:H@3&g駟3i[ژ6(4gkF1f3K?; 7mK]6> ͖\SnԑmI!M3v5MUR?8}2amg6֋g~ʺ:\)_X϶ƥ0NVFG>}tl '6Y&LP3՗xziR<6D[f)`7r4|O˄!H`L>\̆eF5ҟeu+٭(xviOlu!nY=iUH?H.o:W\(ov#<;&(sGaC\)Hvm\1&%S}63`1"fN2 E^L xlҦ8E+ }窫;[oMa*_,~ԧxmGO["$~.pFB)|;.Kk6(ڂ{}g;3x|jեruY'dwZZi沍uQo Z VI1{m{ꩧ{O=XZs5c:e|>1[mUرc|C?U͑7Z}D~^}rqHC*m/KʠgZe3)ɧDz 3n/ڋ4Ƽ=&ңEHKm:UT/k \f'W]G뒷IY9 qN~aIکh v% y뮻"mʬw'Y7+d"ϿZ.?iQړI" ~ ŕRѮ;}Ѵ o=@u(4UA|"\jZW^ei}F> 31ߓn˵XDuټCwi oMӎpF0Mic&+piV=o i^g|IqIڕ>!}\Q;fJ:Tow@I 3Y+u5 Bf) Úwyi*p-%gi- &@dn=S'/f% бo1# +\kOĽ޽{78/*OGyd<'tR\adMxj>:B<+֭[bDb-ӧOc="rE%s9(erH4hPXP:蠰lf=Զmۘ{ a`|C/vjE(x1yG^k׮10YGxP8 ڃ(k&]@#ѿ}QfƇ6(ʢyםQ^6CeÒB z'>-gl9#"o8 h*{QOڒ6u](eg\$,afо!ڐN>h%X"]}UK[^{Կ)iR뮻.H\Qꊜ7ߜXc&C=4m0HGXډz`:[| ֘ @駟׿5i&t }'rK2cu]~!/;SZk2)ηC^rJLdv}liȐ!UuM7w} 'ߟ2`\ib"@[rwQXôD'~yPW6xQSmu9ʁqo0Sk?G~'#-s1v]w*Yz;' ^=<ӎmQ }qq7Nd6h!S|x`=S[}>1mSj_ W'?5> _LL'z[BX^|wbbЄ^kt%MHK5)rM(u?+>n-]_sZfe惹bvZ=\ W NL.,(2z:: 6EJGLX*VYeC \I!+bfird>Ee5$#(((hXHhBPQQ@Y]6YQﭷp$/&~rso=dICaT{QP#?ECLR/J1RGƄW\%` !PH?c'@Dy@<]tKLPW^km2@%=x饗F}ڨe-P' HVZ)(^Lwrr2 -ڐY@$c|@95Pb(?]tJ矏t/x7_>]GV$l@У^={ =ͻ&-dk]^]v%/~m hqLD˯[l`$Z#+Vtm9گUV>n8lgqFS;wm#t]XT`S7!}F}g㮱ˋlE=7>q78yνiPȠ\2džk|+{JQsߧ6 {VJ3zZ~Ay߇es%_~w\[;(>:(?~lS(]YU +&^woAM:1 &ribUb'Xܴ0S ;Ƅ’ㄕ@nBt,Y!DT3yYy BhұMHd0H.0E)RȓqۍyJE?J)w x' rGۺAqaX0XX'"3 ,R(`DG3,Z ?ŗhΡ(*LңP~kc Oh#x88 c>"r>CALȕB|禇0wq"i~El<[!_6R )eK[O?[YJJ.74,_}ӠO߯jP\~߰rT^gWy󥬼vP>WW>k->3 ^ V0wpQB 1|a C}g!B/Yʨab2zqS5{%B*cABJV(yqmڳ-"Hʐ %#H~(K1! JWBCbS0eD(|dX2P=y܃$H BɎl!c_yK!ȧ)~b3n gc<íB+w$RB4#9P(,h?V%)P .#߂# cn}O=TJޕo\AŏYd-AŃЊW\G[>~SW=.5tXXc$SlH~>2ԷF+<9\>BAKCy?+gnȳ!$WFzq4•S-\_ ]to̔Q:<]F㳶ݒ y"-ʂ\ t8(䬭6,Nس:׆C8CyIN8aO3ʈTgQ>f.#8b薾k#H:N']*X%?Rqz˭]=EW3AτMdCr9( W %g}|Sòoa9˖uڛzǰk%۪OVrjk3a=va]{ϻn hDHCMrfM6?JQYq{=U]$7ދMi:~o 0 ™Dˀ7+bVcvDT`` WqY(^VcLQ|VtMPʫ0 CnV),2{ȘdTػf8?V@a.X"~84G~|ic;N%3}1jFonEC/6&z?~kk-D:ڛ> qk>N[ y"ҡ㬌%?T2R^W)@)\vr_*{ϰaK`=TNj,|^l%u[?,o'~>SI‚ǧKy8s? x6T<3"kZ[EWҙ>p\3*{KW$c.5Fla qw@6.MhhIPr/5%<5rv{R_Lou?MB<ӿ=/} C 8A,<GSxĉW:k{9dqgo_{DovYE=7ח+a"~y}+,~[ Z#Kx{Ig7<θvۿޥ8YWD{/mW6Iҗ_;~DߊClk'}!v/zxĢ_Z 8LdQ֣fK{8bs[]gZRݏo Th%N'T62'V{e]W׃_S?;bU"HZ4v}_j1ԃhvs)?Sþq.{ju{ӵ|U=={9~f%8"[!q|BjX+: 5bٿн98e4|0ޝ++QGY q 2bec{2{+qܷjz8AX)\aê7)o9ܱ}:4? ǸmW'-!,0I$Nj}L̸_ 7.I2naaRO[`u ,X_8%<#S<!Zt%7 Txlg9ݗۑַ/}mmM]Xq!^\QʷΩ$+_/ߪ_U[%w}Vw[8~ך(eam]+$?p.v y_hY0HXYI&,G kbif%n{gf&?s '0~CX XZunxH# 3g^^?$}<V>ilqo!e[ f(qR->-ls=^ڃ>o1훥s.|`J·W\ 85G8}uűIqjwjI\qW7 ȟnD3V8*2*"pwՠ5wg%k2D"h4m43/,Pb{(!,@i8"*;e=Z3LV&.f^<-POSF-STIS%a+74]R@0F.1!Sc\{曯etM岇0VҼȚB [N@Hxn\E _zӕ=Lh=jѫK^:,;ΐ.'_z`yPc.*]U0ݪI>ĉْ1YH$D"qh╤/~c{ vUT t+ۖ-.)!7f Lɖ{U"H$DbJыS?=O?] tC|XU @$Kp kIp [Kmb}!Le(H3 0bk>LJx; b1rrfBH48:Jò\B Ysro5C-֭6kfCH?5"b8R{ ! gšaB k2DYq#ؖ%B{4;S3G숰8+23j 4PaFl'zyY1# 3|#Fmh gCߦ e6zNh^%Kb /K 8`.?nWOo ;jfF+LVH?N+ ?"RL x]KX@XhF==|O oGPeL+{Z4io# <8=p7< >`ŝqQ?D1@4a޿х7f'[<B!ALci%3A8h#٥˜}61d?3pM`LqO~P4[-O|F #,Rfw(Ոߡ?Zb+YvkÚ;`Q [ +ath),7jc]ogF %w`d.3϶q@>0mq.bLcp wtZ nHX P"@S-pn  bAr Y`X @ X 6 `8A8΀\w/x!!4h b"Έ⏄!H#|DC"%H)R@_# <҃B }kTTFщ3ꍆ44E ejFM "z /A `,1gĒ4L-2kZ\_X?'t[l<_+Z ?_WE  t,Bpp^;" L"fW# D YI$)TDLK:NL%} ˓uɶr2O^B.#!#_&?%)ɹEqȭ%*wIWnL1Sb)ŔrJ4.卼dy"rPͩԩTu5NE}Cьi^dZ>m5vvA`QXPФpYᥢtB2Ń䔌|XJ *(PT+(G*(Rޣ|^ IX_LeIGtn@K齪DU`L}]j*jjj*ՎØfa`\g|={wq .{>^K^ި~MS_#KcF=M\\s,*ӚUǻg/`m-T\+ZkNNAm@mf: /L :tt< u3՘lf9s@OK/HOCKoHD?N~=AC]py䌜265zolb`ܸIII]SiiU3YVns<üjhj30eBTKozV 0%VV/'NLnى_wY߱Q Ybjܖm[i{Վf`Ю=׾!aCG'GccSΪQΫϹ\|\|tutw=[gL&q']ߝ]H!dyV{>2xzm{_W~___@AРuA7u!N!CNRCcB+B Zw#"͑ 28r}(ܨ&'GM$&z^ž=1b}bމ3u+OP N8?bf/%;ypSz:L-z}ɴOל= S) ){R>"Yլ-l_& g->MsO+M{>/3,˫ ܖ>+2&k8;!1s⟚3sH uݘ; C£NTAGAeYV͟9|9O  e혧7oX,H]б`ᲅ.,Z%K.MXںL{٢e~ HHXtcm+]+Vn^S|ĺ* ?\]kT%寽s]mria607oxqee6Q66[6n^sEFŵJ-Z[VnyrWU6m%>mm#pGSquN΂Ov:/u5wRïFמsۣgM=Z/;uo>}- ;%~翦z@聎r~ i4М,nIj9rխoVմUU;زc  Ox1ēWOM>u:3gN>{\p|bSC~?tRKKwkϤc=/wE\w捩7797ʾv;SWv_~f4G=|#]ӺgOyBbO?4}y/:z_ _ ^FM[Qz_ACGg?%|z:43s/_C,Q MKu $xv8%Dvg"&xr naRTcjg7F$/V o1o E8< 1342 1486 1 >@IDATxY\}?5w0@RhfK%ʒZbY^ze!ZIVC򠬼vlKZmnNdD")#b"f^ܱnco:*wm9>܇o^Vk-JPx1=2|g۴aA H._$ӧ/m| kd,5 PMzM/.YCG,,@P7Pm?3yGbN_ļo9nB7Xll)/-65 [gKIY" w5*fK={ppkQ" " " " " " " " " "pK.m]ߚ\"9~W(2y/-go?5,?=ca i|m-Un WS<HXn~Ao ZS/+ƾ]D@D@D@D@D@D@D@D@D@>/r麻Xwl`١Ad`oaJxMbK 6ZXw,(-]a5u׫6u ȕlf9K=}NXķۥ~~(1U{>e7'Ng76/j%:6eQyydq.qѥt򯜨=0>h^#jN vu\ lkwF,N~pCi|] sx.q"" " " " " " " " " T~pXvb,O)P X8sh%cpZNw[ۛbq۫,Ćg,ڪ= 탽\O7}˲{^ ˉcY4cb]Z\oc8A" " " " " " " " " "pؐg;irKQۯFQ)nQobU'vSُkK8v]4@L涫.`[lsʷ؏ j~u>`[lSED@D@D@D@D@D@D@D@D@>֝];u"f5j `|~g3ۘ=6:smVO؁ef߳K`ܿ/% Kc dEwu UgYlOZ޻ O^CK2N>>ϒuk{{Ɏ*" " " " " " " " " "-l7_yB7uVWtv;Nuo56/]'csE#>HD@=- :^nK7Qymw-66-\; kX/q-ɶ /klCǏY2@u"p{ݟWlGAƾksmz{X:a]&8ķ%wljzTz{cK rqOw]D*eo̭?>ac=3cł˥KZ*+][yfl{|E\oo]>d,S/h"p /\zlm3Bl \Xm/g1 ^cɼ0R!Yq^8dl{K ݞ~c 0s뎯tTD@D@D@D@D@D@D@D@D@u ۛzc#{}D"ig2fƇ6]lZU>ٮMcl{%l&m-ɶUD@D@D@D@D@D@D@D@D@DF`ۣfKyxÆ m'Kc?K.u^7CNNϙ:>ALnzA`˞f{ߎO[,. -;:m4nn~1l ?d6@auNnA6L5?>mp+K',I5B\Vk"2cb8sX߯rX!;V:cL|!]om\q!na]i>;v϶70KbT[}g(Qf\KKKڐlu/U3^׽&nǂ3:a+5xiǾn*ѪɊÿs68:1V5]߻ u[Dd^wmi~EYp S]~\FeJ7.0^p<_\F; ˱ݟ@W]K~lm@蘷oUţ3|յ|"(N2ϕ-?m/|}oV(6,2cayĹc+ĭuZLަ_l#Csmzj̿aa0Fnѽ_*g??k|ǷV" " " " " " " " " "F{Gϐ +-esi BuwX谱 տU }6,^ 5E-6׶b~y lf7_]}ʷ-4,y8ɍ[.~_ӻ|@ lMN-,$Z_@֒2fN4@u+K6?ǟ={s+^񑘥Sqa}gvV,Umzf'yZ(5' Wg?˿l%R) ;⫔JV-3'`fkےAED@D@D@D@D@D@D@D@D@DرcAp*k?z}x"N7sռB5d=:fYڷoYǙͅ_HYy xp#Y52 jƒq N]NYǗ gmg|N|ޙ3)" " " " " " " " " k'&l=,)" " " " " " " " " "  Dmڽv$Jo PtHD@D@D@D@D@D@D@D@D@D`PE&" " " " " " " " " "ЃPtHD@D@D@D@D@D@D@D@D@D`E&" " " " " " " " " "Ѓ2{@!K@wl@ED@D@D@D@D@D@D@D@D@D@/off7WoLD@D@D@D@D@D@D@D@D@Dמc2okk ]"zW:." " " " " " " " " " NݱPvO,:(" " " " " " " " " "] ؞ۮ1Zjg(9hGD@D@D@D@D@D@D@D@Dז@_a{mkUֱU=6v<ϽXW]:Ģa7m3qN%H=OgEW+" " " " " " " "@_a;VWRž7lz`.@ :RZثjD5Faͣ}h_nC G*{d{GM88_`;_Iɦnv 엖\d\ ~-n^oإ˗ZZPBv]8u]_TK'O:>͡1۱cҢ쓝 \iد޼b ybKMT$*`>AMX.?rݳ+~n_q;O~8VlƍY{D틀v'Wފ),*b0HfR"͒dkÒ$UZ!XӇ7Ի۩CخVi'Cخ;^>]fgn q\А]iB&~PrY%/fpYA&-]T\I L:` 5uIBe: ;џZq7[_[T)l -VTo ~P3XoW?Csf%r5KÖNF]vȝ]'8222]͍N}qܓU֎!W^)mt5dg+5C%TB^ٳg{ٳ,rop?{zh yؐ̏E<܃ytwX=$π3x˗- ˠEŧ'61}^ˤ<ÊCUoL&MLL,gl~;zv'(v&nz#" " " " " " " " }6}Hbv|11[ˋZ4aON8q^cDj3Cz|/ڸ īl&2Q`ez7Xl.( ̌m nM)A;mV1vhn}V1)JӟmqP3k<w/~"* "JBدFN}g+co߸%W13g p~JLk]/b)6`O ?E,AbqV`3j ؔ ᙙ^-gc~ ݪ^ ^+03_ '}qq]~YOED@D@D@D@D@D@D@D`+lovDhf֑mxq2w5o0gO| ȝ-(~Ѻ[-Y(^ U \#='iq@ Wy5 paDW q,e^_,ܿ+MK:Dytu.^Y*Dk4ˁL!KCnZ46 A,Ds†M9mMϞ=lK_#?~e/;̀3m+L&& hwGt~y|咫G=Jdvb3}+@q>l_嬥?yo ; A 2T 8P+;,\Z`Ҿ@}ߌMOc2=kv6ю [`8n{vRE@D@D@D@D@D@D@Dכ@_a"fcQ,@طd!KfRֆI (f2ⶾͶ!oNnvk籸f؍&&ryM!do3ڔgX]ksL t~~މ"X<b/X̜ uݺYξ:[ba!h!.a\ܝ2KUIsuMȍȺEx@0.EɣPA$7'(l8EetݥVܮ{ !{^}ҞK8Vu@V4|`c`$ʹhY]3+l6i9bt1d*+!V1^I c@07xm۠f.2]Z;TbJ[yO:=C>CV$|Q7xB ݄8uc" " " " " " " " w ?U(.DZdRNs!7ߙF?!\/jZnelqlCv88 !gSN`?mT?eqí9HNAKcKn+F,z6 X!~1x3qNK/&:#<ΗoYt s,|>GWkn}mK[,L$`RB0&D[*-Y)QK5VQdG -:DjB-dC0]ȅ,8ЇުULv4!@P#cth ˉ8LlLXyuI%E[#" " " " " " " "z} f 2V}_xGmfLӔYlƱ:Ϋ@H<7{d5#S]Qv\ n$!ٚ/hc]\P''^{ͱqYg\63Li@7MgU܆\*K~9qnffƅB"E.J^}>X31SzW^Qߵeq#qiK" wmT `<:CKmz"sSPCzZcY؏/=zրPL[܄O KS9,Xq-\jZ=vWs~]•㖄wELNԭFFv"mޡ##I:{|`|Eٌ&/=2Rv^8ˬ2[ K,,y)BMZ &p/~ln֟^QhCњۜ g!}='x,k׮#gKE[D@D@D@D@D@D@D@D`諂@[^1FϾ=i}hpV)Ru! `awhg`~[Tkn)>K$(QkΝNޱc˼8Qn)t#ˢ5pZ)-xfkgY؅Tќ&, f˵h^1;AmW0<#"G ,#[ލfԖ ! F3Պ=Bbc$#Hd b-NMf,Cn#mcwuk"~(ձpeSaЂF>/$ntیP,լX)"iR~`Q`A^f,͢C̴I"Eg5"ӛ:}?\"6_&wڦxs,mL6jSD@D@D@D@D@D@D@D7vBmwwtw<B1*QXj֠2qm(?ߪֆFlS`g6`%ACrrqʱQqL8P@oTͅŋW_uVv"?mriV"n {={-c< Jr<*O.X0E#lXj eydj;=.XEB%zg^i[qAȸc}~+FvjK+6=.fEر@pM7?_ۢyؔ$yLۜ*q΁{Ƭ k-bE$gc[Gv3;j ւ6 >1p@uo|Ȇ_u#'dv͝˟}{>d3Xlb?XJ * Y Y+SСILv߁ z^Zk`Z ˅ajCg1Ѯl'6:BO 'z~a|X*ЇH0"6y_wD'8K6"WHW~55DqfRh̗J G\ xhnp|@-}+ ^-6*>Q募{ X{ ޽hBfj޾(Fr9MQ[rKY,>?SY,n7G LC1K19VĂ-),᫰!B:ʍ Au '(x;3m`a1ѩ&e6"ǏmLϔ9!Q_ë K;_@ن_JUfYt1gC}r/٩)Sv.E,8Gq@ y"E'O:^x~3,j:>uꔫCݐ}su[dR{]&kw֐|Cmp;ԠhgciwPɻcҰZ"F"ٴSYČ?>3=dڇ`B}xy6x-- A^\E|1vadoȃԡ~3lr;W΍w 'TD@D@D@D@D@D@D@D@/v $mTW^uM1sPl⫆ l 1Je\5Z_PbbmJ%"g*=):[;1B\ %lA۸v-CrnFAش afh\vצX {AEdv7/fSwD_|]F/}KkܦlIhBzxJ/ZžX1ݍ~ 8~ptcv5=aY<nq'Rrnl6_]D|)҆ jW aeix/&TVD/qpNvw *gΡNpYg؅{l*ӉxdeiYߏſ6s3X4B ɸm xSCM`PmWaKjj*}FϽk+➩[ZFȢEk`&,z8{2a|h%B;NÎ164#2 kԵC۞~n,hg‰ ^cvbU#Wؾ7ֲj{eTR^)`sv6X/F֓ɘ( S(c4m.X3oOk"-3L=??D+W8m dn3lNP MgALT9 a 8i3c]is3݄΅3XjZƖ,~ԑ9\b(|!d3Ù6_-CnBz9ױcel>j<۹h{ nsxw#(iEy?#Iauul<řLe&్Xyt_’,e,Hapی{%O%"oZ?l!6bSؾk\Ţ.28 &}<-<*@_a{ي㾀Dąu1,Qѽh!)k9q,6#lf?/+ lJOmSd7k0s _X7P"e )7\A,a18q ?]̎YJaV!X|5RCwtaX~ʾ?vɉn!?brªoQ{+71ChP[Ƃؿݱ[1cR|Â`Q MZb6n #- {,Y-٧?92/Ud|3nB,.O y ~=~ 'f mۻ+mqt",lQvrdbm'.sxiˌK7< NE{0G1˰# W6vZ6 "ho!.xQgN}79fVd5fgKgEF`cX$3qbGP@IDAT+Xؓ^Lc裥ή}EFp'qx ]m ƫgbr!?T#bVFHpN1oUlxr+𤧨M9}N(~g6Y<[VɾЈbQ԰M*K(|UD@D@D@D@D@D@D@D@}vaj J}v(LUrF 0.;gC`,@łflC!ezŽ%p Eَ@y][ؘں P~뭷\Lts?`$_?O:yjS2f~i)1<@v-+æqdg6~!;{GQԣ#P8{} ~B"?`*dSWsON8 9?~xBd lH}Pfg3,P ɘD)[8oϼ=X{#A=B}uϓN~a38!,BԮ*fB0>`$?&7b!!CiӋuiiG҇Z&isI8Iն-aEb<ת 28a?~a %e;RɄهcC~qߪZ PS>t萋'y=###Nv׀JQ"~O`2]o0~902 _W]bh^1n̠Ld ZZs٨xFFjBHd[Bb6-Et煩xPq }T1ahzu9UǶ1Yl6ãeJmfRXGٶ/ wϦ?˗/;a#y S G6ɓ^K7l*vX!.V`%-7=,Yw9d'°)[ ,̲M%)pm "(sf\n:"*tYtp: [<!CD ovZ!d0Y> t*/{pK r :z[='"(* [:!lvxQ]ï1%w8Wy}3Z,fK%?CX^6Cgs YLt%=K!33$u>kzf,7>\4 q%Dp !]jYNpmeL}m C).\' P5TpDXZm*ce~֒ o~"}w #C;c̉|\Xw bVVKWQEQ D1r_7a|)=2/ ;1.z׮]V.maa v.\p̌Ξ=D~{L9<8A'jA_Vkw]&wRn/2csv $I&x˻3ZvF,ƈSl",/|b {t3l'RN'G-DEbɢX17؎+jUg\Ź%d,E9nyl.&?iH:svr㏏#h O|->;akXLڝwbn엯X}Y=1'>><󰝇wxvY,@>~R?bK6Ɏ/}rᥞNqGfo)j/--9Q<ɓ:8qc쾝wm&?t VzʾACZ.. X u C8XJ mzgCأ@ oDEzb W zfS"Ɩ}M5N)R2C;@s6n wdpSkrryjӂAFt!Y'xJq`*d9ܥH/?",DP9fm ߌ;=浠!E12޳rk%5Y Udlcc Q68ρ[6S(ЮיQeՇ(:'xiaQ6!d[{5J s\| (2a I"|+UT@&԰Ic-:u ^29i$qlkaW]yXyN@-%J"c]4368Au?'ЇޥEXl 5jMդ%*NPn'AV:Ƴ9Ǡ[&[BKsre6,2nujNpM.X>-z8~ } c.t؛hXܗCf~^O% Bi%mua:y:Wibqx_:h˞$q^z4u=EwFe_aW(lSk:m|.M45(Rxl _$7!5Nsb+CX!mx]̮wڴh0G[feS|s<6}) m;vpb6_62KAzc)na")K"[QxS`@b\A-B gܶ(2!^BFtw S dk3c/ dJ8 jZ`"`V9ebᘵ2IdcH,7:H\p[w  w7!XF~"7{xFs,rͳh6,hů h1NcbNdhgmLbb}}炿Zz`%>w. 7asXX#1_pHپlFg_^xumwE H)^Ԃ#Azg;*b *$--jp0D{:X~k":|N`ԣ _ɃB/iڇٳYV#ٟ9y'QO Fs_ VQX%$Ydę;(ȀOFq_/ ǃacf3-{).x-ùޙBkunQ7wjV[W >^=l'mY 2Š'_U^D@D@D@D@D@D@D@D`K )*mFpRov V)>STEazY|M-ʉ8X}_dF^Gc7ϣm _ SF|m^1kA5jx)pSܧG9y$?~ܺ:DK/!_2X߇,^``2/b~6;Ӳ7OD KiZ)tܼ|drQ.췎(2q[ꯗ842wwܱ}f|=k|ߏ>lҙ\S@xX߳lyzagϝuNeLDlll=7q#l11_tNpJU kv)dŋ6 '? ? u+V+lS )2y,)- ^iN  ~k_'\ߏZ䋝 X-89ZAc˜674ǵX*9P(:s^I;**`5 +պ`R4c=l2]qik[btq}\gJW{m  X8 ];󱎰/0[nE0k'~?_^,{؇LhÕEjUx+8c׻y4VD@D@D@D@D@D@D@D`= ive.X-^Y\r!:/݃hѶoJⲧ}mRh[e)kvD*]h}K ,Gt`}Oؤ/xX"y'm{69sΖ e'23Ȩ7=qз ?ɝV_o :b_t31F62md@ }Xs[}/dm߳g=m4uǫPЦ;=}M| `qKd3;{}ǃ(\s's| tR!殫IOǾYDŽ\<8~9kl9;'򺸵#" " " " " " " "p=Z"oۧ8KDĸVD&_ڦrMI el{Ȳ8N I!x67k۴& Vef+Ow`崢Ll/f. F`A S5e-ŲU:NP__cD+fVfzN`3eͅBs^s$ Om1۬k\w> {Yfn+EmN8PO tp}&&kcɪ˵@-U;IztEwfp?/2 +" " " " " " " " 6PS woK%˗޳bՊ}lJPzE r% q<<vq6m6~Pb7«ѻѤPDlP…,I@:a}䠍R,fwB=7921@'oU|ΊT*\.,odIdZ$l7ƼCfm|jڸ8rv0l&\&)@gY hb2˂!|1d혭Bv5*!P(t3o H]HjAa;_) gS6F!p.ŝo6 3o%iAqYײZ.0f&1Lp.,^70V=TXR|՟}AnRs-}Y'LJ/.Ja9g|=EvOR2IFg}38Y܃+cӾ'W^WӥģȊϟz嚝2sKc)M@,,H2Y_Lj'Dڴ:l%Z S,A Ad"c?ﶂEw̌ = ۋmz-Z̥+bjF.u1f ٻwr3 x}`v6\L$ccw[xcv 7|>OگHij{8 rlذc3V*#a^- /=zC" " " " " " " " ۀ@_a{bx2|Ea~]m .[TtW2#¢qLݤ`MwB+i@VRl7O8if6wk7~ 7Ԥ`?o[*w*AI" " " " " " " " ۏ@_2݊kmw$_i.#j_(1[-NkV2pFQȑ#.LP gux pG\&3yFd}1QKy3#.;K#sòDn#" " " " " " " "]/_]bT" "i'T9ՅR!<[_л!yپv:(" 0\$V*" " " " " " " " w;g"}AR" " " " " " " " " " ۇ@ߌ/'JE"" " " " " " " " " "kGyX>+c'خ$loבQ\" " " " " " " " " " = HEE@D@D@D@D@D@D@D@D@D@+ ud@O{bAJ@v%" " " " " " " " " "ГXtPD@D@D@D@D@D@D@D@D@D`]GFq$ a'خ$loבQ\" " " " " " " " " " = HEE@D@D@D@D@D@D@D@D@D@+ ud@O{bAJ@v%" " " " " " " " " "ГXtPD@D@D@D@D@D@D@D@D@D`]GFq$ a'خ$loבQ\" " " " " " " " " " = HEE@D@D@D@D@D@D@D@D@D@+ ud@O{bAJ@v%" " " " " " " " " "ГXtPD@D@D@D@D@D@D@D@D@D`]GFq$ a'خ$loבQ\" " " " " " " " " " = HEE@D@D@D@D@D@D@D@D@D@+ ud@O{bAJ@v%" " " " " " " " " "ГXtPD@D@D@D@D@D@D@D@D@D`]GFq$ a'خ$loבQ\" " " " " " " " " " = HEE@D@D@D@D@D@D@D@D@D@+ ud@O{bAJ@v%" " " " " " " " " ?{Y{_@*r"IH 4*cb ?C4Q !qF@EIA@C A<`-J(] gα\M<|c\c}~kaJ_ld"D @"D @"Wb{D @"D @"D ۻb)D @"D @"D @ޯo{E @"D @"D XJF @"D @"D ~%/^@"D @"D @"+~+@"D @"D @"_ fW"D @"D @"J_ld"D @"D @"Wb{D @"D @"D ۻb)D @"D @"D @ޯo{E @"D @"D XJF @"D @"D ~%/^@"D @"D @"+~+@"D @"D @"_ fW"D @"D @"J_ld"D @"D @"Wb{D @"D @"D ۻb)D @"D @"D @ޯo{E @"D @"D XJF @"D @"D ~%/^@"D @"D @"+~+@"D @"D @"_ fW"D @"D @"J_ld"D @"D @"Wb{D @"D @"D ۻb)D @"D @"D @ޯo{E @"D @"D XJF @"D @"D ~%/^@"D @"D @"+~+@"D @"D @"_ fW"D @"D @"J_ld"D @"D @"Wb{D @"D @"D ۻb)D @"D @"D @ޯo{E @"D @"D XJF @"D @"D ~%/^@"D @"D @"+~+@"D @"D @"_ fW"D @"D @"J_ld"D @"D @"Wb{D @"D @"D ۻb)D @"D @"D @ޯo{E @"D @"D XJF @"D @"D ~%/^@"D @"D @"+~+@"D @"D @"_ fW"D @"D @"J_ld"D @"D @"Wb{D @"D @"D ۻb)D @"D @"D @ޯo{E @"D @"D XJF @"D @"D ~%b+@"K/x%\>=#D @"D`hc{߼.D @"D @"D Cӡ'@""++w7/~Xz7.Mkv\@"D @"ܚ@۷џ#D @"D @"D`hc{߿.D Od]կ~'N,?Kww+?kݑ?Wl{>쳗@"D @"p }oOD @"D @"Dhcн(@"puرcǖһnK?я.++~#6)?Uݯ{VWW~#Yj{nr<ߏD @"D 8>[G @"D @"D 8>@"~6_a{v b__.GzԩW'?K,5ܕVJ|u׭XVlnmzwyߏD @"D [D @"D @"D ihc4Ae@"Dx'=IK;o';VGݚ̕+V>O_s>Mw^.l6+@"D @"%o#D @"D @"D hc m\"D0xG>yw`NlfnVl)}}~cя~Rݔws즿{o-z.?"D @"DhcD @"D @"D w26@"@o/=qғ'O.Q[1-eZ>u;~~?|YG,uʯ__VF7}߸mvomt_veߏD @"D @ۇ\@"D @"D @)6"D gc֘_zW,lSy,u_ =y}tfl:uj})Oyuޜ3ۆ[բF @"D oE @"D @"D mlQD Og~gK/t.{fU凍YL>Tu_/QM姪~|{/|;X{x+o&K^<`62#@"D H;~GG @"D @"D m'mgVG"Dx+^xҷ-Km};b+:{6yb*|:1F5T7W~V]mDz<ݡs9gmt믿~s[Lmz_tEߏD @"D g@gh"D @"D @"DKJ/)ކG @}~8qba6 ཞֆ_~gsϯ?7|yʿ(~y؞>|㦛nZK.d Z1'O\ ׼5;{mrSbz׻uD @"D`/mlEz"D @"D @"+mle"D w,ǎ[_{K꫾j _:ogM`T/5<\:u7w/sT7wW̑}W^H?bnozӛV}CK6mt_~ߏD @"Dhc<@"D @"D $|m]:@"pfش}3>q[:7zfs&AlW>u}tރo񙫟Ϙ\:6+Gyͯn>7cs3SY>ueouY;F opŧNZS#@"D 8<><'@"D @"D @"p$}$^sD I~V[nYS?SKmҭ_S럪oX~雪O~sW~Nbj.u{G%K>U]ߌgɛ'/u=Ate-}STnimp~߽/}K_z+Mmt2#@"D ׯE @"D @"D $$RD 87{^ĉKmb6l] NߌӹɬO]f~oX]~OqSs@O.;O<3osWs~'y~^G?K[e)9sVfW;'Wm[/^~D @"D w<6xD @"D @"D @۷^@"BǗmo[z7xM]sVct֝cV}[L9[spV;oSyxN3W<{s}s؜7o >y}T}*?,Vy9WO<ujω{nky?|tg:)?̽+a{g>K7y7~cﲟb-~ЃD @"lhc{MD @"D @"D ؇އ/+E @XG-6\mR6ScTGy ;\:ΡLw}y~񖺏9TN3cTʻmSjUOu?c|sQ~szn{y-=WfQ[V<7mtS^~D @"D(hc(5@"D @"D ! !x=B"D`?Ԧ\+j&s:թsTS~jWs|u>ռ7κu1uOX}V\G͟zԩeyCxz7tn\s͎_-:s5#@"D @uD @"D @"D G@GG "wk=//,=qҹAjc'/6̓}o}3w}Sg]u=|yO<gw]G.oƥMmz_{OĊ}7K.dD @"HVS"D @"D @"lhc{MD !~v]رcKmjȞF)?]=ݾ:7SϹ?\~.v:S~>sS\s9T;'s):ؼ<yQ?|T~Ko͟zYgizjۿ]x;;;WOͳ=꓿ɽ}}D @";@wΈ@"D @"D @"3F3A@";M<9zԣ:ujP7qJ)四>jsttg<~''Og}'cts̛y{y\|믿~}gM뮻n~kG}nvئW\D @"CCD @"D @"D  }#D #k=yϥ?C?Ԧ L6EmtɌMRE]e|&u1̻@<1{N>>ctOGշbsQ٧n;7gXx='oO?|~ӼyP\S9S;9Nӧ~.77/}css9gmtS/zыvoomt}G"D @nM[@"D @"D @" _Q@"p%7,}߼ԦK62IQgb>b:}To{>Ι>usX~9G<6}>W~s|T~:uO;e>_~ͥ./OS՝7U}M>N婼yuyl&g>e[~+o=Y;}W;F<~D @"ZmlWۃE @"D @"D 8>ﵧ@"CJzcǎ-6!61mN<)MxAg~+ɪo={sߚ_CO9ͼSy1jT}yb7G>yˋ͕s?^7;. *Yg?>ܡwynۿwWի˿K?-=unz_zߏD @"I޺u"D @"D @"#K#{D 8?Wr}z 6.5m`MJlbsT}Ty[}|[o)5oK=~j>SOU7G}5w|_Ly'O͙1Tts;G]LN@IDATͼ:5w1C>T믿~.kZϓyNZyԦ? r,=@"D [D @"D @"D ihc4Ae@"ܙ~x==K;G62ƵMGjgsNwC\s̋݇nթ{QyvsQytfOu<5O]:-|7}T~s>O}?'P<lnS/yKVg?:7s,?"D @n6o@"D @"D @"p'hcN~D ؍[~ ^ĉKm(s:u1/v~gsQb>XzO}RM.+^\p&֦2`5#@"amlߣG @"D @"D 8>o;G %O~r=۱cǖ_z7//I(h&jQX9b:[}s)ygE#e;omnnk^UsnmrwG"D 8>o@"D @"D @"pH }H_lD Mz=CsCpnYU矪g9|onۊ3=b>\b嗧gwOuS\}b6^Tu~uyyyoꜣ<)\[<9T~;_uљGO͛~u}Tznu>bM~uɧNg 7ئOySVwi\MmzMoZ}&x~i"D 8>KF @"D @"D ml#F @D??N6u1:݊g~kV=^؜A1W^ӭ~oKcκ>Qy>s}䩼->c>?cW'vԩ{،usQsէ3cPbySnS}S9tgl>bsij>cGSŔ_SVF}GsЇ?ߋg<+^+omo[; mÛ~D @";@w"@"D @"D @"NάD #p5׬YW oxR6gO^lOu15W<5s{/,̡T?͙ʏsĞOL39WS}oMiuN>S}ޟyaz?DD @"D @"D _RmlI6<@";=sfG͝'_x7<;G鼇X=1t~[9os)~y.g|Σg}䧚G6|=ي[}1}&-Y{>׿꛾qysTGyާy-ܲR^zK{>~mtӿۿ]W;}V=8~D @"@J@"D @"D @"I7@"CLw?~|=^smټ;{sO!6|xTGs/W:y~|>u>>*73{s^>tϓ>s#Vg^yG婼s{3/vҿsxGs|9gO{Mu>?}|gOoZKG A%pرuYKmt|6l̉SSw)s\}Լ|uϹ3.t?O5_O]>3旧_xk:|Y7u?1u&~/VS.OyOꛪNw3>b7ukGq?AǜS͑\QT~Γ7)s7U?/?S/?Us|3_7܇O}<[nEjW\~ҧ?KO}jmtӿXQ7wk[a{G"D @uD @"D @"D 6o?F @4׽uk^'NXKmd_q[65o3O :9G|s͘φlï.sy򔟚ߊ's<>M</:OXxq<5[9߹\OOuK[u)s9gwvkvonnj[L/G"D w<6xD @"D @"D @۷^@"-Jǎ[K}'M2jSmnsSOMsf]lμ>N9=VOu~:5W}Ssu{>+/6oIg9Q/|9wɕ⋗^r%K{g7߼6^~Թ}g/?"D ؾ D @"D @"D w 6@D GM{.jMk66mɋO _>59|sOL|ΣYϺxgsݗ{?7W}<>T:Uof^<#vVf5ϹT}-sR1G͛kʛ3{s_sˋ˧1V|^SyjoΓw_9|>c>\1>|3o>5>yCթ~sκσ~Gҏ|#KmtM@"D`@lD @"D @"D >$>|)])@.[Zj̓؆M21 2vbgl7ԭgܩ'vT>Խ~,7c}ޛy~:{ޛ;{^\}3}S>T~ΕOԩϺxCU7W]K<|7c<էNm=y[wG"D(hc(5@"D @"D ! !x=B"^K?_jCk!o#K2cy_I>ΣO߬;o~yswݪ;{;OnX\S}OSէ;S~::G^L#OW򫻗؜s\/E?Ӟ9馛Vކ7};߹/zыv/j+\~D @6["D @"D @"!mlݣF 'kW_ĉK禖M,_b7ڊ!f3FSmoW}Ν~yQſ\~|SgX'6ONO>y}/ǯ|T~uy#/uSU<}b|ϧo>kVT癫y\繞[y͙yu9<~8GNTe|5}^:Vu_|}|3?KK͙>oܛOqAɓG=Q+~ysnn/o-z.?"D @tD @"D @"D $& @"m7x*;vl^ԆM*[s,y|Sm|O?UI&~uO[Tu1umw X5GSsOs3O^L'V|<>*Og^<||oΜ/gY(??՜w~>ʯSӿ6BOlތkK޹cU[yuG[ϕ~\~Λ~ϼ9t[SyQy\>SsԩTuu/?yR}M53g>e⋗^tEKꪥ~w~_;MM.ߏD A;rgD @"D @"D #C٠D 8J?gg>_ƕM+αq5}bj>.թ>]sÑ*O7O>-o̜Y7g9\ͷjVl~|}3﹦O~u9?T?>yj>s|'gos痟ω{oS?wXfl9SR/[}7w}oa.;G'蜫ok>~y1/>cӭo>}[;ٚ9TQ>1_hP+rk'φ7}߸{~^m[.[~D @n6oz#D @"D @"D'#D }w= '[6 -|66R mTm;ǷW|tS=e/\~;ϼ|Oљ{ț<yT>C̿W-^yuj{թsx/s~~yb:;y+oO~}>g~yS}=OLpt>Qs3}#6OxW7O_~yoUt:?Ssyo?9Sթsoygy[jy_SSXF<^ײ\bfl~}T~lQ۪}q6ͥo(>}L\<*ΛtΙyuyt_}sM5g><ּ-C=:U;=Ss<xg湇/7}˻O5O<~Ϝy~>:}3'n͕7oƞsyʛ<9W}O}4T:{g}lz=h+X?}?長m]zʿ%/Q;܏D 8>@"D @"D @"p}gOD g5Yz'N,dIL?6f^Lt+nfռ~Q}[UGm:n͟uaκ9{q3OVOO[:xNGkᓟ|'v>m#3_μ鼏^zϜyT>KOl>X]yz^T?sgUwso*?~gU3W^V<ӭ>/?5κ937o9[yu\Σ[~)\b~[\b~ϼsXtu~<]RMߴCyq{:믿~mn䞪~E-?"D`hc`nD @"D @"DhcȾ<@Ncǖk]z{kPJ[ͱM&ԝ9OyTsmS{nOSǯnR5G<>sO7o9RyP6|sfys͗wOC~߫o7b~*_>S<'ϯ.ޚ;}b|s9{.~Q8GlV^}Ϻȋ\1'/5?syԼypϟSZ /p|,}򓟼~|L}g=;sCm+'/ //g7g|Su9tST?b/Oq7O'O͡oOGq.t+nR'9ᗧO̻߬o=㽉Yg>]W^~G?񏯼nozӛV}CK6g/_~D @_ZM@"D @"D @"3L3 q@"p O\}b~y/?}3n.uTt\}#YƯn__:g9o9N7ϙus<5GslO>~u*uSߜ5?U?Uo}=9T>Qs͛uy~uyOy}b0G̷5W<:b߉//vo>=yn7ukS-6wΛy>j\|9xw_?K.}$s{c G"66OUD @"D @"D o;D Nw)/^w9q~Km raM >jG<}bsڬ3~ʯNgOGT^X]z)g켩>>>;y:O7{oj>T~K͙>jsOK|y޼^|sg,O}N+?>>1?>y>*y)w櫛#6#S|bu-{zЃ֨?K=?&K_~饗&7^~D ݇ @"D @"D @6@"p |c[#;-oyҽ6 =6flƑy~}3Wߖ\s<~u'6oKӷwg3\sTw'OG}T~Qu縗>O\񬋩b:O9O~檋>-\.=9w疟>s{Q~|;y3g;}~OKxsSyj>/3o>n/oX:?V^ߜy>sQ}|ss}xCͧ{6_W\&⋗@+6"D @"D @"RmlcE 8~2o.lF:{mnF=lQ&Vs=S{P>T]<<ֽ|~>.>N͛Su+Rs!~8LS<ų_3-7bts3/v?S:k9\Թ9Qy>^-5gWwglwsc9te ^xKꪥOO<6mx׾.9>7fjG"Dj"D @"D @"|ٗݧ @$ɟ\_K-56r@gӆ_>7wP}bV}ͧb!6_<S}?kY:y(~1tų>|~$󉧺;I}st'wɻO3cy|ssS>>N蟪NɋOs9s=抩wO7g<̟ͧ/?3|/93o9bO_]{h韱TI^O3USs\;ݼ>[TT'?b~b}TwO[uy~Ε^婼{μ{m)?5ι[>~l=y|[}O4:՝C'槳Oo{|y-_~ҫz'#A%A}s;@"D @"D %}=v"F~z[jCF bƆ>y:6ylܨSOϺ6OLQsSUw:U;W̫Sb*?͝O9'V<:79Gy:6cuy>>t>y}+ܩs>Q[:]w=z?b+~s->|Ο~1V<7\K3[jYm7}y.yS'>u_LSs|?Ϲ|{~8|(~y~1uT򞓺:+ϧ+.}۷-k> OXO|bi?"4ml7}#D @"D @"qml@D>رcK_W-Ş߆֦ 3b6x̡sT>j_{f#9Q//6s?cfV^<Ǭ;G/693wujstOu.>.s|)?m{s?skO~msN1<>1̋ݓ1jS}7crԩ5j̗ߚ˧>b>1jT|[s|Sw.|S_Sr/y9G@"D @"D #{=q"#EOO~cxKLmذ1&>s(6;͡PS݋ξ'O.{LuySթ~g{ny[1l>T}9V}>|};o>uj~}[oׯ>Ϲ|/7u>MxcWw/T~|Թ|1XSy*m>瓧s?UwkV=?+6?@"pP }P\@"D @"D @"pD }D_|D_{_jcfM cery(yΡ|ۤѧί.'yf?|:}O|yg_sϼ>y.oS{~Y>y}u?u~Su:eoEL?9;Tsmj;W<>7 gFl>|<~ᣳ_丹<ǜ'?܋ξ7ǹ婺Q>o:o>7ݤa||3/we/[zĉJ# mlCD @"D @"D 8:>:'@"p$z׽uK6clش6lmS~u1[l.3_}+ogS@aηA4[ne!<~ΝͧN}̗7GlΌG͛>uϽijw<{Psԧ#vo0g}OuOyM扝/?|N}ׯoϧVo;G~|S=uyO3?f9[uoGsosC|ySXs'j39|39u>:繯̱/OQ~<'ΣO̫O}b|-<'ϭyޫX7OJO};ӭ>MsM溿bS/̼o_na霣?@"pP }P\@"D @"D @"pD }D_|DЇ>ɆLbƆ48S>bGg=fu13?ُx+ުot3w<|yg|>ysɋ͑S}_9R}38y>3}ɋ7ɻ{.yT~~7}R}n3L9}'o1ߊ9~/Xz饗.GK#tml7#D @"D @"1mlF 8ljXx{{G\6l̍-SwK|%爩 9S>:T7^6ͧ{>;ٶUwj>5g֝Ggg#/5׹x:{wT{bSyy>s={S}b~:YGSL=s'}fC<|W'?csixo~7|7|w1Oo?ު;T>|bg{)s̗;N;GXy}oe>˛'Gk>uu.69<*OQnͣyg>s_<~}S0{~uUzԣO}G 8>@"D @"D @"px }xmODHxֳ󪫮Z5V?mnll\Q}b:+d;OɓFP>i.5O=ΕuoS7c~OysÇػPݳ$@ Ha$`K`7LÀBFDD4ፘRoQBĨ FwcP*dωڵG'cxO5V;>裗/x@~~sNs{߾_A:ӹ 7rÃۯx?t}Q>it!isio>eG=|x>_}5_Я>9/ҕj<(W5>䯮Y=ۣ_?P߹հ/9]}\MBs/>]O_???GG" v%XK` ,%XK` ,%}N`/󧿻/%p~oo/|ӛx!K5~'@m|yu܃[՗)ωG.ЯT{k`s~}<@z5ַs~⛫UOs~|tjj|><Es>O;s sdz/>_~yS={K\ks|'__\ЋmݿyK` ,Z{\Dv%XK` ,%XK` ,%?&x6\K` ,ZKt-oy˅>ti~o^O}i^tj[ͧO>CZA<>Υ/GsKNzϝ:jѧk'ǧ>돯ۯ^m~x5h^4ޛ{ͫ;NCsC:LJӕx|Ӽm})yI9 __{'xB{K>n+ ]v%XK` ,%XK` ,%؋}XKN$9 w\Y"ES /|j<عdg^6|/rWwߜ|3/C[;ש{ɫNhҩyG?|}5HC{ԝ;ׇ^ۧ>C}>>ws}{[^K|ԝW;k>={KGz=|wtj<}\xjX}^|Sӷ|<|;>>^:_y~vNߓVg~Q:ׇ=k]?>o;WQCO>^j$|+Is=b ,%p؋;K` ,%XK` ,%XK` ,^lO}w^K` Pox+.w={% rȕREzuO=EKu׷[ӕ嫖s1@IDATS4/Qg OO}:<{W|5;!su|5?.A<` g.:VWO}7թSE{轴Ӈt|Nh>?hNOM[=?:5~ãV_sB}|s|}{o]9.O !jH~t_-O}//kj9kNo6Wۣ6ׇй͡<ģ;!;W;; ˾BW_op ,%p؋K` ,%XK` ,%XK` ,{^l/x'__]_%#^8>裏^yYsڧ_~u/Z.tx',>/k5~k_][~_ |E|HWwaS9a >5ӼǃA<s}ȇ5ts9/?<}"_}|hίhIoޗO_j:5t8OujszX_|x~ϝWw9!=<;_5wB|(G|>}:s㧏o~ ˯xj;9>_utk{ >AzWCُ\j̡~v>[:Z7_sz?g>w>~!_g>~!|Os;|}5ӛCsxs~x/>>>V9| ?=>|z|}hk^;{ѧ7:szso޾o/;!_9k]˿\?fd<|}>r.%j{}W?{ ,%XK` ,%XK` ,%pO؋{K` ,j{++/|j/MNEKك׹;B'V8ćj~Aݧ|5 >_^(W ǫAs5_>9>[Hе/5W_ڜj<5>hjs|?q?=,3S}kj9;W9ڧ~}:Wo9{çzG>c=jƇ^ۃoϩ_>E>99`?4Swz|}5l_ Ãϼ}uiI<P5SC[HWt.xjO=n꣆S;ߟ>xG|<}q"4k"^΁o U>_ux~s5>ćP9C'tos~뷦\|tj{{C;K+Oo| ?S>Bz{Zˇ9>{K` ,^lOp_K` ,%XK` ,%XK` ܳb}XK๚?~KK.O rEϧ>]˧>?s/>>ԷGj/y{N|`}:=ל^]C}txjx)s<}_4>МNMOWŧ; j>WZAϫ>vηs5">~>}5{=jtΧ\ox|{۽Ǽ^]>ćtjgw}ӵև|S ;Ax|NO:}G]ujsN}|USM\@7WC{ßy1wy!^o>?:S={ƫ}x>O~t߾t~">WCstAs C>:ć9zï5sק;W}қj}H\|NH'G=txaytxƇ9_ya}:}Yü~GtQ;\>o_ygXK&w۹XK` ,%XK` ,%X4ؾN|u j/m4 Kay^C<5W^𤧃:^|@xjx|W ۧOQnPh' }rҧWCW9S;]t.y>79ˁ^9>=_hsbu=}o;P>jOO׼ca_s}ڋaP׿un9oܝ_GG?f{x|k?sSXw=ؾοXK` ,%XK` ,%g =w%vWu __ /JZ{ͽX2ŜOk?({B{=Qӵ6/_]=_-ާ|x'⌿Σxt]_=Ԑ^~|m׼ӇT5Ϗ99ڜVxN]9;W;o}Zq.5 W:zKׇ_U?l>нgj<}:թ۽A|u4׷G};OuPy S|9?[+?> ˳:c=vY؏ث6!  ]v%XK` ,%XK` ,%=M`/k/%B廾뻮#|G}ԅ^xK|":/??;͝{__9?}|5]q./{W=oվy<*O9'st;o^uA|\ k3nxP^jszP<5=Esh_}v|>}}/C~xA}h/}wKg~?ӵƇy|=ڹ =g_k}hΏyX]}Om|nsҜyt4tO{ǧ9𽥫?}zyϧ_s=ڏ߾yѹ~~~E}_~k^ }A>==Nx%]K`/'.%XK` ,%XK` ,%y{}ϿXKU{UItgg_%e l_]|X-^jqe|`|LJ|~5<<>sA|לcCj[ȟ>U?!s:{tΧEқ_{v/Xt|OWw~>s>ՙosչ]ѹ~Ӽ}_>{Σt9ă;'ioy~.ПΥC<>xgo}ҫ}r>_߹C|>w|a;ׇ<1xW>w]W w\|~.%j{}W?{ ,%XK` ,%XK` ,%pO؋{K` ,W Zc?cz^zA Ӈ^xy|(?wgou}R:ѫC:5O ݇WxrxOo/4t9W;cNƓ }'ߜFڏ_9ữ/ϽN̫WCz_t'tj|}׾sW{k=Wv^}u ՝S}_ӗ>9o׷y׺3\tAs|0חߓxr}>̋߾_z<~B}'#;͋xjO}XK&w۹XK` ,%XK` ,%X4ؾN~/B˘Tq}/KڜOKER| oxCǓ'hϧsuX_sz5<|y@|>|!yk>Pj~HsЙ!?5ć݃ÇZ>xr*TÞ}j߹OW}Hߺ}>ҙ} '>> =~hȟߩo {{}T9ѻWy~|sg^"{:os|s!js@󞏟@G؏6!޻O{ܗ_cο5|5ԧo{^>|n˧;ii>:'UW|㣯.%j{}W?{ ,%XK` ,%XK` ,%pO؋{K` ,^֏=؅^ڋ/N^j_ݗ0tj{OϜ^}9gz:-OͯE_秆tWwyqN}sxE~jo\!^=cǃ{nz|zO}:<>}>O|95 |h~k?=·%=|~=W}>xSs?!:<}zu\z>>:}Wۏ q}~ӕo搯>5{S=^moOʷ_ҩO~auί,˿WUÿ}xjW} Xw5ؾνXK` ,%XK` ,% =w%T^j oV򕯼Ћ/G43o>Szق_>wOǼ/~ЩͽQѝj/s^LJ/4o>9,a!_49nթi}:>5~tGjs}o}@tjX `yjXީ_^tέyBO_Ν<5>~ѽu\o/=9A:?yқsC~t_ҫЛ;c9:s[x|{>yϧ~jz}tsz}:/\ WC<՗{uj?jk絯}Uөw5WC|k\%]O`/'/%XK` ,%XK` ,%Y{}>]w ,%?~^y}])}9\4뷶9/-|?9LJ^C/?=,Ow{g::5/WCz5Cx"~~ח}S{a:G} a|n!]^W_ stj/y̡~tx:'xtjEz}>91w:4ǿշ^_O>G[|_.>O  GaC}nxм_>WP?|{sQ_ |Ika9/?y|.%z{}? ,%XK` ,%XK` ,%p؋{K` ,~.|׻uA}YbB<94/zŸ tsǟ5=xj|~յo]Ϲ̝}!~k}P|SaxE{Ow"3:9/IW=/̡y~y9}su|>?uNks/Axj<έC|s5O<3׷?W|>ԇ;>_4>?駟V y }:հ}stMǧs~xE:޿~;xOMo }nΣW js??ezׇ|Ӈ|%]M`/'s/%XK` ,%XK` ,%i{}O?]{ ,%]__zK /GЋΝ\J}owW9ݟOyķ{џ9ھi=x {y |\GgN~kwk_u.g"jȧ"9} oIO98Wc;[o_x3|{z|/}>|ןtj>g/͡}Pv>ϩ໗93?ů>S.C\yӹ>?O:tyȵ<~33ׇTAVڞ//F=sΝGj͇K` ,^lOn^K` ,%XK` ,%XK` b~XK6% I^;gk}xҙW;t^<#>:uȧygE/C:95_y_^/A"?ч|\_͇\_ C筿ZxoΟsLJ+\{mGW^NsN{|>?a\tk.txի!~st~ۗCy?㩡==ۃSC|ؾZ|OԽ>'u=OZWW4sй[9{>|>=կuՙ9CS. ͫSA9WiۯnXK%wyXK` ,%XK` ,%X<ؾ_] ,%&_ŗ>.O &'^@sxi_Q|C|P{!K\]_O_-t_uC<{E|~>:s/W~~?!}_==gyj=4?=jԐt.هG^K=;}q^_m?}:~kn94Sj}>:_9~}8S߼CS]y>]x>>lߞ˷<թԧCUO ~BEz><.]s.%Z{}>w ,%XK` ,%XK` ,%p؋{X¯ a/AQ{9b޺Sݾ+|P򁧾y}3W9xK7~/yyzꩋj?2o_uҩoҹOk<"~yjģ7}s}>j~'=^}!}yszh ->9:5ym?׾ >>E<>r)y`ja ;io yk}甗>㯆G_t?'yrѯ^]?=X>,Tw7>՝!ڽu޺a}_h^OO/77_ ?^Hйհ<}>j۹=|C7\K` b;XK` ,%XK` ,%XK%uXj??IB/TZybe#j/{|N)^@է?:9au5O-~>hWC}s>PVg}Og>_ۯZ.ՙν[sS|!<5,y/o߳|a}ׇ9?ܓ _}|ޭyBsb__|>!Ggxz"=tC}9/OtΩ_ީ=c>￐C /gGM嵾 ]v%XK` ,%XK` ,%=M`/k/%n%O^/Tw]^P{aW4 ȱSկ?R zֿ~|!SC|ؾ?ԯ>s(s|[5σ|Ky|{9|xKm-xncN C\CTC~ s&O9yُϟ_k|z:u1S>烯O=jHǷ;o?W5{!s{1XǗ~_3?k9[.!gΧogV߃7^W$ݻs5NM.^5z ]w%XK` ,%XK` ,%=K`/.%6/T7_ezŇ!>E`j黏Nܹ>=4c#/`_,C~o\ __j]Yg ߪ/S]ѫ\O|z}z:}~>М~'~υg_5_;\ |~o>ss~xEs|xjO{~j/?}_]=j>}X}1wo~k|Z|Nm^/{N ?=_+:s3W;OԐ=S|s5ӽۜ[֋җ//$YR8^kT}5t>7s{N>XK$w9XK` ,%XK` ,%XKJ`/EXK` ,g%׿??׼5z$K9ԧ{ƃ|{OX꾌鼵>5|hAǜ<_G @:=jEshOѹڧ+͡C:9S!N.yקD=>?K߽|`y΁o>,_}W͗/tsP>Çrkn}jqN:"^y W5?!'~tΫa{js{;Wj:|}j ?sONS߹}t|S>3GC<^__~Pz.>=1HΜޜyXw=ؾοXK` ,%XK` ,%g =w%)F{wOO_% ˤE:/F{aO_R=^yxWo~ Q{I/W}j:5OWt}:0>>X9䇯>_ ?X?|}>sC|xC=᫽;7=4?!ׇ^|IO^^ yQޚ?_9{ko__'sx|k^#gu__M׾{GtPz99ׇ9>4/k]?s97_>} <և~'7?霯/iv.=ק[|{@{OX^xלj<:H{[=WC~wxx:5<{xϽ[MOo/| Y> j`yΩWXw=ؾοXK` ,%XK` ,%g =w%_OO=%qNij|55竆a\_jǜ~w^hϋׇ <"Uv?"45?稾}5?=|Ր_\:?<5SOǯ5^~=#s}}gG獯):Gz6xr3Gy {>~}OϷs5ijOϿF~//=z~|ojxk9o%s=~B;XK` ,%XK` ,%XK` <+~V+X'{W|W\z$N^r'/iO~y~i͋yr9e}}|:Q;ϳȞǃܯΫs__^ݏo/˼}shG|\-zW9?}u[>_Aw/Ks/9t~U5ߓy} ~M_>Ç>9S>΁W9`;ؼSܝз'~tE?Ӈts_}:hoçWC}Hǧs}~臮\׽B>9?ƣsO߹=xjйWSC>|ԧs-yќo_y?烯j[}<7w^W A}>E{;!sC}<1W!!| >īyu^Km5[ }߾O_O 򳷟 ?X_5=ćևy?G=׹O>|A^j|5G>~| {Kt/:礧3}au??{{/|Or!{2yw>__W]k߾)oXK%wyXK` ,%XK` ,%X<ؾ_] ,;~闞uy/dX^_~o>~ߗ*j~^Ϳ~jQߋ:tիwG;u^_m/_}| <>txoyjH}>͡9?eNo㝰<(vNgS|tEcN6ק3oN=<>jsSg?}~м5?{au'N} } {ᩋ9?s(?5tsKXTۃtB>B\'95s:<=O|~??Խ??=ts:QWc??^h?>?h Z^ͱ[uWW~|K` ,z{\v%XK` ,%XK` ,%xV{8V,% ٟu)/T~w~ /:յnt^t~~_<ӫWӗᷯ6ڣ|o.?{ >sW㧆Z5?54wO>w.}h@t^A{܇/=SчlÓsWwޜi߹'7ӇsZCs7|xjxʗ9ԇq~ ϩ>ӟ}ӧ/sSuQ#_МӵntxP߹v??WۋWħǧ7ׯϳ>H=-oye=:wc+w">~x&OvZK` ,%XK` ,%XK` _͋}y!oOCs5KP>shlTAr4ҧWQ??9yWշޜ\ r/Oa}顽м:ꗯƃ|txt4ׇj9[Cݹ~y՝{?4W3WC9ۣ|Zӫ୾}|=9OOhgR^O=!>ԇ_=<}|4tEs{ZsOkON؜+OݹO_O}˷|zի. :QCzWWO׹sCP͇K` ,k{|dw%XK` ,%XK` ,%4~~XK൯}g|g\>S /=? art|5cA/?T©=t| j:{s}u/:XݧSw{邏[}[͟^z<9==[~[ ss~jzG]3_s7o߹CǷל/ă'_s~j>[yr5wN;su>^k^k:j 9}NytOsG_s O]_>tay!gڹ}~x>>:? ]h>"?{!yS??=jo>\K` ܵb}b;XK` ,%XK` ,%XK' /%Ko}u'|oo ޸/7BļGN]|`1o]~ͽp"9_{!xO Ctм|xjzw|A^ݯ>L2>>9ǧ7<5}!>'w䂧Ϸ}s_>_stj"^j?_s5?HW]t[x:}t}NM_ιՐSytx=GjX~'IGڼ9쓃x|C;om}'> !磫~J}NG||txjt!o|/|9Kx|aOs<{C ]v%XK` ,%XK` ,%=M`/k/%KW~WKo j/?Q{Ƨ篏wBOK0}/e/ģOx꓏>t|taPS;gj𓋚ԧWC}>@su}˓# SCZNwo5sO}s|<"s̋bNoO>G<МN_mߺ}"?WM17WC}|zsX>WߚQ=_ȷh/5]y^Q=ҫo#|ţsN4W˩|*}>;\U}KW·Շ'<^ tK` ,^lOn^K` ,%XK` ,%XK` b~Xϟc=v_B/39^v\gO $>^@/#-Ǐ=>pQ} s<=CxtmOm~;'oyөS?tМ/4|*/`uE<~!s']y|C|5~k=ʉt1;y\߹A:w>W==xs<>9l=OHgA:4/_}j>xOgCOwC|>[;GCEҫoa}ҫ{W~W^OOМsż~xˡϣ;?o ]v%XK` ,%XK` ,%=M`/k/%I._/Ћ /5>_{ݛ?yyC|uyѫ+>۫OW@s9֏ΜN5>|uC~j}t9\ۺWuƓg}Kg.">ķGya}򇾷м{:oMgSݭW[zӜ/_ߞ;>Q~ysxkO{x_|}Ndק3ws}ϓ/?Hߚ?ƿ޼~@4\ NK]^Uݛ_ys|sN5|t5^9} A:=y5z;y^H>?ؾAs !>Sө;.%z{}? ,%XK` ,%XK` ,%p؋{K` ~Ļ ~B/RB/;Z{A߄x^O<2ǿUÞˡM[9<| ߢ9>~΃9~E|>'wsӗGg^~kj>P}[>tr}3_^k:9Ν_=ӚNۏgayk~a~ >j>=}Io`u\y!OuE}VcK[~x|Xw=ؾοXK` ,%XK` ,%g =w%nu{݅<ːwc/5 >üx|藧^;7T⧏W||-=>槗7A|PT'Ay;__ KMwkԧWo?}h޺| _OќνnA}sg>>wy{:StOH7/ѧWߚ<|z?}hN__ՙӛs/>3WujϏTCՐԇo} -}h^t3|{=js腭w[\xj%]M`/'s/%XK` ,%XK` ,%i{}O?]{ ,^<__t3/2|}^xDosS;_|K/Wti۫gn~57мjB<ȷ=a}N}sG{M/A}9omOtw.>/ǫ^}<>jsw/|?j_|ۣvn:9|әC}:͋xW:s~jz?#ws~jsW׏WW9߼u_uW>.O Oj'9?!~4/ʣV/%j{}W?{ ,%XK` ,%XK` ,%pO؋{K` ܝ~ǯCկ /K /2\kE9?}z}_}|O>x=>KjjBsqn}z!}筻:|9/#[C~Oͧ/ۣ6积P߹!>~>}QSa믆x^:x|}>|@|~jȏ\~Gm>@7~/9?.V{ D Pb#*Tˍ!J AA5N HE+S"6Q$J*?@W`5.h -;vsKWqGc<}jOÖoԐگƳ?>N9^맏Tӧ/>4G>'}>W%?k{Ր>k}:5Ϝ>1;ǃo?y w'~Y;4K?Oxj礇xĜ@صb{> 4&@hM 4&@hM 4& z& \{p^|%/9tX/35/^`y1(2C͏N;Щ!ԷKW5}I|^O|Оv~^8٫ӹwN:s۫tP9oM6A:sh\x>Ssg_?}76ׇj|5O}s}:W ك^xy:[_>jW߹so3׷<`S~j~\mY 啕yЛC~0ux|'C~c/<}z:<>Q[o]Wx߸`sjs1_f#bj<֧Ktڣ/6&K}}\>ޣ 4&@hM 4&@hM 4&' |нfh{uoq*wuת2#|^i/=ķ/uj|H\^{!G|oNOWÉ79x Po=!<>C<9s5~kSٟszoΗ٣vO<~YC}:9;?>O ܓ>t<-}9^x| =\ !ׇx[Onaγ-?W_MwB>46Wm_\EMz:<7>ߘy;߹V~k_[G^黇!:^H5]Oq>}:5@8n qD{&@hM 4&@hM 4&1O/5&; :*O{ /3n%Q?^I>f˚io7w^:0~Ɂk:ϡͳȹ<\=w{!gn}S۫Gm:Ε~Yy||tt頹~ #|%t|Lo>D|}|璃:#zN_G}5s'}<~}Α5>k~!>L^}}ׇw}-k{s>}j|<C?/|5>ķg⛻:qIJ4AӧO/5\K.YT~(? SuN?+ϫj:bhM`W]z&@hM 4&@hM 4&&{M <BnX{pK x|Ч#ytB |w:Ds>tj|<5C|{sw|'Az<j~j:>O'y<>]ރxynz}9C܃C|gCgW~j|<>~tY#|s؛{`ԹW7aI'.| ^Cs畫noO}|K{֧3_lM j}\@hM 4&@hM 4&@b{O?^ 4O/}Ka-ٟم^Hyqᅇ;net||!$O^?dGg6:>|y$/k?٧棆|9_uxdLJOsuA/ g?kMSۯ|UPO@>j:2OěW;7w>H7WC/>!>L/sɃU<ϑӛA'sj/}VKK4{9/~1x?>ԧP~t!?5^G>xj~7|}CZ Aܓ|<: {9|<94{_@h@_l'7&@hM 4&@hM 4&,޳mMu|3 ַ.^R{ 5^ s{ro񽔱Ojzf\ dC| ~"]ü'xj|iU'U3ywB|>~>WЇΫsys_|ǃ3џwBz|׹Og>xN]qgQC:shΟO>-x|yz:sϣ>K>Dt}_Mw/ɯگ-<88XG_}O<$~㓘sjMg?k|M 4@_lOkM 4&@hM 4&@hM b}Nh>C]x뭷.$o2_Ki]\B~5z1OϾGN}z<{霩 u_;:1O4>z.CmO9H0y|u~n7A>}5xCs|U<йx|'/}sN'eN4sMK̹en/~C~;N/OC:?C:};GROOܽ,J> }oKuLs.>t%ϹٷO^K4O9ăxP_gB|ȇo/}z~<5TgsCm}=ԉΣo9h/C~~?ۃGMa$ ?K΅y>4w}9> o͝WHg|OMsRO9|ҫ!|uWMԧw!^;ć0Ӈ/sw}W\q?O]j[;wu!>N W~Mg~ 4& =hM 4&@hM 4&@hM`={&%UW]qe:j//H4S{١s|?/SOK}tWU'ڇ/U'Gɓb>?|osx/=>4?j%>Ox_tΗ-1ϑ/CoB{?kz|hO}N_Wۧ6S?c͝^">9\K=s>in_sW^.|:ߜ:5ޖۣ0Ar=>~0ٗ/ae]egpK=^g6t}7W{٥G\Om:9}x|:M={9Lg>?QWysg?x&_:s̕:}sg>~NG~n!_ĉ$ ?4_a.g(yy+AWw>gz{Cz:}z}>S |G}c[ni;sa_DttC7S?9j|WŇs^bhM$=@hM 4&@hM 4&@h{@_lk6&?ٳgҿYo2^Z8^X'ět#t|݋K:yK^fdx&I|W'_mħOԙ'O?}<䇧˹}jz| sۛ|5>>?5|>j->=ztɣǃǃ}P:oۃ`3|>^?1uG6pK>?\YsϜN?>|5u">=O~Krw.t>zϟ<1sܞs|G߼@صb{> 4&@hM 4&@hM 4& z&w>}zf/*>"^\ԹO>뉧K~{Û^&_|Rۧvn}>kC/k}|xg^>tp;OΧZ?䂯Ig9}Շ|a>5?D?sCjRC1|s9>f[@׹ԉtI_9Ǜ|a9>4WCIͳsK<&񵗯>߽B}'|9{!?5t~xO媟3G|০3sDv!{棶Ojj?dz/97O/ωo{CmG/6&v-޵OmM 4&@hM 4&@hM y}_^ 4]^K^Net'/'jx&_mݪ>#*ǣ5D|sW繶ړ/WO䩓<搿o:^"d: Ϲι|'CSC}<C:sz Io4^:u3y>>|9}>W'N}7-_<(Wۣ>;sM~~x>o<|t[y}j~Yg> .5:G5u篯B{?C<:}~z|WM 4]K/wy@hM 4&@hM 4&@h{@_loM/+b=y[g=kQ_Dxyu"/8tjsx_. 7>_h'}SOGos{Ʌc/A}:Ws<}>?}6>ԇ|/yy!?57wGGo.':sOhS⛫`OC}UC:5ԇ|xe~"}"?{.ўćx\/|WN?}Ͻy}>k&}|oԹwW./Oi>N|OI_:Ϲsg:y0yY;<ΝH/ќ=[hAs|Ojg>y.#I9^ 'ti?ģsμnGs\搏=3_s5]ܫ9tjΥ'~|> CKgι>_5Λ>oF\s[ou}|BfO?a;}']tJ:M 4@_lOjM 4&@hM 4&@hM b~Vhג??\xpp_B/'Vq/2{a0x顆瓈kM}ރs짏ze_ sB>ȏNmh.g5h/uΙOS=MԙCD{x^ft[{O:'kzs';ogzG!St0Gj_%O5<ÉOeKMƇt0| g~:|zRO_駟~x}>SEoj~y?}z:55G=jQN:ٳ A}}'_+x|x0uo 4& =hM 4&@hM 4&@hM`={&nery{~\%bB PyE <χ>k}7Wξ{9O?}0SSwnsDO{x[}sDs .C}\e>ꜫ=!Qqpe{O}~ɾ>jskS/ߗm!i/>䟵=&?9;!~s3We_7ԉ|_6wΜ;:ujw>sh_?1;O'7O>WC~jtU|\m/3=|ӏN9 'zs|}W_}ȏBs碃v<oK?a~lՙ|B|0㓈r~<5!qs(<33ן΍/w:}59?*y<ޤ}GxWÜ=~B>| ͡MTỗI~>?(W|}?G txy2W>g}:}?r拧|:95z??^<1 _-=w>~9䣦wѧSAէS'I/OC+6&v5OnM 4&@hM 4&@hM i}|OחK/]x/^>x a^xa3dz?.:t>P/s}5tj>j>М/yA9Էf߾ig/>g9?٧ӧ7Ǜx0[7WO<ߤ=ӧWo?߬o烿s0c>٧3ss}>?>0ϣ^x駏Nt}B|>5?C铟}:{y>| sy΅>y.SC|΁SC}?js|WU髡&O3O=|\|䠏W|x0y< #__m?4pgNjttObx e_ ՉcsO\.ʾω^_M6O>ʹ:ϭ'9y3xОĜ{s-+bB^Hg~=4KtOzy.yj:;/_sp:9@h@_l'7&@hM 4&@hM 4&,޳mM |#Y䫮j*^j{h^{a>Cňھ xzq?kD0xx0jsN}s/sĜ>9/|}jG 3wnsgO?k{O^~:t/}>j:|}/>'~jM?sMy;5>yܛ>D|̳6w>碇ӜC{N:(gϞ]Ї>t!gM}9~@s{>>/|s'G_srNsU>_:5?bhM`W]z&@hM 4&@hM 4&&{M {k/TN<^bx9ͽbB/O /1m| ?51^șۇ>?Wio'oO4Ƿg{C}z`WK>O.}K.!]{{y_>E6OO~ֹ/ugNaלLI?1}'}9d9Hz9L?5O<:?>uY^>>G|}眉D{Bļ>=ɣ>>>t7O~{ٛ}s|s|So=4Wosy@DzhxPԫuD<('<4'ڃgL<>{_F/x >XG ?0S{/k|L:Y;74O}=|c:5^/;OmkO{%ӛO!uj s~}nzs}u?_N|P>/>1%ߜ\?kos{~[s{;Yxj~ϥ>9ԇ9]O}|7x~m!;.?5=־'L}x|{䜉Q47~{9+< Ltx|O9993WT1޶,Ν;+\(|{xjH{(ԉ|xy^taeMg[~f?Ϝ.}xY9y'~e~K:t~^o'ɹ/x^?=ҧGmo㧟ӧWۗǓ7o)|⻟IW_m|CϾ9Ϛ^>3?88X??Z뮻V՟΁?!}C}B+>:_7ϧos@h@_l'7&@hM 4&@hM 4&,޳mM?'WW}*_җVePC/5xv<5=L>웧-^t'NX祃KvCӇK{APs܇|ӷ\?k}h_eNι~"t-?D^rN=>hsΕt~(75?|=ɳ;ۗ{!9|t>ͽߣ'7f.闿s_C<4O>Y;4j0W;_<@t&%ۻM 4&@hM 4&@hM 4=O/ 7}Nn[BP^{Ex9ׇӜ{у祉zB/>zhP?yCsS>3Ϲ&~/wų9wn{ ='k~Չt3w>s?|D_M|jh=h|hgo_9D|Wۗ:L^'/s?O4Oot5ڣ||_0gM 4]M/w빛@hM 4&@hM 4&@h{@_lk7&ʛ*^ye3WC}/-|g>K㗈e9<}UCs}{̡Y;wRR|&|tjOr>߯ܛs>ͳg|Ԑ^9ųf_ SP:'DRCs䩏'1穟yWۗhoN:}0|=}9wu"z9өs{)->5rOW ?%?k>ٗ9KGy >u+x:>4WoLsz^Ͼ{y[My}[ 5^ESO'G~گr_==x^xׯ|S~&S|@}z{ >>Oin~^Oc^lM j}\@hM 4&@hM 4&@b{O?^ sgΜYzq 03Bԋ }/0tҥKs~jy$OO9ԷNj}QGH♧~Ro}e}͓sӇׇIv9>_߇ ۫}CB :}5ԗM~4Os:<(:}'@.{~|kO_Mŧڹ|w%͚H?UOO?shOtGE>߹t!]bޟ>}"^M}zsK›ni{OaW4Α|ۗsuM5ă|ADO=a~Oq_lM j}\@hM 4&@hM 4&@b{O?^ c7púoxziᅥPVSMEBK /,rs'9<ӷO _?{޹aՉ_?r?_|UO/oD|RN>aǃ9}">L~9~=ߞWCݪ&5|䣯tYi;cQ {LuN>aAs}>/s9OןKn@h@_l's7&@hMdJB@IDAT 4&@hM 4&4n??^{ P"_@yÔw|2xΣɾ@飦̡x\ I_<O_ȗ{!?|:CR'71s~OOWWC}9%E<<ݗ>t>iz5:O<ۓι:rts7ۓHg:S~hsՇSK7ݏ'>?h9 ݄LHgnC{݃?ٟQ's㹟ek@h@_l's7&@hM 4&@hM 4&4nw߽yԩ^x utȹ z/=K&>e?y4k\ϋ[x^jIj:~ɟx0yKtN&_[Ny}W=yO|#'yzshԇٗ<9O>7:H/=9xY.yxΑ< s~xfz<}|hdȗτ|OO SGmO$9=ăxpo:!^"^mŇg㙫K3GK<}|h/ć: ӧW^.j^(agt7?*Nz|g~!}t|9{@h@_l's6&@hM 4&@hM 4&J/EhM&_uG/Tȹ^B@}:/o:~3OOLJ.x0٧Oo>!_s\yt[O?^+$CWܛ5}C~^"?̟^Mg/9yO8Ftt٧w A|y!{eI,Otnw>|13t0@>j8wǽ@:D{קwbhM%}@hM 4&@hM 4&@h<>p1X׾/\/[lE^Bx0ٟ^bl}U$x|ǃSߋsR'Ost\YQCs8>w~9f_m?}9%_ >{'Kwn5^9~na>{]QC/WC/>S'oǜ79WCrOӫ=\ sC|>NڹWd>{\y<>Ds&?i\w^< sŃ|BA:Ns;9 K ?|;ޱooӏ}/9=j:Ĝu5_9|x@h%>.d@hM 4&@hM 4&@ؓb{O>^ |+ _ /҅|3WK'=Iɟɤ[nY??_7tӢ?¿^EDXk}|9僾:^C>pJ A>О!ɳʍO|§OMO=|:<5a賏~x<|:DKԩ~hrG_ }MGwNљCtYsǛ?䧆f}n|::?1uCHu%k~Λutf|xϵ ?.hﱚ$^S'M~jGNө|'Ӌя~eHja &/j||Y'o>Len|Q=?h&qI/'{4&@hM 4&@hM 4&$ޓl$׽u=lą'}{BO}S~+_o]UW]}s _W-]|`k_څ^>9sfy?d"J|s>xjHN< ICĭ9>[Cs{yu9f9{aǟ^\^LW'L?''9RGa|oS\xjsчg/z| 9SGK>L]g͡}W.jHLWk~⤣^:ǃxjzܓ~xT|/qۋGN}-|>_s ~~>O<8;x΃GoxYcw~?K>~QC }n|էNZx7.7LWoN~ >_ [>9s/y0+6&v=OoM 4&@hM 4&@hM Y}gxoo.(4O{?/_ٳgWd//-_?/w{po{N޶( / /qpO5>=7j!>~txB<91WׇxP/9ׇ}S|9?qjѧoCso9tY'gw.~Υ=9ׇ>.kɾڹC?k}{'<݄|&tiχ.1ɚ9ă˾9C|W|앗MZy~spOt~j?5c{ 1{BoŸZG/nO|5S;?|t%&av.'kn}gN>s/=0||C<5ԇPҙaS×g^As?j%:>>=e} 3txй?ao%B: }<|xPW'\^9v.=t@b{? 4&@hM 4&@hM 4&g }nx o֢B/xΜ9sߍ}C}w~wza'?Z>\~O}S C^ve? P_{ x%[{W>5Du/E>GsO5_}.SGƃ>?}j|h@z5&?|~Y?>LϹs^Λ{9>_}{e/mp7w_uW?}>9tns{!9s矼=Qy@cַѧwn5,y>5\fu#:sCSN~ar݁oZnΗz5So'_lM j}\@hM 4&@hM 4&@b{O?^ %W޺/w G,702KZ/c?ckя~t>]?^0 }XOzW} 7?IOZЋn^Jxsp-'<^OH/1u^>}>jshf-ԥ>On|rPC:|{:uWs3OC}|YA{{&HO{o7EƇ쫡.!$'>9ϗN=x9ӷ&\=ԇ'9>\:}u"f^y^<\s=D{) S|ӗ{~؋?y7קSÉg4KRWy1;>u_¯}k Kgڋb:ڃ}\xtP>k}_^ 5?:ԉ䃟h~9ģ+6&v5OnM 4&@hM 4&@hM i}|.%ߕE_^O}SW}lӟC~~ͷ /g,|GzΝ[{{ Esyya>t>5>xCć>gxjO>}|D<ޜtjzTC|ȟ^|}<Hϗ⩓gO6W'ʑ/W讀>t9h"xy>:hy!oK=/yo9'>?s|7p_WIt^SCDsy9@b{? 4&@hM 4&@hM 4&g }n8JW_}=O^G>r??a{ _W,W}/W}W.bٳ~s*G@+_]| /tދn/]߻Ewŗ5^t7=ԛs>L9RSrA0yP;פǣohNӇaxt7ׇj?|o%^t?{/1 <5|-o'S9WO~S=sSA}h]?}ԩ}rK>]??XK.d~ޜ#ꜫɅ?~}䛻7=y">nԇ'%˾}&|+6&v5OnM 4&@hM 4&@hM i}ϻK"f&"|3랷rB~>_ _ª_ekߕЇ>tqw@/{@^Jҧ|s2%^Wյ |~N̿/:۹w_^ĸ~|ϗ݇L_5>?|{;/^bo>G/y>| :3LJxYwsxIJ'>j!>jJ9c/}P~=G}jO^tE*菊t-V*QKleiB5 RL5'4  H*! 5(A;*l|:rgk1qq_3>unXC:m]\ KC| C}b}P]Cs>}?y{҅ċtE 'xP>͑/zb|1l|w}!x7W~^O^㛇}kVg>80t{ub?Ͽ'l`Ɓq`Ɓq`Ɓq`Ɓq`Ɓqӟ7Ӟ5^ Ox{ԣ(/}KQ/ C|7wOw_+}} ́_~ʻ ;NݰMM/˫_stECϞa>O9tncyX^}!(_Wwczbz~ëokz^xnn뷇XX?1l<}͕N(y;t~(ͣ?u(O~_|5W,ϼ"~u1oWx!z듷X;E]޼buWw_{^w\"@}9/i{Y^xn.ؼΑgy9wNv>P]?}uWoܼ}ׅO4M0)v`nl7808080808p̍c|sq:FBכݬ zC}n^wxW\O~›niwӍn7܍M>k{%/xίX:]^_i_>淎'LJx\CyzxyV7 sUWs1⫗/_ӳObh?NyPtӃG\>'!^}t ^߼]Q驷O|h^/x COwukTC|{ 8pVgI9Ɓq`Ɓq`Ɓq`Ɓq`Ɓq`8<98pn,Ȼ k솇M7 yΑ@yh^g/|ֳPxʻ ~[r/~qnv{͞t;POuOl\҅&(>ToL/'.׽X~uh!缇{U_|19b-TP^9bsCa:}xAy_\Tt;9t<>uPH^yޫ9Aus̗ǃxbBϥ{ wy{O 鈡Pzɷ{'֏x\믞|yP]=nq~~Ν;R7|k团~(_:kO]۫4ϼCu9ͅuzuqAǁq`8̍gq`Ɓq`Ɓq`Ɓq`Ɓq`́}d|;n(@gnBoN\rb zbؽ/_?ycN7޸q.7?W7w7ŷpù՝ELOC}PNtǷ|ͅbbbuvu/|//=S[:T>1g;}u_\4Cy7'qҧO\qg>W>uO]ăxb!tx!=E.;ԷÞN|O][o]??b7׼:lXH|>h!bz"g3_/CÓƁq;07808080808pgqƁqq`wcy70ܤpxnчbb}P&9W_R/}ӽ޻M+˿ _e&ww[C?Tjlo+n>:҅;>WTCoNWijz7߽˧s+XO3O {N:x>gbsoLysNu180gŁ}VcƁq`Ɓq`Ɓq`Ɓq`Ɓ#q`nlɃct# 7<7~HqIVO]{Uכ#=AZkvu]|CntC7u{y%퇷y}+_^ꐟ#|\}PsC:}OA<~>@u~{>Ӂ}uygklNу{_.x[C|qy!>y~yB}xb(̓'Ρkx͋!hO1}.G}b}>O/VsOG!ԯo[_wZ7Q u.?!]>΃Wׯ.#xss-??[Oz!=:@>9t}Ck,O]l/a7^W~yzߘ>xSƁq907O}ǁq`Ɓq`Ɓq`Ɓq`Ɓq`8rsq,9n(@gu7'zOvsU_~tCO?.Nay;G솉 B ?-ƅOySVܛnVӅ탏缻:]uH1]Qv}!]ysW/Ol}ݷyO] ˓/k/}'/_uﯽCWӁtzx>sc/O:i"Ѽ:T/V{Ӂ{\|y3W5\ń>o/sS5.Vo^!]qQ]6/\u^yxt[ɻ׿~-;G9olN|!Xy_Ç;ī^cmOlƁq`Ɓq`Ɓq`Ɓq`ƁqGǁ guFM7&wîN҇퓷//nxEskAzxn`_}իe/{B'>+vswvַs=+Mns'_յ4b{Ӂs~u}ͫ;ćcoN6_l.~>._^b>k,ߘFGS/{ա>>zO y.'o_>:x%ytEzWWWu}_^~:wG?CPӏ'7uӧ.֏'֏Ő.kLh>~bWu.ұ~Hʗ_u}b{>C{z.Wo,OG9΍'GG^XO]>T;80Ձ}Z=80808080:07ϱǁ & _n64WēWt^z饫E =!n`ƛs@t7{sМϋ¯kfP9|#Wʍn;>+nZ|Vp)O"][?=uyHGuNXKO /._ Kv.=uϏXH:#>g_^l^c_7}헧JO={ٿ?P}1`b}\1=(OG^]hv|CuQ ;,V7YuyGOܺv|uW/wuzbϷãG_]O~pƁ>OpƁq`Ɓq`Ɓq`Ɓq`ƁqGYvH7v^n<\>o稻!S9nڈm= ~[3ߍi< ՋtߺX7䡹Z>197x{VW~W?~Gwot{|si޸yxb~G>1[ 7_^L߹|K/'/X!=;^tk.u{.OC<||t~s ̓bhnO{"ͳ9>|> Vyy>.|7~)_/O\}wh?Cx+?.//,</ C}.6_Vo :E>jǃb{ˣo.>g~'X_~WsƁq:07O듛ǁq`Ɓq`Ɓq`Ɓq`Ɓq`8R>98pMM޼pM^W9nLOͫ><7.;6_LzxpC[nqsաs/3X3% yntn>u}np{귏*~ߤRt:tau~热5o.T|bGW?lӏ˗'Ƨ[SWWss:ԯhsБOGWC<}b|1+`kz/˫}ӏ׷w/>OCyA^~('[~Q'llO}>(_k,o>oxzԣ/x· /-~?Zу>>9|yx8).,'HbO_,G`}:Pʛ'/.Cb|Ջxԛyk}:1:E~S]Ta?:=Cowsv}uۼ~b>ોn9xt=wK{?`ybst<7~}_ C;>zs-WU '.׽'o|B|'.VG]<<"=O\ϭwAq`Nsc?Ɓq`Ɓq`Ɓq`Ɓq`Ɓ#s`nlg7zF>z{SB|svz s!]Mu|6P]7RǓߡ= &y:·́>u:b(o|_WO~ 馛H,+~޶V&7t[ ,=Wpǡs/{n|1<:n|]:,1'qNH:?}P>uy(9q<}ECs㩋_Z _y}u>b!yt-<]{W_>q7ߟ`w֫=;TW}y:=Q7Cb}A|oW/qt<%/rOGD}=ƓW} 9!:T/Oӑ{(_lnuwu:g);78iw`nl'8808080808pd̍#{sq,;F nFȋ`'TOkGy4vSО˳yxb|so,'n ؗ!9oOO~Wߝ>w^{B}_~ oF?+v؛wusvs|ᇼң|塾wާtEu{W{9>t[м],_X9~|y/1XO ա|<{A}!=uzEzw/:sO].hg~n@/ ́tvX=ŭ{;ա>Ï}cvm U߹}N<__\u|s!_7hO|Hҗ~}w_,4^nvo}^.ts܍䖇O=~o:|t~'.3C}}x;?}ؼ'5kO1^Qx8X]X's}_؃H\usgŇ!ۧy~>|߿p.b<1>zۃ<7.TLJq曧NyzE|y:8iw`nl'8808080808pd̍#{sq,;Л b77<=7LJt<iԾy!OޜƝt׿5|F"<}ع{<́A_nZ.I7}gu{ݪZys/s/1<ѫtх͋w}<>R;>_m=OxoWo#OOL7W/OsӃE|zϏg犧\('鋡>!_ͱ/OW<|o>4^\?w*Nm|İy}͛'/|ӃO^:!sOC+=sCyHP+C' 8pZۧ8080808080scH{8n:Sw_}wO?|0q~(k_#P䅀~u{7>|pN|S]|yPl] ɷn/_/._)n?>+Vgw.|Ӂx\^=غ~><1>|w]uzӵaWo=İy۳t/y}80vi8080808080G>>βn(8c X^of?ԏ{Dzb5ƫ|>LJЃxb7yE}ׇOvO4ύ5>|VwnG+8sމw߽b7{[~+_Y|pޕ9PWLϥs]zѼ~'ˋ!}<ͧCt͑#Vt]OӺ><ߺxG,\h>bsg.>//g/X Cu'.g/_~+Oyxyk<~<1!+/[Ρ}/| ~nag!W{WDW=>ڿӯOO\t:+V#V͛o?uӁ뿮_/첅@IDAT=sCnt.~{s~嗯 nO|bs|Zb~G^\9EQ7x˗勋,__~OX]OӸ]y믮yxvGSo|ա'<՛/wቡ}cؕ/| Wl7~ݛޟN̳J^a^z.t!]h/>CݍgP};<7C}A:3X;拡>|14_::bHL<is`nl'6808080808p̍#Yr 7PwVy7xE<ՋxM 1&XwPGGhUx|Aη:4O?}stvH>]-OnCbHHϹ7_^<~zӞEyӟ+Xq :Q׿?񏟘gC\TT įz/͕W>ăpk"}!AʷWg<>s~s/_]|w|_\ͣG_?LJxt׷C|:{_v.'7n<Ν[׼5 /7>GCy<=_r%K<甇Ot9ҁ+GOG^`؟>q`Nsc?Ɓq`Ɓq`Ɓq`Ɓq`Ɓ#s`nl@o(n4 sF݀ 7kOX=y#tkn z>'wyzoųՕ[Au|i.T/ғg<9ayPbWgO}jQ۾m᳟߽sGnz]-'Wܛܻ;<6^ ?w)>\1Ә>}x{>uh(_T4}ˋR>}tĭ/80gŁ}VcƁq`Ɓq`Ɓq`Ɓq`Ɓ#q`nlɃc䀛 = C覅{wI_z ݈/6^c7pݼ/6y1cΡNuXzc|y粟k_[Хg<(_4y{{;v9b87GXQ_n$ˋ7OCbx|Q׏g?J{>y[x./fwn[||3+v{w[ww\\|u~51ħ嫯Px#ON|c琇t.V7G1}_\l?#aUS~_9ćx>E|y|ysn~|u}xwOAbEl^lysvuzt|dQof-ܢe!>= غ؈'|bb:P]7㉋Ct9!=OO s^}o/o/y(ox6oG|̓a/M|Cl;X|>A!7>uyh?u|O|^wsvqYǁq`8m̍fq`Ɓq`Ɓq`Ɓq`Ɓq`܁}/8M.??kP݈pSnLө7'tͣ#/oA<ۏ'h£STwn1߾ߺyŐ~|x;g/s͑x鹉JW^?>C蚣O\Gx͗oytͩއ?ROxV'=i-?}ntf&\`}ç/_|>To코U=x>ٳ˛'v.HOC҃+x<{Sow} 櫇ķ=}OHn{+ߘxh7hW_򗿌dc{~>qKuzswsuX}o|z"hzcsw|x=/uzPxpƁ>Ov580808080u`nl;΢zիֱct37Endqܐ0M |y^1}{CΑ׏́}7/֏':nț'OW^'Vo|сxnPGO\~_Byhg=W\ K/BN$/nbwi?XTE+wMoZub(<-!^աsRSTt8wJ\_ƨ!xS`ۡ篾+oAtoNc{Ctӣy//Cy˫ڳs~zu>ڧpƁ>OpƁq`Ɓq`Ɓq`Ɓq`ƁqG18Л ,Ƃxw vqӛtP:qRqSK.rCIzt!+/.__ڧso<=o'PHAZ^{nz._и 7ͫ\ ?pW"_О\yѓ75-1]غs9:/PS|񠼿bksC>βn.xC|nNˋw{WGL-ҧ'Sա|Ziƶ>wntxwP_ao/骛Г]_/^t}E;,s4/ա<[ǃCu{Bu7xby4ݹx\ЅE5?80Ձ}Z=80808080:07ϱǁ gt㡈'FznJ+_C7DWG_u_|#'O~97ׇO⩋ayb{!qu{ny}b!>ҁwH/7[͛[]1׎o[ouO}?)OY? G~dnJ.~,}CyGV_]Wytۇg^t=}n!\yz[~1>l5Vܹs jߣ.4G>P޹GxߡsrN>xPK]?b_x/Gyǁq`8̍fq`Ɓq`Ɓq`Ɓq`Ɓq`ԁ}~=eTpF7$\(k\}Ѝ <;|}tܔwゞbzEz룫N7_}1wkwt?ۻsx[~On<_ o,"γ_l} ٷk^ٯsttO^_D_wIAg>sgo\V7~7_uU+Mn-wk챂 ? wu>{n\+7ꐎ~7ƃ>a|cj~!^=Ch?};PQS ˫xOO'q7|*r- [{X=w})S/_xǣgxO|٫ǁq`8̍gq`Ɓq`Ɓq`Ɓq`Ɓq`́}d|; nT@7ܠ37"z|tۇ>75/.t\'X]^l^7XT7WzxP{#ׯ!>.sq̅tw^tӕw <1Hyqb{'Tgy(_wEOйҁ!qy;E ݬc߸|-CCG|^u7tCs%'OWO!{auڇ;1=:CE>YZH~/~梁oCǓoL #;wu`#yC:9b}ꈡ>H_LO}ApƁq907O}ǁq`Ɓq`Ɓq`Ɓq`Ɓq`8rsq,9ftA즂n2'|Q=o]?]Fqg~c{7Xqo9_n|ꚋS+<^噃=w˧w{CyH}ow; _.|_ws+DQzԊ{[\|߸YEK.ԇ'7/y'.:gs>1>~1'_=uw1=<޼yECxE>>8??;ݳ~κW980́}ڞ;80808080;07g7ܤpSPqyuN듧//V'.|!"C$OOӷ>pTg~\|~:>9~bz%<"PzyaC}\y1:Cop'Vw^buy}ԡ:ģ'st_[ouQFO}_y ?я.twk~i7??sW{ܺ46ߘ>g'oNW a<9bu||NX]}W?;_v{cw>uȯwׯO^_җV5yO| 뮅tVpGu[~oⵟ/Cqbu}y>!>͑ӑoCVT^Ɓq907808080808pgƁctM7ꁼ n:<1,ntzO ,iqNw}y}Оb}>u|7Ӻ9Q:|c:P]LGnO|Bz|+n|Đ>ho:xbbhΑ7GK:ÃAu1w }bMOb׾U ~.Xfwo?Qw7w7wޫxl_Cylx'?Kꫯ^xm-/(Vp~st͋[.=¯)_>ɋաω/uy1yt~xovH/Ɓq907808080808pgƁct#gwSʻ Vn>ݘh<^o4֯|Aӳ/TW8<GyP_ _97+}@u:7.g.76OG`Ӄ|P 9أH~9wϧC=i=moHWi\yEzt|ɗo>O|'_~yP3O~cy_߱Mt с橋իwsG1ԧŰ:i/yzPt鈋tİG9%o.280Ձ}Z=80808080:07ϱǁ@o*4vszↃ+nN>~ynZCysĭ Ĩۡw/yV|{ث|o!qyo3Ӄ͋U1CHڛsC]WOl>=|-)ߞxs7<T\uqx@]!]>М֛7ϼw޹~w~_-??Ѝn-r|eŻsċ|<Ý|ӹsS;}bByz;r^:;}<7W OX?a>+/ KO.at!@y|:F?>bHAy(o<('|[ǁq`8̍fq`Ɓq`Ɓq`Ɓq`Ɓq`ԁ}~=E@M/owӢ7,BW]h.]"ޮ_ލ.x~a!OМA|(/;^~14W 7ϓK\}EW:>4g5g9P?b}P_x'.C!Sg1=1<1>]sEu{Ͼ}wH|z~w^y+~S~%/Yf7>G>rŻnzKV➇> wsա|yxw>gWﵸ<ۣ[Ӂ͓=ŧSTŸ韮ԛ&|c/D'VׯOBgwWx/k_}xVs-/ÏS}/.~߿(~W=o^_݄u=yʿկ^oo .-wwoy;z%vt[W<45n;>^QקA}ݏ@{<^L}+_Zϟ?_B|WW ~Ǔ/guy(O“twHу͋;ħ^o{O]_ݹ\/2tAǓƁq;07Oǁq`Ɓq`Ɓq`Ɓq`Ɓq`82=98p f WOAs`SLJΣ1xbWO?ç+X|́k|wuzc{Wzա'Wzxbb}NO;x/=:OU_C MyH~ZׯN_|>wX=;~yC9?2wnE/Zoq7߼Ѝnwqn^orݼ/}Q/t괮C{7vb/O*w:ԡͱ/}Eꪫvm ?/G_ /~8WCzڃXǓ//aǗ헇Asۇ?8is`nl'6808080808p̍#Yt 7Q^Cc<7W3G:]y:bt/Oyq~y5ƳULOXC =pW##VwUO].ƃϾP~u{@yh^cy1s[wӡ[<=۷ɿXz_.?]xu-t<9+v o77W^ݍnyEy>œ _WnP}v>Csa tn1sέ^OzғU 't_|pWo~P]WyʗNP]<]|<"ޡ>5x/{NP}E}?8iu`nl'7{808080808p̍#}sq,:Лٍ7F7{kyU <)NJS \U 7_-B .@ !8H+@ $*qjjKӼIĵ$s {7{9NǫnTSOLGL?Ó#T>AM1ԟzbb?+nbC7wKVwb~w??,? y8㟾DӁSLϑFO9!~'y6s1??C:7Ayy_>tU>˺|>|hoCxί'MO_"tGOS'n5_3o>='>D}3>꩏'z??Y):Ǐ_[Vf7ԧ>xK+ΛMۿ]| މS_>s|}My}b<1=?w~w~3YO,4Wtm{?)h<|g}tʼn.W翼=x} Og&OLǼMy<$ә'|ׯ__s|1~$ґ/ց:P݁'@uԁ:P@uԁ:P@;@olqav 7p:;Nn>_Tϼz87>|1ϛ!B}g_&w/,?Cye,Ϸ>O<~&_oWz>*y~c__WyS'| ='͕㋡xbuzHyq>u(塽yCyH'bhM7Gy̛t@@olouԁ:P@uԁ:P@uԁu7wu0;F bgCyM>y(NUryF>祾|>;Գ#xt̃2wŇ//GDzyc}<}tͷ^ƙW>CyH\y|D?TxxoOt%f]x>_/OӇ??Z~7s<=|bQ̣?6W#<yu)u:0#Os˛ 7xcϜo&e^?L}}OLϞ:9M|.v. ͡+Ʒ/]<O߄=gN/Cͺ؞_<^CyUElzGX]/Q]=!>sf5Gybu8ۇIuԁ:P@uԁ:P@uԁq7wAu`pM72v eS,OsJoGWލn-[/ҊM'|rs~{?OW+;'xbQ>,_qCDu{!|ʼn銓⩋!vuan쩞񦼺Mst~:''_~O_ꘓǧ+P̹6uԁms7u:P@uԁ:P@uԁ:P;;u09n&@7:ܐțxt8<'||(o/1s՝GLQnBs'V:Ǘ'/69n!75|6G؜D{@StNGϼzsㇼ=an8ڛ{<ɛNOt.ys+o8Q:ybhxB{3uqCU|}bu}'ƕ+WM//}6̷8?̫ӃLsH_LWn|ꈡ}>H/\>h)/C{B~֏nl_USG$7s'֗hosBW[Ag:S?G곗xS:>]yMԗΏ=CyÇxtSo{Bts;RCy'~̋ҥ'uLJxPԾP]|ClyW+ա򕯬>򑏬3A7__,گ-Ѝ-~inA{:bΥ㏯{책G0oj;W!]}Sԗ|aW0Rț?: bu`[m}rݻԁ:P@uԁ:P@uԁ:>fs0O]72sCBSS>uD<:t~W|͕!ǗϹog^L7OW]^O0SLB}+ҥKwC='~',|G;}KMo:<~//.+~HG}7gL_y)u'C7yb8!ỹ{uCs>oӝRqΛxd>>3suԁms7u:P@uԁ:P@uԁ:P;;u09 y>e~G_߄5O>IݬO=7|L'SW^/N't県u~zxu<ҁxbyRoӃx:̼sB<υ uԕ /,M7Jw &?cȋƒꞳ:]}by}9%C >!]utP{^nхxwNwy׏[[_˺;<48quyKrooos:uja9ӕvӁ樧:ԗk^n`n œ߿bznxC}cg_7C>ճyyDpꓟ>wt.u(A<1Ϲ|Sxd룇/xd@IDATb}NzP9g^};Kn/s!zԁ:pX${:P@uԁ:P@uԁ:P8;{: 05n&E'=uHG 7^mW|(#^x<mO|D:xqHOP?=|K}}#OҁrӍm:ns=͇\ %}|tGo>yq.N>1]+/vO>)>=G1g}م…:y釹ў6?O^hoL|<:TW^]?>3 ?cG7|n2<|W?99G8_*8qb?nr؏؊?/׾sB7OtB17ה_r 'NS}MzxXozߦrq~s4_^/o^Muzx?oӷD:{sSGWӓχ}uԁ@ol'sԁ:P@uԁ:P@uԁ:Pvāޑcց]p o.0TPwA+zCPyb7.!>}(yy7JFoĐعY\|y~!s҅짏z"^3N<{)'o?Dugޜg<O]<>n^kזswkә>PQ|y隟tAzS=KMmx?+gП1=h|b<:P>1Nُ̫Cz G~d?gT8|s.8='xÜN^MOsg<sɓGO=c|u_<1]{ʋ7x~ϣ: /Ʒ|ԁ:?_@uԁ:P@uԁ:P@1zc{x[فA&j 7|ޤ~Z_>cfM s@ˋq~g~|Co?|1룃}W{=<(<{Au}E=_tImDr{y~y?ԣ#S^"3o)dc֏a骋աeN3s7G: >LyzP?}}xꩃ'O/<ןh/bu`[m}rݻԁ:P@uԁ:P@uԁ:>l˿_/^n@䍄5ٗ7 'ύ?7CPS_uh?ztx~B'<^7/랯zbNa_wԩU|2ʷӧO=IΛdzo'L>W ߹䡺X?}y~CWǓbap:AAyO}f81OhI~{©zb ɺyt`ެקN:xH#GX@vzc{۟`uԁ:P@uԁ:P@ucǭyaooow꓇n2ЁnN1t@}nWn義?|:^t.̼y ={x.?>y}L}{yI?tͷ'tx>ա~7x/5|nr >S"M8C_N9Sޞt!}gHW'OOLO__o{㍅G~ٻw~}}|?Qͱ>߼/NgX|ЕCOK4>>^׮][ԗ_~y!]w9bxx΍GӃg/}z=,}G̙3~H; x0>ó>9ta>8gyPUt>smK}rnx>s?W:]'>y_ 9'@@olouԁ:P@uԁ:P@uԁs =n7DpA)xyA͗o.ģO#䋡 ]1'|ݰ2sg.L>{4~<}x@yb0u1L'\}P>'.o~:4_=><=H^o֯>qp#Գ_[S_G}߱RWPx໖:rd彧IzzίXn B0O.!olBw}k ?뮕?qҷOAMǞ3W|<賗z"bu$C>|]ϼ]g.ǃS^~u:P&3!i#o>{@sxũ#]ߦŧG&O޹<}{e8~˹bH^yɛNW e]?Lbu`[m}rݻԁ:P@uԁ:P@uԁ:>f&8o* }n|M̋#yG737'I,n@?trq>ӅP3x0y S_LX?&t} KӓO:>P7'[N.ҳ{_Շ'7_oBYڧ+uK_R;5>ds{UKB߽m|-xc>>ԇo>D_]X>ys>4Os w0KG:Au|C<}Q 3\n_P4׼e^9~># ϟ?^Zh΅ V [9t:7O/ƗOă?ԏ/'<4^a轳zWO<ōߟR|x÷Op@@olouԁ:P@uԁ:P@uԁw7war7 ܨpcA Gb(?阋"nje^lsqcF_rDu0ͷ><"_X:=HOlԝ|:0˼}%=1GwoΣ+hn=/1O:}~g~|9W//tyw.19O?}}x0a@u쑱st2/VO9SG<{'3?tmX^w.z:ԯnzԁ:pD{:P@uԁ:P@uԁ:P!w7.9F nNbʻ5C'f2/V/Ü/vDzb@C:PNyל̻Y,x9渙g?uH71Gw5O}Փ=ӏ/o |qꥎ::>P||O}yP'xS7\?ti/}y7n`˿o,ޛy1dl_yP^Ӈs{g>- - /ؼgNj/ӧzV?S=yynb:ku/8y#6/1yOTd=w>֯.|>է>0u%3曗t/ϝ~҃t''C{K'k|~O[!bu`mݿԁ:P@uԁ:P@uԁ:c=ftp@_pw zϘΔS>./և/6_/qѧ 3/!x|߄M>=WCysrpg7%8k+м{gŧkỎ;1}Vmpu+/N|fd<Ƿ<4_='=%x/XOs~oOsϭ|,t~ 䛣C{sBu1VOz=z=|>O/1Tg.]y1!]}ڏ^2nQL;ts{~:P3'uaOzxg:~ԁ:?_@uԁ:P@uԁ:P@1zc{x[n ef䉺f^ߦ<3oo73 ׏nC 8Sw#R1us;t)<O u<}1P>x7G7u.]|PM<7^U'.o1=n1=}yK<ĩ}yu]_ۿ-ﻴꫯ/_6wqª`>⋫߾sB:;';s%s}b|<1]ǵނx>W;$'oy}yѳX?Y7G~:?]?(g]/\sW6_KL}:}/W7é&_4/cOmz[~ ,$>/u覎N7Q=<:Ouԁmu7u:P@uԁ:P@uԁ:P:;{:pp n.Q ԟHO<=y.֟a$s<7[LGlenбOO_b:g~S=y:bO=tC|wAr7:Ywp_Ծ׷}ܠxPړ>u}_<1|կ>%oOS9g3_CDz\14|p+GOL/>u7͡O_ÛO?bu`[m}rݻԁ:P@uԁ:P@uԁ:>FHpa:: >M|}Ѝ|8o~O]7'Cu7Oho{w>y<:'9٧fyϹt'מ'e=!b}<ԡCzIG䡺8S>0!>#;}og&t6/țsf ɜ=9'y|:x'LJbMh>:ԡ<'<4OϜ|O'2g_:t')t>y#1^:}~ts~:]u|7inhe] sCͅvc>\(OW9dɟtaOsA}+ƃ|5S./KT7W~yo{boty>b|$O=Cxbyq"9բ~Mm߹7 ͛z֝w3|.i9Ay<:bzb|H> '\=̡kԓS'҅짟uq;s@ٗy1]zSǞ.}x]w- wO{K49.g?:b痧xǓXԇաT9tCx~8͡'r{y2K=sAړ&O.7N+ց:PŁޖ'=@uԁ:P@uԁ:P@u`9}@84ং8oJ 7HcN|>"saLy'K./}/26gO7gl.G^ǣ罣CC&_ gls'?_7'u1Ax<1Gǹ2N__ePڛ^">䓟1~)Ɖ( %;~<B7]g>7d_Gfu㜣N/IL_&~;ON{O]uh/|yԟuc

    P>1^{= ϒ=c+7='>ŗ7G0£N_:>^oœOCzO~##NTsn7E_ o7o>ߟy18qҷg RO}:<}/yOKG'[KL~ԁ:m=[@uԁ:P@uԁ:P@qzc{_&Tp7n.ǣ/y2[TzȢu:n9yGG_ S_>oyC?>ăaϹ!>'K}WxُN΃A:C{gl:=H'~uz'bzfVS<0o18>3/V>#Kڵk Μ9b<7=#77^.OZLK/t2cDuW:e8ϩ^ulO{ׁ:P@uԁ:P@uԁ:Pvԁcׁ n03>t"C7nRq!kK~˼'~|}3TWO9?M>eLsysᤣ/٧oy|u{OHGѿC]zj;?zkn9wz O{9P_vrׇH/ع|$<};_훨_9 ѷ'K׏o~!nLCuHAǾ>OsJ=s&4O8_^ a}zwS 97ot=>qns3ωo=wތקN߾t:)H/Q]3?ۛL}y||.VҵtGG>cŜs;gb֧KK><S{#ayWO=/_]_'OX@6zc{۞Xuԁ:P@uԁ:P@u/@_n(Ft"͈g<ބf~ߜ)v.>>A×.DsNq':orNCubzNb|}~C:O t<8͡KB|=_WCs+P<sG}o SnVywm1kί̫`ٳ upc|m΃} 鋡|"?9/c|:1'#}Cs#Cgo}P] SO }9b_77b߃o.<4~K'O}S}|buz0W9|5_TC:/ƃ<^է:WpNRR{h?O ۄxC{ӷ~CzYC@@olouԁ:P@uԁ:P@uԁw7war3˻ 7ɼnn8Q_.}wn{׏1|8#>0bP .xpy{TOO= '|?($̳7L=gɳTSb{a'M1>OOsS| }F_}&cN"0u^o~<7OңO/G'>8:!/܃:ySNO^LO|DsB:ɋ闧/Ɠ0GsGלͺ>1SGOT'ƣsӁՋuԁmw7 v:P@uԁ:P@uԁ:P9;{:ppM7\ )DhJ9>bs0t_]^ 7TB:/c|*H.sW}/7PL~!&3f\yzb7_sk.;~2/~رѣx?4\C_1!=u:2b{Nǃ9Ks~s =}&4&QOķs$=˛C~txߞ檋}'H_>u!]Og<~>&O=/yB|xbu|(oTOO_8Q>|ybxD|u|곇_|;v><ɛs}0S|w}CX@vzc{۟`uԁ:P@uԁ:P@ucǭ7n2@/fYtIϼ>A|D|nodf:Buz㫻K/-˗Q={v=y`c_ٜ[n;?!Du/zB{Mu<{+'xۏ~<(е<u( _9OHnOs&]~e>O1}aӇ*uա}>sOsGӁ=|13_'Ɠ/oNSϛ8Pybub{esO^On'ćrOyoo> ~=s3!ȼ~1]u<=1yx|ҁ}=<碟<O?cy?7:|OWyKҥK ]*9G>bu:_WgA{]C-̛yΌ'^:Ky7Cu>!CܛnOy:PsA|y綟y7^"h=13W ~y|Ku{Ёx깟<'6/<]M>Kу0ykO|NO]s^̅xtyS>qC{C<|ԁ:>]@uԁ:P@uԁ:P@Qzc{G|]n,Af n.<:=<Ӎzǃ/|<~'/#OGʛO>sա >y&Loωz%:=}_<絉?!×}e~OT~~ç W맗sա||qW 'W?nTw|'O\H/^{mMf>wܢ{+|/99~JADfN<=zb:9xїy}YOu9⛓h̺~83/N9{8btsޔ~~ӷDu \}'^f}3<3'ɺ>MzΕ/ց:Ṕ޶'}@uԁ:P@uԁ:P@;@ol ׁn$M ugVϼ!M7Cu7*AN'c}M<{:w"s/V7G}B=GWAu|y|uh}߇~q"Pԇ/VgSO=(>©O{廰W\b^~tc^9~uy:bu1yk~CUL'<{C ܌;!P?P>ybb<<2/VԳ_]X<o^&/c﫼s@ CsW?_|:7d9ܸ9>g|tNǓOTwn}9GÃXX@Vzc{[\uԁ:P@uԁ:P@uǮ yC y#/ƇnȻ1hF><zn~uyogݾn3r.>hOHOLG^.Cs?%/Yg>}D;죧oS,c/D<.O@y7׮]F=s ,4̙3+x .,ϟBj|Gxtf~ÓO4?}_1>TP^?>@sAy:bEUž[)7C{7uMHx'KL؞חs3Ox|Ͻ/9楎<NG ˺~s?멗|}xO9աE:S+tɋg^]O;yxN'o~saA|.M}Y󄏧u}w>}'=uԁmu7u:P@uԁ:P@uԁ:P:;{:Kg~y7$O>o<1nN~37߼~ 9=n">LMgads>t3ιt~sd.>Óa]<07ģB<}Au<y-x#4{]ygO](P]?y8K4^b8>1=|:Tw9塽zc<~/'~Cߍ|/A#6^ދI'>+{뷇\utP_y8d^uq#M<:17/?7|uqΧS"ygD{@#CyP?{ŧ.O'Q]<~ԁ:pX${:P@uԁ:P@uԁ:P8;{:  n4qtpP>4O~:>y{cwx:xy>Myd^ Ms熌L^O'K}<sIćtO>d6ϯsy4'A<1WC<#ouzI~C}by/XsWn//t'NX?> ~i[9$ؖӫ裏.g?O_?:uj&9ӗ<' <軂S̼N?sd]/S^sog}:%.ͥ3OGǣ+/Ƈ>+ɗO]y|ɳO@@olouԁ:P@uԁ:P@uԁs7wu0;7Ƃ M(73+}tͻ]~:tS,bHO Їɗקn/1<:TO\7gsC}̛x8O><}YCnC:y^ y_#/Yǃ<>͓yxS?c}9Ϟ&G˼><=y;;N7]xq$ot&իWGח>C#Ƨ#'ބt9_>O^/c>ӁP~SP?Ĭn=!>Ket`ǟ1|K ѝxׁ:PŁޖ'=@uԁ:P@uԁ:P@u`9}@84BlP tቡ>7L̅nJO+/9.>4O _^}:S'u _ss͡Abu0vcsSϞ0\xHtړ9ա><-qO}B7as~ztœqB?}ɟO:g,>s^1tNj=4Zs1ǜx(z˼<1Eye^:ă戽oOc.|qK.Է:]HoC:}|Cu}bxPf>czp'ϷQ~+O7s$=!Ds'ǧ ìS_!]u:ݬx9|ԁ:uԁ:P@uԁ:P@u`9{u`78//z 7\n. y|ub7!ܔzO^8A7t.ugӷNG>c<=1/Ƨ1>맣<(Oz/Cy8nTj=DzΡI{$1>ͱWbeO]lUl[~C|;?~"<sG<|3闇˼9xOTׯ.guyuxtJ@IDAToßA}OyDԕǟxtu1+O_^sS/b^ԁ:Sguԁ:P@uԁ:P@u zc/B;Ɓ/c??Н]y7Pp"b}nrYݐ!&nrysuhԧ:>ʻoTw^zs̫'yk֧y|PLu Eokkk_By}yNDuYy7s9f:P~]ߌOӜDD<A>ғǗY8ye]Au1=}{OC|Hʧ髼tyyt~}WxX>Q?s:_ӅYy<}SAu"_uTzc{>]@uԁ:P@uԁ:P@أ}=v؉| _k_cs 7\x7p u|u7#9ntёO:SW× xP&?f><O_fKә>|h}x_?s@y#v^W7X:/Ri h_1ͼ>A쓧O>|xܹsϜ93}fs̓ 塼?mOsoCzz"/ooyb7o7G+Ƨ///[]xޯ .R'O|np39`-<:7ăO]{/c{ɽR_{uyt/6G d]uouhӵ/<]eOuߗKW s_?L^>/@;݁Oׁ:P@uԁ:P@uԁ:Px[vnFɛPpD>|sݤd_9}tUOySGN˺u}0n;/͇gƧFTR7'ybH7xLT7/uݾ=R__g|VU탫*Yn*9xpoUƆ<4_闺|73>PKOO<+阗;}ubh@}Duߘ?!]yxtү9>L,Ɵ/v./OVOԟ}ynjly?7OGGf^ {;8Qg,%hOsdLG]ggYAur塾Y]C7~|xKtc\1^ l~0u}x0o/1]x0o>C|HRCD߼'/ʛ#!4'c\[>|Q:@s^GNplV&IL=YC<9x+)NL==ݐunOxt<%Ϟ:su/oA|q=X^y>Cz 8z꠼ (+㷧OxȑXfe_x|:YW'>x' C<4'$_>'o_}ox)VLJx̟yG3CĬgO/b>>_^X#~쓧 x3.^'~ԁ:D{:P@uԁ:P@uԁ:P.w7w^r 7 #y#yn$1OS<{P_A~y}y.Ͼr~|h?;C9g8g.L=#~:r/H=|5Ծ'F/Ε3Oѣso_ g_(_|9onn퇯./V4O]ũysQO>Du`3l<^SC<}扽7xbϣ?塼!rnҧ'V'zDy8ԗ7gV.~z>xP]=1 z|̣GGLOt%bY,^3~8W#Ε{e]=)X>b>ent~zй|3G7S'N'_Y}G/b?CyObuY,ЗOzҗq HO?9{~e?~xP}|yPN|ŁG}I~*qcO}bC2W7c}yn[.y~|C}#$Kԯ󫯾:J/s>3㷏-Ɓ}$ꃳ>C~Cy|\#'/6'Q81uCAy<7} SWl9KũOG^LA\O_G"}HϞO~xоxP>rbu`9;u:P@uԁ:P@uԁ:Pw7 ׁÇqHpC: bu}xڵk 70ܰ.ƣs`~%ґӑxx/VaΧ gz|0|xɧr_rS{? m_ٽۃz3?ց%I'Ɠ:T OT>g{X?ByͺoK׹L_nC}bs[ o7'QGsS'~/Cs}=<쳣Icomm 4'犝w>!=s3>Nfyύ' />s} ˋ'+bs'f4_ 塛ΕhΘ.OCb֗>47/C:3]}0y͡c=Y?=HWLGLOo/hq{n>Y}сOho֡xs:_}+|璇~({#?皟hOb)r>3O=c܃<gXeL%ҕ{<y<9W g}tWW/ց:Pv۞hS@uԁ:P@uԁ:P@.=^Kk7dpy޸T!>f ]z3ijgƙWO=ăxwͷ>~xSasyu7FGLG^ 0yy ~n/bܹsꆶ#|5o^~uO.?>>u›!+c/.uy<'g/ 8O=f2'H/}ysʼn3ٍznKWL7c<gu:njAč#}ƣwm+ }`WԽۣ2 X'fg­<{Sĉ>{(_ލr_7{᫋o_1 8ӧ}8}%oNͧ||?c!}?t s_:9Ü7oo;kXѣ㷹l?<׽><~ۗăɗ~y|1ć9W?짫.#]yys ̫oę}>|~3}ӕK\>Ya죗uݙ>:\n.T36g}!w=CyOM]}o.^昏9OΡ.g3-S_ꇳ}won'_O@;݁ѝ~_@uԁ:P@uԁ:P@݁O{7&n>  |ҙTч̓e =|>h}WwNuz>됞̧̧u1C缩|~$O?̺y3=}b:bho>3csBYtŹ~u? (6O <7s1'9Bz!]ח<|uС }gh>veUC^/_r;Xn\omm;QxkcUq7վ>y鉽?X}e__/s9+Q?esЏms9P>z'oɷsB<>K/1D2|ӏ'O/Q~>:]1>osn<]uԕWuzc{<ɞԁ:P@uԁ:P@uԁ:G={:pC 7I_>RnVgHWb7$<=ytݠL^k?sRG C[;ϟtCPOCxO]yC~xA'?~'\P:yDu}xbC碗q%O]^ hD>!]|g|%/3Ƈ9O<fu`}eΛ|1~ԁ:uԁ:P@uԁ:P@u`9{u`/9檛7F|ܐa&>@yL+N=11RG}oWN=tݰR樋ͣOW=scksO|[,7ݸv3^nhkп {O'Vq{L:XsQO>|>B7'ţ#NDp.o_s̕@u}g>]bgzb1߾bHWLGԡO>ѾYg^_zu\|y 'w=gsa_ סhsC<,N^#Cy$/h^bkΥn=g|}teXQAuќbu`:;u:P@uԁ:P@uԁ:Pu7ntgs 8!F8My1'֗Џg<79b:%Y挼>bu{f^=K5u-y'W[[wWWwpQ.*nl7^>}zP?x챁//[aAuHFv/V7g[` <~G}@y~:]NGxV7Os̛-xCuD7U>pv^uHMT;>1 Rӵ:]y_w~Ey}Mk_|/c{CP [[O;#GӟOO ?;>C<7K. ޟ}bsʼnelszAЇtŇ9.OW?ϧO?|ԣ+~bu`;pt:P@uԁ:P@uԁ:P@=`{& C7ĉnTgQ<='6'c:nOus|r㝾ib9b|(;Gy.9O]~|<痧'K4OX8u<АOw_Zŷơ٫_'}cKɁgj/g߄(?'_s}{х;_gAaͅx6W>toAz?SX?z'|~gϞ|;~s\2/^xᇺ˿<ɡDŞ9!}Iϼ=`Ӄ>O Wxpݹa3G{_폧n1'VpWϽW sy!=1]S뇳<]U @s"B|zWן!=u}b<>s3՟CoƐOL,_^fKL7r߬Oij<43죫?O}O?~~ckkX>'?1p͛+;w>6Y}uno1W/o<]HW=짋/So67# ͅK9r? *Gﭷwyg <~@{Otgg|s_ĉtS o>saAA}پng/N _ʛ'!};]1NoWO|Sg9_Ӆ9sybz|zԁ:uԁ:P@uԁ:P@u`9{;ہ~_җ9sf*tn |_R#n  bfÃnо99~Xއ!e9}g܍3{':}u{C<<}<1'}y{sSt!]:z3o~1̽ۇ?|=4<>]sC|1O}uy3= F_9ᆃ n&mn2$:1A=9۸Ѳyo{`Yn^9ب_[n_=yrķ/#y7 xv^90̗CWϽf=˯{fL7M3^CK̺9tg36W'A||ts?gӏ3cyg]L_~؞דC|N>L}}3=9W 5_xg<_x|!6?f즷ͥ|@tуxף}Ab/r/y|gG='[}׭xsyC7W饗|(Sbu(?ӣ/a>~|uh|yzɛs?sˋ3WLO sytC}:Pns7wy@uԁ:P@uԁ:P@܁۝:ujٳO'"nT`.7"d'_L§.x9~$|/G=ꍍ;_}6ւWo/|{&ړƉ=̫C_|].Ѓ9'OL?QToCyb}:N?̃9~u≡>ȗu{țxΫ:xLJy}O:]~)_t3yd#yKoO=2;wn|{8qb.xQ K</~2m{nHM/y_ߞb<g8ӱ'~<<ɋgs_Ӂy0yu}˼zK'/bC|e^,o:WuTzc{>]@uԁ:P@uԁ:P@أ}=t~WN݄r衇F__ܑ\n$8xyO]?:O'vB]vm|[6>xg/9GOzcGNuɾZhΥ|7˧pnO{dl9P1MCu}r.}P=uũ?ӡ/A}b8g=5{ :e>ԗ?˫ˋy1yA|?usAy{ա<QwƬyS^lyS8uмO]O9'>vus#ﳗxо4|P] ϐ.8QᩋeӧG/z/0y# :$A{y)N'yɛnnb쓈7C{N_&?\K Bсç0yty{zR>x0onԁ:Swuԁ:P@uԁ:P@u`:{;77~7o? 8ɓ(??OO_Lp،ݸpN1n>d|ؐߎoΟx8oj/Rwh峴Hl[OjwSo|3W?}P'?C<}9_A&=NGynRW.?aхaч戡<]Dusyt&}xtAel':?Tsg~ꊽbhy_=u허G_]Co>z)ιCy(&S|y~f۟POg=l8+>xyٗoxP^l {%t<'y|y}b<{QxؼD}o |bHϼ̫@;ՁީO{ׁ:P@uԁ:P@uԁ:PG|xcq77`_~+_ w.f#|"MPX]>qիz }|FϞt`\]݌^Z=vv6~"b|KS'Cbz0Y7 ~1,;zto;||7IēO}ux}OTm^҅gl{~}&z0ӁYOҝyb:b{%^bY]>Q0yΕy'yqbMo6{҅3z˞@|<14#ƃt}tAbhbh9f@;́iOց:P@uԁ:P@uԁ:P_w9ww@>~~ۿw/n$M+7nFဧ./:';xqW7GwwV_FMmCot~qbٗuSwO fusПwiqʠ|݁|7o s>8p߱M[Gx#ϛoxE޸Oe;8a#/O rys_]Pg<^g_3=D|y1^xP?<~ٯ·ɼ>O?Q]9>y戡>@} <볇<8'A@Ł-Ouԁ:P@uԁ:P@uGcց'Ƒ`/@7jĿ>v/ysTрH>7 >.ɹPTԯ->k{eC k˜OUwt]us۹P_{!9P=ա:Xy1>P]L_O|yh}3Lˍ7=CA[sO]>y|_~yoVCg~zL}̑7G!?x3_bu|0yϺЃI鉓:Y=/zbg'ο6뿭$}vyǖzs#}{Ե/ubK4?yٞsNcyCzЇɣ<'PE~0fέ^L#>u瓟gs'C?eݜ_|ԁ:uԁ:P@uԁ:P@u`9{{ˁzj_??GG?ܭ7|pa'7,>>PrgrF7w?dsUZۼ7Ku;}cln /7V}>-N=y8ĉu}PNOs<1Cy}C|{yKX=̇|"=yy._onPMW]s@g>t'NOG~b/Sb:_wWUߐϏdz':b{@$&O0xe]_<~X4.Ǔ=:8uс!]'?dQ:ʫ m7z^~:{u4ev^Yދp9|~/sDSW]CW}ĩOOXOCy2O^?ͅ'|9Y_g8~?C'_] +W%y3qA_ˋ<=yhY _tΛyC{#nHEޭevӇy~uzKWʧ<#PX]>W>7 t䙇g?uʾu|'z$Cus_>R⭭ z>b$&_.|!s<8~k}y3L]ȋ-[nj@`n=O}|w77Vo|jus{c]y GG]>}OPX_ уKO^ 3/ZǛկ-Fk>… ~şGussWu}CӗhT+g<̷O駋g=WYc>9PX@حn}=Wuԁ:P@uԁ:P@u`:ۻX}mw~wW?+s܇O'򕯌/ t^n4!VwaϺtC!CZ>;{ãǓeӁ~R|:Duh^!tcyDe]l>L]yg>%?Cr9WSןu{2we>6.ߗX/[egh{߮-b?i>#uXϼrΉ'V~y˘хO.TSW?9'VQzav<+߾;? |pLw{8W:=񌧎GO?0#ozL̼//w?:Pns7wy@uԁ:P@uԁ:P@܁]|_h@IDATnlS~{Q[Q3gۺpO7%('fg&Eo_v_çV7<#  NE <22xg.7Pr78Snws /x<~s?Q=S'yg_L>=]}0ug?C?(Ϲ<>_l> 7|sPΜ93ܹsO<9s5\009g8>?pƠY>ju!/^K.q>|f=}S<C{ጟ>ѧoWsա:E/Q>yC~y gLW^PnvS_>H=}}Oy9ُg}=f8Ƿ8:$_>AyO_u6zc{=ўԁ:P@uԁ:P@uԁ:]{7q??ɏ{Z7NշJ)F 7~uq鉡<1付>|~vH峯_.7ݐFIrٳCƉVs#d}~͙!>t_^xbsby}tS/u!sϞ'HY^<<@{AyNCuzOT=|sA:xyCuI=1^{t- g/>a͑O>^"TK}q!'O}14W}t~CC>s#v#9Cf=e8307kJ#?~Oo^>cIm>?w9_U\O>^Oo}'ss.esI?_\y1bH/ϣ/y'訿{~w}wOo >}|y.=ρNG C|1]_^_"^bu`9ۻ)~n :翻9`sCBy~p/FBu>3uO <|&wRJ~冶9+OԠ>|x"\7|'yx /3G] !D|\_'OO ͼO}7ա~('s}yCb髛w7C}^/ ˼Ω.9g(_ձ02رտIশ|Ƴ .7_;XnCX_beLG?@+~ gOw.}c,N=缞C꛳ƶ˾?ߺysP|շݰM|_f++?gN3L?O}6+|34G9p]>ґ|_Gm*wGYǃ~'g/}Oç7O սogC'?*80K/ ӗfv~}ɑzѽo}C!#ont9ã'͛6ɧ//=AuUOO=ys=T_=~YǓ+Vg/Y:nO4/c|R?>L|EO ɻA(賈/󹋝ԗ}'0bzxRu1o?y<'W\gKyUon/7U]:ij}`!=>< 6\x/Cy^'/<Q9ƐW,?G>t!us=ۍO3~e<;^"]zbٯC0'tH^~'Wc/1t`gLqu:xbs⋡arsv[s佗u.RGL>ă9W=}ggl<_wgOuHGLϾ^"]y|nκX>]{y~~iPmo|F&9{Dtfu>{`́g]x֡y%fqu`:;u:P@uԁ:P@uԁ:Pu7nt 7l򆃛 0~qϼ~"֯Yn]hqzus{Y7Vw:4x>8rY7ozs{]9fa7_MiC{d=3_>Q?O:P] Ro͵'G9#7ty A7?O]xP~W듟Q{CsAbԝc0!|1>l֏Oȑ##O}j෾-Ϝ93&<Co"~"}6ׯ<}ƹ-?!?vX^z{ֈ/W̺uhuc>P6l:~:|3L}sta;y!WGAԓǃ#wLٟ1'm-' cO}оfoӵȾ|(>LqƗͣuyb̑OL]}_ hP=G}PӁ8ӯfEsgsgxyS}s.yP~xS䉓yu'u(9gg}3} \ hO{"o@:}΍sĉt2?`/>>SGĉ#~'>}zL7̛#x:<^xrcȅ 䳰 rڍm<,6gml;_P~:gf:f\hLyս_bh~ΣO?b<}tg^̋\< tKԏ~G7gs=%җ|o~LRW9P@;݁Oׁ:P@uԁ:P@uԁ:Px[vn0@guʻxC<(|s ެCuh,xx+VH'@u1}(2ա|#b\slYO}<gyuCD?I!O3zu#rzba^":'C}x=AzdLC҃>u}yP^H'_>cr>u^m.7ً>/6_œC<Ó;~xckonoʈܾ=pƆu.:>C{c-oϫk>+y!G[ 's<Gl>y<̋t`fy} }R_ݞ_C޽Z~WΥ3K (P*"(TB*FjRhT"PQDD "[\E4 iK@/3Nٯз{&s?9kg=k>{GOC|Pʼn9ϞޙhځYuolہvhځvhځvhځvhځvX%سwY+ ?n۶pӦMnqy晅??Qx)1P qSa0>䩛#C˼9t }P] @<̋P^抾|>Ε$^푨nd=8$φ4'+/H>D!Jw#'293SwģN_uyn=RO&_&?cDđ~|_?SCyP<7G]_A|1=:8'1L>~n:B܏΃HLN'7ҥ}#WWi'.5Yme>o_L 7COoN>{ cϚ5=r/be^<Ń:t{@u(彮=s!_xA|::_qeMgݾ9b{7@;0Y;@;@;@;@;Ḱxwq8򗿼r-7W|7|zUG>RW|Yg./&M t Ϻ<:ӧn|}|>7 g>ϛ+b7><3t3><| ^':O=uyO><@O]l8/yxdOݞbP]GO~g|KعN_?WP]HẂ˧e_d3k.ח1y~W|fX~gT?+y][vZIWX>;4M'v UgO|v7?g:Q}2!D{nx9_t =^[n-7XxWk-<1g<}.sQ>҃af}+OǍ!''/u3>zxHTO}ynQL__YM+1~:tMO^zxt Ƈ._Y1_lݬ!~C4=uHW?T^tAP? ytP]HfOaQ[ UgŊ]k/9{*|mjb!O *|;^P.ϽI=o^#l^Z馛 7nX{}ߨ<<+> }g4obxoQ9bCy(o$6O>~qSO}tP~xpoc?z97x7@;0Y}rw;@;@;@;@;KԁD|{6WR?O.ۛy'< z;ޑE͍͊Q9Ψ^޸dz]y:?B>n.Ƴ_峟N񳎧Wg^</?Ot0tSOGYϻݘ'CbG:qD8nO<ύ䋡>cC^x\<O>137wTS;0#4xtgs;y:^C}ty''&~/G:=H_ 3|=Wڟ:gSW ٗ8{RW]>\<:8\f<4/>><MnP_> #|t<s'{!t`C=P~(o.ԯ.GW_~֭$s=U_;LG;@;@;@;@;"sol/Y{uwO{ O8ᄊ+M*υ_}e]VXb7+/n>Cxύ u}FΈ7KQ]}a_MP>u̥C}Qe^nȏ퉧s3B|MQu"WTC^2aꈓOW]'κ uy<(哗1^;7g @;@;@;@;@;,1{}vO|cXW\Qc=}s gr!˚5k(n0r7!!FOysc ixGዡbxL3O:Gh|h죏't~Pя'XO_'c</Q=ZLέ?xoNΝzׇ7-ѷ資y`YţyΉ'N>]<1;O\1xٗ<:1;]ѕOx.bR?b<2/6Ͽ-R79昊>*wn=F>[l~.OݼlR^uUR mVyy|Ʌ^{>$G׷>;N7OL/υ.GG^?~R냮:G|LJƣ<'~ˏ~^Fzt}坏nsN_:7g@;@;@;@;@;,Q}}v`1:Tn2pt!p+oh=A78_G<|u(/s9 "xt%oӃb}PBs8ωO3/x<<~ɋա=SG_OLo=g:xtţx>]u7=|>1>OՓN쳇!?F.ç'.HW^{]CA|1'F<ҁ=NG~ɛ3K}sdL71抓gM7T/( ni|aU>_ ju1 |{=뷗ov{nZ|7}&xsS||Hgˏ:>̺!]~yq=~(o.OМOTճ9}!^c;@؞'@;@;@;@;@;Kqہ>o(3FCЍ 71s#b׮]Ed<~E73窛cz"|{;//6'c4mVMmA}AU9X>Qhi^s.sCy[gσ>Z|nh\s} __/y:g9|~zt2|y9<77cH7s.1I=yH^ >%sH?G C}܌ӟ˼f>tׯMzcWO4_/bh~ґ>>usgf]<9us9(֧.c^ >H_珧?C>]'N>DKƂb7n8`WnH]|<}8uy˽`ct1gyїgw>oũ|bp/﹈/爽F1}'Gӷ]7}t:@^#= ^OB~znˋ!/a3v~MG^_( &_=2~'o.|ꩋGO]nMיD|y:P9s!}b3rSמx|ɺWS_^̋!ۣ:nRZ֮][uWxΟysO=y{<}?<1X~KC}#s˼ìOeߨ_Ci7@;0Y;@;@;@;@;Ḱxwq:p%;?O^y啅y{ /.\l_XnL;:>'駞>MnHOL%=`o1o1ݐySw>>xŐ>~чxԡ:~~ԡV\Y)򣹣y#>3on|%KO8bu|yד<~Dz#F~_>bu(k䉡>?t O>DszƙyG֝@uz+zn^z꩕k K<#+nDnGO}s0ʏ0yb:8*;pÆ rH>s/Q"V&tAu1=|:~">ԏ/s"3@=߇<1<}t:̼Xo4҇򐎹xg^'{g1olځv`87˓s@;@;@;@;@;DKA1|ppݺusO[‹.O,:0R?yK19bu:bnt }a㛧.'OL/y#uH1{[|?}R^ף~7|cΓ|Hf//Q^ΥNވ'AsꐎhNQ̛ӧ/Ӄ꩏/F#<979O{ǷAuh>>ćПo|ܦAU+h^9./ه'_a_KT~{9<<ysN:餢ql/uw11o?9ۛnΑ9qny{ȻP:f~17ԐwB CCSW|}FY9?үwNQ1=u_=CuC}ɓCzPD:ĉӅ~P&/uu}bhԟO]?1S9a|"=/b?/Оt:۾}{}ym۶{!RXhJ>KyC׮][)f^57B<Ӆun1_>csAyxg<̺sgggwy {݊~h~1H=|y9yt~0uPA%y:X=?:7g@;@;@;@;@;,Q}}^X:3 N;!>!]vY}C-o|+B7>슏?B׽n/ys5knݺ}!  In*n8鈡їyuyzC<ӟO^'{{f~bMuzţ:^>{x2μyC:??^}~/˕gN%E&O?/c(yg^tc?s!_>cs9Ox푱>'ґxgo|'ёOWHG/t!g\}յwaxV?~x?7C8f=y>ćWySoBuq/N?=uq:4O.o?(}x'IL17^Mod^ҵ>y}(OG&/O7@;0Y}rw;@;@;@;@;KԁD|{6Z<(1B!8y⑎z"=8m}~W|O8x檋աyt<؞9~">^țGԳ>o"OOW\Qu]rJ#?w(yy[7V~sw}ɘP>~||PG~#G9t='֟y}>-ŧ+OWgC}x<ԡ:>_PO'As?I_F²ݻ[p Ч'ޱcGQ¬gnrSAuC?g{W緺9ĉY<999iouy2%':H_}+'f=V~{@yi9xk,;ƣϮv/xAk۶m;wϸp?XEJnhz {;Q Vgxۿ[ş  y/}>u|7M Wg^%o1^⨞zxg}|~f^'֏7KC{*Os_֭[Wߺ }fɟ,|Mn7͛<0.҃A7QrzpM=|9?}zSyu?w%8a͡g/OOL_=8O">=u{#O=F~aS[w7{̊]7ý{Z7~k߷ユ<*}᯺K=F<={a5AQLkODuAp CT?c?+t}g`NXP pc 7Ǔ/~#QvmQ'<}l/n6dl?72Ƈx;b{d곏>u1WOTLJPCWGhQ9'N]u (be~hsʼnc~<14oW׷8K }9'6?oݺ=؊=>ۍn-K/$W覷4>||!܃)xbPsnC{8s':9YG1=<2@IDAT?/{r>~D{Ӄk}yeϺ^s% W9?i{7&;:zo~7'7b:7yoys&7wZݾ# HWˋ 眬g:zε9ɗYgSx'񡼽'i}CϾxbhڋ}ہvU>޻hځvhځvhځvhځvhځ%@^_:ЯN+xg ~7#ַVj϶|K_Zq~7(7mTN8T=S3o<??] n<&8>zYϼ9#}b72)OcO%ìFy7Zԡ~1$K]L㋝Oȋ!D鎐~}bկ._B7!>8zg<Ⱥ~{]{U:?+v;~w⥗^ZI]xnx^x#)tn簯9[f[1A<(:~f<=Gg?AP=1e>!O^6>d ѿ2w3njpUX9MO{Ǯ >~cƉ΂rGWG?unsst}71%5?ybx~xy^yA}@;̺}c{֟`@;@;@;@;sol/]#?#u??(|ֳUxwu+G?Ze7m۶>.B78㌊}ƮOyS*hpt .s9_ ݤ:*nF t.COxT7co:Y3Is}՝/5_ b9Փooo7ɛO_]_^Փ́ɓLJ9G-oyK}{i>,s~i?͡HO>f͚jy;*73~xnt>[_b^󋽎S>b5W釞3b7s9<uyse_-:e/"oO_y]7[Nnj';={SНWoRCp)>sG,>MUVU/t }(~kܗgtAu᨞saHKTOij<_6y=+e/gŊ;\tr\}b{˼:}17 ˾>hn=gCĬ?2}G'_EWnQ8Q]8y{>Էہv~uol>ޫhځvhځvhځvhځvhځv!iK'ہr5yM-^:O}S>K.xYnt1TOz~K^RPn }uou~(h^~xtᩧ%>b|e,Gxn_<(ϏW;>:o|tϼߜx;Q/|;YN%dln'{A%N?`뮻JgB1cnt-3b}npM}cHǹ#O~ħxbsS_zC<{ዳO⩋!]'/c?z7>7ZVyg{y nGYRqAw0^?ϋ~/dxZ>u=buGGGDOLg39%gL^>{8#yonǃs^uH'Q_c;@؞'@;@;@;@;@;Xo~>켋vܰvUzUmaÆ}Cn:gdl[sܘ9oܼÓ2/vCO so|h<}>1_^SW c>1^x#qƌԟW9/qyMK}[ߪo,mcr<}bHWl.(|ɏ✏QL>L1<<}gy|e_1^|g_s9Ǎ|K_{ƒ'}'4O]sy=OO.̓9|Q?u|Ht?s?Wb77 "DO(u.nx{މs87Oy&s8 ?~3) կ o8=yu/!>D~gu}﫼XC*/MTw^uk>_dLG_Cys2/(MeXz"&?c}yキZVr|wii+N9}UH~UJV(=^tRa'C}~9a#N7]AsrooV߮?y~ēoL~#Mm@?ɍCpS\goG'x?K}q_n"}@mݵkזkΡg3_<}}bUCz߷SW^pL78oGKEg7o|Kăn1ꗷ'(͵uyho~st~y}AeM.7OCso^{n[/IBO̧KT%?:+vn}œǓ秺}>xJ;_җ |9 }žD>a>7>:̼y!]Q9tħc_Ĭӗ7.O=~q"o~n`YK_;x 3B}ĐɧH'tsʋ!=|xIė'?B>$nBm=QN%ǣ">Ϗ<=1wN/P'/x΋OO9W~C|uP]l~}gtOӳW^ߡ~q!ku{run޳?=+Xvng:9}nNA?'M|by1s'҅.۵J+'V@q뀧o˖-axϠO>{DZk{ݷC|y{# }[ߪi7^uUSgӅy?ꐮ}>̋gX]^z=??obu|h^!kg>~yP.bqol/'hځvhځvhځvhځvhځv`87ȃc.->WK Y?S?(q# ύ 75xt3'<u膆 OuH>x/SG?=ݴu1DP?8b<1>:#=ԡqr׽|>B|{Y/V.>P⩟~镇|3ۼ}WVmo{ۂqW܉nz_,=/ѹ _~1RCO/|''/P߈'o_}kԷ'/⼁ٵsgܦ*k?Vvy%pSR&sB|z'x0bHg+xMnlwUrLm}ɿ9WWL^w{߿Jt`y9ׇD~#tu?[VjݺuyCO=cy/-u/}O~ugNC{|WC{f=c:F/η@;0+YyRg;@;@;@;@;@97,:__q?,|#Q3v+X_DpC7RȐwč}W/Ƨ>;רn^4']:/.q>]<:<Q^x =}b}xԓ/G|=<ϯaGN yc~!=냣z{Dzn!/B'7 oڴIisss^Cg /ln77o\nrFB5}~!'csCt_<9OnIL_Y(|pᆯ]-7l&;+'78gv;KuU9yO]?C<~C~&OL_ aꥎxt<ss<:_ =uq@-|9+ʊw9h[<۽lI2x<^>{B}||<-Ϟ_y0csL~dwnDu{d>xe<ħC79МQ@;̊}c{VT@;@;@;@;@;P~!3mj/~w~ғToo~.t#MJ./nঃP?Yusͱy1N%Mf9pTw#E=Dzg:P4_~<}X|ƙCy_yt`~wRsNӞB:P^9g9yNY0bU!} ?R7xbyfƍI}ƬFc_%ׇ/V'Ɠ{S%sʟq?C?Ty`ϛ|G?r_?/^5}Q'e>u^wLD{'I>]kV]'T_](1)dVjϞO~aOn/81:t%֗<=}Ͼ<>g?}y::П<_?_'Cs~5R{&sNGO3G~zoh'=>>uyh#=/2=W?9=m~hnŃx:%/ũ97g@;@;@;@;@;,qgˁK.~ի^Ux5)t |#_*_\pWǃ GW|w3&큧.νm^ݐn郹=9|b@W]^lH7/ ;|ʼn:=Cm}3~|O4?t2s:ԯnO~`C7˲n.=QGUxsOs[~ϼxo'~3=<Ȋ8g=Yw3 o{XxWMnvas3>_Ηjbo<(?O޹Wd_gܜ~g_{wIB& IL7?ꆸ>P#y>C>xO'_~_ޞbM>O>zuܬ #0yc{'cSgV&7¿]Y+W.LL">(txJG|cS }ͽÃt2O}:g>@^gt!=x=gosFyGsہv5=޷hځvhځvhځvhځvhځ%@^/>l9X,_³:y{^; WOnTn.0_9{sn9x3&nџ!/35TʍQyi?6m*ޗN8/so~Px'|Ʌxy*}׾V߾/WRxw:!]j_'%IxԧN.Of~tC z,o?>sܜ C}P~W#? a^:/Y۞駟^#}3Q1݉|v{t[ӟ.+i_<<> =)uC󷯛NߺKy3́v: {Y+&: <䳗g|HO /yWg~}έn~<>ҥ鯘ܘSk ܞM;ntS *|i.w'~rn>r~hM7rN9jq{˖-Uʧ!}><;'<ח'1+xn_>?~sCu'}!~{W їolځv`۳{vhځvhځvhځvhځvh}c{=>l;^A?B7D?W}ӟ^뮻N*\_vYGsc D7 ?XܠPHOğ>KMaiV<7ꪫ*sLnpC?bfs{狒׃WyO=y9sS{qU^ߝw>&q{͚ndž orw۹= o9:Kaw~ W~|ɍm<:V__+|3}vO~M^?%6et>u{/?gtsȋKuћ֟{@׿^)af~7G}t˞|{\΃n]{g,ѡDu>klځv`V۳zvhځvhځvhځvhځvhځrol a|v }suw]/x p3IOz 𝫻ং n:.OI^HOL yb}YOO}“S;gሇoh<^e.|1Cԧ>U{nW\Qh.䒪ʯJG>oy[ 7^z饅/~ }م]tQB5iگzիs_xйĞNOyP>y|S'//_C|zSec~-*='%S V5]?ƲZ͍š$3Vx4Tb )Tbf[G)Apx9ss]z^~97{/ }1\Duzz|%/Yvi+b];&n6:?4߾bCׯO<'<yd}#kwszdF#k7qy禮81_}쩰tsng>:PC'tĐ^"Y^≡F[{=Jyߞv}'~k9ھ >ȁM'F~Bx~>=y鬏Nw&9̝.o>}zxY7E>ώVy}O4Ro>any?.bg]s<yy{::P:wuԁ:P@uԁ:P@u`E}=vM놧u//ƶ n\g^w"&P릅xbsfhN"ӟ7.oNY~{C}P_UQwSOExW>n#ho0#32OC9xg]_'/uΡ//QȼKߟ}77vء4g~_wu#?-''_]>=3yˋ鉡[9!p\Σs˹nOS|S\uG/ϓyqbG<1}?2{9uun7M|]볹&wM{A{\3P//(k|YyP?Ds!uُy}ٯaS/o|Eo}fÇ6$ZY-x[oogyУ#F>S{7mZׯ^|կOÃyy=hT1O!ӗ/?I_oNg^soZ{>9~S_;=y|⥞|ԁ:Qd{:P@uԁ:P@uԁ:Pu77@ύX7G܀9fzz7!nfțO_M|CO>yYC}y^3OV͟o}nf;/w9~y۷ohv~8>xt;yzCR'+9O^ әՓ?^ʼnί?Rw_D璧oYٺ~ _?C?4Psٝx%?C#v;otwq|f8/g̓xP>K_=fKy8Cǹӥg3OKO?C}g߷n^S2$wsQУnOxˆ7tЈY'ۈw[ >_̳T^?\G{K?gLmeS77c:'^#/[ԟz@߭w^uԁ:P@uԁ:P@uԁ:d0ƂD}tݤO>%rOusab{Y^9'c~ɛկ.>qM>_@ug|ͣ~衇tC_۶mO紏xv~qn1˺|y(Oͱ㻹Wy:z"tgquŞ^/:}їO<~CxܨwMl}'>AӅ7jKySf^p6Ǟx=I>>yh~sfA{@y:S|A|q=F4(Oѿ# =nh'c|PG7}阧>KT~P?χns':<8`g>7<|t~8{NW 0gҁΟuySFޞ'|nr%vnrgn|~ 4H;P}\(OcÇYey}!y|sn:~K>htA<(O<ǓtOsݲe=3b~iӦQGߟs'_O]h<.ͼ^̥oEo\@nu7[Luԁ:P@uԁ:P@uԁu7ז&w؃>"ܱcǠ;j__Էlu7pCΓ<3OL?nV@u磇.oxQ~"雯^PRg}t`rzwy{ O__O~rk_ځ>+RӞu|O2Sލ7~{gOuhX_x3~y{KK<'6/b9Cy9훨҇:;_>/b{7ɋ=gy7B-tx'^P}O?y?ɟ3~G~d]cnr~.sgsx㍣~y;eN9OgؓAy{S}GGCy}UON?xY}y0~ꓷ|@}b}3Y=c<1̼o^C:puz0Sb:=S@ϹA|ybu`8Iuԁ:P@uԁ:P@uԁq7WAw=pGt#zJga]jsŸ̫́7Y`i}xtԡꐮ~:#Ɨg]<8DP]}驛/eꇞɣyq}u͑?#Gt%n>Ho}~;"2_oSח3ua9yr_|s~gY>祮d~,GCc$_bbWϼ9WCy}!x3Nz)H˽+})ƓgWGs ]10~o"y' n7暑[:P~эnW2=- tn}/wN?Q}X{ky"4Õt ><w>yho̓zաy ũ'>P?__ݨWo9N{t!^>7yHX@Xvzc{ٟ`uԁ:P@uԁ:P@ub]Nn_b{77 u1<^P_:>xOtÜSI4O^aAu?c}CO'g?C@7/~xn ͣ3IX}ϞC|>:t3K}ؾyc\e?ۓfH>xCs̗/<8ϼsC{'A|b_';7_`g{oN 6}}r駣o O솷v}ugyiӦ!/:aG^u:3GL֧N/;ӁKTcO7G]^:|H7cDϩ_<彇b] O47_^=<(Nԁ:D{:P@uԁ:P@uԁ:Pw776O:iL(Nt!Isc_g~חy9'xϾѕ)7N71b7ѧc~Wwgv֭C~tA隃OלdD9bũo/xP~S7>P>~bzU7OCy'Nw~3/A>ӅC|uz/$.g_9?ҁ<<ჺ~7'~Moq|Mou~'?Ǜ\M'S/>s+6y`LJnlyo<R~zЍ]|8Sc3Ay>y<4G}ų#OW]՝Kޟodyb/OO=O9DD<93WX@XVzc{Y\uԁ:P@uԁ:P@uXs=@?~o|?O<^tEO>'xe⦂b7n0d즊؍!>х~|y1O>yxO 3?d^)t!>7N׼엧_>cO1x|P>(O^ a+t]z@>hgs~(ou8OKݬE7CgD뇏e@IDAT^t͛&ts}x''O1뗧3O7X_O}/nٲeuY#g[OO_o/yh?}e]=?C/C|ɓ=̓t9e:_t_ڃX}?܃9%bzx㏾bu`Ye}rݻԁ:P@uԁ:P@uԁ:>{c9p5׌]q=Ёr@~;qwyԆ@7ƍ̻9y>݌Pӡ$eP?=yz>!7MCH~*=<(O '6?ϡ/QtSFy|7g<^Cw^"}Ml|7$='N>|P ~yC{Q8_r~q>9Cu}Px-?+9P:K7JH>F^;Fw{v{_1H;]s//3ƃC'Ͼw ]ί>{룓y3O_1oΫN_9g]l>={y|u guf?0:!~=C\:X^}P^zu~7O`O: ~;ޱ~}ļ|}O޻Y:ng|bsfh1fL~{$ә'z.C}<g:<ģg=Nzꉝ?y/gNX@XVzc{Y\uԁ:P@uԁ:P@uXs1@o{{<>g oy8%׾vlyg u[M7Đ,3t񠛓wB.vo~Esn-sߜS? CӁ2nݺuP^ t<9_]Cy|uusĹ<}zy1>y|hNR_?7'9'GV9F%/y@s~pnv??YW?蠃F-Vo_i?y}k/RK|kǃsC?̛@y}7cPxGO ~~b엇%X wg_|qa} bu`eݿԁ:P@uԁ:P@uԁ:b=wc:7y~ů}k#>s@Wj)OyXn ѣ70tlP>1v}Aɼ~$/c<{sgѓ}bh_zϽс$fK/>c<>x|Co7SP]L0g/x鯺9Оf=͵7'1xA<}CsY,ߍPs(<=7t)/҃x'~P}/|q~`nSO^ fogOyag6>^"=yg>󙑲wǎ?'e/<)~?o||nr1_|_a(q^q'έG.t!Đ1y?>0ϟ1y\8-%~"g~ũu1~sGX@XVzc{Y\uԁ:P@uԁ:P@uXL뮻nn~S#>n/n 8 yN7vG7'rnWy7}ɛ+g/yt g}͗O'?c8˧Nxw7^uUZyH/κ>=ăxbsޣ~xPݨN4ұ|b%'/^e"?7tsO:ynxo_?~O}|s≡yAyxt|tџD:.69U'KY>7˫ӳyP>yAOSYO~Et3s?μ:T'OtO?Tן.䡹tRϾÇ9WҥG_X@XVzc{Y\uԁ:P@uԁ:P@uX?8'|a)7i~<)K y1^Гdz<D|y|(>{C}/cyH7yCΝ0ʻƋ8ҷ߭:Z|[nf>ċ0?3Mˋd=y}dz7,֯n/qgys=O>'TP?'f݌gy}'AuΞ DۇN|bS_~z>ӹ}0g>8ug<}X߹9?'OOOK?GG_^̺>hӑ7ύiSg̓P{1uC7y'uY#F{>I1=hdy\|O8Qn6Jb~ӕ~|#'OϹɗ>>k1_~tSG>y<}P>{6^C:晓}> G?4|1<9GOG⫋}ՓoosӣCW]'⋡<b9GF$~;߹~ n3|#s!x拼!}q9~|c>=/v>{|<o67(n0ԇ'֟|A'_P?4'Ϻ~y8oRr)#V'+/V{#GG~O>y99GA|zӁxp'q^s.yHW9$>Gɧ|<.NTS@Ł(Ouԁ:P@uԁ:P@u"B^:s?sz衁GWGH y7C71fzg\7J 9tÃtts#ogPbzsǷsEGb<:ɗkpN>{np?zmNG/?y|N7CDz|ա~yb:Y?}ΓuztsPя}:.M7gsMoE]O?!7˫#ROsU>s9䡼8+b{G׹f:y^s>}H'1uxo?bH;v9zO>>teGOz=CGu|x9_iӦK|?1=s%h/y_l+=}P>x9Փy=!~l_{''OO}g}O %v(?:ڃL{9ZO8ᄁ'x@:{w⫯z]]݉y۟e.{c_>G:3cn//cP]LG?̼}`!s ]1/ ꋐ"@@ol/˓uԁ:P@uԁ:P@uԁ:0u`8 G^x8㌁:mo{@7?U_F %ƃn4Au(?bQןy}ɓ>qCu>('/Vnṑw}P䥞>ų;o>׿H'9+3<ŧ+{tݐtryxb:3<:gOy{(on"]tWX9[\0iO{@볓>ԡ> A:ĬG^՗u1'O_ S~~7=HH{|뷟~{'9ɣ xry<79b://NLsG?Ay<(Ogs'N4/_^ :'>yGG=]@uԁ:P@uԁ:P@XQzc{E|Kv@_Լ$7Ƃs <7twC1 sCuF]zS7bPA_Sbbu'VgOz0.zۿ=Z=؁/꣓3y9sxP?2O'y2ӷ>ї@}bu{Byf]:zn!}-SO?7_u੧"p;q۶m_O~8oz~탟spv~;s xY>}~=GsIպ9pc:~泎pW/ց:Ṕ^'}@uԁ:P@uԁ:P@+@ol />k[o~ꪁ_L<wᇏ"C] n:Y!vX>1̛'귟x6Gړ0:t>uُtNۃ>y}ꐮ:<}n>3o&ߞ{%sh㛧.N}쓷9_>=u>C{c/N>zx A}Γy:x zxt񲎗<1]^O>uǓǃtĩs﹩Cz>uCY}͗g1'ƣl:niG_>z^be|y?+|1r˖-Ͱ>{u}_ /r7 8pv[!KU!~uyqNbMWO=atEO7ÜKooԁ:l=[@uԁ:P@uԁ:P@Xqzc{_+nly#~=@g~wcv.G^t#!oL͒Cts/䉡>|zX| }xb7@sa%?u{ݠn߾]:Gg1G<3<>}(!N1&/cnT|K]q+|hhCy ]qt陛.OhXL]79P///Bxt#xD|!鳟9˹:]Y<}gN>o͛Gލn3T|5׌=n ݤ3O:_O?1p_fߢ|͘yY\bu`Ye}rݻԁ:P@uԁ:P@uԁ:>{98n@#?,yDpAlbu>.CGݼ䩛@uzxbuy=uH'Qm.o}'G>uuAٶm@<# 3/#N~{:~C7/dLC:keݹ>}=|Nu{ѕb{nN}_=uo_y|zӁCnCsO>/~uu97csD<\<:buH:'~|{%/Nԟy1=z/N~h{^pWyu3~uPT9<1WLT.6ۃX>TCD<{#uG×#67;(]v@>znp'yγ}Ob[&?cP=̫@yg|ԁ:D{:P@uԁ:P@uԁ:Pw776>3}򕯌xQG ܴiU—<yBf~ny>n%O~g^]fMu7̗>уGg!>3f>nAys''Gq7ŧG7aX=bsċ/Vq71ч3=1Kc?\<~_?c.9OCLzМ#NgĬ O]Cs:1/o/D>ț'xϘ^36c~q:Du}|3O^ 룟uyWקx|U7/,?|5O ?^jx#np??ZW뮻FF9>\xR7%|~ >O}qg{/ց:PՁ^'׽@uԁ:P@uԁ:P@+@olﱗہ?Á>{#kn/y&ϛ7'dn޸!10݇~xld>b6қBsŐ.ۇyC&CwsOĈxos<:Pgb<11ϼY=|ዾ>y1GΡ9Ӂ냹uyh:h|<st¬-B?'O^EٜǃP8oxE?ӧNʧ~uhNƙOeR'>u!^"|^ŃgDuH.ǗϾ7zb?G Ó'Yd:}뗷 }|ꋡybo}E#>C~ѝ7>oy{vl5PKP?=Eu:g~=7sN:P9ĺouԁ:P@uԁ:P@u`zr[n _~/袁gynik^=ѿW"t1+N=7( >bP^seϼ룣CypKO9oԣOxy{l[ou͹tgsEO.sg1W~ԝz0_SW'OO]R_t~<0Kݍ9'ҁOgTdCs!̡k7JO/97MLz3NO Cy0NW缌ܬ<~"D!a⋽'{N}3N^%C:|qw^9=':y<7}k>TC_\{Ut{.tՓ?GOh?ϹO!C}мW?^Ʃ5G}t!^3ԟhOgnIsӧ+O7υg<Ϙ.t.܇sѓϛtľ/L_>y~͑y}}2ٓ^OLW^w,O~t`7o]@uԁ:P@uԁ:P@XQzc{E|ycqEgvmYбn 4!yY8Yׇf^~`wg#;Dڏ݃>xPQG5??x \s褮>C]X='ן<7?b%CO<:~-7{FnD|s_P\XdsBy|qS g3 Cx9G照×|2/3ìO]1L9ũ+:3>O] zfng_z@/})t|ַnzωnvü~>L_ꃼ~bٟu{9 A&ዡO㯾<9f>|7xoLJx<빈ML8X=7g:#V7TwEO]Lٯx+Fj}xoO|Sxby{{N}YsWXtA|:оɣ'/{G߼D<'_h~3/և{xss!7Ǟɗׯy-ߞ<{sǏq70uY7:Fws籯}йg^칈uԁew7 v:P@uԁ:P@uԁ:P9+{vM}c /~L8xz34p=oॗ^n4O/yf˛'V>u}ɓw)ovҥ'+Vw#(Cyh_h/|yhO<}C^~$֡;Y>473]b:bu{ ַ|<:Ds͓='?c!^57 }g,O'.Ε<3OO?L~g}xCGsB<} ?$O3c}1gQ?cg|鳧ߟ2?o_TO^0ugEg1>?nVv܏`}.gWSg7;>s-zx>Ys~|(sz{衇FxũwcE/zшO;nx}{w}b:P:wuԁ:P@uԁ:P@u`E}=r:p9O=u%?n0A 覈; y>7ۇ<~X_`3]>t{/9Pt3'OG^LOl>ח!y ܾ} "No>=/yg:O_ &/uW'˽/y?zz9ONK;룿yzb}y|zxt>u|̫g>c{CO^?xٟXf=C:x97u|?t@}x r><NM:4sC+o_1Oe_uN7$_<ԯա<Cys=b|@zA zֳկП?}FW]u-o~&n~ul zc{=ԁ:P@uԁ:P@uԁ: * sM7%ܬ؍cf'^/vCX]Mu{C.?xW|Qӥ<;OAAOy֭[n۶m>8{$}|{@SO⫛'y>tFzCu(Oҁק:>8SGSO]קŐx˼}~O~< ~K>L]:OgM=9G\zOCy(:x?z}9_ #1'z3c>T AK1&OP>yʼn0ʹx@D9'^g.u|b?bhbb}昏/O'Q>:xCOybuSSg^aSCƇxbﻘxWq^7JC̓x9W~ԡ~1<uOg*1Csk_x!]#G |^6o,}% ^M@@ol/˓uԁ:P@uԁ:P@uԁ:0ul`n*nB@7%/t|70ˋ,o}b3>=OG 哯sb7県dg|C&9~ގ;P%SG;E7i.4|/g1̺l=f3ƇY~</ϙs'/^3sdLW=+g>;yE}Y][b>p?K7yهozO̺>bL7XXߗ~"S7H_^?QYןuAxX>Ot阫.V.u^`\}b=C듟9y=',ZM@t")k\@q63 n, q$.Scs7)"L'"lc&h& 0d2uA˗5w3nOY}/=s[pH}5s>Cz_Hv.>fG rn>~<KKKM 4@_lȏnM 4&@hM 4&@hM M`&/^xxs.Cs53N O KI_|^g|7j/cկ~u?tj\g_|}.t|揧7Ϛ_ۣG}||:h壆ȽW^>>0}ͳ珗I]|ay'Cw7x|sC?x|ԇSCP9>sN%:q{!?4::頹jsz=P>LzC{sܹ|}^93 ڟ| Ho>>to5}ԇ_hC W wuGm7`_f5WGsn_/?zן?-o@X z&@hM 4&@hM 4&@_l{yoVmETP{1{ 1fߋ ~|g5sAs>^6yOO?;]S'_\ʍ^: /g }/2Gy>{9_4O/5W\/>4g{Ƈ|C}H7W/tn5K eL'O=}OxZ9!L>sCW|950}[M0}P?r|\ Ր\{!:"(QÙ?/r9t/|΃s/=^_?|ҩ~|lq<~x}P?|7ü sȏ޹C ~"_ȇ99SA}7nG?:𨣎2*6&Vt}? 4&@hM 4&@hM 4&%g7UR!_,ONj}> !~z)^r8{թׇ|}YAs5t.9!s= ͛7x4s>=G=<͡9>t>>=S'As:h9L>>gytΑ<5^77{Cs~9!?u9=P;7Υ1U0yjfɷG_G_ PM?O}g|}9y;.|3ᇧϾٯƓx|}??͝y#@IDATs$>΁>gsyڵ$?Zʇ4w/N^g>7O_/K'â ^4xq&ۛxRs:|/+!9_s/-O>j:<;96oo_W<ooN& }ug/!k"?ι!?HGmO%Ont>]>o>xٗ|t Cbjs:!ۓ><1t0sW쥳&Ͻ͡>̾ڞ g5w5K⪫bUlM b{U}LhM 4&@hM 4&@hM`'۫3 I/sg}NwBAӋ|`eK}/Wgs%-w_3t͡}{i/u҇꫇<0Moz;}p_ճ:u&_=x^nA/+O9y$O?y3_I?5_:{ߜoVԩW/Cg{SNc6jhs5:5WCr?>0g}<>@x=?o3>?os? =P;rTө=Yya'_л>9=Ǘ]s}5}~s~j^Z͝Kw1Z~*?G=?}g|n|BN :~l9:!Lg>rJ/ƳG:9ӇC΃棶W=Ã/D:8%\:sAN穆|BDItxe9WCw|ɧǣӧO}9&/Opv^gH;Ͻjz~j>_6OGYf͐=^_Ч!@pn͚/A}<}ubS-'^=j<5?}9<0uY:mܸqH|0bhM`U&۫c@hM 4&@hM 4&@h7^moh@^>@/b_򲟾jY҂Cs:<ԥ^N0}whC{w*;d}^أw'Cx0)ys}^3?Lxtyaއ9>LGaWC<{ͳv>s>Pc:^snsz<5Ho>C<:{\=£|s#}5ڜ/w|s>j<}~?{>G~xt|r>%~|C }f_x~?y|S|/tɗ>W YT|!ٟ}:>ܟG >]߶m۠zin'_{GSN:t̐?Ӈf?g/ݦMo/䒁'tQ 4&UrM 4&@hM 4&@hM 4՗@_lϴ7j|^N|!@Ds~|9Agԟ+YCs^|5هKKzֳ_~v@SCxtxt@J> e%C:}uyKasO?˛>y/ytP_^jDO䏯v_z}7rq{ك fs}ߧ;D<(Goz㇧6}5s9=ģ˾yϯ=C{gCu9Ԑ>u{sC"^0xńzWOOߋ>=xjzr3=܋,/r_m=gz|/jd'/5wn}5M<}|~PO_<͓=| >='9?}U'O~rG{o>0uxj{5'-6_.yf>_tg:{gs̗s=ţڞM> C󙏾Cs'?kK͡~Oq5:}u%9|,VC>q9ǃx͡9̾>o/>=D:SK>>N^>a5Dz~e˖!ٺuo=-Z7&b{{&@hM 4&@hM 4&I/Wgٛ4& xŃgM?'O兗!_z/Ep㧆Α/Ea=~5^:}rǣO.}>}s:}y:sf_y^>ΣO/w/Ր/L^v%/3>y|O䣯vE\sğa۫jf5_?|\xt䥏{3-ϛ綇:͡;~{ \ZZ2*6&b{{&@hM 4&@hM 4&M/Wgד7& xِE{Y1{OgO+М~CT=x|fxj ̟N ;c.^YC9χ/_4LJt\~ y9HgM8礏ƣγǟ9|H>j~j7Bsu؃ȇ^ Kmsh77秆|!%/Cs#~"_}|'y>^s"=>}ֹ~gn=pO|⠞tI7o<#_@hj}~whM 4&@hM 4&@hM`&+뱛@ xa/tЇ^Vxqc}/tj.h6m4h??HKWj4WCCos\g>\/c5>/5K>?yj|054wD>3L?>޹G:@_l{/@hM 4&@hM 4&@X z&"/ =΋Eٜ9COg>ӇG={_t|c{xOf?kk0g_MKO燯Spv~>|Y'yȞ͡=9^||of͚'S;9ăt^-̟>jPNuA/ysq5Zy>}{|95>4W39A>y;K_~G>|j{թw>s?1_\z饨&@؞@_lk@hM 4&@hM 4&@h+*^QW%/"x8g^L詡jO/+yj:E/z??x7[{8㌁ׯUrɹ9K:|/bZn;o ]O9 ?5>=L/uYC~ON2Oܛ|5]BH䣟uϝ"?:3wO}|s燳OgN͝/ykoGD硧S Րty}>txOG}5]3:^s~xx%AP<GjȇNL&_9չߜ?ćC|Ͽ_Л;';cυϾҙ7钧6OO}pz<|O}>>k|sTa3/sշ~UW]5piiɨ@h;$;6&@hM 4&@hM 4&ޟ@_lQO#LK/)]?Ho͝'.;_:e/ey`G%;|{vm7|3N'^9S~j~S]{>'s~ۓ9sG = 94|s__~=|~fs}t{Cs՟Cs\.t^_ Ç_Nx}x>Z:/Cן!fo9}h:y}'k{3Wk{:~%7W>C<{C}?0ٓ|}H'?i:5=?WgkN{G:Oԩǧ3>=~קmFck_ڨ׮]ZlM 4o@hM 4&@hM 4&@hM`O/Ϩ'lM` ,e;/&^Nx!<}\^$g|!_!9uQ<ȁG~cҗtI'4˕u{m%cBƨ҇C~j>'Oҫ֟C}>/ʙ.ϥ|9 }>j9@z5>\^:~jڜ?ăx0yW'">uGnҙ͝O}|O;>t'g_>YgМRs>t0}^tOtKr>~>O>s㩝'j~jȗ^~js8Ù~ǃœC<烷>t|`_~wq&@xb!Bi 4&@hM 4&@hM 4&b{lz&v1/&"9^RxQ%rCR#iN[l~vG1oᆁ_W};!i?|\x>{r5 //_N_ :>=̾K"}:H'=g->9R˹ I8|SsÃ9}K{<oہ%w9;:9ɷ?oGǹ 9˾.}Snַ/t!O\7|N}h>CDs9?ߩsw6tytԉ|@}:9>_<?M4ۧNsf:z=ٯN]]zh{7@hMa &@hM 4&@hM 4&@3鉚@x""_P /:=Ĝ/~ɽ^zꩃE=tШ7m4)OyoyO}|9K2lذa'8>p_}~d^:5C{頹o7>uS3EH>rT۟hoDs>j~jshr|U>/wj<^"rQ-#__.9>tH|snyCwy&ybW٧g~1ٗsۣZ?>YyOԧK㣆^&^Nw}\y?}YCs{շf9{N>?<0<׬Y3tj>l>Yk}toobhM ,#^FH4&@hM 4&@hM 4&ޓ@_l=EOnJ`"9Bza6r;Wgyh]q8SFN:r{ͣ~{;@|q1 ߬鯻A{)@~; w(kD>Α}>^yOސ>jskoC<͝%=OOz>>_sڃ6?'xs?xis]sm۶a#ۓϚυ.K?1`87_}yӫ\/sH AHMD:HY_}<}hʅ>ӗ^Az5}ycNsE{S'?xs3w޼_Ν}G7s1txt|^r%X@hM`]&@hM 4&@hM 4&@ z&vS^JxA^ߋ/+!K /0f:s/ۿ> ?[n13j?nQG5h0x3FkF>n wE!!oPGG|wܫ]^}ǃ>OJ>]ry@ϟ'Ń环Ƈ{NMSC<7烧6VܞY/KtO٣k|9:lo s0yg@:>jsh·ޞrG>|S\m_sx>C>jk;!z+-s3jg>s6lxw o@8ǿ뿎i6t|Og__7n}nO~^٧~ч;Pos<7}7!}%?}r7N_GsSo9}͝/钧~̓Q'"н\'&_ 93{'O7;ϋ9Ԑt<~j>O_mo9cNO}Nl^Cs/Ka?;Wk^s.Ԑ\kn.yD|>>>Oڿ?>=_} f~ꜫ}'_?{^8$}@h@_lZ^e7&@hM 4&@hM 4&N/M  g/4^Pxqa}|!o9^zxws|;l믿~Gq@?n ^OGy{N}xy>xwo/~{=iO}B~xjWjsO^yN~B|5 >rJ~@qN|s:8%=Y?|>|o"y\Q;=Osj~t|fsGg}{7k9O?;{~$G{}nBs{:9?sg%Oc.W}/|ERg}]99ٞ/sn_.~^91Ƿ_>9!l/w5/;7?^YמDz<>!}0xGZnM 4G@_l?*iM 4&@hM 4&@hM 4=@_l컹 4ݜ'^H"_T3/k|hgk|+׭[7/}@/?QQyF/~qHG g{xg _\^|ؓ<A~|+_.!} ?}~eNC}˹~UC>j:}WD|}:u[|gtCzs53wܧ} 'C;O~e:rGgr3xΗ<|~jO?u9^"zA>>g}z5=\9O<_:u3ypo͝[xjՇjg}_uxjH 'r}5]3}CP|t=>>xx^oNsgzO}xrȚ޽ӟLsŃ_M6OD|>>_m4|s'}_t|c>9]bgPN33'/ϕ~|8/y%=Pӫs/?y"·C>z}{s>~~¼OK~z}NN=C%ƍo9j 4&vc}ìUhM 4&@hM 4&@hM < q74&bˊ<^l_'A3_-[~_xw|k^3vW}у1 ׾6O:/bR?b9js8;9ҫHH_O=:H!=ăaL41C{d-/CsDsHrss?cHO79Ͼ9w}g{ͳ%\?܇>cO=;wvg͇oч>s><5۫揯so":K͛7ֳlbhM b{7Y&@hM 4&@hM 4&@xG?nhM1J \F"~Ηla@ٰaÐr!? ˿ˁg}=y=Ë!/.Q]v{'yTr.^:tj{!=K}Γ:~xj(\oO\xjǜ/?|!?5=EkN<}Es|<:uއNߟ}zsC~Gxԟ!=>_z7чw.9}NՉy~;Gxڞ<ɟ>oz<\έ?!ˏst0YW>C>Ƿ_~|5?z^y >mF+χGg<>Η:97;>ͳK rgvѧ>Looh/6^ 4&vo}{[hM 4&@hM 4&@hM < p@xb#7 /*0^hOѧOICa6O}j]w5x@?FQmo?_\M^{Cgy;ٞ?;?ԧSC<~·oOG>jyYg=Yͳ/~0нR⩙QC}-%{?5>PC1W瀯o|;Ϲ+9g|{Ϝo{>d>>x9ǃx~>L?uγOf{ӟ^g!]?}BΥWsh=KC%93Ot>䓺#yꙏ{CUsƇ|?7׷_O_s̛sONM9'wڊbhM < n@hM 4&@hM 4&@hM`'ۻ?:6& cC}{^ayE nݺu=3 ox:k'?Ɂ\pN8asٗs<o!h{Oދ uỿ/<}3`!]'s?:} !>>xj>5_5?<>}Ω}xY/g׹@g<^M?|թOf_94WC}C/xYg?3Й'Ce?k?:?g |>tjs0joN6ׇ|{|!?s|Sxqc5ZlM 4 ~ B&@hM 4&@hM 4&@} ˲NM ŅeϗwJ?s#5z{aunsg9~˿?O|?}C9doo _/k糗/?5=秆ş~bؗ|o۶mX٨W;>5ԇ㜹wV|1C|6端竦 ҩgǣ|?sD9S|㥏> -BOM{݋L~dz:_~|̡p>}|hџ':W^xt>Y뿠=gGg/?[F&@ +@hM 4&@hM 4&@hM'ۏ<*@ŋ  }jG Ӈ|g[n{s9' \ZZ5|W^y=؁7pO|oL?}3 6 =}ΧNz, |D/{^"=GS|V󷏏>ǷOM0y>'}Czt|˿A>g3OăέNs}<}h=|x|_9/uf>}5L=9~ޛ|a9aq[Y@hM`&{0nM 4&@hM 4&@hM 4]O/w=*@C \s5c__? 0#9#{'>iOgS3W\1x1 O:{Fs/u]w֯K/}/y׬Y3/T W_=9p g9Z|@K}S3Mޗ;_sz9>~>_ ǟ~6\AsrڇG3O?R}{9~=w>G7so_|껯>f_?9w}|䩝'ϝ^_m}sj<:W''_ms@<3>>0t>|zsODS;G珯ƣW>\9/<˙9xt0xro?I%%} 4&l}g&@hM 4&@hM 4&@b{+ 4=ǿ87yY /3g^tx ‡N_׽n6y^d?)O뮻n'FE_>jw~wF^nݨsC9d1 |y?܌rF ճ=xt3s5KGbC!=ևWf?}g;_s}js9t^~H8WM~xA>w/>j<όx^}=P?s^K|P?_hKwOD~_t\7:}AxAMtJM9~r}KCz_^+6&M/l@hM 4&@hM 4&@h@_lb`7&xӟ>|fPȚK̹{>_e˖-;gq@/nQ_.l _/Pc<ԍ7Bd1NS%\mدN4O}h?}{d=!_O6?Ce)^ӧO4?Οz<>x駏/YS\|򰗏9]蠾}js|}~x'j|k>=jW_\j^P>tΑsx0S!O0jzmNoN=?(ɧÃ>7sH|9y94NtC|DsQg}tR_}ҩfr@IDAT%!>竖kr9ҫ{_1z+^RlM 4(ދ> 4&@hM 4&@hM 4&'ۋ3* 4녆ˍ?O<|yڵkG5y@?SN5?/+i̽ h~672sEKCN?y=; <>WӫgߛO}>9^ors:s}KsA৯ Dsz5^_ǹ0{Cs}js}s{H/s^7a;Eg<3=^jyѩQ':9ԷŃPsg!t_7맏's/:^ѷ\>j.k|z<}5~b//W7W'O7߯>ܣvN>SCWCzyӫ}{:?z{cwW]r%(&@ C鑚@hM 4&@hM 4&@hM`@_lϳ 4^b$z1=z˹&%yB/N>A;>Iz 7[ou xm#x |p 뮁sE ro:Ӈ×L>oЩP?y?ƃ3?||0Y΅tJ5?O~s9|{ ,:?z{ӫϐ.??5ee?^Mϗ>kħ왡7{@>^Dg?9}w.&%ڏ\w}OO:yxin94ZU;?_h9=W?/={CzH~_nsnG\ _M{͡}||s_|r>˜_"C}?jsh=jsGM^0ux0|'O9C_||9o/>_y>x0}'9=>^^Ο[rTӻ>̽ꜫ滊=jΣvԙ%?N:A{͝xPo3szZ/B{fH摒sǜ9atxy^OQC|<'ƍ??_lM 43;? 4&@hM 4&@hM 4&& $@Xy xyK 7DMN;1z"^x9|蠗8Zk֬}9s=w%h>Wm9YmOs|}>y/(ӟ_O|Y 瀟\YyoCG=gz{SgΜQ'W_}/~N$<Cg?޹.}?ˉ_{ɜNH͡{>~95\!D:g9O?}E 9Ww>ƷW~x7Os~xt9!5䗹r拯v~>!9IM 4&!M 4&@hM 4&@hM 4& f5&Ju~{顆j/Eg=$o~z;~go=6Gm/̽PkG8{ EAD/}DξE.\Mm:xj>x^e}s⩡>]As(O5?5̾{%9R砧SN 84ǷלUmN|'}|.C~t/N>>rN'Os|} ]N&Α}z}}jas?hWxs8't.5?>jP/|xj|5}Ω@D>j7=0\m3w>{͓1WGGd^:bhM b{}X=jhM 4&@hM 4&@hM ;1$gq ^=ΩN5?/yfq}HC|'WC=^u^|}h=~5O_:7 ~ǃ/.:}oHO&pkB~Ntϭsţ/gNw?s{jz}h~o>>só9MػOȲ|a1 H<$!NƄIC JـeXt&^08D HACLR"14]DfywW^<z>~|^9ܿ p~uS'_Ǘo]zCo|c7h@ha}~h=rhM 4&@hM 4&@hM`cN/7OwoX^bx)z^K// ~[|-}m^;kx! ]t8``YW=qh_|CE/fלs39G9GntY?|C߾|1n/y~܋\겞~VBO<}Gu9AyS3>ׇY}޸WN>c^7s>=?M=| {aN?G7yzD}>yr/}>撛S1C>ͩCg灎b.9|W\9r9zu!=S<@8O}7'+O/6&@_l[O@hM 4&@hM 4&@hś^y12_Wnxrd=.li6p]wxRGϴN\ [ouH;!^<?"C<|g\ݹq>P7z9FnNͫ9zSsx>WwYnַCSύy}>83g>4ӟ!?H9'}^n|rsz/@?u>p6oEC傼0u3'ϑ>O-Sn>NZt;}OM 4&n'5&@hM 4&@hM 4&@$Hiho^vx F;^8;qk^~V6/VpOzwa~/dA}󸾗5o}[믿~w< YwQϺ%AI:}漺yss~}:ug{fu~BtpV9q>PקgusCR͸nO_NCa5uAz>z>QtC}{ͧ/7aqpwNsg|G=w_uzt0׹㐯=L:?.9/W|q} g^=W=u9CWT|n<衾\e_=Ls@Q_t>{%YϽxb9>w_}^j|C?)-oM 48^? 4&@hM 4&@hM 4&1&;7 4|Etvm$vW|衇}7="_d/yu2?ʙg9;@~0p/eaw~>|$C8<S鋛Y_+A:<٧S"Pݼ>ԟ}P~8>z>}NP~R_އ>:+W/ ݏ>_^W;CE@~м>Tw9n>19NO{q}s8}|܋>3~Dz\=9S.PszY0ccO-Yd}GΣş>ЧIn/4';Wruhٹgy1s/ ܯnn|!=a<Y@hM`J/7WiM 4&@hM 4&@hM l )M`#I Ž^t]wugTnx|^7??\0oh=/zιph;찁sc_:sN/TWq;>s_Hϋ$~t8?C}{ԡ>N3tooCsХ>u~P?c>zޛ=Oc:ҙӜ9uhԗz>~r:>_Au=t03_:н%ҙNC>.7uh?hWO>ԩC}{9p}Oԟ"yʺs껷:Nf?YO tyd7? !T>I{-~W}PwxUW <^{ <묳zQb_oWB1ǡw}(]zϋsesPΟ9s=-u2祧á=ρo+t|C{ y?C:t8~=ؐ<߬/oSO?疫89;~Lʁ~o&/{}t/7oN˹$g??}>yۣ/}_ޙ/Dt~(;'^}{^C>9q}zΧ8cC_:4GgzpH>_]Σ<Γ9zD>}M=zu~3C:H~A1Z+V )6&@_lon@hM 4&@hM 4&@ S흚F%^~xɱŇ9/wy$o}/| /pg=o?Eqo}kO:餁^xㅋ93Hu{|tО7'_:_z77E{ܗ/4Pݹ/?s޼E ݋??ա:o΍˾:}PtN7tΛ(uܗ9w?qr^D~|-[6Z]v@N_lM 4 30?ު 4&@hM 4&@hM 4&&Gۋ5//BKEt~Q[nc%] v}?? oV gҧc8ys3|F?qhA9RͼtNy}u:Y;̟,/}D9\\4}qs΅;\ǡyCP=:4su}0Ň.sIaq>x烎_r=䣞COg}sv7O_~}|q}u͙9n}O^^?,:y{㣏>7}YON|{1s7Og%yyԩ;z/>489us7o_'8ۇy}oqRcO 4&6b{|{&@hM 4&@hM 4&@_lopi/6^tÏ~_Nx~>Ӈ[l1>5D~QSOO??> '0p׋eP߹0_KrzM=A/9g>{e=}u|o^uKý9gs/?s6GOOu9zg9e>P=>9:sYDs0O<Χ݇~1G?:?ա:?yt||Ss{wn>:<:D>sz:=i顾}8}9r^|蓫ՇΡ:~۟|p`3ߙ|#sy}u^~O4?9зomg{}CG}tɟ@hM`#J/7WmM 4&@hM 4&@hM l )M ^x$W:^U馛| ^0tAzrg1sR)_7e/7d/w^޹ա>?:Hz>T{ͺstzyTwIs;yzΧ?Sǝ|bΙO]r|貞}.OݼCs ,š\qOsݘ:?ӗy:>8=}u>t;'#?swNG}__|^'sŸWηHg'ڗt8=t蠾?y.:7LJοz={h%Y;΋s=wk'x@/GY/y{C>>V~cx 78@nzQ_|1qs^^Ћz:wPI_=W=}t~nw/}uڋϸ:?xv>:禧KL<]"}=9\.9{䠎Wx>/̭?O{z={hO__{9p Q\$h]pz衁}sŋ^xyB1Go~s-[6pC!y{>Γ4Ooh>u7ǟ.}S| $>}t8:}3Sd:No:nׇG}3]q\}8]ý|t&W矜^^Q'yӥo35˟_~N%>QGaԩۋ;'?us'g8r/}2@s|'WR3&@8sﭛ@hM 4&@hM 4&@hm}~t=xhG>Q?8gR{ɽ> =oছn:K/m/j%h ~^ӫCN:iHyS7q}{sދ uy_|×s/u:h `9| 4O~p鲟u}痨o :{y{:Wqhq\b5gsi.u}Y I=n?Ny}z9g?=:~Yaׇ!YE=ᣏ>}Lzs^ѫ-sC>ٷ99Gf qO_:}1}pսsNԇoV? O䫞ܾW9|.9_Cu{蓫/B~0Yy>ϭRGOOu||^|>t.>PWߜ~}SLJT4εO8_粏_77Y8O8=ԇyuzHMNg:eH=k^=1||oޟuüK4Gϋ<|9O'z~!~ՇCO%WO?Ѻ{r-&@h@_l_MhM 4&@hM 4&@hM  )M Qlqځx;o/˫]wu;sM74p뭷׿~%KzQ劗0C/Se73~{} <3GgNϞRӛ篞:n#yΙ"4:t|$|!ǹNꛣpMI7syw钫|}޹._ΕLJN=ԡχϢ~D3QOt>~%7Sqh:TO8w`q~<љgNw.z:y/Yׇ켳1A:Iׇ}f>|s; s:?SOP=aɇμ:zr:>3ЧO_^_=9Hg}ΥN}m|WX@hM "i 4&@hM 4&@hM 4&b{]tz&R~?? ꪫz}ꩧ7y_5~x ċ!Z'М,җ4Jgy@/TyA/]'Y!_r^}fz:s0t^.Gg{맏L_~>ꐞהE/ωoNtxb794snѧП[>tPzrY=}GqٟtP>ׇD1{NC9=;L:st8zr>zW>/w>=F.]:.۳,6&@xB}HZhM 4&@hM 4&@hM 4u9^? 4J s{}7 |ы^4Km/GjQs;>dV>'zW\1$?jR>ЋQ7y>ӫ;}/K2/W~|\Cg>;anקN^ys.\9h}՜>9GWtYO{ H^OӥzgtAݼ~9zD:8Lz|^ ^p:{>zCu~0'|ͫ'Osy՝ǜyΥKEz焹~}Ӈ}ty?|%_TW :_G=ϣn7s7szyљ78TOYN߾=:=L"g>~gι'?/:~_s@}\Psys:=T/ա}37'w"ΏߜWW|ttyO3ѫAu~/}ps9[ 7/~@/^r~sN˖}_|k_;/qsP=SO]zsP^O:}u#/|~5Y_rXڟ׹5N>s|!w:}"ᢺs;Tg_f|͟L=_]y}9SmsWwtx_|:O>?Knor{N=pVC'Fꫯx= 4&b{aD4&@hM 4&@hM 4&@_lKFH_N;mk^6l_h~O|sρ^Q|_T$^=Q|?όg?فW]u賏U?HmN/}uӥe7:}0{de_]/C>/ȏ9|9<;/Wu:݇/T77>4o'W|yN4Cݼ:7S!8:UW7?C>C9o??}x_fu|ü/ϛ/k>W7>7}h.>_orty~cV@hM u}֑u 4&@hM 4&@hM 4&bL@xv}Q_W\qOqx) Nw1Jw}=c9zN-WX10qnrA:\"z>t|g_/?nnM1f<=w;7?usOp>z~sw}쫻}P? 顽9w7g:sO"N 7G%s9zu?K_t袋p~&@hk@_lMZ6&@hM 4&@hM 4&@_l?A,c=v^ |7˗^y^}ѣgk{Ab=|xʥ^:F,Y2\ݾΉ;}ty|Qԙs0PK?:\y^'s/o}8 <=|y3ա9|᨟zwnϙy\CMnQ=e^>?M6dX>s_}y>_H9H;:nǼϏ>t34g9uy_Ou|ԡ:y:u~ΉAׇtpy<[[|>;y<}sޜ:C~Os0f9p{K䣎C~ޱOC=tqGZlM 4&]@hM 4&@hM 4&@hMH/Ի 4J/_|8㌁˗/8∁ysŋ|2Ͼ)>:~e/[ṁ>C}RϾ9 yuCSCtO>sO!>@>yÜtq難|q֔<'s.@'ۼDpe>3N>7~rz~:QsC~nz?I}ӟAsko.'Ї X@hM a @hM 4&@hM 4&@hML/ɴ 4*6l̿]x9 '?9x ' KHQϗ%^%{hu]7t5 _%oN_/q9<'89q}p_pGt;7GO?<˾:4Kz~T}8z}О-'t9"oK.7qs΅;?>W\9F}c~kꓺӗK>HMNyϗ.梁|"}>OG_O4n}qKw9,:묁+V@hMiK/(k@hM 4&@hM 4&@hD}LM U7  K~9D緇 ?L'7}{<!]Ow}>tBu:>}oqzǬoOs$Q7g84g^ѩOfR|_G>s/:un?8zYۯJ<̟N~rzD}~ΧHs/3O7C~9/_gNK?:!Hyüd]=P݋Py^ssNۓT}96Pόcϯ!r-@hM M]{sK:p0sS7ׇpߢK|}f:D>Ον =ǝOݜ;=g?|a3\.y/xbO^t9o.E??A{oN7?C{ݛ>:HH/ste˖~.ʛ@hM ө9QC}ݓNNp:uzN=AK8c:O_z}/}rνp?1k?Sf:w/?ԩ}q9n?3qH/ϣNoSsr1O^?z9:}~м3o;}.r㣞\s烧}|CDsYyg~q}3^o.ѼgPO9;~8?>|?_8?sNxzϜ{N?C'So"s<'Ϻ}s^{gڛp:|tIGdz?Y>g8}J嫞~t/C0뾇TO}yAtK_ԙ׷G~VOϺLNݛ݌O4/8:O|n_9_h^w.tP߽zuzK,a1PΊq}COәGoxw@hM y]@hM 4&@hM 4&@hOg}tY&*-r>3+c |{?n-/SRe2ןͫW2K.ȇn^$se?ꙇ:nOWK!>=:TssT7:?u\_Ͼrׇf|9Ch"3Cٟݛ.1GSu}u>WHNh4ܜ{&Ww^>9ӫCs_t~3] 3O;]Ω'|%WO_wO\:C/W?N~s0 ny}Lǜ~7Gg9{Rg~|pu=ysN\Y?|'_}9}ҭ37w߈^b(M 4&b@hM 4&@hM 4&@hMH/Z%1w<ꨣr)<ȁKzhԗ.]:p뭷/Jp"_wP|}&5}/nì{s.EOgfwHCusr7t9O.INra;'t^sf珧~A}~P}웇Pt8:\?̃.p:syuzxЧ/.N7Czhy~ˁye;?K~ Y ό? |7n%gy|,I'ŋ~^C? |ٲe sYz9:s_KztwN>t >C:8'&l2~3|fu_\}|˺s&NΫc}^g}g?G=25p_\݋N?qs䷶>|׿ Z աsY=難s>:AGnt擧s¬W^s|O޹%KcNKs\rzuhTO_}AׇBzpHWO쫧~{q}zNO4ͩ|7NK7C_\+6&@x&g2jM 4&@hM 4&@hM 4:#Ahk^{5F 뮻o|_[K.d#%_)ttmX3fܞ웃^Tϗj7܃ѽԓOެ|n:!'/eBsǼ:9is!QO?_p>!}Ӈ>1'<<:n\w:{9hN\ڏC{ϗ.99t<|z#_>L,/w.\ =ܜs9nn֧Ӈ:ͩ <>g} yK^28 &YF{(^n{bԟ|C%Rr6W_θρۗy9>}L/ك';7?ua:_~p }bΙw5՛ͧ_qWg:Yy}>y/u:禃Mχ^SnџEs7^Ks8LOo/g`;>scz<8Μ}!E:}sAEs:sٯÙySÙ_Χ>A<>}z}u?NS2Iz:s^>>'s~w^|WXUlM 4&'G4&@hM 4&@hM 4&$kVM pɒ%7lnc=v;0b0븗+7<~{u '?9Op1>. 3yQNnv҇?Uo?\:s^ҧzCׇ|f{aշ^u}pǽp1Σnq>P?19:>1=̫MtgE}st3|Os|ω>_\9p>t|9WwnP9tΝ>P_OgnQao|!}W7Hu\:έNO_N?ӧN]xΩ}>9?ӥ_}Og}g99χҧ>:|yuh狛;7>TOιSO|9zKsC>x|ݏ]>}>: us8]o^ 3YypׇYInN?=a㼩71u>9;T{}y ٿ@hMH/Ի 4&@hM 4&@hM 4&r}`h~n?3E;<;׾S؏yZI,Y\q.l[oM2м}P==` |g~󥎏:;C^b>5ѻQ|B9:9t~f}O:7x"ܓ>xi:~P}?ǽx^>}su0G?r2%>:Ds:=>T7{|8?z}~OᢾCo>sC}{co. "x(x6z|B^/~w|{ptxaGϓ9}u99oޗ~VccOꓧDW/2,n&V&@hl}~4=XhM 4&@hM 4&@hM '3K_:DtW1qsN}rz˼Sof:rO|ljh>9>|5>Z9 Ӈ9gHw?\^?븹99O#?K/_>|L>ԟ݃}w.sz΅'қP^?1H7=yu}{`Շtٷzu::_A8C\g?粏ۃ7\3}DV}>_w]u}u>Gy|!/}K}{XB@hM`O/lM 4&@hM 4&@hM 4'ۏOoMIԧ>5@/[GxG~i WWogO<ā?/kB{9省^|{[^՚# s^"K8]"?7Ax"==`=E鲏'΋CPApH?Cz{R}ν`yu>x"=n_#u8N;_8S?GׇsqD:{|&0̧yz{q:~tPO>_>r9z~3HP.Qyϼ:stp<'W'D:?}~8ݬO[߼Mqs|y9Ы'j%Zq/Bfy~|}hOq7\rC=t?sxE bۋN:i z |0K/ZҥKǪkv#<2 \h^H?N{aO/͹>HPf3>.d]_>eCu;~|:s0Y>0u텩Й_t>s̩WOKn_r%Kzu>84yѫCu>Ρ곾yٹ!o?\_f:n.yfέ}t9.'9p{9ԟL{9Auh>:_ᐯ=tP||Yǝs·:̺~"9|9}SK?gs|ͩC}>`#&@hM}|T=hhM 4&@hM 4&@hM b߃&m;}xa9昁V#[n '0pw|d$^S u}/8!s88^}9u\b|D?yg{g}\ǜ:nsz>t9:0oU)w}{n>84R7Czؓ}KLÜyd=1˺9"ϪCϜD:3:s:?HK:{p\y!]"N_|_s#/5Ǘ>K|t|qH~c :=_QW7뛃΋/bvwS@hM`K/׻nM 4&@hM 4&@hM l MIkF}v/y}7p[}~h/Kfse,gyp#LoOC}hŠ.}}s!ׇ|psPGSnN_}7q>^_=t`gstB:/SO8Og>qF?}CgC>Pygs=}忠1y^tӫ9gt|'|sًA:Ng^OԓC:=x9>T'}N_}wE~bqu8?NϾ|ͣu 7 뮻HM 4&b{z&@hM 4&@hM 4&ƙ@_lo{o~`+2^\xᅃz owqV[m5~Կx|/KKŋϺ=|H!?ӟ!/ϛ8?f]V7C= 7鳟u}uGڃy~oߗt =Nա:?Ws;/n|~"9:_c΃5-L , P*(-gp`HSQJ`JcQXFG@q(@1RF@D[QDK.~z9˾yNc7}Y^{_I*QX\1 I~utʧ/!GΛ<*9G͑87u9Ͽ{POsQO˛Լs<~)57ϵus}YO~˺1tVsSwçNӗGbΛy\>ރsi?:\s9_]//}1xJӗwg˧WQw.|缜kyOR̫˛S}Ԭϊ=9uG>)ǿ@ʼzr?|#9Iߊ+Ƒ'|v%P%P% tc{ѿ>@ @ @ @ @ @ @ @ @ @ _~>m .6;7~zUWxzW j?q*3&KM>gFG'Nuqj>MbO]?ͼy/j#I?p 7#VW+/'ϧ\戧mɧ'Nu͍6~YOLs?5{^#֏/77|~f9wb}bTuj\u̗O5.>7G=cܨhO>y{+<ϧ9T}2.?k>Sys3μ9Ww;UOΧ˿7կ=:O7z>Oݗ/:u4Α)_s>壞XSS:UyT=3?|usS>jS>1|K:?M'sSg^/QR|/>guT>\.vu>>Os>G>y}e]S޽e\9)5_]\1893{g>㼇S7Wua~b}C=9T=e]s|tSmj˛~=w#Gu+TKJJ`{}(((((((((Xo;n}6hwaGy}w賞z#_n#~?tmao>ߥ<6lFL?|7W]l㇟f >̋i˟s:OS8稛oy'O9O峞sK/vO|O\:U'o扩>sEҏX_T,w )?(6܇_Ǘ7Ρ|)SuȺ9S}T>O2v~C/6GL'?K˹+_^_O_Q>#vyT]<|'>Se>ī>}>b?럯=N;m&lT-((%KKJJJJJJJJJJ`i|}c﮶)}G^QoX򔧌?P=y#~8GO8_|Cu{ _җFj=zgmGtɍy7rcǦS:u)75Wq1glUR>T>Ͻ)T]~Lx͚_<>yϭ_XyYc>7㣙73Ov+'G#~̊4݇Oy}9/UY7/c/*9(~}~/)~Sǧ.|#o>#e3~Y7_ޜ7gg.XyTOcT=i)|{?ey>k{;G{8#%P%P% tc{ɿ>` @ @ @ @ @ @ @ @ @ ,-^ZOSk}{՚s;ʾ.#e > OXyW_}mdPIn~C?؀gڠI'/6ks壳SY繗s9ws|x\Ooy>E>l5|w/|S|܌_*9bʗ2<ܜ?|{q_s2T}yngyݛOQ4yb>*9w^3<>y3/{N1w>1x|#;W=|S#G3yr8U|>7K׿#{} ^%P%P% tc{ɿ>` @ @ @ @ @ @ @ @ @ ,-^ZOS넀 ïQ¿馛ۿ'>q;7_yCwkFl;73oS&7x Su1slSO59GLgeT?~s?uy|C)YygKع'V'q2'|s xڨ˾;f]1uP>?cOz.>9T=9/c~y|ii{s|YϿgsu4sS=u/ϟsO?Gs4Tݽ2'׌/q_+WtdJJ[^o_}JJJJJJJJJ'nl/["{s9Cm8_r%#j.[l0w>1W\1.z! }׻5OOq?^uUCsSov{V6MSO=uWmLmr㠾˩ӬIgC9G:G=SS'N_|T]TC(XX]>cONOQ94yΡ|9өS+SjW}Qhbs3v.9WP~>:wSy>}?oJmJ̫;G'曕OyT y3o^~>yTfOM~*> |T}Wwn˺>jXLSuqb~ϲOL$|//?}sT>[G>CO>;((Xo tc{}}(((((((((X8[o] ~=uy |w'>C? }_<h?qg>쓥)iM0mÛ~ӟ 6`^wuosu/?6뷙$Om$3vsOS݃;GXW|ԩ̛<15W<՟y}Ty|;G>U_'ϗ75gN37GU9G?zz='&7GqO~~q|;;+ϧ/S物>yb奔Yϣy\uʗ->S|uS~b_]L~|>u./O\)͹bs.|49/7W=c'r~?N|{X>ßy/}Xb/'=Ɉj @ @  JJJJJJJJJ;]~+tesn7wNܠJ`%` )"?OGiM6fMYGg? 7po~~C=t _BwǽlpOmznßܹmQ>WM#4Cz:y3'N{ӧ}ų9{RE'ٯNT|y|̡QsR/vT_7婼7ʛoΣds>5_><oOOL}gs>bʧ_ysT7WLӗyub>y*O|T7_Tw>>y~yYD?u.晓y O?WPTuϱ. &g_2OOSWOuqߌs|R9;aw3NR-((#^uc̓ی?vcj @ @ @ @ @ @ @ @ @ ^,uK 7QffmeSz |^S%ԧ>u6 !2tvzWMMoCԦ"IT~r}b~Gb?OssmRe?<5_<|9s>y4y63}̓/NΥ^pTT}-[6RYy+yYY7Sߥk|T긨OOrN+Q<ռ>/yvOsq~3Syt?'Su|_lnS9WS9_PO_JJJc?@ @ @ @ @ @ @ @ @ @ ,^/W,K ݽ5,h6O^{m;塗^zP=6;1?8Tݳ6} $jÈ %o\V?*56__/ϟ9VT\m?b}|<|Q>bR~ΥbTϕsМ'V7_^w9W^y+z(((&ЍfL @ @ @ @ @ @ @ @ @ &Ѝrz(UxC2 WmXO?}<پ; ~|I>7Ĕʧ3|:~~>1<SyS}|?|'gH߬\lח4}q?_1=YO_yT}Gs>}|T=7~s)8}bʗũ{|ԩ>-|a>U7\gX]o~19knTy}yϼ~爩yb溇 ky܌|_`hGqİCgi@ @ @ ЍZ.(((((((((XXGoSG>q~|{^^=gub-st^x(nܾ㎛S}3˗(j0C^/Sy6͍F9O=le?OS~\;Sϼ8y{^bw.?8_>̣P>sկ?4;cUYS~T>ySbOS\y|ḶyuS;_Ls<6kelN9|i9ٟ`>zǗOL~;WwwSb<̗>sS\uf^|~)2FG?Q(((y/sMb>ĩy7G̓q'7O]>5{ɋg,{9_9bu_L7vN~w/2/gN}OL'?_GͣAg=ߧ9H\dO#O'6z.sJSg8{ybb~>j>QO^Td]Oͻ oxЕ+Wj@ @ @ &nl&KJJJJJJJJJ-nl[=_W/~.'<|z=-[6ќ;nzI'4[xj;6mRS^bʟs4gL>S1SsS~T?X]hrQ3O^lSϽhٜOɾ_(s˼:\T<5/^|{>ssUS>s_=5Qr/ϗ7?S<<*?̫7/ͼ~.yuON@IDAT7՟<~~*?囚}QuysS|*S̻\sϽWO埚>GNݗ|h_bm%bnJJJ`=Vu@ @ @ @ @ @ @ @ @ @ ,^/W(uE3G뮫n6_җVYo&O?}kpst*}g O|}QG lɜljSZme}*?Us.gU'&Os㍟Ma^y'S>{?cuystV_ʻW2/6/QR<ާw>)_Gny~Ts>y7O'sPs8U2O>Oէic>囚\\yqΝUGs38}Ѭ |W_7x8&UOշ;~k_;R쳏RJJJv׶((((((((((uC{O-Aꫯy6h|q _cu:f=~z!F7}nۼμ.'D7K5OO;S}zۘӧ>5G|h3vOY^|C=>uyyT8gޔSs݇sɧO\~s/UYg^=}|C]l~1߬8狩8G<_}J|Oy|Oy8+:h'oʫS>h{yA~{2_>usϗuyTsg闧tO\ssάt\ʧ/Yu?'byj.bb)7C?uT]ΛHqH<_އ_4O7ҧ:9G>x>(|4bb><)sQygl#=|~S>1{Sy>kn*yb|3%YgT-((XC@vL @ @ @ @ @ @ @ @ @ Cw R }sqC}[n9b{炼/Uw$M:U 7mGOũCuUs/ˋ˼<}|b~n̫K|_̗y:y/>g_94}⬋ͳA~w>y3\yNͱ9y6ǽ2vnK>s.OgyԽbusWT}J|Sb:u;G˺<9S})3oJsMz'y=ģmvXwߡ'|Нwyh@ @ @ y^L;JJJJJJJJJJ`-Z%XK`??ۼ˷Qgs1C MmrFm4F 5mT]5n۸<|Q}Թb\>y*O|\~>T?O՟sf_ԩzS/Y>?jԽ=9Yw.M|9%6GOf]/u/bs\GϺ̋ԼWSuϝ</Vz?J`8>zw}wk6?Ϗ>ym6S  7ܐ 7sy6ԩ9')7U3μ{~uS>}ɧ=;>țC՝O?=i<>\yȧ_̣SsS=S}Ww)77,(((LkpǗ@ @ @ @ @ @ @ @ @ @ Y^<;Jv'>1|'?zKNCW16t58[lŘG>rNlp/#wcN^׈sF7jF 7mxq܌:Uח1U.VOs=/kN|;OC3/6'}?~uT,Osԩ|mâN{ns̕OjS]ʛ'v{s>T~y?e]lsy|R>uy~Nt?UOY^9f'N95gyrN'O3O||{;RzJJJ`gJJJJJJJJJJ nlAU%p|[6rˑ vc{u=蓟U^oyݾ[o/ঞ[L6l{FMWq*9'gyuP>|fy9YgsOf]q|x~q}UḑN^Üח狩S}b>{OOկyɋ/ח_w{h՝Of>wOOQ#f]>bSq#Rsԩ^;+Vzg /D̩@ @ @ }^{B @ @ @ @ @ @ @ @ @ $Ѝ5Jn뮻n4nᆫ`co&K6?OGF7}ꩧΩnpܩ}UW~{66&ri熡~\w|t*N݃wȺ>u}|y~y:<9uy>6hS(>g=':˺ϧ*nNK6=<5*6N)5?UzN>\hGQg>ysׯy~s|ӯnϻ|x.y;W?囯{sQ}͟>m^{9Z%P%P%p Ǖ@ @ @ @ @ @ @ @ @ @ nkw  曏)SپwZuD I`m=׽nG1xP?kf7ԧ>5{P>ݹ-O/_>lgSonƞ>1O?oj9̛Or3Gyh3W<_#6S~u+S}LgsRWl{x:s>27䩾|S>g_O~QSyω|rsO:.6GϿXlhQ|SϺ}>an*Xi_Wڄqą^8x{[Z%g?7*`vy?}zmtn[9u~6Ԧ*9iu6hεA(oXOU6bbyE=:U+ܜ+O/6W|Ss1/se^s3sRS~1w~sOn:>~u{>MOlU77(:U7W>_]y扩T]Uw_yS7O?|T.6o=O݋Yq>яoC͈j @ @ :&Ѝuz| @ @ @ @ @ @ @ @ @ J`-xӞ6/_bUlF%Pq:h覛n;=#1Щ?FF7A~ '̩Fw M?:7 &|j۔gf<姙wyq~d}V3<|Oqsyy>3O?YϘ?yxi Gy>s<<5o3=}|b~jf\>Ngz˯?5OϏXs>>2O=K:7Ww1n>zrIԼo?RssY>5=rwyQꪫ?e_g1 O>y[l1Zcy{xFUKJJ`xsNԫq7`iP%P%P%P%P%P%P%P%P%P% ;J`="|~ף@ ,$xe/r[WWwM;对opJsc#u69O(s9ӯ_LQ>ywKj?wy1O9rns~Ş]/uu.7C-/yKvSj @ @ ,<^x7*((((((((( ؾ 8-@ @ @ mf[m6կ~uħ~ .;oF7=Fo|o}[CmnOmtofD6<)i176hS>y)?‡O?M?X_*^2usW~yž{_QSuyqyT?u/畧7_*T}zqzGԩ:!zSqr~37˻9G~J{[2Zz롯x+@ @ @ ,P^/*(((((((((X5nlK%P%P%P%p qw}3c]w1=/oM;9믿~6n1U%]k#_)JhSwne.;ū!k^Siy79Glg>94~RͼT~+u|~|C=/O>>1'S~s)˚~;(((N z%P%P%P%P%P%P%P%P%P%Ps tc{.F%P%P%P%;xaݮY3a{S.Qmws;mpe]6S6 }70C~JmLs2K56;cʯ×O)~Yo/\uyW\1gq KF ozI'>~Mny_̍s_sL5G^T='OQ/hr4{SO<mρ>Ou~fT_?nySnɺ䡻;kJJJ`"yQf @ @ @ @ @ @ @ @ @ -OB @ @ @ !6m6okhcH_~d;7?Oo̧/bjy4\~Sb.K^1)Oy??6Z%P%P%tc{^JJJJJJJJJw^?}(((5FOg56s]]:w}hHjÛ~s7MCmxOmrV_|enlSuM*OMIQsթ~w-+x}Y7=lr<5/cN͓f,Oթx|_l:?՗t|g]߉'8+~#@ @ @ ,2^d/-(((((((((@7O@JJJ`8ꨣƬ /p\ |q?? \sHܶM?Q_{#Mnq iSԆ'g>ڹ!&XxJC=g~__<>|3'6O}oJg~)ן>sTl_{\Py_kJq?~|Ε7OL?z6tʕ,(((ENۋ%P%P%P%P%P%P%P%P%P%%P%P%Pk;X|[׸꫇>׵6t9Y}\r}9猼Ε>;3_b UnsFT,OͱJ>y~&~>盫\:W7m榪||bu*oN>9TSyCh?Kz~s?'l=|VX1Rnf,(((ENۋ%P%P%P%P%P%P%P%P%P%%P%P%P%c54qcvqQOxVii;{^7?ЩMn꾋&(uKmm꼜cS\u~Lk(((ENۋ%P%P%P%P%P%P%P%P%P%%P%P%P% ]cW{M=sҩA?Fws'?9e˖x뭷z ٝzUWө[|!SmΣ6yT>ΧN>uꞔѩs|ţQ|?\>̣>i337՜c9fXEZ%P%P%PK@7 @ @ @ @ @ @ @ @ @ R'Ѝ|%P%P%P%P$`1y̨UoN~_>Cԧ??-oyP{Ljswvo U|w-d5y:U [>7pG9?cT[Sʟf̗sԧ/>j>T]UOmxs757ߛ̑S||O @ @ @ Z!pgmxG!yj8J7>sGoN}qnrSkVl֦8u YesSӗ6ts7a-?5TKLߖ[n9,|#tICwilm\%P%P%D tc{>V @ @ @ @ @ @ @ @ @ ,U^oU%P%P%P%F ;mx{ܠ ܌o7mǗyٗy6_lX=/U_\us36GX]jnNg}j<89󹿼YOMu>|Y_bŰ<zAeK(((%NKJJJJJJJJJJ`R{}((((BZCmXө~߭mۆ7?=Zl͝_j6jm)"Hef<5ZLmo4/bOm$۰vQy}b:嗧YO3֧>'q;n-[65y KJJJ`=#ЍqKJJJJJJJJJ`b @ @ @ @ V[m5~*#~whnx _sۈmx^|#96u^*oX:*}/6R6[ކ盥w{Ks2v|38cX>]rj @ @ zJc@ @ @ @ @ @ @ @ @ b%Ѝz(((5N&a9ni.䒡=Ҹ ]veܕ=SԡЇ^wuC}77} '̩_}#MnTG fq*?UC^.oa>u|sKu~͑ST祦'Oӗyu}t*וW^9ZWX1:59(((OK KJJJJJJJJJ`Iz}(((C5yhW:ww3̡{PzիF{ ⋇yCwuסQ4~<mшk9:tK/w*6ی87Ԇ;Ka7Om жQM}Wvs˘Qy?U}J=>s<~>u\ĔJc^ߦzT-((X tc{=KJJJJJJJJJ`b{co @ @ 'ӟt<ڴFl3/{ }݇zCO<ġG}(Dࢋ.iۏ9ԏc-E{N-lٲ#GƱabjS0}63/ө:s|>R}~77sSs1(va,(((B @ @ @ @ @ @ @ @ @ "ЍEz(((5I_?Ɗx뭷[ >&M푼wRXtlh__'wiM.ỵ}7wg?*o\ܩ6lYLOL|ϝşΧ̕7W̗T>a,ڳ[/((Xtc{{}(((((((((X__/_%P%P%&cq7k껷}vm797tM:j Z"[{: /%mOC}ڷm[L/_>ɜ6)n8SS%3^RsnRy<1^9昑ZrRJJJVI۫d @ @ @ @ @ @ @ @ @ B%Ѝfz(((NGʳ<̑?ꨣo/lznYj\% )Oyʈmrvi#nžC7 f4\ym:y?3Rç?O>(_|Iz衇I'4tʖ%P%P%P%0@7hP%P%P%P%P%P%P%P%P%P% tc{ޯJJJ`b-^{л.Cm(~򓟌o%P w} -[6~*~?Yy&Ԧܔ۔Nͫ?Xf3o?U}Ŋ_~im\%P%P%P$ЍUbiJJJJJJJJJJ`B}3W @ @ Z'pM73ݻ]w݈ ;_W }31/WCJ`83%mlvz戕+WO|b}&l2\׹Y=+oHr]669ѺN; =Í@ @ @ tc{^j*(((((((((X(t/sб͹Wp9q(((Xj|>h_׆8|Ӟ6Oz7((%_b覹-oz껻30Y=u_~\j#[uUW =Ss9gh@ @ @ G9Ozwc{%P%P%P%P%P%P%P%P%P%P @7JJJJJJ`.h8lp.2ɝݽ[ C llof#}{k@ @ @ * tc{X,(((((((((Xl.@ @ @ @ @ @ %໬A4G7p>ԯ&Ԧ n|'|򈺩}+(c @ @ "ؾ]T%P%P%P%P%P%P%P%P%P%tc{]%P%P%P%P%P%poƉ='?uԦ?9˗Fw?3ոJJJnnl.lm*(((((((((XW(((((\~㤳:k;nm]pn7pkJJJV@7WX%P%P%P%P%P%P%P%P%P%P@7)^%P%P%P%P%Pk駟>&C{tҡ%P%P%P%p3nlcP%P%P%P%P%P%P%P%P%P%tc{Q^JJJJJVg1gonG @ @ @ ,@^/W*(((((((((&Ѝi6@ @ @ @ @ ;?%<}(((v?%P%P%P%P%P%P%P%P%P%P@7eKJJJJJ`aaoJJJ0nl/ӫ@ @ @ @ @ @ @ @ @ @ 6nl6fJJJ`/9& {Ѿ^J7xL9((((CyI @ @ @ @ @ @ @ @ @ <tc{j)((BU7|;нkcءJJJJJJ`"}qv @ @ @ @ @ @ @ @ @ >w @ $|s9g\|_Cnao6#~=}{oql0c|Q%P%P%P%P%P%P @7[JJJJJJJJJJ& tc{M %P%P%x}'׿>RnP|:x)̩?' ӝ4tmz8ꤓNC_%P%P%P%P%P%P@7ov@ @ @ @ @ @ @ @ @ @ #^G{l @ @ K_r'8tM7zW}ӛ4wVV\ M3 `ܴ)J$KiS@6fL᥋8Eki0_6CV*M B("[G"?<{{.;x{w^S֙;wndVXkkk#uwo=.K/ÓO>׭[9qȼO6VQ2s˗G;U)/  @ @2TlW{1+ @ @hD@v#0  @@54n9ss{Ν~9GaʕqYi/|!.\9a„ 6DΞ=;nK.իWq"ǎYzxꩧkZ޸qc\e˖ѣG>."@ @Pݾ @ @Z(b` @JȊ[+uҲ_zu\7lذo [#Ef%w4^?d#<]͋ܵkW7\.ݻ{ٲeq~m=\|EȽɋ:5 @ @TlW+2A @ @8\e\_3 c|dK+_}ոnӦM,=]oY|WĐ D~/$w]wE͛#_4wя~YZFi%xMh,^8FtI#<9ӎ]݊ @Tlg @ @J@vU.%@Y|DXνZtk~8rѢEYy/ڝ;wÃ>B+_JO=|;߉3fLs8O@ȟ?C`'NlP{ܳghO:5rܸqE;o߈4iRx  @@ خ7d~ @ @ P$bCT@y}j=\\W__ٯ_7yGn۶-EQ"y͚5=ztdVloڴ)Yi3Dpd8swCsG=hРȿ;v뮻.v 'Y_z >|xG@[ɻu{r]6~ӟF.\0.k<"{ymEy睑:u;wnd͘  @Pb* @ @hh7 lDrɓ'ǥzwm}뭷FIO|"y݂ #2+O9h׹wwM/_gs٣:o=瞲묳܊{36{f%+?FV9衇"33fLgw^Xnذ!NeEy߾}{7Ȭ!Mg!t|'AMO>thO:9  @ P]/Ŕ @ @ @qۍ8C:@V6/>ffiΙ3'0ϟviq޼y/#,YعUVE{ȑϏvyOG;νһb;=㺩SFn۶-2O>d|,{?]2뮻"ׯ_o|#C_*ff)&G}42F'x"ܹ32+so={6qS7_wC t|ާ  @"bRބy @ @ @@Y*b2hH +7tf„ я~4r푻vJ =w aڵ1~yF^tEY?bĈ d\lYs~y{>}zy80ړ&M=p/hװabG]2rϽo+׿uM"sOt@G۷o~ @M x^~}<ځ"W^o}+2+=y/vV~|^redcz1$ rG̽s-C7 Ьν{;믿>2+sܟNǏ/~1OI @:y׾b;E$ @ @T=5$ vuYq~1+W^1+_xhgU=bĈ$rڵ˖-=sCF}"ݣ:v=x["ǎ{ب?ݹoG%>cƌ闿YfEu3gΌ "FR>.=M @P @ @B@vU&$@I=KڳgOѩnݺE{ݺuӟ"k׮9sDh4qx~c9&r…'xbdkYi~g6y!C޽{G^w_so|ҽN.I @Tl7Ȣ @ @*U@v"@Nc9gq?a;""9+s8aqώ "kkk#Z|ȑ##;tM1STd~">={l[n=zDCu @ @u1 @ @ @P]Ud  @jڵkL?+3[LY۹==8>}D^qcƌl͘1#l߾=r޼yG}tcFʕ+#ٹs&;I @ @ @ @P]Ud  @55'|r0dv۷/>޽;r۶m7n<#=lٲ%0KR.+:f͚h_y啑ӧO,{|xyꩧFv=2/R|<#{zK^?9iҤ<:t(>]6r֬YyJ @ @IM8I @ @&bވ @Y`ժU1GfeuN.>Λ7/r„ Yn8gΜҊ|ӦMq~РAy$@ @@*q @ @*M@v!@@VP'@ @ @@޹'&@ @ @@U Xخg @ @x;; @ @j UL @ @Ov{瞘 @ @U-`a_ @ @ @ Xx @ @ @,lW3y @ @t< {b @ @T@fcǎN'@ @ @&b}1 @ @Fvk\C @ @&`a}1 @ @Fvk\C @ @&`a}1 @ @Fvk\C @ @&`a}1 @ @Fvk\C @ @&`a}1 @ @Fvk\C @ @&`a}1 @ @Fvk\C @ @&`a}1 @ @Fvk\C @ @&`a}1 @ @Fvk\C @ @GDf߾}-qL @ @GJdɒŋhqzC @ @e  ڵe_-l$@ @ @wDE>}L2Ge $@ @ @ uQ\O @ @e EY.  @ @hX.ܣv[ބk  @ @ @Y#]/,v @ @ @@k[ԮvK%@ @ @[._dɛxʔ)5=zP @ @ rr [6x څ?*Ke  @ @ @M-YӧOMVb.M ۥ" @ @ j\.Tb7h]&aa1 @ @ "#]Xo쏅d @ @ @@GzQ=IwX.&@ @ @ "HM> @ @ @uQ{Ν5K.ٽ{w{r74 #@ @ @fĢ j_3p# UjDHRD @ @ @@GjQ{5'N,Z.ܿO.z[.&@ @ @&v^j̙Ssqǽy7;J>X.$@ @ @rѹ=;_SHREca1 @ @ P$ܢus[ݧO>aap  @ @ @Y¢s"dž7ttEUj.ܣK m @ @ А@7^z5:u*E9Yɽ{/lhOnoR:rű =h @ @$I/mERIo\ @ @ @QvDN @ @ @@% :)]Io\ @ @ @Y7nv @ @ @ e}ᗵ5}&@ @ @#"P___өS>m9_>TzCm @ @ Pع>IENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.9/spectral_cube_import.png0000644000175000017500000010030013455362716025673 0ustar noahfxnoahfxPNG  IHDR@|gAMA a cHRMz&u0`:pQ<iTXtXML:com.adobe.xmp 1 1 2 ؀@IDATx]|E_.ɥ7z %z R" H""E(H(ED@@QP$!!=侙۽\ ~w;;͛}fVA1&ҤG~uffxN¹nj j5[('LaCD& :Ktы`\G3V{ҺL+(o_ 2wCvh4J!7F}%dc#g$+^ƛ!@л?jjSkgg8k{!&|qUx/GO kFziD䄌>]Sm\ W{Nzl:)޽{QJ6' ˹8=ҤqJCiqOHi\\5ҭM~OɖLU bbA {C }F?+-܍x4|Q-Ϩ'wqc$'{GKB$wAOzp<J("C`v+"*A2)Ri厄S'򨓢qqOy{@ٝŀJ֫H{BF RUd;ݧ *UWwNzq7% cnƂ02ؗL/^)Rm%`K ^$ytip0f yjuv?Ÿj#cnذz3Jj=:ByC*I|hE;,:oX{KDVa|47vc׿rL}5*k&=.<qD/!q $q Gri8g6nLBɫgb 떶#Gx4UlC1_sAL?ۣ77ax< gՎ!o YQj` LLZjaY[=ofqMUL_> %V:sJ [B#Ч'(6'BH=1kO0g #=6jTː_Yj{0懝;ݑeQ]fA*Xwk1IMttfFʢIbmbFF,^$m9G [t#`OI VWp t9#.l?N\uӻa.o[nrF/ȷ+ YfH|l:z$ڑܡ1ztiKc+}7=1߸3m*!`lVxEjbܱb3oףl#FaÕ7GcC<+vC ˞b5|I凒V Q1+n|%>}%yóqBڊ<+ƥWNI|GlZV'fI> F4PmLdU  H^1n"Q@.\RH̷m?O1P֤=3eNIURwlVOL)dڒ& 2P鏎blTIz@ѯYr#[Žn|T j!86/&m-z}hJ%I ZdV4ˏזT+ )]=Q"qxO8NoOXvR3M:\ V$F".vi2~ڋH;aaܼVJ-\MyVp\ e+>ը-6zŗ(F*SD ~۱bDT؁X<rE,܉k-ݴāM949?Èn:ߛg[n6Mع}+~1w.8yr!'2QZk nތM~F Gw ,ߴ IK|8,#q [s!@[%Lߌ[gI8kjI=n6 ۶m&8qJNXf7a}o`mD֭{8ǮY,Ś5|BRf~Stή,:^uR\ÿZ sV{i1"&]?½Sj O1Y8SW^_q=ǎ>aȒe㮹䚓=7qʤg? k@hnA ,joY  %GkK˼eZ(]ZM@{?2u}T4·әP&v;̸L7o#(yS(> ovX=Ϛ\3J +5K>hь/B;'MG 435jnބdjhT,WVD6({xN~E$44Bw\sT1hߊlLĤ߆==Ѫn2 ʸ: oԖ) bFțl]'ӰCf~ N n7|/*jb;vKPj KП?YMC8N=lF1}@s*1 =-kU"~gvDVU%p"ϛ_qÈwAXpԮ%|˭.]wOE)HֵiVC1Xכ7:uLL[୷BN8'rqO|iy+F<2uP#ƂCVc)K!z%Fq@&4"Iն0G7tmR]Xףl0n&L g0i(/ؗZiֱF)@0A^?kfs|~=0kgN_xW(5IC]_jrGWTxo|?8?GTŽA{@]?`oj 8sL׷+*ᔂ)@"%+IO_i<3T*wl[AtHKŸH8d+]h9г ?&їQL ׶zI*AXso#@0O[9D[FOBꕣz`{|ȊUyEO0o렻^t}4큩_~iF H{RG#0~x|1ѵAyPRQ-?Ai0:up!qͯ eKy y^0s??@jA%Qgr~4Pw>,Qf uaicPLch~5~' 2 Y$RFD5x6bgpummxU4bNtnmY7E_JQ S.%/4*횙| mO@'M~3qQhj#FÍdXn,N~ Ϣ#>!I/d=G^ʵȐ8HA4->Lު՚8UKZh+Q(uwp7oQJ JgUo"#:.)ON i¡P~: urNQ;7ǂx+EK^UC\}H eI}{/^6dEiGٺHVbgA)@A-e*Gz:CnӾߎ_gR$k'EGG^P[D /m`\~ 4@ _U"zkAU%GVo6 4XX%t%Zˆ1IJX~LF'!&Hnߚ欓toNANunlq{*Ŝ0q_2dݑ܏&ȝʡzrE3c뢄R4Xak.$Xȑ痼tW6ʹ =Jio!Ē4KĢiԌi78Au&]ut* HWUL\@ d5QhQB)ko6k𫒿ף{{4Ug s򀽙)Lr썚A~ĴX*ҟ63%'VvJRג%ORpLI^wwJ z2 .I-~^[i5k™pп/B?ErV>rl'D!>Q%P'.=dRɔOEx禿dLK6ĚcA7֥֔ Ͽ 69W,k:T:XUdU8fF3&Mn&U%;[݅"%!ʽ K *Ni=񂏇 qNt}Gз..9'8ˍ5%ּӤ28Tʿt dfj1O Is+-~|ҡf}*U8Ul+[1W9MhD",|wEeAfzKE ZG"l,gހuq,ő}@W 䎵0ۑ][e^b}qK+yY\E6͇`$5I^̎lai2YТڐ0_ǾD,,+u>v, o?(yE_M"Y9UэvJTC2ʨ_ݚq2R /?x5,0Y5_щ܍57b؍;)JEo[1֠K96-02+3-)d{ rP%>Ɨޝ1K#=VZ?!G7NǏ_ĵ hymڸe kJTB)s +YO|p=1}Np#6]9+NBYQ*SqV~o %E~ |xOFudɚ9v.:{6Kzt˵3#/nwt1n:cw.劣Ey.wĢvc˼[:zO2Ro"*ٴ-[fsmGS}bz#[V *h+"/qѨ2A=Ov?+3iWeI1 ֐΁w꧲ѽfi0ZݻwGp&I !q增$D>B_YoIH ]r{Xbh~KCjW;O.{UF!U[?c=L#SUR#%_`QIß cn "o^,kf,O>jx]8c֢CAC# JSM_%;@:Ϩ+N/e2{-?II^7Ӓ]cήAspuܹs 6ЏPThzoݿXݩ30_pA"ExQQ =UOR c{8{x>`]EA[3}9ŹRuq\كGcW"1O#o|b=|Kpq/? a~q%3"'];]cA"գ)ojH[#BkiYi `xXkt"XBE(Sn H<~0U6fHb1yγ\@|SrJ!4wܿv!=4F~&6uNdEVf9XXP1LrOF-wF?:M4! ꘳3a2j|7OrtO1}'Wva]&~֫QaΣa,nN|JWi7Eg-9jk\}{koXH3WՓ(+}&Jt ផ[o RB{dӏg6򓽋,CN R9]A[Ͱ׫fQoelkh*4,@V^;olYpw[|N\ơY1X<a訫N?xylcm]YO( Nwr*!* 0 n{;1]!$kL' t"4T 6{P ZF [G>v޻|\O>< za6񷬓ӸIjŜo@y0b15ܤ2BY,K˘.2p"QiQ%J]c^^pMi禤ߛ;kS2˴_0feW1tnIS;,cI+Ξs.^Zk>@#k95f"H1g"KOO7~jEcBH$9 B3%q5qLLLDXXڷ9'S+ƋX#ҖD< J.nixu 8Ǧ!8wl햭 }%}Z'iĩXFvaM*o6 ]&cnPnɔt،Қ@*?%܉9l4+ qH#7#9'wϬY;//rXxȉ4h*;fD^:7Zs ܜdQ+; U*퓣i,ɴX0SՒ)okW-X,,r]G̸0ػw/TWW) yq_Lsqz.I㔆@ҟ&=Ҹ(!Sҩ߻);P goZ>Lȯ7 .R:01Q<<=ډ3VXU~bºܷ+7/Sv <7Je^ [gkU\}6F\x->Aq[|:i$6x,!`-b֩|]i4k0ruTt vn_!Co|gHr[+W0S #>[ɓtS˵6<4֫~yCY_+,SJ(f 7Kv<#0S;Dz22 VwRcV,%{TŢ}}aFQd A\:sή66.0~$/xWͬ?LgE;4ZFki)5cVzwWq;x^8Bjlc2pvhotZXm *5jmN5`N@`0 C K 4ʲX&C!`0E^emb0 C K%<,!`0 Sb61 C!%L`0 @QD)@EWY C!e d0 C("ثM C!`dSe2 C!P` PQU&C!`0D} ,Kx^FF|t4b㓐ak {z1GeR Ԁ=`Oo9x¦@f_ }~vXŽ8- G7+Z\2 @E)@7ƚiM@J]w7؀p_?釖 rO3c5XQ YZ-ѷwO4Z(?33~;>{`O,N60<#FUZ8l 9Qߒ^Qա9Y?q'O"4^xg@weI{)I+@qa䕈I7;vXAbh 1i89cؑ!`;X>wyڋv12RbD6y1(NȞ'Pk4z4YjO2ɸĨ(DB~=w^hI} QoB&˝,REbx oS4)!6;h2@Nғ㐐G_q;$#1 ,oԟz!`q(;}Dхcΐf߾׉އ*oӱ {: &vn^;ayܼHO#I1| R]xm}kpil[lP3[`&bZFT "[ <$bDI K8JS94kXUR HyA5=pAt\QPы״f~$+*uS1۵pk5F6RaȐSH>c쭑"a0Ecgfdȶ5nl⧟=KOMyM^:Z__~=13V돠W NOmNҔfi`Io G7/ͼtHg͈CY=2pS|ާMqKP=¦էHe}oUp׃ޱc! s &4FA:?JMC'9ׯuëlieC!ZнV1fᗰ/(W+ cߟ'4NPF=2ΡѥwnTNͨcNQn#>8$ llмs*&Wp8u""JlY0Ў'wq9B~'NY>K+Nypƌ1S2դ5wk1IРh,GxR ?xd 5/㿟CjxG(*ԱѢ=Yw2 eC!(&X)&hX*#&Q˗@ZhkwMR {LpI:][RT4tDE&j(Эeu WfuZ%}sx1##MdV?:}"RғL?N:ƤC_OGE\"W**; 6P`ߑiπxJ~3aJ֧|u?Q]ݮ(?4LFa mEآY)DFwZdgGC!P` P>)ZrV! i3|{V@ow_sp5<BӇՇ,z@N扵9 /ss6g=ɖM:?vXEF}mΝ[E\!;]'#Noݺ[ۊvb^JHH"cXm߷a۶dz7Y=ƇT62ii!%>CC}]x)o#5k*{ajٛKkX0DZ u#C!( 0(zNC󷖳4 @_з2fr;N Zh^X4+ ]zŒUf3$ˆlrXT\JyhL~g UTPEoM#B( YBB;Ǣ 8D-'"?'& =lhg7{`ٌbd5%u/'$x2vVMꑝH l0n&L g<0ظb%:j 9C!(0(z5=.h- SSbݺX7ĸ4+[U&s'8s=fLkOh}k2S΃ͣB#0ȯmFmv wjk>i"i6Ni7i5C|d =5ueDJՍ$} z%AJ8u&؋8^N-~U s7(!`J_ e3 Б+j7o9 ߳>O3عГxzp&Ο8N#l (W`w*HL"6%H8/ GtE?G|B ^"%>q̳k-CJ 5hQPX?IK,)! VAW8ev2z,Q$]g1xt咘BjދMrVzDڈFUna C(! +&u=+A} \&^@vgnoZ{gOT&3 H|mUG/Z瑇6_[a ,35H{LF2%xq/W$ƹ)FZrU83}$1*k:_J塿Od0YϮ`duF}9yl->.;j3\=ʠEڸ[[X, ZL"chXVT*|N=\K$ !Dw͋'w4N(Pj%OhP`L>(JllP:FIJyf*~eCѡd#|Ե Po~UboO:4$_)v*U8w!(b'QexHfvԐ֔\b]M)ZwzZ_ e~M$eC!Pd` ~Va>/|9sV& 9t.!gV%/ L}ꙟ2IK66gUQ톕g~᪼e9.ZdWց_i!> YćQֻ3wid&5PYd9F`0 5Lb]ܿ_֮K, B،>/2+[a;qC_D ::n_†c1kC 95J)ĺ銦SFAvS|!߼zǸv(|>˭C`/t -/)([EI1fkqZ&%"S(-f{c.=8Q"~]1Q( xXq"Lax65GT|J|a5QHJ~5@ih\M6&ILLDXXڷ'_.!=5OE">.Em%WL)xI*\5_J/>8י4<<#9'wg3Cys˔4ʠTfH3,yrBTJ2ƊNpt5O5/nK/|g1Cv"dj|6O.dmF`0޽{QJ{-lll2rsqz.I㔆@ҟ&=Ҹ(!S@ɷ+G Dv ,":y[,E ^gLlk~\o%ıc%o`ʞUvЫS |v`0l 86kkBtnFN?Q>xxӮ(λE)r Cu#,@X 3ЕfY3&{qmhH%*4~y d d0<L*]Xsۭ}rКX`0 `0 C!㬽 C!`0  C!`?9k1C!`0=L*C`0 S_3 C#b? C!(~09k1C!`0=L*C`0 S_3 C#b? C!(~o>s Cy|8 |lTƀ!`0^)eE33S2#6tDـ ٢9W-kDSNĿ^hD* R{B$c7]Ÿ{E(z|&hn֟s L[8(bdd~|df y2 {V3cMT;{^2*cq+z8zk~S~2R g9ʡNJ@J)p .N //gCO9)cWL*Jba0dβ)PyshdžuB }NtcGb)aUZ8+X(%h1{ =\X7|8,M,p!s+/,VcC!P`|A{N?#7pZUFzo f,{fbƙl!{O?իWSP& ?~5?jA9ÎѤmo~:\RIW Y!`2O%ZP'n)׮ g)_vv^ŷ}ꛔF[7 *>vvvwr_*(bDO"8( 7{9^<;OҐ(]*K&P_I{y_xH4.ݻm3,F M_ ԮJ2jҶaw" 26'6R|,m׍;7i V@RdZ./ECEhJY7Ř5 wA%fyuI0BzrܻHpvw/)G;DF9KZpEt!D={#dr߀?+A®GbBW/-yy(%t*ͿjV\ǦXzlѰ1W0 SsL9 y6I1dV.Ɩx~-:&_0s?B1c1o38 wGgB{>~LqfW7J(<)ʙ=v=99ҺMKp<<ٵT Ee6{f?jIXl_8 .znf9E_! DgbFtwS+8ֱguEr&+i|9 `gT 5e0nhT.?R5M?41?l#gq"%cN %ʤap@=qi9\%:+w#%%-}*khĕw2 6I @aB@,LRYK|URl3جC n_S~Xł6 wad͌V(ԌCi~>-%uo[@OM۶e,ۖ(uY^ұd'&J91C'q=R诀WdBw@ᒹx6aYqHb~;Xds5|I凒V QÓkD&ƊZfCρc9SՌ*?4xt4/?ΥBPFߑkMJ C!,@r/4hQ Sby֞N=n:V\waʷT ~XV/B,gGj!S`#IڝM8yZTsTih+ FYSg#=Y(u9Q{˟?YMCDf,_߿z2tJA/B[2UE)hQB)( 8{q?bl l/|d>neQL~5H9C&E0sBZ1-^T"纋bM[em\-FVf"MaQJ!>A`Tk7| njȉL)d N|.ry׋iO3Z"~W%KV*YhYݟS'MM}9=&/;\G|1X;c~FwRT@q:zD2 66nX_IjhH @a@)@]uOqJj$:I]zVOїdOV{H8TixvC.Ѩ߱bzz#ikʉHl"S47vN']t -oT9 <{'= ZC`wSx#IGZl@#FoeƣB#0ȯmucgqGJ"6u!;)r+PWߙXn_Xot>EYk mY vk+*ғA-ݻIUL} I dҥp7J!yIяU:`o Idv2zs0}I Hɋ  %BgS$鈌LIz%/bܹK4@0Ed pT;Rgb*իnKoJtdP6ItzJwZG'&Xb$B?óy>^IDATOHA|KbqiB ^tuJjf~*!>Sv2X+, ki8J(qq<܍r)=3 S^sE^Ojq[@ZrU1!EPi*ppPO-u1F4PP7@Nv9;JF\~bg-H[8՝L+odY?gɒugw)A_f'ݎ[vd&$Kd0#H[DxClJr'o BQ ɱ*T.?"7}4GJINBHp<Ѹ*][a^NcS-"%1H-V/bSC %"-уw;, (.N\~:Or}?;E8fmʭ2a5@1 ׏S^c/YmZ>37?Ǵ>5]JFTbJ !#:t1ez]pOӘmoGoW0!39ZFUv}.u$(!\[nh-r}|ҡ!-$Txm^H]G8k+ψE2Q^MFF:xJX4kY$cC!P`N=YQ·WWSK ܇6 LK!_ΡUdÈ\@!9.цlQW}tΥm sXI)1n~Dc;p9EW(0EUqAֻ3wiP& Gh́PK)ᘙO+ɇSY`0Sڐet0V7OmǠNjKv'*‹lط|n@ @Ϟ=ѳ$<V =.4!nBv\O-Q>Ҷ죳F˽c·ڽH tk[zv{Y.S"ף30% >>A<6 gӮRG#*]AJZ=w _BjW;OV[+RS{*`l k@Mc2h +Ջ8}_Dj U9!O*Zy"=~^Dw'7NcÏŕH]}BCӣWP33z^ ƌ!hS#q@aض;atޅl-ݫ~i~)?]ݼPl>6e^ā}PQSTT yRh'K/P+q@3l> nxv`2MDfkZoVbˇ[uz`0!Lr&ȷn͒;iy)8|B`Ч!+0q٭ ^R-YdL4E;ţĮO^A}IJHmߍ|r_Vܑo;ŢE1a|10~8: 1UnG ¶?rꘋ7Iwf W1`)RKY)QYٷ$1 O3YPBtag6La09B$|A^a^6*nq,&T~!aQv2&S9t861Gݨ+ŀhX !5` YK Ʋ 2zŪQxx~ӏtPVuvzͮL&'1jN[e[w·sǐV={dM 4|! ;g0Yzz[ i# ?J&Y"Ӹp.1|ʁиl40oODR|Npt25JMCR vt6TYT+YF)[U>dFkȟPs~9JXm@NwC (,?MMCHN>J˹ek~4X,,=<W,, c`j݋*U!."2rk5 ?& 4. i# Қ Ư(YkE 9::{T&֓3t9)a~׀9^@K.6zb<7G7-H/·+F\HϷ,S,w]ЫS` C"`!7V!`0 @V.a0 C [AAFj }*WA!`0r#ilIOp_3;]PyPat xڑҙx$pT K z.aʟC}Ed<" #lNV抛̆v@rBqAQo i i8rIEa7k.H_Dz2,j!R5iX ;ML#侐Uئ.2! i)q)Ҹs* 8 w;sm,:Qny(m`'' N4Eri4Û~IYS5]ʏؠL9'LkOJffynCH?H" 4iۿ(g2 $}e5t0RT_ʮ>2D v\MqfR*{L'J NmS__䅃puF9p'[{ $߯ϷH(F(`{01͊S-@jfEND yfT% ?e1HMIǂ%1Q*:(BD-]S/BO;wU8J+ vwW7Lc8 LBHKƿavd2Bv >i]{cEt@^(2^D'ⓩ(>υ)?nA8p3l\%&)K`sZ0(.-ӰM|T |B]ItDpF.(7 ظʰ'8q)hDVF[Z%8W$>,л }*t!:":V8(=*BJ- _ qPV v1!VW6"-?q!5#Ȝ 'wͺAHoy ڠy]5fr|L Zn#; 䦿u,x=?a e`D2/ )_KF$M.pqb:O= AgGbvO !MHiDto*IX`vXDn1r6,ePJ坑JPʨVS:$ehShFx-|Ȕ{8BB|N ބ27• CSDeq ED/I?4ۢviOAZBЏ8OT8XFZQ(=H!*XNާS:Z:IFC?x~ #^2"A9^$Mᢖed.U 񚒐2o;ŵ!;ŒM7ls`Y ]ąGR@&^@\ZqC!oEX8PMEW-<܉h!e^%0U^҇# +*,Vk$vɷ@W)X5LՔ J4x&čLE1O= LHt4UIPZ,S}2I>QiFBK"xzra E;74%Я%qZtY6hW/;e3yy2,hCΩN@` MReZˠ0ѹf~x=;AY(~C%JXDu QƉD5h0 _UZ9dѩǷi+ّ; 25ް)us(%Q߱EUCn8ZL2̛1B=rt@k!A;dJKB MKP$׉5[#r=Es c6%US=kC#b=h,$)ˉXr1*9ZoNS{bbgtwZVQiuHu.dE Ȳ6hQN%c}T L,!͓ziӓ0w=T Qz,Ět3n;AhUHGQ bb^"[ tM(h>X>&;"{9!'iPZx, v(Aht3ef#~ІҒ:> k`#Jڂ:"rFJ1m͑JY3bRD/2Z:>=pEWbJ|_zG0hvR|<xFqCVF(?L`)뵵_6+.W3'g#5'ds0;:ct,O7VWu`we8/yK})*=M Ę{ZeG=qt~G2-5J:sw[j$d! 1& Ca2+8J23<^/=kI81؎ &@O-V[5U_޺>k:>|V]uϑ s=Po~YO$)JOHb Ydӫ2[i{0{YmlWͲ/Ԙ5x2}kߖZ2|"':ҩ? }AvD=cb>g:tS9xzԵQpJNW~zlaݕm_,iahm٠O8\ /#Zrit=$2r]䏶59 W_:@W|VNOEtunx[/@YVܟ cZJvʟ: i}Y>1VN1#=ޤGU 1JһzmW߀rZjR+kGvMIu3za!pdyօՉX8*9ŭ*m+"kn[)zbO5#!pΐnKCp}hIz(o_١7fGh~[:Un)ΦAErwoۘW4bñ|u"hRBdk\_:;ؿ#f^ޣ(==57O75Crr%K8XJ)=Gg, ezUڜ3^3ב?nn׷qZy# !OZ)ʫo6"6G:ݦuŦ7SA뢝ٜO/`2ze嶘ӯu]7^|Jgt*)5ЩE:*7Gj?'5^9 % O{8>[?(yB%(H'${<{fJ7u|D`onˆ1r5dgĝ)y;[؋>4V(ѷcE~?IiL]*允Nof=Uھ׆ɦj,$ x¢o=jAVc> 2ʫx~CF|L pl4wZ=Kg _ DTr#8Pwތ!HmG[ e@9>+@Rx7>9?_::uTa]:R}X~w5ɍmΏ"n9sL=?ԭCۿg608ʺJ@ >̍2n@*G:Ǻ*+{;ǻܹ WuFFYzv dӜn>N[c-ո,r:6NnC}f>}A~_v=ܹܳ\%[];2gBzhxEU <'s/E|Vt?.o.B:Ȗӥ E(5ꇱEesґ_L4bs =UA;ﶽuƸBX`-pF½;#O>Нa0+gv;rnyi]$ |N"4FrF59A/'Uv#S ғz5mɣ:jCf$H|)O.;ذ Eꁇ9<1PU9 r]dׯsnfǮIVY3Vkkd-pu, j"%;K @5Qf;3/UI ^99?/D5"rau:g|]9\zt('rm7YnYp"P2ˊ/uFij?F߸ҕY#@Gt~~T:ӟjޠRaw =OSJ߷SYI%)E##)gd3mvՓֱ:(Tv32d?o[V@p\1^C#c'o?8OO Q[m|l1|Nx9juӅgkQ}ڼ' Jz)vv?~}));gޭ]3;'W}{BEl8DiMZg58zy?]^lR޵-"m@s_@AQ0UۢgP KttpsnFAo??drPⷊW!7uPXd.S:DC>U=~}iciVm^=M(Eʿ]p0 trMC5ϸ.f,͗x#S@͹]otJJBM4ZJ>: U5ii'o4ˎX\}umE{t '#9Fnm%?>^&(7T|EAګCC|9oC8+?zYS> i?}3wӿ !qAXTWiжs" Gɝ7N8!n+4\uNΗ5 jy}_?T,z;:NG6d z@CaOZdo3U)<}\>3woȪ|8M'd 2A~GKezVs41ܩWtpzBPYEqJHqbW  ў3j1I>H'23m: ]%k7;4ղE^ea88~Bߗ@^MYXYi#PYamG T[G͟e䂏98w* bBPw4#4BCA?E$f_4Qaȝy:Yt L wߔ|aZ!#_Nq߹ǁzYS]LBmqNbzUYaA\BfOW: /=Dd>Si3l\=g<W.y[ Z0"Wؖ[*un=/E_]q"} +ѧ{zۥ7D0F.l^ ~LC+W,Ӻ׆ݒ[˅kP^KTutHFLi0?~9R'hsk:2vjJuR ٴL+WFjV$=W2Tף[P:%')SS@䉷ux?_n(sB>)z`K l)Y i-'N+zJ^Q_cp_p< y%#á;[&Y/N'#.\x<򙵃1ycmFǴV~6I,ڕ{g ^蕗~yTl=& 8ԯo/5l׷)uIM[S|r<u*߻CB(ȋ:GgJ7?gO=1=C}?jJ' e_ҬOL4(0y-0G}eݧN3z j~K<x im~ZRp yn OQv/Vf\yfJӐ);d@{g6Ȃ:ե[_6׎U> 8Am]x=SG_퍱>w_7+O<%ODs.@ 'ױxL,wJ))};^y։1)ߓ6]mYmЩo8.z 3TߦԪE뜮Q) F2_~yv݀]wfP-s0sp7K_Yҕg풩ZVcاçE׌e7>ᖮ%-gv: w-8YV+U+"NKߩ.Wc Ov'_?#A5z85Kc{h\Zaz>92o;HJKuE9c!.0;Qpy9A6l  Rr!d_zдpР϶޳e"fz 凭uf-~:?J7d ttF2zҍխ!fd͍#eMղcCQWfm#PJmPoX Wt~Ks E_}[x<-ן\{p}tFc:TGbؕj9JUs9=~~RyKsj;8ZU.U8H@]#m۶h: t):&cp` ?๡XڕI9@I0 -62vn201nSR˺U0 х@)N@?8 ]i}^|1rkTGdz*pZ=@rW0 ъ L!$޵1Q!`kmc 4NMj@Tu(sÆCֿ3iaxm+ztZNѹ?ӆRdtV|4NNhdر:Zt u}o}UF!`1pEN_U۶ȶf(*AE K z0TWD祔ىC=qȖKF'GRbC0 C{zzK;t`>Oȟݢ/ַ@ODh7k['/C8^2%#+nCܼ Q4s{ ,!`#8A?/xǏח?d_҆!`HB#Ag.}hdaiCyn it4qɔ)S7J L!`!Pl [w6ÖæF\c}4:@iF9h(h^Lcɓ!Bx C0 'xb;A}}}-"h?{`^{M#T3j$yIC4b>}ڵKX0 C0F:g8'uGnpuGhqA3ؽ!_.].E@(,X$6qD9e޽rYxqO3 C0 }sCαs00Ú8 L,\Gh+npry)f7QxH 6Cbv72na3<XM!?!Y!`^`az6 /| r 7D#;ȿkke˖Eb#'N\4MǂT#FIO#Nh2b +B} 5r CҔA1h&4!`^ sDnÃ:ׯ0ʃgvCaaw!3fֳ̈b;ƑĴyC,t2 !O^xtt#yC B]!Mڏ! C4/Wy't.T$]g!Pwv -fzlN>,W^yetZz⋂)3 9(uAҖu}iļtȡ X1#ü OGBe|4M RYypy>4b&u%h3A3OPLP xOA)o_b2l[m"/.M>ui C0 "bvaFy@#TFx`4_>Y8?MFhy.? !POGW'%)w8A >iOSiҌQr>J\=q27 C0`p!`hN!QÑ8>a!_M.Fy.o(W4cC(O>beq1t1F!` ©,)8; \ZSJ[C!H]8m@vcI-$Ue J 4c'إKefθ4!` @;SbApt ԋ>E\<ǵIqM 47? MP\}<74)4 C0 G~øVOjKҐstU TʛjWf>cW7y~t( ^(Po(x!`@Z\{RN[_Zmt&Ϗ.ǴD# ny7sӤ2-K:n4;911in~!`@t|=P!BCCΆ??)ѡ:]YoiC0 ClDwF\ GCJC6%9 ~>`7#!p}=ח!`!0a\_Cr>/zC<=]bbE(iy~:m q:C3 C0J>OC2>2L)!wИ4DH^zK+ԞRʛ!`!PK4N(ꩄ˔FqdJ姭/ :X!`!PTL$+bW֕)5@~cЁbN;ˑn/Ǘ}~ C0 ц@/I<$\9t 4qP䘇r71/N!`@~â]-Oi(_N\S D &qgr0 C0Gqʡ|)qv0iq$ʹI'ņ!`!E]M©Բ'՝&o 6NPӖ_:,6 C0 (Ɩ[֊ Re9a\0 C0%9^[7J@*u`z*C.k!pv!P+W)κs P=;n;v C0 C@=ʺu gmц!`و@=:<}(Y0 CzB6ykBx!`@ՁFNj8@i!74-&g!`y aTqIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.9/world_slicing.png0000644000175000017500000007417613455362716024352 0ustar noahfxnoahfxPNG  IHDR@^ iCCPICC ProfileHWTSIWR -)7Az;FHC ˢkAEEEW@\ ؕEe],P' y{w;3hQ%r@fbR2$r ' PFwס%+X@ Ns > tC|XU @$Kp kIp [Kmb}!Le(H3 0bk>LJx; b1rrfBH48:Jò\B Ysro5C-֭6kfCH?5"b8R{ ! gšaB k2DYq#ؖ%B{4;S3G숰8+23j 4PaFl'zyY1# 3|#Fmh gCߦ e6zNh^%Kb /K 8`.?nWOo ;jfF+LVH?N+ ?"RL x]KX@XhF==|O oGPeL+{Z4io# <8=p7< >`ŝqQ?D1@4a޿х7f'[<B!ALci%3A8h#٥˜}61d?3pM`LqO~P4[-O|F #,Rfw(Ոߡ?Zb+YvkÚ;`Q [ +ath),7jc]ogF %w`d.3϶q@>0mq.bLcp wtZ nHX P"@S-pn  bAr Y`X @ X 6 `8A8΀\w/x!!4h b"Έ⏄!H#|DC"%H)R@_# <҃B }kTTFщ3ꍆ44E ejFM "z /A `,1gĒ4L-2kZ\_X?'t[l<_+Z ?_WE  t,Bpp^;" L"fW# D YI$)TDLK:NL%} ˓uɶr2O^B.#!#_&?%)ɹEqȭ%*wIWnL1Sb)ŔrJ4.卼dy"rPͩԩTu5NE}Cьi^dZ>m5vvA`QXPФpYᥢtB2Ń䔌|XJ *(PT+(G*(Rޣ|^ IX_LeIGtn@K齪DU`L}]j*jjj*ՎØfa`\g|={wq .{>^K^ި~MS_#KcF=M\\s,*ӚUǻg/`m-T\+ZkNNAm@mf: /L :tt< u3՘lf9s@OK/HOCKoHD?N~=AC]py䌜265zolb`ܸIII]SiiU3YVns<üjhj30eBTKozV 0%VV/'NLnى_wY߱Q Ybjܖm[i{Վf`Ю=׾!aCG'GccSΪQΫϹ\|\|tutw=[gL&q']ߝ]H!dyV{>2xzm{_W~___@AРuA7u!N!CNRCcB+B Zw#"͑ 28r}(ܨ&'GM$&z^ž=1b}bމ3u+OP N8?bf/%;ypSz:L-z}ɴOל= S) ){R>"Yլ-l_& g->MsO+M{>/3,˫ ܖ>+2&k8;!1s⟚3sH uݘ; C£NTAGAeYV͟9|9O  e혧7oX,H]б`ᲅ.,Z%K.MXںL{٢e~ HHXtcm+]+Vn^S|ĺ* ?\]kT%寽s]mria607oxqee6Q66[6n^sEFŵJ-Z[VnyrWU6m%>mm#pGSquN΂Ov:/u5wRïFמsۣgM=Z/;uo>}- ;%~翦z@聎r~ i4М,nIj9rխoVմUU;زc  Ox1ēWOM>u:3gN>{\p|bSC~?tRKKwkϤc=/wE\w捩7797ʾv;SWv_~f4G=|#]ӺgOyBbO?4}y/:z_ _ ^FM[Qz_ACGg?%|z:43s/_C,Q MKu $xv8%Dvg"&xr naRTcjg7F$/V o1o E8< 576 200 6iDOTd(dd& C|&IDATx xUյ֑64, Ф#PSSsΡ:tK|ok":QW^a߻@?(KB ٳ_~22\!q~/7uyPU{iaU2@m'ńh@ee\y啈n:<Bd Ǚ AV3{t:w]iu9~t_+YSM"̊9%H19v{>}Z ?>* &Yib-ǷO efZjÖUv/OB@3gT= >O*Bm ءܖmU6Rf!ly ,Ugꆲs0_ ~:;eM3#]+ DJTY+WC~q;TS2v%пF̹%e NtDm&wmπ0KKJB#E˅Çc̘1CpUDaQ`D/߽uFZD1q5":=c ʽE"Ő+F7t8ʀ9N5{Kі$w:xiy V4BC/v*ؘ 19;?BKBf$߀&g"eN|Sۑ503v2T/cІ҄h?<СC8qb5Byk& !c;Wªc#41-@(gc$s~ oNѓ?ܗ@6Yog e!:LUüODކ4(V)XO2 +mIHNB`"%%>$n?5k6.iW}`y]1R\tsB2C霦7tw'b5ݱzlS >G9㈎O,B=܏wUxk^+@`ؐqsc%aA@ی%2h:3OB)ldC̑ehYKMx͌D MPAA&MaiVދ4s mǰXL@֧Z'!z؆đ3gvj#'.`A!OVaLhsiWE6Vyaˁ, 7o/> B/%PPIz'ؽuNkIeMoDFfmiC u|<8n6.V;rjBJ X@i7p6/oZSZ=yߩ`EE*/_䢸̈~ î1m6AAN+8 dO v-nL(Ƨ *TVAIyD8F =q_\]~CqIު^NC"v@_὏SQGIcqWDhD hPtፃ]gyR5y%Fc;tQ-xmȣ^;**0v,'ކ? @nJ誵@3cpgJd |6lJtNC`K.ŻE?ָ&`W&a J%H_mL) ^zwh@5b0}KMܴeZ*`GOċvٟ`u3h,.╋(@A;Xb0bl݊r襣2r#:Pr[:63s0Ӭj L޻?!9ʹt.gm֪x|-toBegMgK-?q󟟻CQL~vK[wMA|hq ~鮣xeM^nZH3Ɩ3(f<#Zr%%8@ظ48# vh_gB͖k2X\]*z g9k!D,K5u1i*330 gADgr//PuKkհ~]+TYBkoTz|8~;%FuoEJD첦~?c#Do֚A;pLb\Pø#LCk>saG!me> <"])"tSXOmoYw)LK`[`ۏ⎑^Sm/;ZBVZShӓHJ=EY]5(9΂f*!-emzd艪 O2/eT;ʠYuf:lz@+L8f:,7W^l"hծxdd_Uo} ܛ)hEU%^[jKܜ1"u&C,2}e/^o+#7B^2:Y sZ8B*'5#J6tͤk="E$KJHY'#cn2n.b+f޲hZHbeS qSxkPUzĥ,5`id-kXzovn<* %FkټҔ7s7`v_;.B L5rU`pl]pcl4*. uup&ăc*>_3dX"Rpz"'bE,DY$~(YQ*}u] *6i|!WB{uTGil"p!fs-:YfmިS$`t(vi?5smjfQY7 rSJ=)+XJ&*QMKcnSɗ9cޕֵ +c-ErUjHŚ0*TVg=#j#pX,=VԌ_dՆ?6+TҲlHAN,;=\`̣`RP4q]eqR`lV+Ϧ@(VW#A+7 <~um@5o!vbk<{:FrE֐kn*Nw8Y\~ےV y[t#mJJ~NI;.b@E 9y)~Ȝq|(B'&L!hǘe#ΧU zK'֜npKwLkMDZ0^/~'toЬ8y?ջ_Zf&06ۈ:u)YI9ŎˆX+Q7F5RI f"3'Ojܽg!n.|g37U~7=;7^Z2Za=O+1SH2ڽ>ܽMSh!gf!|]ielM hӐ6zEۧҢ,6rIDBSBuLcE c۶mx,+f)n_ Q3vuyl UyxP7S(.H8C }o=5; Vyg/I!u'5+/nH L‚&O0;8 KwҴ՛(.ƳzS.r%arWHZA1HL5I=6E6x5Ͽx![7,LU꾢6=d !zp I"4Z 9˲=/ݕ# |,`ːVj 21XUUVجG*-~ +2)OP8Bt|YƦgL[YSݻ~q:A|^ i #/bIj&,-~`{7~4z>| 2/iދ]aNF_ԄWdQ{F<' ^3xP?C-Y˵1 W+6ss ɢ/_"*H<>7d1z-Aӌ괉0ڨ8ڀX/\N8ƹ:a*j2rהbO7hX1F*w ŕ AĠwKjQ^VM?BqIo T^r"w ʭ0@8GDYU 2 : ť|䷻냨dZ1ӷ5Da..ѱzAc%7M:ܨ = L#Н?I1/2lFvwp,W3wﳌ!wsǺL1y$@@Et󸨚5E? Ń @g$??|ـ5;H4u%~|8ppIwAyi5zwM,/B  ! n!L ab[vdpsS^Y~XpQZ#__F%m8'1A,|G,@>, Ж'>O 8r{! B_ P! p$"B@! @' ˿bB@!$ IDB@.O@P2@! BI@ ! ].eB@!  'B@! <@]B@'@N"r/B@ty"+ =;@zD@ZB@ֳB@X]S[[>,oȟ=zOxx87PT%B@wҠX?gϞU',, zB޽>={bРA>N,9*m1r{! @{Ԟtn!EXa֞ÇdT:YT!'@-m NH@[~.r :ԧ#oq T&@kj %#@ljR[X`19  ! l/sbbb 77W &߮r! L"GAUVVXG,8>}HPJ5B@4O@P|Jl9y$"""p_j\veb qiD7@eB+",xqXC!О.Z_EEE*6rgߤn! :̫;?k{=j4/(]@hh*::--=ft"#B@5~"))I'sNh"~4"&0`޽{!|eҶh_ ucȐ!ۘ?VnSB`KPBBBGPƅ@KJJ0sLh a_B@tMR k0ʨ&^޴m?jUqJ[B tJA(==aaa~n]]E4&Gᙥ2%! : 7t >TTT૯5\w?)m !.:,=B= p PaajϦ.v 5&&Fbё! |I@/iJ]Bൾx-#FuDAc:hiL! CKB *ϟرc;v 22RMkҘAG@Pнrh[a9QF-ARڵ  R'T)U! $ I4@/>|ڙ}~_w%/ĥ!D & Z/&'F|:Duw'=JYYnǭڮm}gju﵇u`RK@PX)ñ@jȑ#ۥCwV[op@߭[viG*B@X Ґk! l8w:o{}o򔕉V߾}%B@VB 8 ւghq<ЕW^3K[~;~xY=Z! CKB`W f–kW\qEqq|7ʲĂYM8m$ घ&ZqL[X̰zB :t<[}8GO0$X@8Bz" VSS}Ϟ=-9}Qk@xMÖ#TUU^OxxO@vDuW+CYE j%YCCj8$$D5ЉPˆ[|x;OC!QDuyiWtbl ]YY*wYӉ_t]t"ȋa" >9aKY!Ϝl@IDAT} \UUcݴ2M(T/dF1/Ŕ 3IH:W x1R.fhRF}ڗs8Y۟Ykw];曟!D@" H$Mf5֖UH$D@A@ $D@" hrH\VX" H$IdH.{ڴisQV"pܹ+\L&m۶?˵ n5"ЬY3q IS+pD.P Pddd]'ː(l߾w}$@ ?H@F%2'{#$@,S" S믯eaM oV$@ %j#HjIjDH5TQz_.ʲ IT$$@ }t@- P-(I^#JT%@d,Gy;BBBТu{x'8w5-@Hhmމs,屣*AGHH(h;ON uomB *S\PEi Rsvꆎ|hFUFPRLD!@ ?>W 5q>fNĽ's #Xf*f? Z-G6.B[*}:ùQ7t"K0:9g#KmMvpy O$e@@(|6^twÅNa%RQgC{[^ D ^rN\o՗:}b*}QĕQ4R" Оמu.).Sl9To:$ʌi+wG*e2I^'(|z&C[n*#;Lɯ2^JA΂<1r^1cʉj xՀ]Ĵ,tGwz"t1Uh\njcޮ~\lg]>kaT@]t Jô'uW\… ʽ-[襳p]f>\*Eeq 5k!jZߗʙ3g믣EEE.>>IA=ХKoǧ*Do~Jڵk0M -vM_Srr=3QەK?~_&MvOaxF7G 5k.Ft9E㒡ԉX}GZ0d蠈ʷSq1b(EkI7]/Wfɑ?yBpK"YDdt.zq4?繄!~FuI#p|"$;ѬkcZ#p%Fw='ѷXWxcob*d]c99: "  {!R{E#.r6)RmRbڴi+;yߕ;w*/?>8޽{pާE W"ZtÌ3eMg)\ʍ:cQRR |YP{@~ ߕ+7ߌnݺGENG!y[6opmVɛ[5Ebn[*1ov߉7wNj=Fry,n ;WC QT|\cMJJrk6@/cn:B, zMq9c8ޮiq} 쩈ILEc}#x8,n6yoe1bf:>u1Ұ5; =shƋ1v1Gyh[6fOHQD%o[֬Hǰ0KFKz)1~1@_<C*Nh˸k?~m兾cB$H6$\I+mٲEh^ _=D&@Ln^{-0{v h|kn6\wuHKK 2!ӢE `ĸ  }Mnh=ZL5aq!kx!66V:iOW䊋S5jkGC׵!W]j&hNЀʓ-fOP)DޜH?^0O E`r)wضKUl;W IB맷8AF}1i!屟ƞX%Ѳ{$;?TVg"v_Cdmz^Wɍh5N6oƽxt8S$5c>Bh_Ÿ +$id"@oQ!I*7MPa8S65ؔ<̐ #P v|dcj ^mI_W?<бꞧR>c%|Ŋ !amGݱhmvڥr8L8,YtS<زm"4 !Ak\.gψa<60>~_+6-uaDc̙J;0~SOp;6l"ǏW )Su]Jd9X>>xǔ)r -ŀj2>/.í NM5L(@"&.ڗg=fciOv?]OkRS{#E8uE z{ahʎlJ4M5]`:ʉ\H"rsS\cZO!@ X;t/al Sሙ5O>~Wڱ7/<| 2Dz9UU}SOo˪!{|:U?)Yx$:RT`dyȑ"a9f FMx)}V\ $gDwyG^{5Eƶ<\ydE.cxV벞 LF؏alT E sHKT:.U { \"@$@VLEl:g@efe2ןT _HC$B$ EeKG)R U&@qllrٲevH՞4š ~^E6[yiдYX c߷"Οt> dkN'- Uj'h0dN+$'mR;<; b /C I)K Bd<}BoǼa+֦EpD1< 2d.A=6NdrN Ь~:BEnzzT_4Z6"lCx`g$ٞ_̘1 YzB~}\_, kX.Lj~OH#.pLє &!mН`cĠ6#bg 2rlK  ѤV >bi5Ѱ\8qD6L׾QYi"Ӎx<W߁08#ۙ hmxpF"@~C^~1A8C`6BS\F; Cdӥ;T瓤R"/zt`l:fE`D{BT$+E$A+RVwRA(RlHH_ D"= E<ʞ_lz˚ F%"@,x09cX#lڝiy?k^vj~]Ň:b  b`z-mMb_ƅ%[ ӟTk6b OH;k(SrS,WmjrםragHkeh' c>MFa l9| hq8t'ɗNW ($nՠS3WdӸ;Y̦UV%āO?(f.Э4@'&bgŨ8;#xYDpj Wp2)XGZӴYfҽȐ-YS ixx)fސ%jxm'L Ip[Jd.V ,9Y!op5|KwNa]ثPZr v?LEs|QSҧ, ^.,4vI#hiY4Q|3=v*|ȦQ_:ڒh1Nxu-S+"N['b>xiSU3f/CO1!3cLUؐ3J!+2'Q獚ƣ1,g ĪU2z6$Q$eVZaTj6^}7z˟D^u8]N/$GHVAK9R!Vq߂{`/yV&B3})OgӖDDӾQAhy1oH03ݯ⡁]q$o9~uިyG'0[QѰUwZ*RۜG)8wGt:f[eB'R",YCymSq`mdk~={&,w\Jſc.{nȎyO2 kn` Q:B {e^ܝx@:y'mT" 4hmMnjxfJ[UuW:κOZH;/5+ѝ$L##2f1S#1wNe(y9 9xPٕq˸u: =KaPuT7wMBس/ˆ\gCKQT|-2}YP8M%YN!%k+/ý>9+kh]ّe q}o W4>wp'3֭[IIp]q\$F\.WUpޓTG?)\>\B<)0QG(9{Aݮ-4?6:!ѹ8GEa\h{%KŅv18P<'opB˶~nlK;h@Ae;w{ez6PVe%(?UU-cQ:~c( ovZ1R^vAo߾\ ߚŸ\k2hqSI 7(FI6)D@"И1VUц5vRJ$ Pɤ># ϐKIvYD@"PHTh˲$j}@NRJD $ CV/ $e@]" P]-˒qIG;I)%0I%^#[ }.^' I jYD@"P_HT_7r%j/ Pn)D@"P H ^ Y,$^! 5@^T/$e@]"HB7GC@&R"D@" H$@AXf/H$@C@&R"D@" H$@AXf/H$@C@&R"D@" H$@AXf/H$@C@&R"D@" H$@AXf/H$@C@&R"D@" H$@AXf/H$@C@&R"D@" H$@AXf/H$@C@ gh 'Z%گ@3?gJ.zۺ^*ѵ-([w gmq0#0<*]ZY[U*]c׵<(,Q^hkЭUՍ<OߏK[G?*-ľ#hVWuX_mlUl@VHs]ݱ7(-.w u( @9՗Eًx\%>x2ŁShD|_\}*jEUػ%\Zcܔ%9H| VGLvWZ[Z.6%s]˓A9uSePDZn9`kBYѶ#GE*K;MZN>";o6'0 9"YN^Y5zJy}F u_E >{¯cx c[f&,YSƆ˱qh *NFLꛮX1X6E  ut"!0=),RK]=ͮL[m #cJO oCZ~L s<\hhp&5zm0 :O*}`-ndlpw)kv!yX7%d\ve=P5@d `.ŊmsjBHں)4y֎8v6!W?4G4kQ*%gJrPȪ D!e\RMCn;[t({⌳z 2J !g8|'D1%k'yyܹ6hӆ #ow݉g/+߁}V52޷ dMT$cf- C"U˅z[۩_W`g&-Ѓڷ[R{9xwaTvɣCS3[*]?.čF2!WUZP_T ̯N08oNQP zs$Z *Bnsgo4$-׌ilK𷩑 aƢz ?E} -=oǝÆ"hP~wӻyz 064l\waZLh`gqkk;B |?Ӫl{]K CN/6a8#fz=OsU0]晡ُ>4ͥMa!w[:4 wd]S;ml%" Pn@߃os5f /z]DXZ3UPTM!Ej; 3*Ōiwrd%!WƁڬ2]YM# A|I Y-v6cI.q +"-a5^^QIZb:K#;$AUA6Ĉ.paTcǤcbLMC2رwbĥx0.%"y0>}[Ƥ!oiBB9blAJLvst?qj*mr9Q3%Me>i*XQ)Ѱ6wA|f)| ˱iF0D% lBmybÞm4x pґiRo{^,~<K'RS+5^SmYg,2rV!!D$͈8X-D/O|je[ƔY3Dz%,ƪqpޓ(7eؒKXl 伜HwMmQNyC_j'RɏnG?#e$so#@Del(<Җ ( N 71J}o`7hV60Sd2Bdd=1I4LX@t[&"',R4E"(ܐh89̕sSGjqy"i湈L,D/!<-X7Zm=N@`-5T2^áT1H su r9p%ې0xH?OX1 ِLϤg6F>j+h˦BS̀;>מe*܀>3-m3V9#bJI#?4& l=:=/ YľWm ',O䇏/v0$מ3OZA+EOĝĐ~a0hOrdJ c! s|Ql *-&!Wx,;14f&93\Q^[nDahEi˳#Q>HXƢצ>x9b OH٦_%JKEI2EpkSXmbh2eA$F1ʆ(!l7H/y7cMoZ|Ԅ|oX-egqCX0WVQHi kﮤE_fNLLg*I[4"SUc7'o }W'iJ~ ͰAd[T S!ytϧUD ѻ,m)13ݠ%՞AS`&rDh|'YW w/a@@S]9=T[t3xl>fME &FepO rM{A=UɊēmiZ!ۛT=#sгl{j=S7w"5hT4{ߙDx cRVaUHo[ hʯkfrp {qd#D7 ?3ѓt~2hMr$i H@$MeAAUK.Q+H娪W#Zg`5zɤ)#eGKHC4X!t6/mF˗b/0eH7PA^6i騏$ ڣC(')֮NӘrpqזFvAI{(':UdieGg-ÐY<+{ e~L\">1ڡLϧN goy p<:ny{z;Mi#"$QԫWf;Ho5v1[`o1nBch~hSj2m+6|2tH_iAw6@b ; vZ1wŴϣfZ c^ŽTfckU"QJoSS-4@i/cQ4-zE:~C:"u&--nMWsGTzMb X(8&-i.1Q5m=˳]_z`'ރ;?Þ77 Z5A s39XJm'UZVmI7`o\紙MQIcC|b,xp FG0mJC'G-2=?icU@C@ jߣCJK1!VJoR[iۖ׽#H(N+ϷG QE>_rEKLiy| ;r5`o_&UAvD8-lR %Bt,< rrcbX*C^MrXLq<' $rAPUfDM} i04$.mcim7&dGۛlA@tMr0Z(ŒHmoeC,˿H IL+aXfFd$^> ޖ-Oktw}Yގg_{>=,чjۧxa*lw%2 uz M+/6p,g%':;v@Hs߯/&{A2jyG~){)TR؀C|/qO/aG7*0=eGb*2#NF١i"DW`RUKImֻ m)E.);o( GxMqϿ˷b(`!٫M!)-ouV~`Umڽ@zG{S~͠Z@~bRor<92cʛlmo_rR_qi6OϽnB^i>M!T;5=ZHW6 } 5K/d"ae N`_!T  ]Cd Vml) ]FFq2r~ g.4WVt1jM}uZ(=hӁ2͟/^:A4v//VWUVqŘgp 4J篅hTЪqUb3_LSTB6O WxZ:X:K̕4Ӆ".:cV;d==qWli8. <Fйh.Ġ_oyQ4NS`RPA;FO(9)Ny5փNļx+y .:!М!:h;Wc`>y#[X4Z|)0}\kÏ}| <"( 7~fYsUE-۷ą 2lۑ]Fm.cy" UCy+̖ & ;qf;BǪhۮԗ WH[z[bޘk^_fB\F^yJ`ZE64ISkARiih 1+EҼ&WQ8ڸZxxi $u.YušeNlܺpFNU67q;a9JpO$\{Z6ެ:ld#J@WEXwv1amWa\czh4֞Ky,ni0r֞ *E_B C?r##{h\kPJ>qK̕lPAis1aD8B~ak#-&tOÐu6%۔^Ve)p29K^53d#Cᶄ ,\ﱖdM4# MoAL}+kĘ5k9:N܌ƒsN.uAn7kl߉vڢ9dħ!ww ;cc[lJg"#wTrVAt~55JdOO}Ea+-X)YH$AfEFSQլnXeVj@9Wf y_?XQjH  Pչ3cl{x2o~.ckKѶc՗/nilpe{h5!c -Y^[^iC22Wl]u=Z&&} j{n\?Mt0F'0-cv!{i0q mHɇZ >+ơj_?Ox smhoq;K64mӑ=0'E5N!#|8h9;(Nw]F8BLqϏ}‡ӓV[W)L>8w/ӞB5 JiHd @xX5ͽWTTkJx;ҡQK?1EVԸ/.o._,}S7 )KR0uqO-՛v-;h3&uh#gH P5E^=KNܒC}j4/rˀ<” W.x \+Q3 tx߼2 sCvrb yYLrKȶx)dM'Y-64'V ;_XZd?EY;g밎$ߣF\W)5?~b)I$D H TeD@" 4h$j#H$D H TeD@" 4h$j#H$D H TeD@" 4h$j#H$D H TeD@" 4h$j#H$D H TeD@" 4h$j#H$D H TeD@" 4h$j#H$D H TeD@" 4h$j#H$D V^IENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.9/table_viewer.png0000644000175000017500000060731613455362716024161 0ustar noahfxnoahfxPNG  IHDRD}\ iCCPICC ProfileHWTSIWR -)7Az;FHC ˢkAEEEW@\ ؕEe],P' y{w;3hQ%r@fbR2$r ' PFwס%+X@ Ns > tC|XU @$Kp kIp [Kmb}!Le(H3 0bk>LJx; b1rrfBH48:Jò\B Ysro5C-֭6kfCH?5"b8R{ ! gšaB k2DYq#ؖ%B{4;S3G숰8+23j 4PaFl'zyY1# 3|#Fmh gCߦ e6zNh^%Kb /K 8`.?nWOo ;jfF+LVH?N+ ?"RL x]KX@XhF==|O oGPeL+{Z4io# <8=p7< >`ŝqQ?D1@4a޿х7f'[<B!ALci%3A8h#٥˜}61d?3pM`LqO~P4[-O|F #,Rfw(Ոߡ?Zb+YvkÚ;`Q [ +ath),7jc]ogF %w`d.3϶q@>0mq.bLcp wtZ nHX P"@S-pn  bAr Y`X @ X 6 `8A8΀\w/x!!4h b"Έ⏄!H#|DC"%H)R@_# <҃B }kTTFщ3ꍆ44E ejFM "z /A `,1gĒ4L-2kZ\_X?'t[l<_+Z ?_WE  t,Bpp^;" L"fW# D YI$)TDLK:NL%} ˓uɶr2O^B.#!#_&?%)ɹEqȭ%*wIWnL1Sb)ŔrJ4.卼dy"rPͩԩTu5NE}Cьi^dZ>m5vvA`QXPФpYᥢtB2Ń䔌|XJ *(PT+(G*(Rޣ|^ IX_LeIGtn@K齪DU`L}]j*jjj*ՎØfa`\g|={wq .{>^K^ި~MS_#KcF=M\\s,*ӚUǻg/`m-T\+ZkNNAm@mf: /L :tt< u3՘lf9s@OK/HOCKoHD?N~=AC]py䌜265zolb`ܸIII]SiiU3YVns<üjhj30eBTKozV 0%VV/'NLnى_wY߱Q Ybjܖm[i{Վf`Ю=׾!aCG'GccSΪQΫϹ\|\|tutw=[gL&q']ߝ]H!dyV{>2xzm{_W~___@AРuA7u!N!CNRCcB+B Zw#"͑ 28r}(ܨ&'GM$&z^ž=1b}bމ3u+OP N8?bf/%;ypSz:L-z}ɴOל= S) ){R>"Yլ-l_& g->MsO+M{>/3,˫ ܖ>+2&k8;!1s⟚3sH uݘ; C£NTAGAeYV͟9|9O  e혧7oX,H]б`ᲅ.,Z%K.MXںL{٢e~ HHXtcm+]+Vn^S|ĺ* ?\]kT%寽s]mria607oxqee6Q66[6n^sEFŵJ-Z[VnyrWU6m%>mm#pGSquN΂Ov:/u5wRïFמsۣgM=Z/;uo>}- ;%~翦z@聎r~ i4М,nIj9rխoVմUU;زc  Ox1ēWOM>u:3gN>{\p|bSC~?tRKKwkϤc=/wE\w捩7797ʾv;SWv_~f4G=|#]ӺgOyBbO?4}y/:z_ _ ^FM[Qz_ACGg?%|z:43s/_C,Q MKu $xv8%Dvg"&xr naRTcjg7F$/V o1o E8< 1694 836 P#.iDOT(s^@IDATx |T'{ *kZڷZܩVX{ݭU+EԺRܭuQ[AvdM;s'w$L0o>-3w99Nf9ƕU      M#xj @@@@@@'^      1 x #      D<|>@@@@@@bF]Ӿ}u vF@@@@@8Zjը78x*((hT      pp G<O+b#      -Ю]'XoDя      !'.7xڳg! #     h߾}ONuz@@@@@@ P3"x⥁      @ݻwGu !  EW;վS'&6pMZSRbI)g6&W;pc.٣m{J%L;ujӀrkN̔N=ƪj@-  @BN:숧]v   В tSM󽕺v(_;H-EFiu\C2{ɗ:Ir~V5-ޘ۪W.9Pd>[<3?I79-֝6Vzf@@h!<5gT}2G!oL۶R.ҐL%?L)4r-.W=٪ڷU*/:)]qKuR;r]ΏEA6lS߾vMGgitvX@@hfO7/X+k\w쉝VY bLt 8?mI;fMZ-'gOJ;K;Gs뜓5ScCf~@@Z@ bزkTK_:QZZ+))IJH„<*.ާ܂BU{=OƼ-uUXk>Q$nbڗxgJ>*wW:+9cd+|?4jDӾ_ۧ]^4E~.S!  zT9yw຋xX#oʂkdX mט4o7x#[{*dOLjhHֽN36kkzծ-YaC(fg_kM_ՌSnֿAL`  )бcGg}iΝuve񂷤U϶&,i.ś0*Lmqf}e|ڼw4'J;0;fU7kVNJng>śFDl_i L;76 Sˋ_oV\5~q^?lӍ^WTS{Lƿ  *՚gt H9W8SC/u2Τtu`V=imyV5k$m=:™3m]}2KG}g`_x1}@@dkoܺKDmsa4 J5* $(D{wQOR5vb{3*.2m{_0ހL*8>1Yz5/3m glK~>Kj$@@X ؐg)zũdSF&hUZv, vfNhАAغfRc][9dž,ݮϗ=Efԡ@LHm{G<+ԕA=h& >ENSMk׺8̧n;E\h ~򕨨Nn]w ޒ<ljױs33?snձ]cyZOJ~Z5ߢ)) :L=;֨WdUbv懇;nwt /4_@@h@Νݛ-x*yme2_m;+1"$0$ps(O{swj9zʩ֘yM=v]L(Ng1\pǶLqÄcݦ(mY1i[O}玆LKN5p @|ړ+;LҸ>g{p  - y>xZ3LMs}T7M?[])7xko?=p;[d=tۦ'_ޚ9CSp"o}#u;tIޕ< F^_]t{%9&Q 3;& $z{jWjfstZs+U? Mu@@'6xڱcGjcuT[շIR2s'A]dO JJa{n])ظeV_VK N vLhɃJ˴n>akHW֥~V};( µ1@g۰[<nP#@@Htxjp+l8OZs[:y4< >Co7?]6M&xZ׋TZz_eh%}Ss.:ֳ&"sCx@ǖ[>@Eɦ3n+M>24>}p۩O/ti?nc~~q۔ӡfm߿WߝhL#:6t9,a/ko,#  p t!5)SɃuJhFِ!8I A M=Pb߾@ܚkOqrosJv[`]v}\2:k^Y>=޻&i\R+n +=\y7  ސnjx<+j?Si?VO3(OHS7Mpu3KWjx8Cy=_`f`̷-;aj'_Q)[~sQP'?s~}sr |:gX7u>3<݁{@@h@i NukoRALaRpĽ9+lSUoVS{Eu۽JULW!&ys6xNSz[Ĺ9vY2֯ܡf*CF=,XI_\}̱ͩƸVNAV9N{̂yqyڞQO7}q  @0!/ O#Js1oRi-y|qes˛Lʝ +?QHTaLҴg_!i=ʙT~} P ŏ_stޯG5~){[WU!i=uݛĽ9mrb/ն?|wKsVW^ɶ tMlMrnMck=mosR2l'> gG;ٛ r{qi ldG=2~)~)C@@Blsj'i?)k6uߑs Bnz34y#o"Ϳd~GJgέb_S-мIY&xi=Z1B.I7+/AͭZLֿךiͫ]k,6߻2vi [G{m#nӲyBB~iWm2Mjۛ.9RSsб~_4bc߼L9%uԆ^upz@oE<Oui}Miį^Ҽ_#%y  t)V+xںuk4˔y-ژOvnB۽„& lpނMvmn}'i@FŵNض9H71qfXWUT}|Q-ti}jݺz&stxk LHvucG7}c@@)` Zd|16l߾S7mҖhs>' ~quh-u@k^Þn{KONO6[_s`]XW<;~Y}Ғ6 *Y{^3ϳ[؇ya֗h$Lu H`{y*@KmR{swu?׸ k 9Nk7Ƴ090IhQK )|?2!ӗ&djmS0x*s&M`Uz\IZu?ʼ{H3jF/_5ş7VMj70qkz&; 27~/q LDQg?>ә~(9~o k !  @,O[liR={H p)FB6oQ“O5}J }S@lSG ]W?ρ^XC\ꬄD4 iZȂ=RdUTT:WrC~  6c$'BG<7CNQ`Q3GPf5Ue ͈)Gh]?V TDS>ۙ n ap6K_`GsT<qʍfFF3vPיω)@@(УɆ̭ֈ͛77)5x?B<%iI׬{'-@@80Ltu@4FofOk^Z7Hgi֛W釙}nRh/KS4îYR1'~j/&d?<=oѩOz3\Jhe1Y2ڴ;pwy.埆~+uu֩nROMmߝ8YzxV]uC渙~X)Ӯ5kr.ax'r~_r򏄪uu}NOu0 @@z锋yTq/u |YKgPQݟfjF. !  pD |Z?^ݡKB.ႧLk:gh):^tCgtF<K`+0'zfOٕMWktR;YΆ17}'w@oܥڧB7ό:'h+er'm_)lۿYWTSΖ_x>Xus#68[3f;j:ՄzuS8<@@@& 6 6Vyϝ귷@yrsmvڸH73]NgַMW 77G4 }Zbfg}PͶöqUQQQ᫮̾A3vU֡x3Bu5^i_QG־f  -CO =#8e=Sw)vT]=SM1g'4"79KoxPYN0cFM=R[?nϷCtDשD[A56x9#E35";&7i`hK7^2 }}38˿53aMΙ=3FVڝIsohr{ՙJ<"S9_} a*MנbͪyZ!ζ/^e~x&mZje@@Z@W3Ӱ1z?8KuMnztkЫ w·f%|#; {GUOv1W٫Uo5s3/_O_>7Lgoyz/(s}2 NEAW<\ݞ[3j&S#Oy&ε4_j/\cǾ#lVK3/gvmGaì>OާOh+&>8Kiss`5<@@@&)rz%~zgSViqɆ:yXaӔJ}z|ަܽ_]~CٺM5DZ{.;T߮/ _UFSF]3cWz=m>9rh>4S۞QOkӴj!  ТҥG{e<V>օ/ D 9oyC^0S0T02ʵգm2&xr.T#M펒 _="l)iuo={]\5އBw7餫ݱH'j4sg^v3`RcBfN~ޖ{O019yK@@Mp~=vMO0c~Azs5e6]?zԍ[U;m;^Ӧm:A}3#WJVuSg#-G!μ-~Qj>XM:~BwMho;:k4OEty=2?j.|Ij}!S8f~m N&pNos 3˭{@@h@>}kxڰaCk g K}RS{5$|7t%%ڰ=Wߨ@**Կo+ť$&Xm'*-Ӻ ;%hC7.\+WޠV};(LfTWemmDŽbo߆zd=Ԧ]X  !*P5_ЎrsIJCJqSviܼIRtVި*7ud=LCzcϚeޮ*.9mdTkOn-9XRۋӔT O=2ZZ<T  .зo_ <ٚ+uyUIJ |@ Ovxo&%qo"O{swjg+?r)zJHi1\<**o$%>-&r+y}ۚ/j߹K>;ּyS>_?ՙN'      @LOׯl%U.Cm;p'?rǩ wj lU\hΪgL־.(LjF>a{_(n6fJT`ۑ~LgLX9889#~S罚}u4U      p 9Z#֭[S*3)Wճm+%K72TVʗ_{N5O򛷔)^̜6UW`OU*/Ҧ*}{fج~m=yuIQ[sieooQI.Yb; E@@@@@,4{dRFMs]Fiif8w;3eU\OzIǨc!f7ϣJ:g$u+3xRLHfnU}>+UrUtʾ?Q\[?b󴪺EZS%e) L~gWτu50]}?n)@@@@@@P<]<4~q&|JXJq;sWXMUuꬊAʄNq5["U\Z>SfmuvJlk 즊CUq TMmp>LE+t *KOh)]zF;^|;kb@@@@@h pWkӚ5kZti      @8pӠZի[VKi       @4?S~h      OVj      -FsRkSyh      p@<OD@@@@@Z@믿n      -F`N[jMGb#     a+W'@#@@@@@@Z!CD2 Z     (a~|_r.@@@@@@[ۯ;;GMOOwkx"x     OF@@@@@@(@ڄ     QOߟy.MF@@@@@rB"x `@@@@@@1 ޺KC~>)}iѢEQԪUuz6lҔLVeezվ}{ǫ) !     9G<5wT^^4Ԇ;6HZp~Ac<-[,$xС<0ޒ{A@@@@@Onb\p6lT\\>9ѣպu눇-**g<R)))DXRR/RӧS;ɮ~!      i;sv+((p+R[ rssNag'7Hj';5 T{6T{6dS ,{scH@U      @I7ftl}mc[rg=      pP ,xzW^SɆEmڴ .]oĈ^O^?vwoG%H!5ld/^R"qԩ3/p656xxS歡 +'`#l]6;w: 'Pz@@@@@@Z܇ BίO?dGo V.Jf "okhTn<\Js(9%%XWaaH}{߫Ye@@@@@@Y46QA rrr{ۑD}*#*JO%ԡ̔ynZmKn]=g%o*cLg֭[;!Ըq      p({04wLgTYV_~.r#Oe&f^D{gU rUTh}JIUG(u'xil5a„      'i޴VM:-[ѣGu}EZ֫Ҟ]ZRTU[sČxZЙﰶmjF<FJi[O2:]U28xٻJɕ%JҴ)4xJM'<]p!g@@@@@8&* 9ŨG<=YJHEM]pک_tC*D[~tt`S9S N@ Sͣ.TibV|S>af=g{jwJљ^Ni&'       (p!bzʹG}ڳKG*5B,VThUNmҔ`2Ujoj;q$'V-j[Kf=͔zm;S%ϐjҙjoz@@@@@@YԤfڱcS-ܢK ++ՀiJt'3BRwrձLIJ5NEm;i{מJ*ۧ>xQm &Uk|U$&)ᰡOIuF9fR@@@@@@ \4f,#     O9G<5wc9O~R-?c l(_8'z;'%poEY;E*KHT2Jtj[WU;V(QֵsH~zzOW\qE,       .K⣲rG_%1䤢K/<[i1Ӡk<}CuOJJ 9qBaE^ѹQibLgU%8#znZL>-YT[fVc@@@@@@Y͂Ի`'xӧ|fG}t';w4O(P^\v S-[ͨ'k3U_\y=7tNtNIP\i3ߞ;TiB:Y<@@@@@hS y'xݻ<#<'a#K)ZUUzNmO{WUyZ:TӨ~ijh9S9Sq-9]      @,xㅥ@w} :tൟ#x*v*LPZO)qꟴO Iz.yYnXv:nuH1C4|{n'x:䓝eA@@@@@KK 9ըG<=~AIHE^Sǫ}NvZGyRRBOKKKHt4^ >FeRF *+T|TNr^Yq)!lnI',      p\|jȩGQ.55ՙjoǎN{!'QsڸqFSRRb]09Rai Ȧ %\Req*JIצ^\QxGm$ٯm۶Ȭ>ۭ{@@@@@@Y4kBq͚5 lTQQm۶9ӧO 68۠ʎvJHHplTfWܒh&w9mlsjmÕhe੼PJJWvPkرB     "rQxzb:N֭UVVիW;hT{!CjWa=[|| u2}q-IU/^ }7q+\&N7j(<      yCNO/v4'xZr>ڙ~/,j,hٲeZ{';]n7E.޲*-mEQKT*ʌRqq< >ճ?      p@ ,xfC˗;kjdЛ*Nezz;U{ 3)Ii)N5ntq5ZC@@@@@ mrQxڟSyyOYYYy!gQcNW_9kxjLVk[~3SN詔'ׇ{@@@@@84b<ἂf/TvqNhdGJLLxlϧ-[8t?>>Y&xΝ;mڴqFb(r:*b؈     l|)=䔢t(5BjŠ+'99igΝ;+!!!⑼AQFF 0'x R> x ` @ lݺؽ{;%@j;# U F Я4H~ &vBQON_~eu (:*x''W=S @@ ҰWQQ4l@ jUtD *, Z^i۶m_Wԭ[7zOO^ c'@@ "WQQٷ'@9+@:x[ x"x^}@QTlB *"J~(@F_EF!" <i=35s~ո籹_5}/bҺ2%|*j~oOO p ~S|_sz:zS4Zgڲrs/V".Pt48KXa|jO>5 #>jORѯY|]5f= rsgq`r tvw yPYûlNvׇZ|,Uگ5_]}:רb '+׫ӯ5-~'M\%K5)c^ X:|̞3Iç=Ԏye|Ќ We?.̞.çF"cZFjOZe00aV_a^=^M{/W-^-'x"xjKڄ%PWdE[ty'<ՆMUlMLS^=ս}ϗ#$*5NGklҾrUz/ -ٺV}\s s7jiU}t9]FNx`SX"UƤ`?;[],IVo6m:Rf~8k{`YN{cÍb={ƎЛoG>\Ck.1kîb%%%)-3#2"xj8V=yNXf~G@%kuYYrz_$U=@-`30D% 5~UAa^k{$}G nl>f٧{0W;նbŚ[``{Ms><`𔥻N'Nq6x*\9A sYcg豇oְګ0掛-5y^-)HrQd6[5fmrSUV= )Өݞym Я'}{G\Ԫe[>p;\i1~S|6~=uq_1;?:Żf*s  BN C{ ,OP=\0~ᯅDjܫ_V3|=SWO){4~ O`yoxojS;q 3T5vӛy5O8ͼMl^M<ԋ<<}G<?hs>^ Si0F1 pΕ3)˖?eןє1CۺPS=w'U?xB]XXOMz:w7i?-"عK_T^N.f'\ߗ1;$E)@n?G`5'̈k{Kf;W- _5Tʿ~81Ù(XX317aCV>3]Nߦe 5y.7A7G=>|ZꜪ4ml S>'}u"K3Q­zw51 w+$=*NmחOc҄LLdR7?~jÿTj;\.|N 2ӯy;xzOSݙx?%N3Yk̵.m\}ԹÔj|nF#~cL?SmKf!_"n\+3߃yN]eMu˚h~%f SOOx+D%~]=|z@[_a~R9ѶSl]p绮)bX=Gçe 4q}*ɏfךtˌde.v+-.#mOƨ<%&DŽӝ9Ye;5~lfGnNq=jh kάc~y9~I~Ukz~R]ެUQzhPmԜmZ~dFK1%je)4^xjvKӯ|buPv^If%Ǚ˫kH2sftsh s/\y.<=Pu;9958FӇ35/үԵosT9gD*D.Яb)]8S~`޹~f\}.lltiL:\s]tNl% x"x%fh@7LnBYfɾSڛh漝sy3SԸm[*sAӚo-6&x?vb!6Ea~Y]G|{G;YE>}{9[7y}R{u?2Ԛ |)>{8)3ql;EvF !AULB5D!hԈD1j0/ .,$!!Ā+`AD'#KUWwY bt>bڡ,(JOt<_[x% 3OkwUP;JO 1LG06Pǧ1pLx=-c!Ƌq|LOB2^xt=8*2G↭3` E!Ψ0IÆQnH.bMvOEz99\HS',՟oګ2O%P)W Ufcu]#bQ*3!\c+k-L?莦ڥq6ݷoZճBkrUiI~˰Рm[E~s3'`h)g"@]p1tļA@t x^ԇt״ec05_Noѱ7. ;x8>B]a:}Df}Cj {]amǓ|5{r=f>KPO:wJGn/xIeaьh^=kFEQT{nizqtI' M1)U.vǿv' 䀀28q2'@9"Y+9W Rs]:="@]ef 'n7HJB[怬E0'9kz d篳[}TØ}Ql\&Zl {ΖuY$u$UWɮΕ]& 6cŧ'}^J8ܱSH(/V(&T}=ߡ(9dz7~.ǣEadX!PمWEV}[,(%;.et_Lڴi~ :L͛7;\rq[$@ SaLE~N6"wJ ʼ}Қ a#7cԝF/,=Xp"VUʱ #an1>;QxA*";*33xR&TAqk _B-n-45'祣ZxZ'NX'u#Zkc&/@Hdj*bQx\gM)ÀQcpQ)yUkoSWL=C]$'^vʯ>ᅠN-( EnkSW&4^ޡܳNG0aܝFh uw(zM硡I}U'LD]iÎ=eA{ m? \V /V/ߩ?YFJ@*Ӟ={3ev3<7'MǓ$@BwÏ^V>Y֭c'V(u.\F]L;/u: ƾaV,vW|Ch ճ:3Fkyɛ#:vlGQmVBRټAJ/O%L89/-am r<}2B;׿ur)t2 ݩ]_k%w=25Cr.e7bx7>4Iwؿ$b#. OqHxJIHHevh' F.W FG W9"m؁.Ч>ݵAǀjzoOeZr`Le`*&rrԕ8=2v <ƒ7kކ1.:ڎ8{==z4az]v8)JKǓwIHH QOm VFd PWM,x PW9Ȣ>|t*8%9w=N,+Fަܳ?uS=SW&m9V.`.:PZq+7(20qجBV#4Z5=:d   $0J'Bx~l1nG(kSkL]ԕ,$@$@$$a3W,F>+7(uEA]yԕW,F>+7(uEA]ẏDǓ3B$@$@MF1s<\-ZMnoOxjaﮏp^dt::m$ 8w/>o9$ԕh.\(* |J]勴9o{U-ZWW'4Gi]𥴓Nn=ej7RtvS]ATۏN]ΒYX<lk3:xm c$@$@$@`u)"ٷc}KLוu?NY~'O֩ @]pXz6H?uK#@]ɝ+w8z?*6IiQWO]v!ӢrKp<9Kg;1V XPd֒{GDHG?1w#Ot- mWèN~b=h0ʓCz57hs`m K=ұH 7π:IhivWPWK;4u6*?\H]ɊU@{#*OvPWC6M4r,:OˏOϤ6̩b.Qlk1:xW]c:$@Y۽ו9-g7eHOx᝱0MX.c΀>c,Fn 4lJ!:i2]u%kcxY>>,Z8SK|RZ( p o6O$;J]2iLUR<~;I]ɢU@{#*OvPWCZ't@F<-.k[jCvgS F { 11^7x2ZlkDǓ}Q   `(O Ɉ<#m҉^vFX36^ؚLibFPCV֋x^4A/~ )-?E}n>:sK]劬%^߻UK] 7d*O u[;x b=aCH \7bK\GOMǶVn*v:xJUGxHHHc0ʗAı4PK,Ӱ{MSX3^N B󂷲c; "ң PPlh[kLCԧ!_T6r$\O ٘^lkYo':Wۘ +0rcI嘺DqIw-µ1XxM0g`.ֿS3 RNTŬ*8SLiG۠Nħ + fu F{-ugQWy^䨫rslhNĊ 7藰# :xkcb$@$@$@`({Nb8U-[.=#`4k39yJvtvw+,=PB5!ykmu:ٖ#俶cLԦ'=RlPWsrή1 c%0*u/t+3QWˢGPBeBn&5#V;mst': T, Sl9%p-V|1~yV½CiyiFi>84Ҡ`9|%F(/uUp=YD]e K+X)5. c㉎ /'  (46 mo؁.@xR Z c@{ËM7o㧘Ϙ0GU1+Ϙ"PW3t)%^ 4=l5=Ĺ'@]1Shzgs83f MulOt<徖1   W a*NFF:'@]ϔ1u:@gI':   FF Ff0fQFFjdcvQldFf0fQ㉎FQQI   F n[+H20PW n[+H20PW n[xɭxHHHD 9Jђ 0r'c#uz@gIb  PW3e$銎'9^3XC@ZOe/!5&A$@$@`({++@2DOgHJ@Os<:uds@`z~<3J  pF3e$@]+2FXH}ԕL# hc޽{53@UUJ++   a=C@VԕI {U  X PWV"' PW3d $`%銎':IԤ’ /a 3#@]y ̎/PW0# 1ԕ u 3#銎': dOʞ!c +MWtOM,, F0# 1ԕ u 3#@]y ̎/PWsm@IDAT0# 1xc2qT}څF2ŧpA(RqGuiI8m[>) pW} ~]-ZE-pکQҭ+.@{PԟF;I#uq9m[]-б]40|>vĺ{Q%F+@lFg[f(zxvxE$@0g#$-*[ Ox&ŶqY,v?ntMܿ t8ngyi â'fH9͖4q鏍CI'|݁4gX7k.Iv綿?6 ,w. ;9S>+D8;v(Ns?v~l|p^1gݼ4^$u10r~ků'.ti_9=[Û?жUKt zdxulG߯_Q]Rk2c[)Wr1pԥs\_sT{}<#?C6B~/A\|&!\fH 4]KSSQ%xN\"y=Dd:#xpw ƽqQG" TjNæ7a/EO@FMg m:i8ra=I. T72UǐscC =(h 1j^4|iy}K̹^s$oҕXz`|*vBF^^yaA߿mXWPNuU}5<?kzKG/5&q^yI QDx8UT]LyY-&I>N#y4;Ҟ{L#;%a'H8U:9wmVmcbp/s om3,'G Ƣw kvA1OkӾ]şhL(4οq`I xf|txjѫe%VO\\yWp588i^U y%*\OwNl~ٯ"׿+b˰8/^boeDHcel4<5 8{Ǔ%1qLLe"| [m5ECe6^PP:Iّ|TF4r#dCNZ/΍ҋS.*ևGs<ک'^FKoK3IQOiLarfp^dfs6<,Sφpw0_^jdT@SCsg`z#l#q#u0r\ߒBɢ97OJ >WSt0>y%տz,{q:~Ny߿՜pܡaʏ}wWA]g؎u)3 SCdGS"<?{XztƧE3F`Kh*gJ]57a*0B߲L)SЮx86kȨ'dԤ|.(& Mۗs_>S2^XWizYKڴ@u~lL-ZAd#Qly*wQ[ێZjp,BUEڟV`t@gL@Os<`Θ;Fʥc}p9lv}yotxR#*cl+ 8XfLC1^C]iɑLOQɋYb4)Jb77vT2ny߿yo HWP$dV :BgGLv1m:ޥv.]#ӆ,N6~X*vߑ=p#ͻU2CCigCRy}MS;C$jܲ{R"ߗ֙p@dl~ΩSa+ʨRG { $ .Z fQEM1uJf>$cx <+ytbT)y\x\t3]vu{`h;$#~Ί+_ɫc\4Bzܶt:z@zv풞]v޴VF| "#X)p%aH\ G Y[kqx/uR֩*0c9ulfNvkwOBc{S3690~xRkOKc?q-Njm[8#ȵ[7=Ua2ͣƾa举Y{ :A:ggbDqm㢗[u&1?n< ZTx>w4]3SbNkͼ01h~۽s7~nތى %>h<KNޡq]䦇h0˕F;'mG֒ڤguʍ2+6d>{܌_~2((Rmj.2s^w"󷗎K#t,/$ܪ3HOUan UgE؎os/R=c9gϨVFNm;YsObr;ά܄Ua bщk'v@E.6hlGƿa伾uI̩]'.I[޿:k㘦XǏ[@aiɹhgX8UʲeQ_sSd L#6YܘKq񙧢O1"ZAlز6zaK":``^3ю79Օjpɂenh^OYUO/kkO37Wv?]T}cb[޳4&3C9>!> 䅀+:|xڱt2.@*Hl<q~ L0K䊉Rټ#ǓFFtc_0׿=q^VqG\PZe 䎊㾣hnt|"#rq< gxa\J/k3}_Y+p\4N ],nsT_eMG+S=?Q`3 biG,WTkp.z$׍Cŋsa## S[fQY~SẈ^,vKdWeqg?: *ƖdկL{QFn@ xGW J^,Ӟ>Da>3So˜Gȕiim"DW)8EKí=旓0iw=&e"|uniJG܂Uv5uutׯ9&k^7Qݷ_Abc$Bӏ62ݮg;#]QU=E݇?'͟L+[MS"4lJ*|?= Jk؈&V"w+ƀVI}Rʩ:Y[U?G]W#~Ngכ̀oSF8Η5ý&]wfn%$tEǓO &`dͭUO鎧s7b~9^#Z't Xe#Y! IR&bВnaJJ[ hw_W,dM y'BCaQ~ uO1CAT~ Lד5B¥qR8Ǔgz$=bڦ5\j}HV׹x*0m2E|emqk"=[fMy_9E⼼500Lg/YWn%$/ E;'oIugfGo#? @.nstq 0xʛIt0U4o o ݠxb)2+mΘ_SNé5o#h2My_1k_ w`zI1ab>ؿ$Gdߔy_' #DN0I7rŇ*AӸϩ:c9 [|X؆9 bvH#Mkn3[#ྯ8ѕxrY3tn~p^OUFS=Ʈ cϮ2w\  _O}X*kX?`Qܬ{wJlzL8ҕLxL.ӃOW_09wSLC;Cūsf]z ^Lů/zbɌ(".-W% ( wMA8f|&yxyϑ@K95GPY *Ma0gdOd>uX}z̛/ hg +¦T;XxMo_Vt )>#x-!shk ZsDuBj:E%Kldq'EPou j5{!iPTmD5]#? @&ۚ}xHXzt;]mU:GK⠀\l߮O3I<:[ݵw[fZEK&ÎT{>g$ibCL>'Oȟi+}SX7u[{$EX)h*Qq0rzoI^#e)X@M>pbyn\*tjǢHiimok'28q+֭ĵkp#2=Q&c*Cdz߻g-eDuÔwQޛwnm}1^lxwc1RbiL;Lͤ%;i4-Qo3.dI{$G2[dLJ\9woCVD/_6d4FfA e/=nc#A&GjL"k^; OC|&&ÎteT.b|:n_<ӻn&ut:_6˻ed(gB4:D{@4=Edӭ2isV^Ҩ؈꫐ {=NOl5j949W/uԥɸJduIRm7NtU/Q}Z۫E6(je2oK%?"k$;azoQqRm$ԗÇ]9](zVSQxFa.tEǓOh؁.pxX'w8M薰z4;"p^PsrfSc##O 8/9/!2pϢBHNV_q>u8dC&_KMW͏(LLgW˽ Wt29)Hbu,/˦Qgq᤟p 2[N-X{qpwܲh}x)vS_nM]S٨>qGozюeM# yYȹ>rd 䟀te[ts2*gQ;Ш{ԏmq馓%PoKx3]IuH'TnĬh6[0wPweZؕF~O&snntjRBF{L?3xJ6^~ÝzRv 8Uʌ;fǓ{ŶM3D;{P5&|6M9'i2I@?gb"2;TWu<ێ{|&X]tVx^EbL4]7Ǔ/7=3 Χ:f\>5ܻq܍X:B4ŝO'^WDQyͽ e~qcuf\2Ff>!!?m3~{"ÇAE t(&m8fGuiϑ-5qe;q's M=k:V-,ewpґ$g=ֿ߸cD#yPlzm>EFv!&zXtkQ4u^`\bLsOttZj?kށ"mH;^$a'i7ͬӹXi(pbni/h9Κ^ԛC.7^6s?ŲOztR0uث:hG?>?-I8wLkwT#IeZakzrNøj]*{GWb9?ǝ~gF!п# pa̱'4svY1\uR{6SsNՇpEA˾{)/oW)yro'"Q7̣1o;m9o9ycGҕ tsH/Z-y! G?sDɢ ~H\8ҕ!OO}=6n%c<#M>m6eHF^)N-:a+G2Rcv'=EW~BftM#HWiy}ͮ޽|nknY;{t2툌пd4~[kW cYTAKjU;qvXʗlja#'q6~lNL|^X;}I񩋒1;dLGv R3ю79ՕZ96m$3) ͤAuZ$Q(tx G@O>up^$|Fʬ ҆f߾p9eb^};%v*Q{ nHg֔Ǡ-eNt<.Wh7RAZ'se ]|Mh%\߸o6m[2vD̺DY[|&ZxxשvnY&k;kxf.YWyPnA̹آ*Yul4uIy̚h׎'Y ):bߧ5CSOŹEqzdžCG$jV8x+= PT$ig:W{U>"۾]$TNF貨ox,;j$o^֕s'OpHG3t%[pC@R*i N:~y 1P86y;{dV9 VW˓z'kl?k [P_SQrVgǒBG@m?qYi.qgtm<\(yʇ |~mвet9뜅![@6~Y$tEO͎昩 4uj5u, PW/KWUa3Um_0pg&銎':]SxHHs0I!|`Ds+ϙFd΋@Ǔsv I$@$Pl;S7e C* woԕuUL4]DǓkyO xF3 3ԕ"xu90C> @],PW3 3xUyxrΎ!IH C pg&@]۾,]aPWTM}Y Ý+:xw-OQ::Ri  `s&a|@Y0"9ԕL u##@]y$̐h㉎'TeE9;$ ( 6 Ýuot!@];S7e C* woxߵ @],PW3 3ԕ"xu90C> 銎':|P'HH00* woԕuUL+ۗ+ 0ܙ h㉎') & 60"9ԕL +:} EhΌ8QP-ӈ;pz۶h}R>>ZZSۣ[W\9ڡu!?!g%vF[sR+ڶ, T )Zc6haH}oډuKV^=.+Q8pH aτGH [U PWLx%@]eKI uτGH [x)zԨgxj8Z_~ *!5}m!N{g=󤑴٨j}L.7rDSw /0{jziʻ[^{6T!I2$r:e}~=Pd1*%Y^QkH5k۪Nnfa'}~{xrm.sQop^ghV+:}7 /h9L-Vs[DDynu2@JA/׻wwDGm`ҥ}mK^`ē2cGiO573$J n(yX|*f$exu4ysxj8} 7aե=LWgGuʝ7ߏ[ פ T-t5aq9b-M8k2љ?[z$k 1d bì kr<ÊWu)ѿ 1_?Dz}m5 EOo3 ,tlu'jeU%^^,MQ%ʨwO4.I8SJ!,̎zaţ(ߠ|֒Gi;s^fs3`T6+""~!P) `CqIQNIxq^$TuD`UTMLrNu12ShfwsއY*fc[OQD )4NbEN#բ Sqd":BPGШ@ LT,K*`S &hs=wߛܓ{^{{Z/@:IH:'ᡪ^E{$ G Na&F杆ɂܢ۰ڜFBcQcD,4wL#к:[GCgӘz B 7 znuS&)_zʤ r}ߟw#MB@Z %yFIQ܆geCi%Jh'|;#NvRZRys| m>zw3.Xkϴȼ3v3ۅ~SCiޣҾ(C2Ga+kkUۃK6+Wk>J2Y$cZ"L3# 4bS;͂\^6ybom2H{qⵑRJhaiTWsxk̷Aj:-ՖMqKUd>5E5Us#rK+mFl@K!~X"\7WۏJWF&r\˺0$Q?[4W9OXL,>(.5m-[Ry\r ׯH}z+cZUOgHq\S VE.w'dw{7[s>OO,_u]]5dՑZ{=JMIT#Ws RV.F՚yNv]M0#0Xq1!pw~my=r,!tڛY1KNO#:6Xk`(U\-ĞiѮ;Ԏ(|Op_bܙ[㫖gW,x $>{Y?'ء밋O$e*'QTWdJ}aJ->WC,'rtsS<4|[[!v/s37E='MųrJ^$̝w7鐼L<$RuA>iei WР͘ZANK"N.( Bl ҝj:';ši,c9uO꩗H$`M _tA@C&qoϧōӅg"?ѳ&:<:<Fھ}%wKipQGcXb=3n)8?G#VU)PLʖ1*_ťx8Nسi\5^K!R2cN[fC5#o]~rmA@7h=SfDiӀd0mw$3#؟,I,*Hv ,\*w63h/ fbY^1fR:j|AwIt ,b.3>Ɂ4n0߂!|V_R㎝MShFuQTT ";wA}a@x 5x(>k4"nv9dy]p5pJuxU$yF2{E,h ,ޮOa<5I/u,mrd̸\Y,2DXI>7rnD<4]Yb8YLDkVcΌEXeC.n$t.셠p@2t^uF[K5TYfhYn쭎\͏ӵ{\&K<Ӓ-ы`BPIdۿWPRm!C@|`*fr- YkJy!'u ^LuN% HT+@.wsi7;tE]]b7$B>v=8Q,H<,bL<Y{~Fq=ǮMJ|֠9}R:5X ݄X`xVJ_مa<n9G@*בL!SV+\3ҙW 9Y.cV m}4bSOԗN64~O!q~Xd3e]gqВ"HflQ&.)s76 \Ԑ4K$]YB ZMUfmρJ$ϫΉOw 5TqH>̉.FFZYy'&QUu.]iwn,\q:S~$hmQ_^_ _{н=9kosoU6FcK=dEa@PEP+"Z6;.=k^Z5Pfdxw?nfX8Kw܍*Q\fEtFs$ "~`eWಳNG"^sE:j?͎+ST*nBsAX!)5[iVUP+} b5^wz+9@b,7dɻz;U/<ȞLFE7'"{G<[Ho:O!<jߪI?5{4LUʑ1O^ޅCLy!E@ތw_.ҰFnb3I# f66{NO"OSwVه G|݆h qz{wOqL7 %- `QM;Ȃ3JrGIbɺNs~dNv>g3 RYi 1Tr̆$T\F. js lӭ3RdcS&2I ÏHulq e;ȹ#КU|ZGR1bv*Cd;}/ pR,e G{9Fnc=J+WqrnÎks aaܒ7+`#O1S'C ly߽΀<ߺ ~h-T*>m~S4>zXp=+*ӪFc 6x?17V"mKbH;j 艏?nlkFḅHB٨ų }_b/>NUb^W)Jqg8ECa,PwSMȐ4r8櫸 R }cpLk cf WY䛮Zjb*Z1,j*‡UǮ(V.HnX)ta/푥Hk&dkV"Hih%4b ZdOv[>kف FIڛgM*ܤI(BEv¦UM<DWr3nF9FLtRh lSC_w,xrX<%$ICn#MqZ'/"e-'x7AN-(wl~"qIV<#}8wLç㠐v뱟M/=aë '*˲b€Ŵͥ1r^L~38/~ihyTJj9]kᐺqԱ?0e}CX#bB6ײN%-CSx㮵e|+1UOm(Kg=j UtUDjNNٹ⚢/!a_iÂL&f ~_ue;?y[37lL-<墐xnZ-*|5~'<OUv3B(|)|m_jz54/*@(oU(t.bj<Ú|Z[kQySG#bZ < LJ@@+<}vZFyC} nYUPLjW'!:$ g=cQP: ۩BAqy;u/~i'OM-w{i;4VhxErmz@"mm쭩:b9ĀG[Gi0nk/IOð *l)RjwO']wޏK$D\.) :>mPyx1"xj[XUQ1\lmh{P9nȸgXB;ڂg  ֝xMm"e\@UIr|[[ڋpa14f`=lT/*sOᚴQTJSđp;9 8&raIL/'|ZoX^}hCaR&~3?zwm %k/jxkY<5g)L}$šFE\2SؓYwPp6nz ?-{<%/*;=2o0XwKZ%"2s'T/UZ Tݫ(CxZ:#nڥ}_ \9#*4b R՟d> Qg6ұhp!QviYD!yH;Hq#x'#Į#U`g_5Y"ĉSPS xhl#%,'_\iR9uۄp.iLj%u73ml2|? ~hAU_x~ȮpԷ5?Ev[?x9)vhdVs#QC&c ֪)`xK?%#M@5;x^ ׵+W7fRs@58NZѠC =;!Qjrl.skܡnW2j2fk.2=H߶_YUMt~{Y"o42:Ck?! fL"O n.9sŹG{Ƚq涯p7K 4U? )g _?2Ŗ3k_ŝr#Ow~; \O΄) S b+8sVF@FUǮj& [4VKzBڟ5W7bd \4b C[O y+肧~_V;yXP߲2ШATpl>mVXtT݌ sEёFlX@~?Z)Kq(ͽGlT }pȈ^xy20WIlNnBfl4̝0'k7$8JIDB^Q7*$QO~Il>u x=SعGtx1VGE_{q#4I8}P-\*#ձeBK{kFuP b="3e w>ukskS<ŋi\ eHVZdktrw^OT Hmu'% p~FJ~fgf#C2tbؘȇ=k}ofg=[MD D"8Ac8άj: -,.=܋|g#.XgH PW5< 걵>51ʙ?>eRq7dt e\ijQƻ[: Ӛ.nF6"<9,_5HvpXhc۽cqQ<[9!z1RXЩX;FPt7 D,PKC;EqCN&:9JMhWf.𣶍*_{g_sye@&*!iކϯω^R@jO7Rȕ ӕ gh0+G6nh4,hsCyxH\ +Uv }} {iLa fTGiq\J)C`b$]I!̲ɇb"=JS`ݓƹ) -FocxYX4('#LnR/~/^]+fbBuɽ&:t@Ny:$|U @ .qbUB'GϾSuA:\Bڹx ǎ;PES*6kTm !,BOuc4Fyr*_ŭIs{1 ɇY2ĴdJ95\+{MH2z$6Pߙ!߇%JbV7qnMwckLtwf8p׼ 8-~+H S yĊŧ3⛢xT1cCgK.kBҾ6=fLRr7_(z_x+W>Pw Y|w(n8,3T>[^p$ _UR<}YB:3ݓiSTͣ7i,/5Zaf_]nu3Eu} (ccBQ1cո~98tru?(p6l@+R͍W٘;R}3}O4(@7(?_~%~/^7E qR#>,x+0Oͣ`,XHQox׵^}ѻsG~e-V4Dc\G4`x[8s^+?j+Q{R5a*X~ n`O'<$HX6hSOI'gK:=iD'b9nrGMî+I؋g'?3wUHX 3֕D 77Bqtzg術BёEȢXϿ&d . qͽP)Gy 9|9 ]8¥,F埏sHϪ?o>1XeJBo;fF}}1:E(X 8J|&4%Z6ysFWTeKWqjwGxKK0:?q&9:n.XiOb̑Gv""@Ϗ2N%ϱqK_Jg..-N3ds΂{Z<[1tс⁍&o | bdW#RJR|h@2|+9 ykm|L,-7{?.4tQ'VF;qTfJv?U=5Nk^+Ҫ(N_9NZɧNovVr<! 剃1!W+~g.JhNcX g mKs(q'ʮ |TJ^1PV!r4"I HZy(œ<@@+<SktZIɺ*<9̖\9 #0A鲮OER| b;#JڏeWʖ?gd)T<w " ҰDm=)iYctItF<'#9{#XªÊ;Fc  9awhXdyFA[;;TӡJ75fE:jp>gāzj|(ibW\Q`Z2ţjOcd;Z5.D˻ $n.[Bq+Ic04EVucd5aȫ_$ï]d*.*ss)88{}gd!g[aJNq;2IӗqS}7Exb _b=V?J7%%_-pq}P,C`dÚI|11/i3~b!ѺJ{#y&Z-_zu6 _ůb]B{IsScKe-_MƤjX(/ӄ*_L<9{:Ƌ! Y~baP:rtx-QhۢC3\bXzxiy&vW,xbSVGh4uDO>ԫ`â'h5>k=!3#Uȑly1gE7jǧ_wD}ۧKoplǁUSFnL5$QMG94>б Dn/ߩ櫚@C%nd4Sܑi- ]t3ϢXR|,ҫn|)Ǎrv÷@k#AtxDN'BcW舳{g 5&?? =W@Z'8Yn}N%cT!Zw9=XPDmT[= ~'ϱVFyRN6gU<ǂxA ]|UGS>9.]}1@\[Skwr2{tKUxZkT4b ?[TZP.`F0Hƈ3Wan][`J\n` sr҅UrÌW,xbSxܺ).D#0@Q WhFD` X09@*ȕWk&'h|ł'<3VOq:F`t!t!0.-]0_ y.70_unB*]saF@+<)}n,x 0#0xcarBU(+0 LN(` E3r%U _O̪`*r`F ](]saF*̭uKWB 3Wan][`J\nO,x s[7<Ņ?`F1 X09@*ȕWk&'0_C*` XĂPtfJI9N0#.xc.0#|庥 t!0.-]0_ y.7h|ł'<ǭ B0# AP |fJ 櫀5 Bь\!|arBW,xbS(:j%XcFH1J\n` sr҅UrÌU[.҅<f4b ֍Oq!F`o LN(` E3r%U WhFD` X09@@+<)Y,xRE1#  y.70_unB*]saF*̭uKWB 3_OaqƂ#0C7Fk&'0_C*` P4#W"`0_AP  XάZ <"F`҅o҅<fܺ\t!|.0#|庥 t!X0矯}nGi?&&`Fob5pT@,b5pT@,b54bSOyyyzkfdd*Jj36nJ(0#  y.70_unB*]saF*̭uKWB 3_)`u1}/0uuZW>F`F 1c`RÍS1`c`RÍS1`cO<]~jɩBǓJωF`B7F4Ӟ`jOum-Z i.=!|՞ZZ0_\N{B@+<=yϺ~0#0xcarBU(+0 LN(` E3r%U _)`K/4U^hk$3#0#0#0#0#0@`SO \'i{C5#0#0#0#0#0@" :mpw ~0#0#0#0#0#0`SOjwMOJm~#0#Bm0_ʷW-,gۮ`jϕo!ZXζ]# &xuLW{<]Es#0@B(!cFW``J./#F !?f|! X䫳#<~#-&A*8m%$80_- |X0SAAC:vbJkv+q-j#0aB7FajMKP` JK0aB*Lu WAi #L0_5.AA@+<L𔗗PQ]]B]O#0@xQxڒkLIx` O[rMUpڂ) WiKIpO<z۷opzJ)9z^\kUcF1 Skr]UPZWajMKP` JK0aB*Lu _)`;OA'J_-'W`F <(20;8 ܄z4;ս;NhᪿbLJN;Gq={\/=G uijTF*㻨t]>~Ќu)CIS>x U_ݴ=2_{yeo?OG;bF EcOd`JAND#| ?aE*Y9=#U4&HXĂdQNJ,{a, <,u=0D5#;qC*8 Sw?V|(=)3 rkf-2 î' 7K+[[A$ME.x Q0\=(T}^xz>L |'`O5H8WޟGGYS NEVoŹ4L|j~G"_vg UڋGt1.^f;PmAc}@G_}bhhJ#PQ5`M_ *`ϧ_..η W%~kUU8e 2fE>D7`aH_ŭOJgbdQ )3~-@kUPι2E@+<)=(S<<cN4eW N&hAuܛw ӲlӁW1|ָ"d¾^ҾTS&܊w\U Xib$jc\ύ ^G+:;[E~w(j:lTяlΕYEm_|*_I/SJ0eH6n˛f\K-ԔSO2mM PQlQӰ,e}/UB>.qX#xڡ#IrDZ߿O`0``Lgjܛ~tS|g Yٌu;+<<5>^_$ka8x EPZ{i}o1z5n,_NFwFuQ$ __unw=*#;I mIѣ8$UnUhijJp!@O<ϬǪ5†uns|;A XM-9jzYLS@F8TC}x/ &eJd\';WDSr07LloKRjjMd'\|5W۬߆oIDijWaYYd>WI q|\n !G θA /oYi5-x7+ \%(}b^O)~bU_ZD0xCΒ="Iz SEyy}ҳ$[PN5_t@QGUIj1ڸ] [S+G/ħl(,2{Yǒ&MCJOJ\Κ|壢<;oqT'YS7H)LPnvz;w{D7Ri[_K!$()—l( Ś2IgO&wX!FFy$\mZoILfc 3w}= |%NZ&:_ůX]7{TO#;_|(񙣰t{ݖ=cAReR@KUW,x +F L[3vS/UkO& [ihk| Lܤ"7ԛYZFn ooMs#?&[w7cZcm H^KkV #!MfON# ς\(jKj/kWK.v&øJ)aދ3RRLF KCXc9n/ߩ]m4cYS]YS57c&%mɪ͝UW5Fo舠F&~^Z7 uYzjOLk:Fk$&LD@<ܯUjb0<;>ơBuUZ*2ƗLp'Tiݖ=jcAre:@UׅKdW,x i߯'n0Ydl\y'V߯_.yx v"x5>C7 oMFn7܍Up+MGp=?7Oj0jK}= 8X< +[ORNYHb"#,)RLo&!ÅZZ dWp͘ZANK"N.(RQw@@5g4:dK$Zl'jc~σ`iiNF=YSVē@"_FWH[oc>Qeܦ1 ^&cdED,T0\^5(n nvb_^fSq=G|CRm#ՍQ_=1|!X{eT{B[֮6؏dUsqKG\r-6evon0x`4 mbӂ8sͼhuGC$\G|^J}|Yu|Nˊ&l\??0 8#{d}4M>"OR?>3ҏe7^(W^K6 ArR2$e'q#ɵW=yPuP;T "jժ?#j_VOJ6Wӣ8$Uf/Ze"hTcgdtJ6(*?וu:wA}a@X~ 5x(>k4"v9dy]p5pJuxUZgd /W̳GK`X:S_<ky p \$%ӗbР}IGK]*,\>߼ KObL#<\KD$YG3nCkd}@ڀ96xȇF##᱈3˽Y'hj̙+<2p#{FwAgTH g,[5q⋪\0WƱ6ˬ-ǟ]0 ߍՑc6ґdɖgZQ8()]pZ  ת~JOMGkGbM ʚi#2'U[v_G1:v߲Rվ}`b+n"!ԷnH@s"@X$%6xb9\A}xz ƨ/[t/|v)JDwt7<磖6ܝt%2[e<[I)!\J#ǐHCXۑ{[D@T~ҁ+RH^8=/Jw$T"Ť3I1IdtHJgK!7V(h}3U;a<:xsA־PO\ TUP5R>'RC)2,VwZ VSqY7FuE-QzTǂdʌ5?I|egcG̩M9v@|bZ钧du_8wCmB",?|W>F@+H>.,+d4Q;FS"rh3/O+0ǰS~Ql+ ({ mSx^18of[s#i ɍ`bԓt@ƿbvߒsw['JMA4_+f.ƄMK]I3ћb9XoƱ;L,WzN((2w~S88`g9J8i|=ޒs kA.pOnJ|<GG i wN%hJ& iDZhjnF]ѳ{WeX-# Ʌx|E~R!RUZC`-F.(UbTVhs3Ak6Gu,HLGu6MUE9Yn2h`+\ZWIg~^)d]̟uT" &xKKw cDzu$xzO:Oel⟷e:QbX G ?틬=KEnKPrqqr91sb HxqQUu.]i ?hT)&BItbw3'(7EyH7dŐfmdkO@ 6AxqՕPec"GW@kK'^0\>Sm0{NSuKQe\&l,z54hAzؠy{*kU'':qVΝ*_)|ưqXZqh,gެXwr{eaQ>>P"y%kA=췰1rO5O # !Wq8Vg,n`9)djnn׼ң:$Sf-*_k oPE~y;Dq<GV>y0֤Z FMC]ujC_yÛĉu~<2;1>S[0ȖR%ZC!N ,^1R\Mqk9ht@>ȟ>-W>9V

    Sq$xq.'>LwO8F$t#E,N<%Ԧm/|5 7Y1Q| Om; p FZӵoz ݴ?V>v}{rf?|=u=Nec]~g@oKinĨ6.t)O*|}/g2g(9f2fH|#.l2c_5`œC2J1v6`b;j 艏?nlkkBKCtIa#0{y8xX!lJ o<)F>vY'M(ʼn Z pUH5_ŭZܱZ?\P41溾?7Zs cy"׺M32So,P-3 ~"(xr_+/|x_uobc@!4CH{oh䲇C)m Ǚ2h|ł gWo;~ԴTJFdB5h:^4S'tU0N$=oR&MB)tUYM'6ܦU)#B*"nMHn~`*eͽg偵<8Jt3ngg5.1]i"9hP@."+3P &i'\OGcj5ޤN]K!ZI:Z~[ö*2o 'hbeCmNn8bJ},ѧulF3w VXmf=ħZec:LnGҝгx1yU\I,Zj+dq\oS"3ҌIi!`bs琿kB?=ϵFo8n\P*v1ka"Wq܈S2cuC]R[zrBE9kopw&O͛{) 'cAe3qR D?"HM E4_vƓfRlTǖ) ;S$4tgFƅP+<{|S(.f/" e3,tQWUdcZj/Zuv5Ge|0Ӻ-bC鬡-Rd۠#i{$ۤܟ ý4k$[uOOˍ5X8~,Iŧ'ꋔϴ\iٱ̨: WbmAv?rDktMt^wq^gU:0")-} W,x !GAOlyQWBe‚' ,jf4afP߲xJ>-# ,1,-A:5'}Q>fgf#CCPr {@}ggoٯ=[M?REqq(#YYnTQWýG{6u:~櫚NnP[0izj} Q"}!5N\z>w6S)܎†QIx`Ȫخu{xa+qH6FcD,[7\˽5P(@;&O]O%nޤ9%O~j*>$<❷89,דtƭԡ T2ngE22iD^2x+m廤y^x>>'qI1BR,=W|jAOER—A@@u{&9sδ+ɲo\gݖ=cAV nC*)q? 12i?|ΩbH<( _)dCϠw^ѿ\nzM=-TM\Ԍ$m '0k#çWu[CXBf ynH>鐟Њ'k*ȊvɈa㱯8ܲo:u/=;LiR"&U0"]Hy (h1% û']sE#&l[lǎhS#PN1F1ܴ^5 XA(D :r]rD/k᪣La[HW15;Npӂ"~1 'khy()=I*ңg_ 4OVbzM|NuG"ᔡ L>Y.%CN'. F+YCiY!FޞnۯMz H⻱|j=Nzh&U|:aWqHbД[d|d<25jq9\ywrnîk]|*_ f| Pehez(7Kɝמŕdi"Gu,HLtL|%mZ<~YB:ЫKeNS ~~3:jEx_.}" B&x]_=:g֏6%.x"$PvƸqsp$ GN 8{>:1N*sIȧ(s]ofiTw/VsbeDR䟁/I׈S`ihg/5ɂ'^k" \6I{/1Zjݹ#N O+DyVFG4`x[8s^+?j+"J5@^/-mZ-\7'Gj4AQ L=E(>ةeY櫚NwUHX 3֕8?YȿQ Z{w@fc玚]Wdd9NS{S\&bFqՌgYC6(ڵtdat'/䋦&d . q ږT7F"|ޟ$ jȑ#n}2*ϛb vDSmG1|=9>'5盕]-$dyFQoY)3P>6_9. uqɽP(l"5bUfZe'ᑵ i8 Ek/o#GVO>m䢜t78ل=.Y]S>W7RPAB@|T@}`51Rݕj×Gj̷tP^y%5޳kݖ=jcAreԔ2|"xAp KqmXt`qo񌽕 >1+i !v?8FXUJ7. '*˲J!b]n5ȍ7J7mo# gq$z=EӴ/T7FV=O-DT;$ t~o6Jo)9}λT3o3$Wq+2dq9Vu><Ŵ(A!tZMd轞3mJ_{?~j1nKJt[z[/Y2 M&+8|k:v$Wn2nP>)[ Ҏ _'::f.^]Hg$nS(Oa,ɕ)ՔoZU:{#)hsF&=Őz,q}C}ߠEA`(+p+yٶh*.PE ֻNo4&f 1ZX.]# XԮ \Qw|FQGt?t A˚kV"o*pd|[{Ƨ_}ѹc7dfdP}z 9 JSǨ~Ȫ~zEn/)ñVoN2PGQ5%94>б Dnv/wkZ|ձPU:wgQ OBIcPIi,jpX57vqs}31#)OݿT~:._#7%!7!G %|#e8Ny=1HW +wXi-xN_C7pN%cT!Zw9D?+3~\oWi{v?djipq˯ ^Cp`w/{xTGm,HLvjMWut>磟ҥZkkQCNsyAëAP.0jS\ER>jՋxEK֖ZiZ E( "$) 1k93s33gŏyw5^{ݪzuFcVi&MĆlF0<4@.<`L @S)Fi/('H*9!r3UM#4rUT544b2/)6'Y,WZ.X Olxj&9I)ss`X1w +j `w +j `w Mab0)XcL 0&`L 0&`L 0&2ԳgOnM@>hT9I&`L 0&`L 0&`" 3wrX`L X1J'mN+W\JMs9I*9\!r+5L'tr&WlxbSwrdL 0 "QUg%2X"S\ "rAY T%$\ePepV"C@+6U,WQY.WS`jJvT \Ef\MI)sQ%\lx bL 0 "QUg%2X"S\ "rAY T%$\ePepV"C@+6<)2 Z lxRa`L) bԔ9`jrUS紣J*5jJ,WMIӎ*MĆ@bS L`L dV228+!rd Jd\E* D**\ Oi*aÓ 5`MIiGUTkՔX>U,WQY.WS`jJvT hrņ'6HS>!phZhZ'w!UTVtm׎8_7܊ _K1&d< A*  \< A*  \< A@+6<)qbxjZ'&M;CT۷3lKę7^;r.xkv܎/՗3ncc?q$J#/Oo| ڍ{z[Iy.ndzOmFy-JA!n`%y:G9CrTCf=[@ǂ< />ym{ꗔLHlpyi/9N0xo'u@C0)}n2 Ao}f3JIDAT[GLK)rT{bF{&Z41 C sV}= yQ ޯR}BuYvl?GhiBP (yݽRm;{>C۠W{cn8sfXJ>{b?ԑ6Eyhyka{G9r[C{~n]j~= knV|ĖwbVVT]Uw훟-b?5myv%9;vE曧@r8cfz2 >"}s0萬IJ]B :)q8•=4=ȁ"#4~e_& Olxiy8x`84V?X]C^uYMz:8 5R O&̸W On|-hr}/ƃ3zl",0q3nɑgue0cFM:\yUkp=1SekkswL ?r7f'7`W 1ѧٳi+<׏jD5sj8<4V1֓ (QWϽkP4:ony^grxzv+ rJe+k%Tx8yyDk>ޅZ~ucpYDrJ^3,5(&R05˼,&Wry.^;]R{~ -w3?|kJtqL&}vw@ĭFbHyoE@JrU[]5e0 &B[# G#OЩ?ӗc#Xjр;-JɍCXxX3Np)L"\(Z{cױSf| ȅw)Bk7LaUzSi(}(kX4bSD O_K]m1k܄\5$6Xdizbyqfh?#zLˬh=--D;Qml7\U/soڨ}`trH(j,?DZrR g;1'|d{yYUE W*e=:;;3Yܫ@NSc: OaG+צr?hw3PL%RB~$Dn~7Ϳj\'J@] //gk3DoA<  [/ _}x;M,=߷'ߏ]{#q9iF '".W_௳`Qބ|Kg%3%y(+l&gWJ{U$gxR vEB]7}";ja̿u}PxBǂYGYB@]PVDjEߴ;O@!U(Y\uy 5?l9D72\i;vG??9wm`]L6V]?Xcex rs y"PKkXo UѾi V7:y):%EruETO{{ ėѾF=\=LP&lAٸj]YϼUUqie12xD,V"WyWI1R}6y-:8W*qs>q-(GrV6~hTt=Mm g1.EǼFPqڿ5s@ߟAqԹɲf|iDhu~&͉M# 48ǥXHƌcd<,vʢdꓦG+J=Յ6~6>~Yqe5J\+1\(ŻM uZNO_ \){zA$wPK?_}^ LA@+6ʕasL!_}C. OAr;r i 9^Shެڶob,ګ|TZXmV_WX<ӵ- B(< sv"h,uC|zKt<c/rjJ}x\OpSk` bB{Ǡ*8&+;KL{ [yl&$Wf{QWo[6Þj{ 7[r;(Wg)\&L*75Q?i YO@Ij ν/\I2&Nrvf<y=S+hӶ5j  +6S[碾kuFt&p?ЌYM>.Ea`dzh.Lv f@;Vߌ$$WJ^5Ó<]L^Q&Y^Q>ȃ\``|=s]& b'$*z?-zBtwF~e#&O@+6:i$$1ҷ|3T*aޔګ-{QBחʋ}xY@I~ 3w;I_-pKL LJTG/#W4促$s&fxr4/kbǟ㻰7S0WsGoWJ'UK+nfZ{,^ P40y;arPQSV-ZĻ[|v%ݍ9c΄ˣx3hxLzs[pY74kvpl?~ Lw;>c KasY2&˛~FL?AM=6 'bnԟw{)ʵJL&~")Lvp_^|% og3$ϼӲVkxd wϓ;=,=bsxw3/vDwّIȳLE1 Ő9Wӌ[%2 Oɶ<|% ȕWyo@W<$oxx&,g|fZE<{|V~J?x>1FRxXYL@ Lx5džbOԻj;舛gbhaԒ$tMh K>vXe[u߭Wa >qsC?}`ͯ_ɋfO+}&X S P8jO}/k_-U]g[\1*ڧE#?^/,ʆ1ڨ;6Cf=BKy>dpzy24)lSc I`"gx"+ӖaU: O{i#{^xF1[<]7" L5<{-haoBT %r=;2sw"$嶗F(FAQt{)W"$SR,ݖ|{ _qP+Rߛ&0<)}]JRW'^:?u_7́q-07~.HS4 U{bސ r+ٶuĸɃܶ8{/Y,bּF=a}s>cS4'̸W\X):H ުdXyh25`;-ð Q1e|/i?)bZM>o5yC$\%{?o_'<ՈN)D`shR._h@ήvvZ)W=9)})+3ee4bS OV=fF-`mm Pĵ0:Ld:Qq{wm;13T{گ nv0(8($]>|6WDDRvqQXL:At%W:0!ٻ'1kAD{)1he+k"]Bơo!MD~j1c/ Kض-&}Z0V `s͹dBw3sz8JL?T OjM̃r.J J Tn9:UtXaXmӟV-Y/.~ N ,ĺgr0mW~x0]w~heP5'Y^U'#nk/[X=j|ڿAM{ɘ.J߲[Bnӿ#zcC*Y/jOM*zVO_}Q1nm.\ ?cG}ػu7{J$:T.ikT ro!NDqKL@I6iHT弮Z.?Q,xDҭh;~f0L28jT+%>W|o7P߬'f`zc_pekٷ/Yؤ'SGu7R+2r3Y.>_)g{}O`HW YX7x:/aba`I6aƛ7i'_Ε v2͹QV~ʴMFg曆\Iz#}QonR0uK*|Yݷ՟.9Jj/b`,D@ISݽ8vll8'J<JK;X_ !Mv)fŏ ۛDx6VcѴW!v_TW<iicݘΤ+{=A"bv~e*y۾?zڸ7`loF9J}-_9Ƭ27;ItI[r+"ҵ E m nLl|ʽm"'׆;h݅ ;Y@e5x"g1S?2<äꬃyA&)ZyF[:=6{8۴B8kR\XHgr+@^SQT->t>((Fɴ ix2jL/x{+Ʉ_h+Db lOcJ;ج$}崿h֥ݒI(3;-9.[j_HIhcȩx!:CSO7=)~ O WUH!tӳ}+JuHcG '(d3ڢsnٛo4w2gc1\ _P+c=ZI+~Oɶ׺};H1R|٫W.lHYHYFi8P(ɕGʉ]i^WƏeP֯ 7 4bS ONQ^{7Mrn=k8D<V~w:M$!FA:L{ЬQ1$SavtDYC{qC덫%0<[6 ƸiPSDLWȏ|gNe|%Ѵ?/ơǬGѹ!g yis¶VYpq2c^hrp# #alx'Up|Wc)'΍){C $siGߤ=iWԸzjHշTv:8u,mz\k {gt4TVg8wAy)Qxwcvx2Nyxَs~p5.yAE1R}U&Tp%*.՞զUs^TȕW 8x湇_w{ݰ`a5.{=80+Ң(3'C~{ar%Ml'Ɉ2gY}nv9]_{ _Am<Ҡl|9]QMndM&%x|wϕu2\%.J{'by쐞&zIg]_ ]j=ASEPaQ L^SO^FP%vQQY2D jR hrņ&^f4+>]ryv靱זnh%mxV\q~s6O'ގ'Q~A3d|B;W3ؒYbnqjVQqѾƗ4gѹhشZ槨+[›<\I6^l躚br{3ʮ*_5NrDɞ&jr垙D\M.>%hmƣݩj%(<׏"HP2ar%N<38/dz0=LئnaIV1e[՟&a?Kj܏cZPc3r+؉&ˑ2yFq9cR* |!U‚(WQ[Q&cZTj}=$yTHl/I qZ^)WePU)U$M1SͶm[4 k=Kzh'⻥߆fA3L-'_SIƉyq鿼}<^5{vc\ӗ1)@?4dH=U8%(3|]cj0H4 mߍɴϋ@̀q,vVNT@ψ z-0m8;6;w[xZsؽj7VvJHt7{p 'GNvLy`2Ft.D\meUedBZ{A{{'xUd G:; msG9ƩK6qvelmҙ;:sF:K)^t63of,. A+6 1Z}ZXG^䲪b|L ioʹO.VP+}-oX؟dp0is)꟬\S09e[yZNP+if@<%Fo'E7܍m0D{5V(~Il|X~5Q[n<-@{2\j5\jM˙IDfqX{/c2nc(U⩶W# |$W#^ H_2xe(?vYe],gt.ղߥ U^xBtS{*zԾ ӯg  O3;hΟZw-g="+G/֯h5ڵjUa1{F+=PMV,?Xmοbn'nY\n+qEjje+k:_&Mir,F 4KZ_ ՑO^sUdJ&N;y2>NQ:!$>ٸ O.K=Txwšי^7֙~4^KBxٰݕg{i(og{&}NO͖,h+J+=ǟ^몊r{m3j@|{ W\|%Vm|ՋkFG}UŖUb;f?A3\!OwxUP_R=K1;RJr0"tI'yh.Gs+ǜ}h |7zW`5>M"K4LG[Ǎet;:oa˯cu۱G$aѵ9pyQG~CvaSalZ*@Tkf&/,Ѯ)kyZy<]E7>3Kuïw)B;`pWL L RA^2C-A+߰|3 hrņ6}>z'V1J'"Ug~8wch&4_qFq tK>=p{?mɼKK >'j39*ߊȅm1^1]KA [FیLpڲ=AP9xU H3VfVJ[t:̈́v˺0-I@΄8b|>sϛ_ꨚ{.uTkYAާ4(܌97ٲǐWRQ[")ȫR~j)A{ S@*r,{>\9#Mygx:6߿E栴KxRU%w}`oMl""Wuv`s5t``u4!$cN1/xf*g!D·vG[E=Ze(.Ëk)T*qSidI{wiQ.1p+q[@vaUzWՃeFǦAH ʷr&WlxIo'֝|meҥ'T OZcGkbzI6nC>A{HOxԔū?ӴRơhm͢Ӡv/ R ce&{6~zȚ_̏t;x5>)\&y:G;-[1g\z1vdl!#pަf+E~ ڜ߃``WW ʴ|P6y3Il[O/uS:bQl0 'aٝHǡ\gvjO4ǬLJ!MԙJbXHoq?}-$=Ta0Rvw5 6o`$(9;KU1J u)?)k1!^ 4XeeTJ,qŻh?1}[c%. zʕi߶H+|,h+'N.|i9Lc^g}5|={ ȕyВ;C`SY| 4]]oVZ zo EQCT_K& W&Ḷqۯn:.#"W j{jaXc'7]>d}֛xגr]K+X~;Q# 6 0*zWуeF`/|+|9 hr놧/z@IDAT |UřP@@R*&VP#ł+ViSKݚ.jMU[5SbS8g眙rsorON~w9<}$V'=%Zh>7J,ȓO>9(g~}`wI;z'<B#AG=x l @Nvu{0~ġӑGnGiБc.k]Խk;7Ƥ* ]C0uCiiGͺ;pu&$Be9 ~4:@Ieސ{hPwzs7q~hN=&MU\f3J-=Ӻ-dLr"w9%i]<4wXh0 [~wRWXLON`?uw|@]Aڷh ,'~GkGiȑy^9U#9xFJ r:k 9Ǝu|=OJK9"uS9iô ' 곟GЇh_O0u|~cgun .GƍE!( 3[@@ r:0@<@<dHr!@x+(x\eA+(ɣj GP< FNA@ *00JI"a" Si -Q!JI"a" Si -Q!JI"a" '(T'=-P< :rD  ! 2 Ar@ C ;x\y@#Ȑ*C\AœG>x>e@T``D>Dr@ZBrD>Dr@ZBrD>D@OPAʏ >!WP*UTK%U.#\Ed\\>*!WP*UTK%U.#rOQ߁S Lp  "0\E(\0\E(\0rO:IFJrՒErIrK; WQ-Y+ WJ@OP!4a:vG5ni=DƎ#ǎL3xoO_/?o9q;cg~IycF^atQtaI/aߨc7(8g' V^~3h+pO@ SL ? @L2%ʔ O@ SBx)z4gx߳zb5ݺ1 _gMT::O&8o|>n⦷-cҋѷi^+qDyDlZbO|TR2n]NgxDƦҿ=6I]ysh 肼1WAdQMzzS+c7pGљ'L3&'U#Vmh3vgh>t)'IcRtN7\ :'&A;pO9N,{z:יﰗ}Tt8%lބ̤x~G18Z^| m[i>ZŊ-eƶ7o^{D{R/3yʉ3>NxMw i\r*p tHO4i׷dt4 .dVr%f~>+ Uo1Q`o'Ըo=citi3hgr%.c22}]ŸZoQ'W1#G9I?{l/ڗV՟B/wF鸼h'OFM$!\Ψ~}KK+mrqa:}F>HyWİ %ӶPNX1C`Ȯ\Qվ~:STr:U򶷇^6Eѻ5Gҙ3OϞ4&%X+ot롾Ӏ'Û+(xRx߳.~Z^khm u&a5J)o*=vxzctϥg96~U};)r/Iޞta_ӓQįPz"\Ȫn}&]wڷGW>qBTNۗLB3oWn dS|Xxa2`g ty02ef|A]WӍ;rpQ<n:e{.;rO}T]M* ޫ/~dMy7ne)W[AO*o`s{F˟' ^a{wh۷P:(?ZMP {ѸU\'KG΀u[6z.+|2^/ɮ,'Qr줊ߥUOuwӢF7,g*2={wgiR^WRIKQd ]tQZͣ?ݱtO8JVTyG؂j۸Qyr Wguܧ*ZG‚\ h~RwȊGO2&9޻.a"G[ҩxw~nv降5^)5~nx2~~Gy7,tmy =-7r ޅC8s`כ Jgluo7Ow@ W 2HJox[XIKM1[ }<ƓϞK?YU5r!^K]"u0qC\Ϋv}=E+o{(DQr_~6tHɥe&n[LM:+@>?CbCGseG,D)q;63Je.McnJj˿+wxqBxMgS:߅wǖZ'eҍoep0`'csuw:K)9q7s>Y)gJku']0 sS>S|ElV"-v^eSh2Z|_(py͂EԾ,ov2WY&rxm2"xљF;ߤo+i;2AWuQ"PC ^hKl2/soW7\]"f6;byp'h+\ljoI4-ve)c1.sN꽗oh[A{uR][RbWf^z}ھ~fQV2&e䝋_KE#dG|N~8|w;[/׷d5hշy2zx+-nI]Г?g\xeiPUx*qS+W}^IEVsҎwsyG߯}i﮿>O'T ޯOX}ޙ1`I)W'Uz~(ЕyӯoVK$Ƌl[HTݕ8;EsL( iߟeM I~Cm[߯UG| ifhNtK͹_r5~ҭ <A@O@}תP╀4]+憫+hՉަ,6x/_k^MrޥrI]*{xgigGՌHkᱞwx{:l;$ͥ?u1wN3鉛V1#QY.ۼ)EFbEgf$8%Q&ҶKt3n;\73?)teٕOf_Y^!5˽S+mqno%jlq\[+ǐy;0Үod GJΓû\1oxitqּixza?0Mۃ2~:ېГ+m?\7B+ +esx'[@Eє#'Yl+ljpR"9oFWo4nYX幇v=Fl-ckq%t3/?{cF1MV"j~w}{vl4PQ"ԷZ#܁&ѕs~Jʥѫoٖs>ȕ2oq|\!N;(/k|k /oyO;-a,륝1ۿQWw?P^Xt t>O]t!#i~LBҕcf/fzR$- 7zHu{)=mYEG,v޽(̤ߦ;&4~+7KcƉHMؘ?ۘ~jͯx]?U_k  iUdWŎ|B( ._P_])Š?=pׅһ?7c*-[xd@Ԟ,3sO9i>qq|MwiazP<)ʯ?9*uvO|u#ϒۈ^.nt.={R|ӆ:ޝ'(Rw{h]V9-kq:%\<~ur(@1[Db%S^N_??h윎SJyXiQ/.%|(ؤ "[Ցp˶>uX{~GtF39lҤf)gIӷ[Wff+lg,f..M`>ճ>)if9bvDw.[t=twW bp+_ [2rH%ң[߲+!&H^#W> -kX]=}|^A4vUy28" ȕO1Б+Nop>k;;M>DuZVy,w)}t({f%>k?mhXV^u (M?ֺiՀTg3=p 5j_)[Ә4$qTA#]پd- gz\adS칀7X~ά+C=9x V TL A@rSO{6ҿOFwT`m[bcep6TʓmZ:#1M|ٱ;rE2SivϙsOAoJĪ @x'A5v3;IsR)5rI\%oٶɣ.9MOoxJ7w3ʩL?|Zut,'~ݻ ^ 'ҽJ*W9HOȶ\Lj>Hx j Fx| (Ϲϲh?VVqcٔ+œUI9K@OS<'}.-~@cA%(R.zͷ腿4:"h52tMF`Nۛ b~iN_Y O@fxoEMɭ2a4+>/fYy%]:4Xӣثy vmP<O{E!3iM2`C۳ۖo]Ւ#VDg];5kw\ΊU]|QT\zzş"y7Dg}= 4v]jiM.53uܵyhUY| wCߡUN{zOn/@J>ҳwE  ILA+%io@2QKwo&ZT+*@+yU7Ъ|ϱ_EVwqḰNWC\nLw)=5H}ṬUsN#蕿L76v%m!7@3Jf hɕOuvl|m5{(c[4& ):^N(#V|i-?hڿCw1S{s=swO>;'7=};Wm9Ir^; #@ȌJ6{gCI@?u>Oܥت?yQ$YfI~Ҳ+\0fWNNc/z}g_(MȻOQaqJztNg]-P6mj/?uZ}'{'\ w1ej/3LX5; 1/>H&Dyskөʆ^}>҆D'Pb!'Ό8o>(z~ֻ)2I|^8Dg@h˪匕Su OYvbg/.:N"Z7i~(ٴ$lGOg`m xJO^g)fgi1*#W>s鶃r]N7!%򋴢8}n?qY:rHo\pT2ݏGK8vqɩJ=e:Å􅕜PprxP"%WJuCrXJhtv;~ƚ 2W%ТHZ+B\䂀\$Tw.J77e?d憫ysٽV=LK=H\A1ӟ9[TM^J/BnW< F.̡В3OLΏW.C2#nHNXO^l`'=aL< x,0{7WSf[ *y;=Jk)S-RBpjYywt>cSnCΎ[&ocPOh>He0"+1[ VS4"zIp/ף>k[*ܯ*wePpmxZx:O*3zw'c> -劽A7ܡ7^Y*9n\`o`g3~;LjG:#oK]cr~)Mg}:X*س;'"mœMy)Wޚ` "3{ٴOgD+xō)VP{:_)$B]V<P>p 4_-mG*q9bJuV-pnxWJ!?JޑwkGV{=~[%nYD:%C :#W>--^;Գc/*1~SY^m?}Ǒ"#Wq}kGREg{:^lkbј$rKc%\f|VNO /W3S_~gGwǏS)[KfmR7+>< lʕ 79S" >ZQ3 롎Q)b{5rzh=o~55Tw?txs]z\x5ᳩy:kRyxuw[ y;I,.$QOA9Ǝ'IfR~ǖVcVlfL|uBxz| [wlsI+Kw%"cQvq]m34 }yZ-VN]h%snv}/n9-"מcXkۻoq! _y2]Tug9)}[VOXy@"8beMKu$TQ'lrxz# v!{7e6<z$WL|鷃x(uAK;LwS!W 0=<O%LUM m rlY[Jr1=-;NjD;q O ]JU*+lkMOE^jۗޓv`5&/@K|ij'MJx۸dZYAuV&PSq\A1œG9'/L_a+| =0z ʉLͦn"۵臦-c}wаINٗEshW=T#1#O n,RYU;jqYz~$~zi/袧eviˑhË.2Ϸ"7#Ev""l&t L$NݱA'Mi:jŻnKyby+clb,ً\A  $f$DvgSЕ2"wEU.Zzh죚GI}D"eGL]nmlfo!+u;0Үo!_/=Y[r/+N {ӎR8n:>V}A[)@+i<#&MǦVB[faC]rB|C+(V<.s==U[(+8!O%'|k{iݦu\?ËINU!>n\g7] a>->,x/ō?9.)4Գoȯ{e}\VqO)=buxz,}~?qQ\0&^ꆫi~vT|DOܴ<';xE|mO7mWϦٓF芵NΦNZELN]:w U678ahM<#ժ*dqy44O'g/Uе3"}/tFm@\ַl:Hr+ dfs;Ӄ"7|h p}7GhВL>4, ϠYG][;HSPYy#ҳwEV_+e' vS~C_xZ +Q6IH]3 \Z1V,*--M8 ˇj 6b.v}}ݽ1>_x-//`鷅3gx8)W\iAXgwo/GZQ֚{6挟ĀiC}bBdC+(W5D˪H\v\-'k:ᇵi$W1ʫF^Z> ~nS@l>7/>N_Xknp>yZsZqUO>w]#&Q<9wxGrrz89/԰ =k|[4Jz FO'>1bb'F%yr6OrO\>G6醫op;w=mJ~7^vdY1tfL ZsU'Uǡ+35y Ig C'(fl'Ix@61 9˥Anfүn^N2g4t_f20Ҫo%\Zq/K eߟJ$j\9Ä{v=i}oS r\e7x{TS{1VjwdTVU^z-=~+;e(J"*7] 'ץ; qs^}k }+nwPx2)WȦ\S|NXMttv#v&^$~|*C2^=̂IH@IDAT a-(Lp{&wޥ>؛x4ƌK'LL3&Iq(sK60ӓﮞ.ڲw a#FQޤIqJLlz:9ݴF>z:q O7kbFHSHZ$]ڮ*:0pȤ<ʟd+ #LT7\];ikgO6GgPMi;l]"fߵ?Jy#NoOBXfZ9m}T:-2x( nz[<<)0˕.vP7Fȕ.ˊ\i9ϽCȔ(u;<zݧaKOvs%[Jvj7&A'MLළ'5a++ovѾj0i#Br5\JL !WP Uz#<}+}v ~ W~d @'\s' '(kP|BDV@@ 00x#{9! vDq0`G'x#{9! )9眜TO>=           1(Bx6mZƅ'z@@@@@@@@@@@@!SOsR@-- DO!S<^nmܸq@zq啋k\\鳃O## Or>Aʏ >!WPя9HX00PFr5,+ .@ k WYC@"PF@O!S<|ɱZA# 3fw7   "0\E(\0\E(\0rO:IFJrՒErIrK; WQ-Y+ WJ@OP*UTK%U.#\Ed\\>*!WP*UTK%U.#rOQ߁S Lp  "0\E(\0\E(\0rO:IFJrՒErIrK; WQ-Y+ WJ@OP*c*3g~KSl?naM:<&|y^ |ѣn'J|>=Xy9qG@,Y(pY#Jȕ 5D@ `\Y(pY# '(Vb@(2ݶtc]PUl,OenC7d'}~[K7~yuSnEt {8 #"w~PKc)4W{67:ebj-}#Ϳ/dH]T-tܹsy9G56_6o:tq'=/TO(֭%~31>?ti) DMz?ۧE4VԉѓO]H\xM#wH6l}f??}b~݈"2g,>_j=M6,v]j2vq^#:}?/Ph@SwL|)3.ؓI%4cS_KW/}ɬ~2!W)Ad #)ÖzNv|.9N:jOKiLvoщ̥.%JBD sC_~Ez.4|=<)t]v$;ҳx=S]Ar,n?m}Yzb3h,M>)B&K]yVr] d.Ws3Ӌ^.}Gcy,/K+;Ak^ߘѼsqT?E)luJAFg)4ʜέ//]C+="_bZ[K.`@@X 'NJ'_WW!}re|>P>!mݺ5o <_{QLdpW\!0zW/}MJzZ* hW-*WM%%Fs1jˋtz1*bzN7;(;d.5;]uϦj0I.j2b4SF0Y} [>A"0r5H@4 *BxÇسgw?Yߐ۱c!zETm<{Jcoʕmb{znJ -FgO|6hm1J %0[*ZWcTV;MQUUctꃶrGШm4ں{>hi3ʕ<@YοgWDu)mF>̞Lu[׼ NU^ijQF4 OwQR [{eeSTRRzVRN7\][2G(,bGFMUQ'bC0p 6:(Ѯ )ͨP&xĞ#izezbfCW`mIA,{K%5)嵽A]|QSk} C[F8 gٓ^SjMe'ٍ\JD(&w޽(^3tƒbUYRpIQ^最99wߒՕ7]S/< /}JJ1*vi OSq_aў:.2U<5v boOE3'- ŠHL "!WPe^'呙W5ɫޤpzI藩w${XQIRۖV֨,sqi(N3>~s['KW7zZ]PQ w5bUIB@Wx/#V]OOgdVU%H?iom2}557tY)Pf$[5׸ 3]jFU[nV{LҮ.)6~Cc%i]Шn';W%U-ʛDt4X* t*uՅ,?[z Zu:6-1[1XgK⯴6 ":F4%is&u]VǣӐcruʷh:NVꒁ'0pr5iG VBx{qabbe\lڍPwˊ9%-W4:bPb4m*\u͍F]mQ- aHę0dX| FSK[J q B@OT }g50-OB}dy3*}ƒU{|׬- L[Fm9 W/?d VғE?j&'Wxl!%EeF[mr5^E~u6ױqa~wsѬ,-Tnjaʨi0}).\7핮xBN]YÃГ J%_i΋>y%!C.(ُ+(6/U< \7"N^h+Xv,4+.v0s9-;\\喀\X - + B6Jx>՜U.K %FMcRc=JJKSNKcn!#aG@OTjKI'kMٲkf^4FRrS B6 *J0ؗvV9*1zEzѨ[9ZrX22lA:qn) v&,P#;mi [̜7JY$@=^eW?:\%5 o\Y 'q+o$dYNpq Zr 򢝊FQ)xf(sAMp?$ˬjNP5.+_˪w+YZn0 UK9v>y{1W()TK(d!WPNsW77jjW[ݞLQil JJ+X)>$ZaxE4B~qjɞ 63Ggm/9-|_kԅte޶ ;r@g"/reE]{yL A6 WY1c?uPg <t磘XNZ:cK{n4TثY;sbAEi7WJnx=Ln9eǖ'oV^e5N3#CWg`40)WM"f!<ڦ-F7O6:~߳UNAoБԔe$uq~`Ǒ0ռNj":yHG*pVrf?y)g9-ni;NKbgJVyj  ^N|TQWiELV[T@ʛ?oZɩwxCr2|k\ֶHc\A7%4/UcۭfwSҹZ-;r\嘀\u*^ -1{mMF6iX?A [\A9SqυjzeƢ2j^'V*w5eR'K.VJ*<(C (^>oK|jc_:Ô'MV;7+p-& sUW$&*q*(h= .N]o]gQjaѶ/& "R}TwБuOPwoɏuii*1V -WmM5TVV)fШ೜*p{Gl^nO rmCPkY4&tMןW}іS,-W)3fT!eyrZ`q(ɐy.3+.E?FWF} \c E69g4'ӱ'V<%nyrC@W﫷EI'-碉> US]Ë<%ZsDCI!<_ů 0H\AAӝ3(Jq 'fcW>ex唷¶FeyQCB +m/l*פq[}_dN'M2Q<ŷ˦Cyul͖>ΐd/jnC-Y.;G1XhbcS#t(zA!5W"ǁl3iȕ1m@r&qCS~c) @ HDyhhɕGڛ%ߪWrW}q*H) $-Kp{:e{O*~+o\ ?ЕS8*e{啩R;%J*-|eFYSaC_KK)YT2le\:Kʌ29#K,ݲE hZzkqEƻ۶M <6VW Q,G*~eGA )vn3njtv):ciWmu3IQ#mO4'+4J#wil4 hKyXϝثIQ$TldYOן?D`ClVg浝P\ W,mG7jE.c.҄ie<Ɯ~c-藩] ^WR!ΪOmuAWY^p3ԧ&Ia>RӨ嵸w0aY8K<4[IӁJ@K8r?Ȼ^%{Jdn¯)Y psc̔q|>T-G@OT<%Uub,J4sO[X]X]WjnxC0XJx#aj"𠿿U2o$7rw-q~n|x(OKIe3jmdT6=( lpT1xĄdwcew͙ɿL%&x0UXXǻL#)5Ө f>~mR|@*}z7i ʤL=C7"\og2*Ny}ש I{b2&x7T ȕ4Pm@2ѷ&ˏ׬F"٦&Kx'kP\"@>TOMe"Ԩm6O4&~o+˽ϙar{hYYS^g[ZPc<{̡tMןz#j -J^9lqdxuF{a黯ŨJbWR(2ڌZܙB6s34/e/vi\>AR_m-)壸XRZfT5m- 4񞢨N5儀\qJk:' ݆seD+("x޺ި1jj_03\Je5DY;7ɴϚLɁXC0"'CZ-A#T l( cKhJ%[!+ [ݜḍr{Ӯt#MW#L9: JueiuKOtkKmeCEOmv"C7\]< >;L0T܉@,7DEj5 lz!JLꗩ`ϋgs._T+T U]:Olۆwm깤p2Nk<t*ufe$Yغn^ Qm-IC~t eb7FMQ'/W.UMkITse7jOkY[]^e.w}:ό+̳ r:ʎZ/݅-NI˖zK:u"N{}TҸo, @?W9&+W5IQZ곭1(mYxqA)b͵X=.=t޻UV:xr^HW&ONq40. (@nR)x&+/ RdϚK$eGdt6[ miXx=Ļ/3%T[ICB6GS2^;]g@Vѓ;>EԔZߖ t'+!R[Ќuz$<]*]J͐5\^<.=nm0H(dJa-NI_)n2JLw70+[^q̒6t j>ףދgUҤ WòP8L9E6!G&=2}ˈ~G?ֳ]E.ĈeS>q ʕpOXh O࿀L^VXoxԱK/\7h^TW^|}Զ U[jbqCr%_*c%/eD+("x,5w8'\"\,+M ˉ(xAT!"D!T(?%h ]&ArYY`Vewoϵvv.=[ުMY'h\*μ10fǍJnh*k>T2\0)uy22uLFW_B&}xn%5J5/W 3 =У3M5~I sAYͻY028K zEÓ O5k&u n}kv 0^~v<20zL^fk1GkLwZ|uF"Ұ{p/!'# էկf矛|}@׬Y2Sy1mI5 #LQFԌA5|[Eb wîԕER!U? <Z#k5hexQWvm% 0=/#kV4+un<ɱprQwKdˑҒ\vCg_l]= wQw`=o%ycƳGv(}kV =סԽ9)pɢz;=qY ` %ΘN>N+[\yb4~r?jT͂~ ,sksjwPM}a) y +!Q%H]}ӍħRt!W.7-ɓNtrQc)TnҚJg+g>U)@&ʭ,J}žkMãIu5kJ5ׄ1&Zm[j |ukx24bA̽ sGj6fiBWPUhxJr+av0'sԼ$ɘ16fW#\b79sT[DFn\@)5񵕈2Ј5k^ *S0pL#v`ɺ[$è#kd7~eDg8g8s1|ZiLЍ'Y.,y _H!VMW316lW5] W: v`҄Y.089]Wx0jʹndpbяVucGB \Մji1~v=e=R[<&25G֯QA5/6GU{L7>pbQTn۷o, QՋ/殻K}M6aϡ qnrɥ|QwL"p Ǿۃvg|6lK{h*tRq -c8|!.ɥqm1r+9m'tmbUn<7;u0:#96<r%ꦫϯW۱dC=$4ĥW@iNzyVtzg$4lP7DN%h8-jQh嫝DLEPsW{\_=%}7;6a@۶8?vgzE@B'mqvA?3cx$Ke:+_uz_>r n޺GNI_X }UѲV]}Ӎ_=E z ݖشs?8.2JX;<a ٿL4M#UzU&Sw|JҠi t2\LfJB-1-b^t 7hi0~g#<$|?3g~~mD]f?j޼/ E:CI)CNIH2@Fb++W"@ ++W"@ +^OW՚eHH80nݲd#@J{]+-K:ԫԱg%@nݲd#` O4<A4^ݳ<Ťl$@$@$"Q@P @*_H aW CɄH DzB/$0^fnݺ*u h&YXxHH҈FiT3WJ$PҨ2(gP*WkJ%U*3o^yfYT^>*ChxɫUhxrHH҈FiT3WJ$PҨ2(gP*WkJ%U*3o0'ھ]+bl_,۷AǞ?Em$qQUrTH;2ȏдU34yFɮy1|w8ǐvN]rxke8UQ-?$6G;o-2 ŮF2oDrWQ݁/Ǥx I@ spIθrvH"p`HWFH * /@ Pj @U^ Z0'jݐ29wOO;艻~?^cܝ+2v7yߵ); ? y@;(V{mx$cg"'F`$]ا\4Ckqq@ 5ZݭYQ.Ԟ9ذv}{ g:+.aO78oua<-96jֹpY߮hҵ)\nP6m8(~&\W^6m0ݲ G~lRU1.nȿ3p6/u ^aF;Xj<*—E۱s78)WQ^4ik̩BrfD ۛ$m}t7uϼZsxcΒ4[O'LCq+aLWxa.nyӛx=11\4W/]}s:` ~]@6w1p> /Mk1_sKW7Ln߽%ҁUa,dN)+g$LSgjl{vY_cxxxGK(2+޶\V~  ;_3@t8q|?䍼>to}wcE.)?t\Xxyb6Jc3@EQu۫Q&^4ݍ/Rgy9iZVfDiD+_2K#‰=Ŵ5hrzDX#WG~'e~d%iXgOq_ \8JЩSh>߲/V+BYQ㛰3׸߾?jMQ3͉j?*L~uܫ_g gN^H;t,x^}Hw^bxm2< S~>6FC/xEXM7Ë:oX5ve^3r,>tYhP.&K i4ȿX5@v@/fs-F1MMUUݫO{ ;Otudt᫘:9 NSuw=(ua$U6P*qt~V 8K:BJ|w 'dGWE+ġYǔF"^4\N,k6> 6MϤ7;XX'*{}7'B-^w]9X81,M| yO/k|N^H/~bcw@-ʽ =q89ڞVDc;TjÈX}t3F߸$Fԇ1dHgIo0`waO(a~Hzu5x7xza^q_<(? IN•+7;gh{?Tc_.ik21N Ysp򱸺ek}X5*)=ԍVGi:$Iusg|[VâPz]7 k^B'Zs}evKX?|3sr|$+_L1 jox vҹobK21:1mQlCdTeջ53]);;*;;ܮfL,Gn١N*iS%g[ ]6=̻ΐZvYް`zS ~7XHW7n08_9 roܕ7E@ .bPooP-Au]gu&ROTu|7QʷJW>t0|+؉%u9G*Oo7wH$2`;0noNUe2X7Z,KysB]o[:G%DS'$J"NWϣ:kVH Xd>S}C >9'?3^4 ,~ o7Z^ ->cXuSaY-d=޸)ү~PCu+}OJc'#e=]NZ1X4j߳v|Cv}&W"禽dun' |٥[uXgFy+7rZCX¯T ;n< "UJÃFpU2Y!iD;'J ߵFhoNLtA-Q'=y ӧmP]&:Ὄ%Nze@}Ceq^]զ+xd(ά^vIW{jV/@NUFJ+F@KF39զUV.WBӢ뙶Ave7cöS|씖I;9Wr+S >N`ӟ7ÈYϷv2M}?t*y"%L$WK^;5,dҌk|,ZpoS7OSp5qhoxR'2o{AvG^i)&C]ߴFͺ\E(QΕ =45_{c4Yqjn6i [uWgV ]=Wjq4ݯ$;:cUEuZ&RerVoungR430ooVY*]= (e#.d'=0F\zIs9<8_8n7oAWQzsh]wsZ |-{Y(,;H\]~\gNx9Tn +#vK%)DLSCg91 Lhމ):E<\5N k6@WCΓz ?w,z' ƃxYk \{M;(; zN&2gX%TPa% U2kWcI*^8͸}o#WjPg-~ikY'ap"/sč=H3`O=dhm 9&v}-_2wgD;rzhU'IzM?/̾ }u!'Vf2`ŷ r˄qx9Sq!SPDs4@/Tg_*ȘTqr0M ^#F(nYuYyԒ5~+; 'n8F%_|]eaO:[H9F wx|Ϭ<Ǿr{dfqYHD-۩0z@UnUdRr>&}_2&uB磕W٧=,Tّ+DK Ǒ5SdYNd:zU%(6+;ٜW|o\10s 5ZݻȹX;sX}L^q)5mNOuW.j^}k1X|u{43ޯsz"}{oz-窷E1wS}L}/Srf</CvR@+O_X|B?ș)};MdKb0\Fd QY-@ ewMfiV 3K3<%cd'}L'xugPۃS[k |,TCm6EVΙVV/T[,jƙо!N݅ǽ'X>7uq~`l/ȺpWuWgn+'7B_9S8='By6d^Ye+wcgCl\@YfO~{o~TV4!7bY܆#;y){{؃mEae6oòھs8["ҐM/!ڒk@^!dSN PP' kcx2I{9ġfo[ ߡZ,Q;uM4Ga ~7!yr)PTVϳ <#pSˁM׃!&Ł,rkqlߩ<[Ҝ?nFޥ{]R gR~۞gYa=Y8W?(͋ Ƭ7WCgwxGSNuδ)/DX -ƧW&%-ݛOϣHųXn3z'{q0j/x+lx;9Sf~yw.eD;W)n#_'+M`(۷G6lxJ:"rɯhu; ;m߀w>oL*ywct TF:#g{S|jΐӁ]1et WizV|Axqp?H:g$!oϾmfW#Xy'dI8 ŴXv1n)}S *-ړ";US8$2?_,['9މ7cpM]my୅CqS[eI2 Stpqx7}a_vU,;H`q~-EVoCq f3V>u{gd;HEJ5JU"|QEUƭCk?ƫVƜK0g(xq?xꐀW4pca8f,k.܎w> b'.R),q'fwroeVP_4in0niL@GB-®m>/Z<0s Pig< @ ܄9i0T',O Ty1GdWԵ<J&ҫ9{s͋g}C{v<^Oo1hi/zb]ܭImKj5<~s/v/>ē|W~A|HO0'N\VW=1,'Y?'R+wixrmxRwF=b?`r Lk$%e!sp{ R1vK5%`NO(߶t.rr96H*X7rTYh>@Κy_ΚiLĒOm9ZA ጫi, VGtnco ԋ&vY*-gU܀;R@Ln)hSK&ID0؉,-)goڦY=1<w3W_LvȉGS [0 iYqפ}Ƕ4]~ތuU;W^9h4 iGNKijF]>[p]̂)_MeD仡Y]=W^%>zg8uEr^&hYX욾?'+aJD/O+Ƞfn|p5:zg_c.'JMˆv$9,:YȶܗhNFw^q0ARiL&WK^Mc8`%R-Wcojisd+yzS=^cǰ_]V͕ mǿ2/E~Ly)%c 5LXY&L΂bxOvEqqULwT}fL}T_S={_zx wR`Rt62"9b%/Wּ`﨟Wgt1+ۉKPal֘YQ؁8`qASmڼ#~zM~d1LC:E oz#NW_E5u3'QWpnI;q#NzeK/X1Qvg gFQUiwX6+OGgO>yn`Ndh裛 PJ+tB2 zeL]3 W2vxY$sD;i5*u&Odx% U̢jW1\n>;hiE`ᾏYUTF ` O4s}tG{OBsR~ckЪؔ>hxJN`. ɋ'I܋E`"Kg_d2浨9989v&z"u,WGӯvI #<5 317ĺ^}Ӵ#_vRE8:zUKC #K+"QSٸ6cKobk6,C㑑Xs%} yG Ul hzcIv~KN``T̛"WAzd0|:Xd@:o` r]J_OǣSrl]7JTfH+9vh7DJDAyjY~(Eg}Iկ-:~lkm~Ɏ_~DeWd+pa+&cE9:RFiurYʺ$:+9?;^-覫ϯWpzh޸%./J[vt+T%aƑc*TG9(m$WYn.<Fw`d*~-ڛ)]}m" pG4z[)u^S@UsLt|[yV^z/}6j2iːp=w,v!aDx#I`јUl`Ikӌu3yƒ@O}[|7Sqc=Z9Q5jm8cK ቆ'h0tPt1 8^9]!@ҡ8$LżwI@W4K: ቆ'84K:W: P. ^Pcp&@rû$C+hxi;CÓg! !QT5 Dԫ$fVYCz5U͂&*U^eMUI$` O4<%ɥ_V4<_P"  g9]!@ҡ8$LżwI@J㐀33%^DÓNLK:W: P. 0􊆧43K:W: P. ^Pcp&@rû$C+TPPz)I=$@$@$;s6ꕷ뗥K Uj3Wo^y~Y^;s6ChxJ3_V[Ki*"  3<AzLWfE @JEAf+3"D0'і2> 2 Y eMUI$@J"lf5WYS,h PYe UT5 D^fk&MY ^V+?IHHHHHHHHHH4 f:hV%ՆjqIHHHHHHHHHH Oifxj׮f le re$@$@$@$@$@$@$@$@$@$@$-4<ቮR`(^+ I>$$!8 P$$!8 P$W4Y=԰qKty4߾ojdWy,ڎ[C+y&͐wm>z3E}>B^t~_4G\uU-8'/gc1B3Fe \ܻ+{'41r. 漣@*`8n3>/چ2>7>?;.-lN}ZWߌvPvO`%!Z^,bOv8ҝ@2*P>H4Chx)*;xOދK_bXyv6MS1ܱ|nZsxcΒ4PO'LCq+aLWxa.nn9ӛS>}E{xnWJ8׵qds71 y+>:NƓ-G5LC:QR}ꦫN, c\)ˆcskjzz#;g;C5_ RMb*jt/N!=eAsѷEK#LJ9Fޏf>}fc{ m ۛ]}'泮;^]O* 3/uO[ϴ#zedzX" f,b!U=9{z`ݸ24s!cjW}ZjZzU)oxylenZ9t?1_ P+r!V{tƫcߢv7Z]+R$V}p3f XE,Z=) 9b~hck +O* PBH<ChxbѨӧQ硘twoO|ʘ O(0+B;z➥{ѬITWȶmX}ߋa2?4Z4Kg'O}O.U :(Yǩ1ozw@?Bթb|| CeG<{(nkxxj#G 5Jٌ=E(0fuȻ<4be)ܰo5S}=\q4 Te=ѫr 7aAh2Ɠ,Ř:Sjя7 F_cMW7^n,.ѻ  (w0殼-sU:1?@zk{+m_I$iXg2.j153 Fv™37~,ʩezu%wO{|*߾? .~}+n8W^iqō,}<7v6ZH1q.)&^ŖPV,GXǯVG~'wJЩSH5&gq,կK~t2&"AcB@]UJ LI zEÓ O'7.WOt׼wƘ&^EN(-Ói'&j¦M7>i(YV W wu]]( bb Q…>8P{Sd3|ʗ]bDײ{),M͞9hxycFwe׃R_ ~wcه Pbvڿo}>K i4ȿX5@Vz1_ѯOob8~<{lf2tIHQ0oFG`+;Aħ?(^.6޵r>0X~_R% U::2Zu`ܳ|,>Fw4: d Mmza߸Tq_<(,QwK0guk*J\cߩg#i:ݗU&i3Bm?fv.HmbH/2ګ޳8!kkC?C  Pv4 zr$M? xCPcbYd}]#@ 'W}_WX_82i>O&CCBs{K08ʉ7:NPWb^Eq_ۧn;cӞBav/}O.REknPL}Ct Kx旴#PwzvE@$4^1oc̘idCj-[+Q@xSܩAb|niW }";Rp/V=ٳ5`w9> :}fL}džKD3m\YnqeKq|Z872٥2yJ0dlXNCdWKζ"KzL w.]&FH3{y4fŀXۖ*!~ckxUfY0~*Gۛ7]}4u9%̜EgӟcgP_SNx/}^ g>&ص$>okǺ),B(Rޏcy?ZƧk;_ TZ}O$uG@Op׏qfW"Qhb} YH'*_v߫0jr5NXeӫrW^WGRb ,T`NR%VEv߽R\KcV h>2/00 }~Tz% Jȁ ѬaF8Wƍehtli >c%8кc[,C{ x9S]^{R_hr~\EJ5zEÓ Om( ߒPfcǺ O9]O곜10mT^tD攕8(!^H6x OQϑ ߰;"qh`WCP͓tT+An oU%[s{.y4u%;Wߊ1mWS*C{9+|5٣!dVWOkNɁl9#M?٦Ã_Ht4A r9{j`%^ʜp>K:!rDM{8N Cku`U)gfֱ5슿/0(ތSr~K;W sgrFt%kATx>g<Iִ[k`TLWhSOyIs1096,^  I :bcC]$a=3XOe7c6i]oBv5Q#+uxpaQ0dq6E'tjUZze 5opv8ל^8axoh`Jj]XNPÃ5N=-~{UTXeƒ"[~&V*\sa7I.}yv]o :&gþ.tJ}VOXy-v1=D Rx.ooQ $P ) Onbwwy[;u/ N? MT'tӰɼzVm1.&T0A\a ^M*Fv !Nˎq?}$;˾jw;X1n'{@v̓7vr/;>)x׵`~@]fv~ &̑ZD$nҽGdL &+7v|l8W>{8s!h!H Bԟ(\Q:j~5Z2_bpK/Cu2E5ߩvh&D&0f&&:gsYg98Zz{ ibӼQryIW.PW M[y3VnQOjֽmcժCz-p`!}%i4WRJ'.wOJwhx'g.'e/c9H.ѵ'9MB2em{ʘ_8^2]A ]T"y}Y\h^A.,N q<90Egqs)MۃFw+JüIT^BꠜƟyc@q?eeK*;yG.r3жdr!LȲmDB٥(S}~x*/~|>F 57a0vHLnR٤{}xwdse4yK:WޫC*' OG_l^cr6֍&ʫgz*u0sŞEGh}w)VW'MRPiHΩb}ʟb EK5tF21Kz%k*%"=1}8;*];WU_EJHRkϾ`ܝ +ؕAbkYݩ+IfG/ι$4Nr@mYƏwPUv2_b=czU|wo$n[¡nHI^;W~h_|JO?Ewj:&+݉s@@׮l}2Ky]3 k1i%gyCi#FOPYb)G/^t3i)M"~; 0]Tut5Z=MFRš 3+nq/݌'gQ'ΠIRWFFJK(}Cʊs̉x myo9SiHF8 f$WlfΛ\O?8jݛ+YڛIm{f|W]I8w41׷tU\еbS۷W^3^MyJkϹ={JyT H]yHg1畍wzoruG:)*=h̃D5OLpfT_C?ށfZϬ^|QбhUW؎PwJmO5"fAlzneZub]ȋ](&u{-Ix ꗦ=]fˡݦuIdONO?mӑ+nO7C/ "'cW銜mٶvq(OIs BH hXLOKZ;^ixLWr\_nɪOwWK;NuBǷ믧ӿ8ˉEG@׮l}Z"} blxjzkǴmaRlbâޒ (An#k#ڒ>T-ZN@Ow:ZU]lqC۔ PT-TsebB/9y`H$IJnF'%0{)a#7 0OoyZBMS^|HmOʃf_5c,}jz)ʼnq^ro(vWQgeu۶' y=i{ZVsyJ:7n#mXO ɆMG}>X7ȘFѾ4G:":'o˲ꪧ?),SW]":`|ZvIf'Ɲ)<#i w;!=#:8s1S;s5r8k~>\Q) ϡ]} WJ[.=!NL(ſ);{XO]YKOU v0~ؕՌO=s/QXF5_%"Z{@"tб+Y " ?Q7cS+*%EreWJ;QPGPV*)rujvuG,3`I/d?\%jd%s/[9 mS'By݂A@ˮh򸓻^"jt/cW6gKPjr{cbK38jR3aWp {3oOѢGW^`#:R\پW|7er_FO_iϮdOMfW?"F:>~Ǽk:v0_z55%~[+ㄼ/Ț<;)Tu2sE._w E(m\8~7kM?.]H`~N+cy=tt-h,:|{!$_i2L?zui}t鐹i~н&1Qy9!W];Op%G kv%^'GP6w'F@ˮ?]uqvs92kS:\X&?Y4m~ WXۓjTl3:ΥO,nr,/Zvu<~l^ShF4Ds؋FPvSO>xݸ[ۋfR )T֎'y +ao] {FMs{7E3=Z6K(8^818Aeëߤ+iLXȕ@A86IZrub_H37ĞS$|=~'?eD:%3ǂ~:SX%hMPoMP͝K|~ h }qt.d+8}GMWO&R?u.%uo&q7sb% kcOO$d|!Duh6;aᄕ3um+,''y4Fj!v?M8>}% Ƕ"xnz\~qH6sf*DvE鞛v~ч.zfǮNp9/vHlNG%`YLI3fy"#,q+8oД<ܞqбۈ:KϧؕngĴo?<o@W]2a]R*3}'bz-rV˸ C7dn3Sats~ߛSNܩ:}ҿpRRtJ~^4J }y/ΟII<^7'1^'Ք5x*)- )Žw>sX7gQhdkX5/2gx"uvwfgƗ<~5oxjPzixFKjι:~;O7]Wŝ.a_`E bI-Tve/FѦXֵT[nX?z QMJqؼ}2cyWϴ4ǽjOv΋՗?{|uWħ5!JhD_biCR[4z#}}KC^syRO"Íһ7;Wė'&rī瘫ҽ'6dvpLؙp4)Tʞ=E= <+| .혴eWm'lnr9Ӗ ?]eXK_UR/?{L'&*鮂.vekkdtK.vh6uvzC'&Nn]עd( ڕiSMmɓHl,o؋)l®x3z}9 WӪe5<N;윸tNpC㚗{dۯӝyWpE䞆*%1@,3Pe-sii=ILtݖ\EVR+y8.~N&&Snqgq'J,ս[_,v赛1{v!J\Q'5&@a7@Uc:d|^9ѹڎo1uꗼ?G4!66e`'G>ED2F8}}qMZtzm9uj]tO_Ď;OcI2jړۃ=fK# ®xi_tRZv7FB.O\DG[̶_ :iGs>|Znz%6<.PEJE{b ׿ߠ /A_{}ݔn6Wsb|I/ǒNudڝ[CjK60פ#)Fȫr6J3`8)M{w@grs186UflrqtsF_6ъ^J?{c$~x _%W➛NK7ޏӪgDwj3:Nϣ+O/C_>}O[-5x }a4ܷ︃h7?}δR nH[b:?SNJk;^$jv0oH-pٕ>Ŵi9Ֆ'gъd]ϦW܆> 'pUusr鋭"/h"qUb=Y-rL@ʦmyG*,;u8fbo+h{Kp jtiBFL#U-A@W_ǏRcY-JWUuyŻ[~C$N"-iŜcvh<KL& f"cJ~A/9ڛ#m]Ko>9ޮ` ̼^veаdεWй2cC  ^1Ah<&x BA8aWpPV))'?zkrs釳?n]BO@׮l}v M@OQu<Z$]Ǔ(ޗz/6J9gor&=Zm_Dvj|O9B³smyo9zGW ݸU1F[ipŠC\QzGx=˒_ALptNqXkfYpڳn%Uz4." {0/ՙ,O7&3@xiOZf'54$$opo_H}  =t4{W}l/>V>[Ǻ#Y[>휅ZxA蟏=3'>OYr%Wqpv4N5PڕL4g1XEA1Lo O"\ =̫ǹe&>v{{_D@Ǯrod2q =|GjWmma.Ey5>@IDATu2Ξ!Sq U媯Cl=kj+jRB9vx/^)+}|qE ڕOShSly˂x,_?BL:Sh˿Pw?{ Q#_7M9t OJ{>n=}>9h릫wߗ'pfھ(E;^LYGnfciۆMĉ4尩Xޞ֙nQWn-ۉXF0s^7[ ԷIN[c۵gvs7%={D4biNcis/| 0ە>%~ogd1 9h2YZ#w'B .J807Ǎ}{dHhCǍ#@vipmN4ixZi|g1ch4>Y<.C4jn]spt$ '8>KgV @@!PQ #)UH+b5UQW)UH+b5UQW)aWp<Rx@@ 8)Ive'GvK&ؕI Up,]Ǔ% SIV;  EM>RV *j>RV *j>R®x)92b42 @p1 %R$O*8H L+>A 8X"%0  'SJv@:FE]}>`W!U`WE]}>`W!U`WE]}>]SHsdĂid8#cK&ؕI Up,`W& |@p`WDJ `vO>'O%Y(45t |H BZ1 |H BZ1 |H  ȈpF.  @(8H L+>A 8X"%0 Lc@$ '8L}(O8JQh(juA]b VQ]uA]b VQ]uAvO!Uϑ \@@#Qp,`W& |@p`WDJ `]$ ])I@Op,Ud+ aWp<4k֬J>T?)4J   P1*!% i@&*!% i@&*!% 9ΝWQFTm%zl#*J  YEjQ]>,Ud+ U#]EjQvSO7 #P%(*UuA"!*EEvUTaH* bUQU-®xH5b_H@@ 8)Ive'GvK&ؕI Up,]2I'd>GSO=3!+&8Bxi!/Ug?B.!)dI&_k"(aW E%SO7`6n8   j?dwߵq Ovwؕ}+}v<vSO7kwj3f@@F:F#Iy U49`W#Iy U49`W#Iy aWp*UTk*$U!#vOQo_&\  "2 Jd"S(HBT%2`WJ$D`W! ®x)2 S8tB@ǨwT Z(W! IyG*5r쪐wT  귯r .tBT%2`WJ$D`W! T% "UD aWp<)O:p@! cTH;`WQY`WJv՚E IvUH;]STWx :F! T% "UD Ud U*D+8xB'j@@1*$}U,UHBGQ%j͢\$*$}U®x)\p<„@@BDUD Ud U*D*2U*DQ"C@Op*UTk*$U!#]EfQB]>*aWp<UU OWA/2fs214GL}Huۆhc_? H;ihNm5oN/:uDcEchISٜhQG^i 4i# 88,9˳XoX+Ƶ}1~{kĝC鍿@ϷI}B|nhGIߜK'믾B|͍K8 t,8C@"P# %B+8x L1\O־Akt}KWPC/isu覱owӸLq;?Kf˪KO^RN|olz`8BGf\ x~$I9u^9.ٮt^OK:尬Z>2t)zȝJ+V}>]ik8N{XZǞ}9ʳWW'{ӴF-<BTo'Ftյ\tPR;`Wwk7pci]i#Nvw谽<]nr_ [+]=~yh?Ƕyz#v޲ЮLO!NmZAo%J'_'ѱ<cC5?|>.]jV0/ LFýF9 _Sr%Xg|Wn.:gXIČ ;x~u5_yCG] 3fj~ƌrunjZV7g G3>:UF{_J%ͺFYj0r4% &aʬr#nVc7hȬ4>cf.}j=? ӢSUTfԷ!uuSϲ=|G _ VL!9l=3ԧvys4VLLWar}FP>?8eAYǭuh)m3)j9UQ]^Ok *c[_̨ϐ>wgL-OgSen2U]f {c:}DYv\9ٕdwf=mFe$UY $}CW\?ɤϑ"]e6^H#8 Ft |ׯ7[~ÆO>L?K8f^#S;K1oX5ϸcM,bq9e`hl2b`dT8]`uni wM FcchW;'UFrMdtG̨kn7zc3[̬W-:㉪Z289zcROFK=qs XڐR_~F[wEpEF4¨(wjvUpTө"\uNҍu7Izge'g,_eFMupn̽?9RwqrsT 4e!ujp<ɵ1 ^r' TϹtyJqз+EWsy;=rClve' u{ԉOim~ ?}5RejZ>Cܨ1*m<{r:Rl(wڛl>A}LPG3%z63*sWf^+SUb֥A.+6~'̱d#oWٷt i˓VXXJ]9ZyIY}s3O~nR~+P:+Y'GCԉ%⡮͚DYfxRVGQӚ5sCb H/T;;r|CV1 w_J\SV3LvKi8~%*2NeQ{Sg#V$ Ε,UA.eLy:r` &VX)p(B\6MmO6*ϓcLuڳyEڢaY`Ϣ缵T53tiv+|/I;]}oX6|{ BO;/B5ZvHsC6t>ɍF;ĬڞӳYܲ UFer;!gWx]m{w55zZH Q^{0upq~WaSU~Lf:ퟘT'O/"Ag2M@Ϯ2IWH$WfKN`4K;wEVڼmIVqP0v}H罐LAJsP,=Ẍ rH0>)^USҩ{p*|ɁCq)5Ćb;qUFeeQ횞ka9]xY)"jqmx_]F{}RW"xRc}Az{ &m]+(?̟g1в׌I{^9R@Nv6_?aWpNJ<^gzm}tӆ>x0>R1 OeU[Fzwsc4?OGJ@$F1tO=sGM7|ڹWipUk]xs",N~6ug|gxR5'I" 1Qn'0r(.M][sdjևOz~([y]Gγ!bW>Jw_VvݓPt oV̩V~+ *sQ GAI@Ot< mtߌNtO˜j% :WԶTbY^K;pnDGWv< EuvҐό-)hSjݺqz\oq{y}^CSfʢ~^'5umF߫|.|STa~{yZc;p[g=2x:uJs{]FCM@?8G@K(t۫2wţ{狉]e.fN9 k<0fմګwOZg&k7NUl[}2r0=Vx0=Tz1jm![Uߚ/pʨw5͆<lӨFnB|_m˦^l[1~[q.+m#@Dԃ/[vSOқ}g-¸bznFR*ʙ~P }=F{kQWSmv DxAX^KΝ);*\O0+r(V('b O9g$X>訳W7}tz^Ku*Z' ZM:rz|1ʏ>nlu68y%Ws{{!B`GZ8Zv}GG[}Dkb|6cd7~7mŬIR1q82Cm#ǍYwv"w|ŌrYֽOԛ͙fP3{g ΞWb3˔5=(졬W9Vd=ͥ<)3 A;dU6Q$~P=G۫j2wԶs/aq>RtJx; +~`99ӄ`{&mp2KYSjOP͆ϊ.ZveK?ЛU͝Yom]qgӖ߇v4h;kEP-Ob>Z4ȐųA_BM@ˮ2"w}]F#oGPQ^n󿊊J1k̉neFsOf%}_6.&zO mŠIvuhhٕlFx/8oZrUH,E®x͐zRKˌ󓝼y{zœ77~{OA]6/i՘<{5'.W FOWR6]iȜ]*wܙü\g' [5CVZn{OL\=IV*yk^)[b{hu_g&Oݎ>sT><Uڅеt5p33V"qO'Uf*0CW&eR)]VXj}r%N!-R&˃oTՔ: 7f\3N N"(6i>xLt;VlpKJ@ˮ|F[N Fw`cE{%vWx?G}` iW{DYy^ĝ""|?ݣ$8  aWpQ_-\_+5lTK0k互a9#7(z[e FWO߷hUJ^{p|E5vqh~ceGHPXFy )~2t}>hS󱹃m:f؁Zo:6P!tێⲺĆL#9܌XgmO}=]baڱ"_u ynr 8O&ƼZvi_GMߦgNb#kW˩߱{;V9gj?O$i7*IfPg$G@ˮڊ_UQߖkЧXkc->nIDӌ~kO2OtIվ}2h4g6UWPGT;w](ީuƌa#{>I{( yd^}ep+6/sk+OQ_$XAڕ$VxL e)eF.ɏC :bkM7[!M[$LnOlP} sx9yugD)6 ,f֩}Te7ZYxrj´i9qR|eyL:\>&l)a3AqL,7y9>+S  |=L:v| Z҅EEٯ@0vN>&RNySK=z|FzrvQ:ΥcO*v֋1 nsgabW l39έXC,e5-JfY{x Pб+%̮t\`ꘔF{ ^Ui]_nxl}2a >ڣ*Խ6(RK Uw)@,;04}WZm^!f[1~F@vu({!VxS#P-Ȣ )g5ㅥKk0nNR8x?̳wSB +q\n5SHCy)#,}]+#jlK fyL*g3|O,CeSI,BlP }zƷ9ڙv5{wk=t֊Q[t:5F{KDEގYru-Vkqi윫ɜa2؛M΋_JUFp:t"s%LWﻬl Tn7\б+eW8-FG['ѹfBy㘉*}%Um^ ^[1-~'Yr`iWY2 OA>I@U;vOܘbX̺9qO'(߃lٲx3f̈N<9, 6ك:W6Z &ҡF8vLi%֮YmM~0M?x?+p_i ._mM]hWUW~KǡMb;&5itQG҄L{i z}9;M~$yp|Y'90v*0]1I{1hO?hsȡ<Dyy..BUOGΤ'H-[6V\-3wt2:0'B(z&K/uAI[^X7|ڹ*!@GAuGY_"lЇۅty'yb"&0Rv厈*+hUO_ -{Б~9΂@(]e#M=,vNڼzmE'e@*?O?vas=B=?=sMb˖-oFsٮu4nܸxp<EH TE  EJ :"IHV+ U`W$U$*0U+G+8)Pp<%@@BUje UXjrD*J*,59D@Op`WV`WdI®x)Pp<%@@BUje UXjrD*J*,59D@Op`WV`WdI®x)Pp<%@@BUje UXjrD*J*,59D@Op`WV`WdI®x cP           'FV={u36*> ƍEO{"0@OG\T8\/[,~v.5kBz#&lkkV<%QSN9ł(&===qqxJ֚{+F_*|E           #Eo~s@V^O+X @ 8i YDOExϩ6oL[n%0(;@G=؃w_mW+O!w<  6^[ @hӴihw L@@@@@@@x I8/_NSPW 7fΜIb5@@@@@@@@ x J0X d8q"tA!8Bx{:uuu.   ʰ&;     n@IDAT  ,8Bx~`kDI=Ep(@Q)zJ ,@a=9d\A@@@@@@@@"SHOTM8N8Wp<4j,@@@I`w9s3        RB_\   ƌCG}4X@ 2өW Q1ׯ/@4iM6-BBxeڼy3    P"ĪѣG{A=JQL(2p<|S(apx^zj5{l8gmT}Ǎ 8@           x [$e˖@)Yp<           AȷIX44@@@@@@@@@@@@GE            PRx*FaA@@@@@@@@@@@ x[            %En@@@@@@@@@@@GE            PRx*FaA@@@@@@@@@@@ x[            %En@@@@@@@@@@@GE            PRx*FaA@@@@@@@@@@@ x[            %En@@@@@@@@@@@GE            PRx*FaA@@@@@@@@@@@ x[            %En@@@@@@@@@@@GE            PRx*FaA@@@@@@@@@@@ x[            %En@@@@@@@@@@@GE            PRx*FaA@@@@@@@@@@@ x[            %En@@@@@@@@@@@GE            PRx*FaA@@@@@@@@@@@ x[            %En@@@@@@@@@@@GE            PRx*FaA@@@@@@@@@@@ x[            %En@@@@@@@@@@@GE            PR xڼ>\O;L4L4n4uG L`4m'4zC PhpīvGJ2.ӦM[hP1ci}=R(a@A@@@@@2(_OZS䛹gt-צa:v)'OKΧʆ|g7 -LGѯhh57"Kkh&-?{z>i'\FtWJvm]QVD_<̠˗Ӿ.;%lxx})tsA&:SF"U숤~e(tShwΡeS Ww>g.ej 4KF&R3A=LYs!Sȼ}.:LZf2~ޣE)N*ӻ#/{ől[>{ɟn^ϙ7:a}~;WiO%Qsy9\v[~;=d/.qǹ]#nøf:|=z=ss:Ե|F    C`O`N%Sbz=~ Z3м4ӂ_HTIW~OxyN4EMO:NaM+Yo7#r _Kxͭl#w'D'(&&_JમE+) lzKd:VC!@$57JEj+IeLPͫ*J%U0>JJ-5F[=P@߷9k}~J^A쟵^kE gb?CU&'LNto`@4tS0TD~ꮚO.lk~OIӡ>ӫߥ.Hg8qm5 5\c{{1k4,p0csPFgv:o6Ǐ;p 0O*YԮlL㘓J1'[ lSjۏ"VQqxkzv̤Mv_$}z~cX8\Tx7``Ƿ$C;R}T~b͡;niP 7! D ^UųB8htfұT2܋^~߯:>}I<s'Z͙&.kG4︯rmy0zW[ˆ& +  C  qN.$+s%Ct:K<i*(xaJ'pwuz8 tCTh3fW@hoXBd97&cU06վ?IV(#x]8cT[V&0RK0ئso~xykV]$y;YЮЧU҇E\W\S`xIdx) 8ԋ;FG</O|^$hs8i!:? O3A8}mOot,hkb1aiXoghC]Bj|ZExi4u|3W3oD`.뼃BT0$(>-VftCAp|{Nyǀߨk/TIt3<+wCkkyG B&:+, 4?I hx+oT~V]*Xw'}o]tkMO&{oX.*u_ z^}4NE0ɪ+[,E#_餲fݵʇ 8s2eY-ؐJ%L5i4y҄1)w]>m[#oötH4t:D=)h#s&RU3Sț8 4Nc@T}^W:'X#8%xʆ΁^&d:LƲX<i38:p._FO+Q:3Ry 9$B;<>huv8};*^wGiES׆αioM)sYl}&NQwYeLj17)5\+=M2qc3vpcQ }Nۙ8-8mjsetx,Yo)A8M 7֤\\ }”Lg@}r 5ϭ$S_DkfDA@A@k &ǤT$cU%|0x336ǔN3Ql[zΎ_HÇ-_+'5FIj7h,q2t5dr]ňMzUȨKi{m#%褸ZXExȌ:΂NkްVъg@ݮ듇Rձ'9sݔշџ~ӚXG с˨| X6:mvU a+tpv!CKM֡49EYzxg#ұ>ZF]R@] amW?j06}~TX:־Xntݭ[j7YNߨ*u깻:6Lo'; J6a裵h,x)f+ӛf oi5͛.#][/MꣳX2$qǮl;x7!QuS55Tm[7rZWmӖOnTYu и'+i A?+^9#d-Zڏh36G<.˫b'ԝ3\ilAigl ʚ`>7cc0%/<   \?'`4h;Ľ;iySNi?M}[LOD}H (&]sOh]>Hx^94߁4s`*9Vk>F,TNqje2k[3+agL<6UYE]ܽ[3 ٣@HIGԪ˔Rڅј\M܋ۆ _Leeo>]&hxNuL`ܹ{/[\c=v/NoV8TmZ40p4=adA4q0f}xlLO=*X3Z0MtD1ZA:}~Hzĭ˿O2;ݮ㩳e% B~.+GѺWX4ǶOEiLL3(;ES^⍉4ǾC1E_2Y`FMov^?X{9#0^K&OLkEZnAs(Mb5qy$| x>JsҢ*6Xd;M[Ɓ 㰮ڎ|;IJr^܂uˤds?>X VS !fq9¦AU-k}bZAS`D*8q N\f2rAXXTih78M|`ǮgT_cpF;3Qk.cPh.jK3 Nr48C^jn?sZ= R5BvhxNRIǪ26nOض{q }8ci; q>Znְ"۱+zR߆ !oۊT5ܫ5>OӪ`U7:M+a'C{諸%ܖ'WZrmtfQd`q5ߋ[h g6=fOP s,393C3FA@A2;^reHtOl\M1/#闆{yۂ'1*47r‰\lj|Ck MKTl  nzS*d-OZ/FAMx!'#)υ$)g|_P 49W9^Կs|DkEM;h݊Oz \&eQ6e57NizD_lz:_hqnJhP E0؝?p:阆:!$ŝAgx^D2 \^'>e&ij }Bk4D?= -M\MWY: &/̔q[{ 40V!\; { # }v)Y#d3>}Zq&c&c9+;!ʟYڌڵbѤbz?׏Oq-NotOh)WoRP>,p+ b/~%N\'56yGgOW3y~ytYcڹu3MC 1є]iekJ5ŀN5mnHt,\Ve~HTKu57T1m)ۍX }u}ꅖ\%sͽ0<ۑ|U2p9~} 'kn;]D0oL;(x*]@ZF>^\ BfDi;95)M.Q/|Stɟм"vnN+{7_dw۠7,w>ߐ:G6K1RG N "ɅFƊCeL!Ln6s,LȢla~)07!e䲩jۿ䷭1>rpWa OŸUE/Z8ߧt1 u,qdUVV&Sk;s}+CbtyǨOURoX;yJk5wv0@id>NFw%c\5h&]ޜ:~77X~X\\F9Y~|~:%tBK ;aj>gҟRpNٷAWƫ$~xS]Qs'{4P6#K}s1- cx4yYBOYBۼhOC>wô5W[N-kr*N*z_arZ5NmI㰀^Ds6e;C:5}HP?5UOclpfQ> jb!-Sy 9jY <`P9v2p-Ef1eHt1*V?; iaLNȬg,O=JJڸ=r! c 54X 퇉0Qdl _A@A@A 5qN*ux`-r6>e8B}t~ z&;^muMrDlXtQ䝫7XÔ[c ydcTf 2\t鴣KzFh^$WXe:@];VܻQۑx/4i( 'GAMOx fv1CDK6m|}`ł6g2qBoe{+N8s)G㽻VѡݷG1az~ٌS6:[ӍUs|aJ+Aq *PWӮf 4܀<&⢀_38l2CVb}OT8x꿾Z^em}ƈy]^O9I}9o6`=0)~L!2ǛiFfO84Yhn0}yu=6}ilL0xZdL)歴!:N4BTs4uUӱ:O[0cDPDt֚6ud=8тlFy%S:e%*$kwPEZw4W0M]D.qj >rTW(=]ƿu2s̜ܜyS^-x6JTcp1KӥJҍkhmP.*Rn삟^^:eۂ'h` ޔUk$+iˢ`z>:F~bf#c4E(UVP_A@A@A qN*ZR3#(/qm uQ2,mZNC)|c8] V?AiL[>j.CsχpZPJhH55,Cהڽ_O lGuߴOLtS8K0cOh%q":CPhBwK @[@YOA_PHC.ԦiOR w,KˍNKs׹Ħjj .牁.ֲ̟2vNLtz9S w!n^b x&N$x@b(kƮϐ a|LhsvvXds5WۀlcٺJfŴhitцV8[|1aSf<Xܨ[J^l:м7|1Ny`aD׶`.Qj^Z``MN&Amy~R|1G+ɽu. Pǹ)8saub6-]K8<G7X%Mj*iV0!lTCTLnF:oFu,KY:U8j?Sf!aXd( 07A481K|KIHj3C{>GZxɝg?j&Z>2{piuG88qLjIaߒtдzAZ M:f[ alj`̅Φ̈́j712ŀ7w*3^w@:i/oh3JxοET >45I1ڼ>SmdqCX!x+oe00Y/Bppag俵Nۘ\qeZwG]O'b䋄 il&NLô`t~C\VtywÃ}Խs3jHDla %:-O`Uj9(eˇVT9k/`yFI@UVKy̺]ߥ+\jqj-ԎdTazxc2Ջ&nP(׆nGT;fN7Zv^zסZ2vN=z%5P?Ǽn; ڏĜoO,Nџ1,zmG?K.WE-3VckJq(k_C'OsVff3_{|̘nDm;O`BQZThӂ8ňZƧu^.64h)+A@A@D褒`ѸчL\Zr%M:alOHW:W''ݍ)Rr\㍆u}اנY|$o{ttʧ %5gsv{ָ& G-xL+WH^W$+vzZyN2h0t2JBCC 4:_щtq> |hi? YݾJ2)f\WoO_3ːrׇ sMLK% !/QF/}Iukzm;8ݵ@.,m;c}>sz7.+FXs ֋ٍ])̈́zJe7??'![{Z3,/V(}mۯMVȦ4,Q{Ƣ}{lq.:6:W_y9YA@A@$tt7d O,tݫvmOʩwUe͍HY7M-S2 m\]Y4JSi%-ort89z烈^sz9)mԼqa 63GW.Sik6`4U~M'*j陿{|( ߢBYNm8 ~ko"IOB1*Lj_x @ȧds7 m+}B@? TmZ;-~t#!xLe;~ myBև[=`ﰡp"N5`lpMa5:Kj& IDATlƣfnXLc02wTMWs?_%O]!.oOUf:}LYyҪ^N;O1}8҇-f+COY%ÎSyr |ޫǢ;6ТMX\M^qیG`VdցPpN 4>58_Y61m`Lq~vнQ[ݶB1/tڲ?4@Zm_˨)Zipl9nAz*Z*qb 똨6%FE=<&+skdaq0.o}svԩN(ޜ_9[`^n| K%+%D~i>=*jk$|=(x"y36A=gXo$75\̹+ME֫sx]a]#XDi[/MτơƅQ4݌z-+N﹘RD/|5ڼm_Zbn}5z`XMXNHnA@A@LB'B+M&eFϾ>餄N96NCڳs@_EeWq]T0ɒ89͟.!T%Ρ\2!=2k'oS0s0<y9t0M0ۗAFM*,":9bML/ِ2[Ô7* N|M˥d,2eM (=t&ƃ _+oewp()h?NYC?ޛ x&QFS8rs.^O9h8o*=0p3~sCi{^tE8y&sӂ{ Ḻ,DutQMh90_54K/K״< cf1{z/'>r'LEu1oQ˯P߯Z*} gCEQ1$w̡~ tu% (?`mgP&a` ඐ Sش8#yN<15\+zTZ#uK:*]47`^j6zIY?}] 4gwfœ[A@A@,T`)ZN<}l) sHjIŎ_ĴMD $KA@/Z,CT6aӔ@3)ߌI:D&|n]==ўxm_>-LA@A`*=I(?;h:)ӻKNqhӈF"+*MmoR6Qq p XvP+$ pmI]gw[{4 f lͳEWg"t[)  @LJ褄RO :E$؍;"/ӞD   ~ LZ Oj=4/lsqXF_(}D J1{=~jD  E`N+WdejByyy$4,r#     :)Ӓo5Cg(@jԩ+WkCy#7VgA@A@A@A@A@A@WٳyR`I ԥG,tݫ& E       ~m??իWknS;NJo4$MRA@A@A@A@A@AB`N/^'__ɓu!Uյ ~ytx0ɍ      77C?켣餄NN.&y=餄N*ej)4,r#     ͋ +(tE}5n6*//ɓ'sI[o9av>c:S\N7.\p9MA@A@A@A@A@A@nnXhWߡg*Y64       -TVIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v0.9/plotly_exporter.png0000644000175000017500000057251413455362716024765 0ustar noahfxnoahfxPNG  IHDREơ{ iCCPICC ProfileHWTSIWR -)7Az;FHC ˢkAEEEW@\ ؕEe],P' y{w;3hQ%r@fbR2$r ' PFwס%+X@ Ns > tC|XU @$Kp kIp [Kmb}!Le(H3 0bk>LJx; b1rrfBH48:Jò\B Ysro5C-֭6kfCH?5"b8R{ ! gšaB k2DYq#ؖ%B{4;S3G숰8+23j 4PaFl'zyY1# 3|#Fmh gCߦ e6zNh^%Kb /K 8`.?nWOo ;jfF+LVH?N+ ?"RL x]KX@XhF==|O oGPeL+{Z4io# <8=p7< >`ŝqQ?D1@4a޿х7f'[<B!ALci%3A8h#٥˜}61d?3pM`LqO~P4[-O|F #,Rfw(Ոߡ?Zb+YvkÚ;`Q [ +ath),7jc]ogF %w`d.3϶q@>0mq.bLcp wtZ nHX P"@S-pn  bAr Y`X @ X 6 `8A8΀\w/x!!4h b"Έ⏄!H#|DC"%H)R@_# <҃B }kTTFщ3ꍆ44E ejFM "z /A `,1gĒ4L-2kZ\_X?'t[l<_+Z ?_WE  t,Bpp^;" L"fW# D YI$)TDLK:NL%} ˓uɶr2O^B.#!#_&?%)ɹEqȭ%*wIWnL1Sb)ŔrJ4.卼dy"rPͩԩTu5NE}Cьi^dZ>m5vvA`QXPФpYᥢtB2Ń䔌|XJ *(PT+(G*(Rޣ|^ IX_LeIGtn@K齪DU`L}]j*jjj*ՎØfa`\g|={wq .{>^K^ި~MS_#KcF=M\\s,*ӚUǻg/`m-T\+ZkNNAm@mf: /L :tt< u3՘lf9s@OK/HOCKoHD?N~=AC]py䌜265zolb`ܸIII]SiiU3YVns<üjhj30eBTKozV 0%VV/'NLnى_wY߱Q Ybjܖm[i{Վf`Ю=׾!aCG'GccSΪQΫϹ\|\|tutw=[gL&q']ߝ]H!dyV{>2xzm{_W~___@AРuA7u!N!CNRCcB+B Zw#"͑ 28r}(ܨ&'GM$&z^ž=1b}bމ3u+OP N8?bf/%;ypSz:L-z}ɴOל= S) ){R>"Yլ-l_& g->MsO+M{>/3,˫ ܖ>+2&k8;!1s⟚3sH uݘ; C£NTAGAeYV͟9|9O  e혧7oX,H]б`ᲅ.,Z%K.MXںL{٢e~ HHXtcm+]+Vn^S|ĺ* ?\]kT%寽s]mria607oxqee6Q66[6n^sEFŵJ-Z[VnyrWU6m%>mm#pGSquN΂Ov:/u5wRïFמsۣgM=Z/;uo>}- ;%~翦z@聎r~ i4М,nIj9rխoVմUU;زc  Ox1ēWOM>u:3gN>{\p|bSC~?tRKKwkϤc=/wE\w捩7797ʾv;SWv_~f4G=|#]ӺgOyBbO?4}y/:z_ _ ^FM[Qz_ACGg?%|z:43s/_C,Q MKu $xv8%Dvg"&xr naRTcjg7F$/V o1o E8< 1314 1250 1 p@IDATx `ս $ `XԀ *`UZ{łr-E(B("F4Aٝ&$fgΜ;99(8xn*,iVa}9:GJ\=IZ]F4( e @chh aȌDP7Py>9V8r9HU $Os_s*g: 26T@uA 7;Լd0HHHHHHGZ((@|S%s}fPF*,Y>n]tQ%| 4Og=ztqq/+V(UO.:hePԷnhpիk۵k7Gi:F$@$@$@$@$@$@$<ũacSٳ;{M'NkmmQqV>a@PԦf3O<A@?$@$@$@$@$@$@$@-2 i6 vZ#oذaR>uQ@9l+3.~wg)@MM -( &a믟'3Is@5K?߀ i͒gHHHHHHHEP.X__ !a?'O_$n$@$@$@$@$@$@$@1F@l{aYM먚!-0a IIIS+3C$@$p héSxwa uzzSh׏@ztSve$@$@$@$_{ϔQ@-!h۷o߁|@= H$@E||8'7u6$&%j& |fJ$@$@$* H3222z(*H*x1 G]dt&y6x 7IIy/jp   ($3 żA$@$ F5zAz]JX+_ɾ2 ;g ( }%   (NPFZBW|e/Q Okĵo2Y.F?гgɓ8-zUUUi7*zcJJ ڋ;vDvȯ_)8x0*jOi:G2}0:}Ó$@$И1L%VTWסt%:6CF8jB, m:g "mQ@mq1K78ENm"v\dhQ;h3T;'O:Ǐtyjl$ЦʻQYYsθVnhb+܋;6oc2۵-݆6쫅ڃqKq gᾞw#'OSF\ک)re$@$@$fNF&/"m:uNR5UO,7yf'^;Gg[3wXƎ4PΜ9n2599Y3׿=P,_,PRƁr`\t$dnN/^U GDI=e %5S@q_]յHUjuu sj '/,/eZ`gq(LkdNfM zMK\ D ^9uSlڴS/yT^s -W#^1HH>S jyo %C-wVn cˠ;MB:>;vL3 ,g2 0du^.[һt|r<2tTv7Ik#i[Ήp[f)&wIH8[, g! ZVϯق$\}]23Qq-|xgi$u:CCL~s;=  mlzyߌ̕s3(  2 -*eAyW~Zl%P=2ҍ2Uiwj/=zoʇ7| _6gbΫjր Dg_/EZtA@!StnYIi:8-d H폾zXHHQ J_iSo-P>\ԛ -mց64;зW]ɿCT9"˴E<;jyV^S(u[DUP7! #zULA/GٔuK@7p-]Qݫ9[(\3ve$@$@$ Hi^zj~>[u̲0 2oE&;M!Gўda0g ('A4;X29C@Q<շy *Q%gSpH uQW_5?./8*2 (.o[7unDa;솖,Kqh>mI:cx?c{2f$<ȑs =O   H(NFHH h }zS~[M Sm+ UuF^iՉB1jP7}Edjp.%hN\O]:ԉ3)1)) 6A|S!v4TAfϞ,S8X*o9S# 1-;^]R]j<93jpM 苶+Qq9{$@$@$k~c? }&%_ VUo}s~u^d)'D6zS]!7+ꉓoyWJdKR[NlCB綎 ,bﱩ0>uRԁUᢟdm푒$@$9o qVם>-:F˔jԌZR4} FS'#)) 7TYe0%rW]zLQC4p_q`EJpGmU |WSs<o?3)ub b X T3=loÏG~͔>O*LKA)g'70ح-kI8\ˑOoWxeT~#7sq0 }9u>5H\]q5>ϱ hPy&!^) #ǡW$ @h7Se`,w';<׏b*cdH lJfmz@ZI,My1@4qN$w3YʷJ:`4:Y|(E*)HH "Nҫ<(Pu,e@'eF/є1 Kv^ vk3յx5}TF6F%;뇎@/~x/hVeU~ Ob"9i%ocU1{+N?<U~#5<~:~MuKϕۨݑ&o%sQ^H^C/z #~8!W3qasC\uU9HDG%ojg>}C6T4M|$@$@$k@7'~͗Q$+Cѧ`QK܊'\v굔 Hr>fYC:0\.\GȵC+ļ&>E?9NQ}@=|1- _Q) ˁTq2M0l}xUt 'O?iVsJ}4R[A"Xd)NUD_(2. @!|vx!pꩿ?A7g2W2&xY(`#%V8++HڳXM|sfa>7hqCqǐzOso۟[gMXY~*sn62voxys1z c(~ _zápS_Y'J޾[YH+L; w;R>A<6n yviCq e-Q WJ2Mbj/似HtFuYYs5 ڣak_9FN)FͬD :2v0uNc\ ;ߑSc=+KK_κC&pHHB$)` Q=d;zkkl"izWSyH $&&iie@iC8 !Zz퇈@ :ff0HIN%qv ==ao:|u{{ߟr v.QOh㙧ah\mC1cƲG>v7SAh?«ן:N]0h"];|S:9|?"sԯ64U,0]^Ӳ$|6D3]۰d}ԵJb].h̨8dY},!ܴҁn )&9Y, '`-8/{G%d"Z+jӦ{}{|^ ~mK^zBکf3H8x Xƕ~J67 h m]UOmPbKPkH , d bzP_:0WCIHH dћ)ߥ pO!͠iW˳S16ӁZG*Hdڨ֤v=!pH H⫏d{ UəS{XX)(4|z]Ŧ[#"K l/hQrtQ;ˁDy W>jjKƑ%ͻ=a&aG[bva {Y<ŗsMD4Wo]Yh{ ɉxqBdg9~p<9JgGfi~\y?y+!J/WYuH)KoEcm::hԿ! W4]gY~hX:.c xAE`߱qZBP{K6Y~ @R/鏪wBJZ(S+jY,x`VW\_Uu߹֣xᯪҜYmePa6%SɎi Z<)S݃fTMoId?wJvYd  ZCe&k ^| G2k[8 zeoj8ut u?sMG`tN;>U5wGZWqMOUo`[_K+7o-+#\G4v9 ڪ[ڒҐ"tLU9,$Bg1GN\u|,)  (l?/`1TkJ֫]%  +q\N\O@YUy_eM"KT#򆊊 WJ4[ p3_szdG]~5jZVO!1hGLYJds# F-nHdKFj X07cRS~Oƴ~-7ueaIqH_mݱ)7G*|%TK<6O| r!<2\5 MUA 7'9]L>YNe(9#ǡg>9Nݯǀ5xA$@$@V6S@CQk$tNM~˷5^'OkOd6l,"ӧnX JKoj_[Ɍ>)q}kA.tWriz)hI1c@AV眼cMO ?}&Sny@$@ 0A^ a 6X:܂Ut_3^{ #i.Jʐ}6Swu,#~Ru/B?&Q"NVǏ|.tOSV#ܔǮ+\*Ty,zw3@\w.>rQG^\vr+٧`?|x%kEo  @| (EΏG=ez=6XO՚ ZkJ}]eǑJMv&.믿j 6!kpMOBVcbo߾ )?3Dl#Tؑ$_B~/.ߚ]-UceE " &'`=jEjG!`o?ƂSѽq%~Vuׅf;7>0^f1U~ոXCA~HJow㾑}U)VY1(ϜwCQr4F u[ju᤽Ō,CZuԫCh$Y:2vԟu`.]A~aقȲoD A)FZ$_;™ xpV^ _P#VvL t . MDf6e޽6stgDuC:o\P-Pt)yػ)=?Nmt֜wy|Wx?_' W~"Ln$@$@BfϷH)޸>'Ɠ+ twMڻ\]|߯W7a ܍D23q]>'?旈\]70 pXxpnϻ ľ5|;r|eЮ)5_gZ-xQ*u큌`V&S']gX5H^=: +u \ȹ)=pݵR/}.1[LK$@$@f 0R'8^!+7 E=WYȦG1@E'j@{<Y48~@}_幯f(@RR2Hf9U^% czտuc¬f(~JVUϋ$@$߶%%X4׃cl^oo9u0eo p; o-;7qέYRC9tel`'1ώ*~{3'2λv,׵ǥVV1 4)f˜8\B?}%")LzUj8J*Rr=Q ²l6NlN9(!;$@$@$`M <Ώp{j ldJ5zD[)^]iBnгڵ+NwA˝PN4jզSAC@ST:JJha2 ^<]H~qfP;Mmf ~8Vc3h92_ hjzk e@uJRVr4TW87a)cђuZW7|2 1w4 6벜" )ns9sgĺmIՠ{l8 Β +]C  מ@76ͻ !.{$f<ֽ]Gt:U ,4oSʲά# Og &L*Ad:*>v jrV.Ulӂae]|uO#  Vf'/ZkG>))IMor|^T|c-W EYG!1p1m)))ڒ6N΋~jz2tA3YM/6WlCB$˫m ؤ_]mU>wg~}Rz5z̃HjHHWoy.mS)v$}NzIOS3mƑ8߱#egs8:tM]6:Үz9+Nk$mfn"?mj55뮉͖Wa?>y%]F}mRIFj U+o~Ze@)>fH}G||$Jix`Ibo$A <3{tyɂ n$@$@$]^zhY/Ku$Y6t<p/Jd+x8Ħ]~p4 = dDDѼX|(PPu65V2ӧ?1XY?3yn/>81>;UC2;E`k0(5߹*kG o*tf @k#?ֿSՅ9a:Ζdp}?M *TdG:-KԶp.c+q5e1gtk=v&J$)>A$@$ ý ACy]|GY5We5&#MɆњM_yHSFԺ,SZU[{YwB_-Ls Tkƺ0oZ/06fO$@$1a;7Mo  GZ4{5Σ 5ڋd@$2T8гV.S/˲UPW[Ϊifkĩ5T@9RV[c׾suNPh[^Sz n&Lʸ  c@DR @k!p]ʧ@ga~ de`/>z*,;z;p.1 TU PK}{T;.o5?#Klzq |<^.Y`ߌ7b!2W#px3Z|Rt뛌N C)OQjD$@$KrK5˲ @S}(^f|yB70(f HXs@6S@U35U땣B1.(*\K\% ojn}V`Oۤ ],@;PhZwהG$@$@-k@ -& fϙ}sg$@$@$@$@PK= bz       E)-ި, DL N)$       hY>}گttO @"'o{ pرqyHHHHHHH 3X*B$@$@$@$@$@$@$S rxHHHHHHHЌ-LgK$@$@$@$@$@$@$ 8S 4)HHHHHHZc-Ҩ) DS (HHHHHHHTYTHHHHHHHX>EE$@$@$@$@$@$@$p@K%H$@$@$@$@$@$@'`åD      h|9u#      $IIHHHHHH0f p@$jF$@$@$@$@$@$@A))HHHHHHHf\9THHHHHH|h       H3cP'      h|L:l|́HHHHHH:lNA]HHHHHHH) p@Sf^$@$@$@$@$@$@$|JԄHHHHHHHIp@`f&$@$@$@$@$@$@$3h "      F%@GIHHHHHHPƙK$@$@$@$@$@$@g f9IHHHHHHg x1 ):ꙥ$      o)̈́!$@$@$@$@$@$@$p@keHHHHHHp @LدcHHHHHH p5 @LXaHHHHHH|L_dN$@$@$@$@$@$@M3b~Y:      M3|      e)˵˲ ?)ϑ @Lح[HHHHHHL|xHHHHHHb6S V r !f 9S$@$@$@$@$@$@$@1JbbY,      @i      Y)U˂ _t4O @ \HHHHHHtxHHHHHHbnݲd$@$@$@$@$@$@$ó$@$@$@$@$@$@$| f#      |/$      &`pX4  _*aӖ/Q+BH57GU@*Jy!_]z 3X&|rKÈj8[CRIH^r\  N8yV/!#5J(Ux;k`֎%7Z55SĶFVZb:  %wK-и)HZ iwFuŸ(!Ũ@.3|/Yc4{Ë-`J]  h%,& @0?E-soQJ[r*Ruw0%7ǩCEy9N<*} DB :ut+uWVRV$#1?9!R@RJduܦabYxg`)(cfLŢFbVbNE!.#CHHH \n¥t$@$"[^Q*Sbv\;eQ,Z7Q#TR,7(AA*=v7<5Oy8+?#r2tvxٿcŜ%K]r0mgb}ܧ5R~J,tq;u,pLęOqew:|m u(rDrTpw͖O+WL!35pڊ|9ZK;-_5%vZSqᜲt K@g U{lJ$@$|daY(  '޵ v-Yd9^MYQdz[6,-S*3f d8s.f|l,fypFۺf>ԧ`j<35}`ؤXoŢY[U-^_KOGx=Ԁ{-r‹śd'o@0/ϳG$7L4wf_ !-Hnx!͆ڃo`R;|[&=KXf׹) c @l r@l3KG$@AH1c8:6ﭟ?m)3B%k0 ,{ˈN5ά xLD/s%>A,T? ,R*>]䏼 z4}/wRB8zpFP &p?2ek+[,\H$@$#t@  aΏG}}TG`iGpO]=62R̵(̓pցk⿞~j/yS ?nl* 49n&WU~j8ܦaF8>!dN]X3<Yʼnʽx/cD\ne}   !lء OgFIdZmE*gysVl#Ƹ?rYxMՑHH+Gr@sE$@-@,gSaE^X5zP".<! _"_w&8aTL9@Rҗo>%1*0|8v\U], 6."m`%uutVr;44HHbnldHۺXF`5výN[q Q01˔Ż@'[N~c5/3  )=y!,yr|(X7m?g[˓>)̽G;bF$@$z "hϒ @(ݰ,@xݘ:TX` !-XW|{⩨/Ϲl@|X)R<w66r\rϵ2qUK搌 y_r˧bt;.CHHZ(%YX M(//CiYʂ1 1j+%̶r_g÷/_./+ۂ&ZNvYsy*nl1x\?2vr o:vҹDX.e1q"{{]_̫o$/q}}?  hqQQS( @#hH?W A~ ok˷IbP67&~ըC(un/c= F7 óx{$@$@1C@O f! K@ U6hKEr zU?2)> N= yB>@IDATgvca)1+ ,\Xe| &Y,~BBLԧj|dmji$!Lۧw%W>7  8SW RK薪9{O1}OO!? l3 0hl^cCNؽy.eRlZ6[Ė[o2Hb?5-'p7Vm/PFIP:ꑬͮi)zSKHtI};yws!M_@ww]5NHHP'w,0 @ `߉^8o<(-ށ]_YXU>zcˑ@܌_~t\RR{tw)_YC:;***4gZ|R3j f\&o!E(އ3kNb_qA+K$@$ ddd$[  .hdž\PhD-џf$m!E(ۇѧ3 . " J@_>@ `IHHb yb)O!&e^yY"  p q@蘎HHH}X"":oǑ;nr6' !@LIHHH g*\̚tM38  N@_>AIHHHuG=V/VСNdy΃$@$@4 J  hjqB^g!A$ J_(3.L_1HHg$@$@AH틙˖zݠ$^M- #LA/U:߽#g5k4[ jHbz\R^^X*B$@$@$@$@$@$@$@FFO7#!      %@1[, '@>؛Wqޣxԁw v`δAk%"mGP,~^KmK0t  @ W<;y;+Wϖf߻rC6]//wX|'h$o_h2iʼL@ f0_M5ndžR홧͐ﵓdfw-l.~Zs;n<]|w)6MF]ʴ=xAs(ۍP .xAk7 擋|dOb5H4KOVLǃa w JK\4Lf:3ӭl%F g{Bzj3x*Q'5zV8NBj/EҘSjӵXEƈ@EYz\5ӔyYsxvtԞ/*g\O'Zg'+-dI\r]4u=iʼ qG8Z4*}w/8o%wCc(k'XM']K,ԡ0PfX:+**e Bq ʏUI _:]}-2H>uRQVvƷjAMU CvgtcIJIȳb1JO:.Ԏ8c$݇ َkCh;T-U8)lr-AhWp@{ {_\9$'_:||^ g1+C %JH3==UjЎk{v_q|9v*P"w(2#J]Y\aCOR:/-Iep̞ժ`;:o+6E8+TߍRS9pxm@>͎ҒPz\J@.!S+O6'!mMp}["9P{Qg*Z8::7{y 2ap[V/rG,Bͪ,0}.-bӳ1ap댬[(o!TB&xɷ#/s庨PO+I;̪Ug xSkSNSaEqD!>(GZHq|7$gaϘ{uz '/Ȣw^mq%6-~ `2Luf_|]6vOݻ*y-#shVyԁw}kT7ob,y. -i%h5JQ}T5; ;6EcYa|Tae miP`I`SLU{09V`k|T5x( ^ݧv6ÙY?4WsW>'V@9a> *s7ݬa-r/LiPҊ̑vl*eط`n~k+L{LqO[\}[) ec|A3tà}oKFeP5=o_Ń^wk0 A@s]DR_y^N )^W|]7RB/Jͭ{> ߖk'c֕ܢa+:n9-bA.'cdOi2Kw]'5? b3 6"kA@%(^N: v lb$ O,z4(cxOm20\}ܥ8|]Vq#,pW[C߸rN uV+u` ~Uc}Tfk0=+miP)ϙꜿ-oF;uW,Z#%kʲMZ؋a>aRrK9wo߈v~+]oWHw.tZ9-ϙ(6;07V9U|9QĦ{LRؼz᫢xD%/y0kvl^&ψ[޴!lwx񔾺&\Z~Z@X> Cjukȸ~ t_VT9uysIpFV݌{cώ Xm׍a] ~9rKkm޳g76]hWp@MU|S]SAv_*WQ 0Oh.[9} GK/=# ~}l |{3^BV+_6ѺQy{/݃> zJWE@ގ>ڟށ}]v#*ŋ6FӟrqKFFȴzl_Sh^%RW}FGk{i|2^sF[ z>rX1cvsDZq>>Z_1.S3٨yqI'e62Êݦ}m^c<; 7-ĘY>C&`Ş(L>TdffԜ} :n6\'LU2`uƍ_#t-ޕ={v4꓅,afK|Or~l g9 $7A,_"d$IDV"zD蜥!-_'5waLE2:or\-p}T04=+0aW~R31f EW3"W!f~%xrkZx3e ]LŐ =(mvܖ2-z&vArEwŦ.޳yva1n8 +zEqQs ~xK)S FOŃ|GyIq-;@Gkggua\;2~+"+zVbtK=xWb.'MSGpWLڒ+p׈\(YC0wjPeZؽb GrG]kuU0@ҭ Ү@?Pe9Sۼj aW lzT1"exO[ gdcϿQ8L]Z]vÖuеYMj/溿λ߹8C&Jn5= .*i+w2N?RԐ cm{RG%ӈZQ`?us{-7i_a>1; d16TɈTeٿyٞ!;R}5iSJ#~bҕގ'ᄮGS~{Y|/`g^~_>C`RJٹG;u0=BOwj}Vpd_Fe=g(nvEk4Ówy g>x"YG/Jl4_׿1(;|k^WI 8y3^amǗHiRt( \b][>GL5g)r Px6*[ܰgAܢ"Yo= wdj;x]qyg&u:nQ"~n6֘f82?xLYlR3{#i*Q>)5"r wZy|erhrorQ$Sӯr#[`]n:h29R.{Xmuطpf8^*'%1/t oxo|Y lƦ{Kw 5"v1KݾV # ŗǎl$:,ǂe6܈@ :(23ZUۜgod,NOF`+x Q$e\kCi#pnPm0v6W!aZ>}-> !xI,X}5zE4E*CǚwY <5ڬj֭D<o}Lٗl4˸5j9~=6QU\o"3F;8&iؠp8OR!\'OJmTs*9}{l=4Xm#iJ01괌:ezgg&\a{{뷾^JMhEIWIE-(G 2$~9'(̟&3!>p9֡I7#=vTV۷>_2-G.,;^zץ~]Y,чҼ͐iuzNx [:I6 !pl%Q<_U ۍ0baցRq'bD># rdV?@ˆuk[/hPB,W/uKQ3y6ndC#=l@J%R Vei}zt9_\ouE& O•Gz$ʾ3TS+#D{}mܞwl06U:*XF_nb5-~Yw2[Ӿ?q`8n5tf,Wy)e#`Zl&6[`ڜ2n>Ⰰ0Sfh#٘H}Tcj h<||OP^^'=v )+/᥽/Glg~{fGX322,P!|2t7aJ<{Ij?g<\]x[܄tM]=eXhAb#G0)yht aWyn@Gk;0ܸ$uHPF0 Z&j:蛶;=T꫽Kj7UxoW0OQKF>ŻһYj|DeOlWa uP) `ZD+,6Eւzbo20Ii['^o…`iǚ"nϤ&k!qha5冰k*jhOGPAe@̓_QCiu۟\ZKQ+N@[K$\8hߎ- ӡK$=pS)sBN|(}atJjv~Vbca}Y:roIlqpI}uZ{pkÅrZ^!R;IUڲuu>>$Xn) 䗎Tо~i!hϹ~yi/L:P{HJGAYn gqOObW#Np'[蔌8Q{< GXll|àx(He5v4T =7*K o#8ގB0.muMWSTFG6*b+:pR>6 g؁7)M%yY1Ô2~ڦoAnz>Z^6=t8%,s Y=qƳy:mDF–}=3qܪcVoE9+(Nǜ15GmRڟti8g */ʲSMuI}:}*.yȶ}= W+t[R,hCމ9XpW3.]-lGOly#_ gUHGDs;}ڲav@x*@Io.PҌ:ÛcXJc6u64bHd菼b9`R}!HQj֍G_n o}uȝ `5f!ߦ[AS+M0޷/T, hItqvLxMoAr|C9v KHcR$o,1p*{JF_ (8ʾ Et N#lR1|oY4Ju{_υV.xYukp$ ^q~bL:~//Ys Ct}xt[JBz7%!wkEȫ d uCQ\A8g r 5ph\'Bˆ2پJ8ӈYrCI8:vUց:yk?߮X#S\8(˜tV7)urRP @DdXZV뺅n= Key0xl?PVw[#Wy+h_Si78Kْ5蓒q S]b`}|ȳHޣPI)\4I=p[;0A>rV4xS1tUGp@<͕?5+kA5*(Gӳۇ?.Es|zbz)_P. *-^_QmԪ(qb?͆(G>v`l])|HwU?-}Oؠn7wP6,iƢp8=yp=Q.w j"OHivFrzJ3|ٌe-FvIn cGMy|Ik*(\JLUwFWVܐ4T[.ٴtҋAiҍъ :0kA[*馲م͸X.[sF>,煤I]ik>տ=՘H \TE&Ɉvϗ +r`[^VtEb˾3QбDHW/`cn~'6"l1d\ފLGPk~JgU;iAOa24'tMGOJiQ@tCoeK dŢb#ñh[F#BFChy+]U]ʒhv]QsO͏>j 4؆@7@{65 E>ZK;חN4t2=^֨/V}SG]91vF:=ٙ!JiM^+V g4($Vk'PmS~UPC>C϶θ=D {sm&>O?$N.F7UE E7 #GF,K8ۮt,yxro-:BLW$DUw+תƗ.L(0iR|HyPQ[FaoD?g0~]u‰P !O*,q2F]n ˌ_jH֕*;eiMل /7񗞏KyeEQ*Y!Fa3]R=xg#ڥN2Mz3 JRZTŴ>$$tIzGH^GS5Smu|C쬠0qvjh[;iG؇#{Gk&6ӞVF(oE+4'+:&W&󖱶ߨ^HjURPJ1-Rޕ4d4cʗrZ|!VO; G߀Kfp I29I\+ٜ5EwTe=VV7CZQI균G+9o@gEz!,OEcB4 kïF5ҧ^!'l/S=U?Ázw%5p.9]S&*qrE31Pʄ(.^9|@h?EԿ#xpYBk_۔$))VT;5t^e'&U"}Ҭt~n"Dvێ`w .WJ;ɩPA&6|T{&%o6H%5 iNGL-93׎]4Shu^ӈ6.bL'\5NPs(VnAgs6ԂeC͎.4cC_qeR\ M1֡8V1ڤTl^5C./5/풭gKG*PP]\&Ie!y<ᚉ#KtRdƟNԖi SѮ6:hXGJ̈́MlWmqշԾȐ5J{׼>ҥ ~EqTa/(eڜT'&f jmM? I))m.p;5վ"f*T(7dd_FuK ,RҦ%Om3qn(t1_wgl (zm)q0o?BM2r-'ɡӟ8 I:".^3aЩhX$^,˶\}<8)uM4#NEdf WQ7/M/=\G0uofh9MD!NBv?ChDxs6 .1+˰Y#u JÒKS 7 _Dq*rr#+Bi쿾ĤqK:ӊ9ѦҒ3P)N(As'l>!ݟT\5Iʩ`q^S`~;zkd 7D'Ds vrSJCnϘ/ϳLv ?`Dd4[)$>tm*Ɗ&Mcu*垳d)_sƬ̈e~EL Yy=3]B'}͘] >:їf~"iB}6ff_?SM_ax\UW!=F̦peXueܙNA=c 3:L:m5Qf og&WfV)Vs  sV~4chJef2 Hcu.μX)_,j]+/K ##˔1q@@ۉ 0“|ǽFX:{qR<pf<d!Vus3 Իy0`2N`4ʍKQXQ{*#}9|L v _>{`L 0H1_]A~bR‘^G}s`㿄%胃/OU݇ 01I` m,lvMP;tJ'1,9XL 0&p8e+6K47li@+%|[ZAUoj`W)7 Q]C, c= WG4j&U!`W*7>0\A$ &{ F%8\fN^vM{x O8iӶpw!;褍@o^yMVJK7, nC.p &Hd`!~ϩ|;pW~vQdo48b`L 0&`Lh4v`L 0&`L`KLr8`L 0&`L Hb֙`L 0&`L <(0^b`L 0&`L F<(#0`L 0&`L`H/ Ngf -_]Nx}@JZ:2GڷY >.W>8:p#毗|8]$23G&K^ V#QH9>>2f7#U>_#?F(V%DsS:zX3,c(Sck.Qzq88zUuЦu܅=!r*WNY&py\p}Hѧ.c'ٷeHj1X Ɠ%LorZ K=CąWvᕭD%y%\ ?G&+h.{!~͢IT(|d J%:*]˃ڒp2Rc]B`[,*]3<|5j eBv;&+cP  ZŀU.9`b,>R?S U0R&b 9SF|";+u*uV}sκ0iCM)ye̲R+(lpMyx@iE]3l/G]qz3F6ߢoY$D vTtzJPfEm{mOƯ+?K.Cy;fmӷHbI:pbr{WgỴllv%\Jۚl5~*Yd&U#)c6"94~,2l7'lu=ڱeW)I%E'uxFktl&{z ƾD'xhX"aG vwMp- çwށ$,ڼ- L FĨ@L}^x;Fp8!7­yِ3N }A3?\tuι YA:x/u a4MTyNnn8iIl/ݢ] XI5`_xh-Ŋz ]md^ MATdܘ9١[/wA1ao43|nR)X)i%L m.Ępr)μHt5fϣ͐…ၷ00k#_K)~E ҕSC4*?2뜯`A^~hAkuw4%Xƍ}(L f<sp7m[i*`xb/Zqۂ\GUt#p{nxĸMA)?9\n1|Bʊ^C[ 뜹eMg iB25}gߠ抂7b8IDHۂV@IDATr׏)uS h5V4bI lO)bH嗏"ַ߅류r;BtTTfRzgXіJo0tS:}xSN CVʼnz,|.W?b:mNH>W|L`!!&rc馬~/~0saӣ(oPCrU]\,L1=4Hùaf ➓^-1Խ4!k}I3vm]?l,EN0Jja˖!x{*жu^,w' íxtY5t.gBkU`E0zUe,`3_!m'PqaA/֢j {v}Mq]R E}ÇeZlt]F7.l=wu/m8;>Ӕ$3؅-XAߒ4w>yNWpu:Q/շkCAY=V.[¯gY;6 &<P57 h;BW.3ֆ%4BmTo[_5Lyi(|h#QxA-҈5 )`ϷfZ]80* v]BOT+#EhaDcv,*fƤJojjOWTUAf;j#3E3X<\тLGѻT5*7Ju voF:kJ-Yl.E VQ,fF + )'ඁ /D3/ŃaE~TPьԤGa 9v7E`m&Ϩ&ݭjmj7va9`Ԇ8hX6_l{4vagY%f kRj'0 vށUE)n쩶u(]/33ᣚΕ fW»]Kǒf;s O}kကCegSEU;Ms [=[UխN|1Ydd.|Ezڎeހ n6mvu΀тV}zh@@HeAS\Z#SA][띁A,-bN[; (Ѱ y/c1,|8ҍ_=p]/UW~n_S XY }1p@@сE93HӁz;0`@@Ӻʖ8v/[e0xS O))/?ѺjAeyqj%Ly$"}שZۺi+ACtxE)ݛ˛&X4_swmlL!Ӎ)uAx]E'csM~ _G`+ytpmc.7(oH+ŗpg6-JROWn^?p@@𡧥9{*=sc lɨݭLgLDgת5je{0:m)YQ60AMjPI懝?UJثbX5 ߌu]v ȫ*OQG;Od 6?~巤K[0^à= ^*a6 =}W"ԠiD݇V/n"s4@ǰԙIFX`sjiu$\ǻw@[P ljot]K@ƩC8}2.zߴUUʮ6:} {e ɍ/fGN;)P7Rd;C ɵ_o f-~q,RCN0*Y{% (/& )Kj}jXdW!iX¥!ݻBh@Hnh}E9&X XLlNt(k)JP߼;v`|`U%Uq.'v(_T[+ch4vnWE~o7Cç٬/[Jgr!ށl7)tߡvǂ򹢌Ky$>>\?!tP ::GAt]n(Q¸&$2-+ͧ=R5AvW&UZfܘi+SLV\IyT[k=(w&-WJv"At\`[@Q~Sjпy ZB oK] T'@fnI'cL+2V-9{ew*/K9X+Fso!^r lkѺ]oo;H(A"xz' F9Œv@rߏLXgΒ3|4XH{'+@ډ6qtk68CQPS;7:gVH8yԗ%Kr,6G){CPn.AXA/}Ɓ}-A.Bne#ʹ:p5]UHzuv?;i9M`MjTmg3i#źU]^GKW4:wǩL h=JdN(Gs'֮\%)R.(ǖ# 4d.Stڱ rkYWhW#RB{d-ӝJ:} Ԉ{@_*Vn^Hݲ;$(qMWmٶTX%0 0fj~md*)='lC8^}:-F3rQ*|_zkVLِ~bݰx# -KFI60]7}Nmo%뛏ȑ#wTpMTV8 ?;—A9lAvBf}Ȼ :eۿ_ O|(σWi-n!;Cf!~32.{F4;{d#8ڦ?A+ho1ӯO^hiGt7ZPիaWk\WњlI+E4 ޕ,C_?,/ִW9^VN`Ś" I쬴驶аQC,{. wZN[hNJ<ٶlǏ +^U{t+|dy% _ ~_p8~o,.7oG] Ga8_Mmv=q\O^tfU8sG,9:tnlL z]E]۱}{3vu~+N"X1\ލ*]rP̺^sm:]ɒ ڲ7\O\<0ۦ, u$z7qC4AyFG*뼍MhW\V+0m)|"T{n md zY2o I{{״B[iJ6mTT@nN@a`Cҁe3(zP[Iw݁ 3-m<%Li^~IEF:xҖ$Ґ{Tq)%}Q FNnQ]D M4(dC-ոxb,E_?Uj}SIi}}4^5cd|@zuAo*kڐ6GA$:WE1)a,s rJ_u즬/%Bbp4TUa O BI̱RYzaKAcz?Y Ee20xfyI%paȍ_'RCueO{y^]8J/I ~hy[ i1V^ivR*CF"G.ޜ )c/16 yCL1Z>tûwKNc^<- ArIWAneoLuyxoC1Em4/D.=,>Z%]^Q3nF ,k3d3uTb}I~m2-G+:^zsOV?M6aВ\뢵u4(`|Ivy9!-ێױZ8:ZQGURQ+ʑoz>|w鍰:**/.)Zy5ndԛĥڎ-xJ@kKJPo|y2@k@; d@@Ӄ&ھZ8ݢ } } D p?PdQބ^ ө;Ĝ޳n=G~܅i#<@$q+VG؂je;pXc_}퉙}%lqAz ^G+}|9nY|gd0=O< 1݀uY.i?y | [QGve^o 7`2FΛef<{ec7ӑv7bcc'2cSn ']KUkUҲ&ҕm:9bw6Zgu vDQr26IcCax4$'=P~C@뇍.7F.e56:~ JкPuO뵿;Tj ؝EkjKxi 5C MGk`#]/}D SUXP>ŻYj9O>>Kߔ<4x5H[GpУ5" 4f?:/=g'G{џ檨mu{EvhJ7oZoPUNh^9POA"|K~q⠲ti}+'Jh/:3=[F#tӮ0#+=XZeCFȹ%_;}kcƝ7T|l ꒰*L{|],d+(Y9ȿd_9 (Xf<*X|g].CҲtnŨBjGj鲴+LH6~µ~뗧N$C;-w6ԱQYr;ߑØ w BgvTe}$D/lLۀ-nΞ9Cu=|}S}˳!iqλ6 ! K1.6d 4q'8}OWCHD;Q'kq l4-|xAq;Ѡva >XC"qe7<݃7KɘQPN!sGcnƻo+W/JQFoʼ>$ОCpV;]fe6FsnhoSJ0r4%nѹsUAҬŇ:H͉sIƟtLH]#7G Lfմ)yT6 _΀j%|a|.̴nOmOKh5H;)VP}P؀sLC'vTӼJKBJvaڌw t\Ӌ# j`a{ɠ o:\n}kN^ғk~5uku+Y35-]^\ێˤM*'(p-!C,?Y4I+}?]w>Vý4s{^s5WbEKwQ9݃Z"^iЧ៨LǜYQO8}ҍtYXd9V\動 /=o&ZvM|a:@8ՠ.Dؾu,8pqyF ʾ Quf{B Ut_*rh0Y-@.[VԡC/Ul||gQ^<>ZlOI@U ͎0~Zta 1W)~ G?VC廩1қ‹AoP0T|oow \TDJU8:'fUxr}M1]F9%;"i|YTE'% n|SaMas;ῒ3 ZUge= > =4RR?J,a`8 N"1Ӿ󁫽sna K7Jo5 = XT1 lƲɉs {%W`oX0yހ :AqqWF_ .aEᩃ k{6 64:BrWf'仹pzzEy7\)6#mr7p'm:a5 5ѱ^J}qoq-[sNjeDs&VV~ںn![R^e5e;pտ~#oE{ꐳt3OXp:bRe0GA0S#&MɸA"~/y5{Á2OF+ҀԦ!7ښ-͕ɕDxC/Y=& Ӳh(ReC{#gNypUӏl>G ?FptV(4[ʥ؅ڄށa :pǐH8F>+ڦ}Ծd߇j(r-M lAD|;JFڈ|ӄeEM:Qh;_Y7@ttٛ+ z$m_EMN;SCDiZdxDee& 0 ŠYŁA7ʠN&%O9%"!\{"_=.mZ)R%;%ig!ˊSw6}QO* [A=~8BTP4Rm5`tH2* *reSmjm5/5`{hQcV%$£IugtkG r~S9io~^t\s%Ee\MЄ/&ayS#dnlLF TNT|s- OX0ts[߅}' fz|>7̏ć,eJOٺ\-CcP'}ؠA($#QAuO|"uuۦU1|i E} ;j3^ U"xϹ;@9V엥Lĵ-XCJH:3PQ I~%]]#;J۞F]_-@}l[+_۔)ޥHJBLD%:a-NԖi/d ͺ4 -lS_̆]h^iFrKy8-rgez6gtmp!8釧j{'~SRTLb~{U=D+;vuӠX5ME^_F%X+6:@XV߆S"Cz нà"Ye82Ԩ$xa-ހfeƀ[ j̃7*ΚB^)]FK?6 j̡Y@eSGi_wF|MyH:ZqXpTl幑̛j̖dJoU\D2gWTT:p2}_h\|.@FQpc>7i*3[S?Áz2*#rU]S^1tE(66SX'J 7qͲ' #B:ϪdZ٥ȋ:2m)Uަ-A@1"t/ԅs\.WtCAFփݫgR8_nN|4i|pQkffUkiބ.]"y89󑕡Dh4M5QutY;k&Uе+5vQD-Fa mpp 0y<rL,oƗG-'&e\_ՙVɶv $)>/MAIJ%ՎLIz".AAwf.mw\-a4ǩDlj*>c<φp-J L_+vǦP7#hŌ"gnޮ0F(L)an8|M6.Q^NM>Awђ+c?/@8Mi3L ^: I:".^3a&!Effa^)kOY@lpqdհN {xԉ_DffzeheKgօWsB͛Z}LfƤIo%4d'bV=뇓B=!ԇ3)"|6ڙ/fdR]mx8Qh$fU4rŸz>gm_+gO!]N4dQ92\u@36x^n[2C222@Urm= ce銶ZTB]OzhRćXW> liî]q-Mh.oHx4םQNs&mwR8|s~7<{`]NTlQ4J@.)o^|= Hr8W˯H3&p_¦l†a.~U?@ Q*%pЗcZ'/pg Eߠa}WVibkߺ ?zIlrFnv k`N1wU1b#C€*;^ިNκ|1L 0!oIUxS%LM dz/kooHn _GxUlo6>:%_V$dcX,ʝGGϬz+ZWT|eX'sB}db Lv; wlbߌ2dG{HcjP Uv&Rv'u;6$mYJ8)rՎ#AXIWmEIS1:ؾGe҆{i$N̿>j2>,p{3dГ(Ha,=y3#N+ܶ-UdT.WF&D` )ŽT '/[h D=C>(w}U'$ $yOo^y焍JK7, nCwy8wkBe^'ξ0c>l&vOl`!~)\w+?;.qř&7LptL0 DYs0&$cl+)j8L 0&`L 0&Fo48|Y:`L 0&`L 0&$urL 0&`L 0&F [`L 0&`L @RG+`L 0&`L`ȱeL 0&`L 0& $urL 0&`L 0&F [`L 0&`L @RG+`L 0&`L`ȱeL 0&`L 0& $urL 0&`L 0&F [`L 0&`L @RG+`L 0&`L`ȱeL 0&`L 0& $urL 0&`L 0&F [`L 0&`L $v9>> k&bHO ;_&~zd/p'HM%{.a56%#~`L 0&`N <**,BO"J*g[m'+ٚtK8DfIz~#T L 0&`L 0&| %(>jV ]><4c6fx})1Qx!%LogFdo`L 0&`L c{Pdȫ7c@zoRF:kmL}͓?&`L 0&H<'n-lX0EX.^{T7mG̠牾 KG2ny zU'nfSU?ǤY-`L 0&`L $mo.4S5e`qj%x`ւUzN=A7ϾtghcyO8ymX|0>IЄĔɩH KN]':ߋ.N۾; it/mwŃ]/N¤Iqy 0תix. m8HM k)iqT"cdu@;C a0U~<|nee1{}^'NDZ4q}($+nӄT0&`L 0&0 KojshW)^uks;^=Œ.\ GzU+M3؅-XG^4TW5M0鵔 <%=+CTKVTǖ@`ؿ f@{S"%*3UD 0&`L 0&0 dddT]{ q̈4 iBrLsB{̏0 8sVbŦ.~@@h!Àq> y;g?Ta7Toi.I4vL@Y L5* `L 0&`L`\w&*8:MCd׉ul%(vG{ ) JP@ֶ/G䮶y;vlw;v/ X:O^VPZfs3Ja QB-: vf2A0>ChFW) i2TL 0&`L 0D@{\pj| R oϽA2{ *{>v ~8ʝPs`uftz'r#~Aj7@eKcbv֍\ ^kݦ՚ t' ?BWNhjcöt,覥wyQݕ/Pl%0(|L?D) e%塻ű=MXIPo@-L 0&`L 0O` 8ZJ\?sv~7^+gYA=Ae[=m/%_h~TS|d_0`w#gŊӠ@b'KS2-Yb,nAޛXT Ȃ2 0&`L 0&0AhEZ\;Tk Ю/mC7!J1m&i vuу:Vb}wݏ/0 ^OQi(,ɴZ`3_cΒ1+A[$^=EXi_UAro zaǡ757X͇k:ptUX0;S== ub|_C%F$Ug upߢe/K~L2Օ`L 0&`c(ޅSqxQ=ޏ! 3#N0Uk 73_GPQy~CB $E~74 dqrL@穨h2M&N `L 0&`L@@ h'߳ܛg"#3[M?'3X$ʃІk#͘ӳig1jN=uo޺Zz%fu oP _))Hx/,xCP]~zՐ4$`L 0&LhG2 rE DvL^3Q*e_;ߌN_Mr.V~-Sp)yԧ!--'47㩧[7  G& 7ޤ5h> "@t=7TW?dL 0&`L }nP Q"nX؎{p`w7ph5{٢gzO)tTc>] :YA 6Uڎsk4샛 x]hb]]6n(=Y!E;E;3Bd$A n5AXp7bOf,Qd"`L 0&WxP Ltdݍ]eAXQuVL/i\\iuU߰IPV-Knhuh˜ʋa(ҥKi pۼ$&?vX6_ULX.DuTmŒQL{O*̝1A UhN6Tc]uCH|}4T-L 0&`L 0C` hf"&4,ѐiFʶ,Wym(wSei6;}!"&ܔ4B<6PP?[.PP~ A.̯b'e8uhDT#o`L 0&`>Æk&v@e&&-Чeaþh2vfC͎.t5.+݋d35sfa%NuBY U{qzgo+D] r7Ydu,^=oCjQXÔ68VZL B2Q ;i'C8\-jw9de~>0}#C y6k:֡x=nxE)HvXP{O|p ⽿}/^¥K ?53 7ɂ%kgOcr'M:7Afuyᦽ|D4X,@0=!)B) (|\X,h}ASE8`L 0&`cmn_Ńc3XvBq%8pj'=MԣBdߎm1*?`L 0&Hr @oIV#Ŭ([{4/^oj lR8L 0&`L DCv@FuSFF,&`L 0&`L  &22Qhn1糭] zV`L 0&`L&{ \? 7Oqi3 yf.c;Y{&`L 0&F)0tYH˰"Xq&`L 0&$er`L 0&`L D& &`L 0&` h@1&`L 0&L"3bL 0&`L 0&%ʁbL 0&`L 0& Df6`L 0&`L K<(0.`L 0&`L 2̈m0&`L 0&xP`\F+ 0&`L 0&@d<(`L 0&`L 0&0. V`L 0&`L 0xP 2#`L 0&`L`\Aq(&`L 0&` D6`XO(n&/%0&`L 0Q#ږFͷEU38 (*u"@IDATEEmJeu񗅗eܵK[q-oۮRݕJ [,C)4PIG!!e̟s̜០ |}M9wg=:@7(.^U7 P(@ @'IֵZ@+ )@ P:@oYߴ]ucPY^:2 %ʍ(@ P, 1֟B ((.\C]Y(@ P@'mˠ@'i (@ P(@+!:@7Hm| P(@,_fk׶1(Ю,hSLxS`x(@ P(wG P(@ P!(@ P(@ tu)0clVX,tBCaX` PZTRVQikΔH؞8~ 6C0x@[m8q8?:ݿ58zꬸ#@| O՜80]UB|e'I(@ @ecC@/M^f(%ex+Z&.Osevu007LZghݍp{K \g4fv¯oU^_`د05dz6 P(8} 6k CRXouāՌp\rzז'ί6x+w­Nبmrўm9rT@3E]`. P(@}wQ 4)`K|@&دPg/^9"`LLQa!6w?n1z:o;mCML8O(sSnV4;,N3=}f)(@ P 0(6 B0-ޥwoG ۏ_@-.u~SCV:`hdlY?>[Y˶%-={]CS PJ7eSRlr9Vf|-`EaKz‚cubV|3!4 *҅QqTA1-ͯtw>}'%ynvn{gRqjjS4 =XN0 =ѭ8|Gq&G5 _:/=HԠNi0пsW9j8l8b4Q\6W9V DjgחkFb4fӞDpc M@pbo[{_{Ί'8> @ač(@ P ^@,x0 6 ^%1fc!AQbɒ1lFԷKF Ę1=- ӧO1:6S2W, ^[=ޙX@c%5ya<_V_VAc>OFr8'lP!0{_Si2w0@ >III3#xӓE͗ŪJڣ3?ĺ9 ^)l'S8pRR0yr RRA#l&͟o߾7)0ďSS:}{L=_ZV> c3Qddd?F M3_=o<,{w_|aʈ0q?ðjܣ(@ P%/@ (΋ήͻ*6/oC@&v$w!F.yM@>L[L/XEP8نg_ĸ_a7ޚ5eI4S=o"p=X OpU" %Ajnר]A>4O=aa@>\Gu[xLYq_OKcr|V^g"iny]4Ò*t?o(@ P`Pu^LM0޽]a#mV[FIS;Z)Wb{Y%-[)J|L'F%(;u֙Ѹo,gݝG:v8ȯgUGƋbC`<\_VzggkGG k-0865bǿylԊaejB<ЍuZK-o/wZ1xp\/9kc08*J\Z:(@ P.@혓 6+ ]5[\痸xiV7|,_nF A\\ T:N%c^ҕsoGCp/E~|?;'FٛJ? _\xc*"gm+Tނ+:;L?2Dq^+kXl>'d zGMFd<0F?2qd¾#N=1S P(@!Ђ(Ylg+1{6}fK| bSV;/)Av~p9oawoߘ0j?*o(!b"S_>t{&oFǡdW%k"J_zI':h/dh4 ɱ`'j~}nrJ }P(@ \y9{Pf\7oǯĴ9g|GpSfcxo0އz^`gϑl؍?9BSlgߢgOYO; eŽ] .Es^j<^ *5jayHgI /E[w;W$/> 7A7`v^#PPI=c Xv\m:uؑK P(Ё t .mʌx}}wz3^%%mKE]92` -ݪ ͟=6Dk7ģtxzUu;tTjr}9OHij  t^i#/׌R8*u7yn{blb2 rQYx7ԘxP$*o? S W+ʵ$[~v p:ſ(@ P`PfAO]Ks(mx%K9;{2sRoV]1wXr])W㧑ڱx?= Ɏ;Uȕ!7u|Ė>_2{fD`˷gf*7hA*o2~œ)%OΣcpAlLX AI H\(*;! u53֓rYQ9k᳃GQSSF !^7kj 7c {P'h}.~6Bq˳0P&[_{P#(@ P.Y)%sz6W_WF:%C>2LƸGU(ە'|X Qɕ9U&/0fB3(Z4ḱ[[3[։v3ԋ?r¯c?W O5M??~YM%?îOOp5A{F Ee{$ 6c'#9SB9)v\_o(T9065[]x%)2hޟP7;b0"Q5 jAyPy˯^QŸ}EycS pXoȝp: ~5v-P(@ LA91Z&`0`r 9[I7(X6U3v7cBS(^%֕ӳr)⃽h_hTS?LS[do!#ݞjhEǽE%g+wY3C U15cHrv%~4) ߯/=8M8S&T)^NSKYލ coq?7|V{-v4cv;$DmAs]^XSEGW46[(@ Py"IX,9'g PrجE'`TG 3U)MоAccW rysW]uW;` Hy1`Ei̾ QMm:ԝu< yj^u &Gӻ5GH~6|_%ڃ/Acyبw[7E}ɀ(m(UN(_1`x,n jq뉃عkjϋ_`~6ԜYQ1ЄC`!_tc5(@ PhZɓ~)݅2'={c-iٕs)mS81І^b 7K6JO׷G 9؇]պCpG݈diϭ(-dnmA϶ =7݀U!Q(@ &ˢ:&v“|g^N~В 0x 3cO:_΢J?í[R P(@ P2pe@fۓ9x'q1S,'K<ÞGgp NbG*% OjN_(s(@ P`Prj. P4hIhŠ]ET[[r#=ꮟWiS gȽW|tMEÕ)@ PSAol5( 8ua~ծߊ\ICbBAimۊ?فSr=6n!|B,0q[EĿ(@ P~ l"(Y c#МĎ¯hboǀ kǫ{E;Ϣ(@ P@B6")@ P(@ P x(@ P(@ P[0(-n3/(@ P(-5Mx'O5: P(@ P@ p(@ P(@n*)>SfpUy](@ Ph'=zSI_ yK#aweh P(@ t%POˌ~R6(@ P][k t˫(@ P(@ `P' OP.pҋ` (@ P]m?.6]S`z".](@ P@ѣmƠ@W& t"'Nt? P(@ ȇ\OĞm]&?JͅfGX )GL/폗6S(@ t[sX;kpR\K tcUjp P(@ tt*(@ P(@ pF P(@ P@0(,(@ P( ]b)@ P(@ P @H P(@ P?0(wm(@ P(@ t")@ P(@ P ?%(@ P( t*(@ P(@ pF P(@ P@0(,(@ P( ]b)@ P(@ P @H P(@ P?lcwR] `0Bj ~kmi!&:\@Q_Tmm/zG(. %†a6\˜(@ PD 7/- 6i#k\6*g}I~ $/, p=9_0"S[ۻubcO'̧s3ƫ/z&w P(@ P))w&zr6%N(9B@Y 2l2dYLYCō(@ P`P@߅G;@]gm9e@0J Z)˖uȺQ(@ P ((Ёr 9eR-˖uȺQ(@ Pe}VKvV,~M4FDe mb>*iO}1dHD`e Kt7* a G􈑈 ׯ(`Vo7_Gb1_D4nkk_%*>Ar H)&k\&/^d9=z@^A/79@ō(@ P'Q ͂9O"cI$"+/&霫ǶUY0\uL mCKF<ӳ,ąjbd{0?#L$pbWXGѲb|WBoJjd\ylˊ+| |,*`?1'g68-F P(@ 0(£]EZC`QZt ֤'h͜ T&حؼc:t% %XM`/|P7ȝ飇c)D(֋E WC\:諷yslD,gR\[[P@Hf(@ P(6-6l6 Օ{ E)0srjbnaw-Ʋ9J>fC[F@^VjK5Zc>&{jEo2o2TVes4 `U5=;Ovb5;~='g*tM& {vz4(@ P(@ 0(r+3kE!Ҕt` AXO,_: oe( /ExDf,.p˧XQj5%[>xvbƒ5e@rӢ$wIu捋 "ab#pdPۉ&#4<5-MU{_rX|c މ;H059Vy)FP(@ PŶJJk0V;vpu9[P!7 eJ4:\)ǵ3*\`k]xiv:3UX1\;;6pF=1m@6'9zhb e(@ P#)G7MmKe>ǧ7h W(A*S)+0H._vӵ+Uz\] !>v/*Ơ ?ָٹT)*s: Љ{#\DT p̿ia {k`w-Ĉ 1 2(@ PWF@qeZ)о6:hΙq=QMTN4--<ೈ< G.z_>/\q1:u~t\0"A,(@ P 4ы &~ʝbZڙlzXqT}Wǐ.mΧć˧O=JaQJmc+ܣdSe@1`9>A>z|z_ٖ?pMLg6Q (@ PZ,[LńE@؂-7W,|²S0S~oXR69%m~qrc*ࡣ7 c>ZU S̴6V۾ , ?zEYG?&` с4mQoGQj_:SL0ڧ Nw#xhs?ն࿟IӬ&&xnSf ˕z3^%%}\tkwk%Xګ]y9ŒRǢc6o<|w  7G\(z=( w(@ P(jNh53\QL<2{޼SǭЅH]ViɴkK:9 ySed#gDZvǘP͜LWڿ(Z4mԃ>fh Pe[|<*$c(gwHg{e Aޗ8r%^'xn`P?# Q܄P:a>ݛ *2ܡ(@ P@8}mnu F-6jr3E&@1R=zJA^g+G;HZS-+~K-EX.1`BS(^5gc{RĻ]] s C4<֧݋esZjϚVq3`!c~%69 P3OU566drm@F P(@ ]@\mb}Kʌ=T#(Ѐ~wo ?7,,KuLMHƁYPg;s[-0ۏ3Ag<3ƫu-" :O"H}[Z`-l~UR(@ PNW o@hdRA 1#d?rcذ<]~[vZb}=Yw =p5n@,i46К} B.(@ P(/ vk?7K9LT̿uj&||;-6%"0 79 wy5dP`(@ P(/ vs#H[|@I>r4;OBx7jar t䴁" F P(@ 0(£^ Zʴrl݊nb1cw֘Nh$y {N4`^`@T.(m 4XM F P(@ 0(£ G` G \-Vfwu:aD@/[c5#GȀCō(@ P`P@߅G)@NJJ6^8m;j%M䢂r 9e@`@%rLC P(НwN+,`10ʆÍ(@ Pb/n(@ P(@ tG]5S(@ PP(@ P)@ P(@ P 7@ P(@ P t˦(@ P(@ 0((@ P(@ P 0(Mo/\lIo"<2(@ P.@TФݢu;07ab}W[-J a>"MW;% U(C|1Ofwq'b"Q]>cu ꀐbʔ,X8*xF%yص 4ƾÐhR}N%|.&  V{Pܐx7~zo•_ jԝڏ} zM$n(@ P:@іiXr:QZ[MLEQrĻ ͘BEX0-#P%ML-^wm/3ՋEo.@|] SM@Dl,@KWaXB%qxBőUSP͍tEHD.$PXK`Jd^x^V̋XyQ"QwnxvR+)R(@ Peԛ=ش}Rpj#/L@@͙\&"w戦2uI&.Af]z{\@7yDvXf0bk7D< DVj8D`scV%(D@&7Vlr7 P(@ P]$(`?7[cnEr94^sUx=9S{D~J羺ud ˈ^X,+f{9suxt]#bJަ v$"=5Q' lVLm)HK"R0(D}5aNI(@ P(@ tw? V/Y"( ̟(!'6{O <`T Z ݰ!8<䡢 J?ÅOp?(6|%$nQC{P'<ާ} P(@ PG7\Rp_m6/§cZt0Fݖ((y xUq\x>6"ai$ "x>6|{OTfË\ĥQUJƗ,3J3i`-,tUJʞهClvj|mb[U&ŒCu6^DDtvMRR(@ Pe}8^8p@BYJk69~wvsErL;F{h,Q͖rZm6 ҥμþMN䒻H?EjXeq=Ҥ"}BXc桨f;Un/u8Eݪ)'aBr2gB2 w)@ P(@ \QgW=܂GbDŽ{= R4QE V ws31N|˅L-'2~D<GSiE͹=^{ҹHӵH "q>0q/{[ ,-Dz#d%ịz h3r(@ Pߎݭ/gZ?6<|sZ'I5ujr*bkB%}gYџ1Dol[̈́I,SpO[y1x nr;~.djQ(@ P۠/=O=6c8徣DI(*+זtπү' ƤE(ZW6 .vWaF{NTC|nqXZtW#8nv[j_3{\R[9A34b@@`v={ e:1"sCocG(@ P(pYzd7xbɹ5w,(Іe4"CMMjAR: GE0:.a+yD^:x8n!U `F]YBBx󙚾V׊5'BMtC^uaE ؎`CmVY!͖]oFM&3!<"]Ԋkk3Bjjւ (@ P(@ Pt]lױ(>lj#jA!b=ŧOo깋=p'B{_DBDڐ0[dKJDQe⛊ XK176[D_z /_ ~.nY*vY(@ P(@2ʎ@|r7^ 0OUʠ' ?r_dVyeK)({M&uMrFebtD`,3<37׬)@ P(@+/%F < 䧥=P !{o{=1-c\hi1L$b!`qԟϋū(@ P@hEs^C9~4x~)YFY]C̃?Q t;Vuve t[Q(@ tc AL7 ːe bԳpS.Ǟe8 X_Gqc}VKvV `D5)3lf`Έ}1dHDʟ 6Ca @IDATlu,@PNTC}يݨ8Z+/ƠpDf\z+ʿ7EbqHSSRl/\l#;fD(]dMjn[j+}q1nE|tWVKX2  `쯨) ܼi=a1gK~m?_/Y P(@ P&tݪi9e@v+ j60^Y# iϷjj_kr$2sKtgf0,ؘ$28/tϜl̛~oض* SzL_SP\RT7s6¹GRE0+>ԭ^KF<\ӳ,ĹgqO(/L-S&O]Y!9(Z[L_S +}GbGb&{-B+>^P)3Q]]Y-(-Zދ WU»*-y6,+FPa!>ӟx !VKu(;PhrVo7p*a#uÿ(@ P@=v1ՂM \pڏ\See]˽KʬA|KeF\,LUW~lZ+ tSL`1O,jJa^;Ux+C)Yby(#0cq[^f(%dog!&C_ADL(w&L#~Oek0c|}},^V Npv2$䚡רKn (@ PI]F:M:2 e h|=1yΕYJm_jN^/b79U8m<;yhق ёkj7Wx|bvcR+i~D|4+x~{lF 4GLy+r2myWhʼnOy悛Mb6&nB廣#C\$E eYS=Vtsz\]!>v/*Wwc =֒ &$L{Zr.TW@wqD|~%ԿZG$@ wmVN.ڒ'R+ yPic@hԤ.(@ P(Y}Ҧ&^0 w5ŋq_B`zp^}ux%0ܲNY|ASL*,\ERdH٫q\l<tdprow⚾@W3ix^$ǹD0|q;Nd&={R:}oXx(_W+hLiltP/ U^Ĥxh0U-\C5_A҄ >mˣT܂ϕ߃zTٰo|[G t?-;snc>f(@ P:\-O!ؒ4rd~S+Y2vT+ߵ;]H^v6fl1$%%bzrhVm7o@tw^ES lugc,g҂&vS,<"EobaN!M/NToƫi~ ?Z,-zjd*QtW’X[9cwDMyݡpY QoLCE8mK9VsS(@ P&wAFсoCvˏ|0"msAY[D/@%b[(ݱ2g"vB.7OMu+#2Qdςfb8@ʽc8 |ϑԻ{-Ek+Q>HN;X9<MD #m@ M\CIN`cGy1cg1j#0,\qKU\c=*vmaW㧑n8 2ahLRCQy5V+,Ufl.FMU;ru/U9))?96֞>L%3oohK(d=fԋj_wƘ /rk#{ Ds( &df(@ PQ(K4FZ,uq;VECo+[veLiMV՜7N'mB}܂zSa&[UaU;yabqĂt{9,1>q)Bd(tv`V+E|ӗ4'"tuUYaJ+G"̒~΍Elx \lCql45wىYF +={! ygD4 dldY^Yز# 'n!8KBQJ@M[pYʐyX%"{dNC$fĖYpk<[L,G݈Hsbog~h@HTzy h21`Y$'x"l߿;b̹ " PZ`޷gxgW!:ZD0W(/݋jaVx)^{[B F@"G}aC":{Ԗ ^0.B}?7K9VHBNb*:5_\jCeS~>*< w(@ P(@ P3 eP aEuM>7z71Q\#\n(p^GJ򑣉m8o\wg/^;(@ P(@ˠ}'j _ʴ- tM䴁jץ==15L+G֭|a8&cpusǝ5&珃WM P(@ 4bZlhh60К  c!C P(@ P{|c\#q!;~{tݵY(@ P(@ P+ HuA,x5bI=KʨW.*(S(tx(@ P䝐}/bwFXm= Dp(!Gȷ Ez2e~ P(@ P!%d'~qڈs"8 &H @&rT= ^*"4_;2?)@ P(@ P;tfN} 8CJO=qL/XQF btuaE ' p(@ P(@ t.p: t/(@ P(@ P@+o^)@ P(@ Phbj P(@ P@`PJ^(@ P(@ Pu ΋)@ P(@ P]FA.s+y!(@ P(@ 0(:/(@ P(@ ṱP(@ PZ'@뼘(@ P(e2B P(@ Phbj P(@ P |TECOЁ IyA  x ;\q]x8' DpM`[1! A::I:VIȋt/>MשSoS׿E  "@ D"@3w}b߾} hjjlFss3Z[[xxxC`` |||zq0sChoyhxt"hm1{ 04^j=\0Z<L^m5 o.ם;칫Ƌf6^xx"8grq, Eg=d͆V6:A 7ʄ"@P `4p0zyabD[[ZZZAAmm-z=xA@i)QW=-Kmvne9c%+qW!]Xz9KB _dM:zv-ȟs{zIKclrqX& =6\(ƢK>;Jh@vUe<"@  pM>:t(F% g?O?I. i0ի FvcS!)t;4lLlM+ o1VO)hm_Bjıw~pXhvurCQYQ:>'?dOƟv0=]qQg-0 F؈p{cf? D"V(TWW .  + xϓ;^?kעG8ƥj=ȶIi: yBnm?{:SB;ecs6[ 'pKx:Xz J{)>!6a+;(e~帅,|A(N66K~\uFहxj#O3}lLj[;q."c'bPWNk?}scbv;\H ј^HEQl r?WT rsG~>_Ini+KyWOgC9mWCW!%"ll}N41|Dp|*~(1J3C|X-29qx4q쳏q֤Ea~܊kyQ|Ew`o'HaҢ03ަs ~l5(ƅ23\œ+W;a_ʳ m!L]sLװ|+T)|b]mcVv=;FeMD"@pKMшaÆ 'vzhcJeyJ*24@x"jB&ݯ3ע9$6 KǴD Eʢt,HgHy?جK9x7Q"}jr@ BS1YcZfۂ务Mè`WMdc =}i`U `zBA(mL{Ce?3$ _c\1S%_>(OZay΀L$-Hc3uv|y;w~`R Keq V"4L2Z>7p%qOQ:գ'm"<0qtK_`{ 3 D! J$1*lLأ. E NIxj0>qOųT}UjR8H9SR>U;&u9B@ظx4J)qJ qQF'07_ vD/Xi@g`m<bۄaue+ D7$vBnKl.Q xYL^rvңXt),XKRc\DIRZ; Gx NVX ^X `I#7Iނ\|NSҧB;u.Unh褰z408'HZ4N3;sOCWKeB=7E"!"v21Ʃ<7:9A, &o7ur 4 S'MŤ' VR- FqYաƨD"@@?$Й~Um??? {[(WxYL^v_ x~7c϶bxUq1E[1egUYfEWYrۂx1> CW pj[Ws`Kh5&-5z G82n޶y0bzv?w(c&_yqqc~+!dr}']_„h,78C|raiBفAN{;*؂LeLV 'rj#Nf_ r3۶p6EeT;cj*rV sjzBJ? q tScCh_ EZ (=j09y0Õ1t=xɿ2v˯Apf8 cSl\&C[&߽#>ܙ zT5ݺ:T%OcT*H D>#vB <ߛG,^&//רX4e{Gms'$̘=>ߊFbT//Yt_} a@5 Fҽ30{Z`)Yɪ'ܾz;;1U]'9(USbK|Fpwwf%j.}fXҶO|,$q\ )ɈeJ ͠ Kg{e1w%t#Q8sKȌReBMK1Ӟbo{qL`3B"PgB9y ?^hTYpٖ%3>lPtZj4~w7]jʯKٌ&=\ƩNί*BV%6ĝ c\ "@ msj {X/B6/lfc%11a޲b#WZs( Esr63t|*,;cF a2O).Gr.ۡn,jˉO_L RqvcRwwrڞ53E,Sw\鈻IH1 anik]Z2AdfasGL?atG)ؤ.UhD:S[%&̽mx}6 _@g.^xJ1m"":c dA<`Yލ?1vY m^X& mնlmmU4I65QUU8W,k8)OSϟyLkN.3p"|,kP^ZA/"^JمMOMw/<ҟΦʯQT<}g=A\ڛtݖ,:\f}Po}qʧl(RdϾgf/Q&[^tV;6⢅f\+agPxƂPLЅX҈+M MRkݍ'spD1d?x䥬͆2TpdhfR)"9xҝ_z _ʮd4Մ5-,#3*bN0j_D3sj3\VÄl3@ƗnC'Rޢ?%Eb}& :2?ۂ5 &Lmgzu`_Y|yª;ѧ1Z:w/>jZfrjG8GՕc'+H{ޣTeG([m <cz=Qy?6'%OrkQ(YzұmƏ'qL:ҋ [?ƞ`moR{⵫ƕ inB_w۶uxaѪ5w!<5TMVYK}jBLwxSr#}Zd1=Ѡ'WUqKqcC"@Cn}/ =>wcA\8{lx!"1֦ j4ak-)${X_\{jʕ=/]=M50I/90}a4*\CG]ׂF5Չ* bt=wxr;Α:RQ];)y/x|G˧$抲Zq aHNP<_x`kvSꋽBHKm,RL̙31M{"E=[ZMv?ՠz'[X8w+}'O$:c;X&^«CvLM+c"Rg1eaLl1 X"Jۮ:~{cisosIJĨqJ=ÔiG-D8'ّQrG D5}]X, n z(/Y:!U߆ǏWWY@>9vpD'{L\(mhQ_}͖'f!I1/WiLԾϰ`qsq)gUԚZf#A"K9硘$?yLb>h4v&j"mcPZV Ċ-WRT'R]cV08c|u4zf482d@!QӵSd^X?N6Ҿ 8[3lMBl̕ONNDsB[-"4JeǿĞ={ϮcjB.U=}Cg٩&_7 EL1w}eq#X -b&C161lb5j YĎ)8L5gɈҳ{qM;q󪽋wyrxW nþSlw- T_S.qaCC'Zl c)]Ƣ϶lGB>j,/6FK7QF?je6l3#v)kɧWʊ*J6q)gpOZ|M;]?{?B{.9A8Z+m(h{e\£&BMc;2%?]_ܦ8we9c$+:nSրv0/=C:n >rBmyij8N^\Շ‰ D~M4~>Ym'˼S[ xfb$iZkhHQ _\y+ޱhX/r~}%H sJc܃KX)#OeX4)>]U?c1~93Ja^<0.k;J$lMiZB^ 3> hr3|J*yh &x^ah1&q;v,Z0oynGham|:\IY:zSmv-w;b'gv̥ 6qӖEbr̔Jϻ?f{oB=1SBevȜh9{EL#xJ}pF;kLs*8~6V-bd·v܃H>M2BgRvyb=LNa> cgS)} ڨ.QLj c|DjzGKVO,"qŽ7(6y Dw"3Vمh0sFf5JPWsSYY ___(lfB5/𲉛PõUXT˩e^Wc?1ҨlVdab^z!1JrHm5բ:Y^>A0"&: s(k>Aٵrmˆm#YVjmaj c_/]w3f>F` $V` zu&oa%Wuu;3`mnn\v[5y'.A:;ޟ<}Ξ"[q᫏%0[)9t wxM0 q񠓥tu'nzcDDXcPƩN6F%ZW0sm%1Lc3GMl{S vcJXPJD"@@Oh4N(܌ׯ#%-|42#{9rk tQ~}H۶ o?~jÄ֟HŪ'zh>l+N(0n1bݗ)u{W̗WݷUTs"@ D&n} L&0a{ϛ xY;>u<SE3>lSevK.yrؘNeAD 5Llɞ@H Ѓ)+"@ D 4Fd7jY`` nܸ!cD-n԰#Fp#BT՞! Pl3x5w7?0dY e1jt8"9"pwrm\?K D"@ o`O;)̓e|f>aoܫW^uc?( >LŭQMxjb'.kwND"@ D@$AAAƭ["Jϛ5 sm]*</Rc,3kz/fvbDď m}#Ţ!In"c:&&heLQm|0Q D"@Aea΅| Q_)Æ  t6G D"@ DpCn)s :Tv@IDATawbg D5PÓLu$D"@ D.pkoq@@ yno%4D;5(EhTo N D"@ D dn/5<==U]&W8\ts!?vP4*mJZ'D"@ DH`@xIɂ*85nGB;PU'B58WwT* D"@  zTTTDOfA@؈raȑVv5 cgo{/"۹ޅ yxf|ݹw]_g0{SxP^L*G"@ D"? dBqq0Bd C >?τ^駟&|ZZZ޼yOFdd$ㅴ]29i/:5#h>TpJ D"@ #0 UUUr加 "l ===*hmmbC4 *++q9=ZP^~1%^dW#*"@ D"@z VAԨQ>|p-! j 츶1|Kb `o)B`:mͧ"@ D"@: p BĀ e@Mء\0\ D ~Z|;B_;K] rAymPw`(OArQ^xՍ,H h.ln`鸡@, 0pv̈"`2~,B 1u:fԐV.\-c )6ZPUp'b%Պ\\ 6`hIJF-]WɘlF4!{ژs Ttuu,յ̌o>ev{fA@fS{WR8 D"@ nITn (ak V@{ \ xz~nwM2£8u6a˿jG.$IyvL 2?X=vKi!ZڹmXZ^:oH{=L|7`٪en"@ D"@? ܸq|@tt$% x,^fo=jose`swa`^[p=R@<|ז"Ҷ3Pɋ_MˮESYzv:\xbSlS5 K9\|f Evv6q"y D"@p#n'hh`F*x@Oj8ϛ)e2yٽ=1spJLw ݾ×ٽ[s4r /<[vwӷ3;> K:WQc]A1"MXPo⽒#o䉮hm+n}?̆1ؕv;t(ꐫeć.f-|$DYy+4q tA7 D"@ JFx?mQMM ?tۆ@GMm xlnk7#.bZj}әG R"!J}NJ O=URWޅQPUjP;RL扙E¼l;M]CڂG]zzNL]u L`g!c3o-Abqc8c>tM D"@p|wר`g{N 21r~lۂ+t҄ 1x w`i6֢b(TW7kŏY) Vbk sgHOE%LP:h"@ D"@z)d҃ݾ}['}%prѓSY֙3{rD/shfwYpͼ:YJ7s7yϯ#[rR랝ˌ|>8iNQHaD"@ D  t>Q^vWܑOʧ}߱)7WΆښ2fm/M2ڄmg w׼{҂AK'uTVWy&lҿ|R\ R@v|S%D {Տv]UG1 [cUUe,Qlyqn"@ D"Я nK(N{X/饾:g$'l6^HqNdZݏM>]e>XUttb깗(!O]c#PB0~uV?Le˅~}x}M*;,¦m*&=.30qqҙug ۲Ob:[?;u XJp^!)x^QQݚeI X[qOY;ف9;v! I_ CG7q =/( C?6}X׺\M &&3 ^/:wOa@^UKZy{6˧Cރ ̨3Աgۀ=dO`ˠqS D"@9n)'/_Å-U{1p=l1l:DdH>~w/\ `YgpBD"@ D`pKСC@UUzO [ z$.^fu:g64Xi;DĺOrZ W,)*{ˈT\"@ DA-k~'#G`w(ClllƧR;GNZYo3gMAxvwN􍞃m[G-3 -4lSR] D"@@Op ŵk0fa5k tGc@!pM!::bO D"@ D J|'|WwbgF QCk!DEE@@ D"@ D"007:ȷ \~MMM`B~q[nToG[HC`@="@ D"@P!B&1 'O 6$`@&I8vkp̆ . "D"@ D"0p >y. l p- Go> ~}<9"@ D"@ =D`D|R?ܾhĭ[ , .D\`r?9"@ D"@  'j &D"@ D"@(%D"@ D"@ Φ"@ D"@P  D"@ D "$DMM%D"@ D"$@B% "@ D"@DH(0:J D"@ D@IJ'D"@ D"0P`u65"@ D"@ 4O D"@ D` lj* D"@ D% (i"@ D"@ "@BAT"@ D"@ J "@ѣG]O?G7 D"@@w g?s̙3]7H(0{D܄`pݤ[D"@[Ph@Yj D"@ D@6 O ,,  D"@ . `}L-$D"@ D"JX("@ D"@'@BB"@ D"@ Ȧ*  Xu0ԙKߐpw\|[Peh $1(X@[ m;C|1S}low:Bk"@ D"JflX(P$gp9CbxK;/a,n!dm?j&8h sZyljCI'kҞxsVx_^.N;gbN Ϯ#pE*E=tftZ|+#DLxNV YJjk1oUOo,k _ڈiZ17[4B Bl|i5~}.13⭴.<&Ogy(S?~a[%Z S>@2!ꐱrkcO.L5Ur{{%߼.<͑=%f z\o1n_^MK,aF/X̸1&/}qf+Ƿ?D%/Pįd'u;@m/3f;x@ y|@'U] |:Pd<)8?]Dsr~A"@ nMn}Syh;ِ-oE0 0T";my-GV8{yb;b\VU"mX(@Sn;gᚷ'</*| mE6PZ^QٌͅYX/gQbBPT "gMp2EgbAmpg3.Ö}XྕoG.˳O SbHU@A^>?Lyd%5}ftNW6Acg[ }Syxo്둾dtpN^wJw;>bXbyrGIl %j"Y~ ׿"5vw҄V˦}M]_݈xW/g5y˳lC@{vŒ7׿z 39U[P% my}xav+5+V6kM_e^ħoŋّwqN:X~|R"}L^v%&GaM_v*[o`#mОM+R ,%yҷõ3[ ׶ceSЇ R@$hFֻJڑdYA.n а Cؿnëɂ\"+$!EW՞9y郈5b)AOKסQ|W=0uR,]+^H& ۝S-v~.Z=J"z=+wzώeÎ{qq":6/e[Qӳ;f?i>&= }"<)"@ Ͻ6LjaU:_ 3;'ۂKz30\̌WnWû~<^q?k8%*sB p' 2D.m>-7?QmŁ?}T>(N 6D>_hο.ӨZLiH\g֎ mN<017*p^H3Q#x"`q)L6Pxޡ+d-Ut C_Z+3б0`E ~RD@\iQ:|39,p0. D" Pwݮg}X'UQ%]+=Uxub2J+f#l{XLǧY/2Wau,x(Tm%`[u,P4%:ѓvf|es yxw8r$KAP c c̍g{HmC參%J'1{3p‰o7;O*0/:,mmVD J3,DCPDI;% _۾EIIQHZ2643Ø+1`R^̖<:f=UV,0F*cC V5`b;ck)4 6ԕ`>Vvu̓.ԌBT sy(KS)DyʬBM_8VMRg`}FP'%ِ(yR|0/g#-~IEbݟ#+{lcKyg"&ɓQRӎ/\ck//7Rmʉ>k[ѣ7xgaՂâ_/B}300lBqLL~")qsϩ-hkkFMyx[r$m1'B4REz7/NR sU9sqS0O! ۿ TDoO] G|8g]׭+iV_2&5 2|6r{:RQ2q0e<>cjs'TNvU*wz?n,cFl/vGʎ]_Q]A2? \tzs.u |ޑx$}Id}ܼ%vdQI+,:Ý1ߊ̒|A{%ے{Ĝ%mZp>kp.?K W#},Q?ӞZwY?$ ]KVcA "@ Duݨ'^>y[޿ZY~x-i 6~^Lh/-ۄCfodg}ࢮWB ,4@i2"&bM Fk?rCWڏn]kwPwsZnrMҢzLKQ#EjLYqϿ "?>99}y9yskJ4KVh rեクftޏv?!{"K~PY_OlZ!F/Pr6?$vyNhI,@(ygfhSrVUHPOvg< (z̅&ॼYm l' ;%L0i=i+ <$eXX[d>Ҷ_s;*1d* +>- 짱wf&FQD!mV.2CJW+Đci}|5JQ/~`} EuoT3 ={>^^'*m W%BWi Ѵ)sX伔1{>H)ń\RfL 0&@F7f|&9DךPc:np 6,:2PP]:3?_g0Ƅ;%V sa_&`L 0&`}+|s`L 0&`L `>eL 0&`L 0& RW1 0&`L 0&>V sa_&`L 0&`}+|s`L 0&`L `>eL 0&`L 0& r.]dL 0&`}苵ebL 0&`L 0&^)$@ؿQן=`L 0&7vm'dROT# z&Lpes`L 0&>*`L 0&`LUpcL 0&`L 0&RU( 0&`L 0&X)*)`L 0&`L`@P.`L 0&`L 0W RUR 0&`L 0&@#J>V\&`L 0&``8`L 0&`L 0>F}B8L 0&`L 0&\%JWIq8&`L 0&`}+Xrq`L 0&`L JpL 0&`L 0&V 0&`L 0&p+\%`L 0&`L 1caL 0&`L 0&*V J1&`L 0&cX)*`L 0&`LUpcL 0&`L 0&RU( 0&`L 0&X)*)`L 0&`L`@P.`L 0&`L 0W 1&I`NN&`L 0&]vS&Lp/>`@_U.E:[Z8L 0&l⪯B}f\L 0&`L 0& +ďy D8&`L 0&K<޼R1 0&`L 0&.V baO&`L 0&`}+~s `L 0&`L `XسWhkD]c?DFGkAٌ6K׹ Dx3im05B!x~! q&*\fM^ãbJ}5XG{ ZuX.ՂVH/8tAڛpt?71qC죲 FߍoASNpre;HK+ZN贆l; ?9}];"7-uRF7us罿 AG#Sgsu+{])2n ֖&X BpͫMaD d=e2AZ\(1eYlZdu2lnį'CDud,\ .q 9*Ǹ5%>] ?q6.݅IzD?1fJ و$Wk΋uE&XQN1h,Ɵ+(2/Ab$wm /8Z.|ڰ,.Acxm]kEθAJs? D"o H ӳk?߂7wջQ_.*Ӧ>цH`G$9/a  0زj-[;1n+ M QX77)'JYѮ22MsM)9pa-.n-[ڔw Ҙtk1ER4 !xܸ3<(^Y+FCsw?7f|)w8*PTO^! p5 zl}ºbCscB *u6X!`8V! __^a/Bdngݏsf(_8=;~j-gHi-FqS5U5ˑ2`.(f `AޗF@bRu'-'t^>߻I:(pOTz/ˆW^w!`nqv2"ulA#F[VcE ܯ ?oNY;[k+sojWʶW^2;}yq =~'4[202r #/y ?>av8Ǥe/@54Prf9NgEL`:OC!v8b"*PA/0N Zpch!pjgg!#&OG7UOV6u|vUfL7( >ɿW.|WY4_E"f zv2p(̼+',~ fGei1̷n; O1h+x"o ǫVC>/vXzR!ØA܂}6מåv Hs8$/ٙ]"/k1~OƥM#՞a;ٽj uiMlii ɗMpx%6a'e:[ʻIML:!"T:>h|Lӊ,,. ~!' g0qAn$ӞB?J4O`u2Q.\6I]m ŢTW"xA!`\/Ag6;9t6i?<| OaDݭ(M,HѪa36 łBkkK sMM^z& ٩\*&QU1jFAk PK3~Ř Wi]&PY5GF¢_]gURm5>BM!9O<68T7GLTEHH>9yηQ,Wcǧ'\Ԏ#ﮀbQQ#G^3&HUiL&h3} RL2M4iOa1g݃ESs rBKĂcq܈;@&~ CvfH6KFX/ϛm0WEEYP) &l<_3xG4jD+D5ݝAO@PDHu4|ch#JFtbăG1y ͉u\;v+b_o8R=f6y0ҁSo@5-<bK |RU&{&Q"aMw˨Ǒ?"؎R b_(I[,#Lǧ#b䋋KaqW*giuPT,X`Yl2ÅNq{ٴqPqvFKdwO̞ J=` TZ U G[Ҁ ]6F 4ĉ1ၲ4a)eHFYzai<0ꏆh;>ԉW{}5F8(jD2e c^LdF❲C͇NWKf ̇ޅ1/F\uy JBαyi:É?y/t'̒KבqWaL}ݍYAQT}}QnC3G+ܺhKEifZxQU~餂۱}STr 9_44yDj[0VhKߨpRgcdȄGw^X) 6gۄ6]Oݮ nyEmcb瀻@Fjr%*؊ =(iQmc|خjufP)O϶&N9)?>}~1wZm^(Hgeݎ_F|TU+C H?u3D}Qjk>~;A>y`L_XG|n,ᝐh }suҊ?2jp߱4Zeļ`st<\fSg[ %i ha{]U1>/}WE޲-_h␵(+ IY::T{e@n_P*G(&debdΚdL?&jga:! 9޺ w@9bߟhAzW?} p\D kj\ ;0{(:ϝZjOi6.*ahXU/&Xq Ql~.F'#=-Q+\{RQlg7"5,DKgGsK{X}{E"XawۄNw]&.{|~fukNi5!'D_:a~tꄷ/wՍ~Of FôVCT|bb0p@"mv{iڿՑVnjtRG!)9Cc㮨S#isn&w 迍}\^K  irfh휕+z>Oxy-l NoߗOm!s%Gq>~y ֑RC{ 3U['.-~v4?ܗ|T?J<{rqbA'OD BΠSof7nD}}Yguςn=|oҧX:Z:Aa$B*rNۗ*'*Ut43pӨ8R!1jS;+WZwuIi9r~zv[p'j Yw N 41113Tq>gTn_@[vh,.`p7[&|R[1mBWt BNO'RCGMT68#ux=ռ]fkmƂS~]Jc 14Kg٩{;+sgS5굡C S[A _ɧi0a8s@s`* 9b& R `m՟b]a7\_Ay1zO@YXSZgc]m=1͕X=kʊ&b"EfDe2,+,:9vl%֏V~K) K@sT3\9 jɗY_0p:"KGjʼn2UnݷӖQ̮;T6v Z3+sMXgoj 9JUE̘K Q,֣j;*Stqx[qxIAiMяvyjg+Һ簘n$^8XHL#g6bx?5ć5>gw RV YhP%|= %^7v'9}@+ a#EkGɦ0ďF\n>{+V30 5'qw[ Y;{3 [K9n9{gJ)u\kՀCHG_aL5]áW p"/e4JǞU ŗZ|^ /Ѭv 3+TwAxFa*߁nAzLGȩhYD9mg5ボ<|L-5~o:v;:ȲdkK*?݂- OPlz|uW=YsYZ]] r/ chkZ)?-W.`"e@2F>?kv}^v2ނzW`j(+)Ts{9n*N_h%v4՟ݭUAw!١ȩ57G;ٮ[E`HAʢ\5_ACC=N}6SyqE!hkSy:2Qoʏ⼢P{+ ]؊Ok`:<]3};a2[wۮhn0s=vJZj=@ĜL>?V f[P$>(~p-Yvbj-i;D1]}QT35\i3AE6)^݁&IAG6rj;ēu}V̝=SrGLCj?_A @Wxb݄%+cF~) QN|L @@om 8JJ|E  `6rwtYo Ӑ %>N~wIn偓(; 椣@}ڍuѻs2m4&ÇҭoVuhu^QS0R/&woeMCiT3B*7B5A.α_4fBҶeW JՎ]IWϩdNjyLtڋh 8ѬO-:4&UdZؽynL1;> oQ,Rj/KQC5 MW3BB@δFav*ZClO & 㞚՛ eT\2ԦǍ6a#Ћ7BC Vb-/aBkqVMR{*/*@fa$NF<~OI`MtT^ Iȟ$i eg>y6M`yf~?8Biy&伈Yi{ ix)gmYE%f'uOq~;?[)_?>];u6* |7m!x#e(:k"sۅwQs(y:֫bS35u4bRҕAd2f . {շD{:u~b>_WYV-k?NAެtqh)4$e`s7ڕVͻ{g+#.G'}\Jojg$V!E}~ZmlH{;)3F5_(htm%EzM&g07n (Ef~]@fLm#L5Oك 0G͌'Qw?HlZwdZc_Ur9iQo{0a $*2u砈.?4 FTB=i󝕹g*i:,GZ2 u TAOUb/a [Ӌf`_4_nE(Ly,"p%1ߨP=jddBb8"`=B 0ƤT+xBӼ{" h~bA1 8( R{2P\ !} p?+~Aӵ>$X!'m4z&9\&g>Mfi>-+nAh$㼻Wҵwx;+sgJͧxoZqV ;`R6K/ſqW!#11& RYL mh47AB3v{ŞWQN 2JөH!=4걒`o6-?9fSŽz)gQ ԷR@}>d(#ߟe`$+Ie1D}NHLVb?i8UIeG B9:MwS2m[C' ɘW!!9\ErP)飹x +ď-Ar!uTHvNC6nیW0M(L#cZ;KQjH5_cKi&N&pMr{;Dڥ?8W:E2 !lk l B!3a8$mfҿ56z '_Y Y6ruQ?޹#z0bdβ@WX)!~n~q:3)g h>2>7:]N6{릷یk&7 X ws`L 0&`L`MΉ2&`L 0&X)ps`L 0&`L`7;'KfL 0&`L74'`L 0&`LF8?L'D`uٓ 0&`L xm 1}B+D5r!G`„ ^9L 0&`L}B8L 0&`L 0&\%JWIq8&`L 0&`}+Xrq`L 0&`L JpL 0&`L 0&V 0&`L 0&p+\%`L 0&`L 1caL 0&`L 0&*V J1&`L 0&cX)*`L 0&`LUpcL 0&`L 0&RU( 0&`L 0&X)*)`L 0&`L`@P.`L 0&`L 0W RUR 0&`L 0&@#J>V\&`L 0&``8`L 0&`L 0>F}B8L 0&`L 0&\%JWIq8&`L 0&`}_+ 0[@kk+p%455[@hh( hy,\]O? ']d$DK `ikD!<:P%Ҍ:s3UM #ý%VݍLF%wPgV 4h0CzON%=^n`qRL U\Z&$peL&#** ;0_.*qI ~nԕc͋ShXejr I*טLLz3x.!fd!Pn& /PJj74'*_CC*EOu#XTэj>yc@xKKdAITݗd{qh 0&2>2*+0`>|8 pt#d 2BBZ^])iȟ59՛ImCa۴\R Xe𗇪\RHRf\wmwkQ|: FA}2y2uQh]V,+ϛ͋~v_rsL ZX)pk ~a.(X.A)НKP %AP2 8ΝsMd%isQF$kV$#.y&6֍9X&g߲R5] l i:ruMG2FEF3YـEno_Knl2&p R&W'~^~~~g'+oAƩS7W# fh'm6b0X,z)G$h[c-NW- @ 1b!v Y ]pwJfa!M>Xلc&4ƀDQq14˯PufZ3|$ued c0o20GLl mmt񂄷 TF(.<%wc3ɤ ZA%½|<]d0 l::k=9Z\ ?] GtP/X ~n5$gc]5:V%?;FU|ں*Rȧ Ae΀7r4ݜ% Ig#_ L !icm%:}-׉}8I|qpNG3/ j+8pZ)6@.4 jAQ,_`>+%*P]*NKe w3&Tsk"-mj-cH\G-d%},=[^ryC"!vf>uuWag_g}&.8NN%&@ 94pp2B@Ȼͥ`LA[S~/8\Ȱ,Hnc콶?ěQߐ^Yv`+Ę o,,jua/fl@ƙ:6?9VQB4𳘱ck1K7eq~g*-35[)4a9޿#5F3+vsXHYxFiXo$ctr2+ެJ>j#l,Į8Sj*9u8n¶|x֔iK RX7/K1c4U#g6db;fӳU8=G6 ~< t"9+ߟOK#\vҾ il嘖^imZZyؓ7 xIm/>Zlǂ5MƢMXTśh-f` )މ;stx#6Bs{ UX|BvIdeMtMxq,H]-}Ӗ]!;IZVb{X /އ\`י5՝wWhs5g^~ζ;mniS",SfGrga˱՘,Q.ԯDf/OV!|lhH&:' ؅=} i[Ir ,J+hZ 1bHU4T(=޹B@VY#dnH3o 5Ed#%@[5Ow+òtApe>c) 4sD! *+Xy(oT8u\nAӧps׬R ٿ3&+fuR[*3 Fkf1Nb3{| >.W-3ƩB@\YXgbom9K[wILӥX9wʪpYB@Rr6b6S u}5WnEL3R|'2_/F+2M:٦f=*]VƊT! (Ƃ)#U[m(ΟQOwW`=TW! .Ĝ(/OT3͉B7`}]_Y*`ۗߟT]؇ 0&}=yfL d=] aŀʇ!G5S_֝}M{g[5JL`îct^fs"uvE'Yſ_h )1,XO߳`c2 Epugc&1a@PPm/A#>ZL\^ 7ȒWTbk(\Ke,܀Ugu8 _`˵8~6-Lc8~挙<~Jށt&>xfp%Kv-,AUۅyr(šwBleGj,\ ǪjPE^;O΅XhxdyΠd ESfk$9_ ]v6\B4壕ui m QSf~Io, BMm-j o[P^o(qGbʉ-2Vyʳ2R]Aj+4/f)wkvZp(&-;#2\yBT㏓2h5}ڵQحk5Mp=ywEIiѦpL ڠytoTE(.)-;gjkkZu>?W2ouL 0"Jb%k2]\g`☑̼|ݼusK̤i" Ixam2U'k oBuc4LŘ bDÈ3iO1 6h:냣Jޜ:y'@JOBxn[Ȓ#:+ Ⱦda6.8ZL9Attl*R  ,Z :&GG#f=ʑf 띕;sN5c|e j&%٩V8"˷۰Ԧxun}PA!3]((fΑvіx ) T,^Q\uBn_e%TXS_Hu`: 㣁ԆSZUUBiW?u%Bc\ci d@*o9Rc=9sbc+Z}vJʃj^REr45jU'Cm ql 6Yvoz/_|W+pn#˞S<|wU@.E1$A֚]j;-~{ɒ#)oC?I^mH(ԯѳ\L?VG1)), 0[@1uUai5 ͊38߸tI$_@IDAT<ʫ;*?uqfzGE,I-yj=niE>sW$ҟV>VCB{W{v}Vl;-Ј4b%qőmV7fӲ^=CL2< gGɆi*$<85>6C ͒mb_id@og7`L w=gL n~q`9}^քU8zB{ݯ$4c73C23_c}2~EڋEhwI_"_ƪ_ۆ+ ǟ#U\r-N(A+TG0L$D$z͜4X6"g^<0wBG`~U/ 'Zqr(';PGpr jf3>(%66aM$Te+1yJ3rC3f%&t&fz!c褭GA#6q2j~hCjF'dHst6j\'ٴ+5,@p7I;Rk&tBǮq=6Lʤ?Yn Sq-٬(D`ZVI+mGl~H]%i3]3 Yogz'ʀ145ea^3^vΕUO 1&zϞ"eL _'yt,X±T ])QPl%J#U]/xqbe ZLF~|C:d #(-hN\&֨Bz EH% /a̙,*6+![i{}uB|m9j>{Nyx g ໅)kas`\v:)©UYLI#__ArLg9s'+Cwdw Aj֐5tCBSaM1D )xDJx\T b :жn4Rv-iZS&_֣"h%O[u$i$jK3njr/7ew<(Yn'#Ъ GNdaf:s(3(/֕^IC7P<"|ϖw+kSb,˥gvt%0vO9q:/CI06kFCRQ8Y#^?([RRLJ̌kYVI]zI.OcT kZ<> Uٵض%͛4m,ߣϜ8\?.|O!FVV~CO:_-GBVF7떋lcVT}+-%} Cw]:y@!^elsݜ EY$|Mpi][z i 0&64 ~O@ eUoKeߞ*VKcL_n`mWkIe*{mAiYx!" K4@ԽjL2G;:Fh3әȜ4ɪ}dsj{-4q!CFj$;ʮݻOsC:  {2e[ ;_CwV4`Hi*X+'6^]4l/~XIV>ZxCq;9YI6A KhC{2v-6l*BM tTGҹ)K$6b\)S-ڤo1JYa|QPx9uE{ BX8>ԨUbxJ[B<ըFDL2S&+ -xyC7?7X,x,.ԯ7smV=[ii, 0[@ =@ӌҵx8hv\^ה `#D¢G@]s3LR1F}eVAƅO.Hʝ>*Nܼvl\zI,ʹAblЎW `r*[ms%VZbV ܄+mꝽsW콼v2$I.3leՆ0m) eq> lLҖ;dXj9x+7kn UjI# a6CRs4$`̩ӟ)'f<sV EPi5=UյKLJO*gU*kɪ1t'Sy~{&Zn*[i+t u;4UciڈqNfҳU.&@OpsY>`LWI]Pc))|HDF&[,3P cʇG0l]^l,U´uHGb4y4HuFC9:1) "QY0CE5Մ_.@V~w& ?m'pЈvxcј6IZV eC *ך Ri*M/[Dmm -RcQFu3ǹy^ s{|0*V"Y %ߪ|AމB%/;6]B6XcTa?Y~"Ԙ ept><ʈ k2R*5KBWnӮB/!OT"3|&‚Q]R=^E}ic"; GyR2FHѨ<ڜTgc)legcL 0 3&<,9pE]y~QB 7xӯVU'_Raݖ\I뤣߾tvD /森}Q8yMZB=hZ3Evw{8Fa߰?+j+]Y [2igEpBsWBjbat(o@xtSzѰ4yh:& d97+7HΊ]GZY*Jp|Wn>?}I/p|lsD_YU]UpUCzA)+o ҩy ,Zv ,QV4| aR9KqdzX$? ,֢M8_K޳~YӔ>J&BՖM74hJQˢ3D%яKtcTH,P٘ج8u&} )))>,0::^xcC_=w?ǝKyayu;qh!NzjQ&.Z=Or9B.<1Z.GqY 2Α)ڲ:#]H3_ֆ4(Iyh8;ٔ+1{Bӫ?Sj*Ȕz rraOMRi0 4P`'hA8 4Rh[:t8ާbi4 U2R•CKEKKf +{2Tm-ҼG8s }KJzfNjLJ+0[t`hGsD3fgaQuAQDqwlHC[QH2f!_R\ W^{S2z)6ұ'<#)Ah7ycAy؋ 0& i0&0&ɠS8pr3TēkyB =1|ZPE{Q `WL4h\e35"O~_?Dey`L hJ>A'L auwv'&W!DDHĜy!@ˬ>#[?]uL%4+ 츨xɸOAjBj\X&@ k0/F\&LK.3g0e|g>#,Z+!M n,AbcY!KPyїJ2vGA|ʎ*UHT jb%fL 0OXB&p+x*$ #`[rcmK@n‡j K&='_os,i=E0QS&HlS *Edsɓdd/W_}Z@P ߩSp80k,L<9IsX'A HU_DM3y@<ۀǁâQK,-=xf,} nꚛLn@i0&`C㊛3cL@M࣏>;3}/Ǵi0i9)uH݂ a[Bv㪫¥^it`L 0&.*R\TU΅e0Y 7xgϞXԩ)<2+b!q`L 0&.Xj& A) l!`L 0&`L` g92&`L 0&`1`@9:`L 0&`L 0d%Jd9 0&`L 0&@X)#@`L 0&`L Y R YkfL 0&`L 0&#V 3&`L 0&HVH֚c`L 0&`L H1L 0&`L 0&+Xn&`L 0&`1`@9:`L 0&`L 0d%Jd9 0&`L 0&@X)#@`L 0&`L Y R YkfL 0&`L 0&#V 3&`L 0&HVH֚c`L 0&`L Hc q]_.3=7݌ޔK.|!5͂ԉŃh'( +,qr8~YQȵc 7UdZ&`A. ,`L 0&!8 "dCx[qϪuGVpsywbŜ%P$G.$V8[Ʈ0$=IWN8.{=pU sן)ifUI>`L 0&MbT&/>BUoX[2M$WkZɚbJViH9RE$уeERǒ[gDS;,1|{5 s܍i2k gL 0&`L 0cIp <;t"j}k^s"yGqJ#wh|'މ ݷZ! X{/.f 6.@ҲוO`L 0&`ALAD ꓏.棡rL=.t=͚p (/(M}kv8OqfVk 6Nţ-4m,HO;VYױע^R[zD%M 6Y甧+YͧL 0&`L 0`S`a& dV(S ֥`VV*apVgMMN`Wo7A F____Ipaj2xrrl4%!ـ`ʥscKӾ{O8z|k4ӄiWP}dhP!|_Z:0*J ^5 0&`L 0 vvdT'oE foS3cGr] vR< +?s !lW-y o[囔 umjϟ|hcw4(]T 5Cr-c/$KQ5{=8R× =MZ[v`\`؋Z(O Eʶ*:mHK P胈* 5_1ȣIeRG LJd od2aBcZ;Y2ΊEjj<ԃ"2;貓En%XZ`L 0&F q}5EZEY[nI#ռEYV<3<5 ԛH/(n~%uE 5CMpWU1~eu+za"-_(,U?"_znUuhX hЌU[]\j }]:JC`"XW4aįsy(]ɑ/eߛg^N~5)<ހm RЈ~4o*ێѤڻbR2a"^7`˓ W{>y=1O_Vh$ko_AvX/4m6ip'i ͣu}2F6߆o\9Z3s fh3&`L 0&.:Ӣdd`V@+oV]<1GkٺG@AZ},%E V0TKyuՑ2$ʎtES&`L 0&.BI}!etr ouPOK2lh*?L!`G9}/^XU~w/IX7nA9t#?9V###򟃌 FnDr@0 m0u о4T'n&`L 0&.RɫoKG÷7O\I)+mز*f:p1Abb5ڽcj趣<4YvwC/t(_^C^RSS?hjzmWԽʟ=i7\΍X,9xrt*VbczWׅ{Vp9?F 2&`L 0&pa)Pŭ_\9R\9gȑUv3+fFqV1RXĶ{WGepPhLwTرaX݀fƎzh.kt2?z]_c4nsa3W\-=C~;WVE(]i(7߆=T3,:tbLND/c# BVj'JѰF~o F )t7?rda2[0whM4}w(,,tJU*`L 0&@lW)@FV&U([٪ӟ7($Hg]GAUZJ+yF.|ҍ bl6}n U) aEd}KPK h+Ei +կ;jQDX~׸ rqwEЅ>]P;ҹrR pjj B+/R#Szf=l~8aU7y~DLfCW`L 0&w!MX5ck+GO[iB%{/5/(4D!5=A040qU uMW' ؇|@1{bOf[z^)|<4`Us]~S_uuKpN(X˜KM_;۶5b`{BhK\0tMVJckˑ_vii&Jt֕jē7OO) bS%sS4e.݄Q3h/E[O/SA[AT9؊+Ѿ8Ji4`L 0&K3T4].,N'0t46SGO5z >?3 2H.q/C0<S0 +R~iD x ^8¯ : `+֋ƶBLq9pbx]O7\d瑱p-mvn LJM~ 2,FJvSț)T%c nPs0ڤc:#,M$GBzEExbMU;zcn28x#2J\> @F5 |0&`L 0&JwFa/5 oqM49x[~Ol$}}}d,fA_O&QfFrԐǘ kf65^*(RicرZHQ'+8(-'I Fq`L 0&( \XJ /ړ.,[X[py/56|}_Z)`+aWOv.C_Jq5ƀ s<5 w.`L 0&`M$<Vd/|H1D~ @z9XhMZY:jM!T0}v2&`L 0&a@9,l +‚C lx4J~Va?&`L 0&b"+bwD8G?2G~/`⵰Z$p0J\I<Wb?ɰ|~dg$1Z8>2`L 0&` `@"rL 0&`L 0&&8>0+cL 0&`L 0&HlS t9m&`L 0&`+&phL 0&`L 0&Ii3&`L 0&X)0+EcL 0&`L 0&HH$]N 0&`L 0&&J \9,`L 0&`L 0D`@"rL 0&`L 0&&0V Laј`L 0&`L $+IfL 0&`L 0&0 R`W`L 0&`L X)H6`L 0&`L 0 LrX4&`L 0&`$JD崙`L 0&`L L`â1&`L 0&H$V $.`L 0&`L`` 0&`L 0&@" R t9m&`L 0&`+&phL 0&`L 0&IN ^FfK:aJq9RaIOC/Bsx')3fZsQ'ǁy7"/uZ5Db\N<ԨMiN'֫&[W. a%X0QSƖ)WDz CM=7Դt$7c@!X"<_bL i $JX,ߜ0o߁Wa'e]ExJ8sK! k׮rhr{ڥH/\ֽڄ<c.HtU]_$C{3&`D]{lDA>\7av"2H{&mp-kñ4nsBL L$I&kY6F=פiRiHMbllML-cY^aB&˹QtcrdXoCq ].2 c`4`4ƘU3Ųc̉3&0yT7cI&BLax}NS"iRZ1D1t%dB~=y)$m|ZXCN=Mf.qؑcx[Kf`V|,Ȱ11p4ú`!Ʉ]4q"+fq%1ճxa8v58>ޟ \kӶňׅW^őwaSe7 7'#vc;|aEP] M[a^jz ~L%i"dc 18ߩZh70Q6ם(Xc508tcVxf2Fdco_1o$FdXatH8NS|\A Zf+n a^q3_nE#3]7Rzia=7-L-U/F#g,mm~P5K/T c'xG0Ϩ,B0Hq(4N IpK(4!$Vߘ& 4r}TEP}~|(Wz(C?v7 |mEܓ g,6'%m$ a;Gge{u+U5+^*?7uQR)8ґO=Ch\b`(Pi]'~ p2up>GA/oBolSznJ_Gh\! ƫ߀j o#a ɣӜOw&M1 bk !>׵f3;Ԫ㜿]I灿jڜ,Xj{U٭v?'=є|̓:vu !6P'0'B@؅%YةM 1)P /:dLgMi˂(:z}ι Brнr-qHꈾǾ(Ƀ3jUP! i.[tR)KgDTJ8'u'=Chs B([6RM?"5>M{*kPQKz4V ~室w9r*ЭW24n58'B@ȯY)[u`"E! \1ڎ/!k!lKa*|vB@U7>SH!1 Rv0-8Fdzfw[i G(sl\ 1q%p(pRJdka/ki/ܠxנN'F\W%|-5:t齵ȯlC~vqy k7ŕ-wdP^tCx"hٽGF0|tUۏC(.M8O.`v2:PNomZjNGQ)6o{N|:wi3gn%;=T&g]\׍[V!qdֵ q:B穏VޑXI{SaNƖݲ]'pJy^d/IV~;,߄E <*Lfy^+{fΛ+pȱ,ytg k>cd=&拏v`wG,X]6ux _#Z 4:0`+:ĴQud]:3 ;7_3Kʱk%#v9MIVo *mM*͚-Nĉu<~Kc*HC;0xⴘM>>g%K~ʗg#?5۰~ۣ '+")h.I@Qrb,!rߨ\Dg+Zy;2DFJRO[ߨ 0ڂ^3̳_>[cˠl3\ۑ2?3~g)n^`<)loG<J$**Kv#_8R^4v nOG|zws')Y0%e?(<͚)}ufϵ"JcU(-me L NS )-T*iƕxB7bs[pe]BݍCU.Ij6,9vii&j4Iv c$O]{aT q<;Թ--Mh|T6y*wd"(N$ܷVzytT Y$?ޖ( K˴6TqU*sPҋPwѠ딇?(BVY~ga[wM-m"i.7{L7 웩6ܮ868bp%proR/3P5HCH|%ꣶԀ7þ}pݷI #'fЇ)ȸE8A+qϪLԹcl5XK,J]pzp u{C˰%ߴ|EӚk!OJLDq۟IuWQztlT&%޸Wz<2zP*/1J=Ж)#Pj Qy!϶Sz[q9~Jt3H>m ++U4Tg'He1.y@>dqB9R5Ikdi;Fppr[.M2r4ϸ=l)HNz̋&NYHj/Tzq&Y6ঢ\`[ Jdm))ˠ oh'J_e-cF(<4l+1m5Fs7|4AňoƫϨ{Š0cVn]03cOs7M{S"c_=a+.E7nBn X87!?1RAz*$+U^]}!ر,ݥ^9zzeن+~5XE_ M+1Jˣ\ p?>f!.<9>ݲ[UϑfJT[T7R:qR Bx Q%{=j6*I$Yӝ*LN'jյV. j6l$ye#o Jd=_/WI޸x;MiU{>S6ѐZ:Z'>w7X52 gp&d>}<ܷveJ9MNd*NbUI;i2̐bۿlU2;\wd h^rBNA#iegcmϟL~f'x1e HИkrydcOQs#Tj&Yox K|DCۻZ3J,.!*TZAm}4#y!zy"gJeXԔB43-4][B+9ggcBDfj`9)c?]l&=oės)~mQ.&t"SDbL ^yoQc)-% {w6l< IGt 2 (oWI ۷AŸ&Bi}|Ӗ'iσ_fHˁ?`fPhEJmQW,suWDzRWo4Փ砸Sz0V&j/ɊGLǵ1@KdȘ6!x:^{v=Zy!Pk^ׁYD[wP>")b9X4 ZHOVX'Ϯ j|8@$^8N}?gknRH߸c+zf\/,;/R-~Gдn [茵iC&,1m4fxO# e 0b$Aˡg{|@"\JBf)z˪޴W*OU?ۉk^t=̿[7PeYЌ?\\n;Z]./eM l=o~Z)G=Lcn[z'>=sV=-G*"7Ou4Eͅc>Q] 3Tʹ"Ρ̵?m#ч s5Muyʗ*A1}&L~^۾Sý$xCYG2gΰ*[¨m0/ȅmر_ͅ7*PMEWҶ7zw+V,ѱ>%rW0Se3)}yن>/P+DVyN gE80_?==: 0FW U?P}w=u]UX v^.z el%ڦ)M 썏nbX9\Ũj ׍Oa55pA#2e?BϠ*flڈ`#ʗd.O$GRpjT ŷlA'ՋnPQX!?ukt 0iEԇץ2/,W7?zG;p] kQё;I= }sw`On-R w~SMX~>ˢ?Ϸ܏‚lYHYa$#H֤}˰X;,YʿyA .4B&c_ UX9+qE֊ .GDžiTQ'€}ڪVN>cL` \JZ.yh{].9t*x j(令|X]{t2C'([Vŷ,yw:_֪X8NJ9]RZR\B) P?hz塒$[8 V*EPj"#~K1#Tt(+tpDׂoWko ^1gPJyKzTʚ VѰ>eW'r Wz`WU?ؼZ9 Qr/cXUX4qPh͢UV.ipĉ%[:q2,ZKKuעYyvˠn֯ss@`5'B ҉ْ r%M״c`mY@po_ ;$jץt`u'Ј۫ld|Ӭ*\(A"r1Pygok}jb9|7ί4I'M`IUi߭Pdgua-Xұ0w 껤g)~JRGlkGk0_o+al7I_)kǑ]5߳&N|i":'VIlRƶL)o4ՀEj`mCn*i!PMkČBeuZ[3 A}lPO&-Xyue,&Sh-D {gX6PwTCg7*UmKLu95YQWך(n]TXSܵHth7]wEaB=猾?XPmu*!Tr`}E4'e\ jAu졷k}0c v mM[`@\2rz˙@ )qA;H-Ys3 A&Nt|\!qe˔iWbfFzBQ>' 53Rpz؅9kma&__ԗ Pu'&ZNEYgdz9hhyh^Qhyۨp3d!#f<- ƠAƽ:z?Mü/fM Km+S0 AiG4o;ZV)m +n o\4% h% wg8D3zi;meT'Sjp:`%`X*Y)_pp>w.s`L`h&u=ر~lKdʇ&SmL;?9W&0 xɨDs^eN(a7 0&`L +J3 kV^eL 0&%loُ2%&` eL ^)_b'T|m6>S\uMF^&8 0&0n nClѨs|/䌘\\6&?lh0~,9%&`L 0&`IC@04XP&`L 0&`%JԘ`L 0&`L $ V $MUL 0&`L 0&Kɩ1&`L 0&HHbA`L 0&`L ė+˓ScL 0&`L 0&4X)4Uł2&`L 0&/V ė'`L 0&`L iR ieL 0&`L 0&_/ON 0&`L 0&@`@T `L 0&`L X)_`L 0&`L 0!J* 0&`L 0&@| R <95&`L 0&`ICISU,(`L 0&`L 0`@|yrjL 0&`L 0&+XP&`L 0&`%JԘ`L 0&`L $ V $MUL 0&`L 0&K&Nj'? ,98}sQSSY/7d_u(.o$S,HKc5xy4F)iWτ<=.8\ڄt RE,*K7|8wLfptYrxip1!q#Pbӂ&z~.#8ufiiHa86G \&i\KMK{R8p4RRR6FeZq9LHKy\#xjv1vD:ہ7O}BeHVDu}1H7`N[)pgП'a S?{G&ɟ'\a8Bܸ;XX q,E\}L}__ ,r,p)EHBjpq$7#g@5F02H<\rc]X݅W0jfq5d7Q*~+ܗعYPVVk{&~{B}x886 cee<폗 } 5 bZg&@f[g>m/MO ''㓿})@HYM,- [ aB!VcH }|i4cŠ)| ĕhTY]g\L4Y!(eeGʘ$d?XiA=?&ɨn=Fcsr*X,JDz41Itor ]. ilh֌t۫P132In_ch4ۈQ@P21-XEa&@"Dz1+o};{)D%"=DE#xxbQ~o&2\evxpwJo';q_H 8_4Ml@Y[ Y"!mՕ4U:>(R̳IqS  "i0 {~z֟rJ{}W6[5r3Rqj\֒vԬifԝٸt 2r]^s20쓦Nc*.r8rS&@$&-'}st9x Y#_!Y(1c |b̰.Xs2[ ϘxewMM +lIYo蟱ްdN4rGF,iB'9t 97)S/CkaRtMSh^Z+X=hsZܽ؇?IܚsJrdq`.h&23 2cZ#2r$8x́EU#":sVU8+1- 1;A(M`܌0a d!. l_mǠanц4M[ ǃ~ M{'MBT ڶoQ҃N4R[Q"5}|#$̺d.CDo˃5M=>`LP ʩkCS}m~O_)´x~@IMkh(z-צ@]0t7'ڇvB>E[{Lx[7u~>w~e ~Y p- $m-S܄k4zp_jdAލB>b^YOl#_HFtOQJF(/=}Wlq+j"ǧHW<|NjښQTXȩ&׉EY3Y}5 SBq`Eh=l<ҋ͹A,\^dض*@cU~R2՘7vZ{ZU^qU5⧄9gmEaZ"-=aUbԗ ΁poQ,uz| ⥮KoVu=DZ>[Jă[ג|.[I VQ?{oq $Ġ5A"$Q1j%{LT-cN_j=<7>/=bzRK%I b`#IJ(`@EMAU/v瞹f?\sM*LZfn]sȔܝ|ycS m/SdMxeH=zz~^j32o].yB܍":jliغb$̌TTlE8a'r|3N:J^FJ4[kM; =GԼ- d=R1$E1k$DDu|ϥF ՜7~d|y&8-ň.CzfLr;7z%(|~G:m֧phfȸʌ}M\,.E=l8Ź"/U..ƣڍ+1{vqUK5jʱ7q!C@ p_8!c1̴C`R[ŚЪi-r,Cic0ixJfҲK2%M~< 2GLnjbݲ F ނӱ N%5FKg`j8V[%3,,cig]s'*Õ0t*s,Ai]|{!|Ą_M{F ! .O 4 nmL vM9`Ƕц FROq.DUsgT*(z(Uֳ5Ad(5mlBawoiTak,G]hT:Y .c͹1kJҹ|QB@Nr^+V3`0eX۸N^_!BTgb\8qɋߘ`$ƵLm 1ň!"?-~F_YJ] UQW NcC`8z悻p8EG2Ձ Ż/_C+DBbr~K9jVD@:#4c3Ź'r kqoP6C4BJF,)hRbڊ*ARXv@r &+7s- X3VIݎ! -`WY6^ʪ-!(G L3&:[wp7,7n@~F)dq83exmS1|M߫l:ڭ,Rfю'@"k$#!B#U*/n3؁UZZ8S`29SXǞet{K20g$6mycXl zT8*MMH )^XjG_vt$ue"^ģ%QH^Υmٴǀo:iReFᚶbQ"vf8<;^9[|[]Q#*͒r{ꮨڀUs D}Ӻ;!g!+rk!ZKa Ge\bu Hz!]Y(:KnBdL26hMmWHL+H7[Tp@Uj-t>**ÇR 0bd4E4Z!Sm x~Y'wTl2䡖W4#3*CmcAvӎT֦\Gaf܎jQ9VC:*U K*0#εU|w CL95~Q#H&G%]*ks4/{Fxۊ?i+ 6Cz&HQd QhjBQ c]πnPғ%;>\,@ǽ):7£X5uq[_AM!Ɖ ς:}Z-԰U&}J&ر!0]oGqL3_ކA70E?cL׉yk7e Fn-vDF6ikHtU@s2K-xF9~{&[ݑ| 6ՠ{^W'iy?\ fA;p$)I.qzjcX/26 ?A 5I yK#&Q@E9hK"M'}ֳSq_NP˶N $W*Y)C"+Mb/8-+}.#b6Eʎ6ͮ#>AgOXNCGْؼ%s+/|?KU ÿk#qD027+4hX,M@i*☊itV*sZZi! #@ذ+`„!賠̞ꐩ0a_ܹ.îy0',DRw j%)$ }kK swT&E^rFX[gLЫ=-ӂLuItTJhqqBZqTGd|x=)s`@-!)MhR.JٱTbbkCjQUǂMߧuv,H²nhcSz ԡ";da0yid$EI"RnyW[7`!aaZ;Ţ=Чv:7ѓ!>%lJw4ґ a]wX sQ s+ vLkBS[v""Izf wK /.0 bEk$IMszRؼyqcqFǽڂ}긵1,/0/m 61Gfq!P@4.jD)^GO R[(EiQPƦ?q:9>!uW s?d#_tNUEuRivr ~ C>MIt_7*1/3^;Ψu>q VZOryjAF&>NE۞hAY$<;}/(`]2ZyG4qDH>3&-x5XS,%ZhawA:eqZ:nhW[L3v:̧_=g+ɉ{-HRrkRR;O#y]L}n1>D\ZSpڱЌGбLLHwD-Fd.靘hbD>z C bdȻu3!/WRK:S/Or0"~EBFkЖpO&ox'utRy@G#LKr78v; ".C@xx, ϼyЪ7iwf kڮ:(Bk2  %E u} W\P~e[QEb"XL䚉/$4ʤ7;ˎ+uVV7-;P,wfY?AV-5we ,Ե{`[eGtִT?)fMI4Ș6MWJaGa5].P䙓0~$N{m3a*-. "кr.:鱱ny9YVgc[gJ+@_҃`d"&>AunXϊw#68~ ̈́'U+Z4|lCH}YhXQqj{]sl eƘh&U}Z "ւ5bW. M%JKW-h%f]M[̓8 G#`bP`I <@R1C | :ȄQ_֯J 0,ddP];j22G6MP!̗EA-joąiN hI8`Q/OX_[$"(\[LEٞf$N(]lYX@zt }FT9|Z+XڻդqW]i@Q\1Mfص~EeGtY3HCz[Uw3#3f<1ehӺ B C [~Fz'; | 7~A90ow]ؽI_F)8v)ֳ̱x]y5bFJ3nJ?}RFNL+Δ{t| "!}F9ӄc ·hc 1 Ӕ?('|cc_;hj s[dˁng8/t8v Nq&{Ί9(8< ͝9 @IDAT@td] m^%XLZ6rmBo!t0z;2}<}ۑ)')Q}'c1{lNpU+z[]\%-9KH@/..,#^okdcoexsn݌coȍZAl>#y"Yi!&*U"?[Ql!ߺݔp~<KŃLT|g,oLZ:J/?9ab1<(rb.}OJ|Wv-i݃^n ,5[Z%o0 d,=ZsIxL]!g}xXiEx寍'nˋQ[[!GT|X.K !|dr*c,Ic/M13'Z/_&?Nu#1mʈsa,=rylGv Թ0I*gP ㈮Zcs#_tݫJE-غAp(SJƆ< J;qtI2p8:8Яi[7~"|Ա ,N2CV*Z9Ckk3,څnKߒ?{^VVfl[=IB_E>}'CZ[Vzv.ܔJ@_ >#|{\3y^- 5O,"*=f|CT9nѳ"bP-v81;^DnwIW??@f7uZ4HIv*LVV`؜ˎWc1WZg,B@[4w($oG.cG(,j2ԶU|gm՗͗Pci͇{K8wW3yJ\4N.AہUktuODwDeJ7wA\^]4m ک>:BS24%e . p$ g=2bzHrKDE#U~g i i!ГP,E2w;MZ##rYiҎTx5Z "#'ovGc lΌ%׸ڂyXj"SI^BZL0c!?Y߷f6"B$֗Γ3n/G@D@wV箰8svzn+ԁQw{~q~#Lt0#'WS>K8D}!Ej+Ի5^rJ(RanKWy|Z++/Cy\/ΠbXߢ[k1h _] ,X yG}^yXyOg$~F)KEKҦ*3`"UX$+Ftb ELBuhxFI/ӑ: ~(i݃HZM;Bʰxثgǣ|} m + F?ckϧx8;D&{XEcuU~{WvV\,VgU&dәyttLYٚ([Cƕwly$C !а`٤$Gânp8GJLߘ,=1@ 4|V?'?$1ydqeH+݊`S`fBN Jrh̽w(\lSB$a}^lZD;H@/Gh~^#dG G `i9ǫtK)\G# Sl]+Epz6)s QxL_%}}EÄHw@+qɰc! f$p_gx(r@#ƀ~_U:grp8G#,^՝%}wb8.-`"q6Q 7H;2;Vbxd@N=5}'7@@gG)Q+q8G#p8EL  %G!1/&o `q8pz NAKf6TT>qp8M Wg#@`Z0ǿ2츀w!B\;G#s>% )${0cÞ[<G&#>f*w J?&Σp8&=v|߀h\~_>l~!poG `ŜE'C<'G#QHHGp-xQr 3m>!r~n`8I 1n8G#p8G#A1cn`@p8G#p8}6#p8G#p8@`p@`xqG#p8G#5p@)JG#p8G#g p8G#p8^g p8G#p8@`p@`xqG#p8G#5p@)JG#p8G#g p8G#p8^g p8G#p8@`p@`xqG#p8G#5p@)JG#p8G#g p8G#p8^g p8G#p8@`s|_v z*_7nӧ_~G߾}-smOX)\;8L3t[̜0G!n)^MIaMLDw'gg? Bpװht;am>p"d wO!ةpP` @d\ۜ[m`#{RFD""VԴUjg\9â#q+>uEtP}pƒ p (m=ҳqwqnv"x|=^')8u {Ç~ܘa~_JsSU\mp8};@R!+2vN#q0aL|kl?ׇZ”S0eJ<ްػv "7_*?Y<pR0nrtXкH^NEwO[kǶg1gQn,7A}ͳoVvKԎMQXz':r 6>{{2)$Ο?/,Y :TEFFbРA߿?„37M°E;huC.jE  }җ(,,h%zlnx»2L*,4$$ri|sFF;^MI[J#O*r)# ٗA:i4喫.^Mm=[Ѥu%KV p󗴞`ۃnsR-."l#n4uQn ʆ8v&z c´^t 6 X@ttpo~v6k|W/$$D8J0`|8w0epE <K#0047:.Bs^y2ʉJTB(QA ,7nS. ʳ`_+v#_7"yQ;U AI1 O:=`;:nRbMJ +~7 r4B-p_GXW-6tfxd:dBug쾠(!~hx[` x&Ia0f;C@.L t 0VU`0/>E^c f0cT1 c(.y'$ͧ.)}vgXSKlf(.~NLH_nu]EMM̦jf!୬JCh,UKDnr^oCϯrxt{.nrlx7p+㭐>L&!pEDDD( `23ٽ&-0 K0[/I3\.i3Ď󕳧qH;piI@Q!r&ف㬵MQC1rP6\'}G>h;ӟ:0<]V~Fq ƉK_^e:q=[,Qt*#@7l1@m_B+")Aɝ)1qNؚOXUh!a3z<)ӄu:` m)0rǔL1h\u9ZRa )qF%|bS Zf7!1Y ism"GR D61kIt4ߙ6:rkg߈Rx8,;O@$uC$}@#-u8qΎ crSI9̲,Ob&]`:4@ 5Ƶ~nm?S6h06јDgct+J$غ5ý.bܔ/jl-Q8u2"FMFb;Ol)+5>mipjw3&ҸJ^^ktuͫ6.C!ŠXp~WI͈1 zKފOmx˧l tc&aJB8]pѵ34腈PB,zp/~֩@p4ѥߺg?30x`AB@fbhs+3 L$6}TX:h!ńFbf0(Z|^嘁T=1 "qK{L@!Is;<!'S0 Uo`GѻJ6~rN¥X[oJNt|yj1W(I_'}WWa rHGyu_3e27wBz_֣i xN QfU6!;AhDYoF^I̊{0T3ӆCf Iz8](/X7:E»ǫYqi%8h݀;{ش($Se"GZSF /뫚"cmH;Q"ѕq懽j ~vs69k܏0?P(Mx.%%s̮YFiw4auN%bu t]hGU`q~ 9%)EZ9:[Yw:RRy0gY9}^PG)b٤ea̓xرv.2軤92p=yI7vQڴ>AK4^Ƣ8M"t(.X]ɿ9_ɣD&QntlԩI9߹ui1@!o{f1lʃ[fn]1dڊF#S+wq<k w TSdMrxeH4F4NO|;16hi/ì.˛k\j k$G3/ӎ73"TKclq(%Յ HULL+MG6MO~>[|l@蓅ehwWh3J߄ÂȠYf uo؀!]pN]v%}΀! l?v`5QAG4 WǕ-uz-c1 0uGŚЪi- \ "? /UYf"U&Q[ހ?+st+-o`-_: _veS G#֥3`0_Bˊ+A-$KfXX5-?{_>wHpmz1.G1[!<1&OXnJ-.qW02[ 16 ,TmL cë2Ő!(IìuU(`Ǜ7dj1AKZ*C@*?#)k0]G0c/ Fۜ+׼zOy_YcrJ+_*Z](^:՘!<[(=Y2 !'( eʳvJ^GW+th7uerQN4Hz$S]heZ@W1n(`qr]aѸGvEt5a.쯳/(a;Қ #Ƣ?Si_6a|v7o;!%?ďcp\S?rAum1~TLMNFT(LzM.]d%0L]K,t.% ͟T{kҧ ;g"Cq݈=*oZK* ˫qMuPֿewv>(,ZuNoJG(R㭂֖f)j {9/b(0A.;s/ZH牕,UX=ٝ,7{P[ۤASol̇ ֊1mTt=vofECDo)!<-gOkڂdٲq1*Z_leO/%Uhj:ڄ͚go:*9Yev()l.+9 &0;=vl[z}pǪ P]Ş h~rM {&:-] &͕ulfXz*I셬% -T|: 6/ iSih}.wĘMQX 8!o.%Ԝ#ڄ]{`by1 WvlVH(ޏO:FiPQ;CJ:ܒ'W u}u>Z+6۪>]4JE&)kJ:rjmErru+(A)qDí=hkkÝwPE@WC/`qX~>CI.=^ZBF)vP_ȓ>9HT_0p\d,xC$A 2IVmD?V;KL1 'iH 'hBdT$%EG V"`z@LQMX#ko~ߊ%fA@Dt֕* ,T&&$ov[ȵEg,],ek=}2nk6ҹ($/Y *5:Nl&"BAᚶbQ"$ &9ؙ_Ĥ`MESAuHyQC6`Μ;zęa$(F_}C[|EP%;4˄P7\/sΠ_<)B"/g*Qֵ.7!*n>ߩ2*6frt9Sd!N>Lc)+PYG-!ЊW*9%ut$N8Gtj$,ZjM}gn_lu+0|$0NXV vB/[C`4V-# %DzN|1DJ\܅/Vz!ZX:HNdL26TQ/&"^*_GeZϳ; KO/T ;XV6KhtB#.~qUu^IG{qGw{Febo,;6V2bU:ڱH:>yU5',UHq#Ne !3xGJ>xb|kYB&<ʌ^S3nsџ{4hg!QXj&6O>낪S5-@c 0]W\@g *88c{B+]`[P J+x2Eܕ? ρc$yy߁Ɠ,VaWvKKK`=hV7 мLٕOMw9S ,t?S?COP&ivDNs)|9nWE9hr耲'3zܢN~8l;NlΏK!Lb/8-+5vn׈qHbCd?(3?X)33mw{*y}Rnnƙ&Ȩise+N$'Q*9͊ŏbL/ %U{}6*aɒ"[]+v̓/`޽;HW[کKԟDg+L0m${RNHrҕky i v%욊Һ?aM9gi:W.&euW%?ͅQ狋SRU잖.S8z]zg$ׯ”k+)$X\,Nw  Gf=Eh]hi>}۩iw/;$>q tR~x3Uzዶ N_>0/X2ڴ ȁO+DGmc„! A=ۋ7L\ṽ9cH=p'y!މd )$R\XN 1}L4弢J'3&YБN 2YR:8Ux z)bKQ#SidJit`1hBX4'aUUlx1 1\ ?KJˉ5  )Ú&MmdJH9xى& LN£߱w$>l. EtRA?1ȿ{r=d avΔLy׷PpW*f(F>dde"1|]8{^&rk]^u6 G<Eny*=)Jm3u.q,1tʂbWWK( [:ǎ[(H$@+c;t$ X^p<;F5§ilOQ،clFjR‘r%HǏɂ|ITO(1kŠ%|AMSHK)M<#0oݼ9ۄ_wTI lЁDaw:Wu#a~d.N1m1nCj::0x`?؎G'kE [NOz9u;ò"]1L䚉/ֺ;)4i&wb]2Ҫiw:Nrg~dBֱSc%Lh$d imݙYGhS^$\W5s'i.(4y{?lWIqjLUoHDL 6df /:]uE7'zMf%ၡK)]^y܉kľxNz>ڏ`O] ԅ(T>2=A#ZE6ˋYE8a}wKdnfL[ ׼]Կ x%nKW-h%FC b$=z~!nSqr͚v _} 7CZ!ɞ>͍/p#<6cD0m@8F2a,?waX)}psA&밝Ź/I?Bp ?iKJ#rtp6,_H+DC\[Le{ȶ;m0wy: V@*dPЦ*Xr+w"q0꿍Zb6M;:ū&%EZ}x梲;3,'& El2V!&̘ULQtoMR*"O`݆OwcuǴ]`=aJ8q zi'}4IbK7F$wYCNz5*VӱN:r_z` Lrf@_^[G=k@jٙb]Rry A\?F^@UfwL1H#EE,exopJEwLNL6+[DǧШR$Ĺ}Ոss4؂!x%t519+n )% {W/fǎVtg笘oF ]@~uJLz7nt`q4}!R9HyAW2BOYs Z?w5R~.˞ё:َ]hwjG9s}S)'GۡU񺤥X<'q Hp%eRn 3*dV7H zi#b{4Vpp<,k*Zr'ъe ֽݯ$2+8@/Y2aB,ͲEݼI{C_v֪B,\Lݓy~iZMm 鱄dd<{ LCuu H(ߧvmٳ1~<=x)z"Q(eJsg ,Q=͝i$(B !nUuweP*BtyUS #p7yY:y 4N|:|3tmFlJ:B%4kY' G(;Swlfkh~.˰M"n 6ޢ l 28,OZYqGxV:ON[`ߔ/Djvc N)#mт^Qrz'UN ngQ R:&D = %WGa/ xQ"YJp߿E8~qߌOxō$&H4ִ:N=MщX6"ғOW .wEտ٦^姤^TRҽ@e_řQg>`yeHբhCii)s#R*w%a>-N}딇.3>~73$QHG] :$Cqg?=Fl ZNu/7`8L6,NoCSUwI[@eV!n0hkgOUoUPť,HLDu6ThByv({bbE:i*QFhCKnaG {&G͛%072 #Ξe[[E~PsQғc5R{ PKwv蝆<<-'1ɸEϪhk;ݘЌ/e"SVG|7N7@#<͢tꬵi }`Ź ӳ$|,Js1^ JbsgReq*tk;Poi/R~8yJ+buq l*v4ށUOW%^ڳ'|j0ha9z5,Vi [a %ǧu1@U-p·,֣pVY3QOZI9l4I6k46V*mWA}'FҸe/ {; FNWEKuKLYeS?_ڟK6ѩwsҶh>{ۺ!MR62aԹ:OSt5j?Db ,Ez[r5:XR+i!4Qb`&IIqO#| <`VQ}JIu𶗐V$KG@2BcWq ݎ}۰PZ>vN,e"Ri(6䮃++믅8}*ؓs: is'҂_^wG{y/l2 36{^/|42 qcdIkt=C1k0G$NJع?ĬQ$<]z:ەI%җ!iS^-4пEfɥ$ϣ%|ҩaN1</"MUt陼Ww=7tFGobO4 !m2p!^XE cBSܙ.ô/QxdAKjt}G3Ʀڬ* > [O))8CgA D#=X,T: ŚI5TSl.2!p1Og6!vH_K^23()Ы+s+CH_g1,+o N+l-G#wD93-=mp3(y >2IJ$e:*5mN6/h]࿢i0oS4Q W+1~BVHlbqVelNjW=M49%HhTggjTнKǍc Ә)Q'^g1 )J3Ky*⍊AFwL;ūdVN Z=<?'&lEػ+U{O/FCJRiPra'@IDATtb.c~3EI ՀlF 5?Xt!xpr,:Va g<=Gqr5=EU'{!Oq1V=*dҏDNrEhrݘl"8as"}1K="\rR&3'l0BTr6Wq](orsgMw7U#O>xB$a}^lZDH+IjN e8N̨;u)DhHy~;XɴgRzmS+: ;Vh0B$26t=1LRXQ.-*W-1.+-}7Zkb4qk]`1!%MPPη9Q^[Yt{F]Z! i7 ]P֙HTXwg6ȝwbF%kĸ|w[{vq5ro58&fPGT:Ob[٤?ltW17qw?ތ)A_\>QY*݌-U(\ճ#Pj.*ɑڠO`ԃS8꥙4nvf=:TO?1wy !J gNv= gϐ޵3]õ>p]Q`կ$8_A_s. $!Jm=wt-NTKQv6T È+D@s}NKIw{z +aDb\ #~%NX-hڨ!".^ ]"غYNfH}=.%,AaӍe-[`=ym_^z!1|OAu(qF44Z6#Ƹ={k38`DQklԇ6ҟaC0nzuk }ofqO.kl~o|x㍰٩w.q(<8]\;\v?TT[M(ljgW/k=qSn.:N݀@dddNc \~| > ;:~W\sp$- eIr8] 骱ZFלvxqzG#|[pe Tl_" wP0@<>Ia|?`qX<|c0vRg#e}PR*32s8G#|+4MU3 駂(?&ZZ@`J ϻVN*Gg"n3FOwh̼Ts8G#pAI ׏ uyΞ]e4C#%8ej~6T+q8G#FtZ F~O@"#% g}/WJ:+1e0L!CuN#F>% ({0Gp8G OVϘ!+_t=)4p c&Xƀ!p%A;d`#K0Eb΢^ G#pz# ӕX\z,S!ɔ1eLb]Ș3RAfg8nR p8G#p8@ У,l!ϔ!!!AgFA 3w! +dGc:pp8G#p8ފ@g aw&1CaLɘAY !ԢG#p8G#LV0lA.,ٍ1зo_Az1q:TkիWpA~ˀp8G#p8o) [Գu*$ŸI0F;Ls:$!w! B\"`+mlQ,Y*峥X>  ZK  5`An HdnfC!ɜ9ssvӨfY Ji2 3 @;1qJȧoHHHHHHJhX:)$@$@$@$@$@$@$@@J@eHHHHHHlP)`I$@$@$@$@$@$@$YIHHHHHH6PD$@$@$@$@$@$@@J@eHHHHHHlP)`I$@$@$@$@$@$@$YIHHHHHH6PD$@$@$@$@$@$@@J@eHHHHHHlP)`I$@$@$@$@$@$@$YIHHHHHH6PD$@$@$@$@$@$@@ _yE… p8(**BII6F!$$ Cdd$7n\(JpEzseFe汿)mIrQH$#1œvؾ ;ƠMkѣu螘O~nN HڊjJ#!&>=c^9r"I˼F]qlpAQh;yDp5H,Ʌj||9@f# FmQ\UJ ?::hP7t5EA^^?'&&FSTwGK|9+n 4 "Y@eSm,sNQ]j-x7OaAj3<)w'WH0*RG::TJ.^,@ƈu]:u>L? 1@%01SÐzbrzob{cjeF.Jpz9h 30%xG%"fz-ϦHHf8v:UVPG\\4ik4uȧ UWUCX*(8VMl-.)B05:A(QBHNQN,_ x'HEJ2/ hEBʡP7aZV#kUŐQ)vGYqahg3sKkK֓sQMXUUo딥WRU!?+[7Rs,d LMU||֫N_\W?U˒ @= `ʕ>xYBm HHHж:u:4hQPPVj+ATTΜ9Gj BOs8vh?2 c㚍H|de0(Q`l8BD-v֙jR=g'Y{FLHYxah#&qLgX4bVaCʹjZXI9G\IZfaز9*],͉ /^b !*2Aah'/֣iX Q,W"`HHN xJuڕ5'Nhz S! %0߀*:srr4jm0 M߅V Nʗ6\Ty,Ya}8wVLȸ $rnw l7R{Tˋ:&xWǝ_킎itjCʵr+%4srk WQoO \,N<0C`5R@QV22Pu{U(Fg两au/+{xNX@9 RljEo]ZE_8O,|eTsa>ù ~#V O?=v?EEItT3ķnPA+gΚ³88'JC9.L-OO0} \[ǵp&9d$ a+ԥ2v}+8@ۉ}؝=1 o++◤P9&.ƽUǣw+7_3 DLd:Е7F*{zDYJl&$_d!< 57c>`W0JT9.к.#ct+UHݙ0)TndY rnٿ {l+ E )A|Lߎc<qkU#?ñm;tFe9fTQmfX9&b;O$xZLK]!!k]*!|'s/fgkٳW|ndIwХp䕟Z%ez{q\<@]=V|hw0_ؾ'o,2}G/{*Uͽ$f3cakA!1T{"Ll m!H9N+'TC;ogjxA$@J?!)7@llf!`(ޚP4U feAP3!Xϑ/jMS wkV*.(ti~,Z M߸ c6O튿b)}4n;Dkv_Nf:] @ /wXraAXvG^q#Ц\{&R<N|M0Rge(̌IղiX:nh 2f.)6=US*m<>)~ 6%YԡeHūb.gwf zđ6:_Зxz!w]Bx3!HktdoĜƘVzof-[kF">/M%y(}̏vr8Xtz[?mrnf=Lf)9g>~5_|x#zPG3e(]אi!_Ո :#CNDIo^yb"D7\,~uK#׻ks0lk{_r^’G g..&̳8\6ԺATkk-2lw?:%ﵶ*m۴$fn\_; kѤXbhBi[aŪקcLC-jl >ܹ] YL>0B_#"뭱nL,_a(Utrd,E{0eb ;S^O#g,'oш9e5c$I)蹩/}gلw֢ý% 4zhړ}%+6`+:Pu:UZUs! - )|[!`퇑_=o˭(؃â!/˥!8+PzN lw+ў ߕ`.;8 cVdT%1LZ-绮 w W9f #quH3y;̸5Ue#Cc87-7fQw$'3Kkui0c8bP30{0~ig2Kzp7ǜn!{qyҟlrK+S{?G-|AJ23WoDJ2Â]nZ0 ]+"9C!jڴt&um+5_Z|+T4fzk#7볍Tp'}V @J㵃ñU!P]hU 6+U bd \]. yĎMB}eFt4hF*I=o$]ƗڟW'z\1v9/0uTQ$]teCp{t8=ᮻb츱`YbIXY6Zo_`݃`vx O!,ňKu]t\c j]VX* {dul,PzK~]N&>mLY?y&󎜅=ٚc쬝XpyOXkz~rHY+OP|oc0 -Z_!\~,rcvu>csTuK"άld m׺ę k5R 6BNnv_V vw& T׫UCxv3W3{ ˺~_b2ߌ!ӰjNd=[W2̾UdZ{Ut Ȓrd]-t$umXqB;=mJC-۱ YٮK@♋ib.X⧦[n̹8C%ӌ")Ɔ$ha?۶F4^9YH[>T8e,ut˥%p ;w+2z}k_$1ČeiVlܼ].)~;e-f@,Iێ۷cB c$@$PRM7]ycg1Pm6+Nagi/<Eگm=Im;ɈumƋ~KK_H(C;IғKoi= ) =J htw6\ϫ\o0uPL3먢A)DwVrO< }?[1P+lzCXȂjĔv[;4I/s/7"u]UP 5~[b6x E- CqiA8&L]:K,5b}9ioIokohd8`|2f*Cj݄Z_$h1 '2jqnLNTU)5_<#[APhn+5@F]qjt[{>t+R_Wnf9ȑMp)Hס/\)TG9-5 M}PK/*XXJzDg ]Y9H/HOgs o=]#c'yQm"!>A|/ϫkd-Ƨٖ3dӯBd|&î\.y8 o=)1^ɋ仟Yv4{ԯYdçH1KWRq3l_]4zw_o';l^V[- kudo߅3ē5J{~!bbGi:R"DzkxcuL@ʇ M3H@S (_ϟGttv+ͻ@TmW&cC^nZcMNx0n-h幅a=h̊B< _C]lg }$G븫vs7 nuUF@SqP㲖M;~S( 1C8]8O?Uû>{l#:`dun*|k$sdܢmT/ĺ% a'N'd[YP׬ko,x`Hfmg!?V!wgkbl<\iaSs+ve!0'Msn K3וi+{Bqkd-oբ hUe4:3 -'~s!,, kkjJ),T[Mv; F7=mq}d=ۖѫu06UB|~NIB!wNwݫ'j`<.o&8vl88#['O)`9{R|Mp0cN\|U4gO .xsm'ԯc,]:+{*-Z%wknt啣Tz\y:8Z.ՔD(6Ե>[]^P4 JD q* Ezw-OOM%ɐYc\hN]uč=l$7u7#cUN]k_l,S"N |&Ud/җ{'Wi9f`iV/Z',RBF ݎ啱_n=߄w}CPXod61>y4/bfd1^,)6tb?_*~-n +:^Wkk/0BZ#ORWt7tX/ߺAxdOPXcle]tᾑ!r/?ZDs"~fּrFbĄj9 :fwv5nn!0ɳ00t"}h.S9KQٝntCVY  2UN%+s hGMj,*l0aAc/`yA>}wg< >-|tO$Z}u5{uG= EBpٌFy|Tot(3#6:,O_=|W.#sY~r[{ѧ"qC4uiG;y̪g\1p'vE؛$B4O¼EƇՏT̞$ [Tޡ"[ܼ;[-A+4n{G>MGSEśp_jI\tD3Ѽpl,>.|w|`: @YK4~%׆jhSEhQM?}ϡ=k>.=jE! ڴDEunVxWl!'R{heۯ5V⯿G9,79cH})W,w H1c{[hlڈcg!^4=D5}ax'KTײ8zvm10Is{)淗WӅϙ%w۾CJL$Ih~ (̺! @%P )}6U% C uu{?u鶻ЯB@k,B?ykSGq7jkZ1HyZeԩX1Ǽq_1a;|"꫞rG&Y^% V 'MKAR%d.72O F bw /|xtf~[I"c=qyv'xۊXoIZ$(ľB N0_{wmI.\ūt1 7/t7xc;؊-׉]V'A¥6ٮ[[p~[x @ ;PmWt̍Zvõâ`{OwphcGWy:(r-eFłHU f& ט_˓:?+ Wx"GY^'&cz>W/ -ߖwԫǹ e`ن,v#d/Oa"ǹJn'sq )mظ!Е7Wҁ$eEoag~ C'WLbͻR(b(@e;>gI^94hH=}{{| R@ )k;6U'DW]OufqJ}P,{ rOZʜùBz_垓8ZVb8B~acrzřZ#L rsޙczcC`H؏H1> /6AL1nROMtr!O0[2k<-_ne&Z5f T\_̔:%6٩=5W@qe+:3Xer]!jeBwB>s;\ѫ>zߍGFHՆSؼn-% Ǐj;.m,thQoH`4A #tL!+#u; ;˳ZS~_J ꀻf ٛFο|"o&V a]"/29ȳ{O(]Cd_jRǸ09¬Uzq=nm0.t 6M#)cWݺb# E^`=R_mW#cW8ϛ4lL[c= |g|7EL*cs 4 WR\1~鮩Tlt^ 벖ye9Jixd;Yo2!c ̥,\/殬l*. +0.4&" k= Y"u&SЗY eT$Qn22ؕKTOR?L)l'7yf vK{Ȭ1z>琮[o,:eG[tvu0R1 "P,~ՀJX`,/(p?{CH% YLGZ(Xڐw6^-ݢvzk|)fIAnqɣPlJq#Ev?8՟`tO?ewh_ߜF<NZYqcpW$4 _P%}V (Lv5OzCg@W)#I^u_Njۤ(dS,M~M0SL\%oLL֯TX6m~yMJji(Sn5N^Zcf~W 72cBHP1IC̕7rx^B1H[2KnjCO)d+ʓò{b2 iY9ؼXr!M[謓/ćfYo$ѱ}3* 0padD`xmĸ{kwH2-P$aƒX8ֽ^os r?ujj 1 EF%K9("vTE=!I=N2DT C,/L[Om?ΠL]Kf~hVSf,ÁS` WK;?,Xc W.HH 4%77wͫ񺠠1mժf_&ʹ:;PuW#Dz#SW)4E|8s8~R=#I,",'({ 9(0B"jGТy]*F1W6ۢ Ad7ʯe_|#nj=_8гqYSU}nF_t'rg`NpɢtѳBk0c)hFO[>g\:T-"_~^(+QD4GPfNٺ m1Xb,C$@J ..nZS ~@֭m-j:Ο?Gk)F.`Q߈ѝa➴bUK @%x([+bYRYHH (@S*~ 8w&(jKY>UH 8ũUa<>Of ЁycKc*j{r I)T#ec'4S~PGu[ V\-??-ZMx +B=3oߴ<y-0=tvٺsN#ݗ @-w0M1uCxW :UJ d O{5e!U!0cUzA @$@IvH E$@$PUR@ :** N™3gĉRU- UrjجYrfy zE  QyP~LZ^;7LжñlBI}R#r3WpMvHfDv>L֜ F^3V  'P Z(Oքv$P +oܪ8{thڴOu0 Ѡr*&Mh*"~2p*B@mB q      "PmW#""4S@ 88XP v C é2@ EHHHHHH {e1eaM1B@QJk0ꬔ굃SACbHHHHHH R@M07֬@өY\xQS\pA+^;ȷ hHHHHHH R+%ԫCy WJWJШQ#Ͳ@)@m9eAg      @!wJc┐OAߠ3 &аtSHHHHHHH0# R HHHHHHH P)1 *l0HHHHHH0# R HHHHHHH P)1 *l0HHHHHH0# R HHHHHHH P)1 *l0HHHHHH0# R HHHHHHH P)1 *l0HHHHHH0# R HHHHHHH P)1 *l0HHHHHH0# R HHHHHHH P)1 *l0HHHHHH0# R HHHHHHH P)1 *l0HHHHHH0# R HHHHHHH P)1 *l0HHHHHH0# R HHHHHHH P)1 *l0HHHHHH@P c$   ?.]\|Yq?#`OCAZ6unԨ6Wr]gVo4Iu Tŋq98**.0C!PDDDqWU8S//cO^DžKWUؙ%ְ‹5"$45MB6*IaHHHH (ٳ(((-Z 66JPdJ|IۊS@xt 7<t>1\(ÚylQP>%Fj͌S!(yX Hq]6 u♓pT4W*:Zί_J6kӫѶ51cmPR6(     Rk/\9,u|~2i^DY6cEY}{op_ؠN~=W+A)>j]. GSqiaD@%a0^E;n`p7b;EX %[P}Am9\"|+,ݫ(I5 E@...#xtB@ uJn,k8~y1@m@ M83PKg%KDܗHzȊ*M=ZݯkeRP]_UDژ0,uy$a̷іL7ɚXP2(1'   6l>,$OÚ%axR;Dl29k\|nTg9p?T{fG>k0nֆ> *@ sA`4]zv]-Jc7-!զs{VI5/թR1ιFǧڱ2v"x턑Gg*h29    +F[ß]54>49> o zS V?-2G}#?%.E.3*XqYWYZAtghTaR GKk iz͹\A"r].~L~:>HHHH,bͳO'D!P;O CpP*kTC֯Ƶ?i| b@R~2oU0H,LLȻ |g=XRٚD%>ܩ}m 7R109}36YovY|/0efNkd%XhK7Ѿ)v2_+GO/e5O /ɮ%XkBu*ME.#}vկUAr/_*5Gz+ݸhǴ.EeZ#pJ N8p_ſXhd8 1r![MCOsϺC |Zr޻ߓqDG^k-W> Z*u ß-I/cYЈan9/Ã}!,aB^2lG/nBPSLh6,#qaMf(;Ń҆ן@[[ lFIjn.{CC4+ J;ҫ,u['OpR]zm$mF"O(n&rj4Fxd.d/{cB~_Jig⣍pD|3iūH4倻qB(+uQOu/+:eՎVgo pHHHH|ںɼ9jͨK{X,1{].Ɖ[X0QyY'Pp4,f;Vd|gB5pB1U.z qС];Pm>Q }-m+2 7[sWh1w%u hoJQ rWjFIb1e=wsƣ=·6j~U\s6hh JL s5  @E dY>`мvI64 Rg_gMϩbDS›?Yi纎ŀ"$O^n܏:%bW`V>?iee|qan ,XçǍѩK#^TD9TggWeN퍽mĻ5ݥȘ]_ߌH?lT37yԗ0p<:%_]\nC͗OytbrI/$=.RNyw6h6A8re<#ЮlU;#%B ,QviF;^Aͳ]@sl@U_Eᯁ: @h?16?_Zv>SW4/ #{Ĉ"-~v07 6fVA w]jwd`GH ^(R͇bޒqty+P#JTSc%0;)Vt~u+#8=sŽѣ.L?§z5=2"Dv2mcV>)terf}FnEQP\Iu<ݗcvcQ|!ѓSWM\.W wծvHkkPmk,E$@$@$@$PB }ޡ!?rYGyp?/z/ryw& =sa|nzxq,H'X?ioH0DD g_q\J3!#%":/j'i ꯢgWrݯ99ʜ%!LUZ9Et$=7w=vo6\j|t_#9,*4-!nlYjͱIAt+ڮ3- Ke~m$cע mhk-UW$À]H6AIGٚ`2m|k $ ;Kt|N=.љO=TϕHVS==^W'/\vn6"pC?e@|VOQJ@'!$q=̑ouY*\<}LSԬf;VK%Z5WODm Qv %~l)P^k  @& &GĄӿ ~ QB@t_V~MR!oH8ɒ^WhmQg"Z¡_}׫ sF^ EZT5y(T QBpvo=1Fvij^Q(k|N}kT vtt(+wɉo^0 "Eٶ٬D|sYTOrr9qu}IDיHHHHl⮧v@{4#Ly vb7S;`f!.P:4>cxcILyX{ƒ7v`c`^Xǫ\݁#Ɔ k%6O-VSyWƽ>B0B2AԟTqxiL:oHz|wΥލsqx=ծ rLC!D=I-Az l/\r|-2ҌLcѬ 6{<rx띯תz?4(Jc(~tGޏ&C!   2N ٿ`!oE,~|cw4 B,`o z t}?Np /A,( l=tPhОxL^eZ8f5 XkD7D'@psҔЬ}"68&cύӰt!}=8'd7hٽ+8Ϣzf1՞4*rțj0n9RHAx]kU h5ᯁJYHHHH']T<$V&YgV`?vMgcK쉑Xge~{Rhj`=}\|X2LxzT_m܅^2]]}~lZ<>&n1bpm/YX\~묟1M)Cu`Z\.,2G#oebR߁_J!ET!sw!d^KYfk!\U7w !RQ=zԞګ[C6?}zCߒ9tay,|X-C?sx31Y󩦫PXӇ.< ]`4 K`(٤%aoWbͳ,iq#6 7u-Dyq+DE[4Ǥ ]R`4I?Vz2">/n`wbx\ FQ@9LETfJxU_gY.ixn\i BCp~1 {aҿh+kOq-Aeԭ/ =Z7!!n-?Nw>`oz?pO&WD!ku~aZ#965Ƶꟿ5rt]௃HHHH>EEEؿ? V;dX/NjBо+vVns `1h1 횻Miy*[^pC#Z2t@'8s >s!ѩcG,_7ҐoŠW!{ϻZB@dE-xn/q57xCqܓx: mAVt ٍ/;Fk#ݟk/_c0 ռlq@H+ȾOX^F7iV7,G$@$@$@$@uD UW 2!J -llN^VitV3Ue6G#`uO9S򌠶k&&<ʍK P^O/kO@xS2av*1)DyW>d*e5?H  T r\$@$@$@$@$@"KjZЄC)gFaRpA%j*rGo&5P)3q T%H^"BؐGdJ@ ٱVjZu<鷾P]¿ Tu@,E$@$@$@$@$PI^M(IQkjCXK΋EEj1:k\ 7}@?$@$@$@$@$@$.\+;_}n~/LN6KvIԗx?]s]^Cু~: @ƶzX< rK0o ![ yT7muXUuRHHHHHFCbbszpl5Y-se*j۪JA}=jY5IJ˺IHHHE4B@)sue0ץL뱦S)PSdY/ @ (j +<;\~7>`( *i9V      RQ      $T ls$@$@$@$@$@$@$`!@$@$@$@$@$@$@$HXIHHHHHHBJ FIHHHHHH Jˁ4hHHHHHHH Y *l9\      0P)`HHHHHH6. 0HL$@$@$@$@$@$@FJpHHHHHH T $x&      #l.  (*t9l      RkHHHHHH:6 P)5@$@$@$@$@$@$@JJxHHHHHH _Kȵ@$@$@$@$@$@$@Eಡas$@$@$@$@$@$@$@RHHHHHH:6 ( p @@N:L$@$@$@$@$@$@\$@$@$@$@$@$@$ЉIHHHHHHP Я ˆR ђ Ч *e):7 @@~HHHHHH P)ȳϱ 4*z9x      @&`(W WN$@$@$@$@$@$J@uHHHHHHe)@+      @:L$@$@$@$@$@$@MP Z GO$@$@$@$@$@$x4J!PxcIHHHHHH (KKr4HHHHHH@X\\|! @p.*:pᤄ @p.糲HHHHHHKpP 8-EGL$@$@$@$@$@$@C@J #v4? r\s9pPp$@$@$@$@$@$@$XnIޓQJERaa;woѢR0 ]v}ꫯ}޽'dX%Rk|%ӧ3E)ֲe6$9      HjW_}R|S.*bwzXAaaa!rHr4 C굃9ӿYzSLye!)Ԑ2`P@)䈐#\P9BPT#qVi*6r}4]H;ۥv`45}Wy#]]_iU)N @M\J+R.wW]^q5=uYq{WqzV}*c^yUZE9qN rˡ?fIENDB`glueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v1.0.0/0000755000175000017500000000000013752535025021107 5ustar noahfxnoahfxglueviz-1.0.1+dfsg.orig/doc/whatsnew/images/v1.0.0/polar.png0000644000175000017500000050320713752534424022743 0ustar noahfxnoahfxPNG  IHDR`D `iCCPICC ProfileHXI+$@D@J-TATBH(1$;zgQ詈' gAΡ=aA<,P7$y73rQ]`քT `s 2Nll2]\vP|sPA*y2yV SyԼ\j>`Dž˳nzV ~U*H1  ˛y .쌯|fƐ>?ky 9Dgߒ\6XfH wK3cTN"T*VF$QS ]HȦäQ}F$tH 3șr.G3/XWeߢIhx_!Shڐ 9j̪H̍+T@fjXZ<,Nc/S 拕%h W"v AI9~D QE!ܱ64Q/vOV#ˍdQnJoDQ)S&ӳcc pA`%l`*nK=@h43FD@14/x`T ӐV}u3rcy ʁYҡՒ#cu56?uh~Y:Pb1FtM p63{cB;pkXM,@8q;wg&p@'r5qrg<2;+ Q9EUѯ룎5cܡo~Ug!#a3 v,8րbGU< Ɂ~$XYSUIkkG(M/P`ܩrIŁo'rsus@NQ?^1]~>PEǷco_>^(j@QX~  $0Y L|Pr6 @=8Nptg}:@ qF6"QH#YQ"HYlE ҎB#]K45CQ(堑h: B"t!@=hz^C;gh/0-Yb.b1X*ɱ9X)VUaX#`X7' ='</:{:L%YiB9a: oD"hOwc 18Dl'>$H$c3ɟC H%u=ˤN;قF#br9y72 KRb(B 2vJ#"GգS l|jzzJKKJGkDkV~ZiN4.--5n^t;z=^@_Jߣfhi jWji_~CѱL))9sQ[kѭ={CW7Z/F/OonszOIvBO?d` k!`,`lgbt xe{ z = V5`bL;&\<0lgha.{k4(HdTjcqq z&xi&LNt77\0tῙNq3M֙46gg6?feX8nːa*X-KSKV6>+{Db}VwlL=66lffKeۊmڞ}kgol]S{#{}}CCCUG#1q%'ITtur8otnA3B:j  ǥХHȨ#G>e3*uԊQgF}vtuz{豣G7~&ptNwsC's͞^^]6o ر%>`>G|z/o1cDcyo Hh |d$ <v  ~6`!!!mCYeՄ{ o DDF3 xռcgmEG|%j;nո;ѶËYs7>6?+?7+L<#~J7 n':$*tҒ&$L0j RLR$) ԤC'ؙVv}M6;)) ?cU ^ƆWVL$\-Vdg|埵*K(.wKٛMݗGK;,՗H[O>],+uGw($ECxoU:(S/ (,|7-izӥ[g8X9Ֆ-m"O=vΙg9{y ^Z=[6.\jl~rWBʻzZoHqSx[/~+;;wu3W:: 퇂)}\œnOtu]cdKss?Wkτ/2~{o-}gn{3?<ϑr ˝S%xL> >O>;H@F-dU AuwjQd}F>6gTM곦JlQEmTϡ_mTxoj'Ш pHYs%%IR$iTXtXML:com.adobe.xmp 1192 Screenshot 1160 {ؗwIDATxXTֆMb]^bK{bD;((({oJ&C4& M Μ93wksB!BFO!B!P!B!P!B!'Oܹs'$$ӝB!B}A t:e=~888w KOO!6>!B!@ܠo8Z_,n{.!B!/:ՃA^DŽB!B4(pwD'B!B4 r@!B!q@A'Ky:!B!DB?B!BPAXB!BXCGB!B4w޽{X};z{c?WlMs<]{oo3f1~FT:厧vாtr~؈xB!BJ[PƃVa}0⟩c1A>Ä؈v{XbϬkxqvn%xq]ۯ\Z}סap&a=0~F܄CB!B)-WT2;woPI̝ϜgO ;kVozxǷ?>96z ;s8~UEjէEbD(9n ;#ZR: KoB!j&~cGg7{r!ߔwyP$kcH;.Y١Y//OKݰ3B4??EuڵaÆ-[7ni};L>}͚5Ν-## &oEÇ߽{ݻw*B!" 7c|囤43:GyO!Hw9;k1^v]pG^Uh5j$~޿nom۶533{JMM]dt/رc,۵kW>}`ٶ`xBQE_8PEMgV!shw_d%|<֧l}y esm;EnPEf}9ƿwqY ?@F-/ @Sl7o-7n՝4iw}'vob7͛#""200h޼9^$pٲexpm ƍ j^_A/3<[DNqb[o^fx*[GܕGm;EfUh+f}!+ ;BB%''K;vOGG'#lpSf|||[SNnK.ͶڵKKKUM ]p r[ϜB)+>w{v\7Si~'x{E!%l%y5\Mi~ޭqw n߾߭[76))ȑ#VZ9Q^t wٺuקLp!? }yX_x< "ܗ\O EywIp_֗H)@ E*mmmW3ӹs縸:d̙B)?O8Qgb{ӦM?2 ` T6W ʖb}ӦM6MGzXxb[R!}{-+'*7anQOޯO9'YP>~Rϱ(Oh4{rq?Ewǃ%M… B{PS(] {y=,I*n:DX/=7l  ;lΉ}=*N:%Zz(db JdPBmWEPQ$ }$"xB). )~w 7gWOIDv1:RnTޜ]ee?~bO!~n Ey oȻK/~J⇻Ax!B4 VqFp!,둑)J]rEy7jP; 4H;fso^8:bU !G^RW1^PӳU.~ RMz.wǃBx}#F; ~GG @z)-vv؂pΡ!ڵkqߕ+WJ!~؂]⧼-ϟ?C 8q/B!%\ޞ"P\WP\F! 2][Ν *+ SܱgϞPMlG< yC_~Xk?itxB7|7([ nx yB) +~ŔY$+TϢ)qG=ȤD;kSesC> צ#^զ ;# e샻{2!hJ^tASsP藀^z6 >\*"V١(@d5?QEXw7{ltP]?ÇEqݰDJE4ix2k֬-܃  v!R^wT֠^oS1_7c<;YVdz+)[gKw,_١Y/xe}wy BHrgΜ9aHgtQO"ޘ>HZ>DrބO`wŃ"RTR~"_o+ɭȁb%umZ  ;.l~Q}^߿P>ݰ3D!B!%OQO~Mу=I\y5ݰsIFniͿmkH<|\v%Z^) )7a솝yB!BJO$w״5{vx@?vÄ =;avHzst?86wn8[;`{vQ#jMРٛqvnw!B!($ޟpN!("WlMs<)Ҭ]j-b#n*p3aʷ`!B! 6!B!ͅG!B!@ !B!h.?B!B)@!B!DsB!BH9xB!B! ŏB!BʁB!B\(~B!R_.C#ŏAP'B!B: B!P!B!|SB!B!j7B!B!jŏB!BʷJ!B!D͡B!BH'B!B!B!o{L!B!D͡B!BH_!7?=\;^H關鑔|'.1zBe|Ÿs1MuEЃB! 38ғMotsWD߆{r|$Jf);.*ӡB!Q)Ί1n|ᕷNXl>of.63[ +я4l#91A5-)i׸1_}%^z# 9(էoDrG$/ ytTIK,~)p_LjVhJ"Y.NGˎGʎ(};Be~/ŏB!8LA!Ç]kV}Z3|q5k/ѻjӶ??}OC__V6)Yb}㿨f&~)Ɛ]KևQcV3|AZrUGn5Ab4IYEd&gyre/aysz:g#d-!u(~wuu30<~ԙ({}zCy#=\w }C#!=,0fJ<|Kc-Sk@ו:eָ/*MUew+},iVc—Ts†ZO`.90MvbjZM嶑#=`zk7v3٭d.m߶=,`zglևsHHr<쇤]#f5O0\/O.H__u׬ۀw~~qۮ0t u6m8fƅ1q)!t׽{K!JfSݾn܊IZmڥ1*1:CS9m:}f<^Zf hc`$^#pKB`}RϹd>S\zLt2ζÈu`;/)!^cfm'~^/N Dc툹E.?v8AsĬi^6*cb6hmZ|]{5@ҳRS<5]ln7'[;CeCdd˼__jjsw569⋗̅a]n8#G7m38pV] vkdV_x}t$DWzS,6mi!S}q)?x !D `3I,ǩ4-FЁM9dſq̪[3u:\F!ss!~?Yvaz_oUSIns4XU>PRDd}/oF=.#[p}bg$slDvlul\6oǹd~Y%c{eZ_!F蘜 􄱉q#cU>"S^!(jx5kFUN8fc]ƻU_gik) uAo]}rڻHu7؂p~xL!@q_A_Zu[zxr3y={om?k0bYݧϪ޽V蹼Geݻc,n˺u_ڭҮݖG],e|{v_ViR44Atxv´]jB/`}Nq9 gqԭG~?Ԧk݉S3Ȣƫ$-XyͿܾFZ^6N3/v3t1۫Gan~EY][ru:DZ8َ%vM._+Nl]lyl+?xH!Jym۠銅%!-RSw+;UXSlkϤОO99uϩe99a^]=2g~+{׊:>zoYi]J+פeYH;'RwTy6o G̓-glb\}\#=pyﷆ<1k*JPPКdq?,,*jHrrpHp@v֭,W-N=cv>_ȴD/@6)Evݧ)>t" ݻk]oI }Wd ɴ3S# ΍LϜ9;G淮; 03-ouPW ʞ>1L09sZkdTLRD}\^;vI;[{BH Bu:-`@j֖Wpp ]eny񺻙.,\q<}䒽f7i6j~ȹoـ={"j?usWeq!u J-6֊plktypkX111ݺ}z^eK=ݷk>,'2hٍ;ĸ9u̥~|2xf6ut)dûh-]CAzɏM:m3aΥ rV㧾'ÎWo7BkVX/O7$$&rBF!e,~J{NCm8w~v^­-mn}>9!jabJقMybFckWk#W` B49rW6_Ҧ-]M? KO4ƏCo8Ħk!R>1.6yKMNVc_mlYC翊(>OT#JM/{#Ga-}o;F<0&Ayn3#1y{E >sO:'kX_ԒIv殮[1@mm1DH!99E5gRR*m&Ev/pԱ=LA+H>?wlyPwܹW>@ 2xX4u~à Q2D,t΍%5kaL7D)P&o+H;]ɔ}w;F5](_a'NIX!ZN#8DdWÈv65Zh`٦v x𘫷oltrr?V55p=B!%n o#hvq;MZ/m0{[Ý< ~nT5ǭ}Q}v_0įƮjOSSM`}ۚq; dnM8M?ؤ݁FmlK&j6U=IqÇ&Dс'&l59w}ͮZ|.:XIz`ꑛ~ z6]曙Ւ}\z {A С_%9[D[-@_AywLE/Ӫgn9#|pluZ\d^SB'?bQ'6ᏪK|'~Z8lܢ-)}pa $>? _VP{WSHLL:ؤqcC-S̢dxe΍Vkݼuons\Xlf.\x=̹WKI{FG#t3,--l+!1eO`Q볯~QVFMjth꼾ZyF=s#E$Bȹ-#?C$y4wAt[B[' \4ڍ`0Hytgԡя~g}?3jϨu?gԢQ~FM5gԨQ~F gTQ~Fuv?Z}f^yk6&~k79ncF8vjc?% WD]hp{%LuU{ٶc'm޺?ۭDFFA=&/y_HY,TN_X/׼MNع_ɰpCÀ%;ˆk?xsoX!bv".{:gtk<c,YXk}v2mFqy͑C<>.&-jwی|%~a{T#(ש|k;v_,C?RGwM>0Ed﮼W"78I~mCc2F&~{A~8vm[; 4Baeu\Zmڅ_GEllFtKH~)~Vz+S1\s-^_uǨ5wߡ?p 덺|8/BHɠB7z E7e!~Htr`_(λê?Ə+0 r~_ˊ=W@kpaԚA֧ݯ)ϭDǮc7j=ry-^9}#48k#]Ww [U^q;w(~tϘZfM}O*U}߱ӶCG/;DĸvItNij﬽@^*VuCգ;|tɟ'{??wqv)v<=4>=l&9/<0 Jό/GNGlϕ VNv{3̆n]2өWCxU>W$~@VmtF Ϯwc^>E!ҭFwE$C$[ul{]P#N] W#v#mjy#V0KLϊWD_kbm|iDllsl^:QLo&]ilTDmz:w-Wj^7/˖vjto37ޡw3|~9YKRkO+ }1993B)K{)H{{>%eoϩgگ8j_Owe#Fo|vHSObWw,ٴۀin}"ӶM Z ַu g\\>}Ôx|~qQjt6r=*&E[}ƺy_׼uCKqw0xqMtIpw gϞ=yݻÃ==ln)_*rwS"6,FHeS}gU;sd}/Zu3DZY7?1f}-bvNNp-1!)50p7л\J/Lf7c4_1p3eT:" M^uf$++įEj˻ҴReock39Np:XQ}+sU-[Â6l8uԊO@z/Dg[Ϝ}vhssD}X-ߴ߰½ok Ódy$f9!3򱍵ug'=+waMH]itm*O;)çǂ۫i7ތ;T~הpuѥw=A>BH_j9D[2eO_b `⯔:echt_[v|Q-9n}NKU7Xߎ= ncKksY{[[$y$ݹ{Vѣ䔸__O7w;k{á'b_?tӵ[SXgtݮk;f2BM2O`}[=ҝ;˵sco:܄;e-L/[8RpXmFn|t믽A&$B[HК=Jf cxsLkd>m9~-|/o|:竾=ArEoBVrfD.↛"=5ͺڹtovi,"ůŨ}Jth׷M#_ߩUnJ!(6ܯOBJk&99eiQ7yʓ<wU[OK]ͭ].\s:opYsӗlO^16~ձ3W?y刑l#!~J6^Cf{9`sIk5- ϟBc<l 7]h Nbb! KA !~ts'.CA,KRRRЫѭwO??l!=lsZu!wsJeo-{k]suiCC?wX[nua_?6r/;+_R<-  AËQ2ECL.7fsM]y-xƭm3e`f\AQ6ixF5=,b}}ܮ6rWe[pCޱ=Ktl?Ա}c;Z8L uu{%~)))URkmzDO #""TYIJVceU猽{ >k8*jtf̾˜_CtjRe肈zrpCzZgyہ?8hdBvΚ.{r9].~W§BH_J o㯫z9bw_apM7/Z^r1t+N^5`cdfsV=iqa_5^}G _wX.~s!:,eE{!s|8Vq>thX'3_$OHd?YYYKZJJ|l\xhh;vqAb"K`C#9%ufy4CszN߲Pb>wxJVNv K^?sԮW6 <#\S~L-g%ܵOIurDŰ8+aBO4iǟ}V4sCFj-\߉R}Yͯ C/.M1>YμjqHJyM2jd|ie˚.6mõ8yl,g tl_/>q޲anN0%329Bʂ8-U1cg| g/C"Xþ= d OOXN9uUږR=!~#wd\G(R0OBF/vJU=%׆w΃} AP& B7_r0zI/[sVYz ƾhHylcw |󤱳mrQO#YM [^yYs}0ñeuVqkvX'<<~?Dow!AAވ :Y[\Y+_jGrmZ-'OYys^'29QɄB %Cgeߝm|;o Ĩ?gb7{7ם bԞmo&LoږS7cTSmNΈO|Mg}8ӶuC~ʥK7l]]2ϲdn:2>nwդ(zyH vPvR@G'7_0d@>qȘ I?5MRMaqp?l3j` 7D0NObK?Za6~ALI'~qZ@.+7:'Z_ي y p@M47 ḻޙZ8dCEgE"SeX typj㋘$!~ .*}FQ2pߊB,_yol{0\vtl!ƛccxF79N>F3>l8L52tXAcM*ƀ1ǘcW11=ڴhӞciQFvU.L;44ҴbtiƈףT5FDI>Zh؀K8BvF_52܊w3&l(MMÑSKy;ZrW4Zwʄ<=_sGKm/E!/T7doÑ[ ' P +pgαi:ok\}]VtOK~pWjζ6MN֮7QOYTȐE: _X V]VsB_<]23֎Cg|S !;||Yn1;I1h퀤P-{kk VWo QGzՇ hz9GuIr]viZrsoYΑqn QeGo2_eyY3c$/Bz(\ AzGy&d>Oů4/_M-At6ty{lm@"Ɋ]E  ,h[;_':]-wn||HxϜr0C/\UvpXMS[O}_/I3{_gQl¯FL¯{NQEz=W_/z6W'AO>dߓO6*/X"x6c&GXjuLu=ۨ󜯫Mʙ-[J0C"n:VUfYxt +#;|/[]rvsusIpI;2,IMH}'=7n1όyj؎ fgˉ ;QǠFv]Vf 9}P BKJ碹[/ݚ\qD[5}3/ %:7sϨQIA9ܷc\)HxiAį ߼V%ðiyD"$:R #-)I?jo.ۏl◜*́|:ֈ!!UlKw6+ZVVf*m[?ovl;Ι29f6Ϭ{+]fl2?n+qҷy'˝l;d|㚁,FL .21Ȼa*pO-2{_8>{~Wojpߵզ\m㘋{X)ΪM`&Ӛio?7vٳ;;>U WҥIwNvleY_ބ!v@ >{Yw٬I?,-}j6X}nj0I yFM\76Z5{mj5&U̖=/:uLϞsvpwt x}MO_F/O N<;tz*ԍ F0|89 k5vəgT9؎J%zn`w-<Ϸz;j[Ro{]7q3mhCx ~.Q {K {`}g`}v&z6v9&65x7 }RlCUYvw"#Tlwf/̶n᠜ܽAD/yjFa}n`\ahɄ4UY1p_<ܗs56qJѯ⨢rnEoxD9pDoD83m|/pԶN5vȬp]׶sX_>CCC#Sl 3T~&^xnj?yd@F>[1FY&=UԪmZh0%O{8aw s.$$.q?3!9OVkͻb XWgŇu>v#aޭļBHiRA_5p?CAߪPCʇu}ʱ>a})%y}ՓW\Wkڼʃ"~t!7FmX~%+VЦ/6|;usxSE ywgB>ǂ@{D߄*_,_P:{;~e~yS]xl -{k|B [B6y%(hLjص[=Baեǯ.6\xb)땧X-6rq3`pqNBVW+>#n3w>gv[>̕*Z84uˬ؞m͠^c+>eutr>g15~3+ulߠ.,hpO@D@Ov>ͯB([/>-+2ECKF;Y ^l/~/V>1LFi![:pߗ73+(c~c:+Voܴum/¢{fGy2^ -ۯm5R9;)JzE7_j f:OZq-`w~+uC݇mnZXO|`,į).] gz^_oB)nSN+(u_;ɽv'FW?ߓ|e|]ߒ=٬Ot!r>5?ք*_NQʑbK6񋍎~왧ť_Vu.'sFnG\g8.θj_> mr?<>qc+W@2KHcvh8r;kXqrv3v^wde;/;viQ E>8QG>`ѯ@_MK Y-8O|ܻu7OEpSг!݃O9scyUGKkׯ_N%>COGzVo<[j,[8dsͨ{ TҡGa{e}*"~وM_o/Z8yM&ep%[4shZp_b<܇R=Yw\og9'9"M:Xʨ]n)[l"Wg,*V \Qc&wiHi"rX߲yQn;"w;E !](g74MGƝ/lU>8vĔ ewU/yF;Mڧ㨞Fױoaxҽ9m_/A*{B Q!DE/R"-u.D;Fd1c]/IhCWnŗqDM>tv?A[ZYlC#mB"yk9/>wĺ!;LҾ[O`u_ǻcn>!Ṋ_>w1  z3(*0&yɧKu\  nss@`Gño6cը ooQZGh jtu5I?|5(Z8tn`s7wu -)aMYg:r8[qyH/vwlQQݤ%^_[OK۪lA9p_RV?Gb B\֥XٹyMDt /.Z!]6s2JQngLti\@w5]>Y!pF;%36y_^llָؾt˿ȯIkz3&;,~ߟr~}=_u _xX{y$L6t*7"2vtl0bB.X====wދ={uyذM^߹[^(V%B)nM$+ē\͟R=aFn4]QUÖs/@'V}a^_5=>sGNݤBuӿ^GWtfΎ#cKujwsxI>TK~WY.XrGmF@Q-.K'E1OZ)} ׾ۆ>:*Bޱ}clchE=2o#͏z}m;TY&Gq J"*j" MQ jXJqYCR,GE*> -^pa{u_>]+ĂFnW-ZjPU OskPu}޸!4"XcXwz]qޔ_N-VX_]J= ַnuSپĮ?1D璝n(_V8W}% >rT-+F,zIM:!B6 "iS.39oîOthmslx˼e0o>au9JJ--[g]WXr{~ "~jK6C._zgޟ/E/^}SFwҽܵ}-k ;p3t9m}1'6+UZziů4 eG@l?y 3NUt<ۄ$33R8226i=Z8_t|%BۈW EI?bۗzY=pHpTn!S]nJk:qh0Ru;)"ܗCb|fm0?efcrY+S&]w$L-KޏRph!o{d}.]ԹK7[*߄hW-qfG+}PnWWSGw2$OchLȺe~U.g޲6QA6S6Uj+*ٴs-jP9#~>S (r9t8ҸA}oy^~/ t} Gu轇ouJ /7\Ҧݷ_[X~͠~?ΥP>_9?#Racxws.  PJ.㐮i}k'ơcYfxR\Oa} L:p9o?<8Ǽy!{?|!_7 d?=pz М7jѱV~IvW[%^6<|;༷+F~]]ڱ ĺ>ko.ZGk1uǟWVq;Q\TG}na-|%;[8dCN~C>IL/y y^4XX"QU':~t!$/[8 |;.!:>"Nv!F-K-^ەZ8}}J6+^ʣ?|W^'Y .n˯N?UPh\Iq(cߪoy;W|U/lOiΖ5k7 ܇~exgހL*țKyGDB)nTHZb4ÀfB-mxr~˯y"oSa}V>X2Vf7nNE&t3f t.iMwDN,CvkQ^\ĺַl5j#|̋މ +[4\ٖ=f[veaeYʹl6ӲL3,ΰl2ٴmTft˯Y~5Ͳb|j|1U>*;j ?7v>e ѱ MM(}(myR%R ,py^vl-*ulZ8CFs٥. g{+ ,C ;TF6GUEYcBѱe \Z84vx E# Z ZvzuzhXcnHǽ&{mtU۽{-6f{u-ֹ7]xk<[-U.-,,ow 4:V[wgHl9ɢkD'Z$}GnB)THpєnB= xӈFm^riMoߑ3&W̏>ַ}!aZCj}579594)~CK\^&dRGu?0K6Yo8i9f.O"::FxL]h w:u\5md,hs0K~}C/\5T[j?v^jt-H߰Rs+o{$=tEoᾦp_-E兀9(WmZڛL9DEE3qzF]}ε_s ]9y*:GYE[F] @OPO>TnJȐ=? !%Cdd Kzaa8Ul-ZJ-M>~տN=.O`T +^O"Wk ߹kxLy 7y!xE #_Qo†o&l?QK16H5aysnMoU|G_<=N[߶Ƿ>&@_s+?M{Ta}~w8)vZ[ߦ TWlCؠI =H_ʪV &qS>mۍ9[QݳF7o4 [´b}$;Pﶶv'N9{.00(Zsv֚u0 sJGEucd؄pUe5e-?43hhxk2syNz6Ų®w'~^m%osa<RB+(صbθ*B!g.ڝ29eagsTXIEHX]-,o7'|Yz]ov>X ]49a ^֝^hׯ/X-4) ^7@XpX*BXTKXI{N3}e>*UlҼ /م=_y[+C񳶾EPb5]{i$Ǎaڈ [m?S{A.Aזm;p6h_fe|A5R|MS,yf(d*UAWy !/+VkZ/}lmM/?XfW>m{O>4oPtև$OXߦzůEwy%~QjKK=Q^Z8iCߺCr"""o;acoyQDF$o63jrvV_wZG#&ڭuj@*??o!!~ks^|GK+׸{xDi(gϙJ⧳yk@@6Cކh_.:+Zlf :P•?a,;#U7l-(Gc-{l[8O8!(*!~YG) `yUݬnuVgS>{;zvTַqǑAc)~)A^fߚnLYNևbXw̺sU*~'L_`SsJm^_ϰ>tܣ"ҟ/C/b ll .z;{'+_2oaO is>A|zlr5! ᯍ6_ ^eW o]CGJQg&hذ]^x 75~o޸Yznٺ&BX|D=!azU)3:k}̵i~?从#wN1 ~/xxYև$Ow~xF-}1>anL3zpY^[>s!rݮqǟy|HĭEQlWhgwk媵b6th+m  \e/:P:)ulU O5$~FƼkI_ttxq# !-)~E_46}kI}i?6&Z?q3VjUc=eŨ)GN^6bҲd؄(9tܢ!`ИUc׽WS{UOV0J1Ff9 t}Vm3,?3Q>TF_qo;ǝ}:c{H@S InP`߀UrphK 5?X:|€h&jB?;Vo=*Ο7wprD!+~O`-|:u3|,ۨ0[GKOy@ v w ~ ;.%z$C6XȈf&Ga kpx7=8Z_ KKMU'͜coR_ ƗupܱYRBԱhԱ=W[ޒqusSBQ#THo`gQ#uh3nP1[XgtS!E}Æ pv{.%BZe,;3|冣s`t쥳g7Dׇ C& eof~}wѱOc wA J??? B!/䁿ׂZ8{t؇F,x$ k}[8*~<B)8wҷ>B:_c{W-(Z8dN#BH_(d@)~ mY"B!-t~ h}B!uRPmȐ!3C!BQ(~~>B!B:_0)0>>>s B!B@#B!-~AB!BԜ7_ !B!5G!B![ !B!3?B!B)@!B!DyC!B!D͡B!BŏB!BŏB!BŏB!BŏB!BŏB!BŏB!BŏB!B(~?B!BB!BB!BB?cƌVZΝ;yРA_}ܴiӍ7ynܸ5jWߓ'O駟֖-[N0+gf(#G?B!!''O\vŋMAu>}`1c(jvmmmi#~Ɩu/^T\$tuuϹsB!G!%2?pܹs7{i6bK&MI3`LbKxxu . A!!_"u-Mݺu={H[-[lɶ͛Җbˑ#Gį*Uڷo_B!P!/::ZvӪU@i J`M=oܸUVU(X48zhXnBB!G!-~8l/rET|į0CٳgFzjb(_B!P! _]rD>x@9d&dxTB!G!?B!!\'!BŏBTB/2vXiK*U)_<B!G!*'~%΁B!G!"~/twB!!<{{{}cƌQkժ7n6glSL!!*0|y͋/8qBWWwӦM˖-1cƨQ?6mڴf͚UT>wO>䫯[.Jvtү_#FL4iΜ9+Wܾ}O\\/ !B#r r!444ܺuEƌӭ[7(,5/hذar-;VKK K  hɫBŏBH1dHoooOCFb=PF%PV!x}]t Ni8OcA@L8ϟǢǩSvܹz~Æ ۰aiB(~B $ Ppeͨ]uyYR4w>wܸq 8@k H_~%K -VZ B/\)!P!r(O?}駹*m7a(BBB+[&&&Xy tuŊ i!B(~R.@ .Ě1,˵:Qm%BI*Tjժ9_ي+2*! *A!G!ãG(*x z͚5C)ݻwݻwEɴ4ɉ'PuttZjGTA3 I#B#u}aaaq"u9G#Gݻ}ۄ \Ww<hQ՘[l2dh*b&%%{!!"HC7ZAmϙu]z6 2 c,H#y<>,PB DG>4B!?B)K0#CM>lsƍ 8}EFFn5*AQ|k)W_!頄B(~R@fjҤIygj 7-,,~qqq?/M@?@C3(,YD"+Vzzzb]\/#P ֭[o 0xz( ~1VNH5}^TYtiN W>#B#rhݶmۜ|k׮a^^rO@9vvvہ #98DKh sssǪ( O1svvvqq#{xxxyy*]U.q#ࡊnx2xL $HE!BKpiӦ8P uk֬ @B!?B)pǏ>ݷCӧO#t "o^`)ouG"#$ A"%:l (~!٘0D_kH„!CJ" S p6"J!ʇ!-> {+}8QxXiA4X__Wi!(, .\2!! x MfkסC[FbhN>C!I1V5BT 17IFDDD$M!_B ,M*V<-<"u"˱N#8""Ɖ"tQSvI<v\Pĥ՘mР2]O!?!֕+WP%[\9ALE&x(TX`M2E"Ǩ)m8!ybE"NUEJUJqZV\ٰaC+HE3ICi[J3f1ZY:0 A!(ݒ3iPK,6"u ꂢ Z@/7^tB,P(ŋG6Dkx@ !G!jb(Y9qDLdkϚ5 -d*L"T@XQ#Çp>lpqH _\EtG \kb 'J (]&@|/GFl#X%[BŏBTBs~?=/}D-DYΜZJ!|c&.UVkp!ZXՉ $J>ժUCy!P!\y͚5>Tp133+HG,B\sމ$OҽLG-p U|UķTgGLVߏ'B(~R`JtQ:JJi01GL=g :Dx|Ru355+)iQVa9\Ɯi=4Cb\5W|GW"4[P8!J(ޫ v5j(79s&.~BŏBxݻ5j$M@?ya~\:PE )q瀙z Mˠ\ /c-("(YåE4'OV¥q¥4B(~k^EG4fݻwG RbbXs! cb/_vz( ދK ʹi-K)gΜANf裏 3H!?B) wݴiJ JSZjikk#VSRRPx3ۂ+Guh<#W'.B%}A/`dW_}q}ƥ !G!q4F-D1c`R[-PV؅d)vXX홙e@hOf@!ğs^*%r,JW !G!yza-Idݺu KtSTjɖ̉Nk*TA G<%NI>$3.oa^O Cd92*|ba,FbckKKq}vYz#WTi\J!?BH^)W_d %1}qxx8GJC WfH *VhhqIVF %wrPiر>eȚB(~h7(W@b \Du0fIIIӠgL Ī-^<`_ m^~}m?bM ;RBŏu&Fz%4I"˜=+ʜED//!RDDAN=mRBzzڵnݺIWv{'!P!˗>گ|zzzI-0@CDtHIDJQL<$"ZML:U@/ ==_ BB4 6(>) 6A%*=BT-EceGL_!GQ{VX]ժU1ːUCŌ=x-K @O9l7nQ_駟$C6-B(~5}>c파>__yn Oy\25ip(k~(\Pd {M6W\U !D-Yhfu5*h 4i2 K pB" J"kZ ޾}⫝̸aÆI׸qӧOPBQ!%JSt(Vw!>|xQ)vb >= B$xcҤIԩS@BŏBAqL:vX"&|X\J-ˍ žHA q48>Ϟ=CP9ExK!8w?P*lbbB# ӉV峛1_h`+Y8|MQgH9*ްWݻ| * q!P!A1>|xbV%%MԴz(""By VU,!(@Qe?+>魍`'Or%ٳ'ZABŏr`͚51c3f"I Vu;6tâD@'3QS?4P՞/")))<B#\bŊ l Ƣ/³A>|؇ @Qq}˃:u$k>}B(~UK>3Վ9³QpY3ڦT{c)R,M)ᖨ R3 !GQV^Y7|þGLCCCy*:B/RN,XMLL,O$:t/רQ#+++BŏRu7bOQ_л_E* s+#o+Wu UFy !G)cNY6m|E !LJNXGF.4 GIu_&OU<ŏRfDFF=lL,>y6j &P(~R_ɷk)SlbNSA4x"=7*doذa*Te51c;y3Euǎ[Z52"K.ͫ ƍ-[/xB#ҥK,PRSSEl З?!">,P#]e)!~]tlQ7Nj^zF~w% $G!˯WfγFP^L5! !Yj⣈%m۾}9x"ww{~=h( T]tA >]b ˏ!?zzzjTXӧ</$S4yJP4?zsgff>yѣGR)HOOyg(x~wD`\/l t*X2@(054K4CäQ }B.&Β8"ٞ;vUh3|pf͚!!?B))sSNlxGY eQ!eܢ8tg#d2bڇ)Wôe3#LWZ2B|$ ПT.2WEXL'N5*LS5 6xu)^< >i$aM}w(TӦM!GQk׮<9vgaV^ppp><|#oΪHEg O` Ũ^D$!K/OQ_NNݺu+g/->ՓB#FSr~*`kA0Cb^=(!A@f dg"syD{-x^ArQ-ZH|a1rLSO>.?/B(~?BH#֫=8OED{1PAZD@KDe d!)`pDhIz $\x XV~'?ע<^ktAeC($$ҘŖs P(~0/b?4wXu51%?CAhR-R$:(+k,<i^"f~"{=|.}'ǎS:*UTInW/"[wBŏG))u&M05YpbG!Dt(g'L? SM.!<:Hx`Κ1ߢj(XOt'u;%^HoET_~Ȭ.B1%81>Sl411|XďP(~XA)Et@:Dѩ< ! eE pB(/€`TF WDlYBĠ`؇&['R" }.÷Q*WlffV/H@ω>}L+ 4^z+" EDt!֪Y7QztK5fDKXxʕ{\vmDoر4u\]X/P^%K!G!̮]0RJ˲21D0DyҜ~...X$b#uLZ4rM$Ia(py0$P{QB ' C JKBH!ԪbŊW !BɄlyv#,UAWƜK=QL}І=d~b8ɘ`m)B̙]^BŏB 1jРVDC|Tٖaqn*ߩ"LKEAaBVp@1IC=;H-'  W~}CB(~b#*\rEtڲG%*vZn*U&1%f X\ %[Zr[W -FFF"=W{eVBŏBW0:xXYK4+h%T4HM¿lY莍K7Xx)5{[8۷=kxO/yb !?B4VT 3z<Ɗ>,S$It/RrDHo+A "u|bn=-ƈw}w֭lNBH6:M|PY߷~,\XBŏBra֬Y0l2A>c,Y%P!$;0:eyYU[=zi0}.ArQmR%:C",ː?S/%..iӦloN!?BH`Ȑ!*a[_'&&vM zd m/E\AwLX0B"T=zpN:ݿ'B#נ*c j*nG[<ixqb!O)9`z DO8!?/AJ֬Y#ܯ]vX̳JB;ϟ?boγD}ydx*HIp=AꛂF {%˽B(~rŋ1+jԨK#"fm=QPiRu|W3;=!!A E "è#Ozzz3 ?~<ڄ!L3ڵkT  e:=H1(FDg>&"pBM!vŊuƲ!2vXL:tPc o e+*/h̀) 2f$ }nEw}t-Z@@^c!<9:˫ )1q UAT$7])UDruyxcSᑭT9,,L\ ^AWVH^]!<ҽ{ẇ V۷1qć9/ "@i>^zyiB(~r7L̙S <Ϙ|1!})]tqq'!""B,+`䂶mV|u!^W!|qL>裼VȔ.I DA`KPP.oP?,ѻo߾V\!iS͚51 .AD>/ (}A+::}ۋ3{oۣkԩlGB#rh\nO2D|^Du_Ou$xc5x%{)8W6l׫WGB6}g>|ܞ $Ed.eXg (L<9?Up޽ pC(n_xB(|,'UK}IHHNj法{y+ D?2-k& !?BHyd՘խ[iT c\L(sm]wA0MMMEy$"+49\]]BzIl@  DW̛a:5$.D\uzC"4mҥK>>Zn/Px9B(~> .3Ƿ71>!4BiD j(`I,A[n):T`@r)Ek̡QD%b`R ^J|y ESHڧoפI| Z"P!ϴi0iӦMe m! EzѦ`h!'mGEEA8"8AK =$"QG\QGu,“CDPq&{;ų`C/ 4h˄(&rf% Uw6߭|C?1y!B(~ ># T>π?&P#Lab$(x''v˂nThtb \1*_?- Y׷OJ'-C(LZS" Rppt~۔uyQ ~5j!Gp \2 PJW$bX)/4DRhd $LLL |ZE!D o!Y% 5)ķxEw 'CŁ]EwoCXJ yB(~MiT"ȕ&q6Ѓ LvcLQ,kDT0&d2 O DpRtIODEo@q9ssS_RmV 9صjf^B!Dcڵügv\pL 8׀&IP^yk]tDr,0L1Fx W w8z]X([_"0( 3O>:"P!9&=(q^UYPoQ)dmN"S J("؂*<<Dl&Q B+paާo5{/>5kATĥB#h&P~}LzV\ 2-@B@  uz `rӎ ,{=xx19xK(R _}Qm۶pxBB4===x*V1E!r÷HD1 QC8K+Zc6?/0C_S|VP ʋŏlڴ ӝիԤs84]!L! L RDx|yȄ2cGFn-l 2"Rs|u,\չQE<1T:XH޺uk~BŏG@?`s22B C H n*Vx(ZL a%'[RPpU! \^No]CVa#(ˆŏi̟?sMUELQQ  (.x 0LV%w1Chq2qWp@oy=bxXCߖ-[ MlQ!EbbヒbwjB;V[0+-^= PAE;x,˖K, Ҿu1*'Z&U0ܯE !G#h#GDk׮%D·ʳy,B^& R? A(QXFLJFmiu|e oIvj=n޼p?LxBB4$U%4wG RA*ˉ,|I 䗊j1UD,;3"Z:o@d] gdd_V"୒%駟5CŏG:wYΨQa1OHHKX5mA|S& oXd2)F%\WMKya-Byc|a&MXR%6$G#h֘`ѐ9ӭ_≩EzFFFf3@wy j`f8%WaB ~h;Y^=|0VZ6!G!% ,( ZِމE}5]/DBŏBwۛ7o.<3!|/ EP)2BbW_ٞ_[, BBxrr1.)j8T2L@p?$*|⳱}\B#a׮]TRR=d)4WX/!l |5>9aIIy~d-_E?ݻwg!B(~o+VF__?4fN["fXtCP$)4"MhCQxrMDVǺ#^<ةdK8d"G!j1_~6D !>|Wy*rNy*6`#ʙڂl' |)S*ăܾ}[<꣨J[)X &O\p(!G!e VF\|YlAI_<U[PFR:AD|W\b'VSGV!}TR"[[-^X3> !?BHeҤI|pj\,AvZrġAs9DU.MKWj)+CFowhS ʟEHU尘~xoc^$P!DWLkv)5wFF.kAz{y\Rt x. #ߘHo-L@xwT"!G!jL>}Dr,(;!*"GO芤h7 :( q3R8Uˍş@v(JC>@0B(~`'F3>R>&TPn<@YZRj_!R*gLMd* O CRJX !?BQo֭1>}[6|݉tUA'56@rs-;>>^܄ڽ07!#ի:u)!G!eHdBfcd'Op$C +uqN$CT #6i k!`Mc6_|>*۴iöP!D-uńfڵ}G_t+O>!Yb[yn͓ʽq~įdb*C ~G4裏Q9`6v'G!jɡC05¡|I>d2&V-4LqĊ>,kdW2{ ~K.EsrPB(~~ srʘ͠g&>TNIAr#BR?AJKK+s}{qss'D3#CΝCgܪ%cbb$哊H^]"_ x/D OK|f &P!D͘5k2-[|61se3.RPrӧO lٲ٣` ï+_oݺ 2WQFl#{Zj!KŏB4Xi#fxPofϞn:dx v횁NbbF%̅D~M*΃6]"qjժl۶f? Q!刡CbӳgOM=@+DZ_r ,M$mӏ3fXbő#G,,,]r_%f,~022¿jQHzOZDP;~ЀJh!_ ^N۷o! ~$fܸqyk?]o߾9055ul{^ٞ}|T١;v7U8U;Q' ؙDžȝ^^^^)?-_\໌! {y"׺N s1\V4VNKKvBe1꾠I(q,H~p$3NH0&_ x/\<}槟~ޏ|B# 6lI̒%K4.,,uy#55uc!zᎤP$b ===hѢy苮.$PXիWP}!ir"oU]/X͛ohFY^#!G!e V.a'C`b9<]=}4;"|'/3gDyOQsiӦa vؼy3 GZ>p љm#4RDj a_;$D/P!D=@CL_Ꞹ֊9v|!(]%yгC,!Tlٲ Doߔ)SDOAaA"p$`iAYhAp/z:s"0D[}EB/ jWhءa/1$ "rXDMCnҥ!M|;7iҤɯ!Pܹs?DL(DDRF.<:0|*Rݚb'g pFŏBԀŋcB/HRqPz&sL##?$= ~QXBYs{Dr!7"KS *8J5*$ ^"tBdPbg\6P} xr^Vtn߾]'Y$ŗ{Xo[Q?3|x27!?BQuƎK4P(Xi>< 7gP/44^҅rRx{XeT`@,1z[80CTe#Y8KM()@(iʚwa ~EHv7)pPTAw}J_@BLxPZ /'GDL\H-x+W~w曎!:X$.ȍԤ'Iӣ07 ~B|CQdi:Sj/1r+pQQ-OoL^p{!P>Z,vpmذA>x:4 1ȇ400@!ˤ|A'v>tr}\"+"CHB]/\bpݪ5#(^k|x"o!_oBŏBT=z`2l09"8|LLF!B|k@ ҷD 3u\qB8:'rP!x8:<ÆW+ :G NKщN8?̞=Bl}H $ZP f v;wN '5׫<ASϽ1ڤTUPt$k7NyI0 ?0&O!W*,Ж:i!p[H2 (gP86L; E] .¦EyЮRG§:#쩛_`}r_S58!*"1:_~9٧d}Q݅x{JG'hbuJ*0 0 ۍe֫gR\8jBW$s*pJJRH7ZeL#qeijq'؍c}Iav/MfPΝKl׿54o'N(H= &@ YT!bRBΪ\]_TO6M8C`Z$*trr-yv50LL 0-^\wuZH~kȰ"єT|k>'t?Hiv W J(Z/pDDƑ^|2<0Nj c#?n4O ?oq'8 4r9眃²L*>٤:^ziIRqzip4δI\jt!W!|UV-|!ag/hqFȲ[ӛNf-9%gTA0]-N>);09d%Y:5p *32֨ũ+'OmNh$3ɵ ć$K kSL d惬Qb6k;ۆY!P*[Y0 ?0HAQܪJBR4AmHrB$PUTГ}U ۡ)I*A.hv3*ꫯF`^.\0˺sT>%ph$F,EAZ.K (#[D͢Rb )c5&2x߾}y~_waa B:•A{%PJ IM:X4&/NZ7wCD)aɵN |HgkLA6RٳR 73j#̛s@:Jnp3w @yƷ"3h_WxbdaF|Sb|r,C YPL j-F &.aIc pג0о=I`#ޞldג#zW_s5E.9sPΟΜ9G'ui&zx y4Np]ыWaaDwߝxe]wmI #%c)u`$֙bI^De\H%ԭrfNZ" $Կ+"xuYIH. ,@M&و E3gJ v$)*i҇,:[]9a$7Ek0旃aaY4W]qiU.HXAxY^DljOפ%FmӈfRń^J Kkr-/$x&FP_2V9Ne+23,n ӹHL dzz2U0 Fp SG.ɦoۼH 0L 0V訽s+HO]朄 {+(dȭʍMGt)@W8;HˑRVz'@F>#W3 VCckZ; ?b %G $Cp8'J,,Iˍ5m&}օ 1j%૚E‚8}.o03 È_А*;S) 5tI$8йWFH/kg;l0' .ȷ0=60,l)[˨S/JJeB4q^lB$٪R9 3pR'*z03 È8 lƸPa:M˵ǔ0i2ۈH]Rĕ[y S rӦMCk9$|E [͆dMK )9UI(OIv6 9ܒ].b$|^ƤC~q 䱍9crQ@*s|x3C|5da;3f%B BqU))$~;uzrƍOUA0"HԩSd#G>?b*vX>X}juu84Q)5DUT;Zkd&~aq0btdᾚjв(fmi@Oe< ȹ:ސ["7>~aFD $F_QA{䒹VGF,j CDQ&NBlh4u9<F0$ w&FL1pN5Oz P229!rŏ1OUIo3g"@6B6,CW8xth=k(&ԸOaFD*BB[ ^}5]p!L';tG.R19v /Tsyf~ ҾHT0)PS:ӫRxI*KrDKa}ۉPǒRԎ1%OD8fxILMu]01T>l]D l6i"IYjKpBXy>W" e͚Јȏ&Pgơt{uvjd 03 h@\#K?fzJ"D Yd{&)nʔ)LHϩw:Zt ¬d;,6d}I$Tccw$R%؋?YjG\VӾ~ӦM#SOp [?c 떀D!ؔ)*0 ?0X0h !C4ȉ 벌RD(Lv4 =0O-/K(J$@2H -$,M?w%Ő&JxDӨ`|#D :YT؜K/CD:FhX,Lq"8|[1ŀZkl2 0 #$/{HQ$أ>^|v'9KM'MtRebܪx I j\Ia TAWddmKeFM\4ŐF)<* 0Hgb}b}rws̙hMIvDZ#`7z<,I2&$LF[n9K7 ?0 :6I#j16 Xw'u^<Ǐ2-ic:y~J*.{ sz98!lWh=$ 9AǪ5_{H@eI7  Af~¢ 6'bHTwޙQ1bV?M ag`q(M&rhЃU03p-=RY,M^bI1*ՙcYURP'dh+>O(J. +uA>olOݐE}[)+*VEah u8 ń+\4Ջ/xIqR-8XrVR2!,,޼LY'Sw5 W0L 0}N]w&Y'XTMF@rq|v}b{V&YXA FT3 7 Eɞtpʎ8 ."6 KRI~Ln8^,{G 'è鬜%TǤ)$3[G&zm!@e̘0`\v>,CxB2rE0L 0j V[o= hDath8R ,%lMyg#ճW̯c]K>dF~Q]ﵠXca#7 2~;闁LeM&ロp ,yɦs}ƈ L*3֤=xxu#g/Y$$y兴O" 0 # PDt馛4˘7V6j ZUTV$o0C f<]x(4B ;" 7flU03Yk>TK8*4>1LCDhRc_ɌMbQ`rm9@J%,i$|"cRvpɼPto:!06=CyКZhTؓfYc b-a&~aH: k"Bc<A$^5O'SeV>Y 0T]p(l )O R'MPC#\IɈj &ݱI9 }c%'2iď3XʘգsSt>` 3`ZdqayKq)0L 0lE'X^#"w(Yik-)MQ1Q  ?/䒂&`}!+i8bB-;@NkP{xj`3{U,mucaG B N4)_'4)YA$&)CEF8u+I$RJɓu¯jaR`K>=cyPXCx,[O6(? nYkbܹt=3ŐldA{ӡq湖NeuuwvYaa4 =\ÃC( "DP0ْh =H>1r,^Gm=YBjXh6^D@1CKF/i`}IKM=# e')CN Whl+cTG.q$5=`RSN`vTXnrCnl$-y2ZD5ĬU[SJLSRN'J503 hq<yw|1_"҄ǒL `VP>5hXY|QGke1,=8yOO'4WFB(# #7E"WAL5c8+ئN6 ?06jM/ب1&^Gzd>莹s^$MdY"Rͅ݌AʒO}NP`A )r0 5D?N&>OѶ|V0d1a`9KƩH~Z/@-0)G4@?J#e~0d?R:s#Iί q<I4u5bɔ 쥉dRjn`&~aQ~&cƌ'A"H%~ҔE-KpќrE]jֆ!' 9E%$l)Oo{\`kBB#\b?}Ρ2SLygrHOUqA2؂rbYR b ?sc uaL?DgGK"_PEhv5dhA>F\vaa4 P>B_$ˉJ!{o9mXzJN#, Z.P;WN֦i"rϔ -Ÿ`8IMn+Ҹ->ON!9NfX_ytNC\9ԟ9 qAhYiɓ&M %y|(i;y ;CN!?0,^B&gHLIpرԭ◔%| J` ̩9L0,Z?սW)%gYs Ah퐥(6p@^ݺuk\03 hjEs)+| d/%)s޼eeɒF&󭊝FJ%,H\Fn$-]ts'F4gK'| ,ǮEKkOR@S=ukPg ~)Kyu` R9NOptRgiCEcJ/XX 0 ;q "Fa2>  1%9`b5<.ŋå B! .BZ H.Ў(#qQe{0DG*}r'$M2'nFܹs2Wv/կv878'ɥ,J;K&p"ǫ!M4пC[ X }u"¥^˧*&z^(FmsfR4Ԍ)ÇN;e&~a _S^ėtm!c~z7dt0׫ E-'_6'  &KYnI8bVxo,+pTl>\uU29H=`_*&./y2HQ;5Thƌ/>75jtN@'#c>IG]Y%HߔRfrd\K0L 0 $l%0++@C66#Ґh{QZ}Q-I$QAG:T9;KY'C!9$+szK.L"1B,!fHrDpQg9IJoFkh;Oə3gRSJ.<33eY~ȑ`۷o\ 0 duQ[3$xkɵb?_2j2襖RuX :UELiꀺEq9ꉗʜ>tvRHթ&}YBv/~ .!#iHf V(BϠѐbJ(B: ie1f?u'f{.Ct7w)rf7tV%fJ~c(k0L 0 &]w]B'z!6B _Z"+Hw#SRZ`M0+~%K^%~&:WluQ&+a ~<8/ /S|3|JBrȵI/҂|*H՟NFLSy(Ül+ȡU-%NG vwg&~a ,q 9d~F8< }jBk&t6P jq:yDȫYeT(D1MhUr+HX#tɘҤ'Ozqrz?0˂Ϥ;ô 4k eXiĕ$V":=F>TtID,UFe )Qߜ9sB߀;*b#n*OSr#ZOtuᣕ@-(`tḱ&i[ Ls^K…#@ @շC8'r3Prx~K0L 0 "T:DdE(UvUK?b$S1(o{ieE3sg((_^ ȇ8m7(ʼc`1 ?0F?OَSfT /J_($^֚U|9LrIIBh )#8r0'UzIc142hq!3c 4jSqpFGNS+7/."j03 X0dAE86u'ϗ .9@`ݥxXe2;~BX&G %EcZ3=U$%aN vdDN)I~/yFcIH'[db/j`)_SLCy*<1'.uUVwaa4zD?cޡL&@FC ɟ*QA ]isP|$mš)IkMC#Tآ*-Sv0m3T3jt?L½eߢ5QYCAOw5e_Rr8DR?y u)Ț}-IDr/[`1/ٞ!c[d.(de5'41:_+X'oV^w駟[ m*tt.-]T_0 ?0%ko4OQՁ>]D"zAR/.{&du+-:,u.^xbIu$s= 99Y$_z嗋 e*4]<0dŘL:_Eq~trgb%1tPޱ 0L 0>h 6 .(Z=CFUـBȒ$]鉪9ERT2BgQ:.u鲦1d/si4]%EDn*瞋udHSNeH#P]^~iPHdVKc&~a;AoLϨd@ TuPnEOVOb[6d0JWԎ}hqc(FêwvG3bmQP9xGkYf <0&.pI c<5$Q'_=S>?vqyy>/03 h$Xc իϦUz!IJ})ADHƤXK|G4YNi$X1=I|K57|$ۤ`1V 9SK*Uv[7'r8FaH~Yz$/ؕVZ)r%0L 0>p$.~)"U6U-r=rj`/j䐝A?(S@\JiP1 ď*԰n Ȕ)StsN~MJ)+#(%| *H@';-@ZItl:$Y,D6zd()d̊EHQYРpdM#WJ ù==4"kŋK3 NY^2si9)RF~ 2?1 Ȋ j vV{饗"2LL 0 OKvexDqUqjS$BplMb$IP@3TveU~EjW]utLIE[yAn<$ODZ#9s/~ 03xzlɠ/"j5qH 1+!yZlٲ cƳuda44qa$C"&ZdN9 !ΐl1Wz5hօMav#RfΝ+wJMzU KdB'Mj$daӜ.T@k>SsB3`.dQh %fYK;W^~5&~&~aoL\BJR<c=KS1H#[2dEٌr]aػ( 36Et8D6EZ:+XpAY<`0SQ_A N`~!31 z7.]Hrk;4QĞ[mJY!''/rh1ϭhjw)w--V&~&~a %믿>V  p pUu^JDDOß TֈI(>={v:3-(0/J7m D۬X8'Cv?c8COϱ{p)뢳UPUzoP0- 3(uN9IC8dbAьG(/E'Zr_pN"͐/*λc%0?GI7vvJGJ5sv2LL 0 t!⒭ڪ?$) ^Fiۜ9sd> @@O#񏈟B p/ *)ᆒuQ! d޳?lW\W<ے<ځI =㔒 ~@ t~F{K<.,1ʨG% 033 h$[eUKe; R~i @L ,'9'RUh=rB&(YwG =e1=RwT= V$"dh 0Jz>8_2 :0NT﵀>)T@F{Cn6J\|g!6YZ5py~_agg`zs oy#$5ʽ\lp0 H8БYc>A,w2ebWv0cv;#>wOD7`И~0zfq6Q%ԒC .)ɒKQd3 dȭ⥁*x [a-DqhƗIz򎅇}ea4zƎ#z衇|Q2ND>!Q>A`}Am\1*dI~; t0Q{~>gr>0w zrAw>g:tߡ]:}9w*$8G#m3ȕdҦ{pZAK78&dOM}A!Th0P>]LjSbc(wmyG!sUYFuۣG ??0c]w%.2dHcF^WLH+o6x07Zf H%ۇe>zZbmp2;<tI DKGmJI {yAg=G |a^3ݹۼ]벨ʼnw]91}G4ƑXv\9dR=qK'\28xjNf~g !z6,/N"e̙<[78!g!SZ\{uca4S\Zˣr& $ W^y l̸c* P[H1OtB48'dBdnXdY@ $ws{rAwթP-P]\ _J /7U0iqd F=C -we=?"9&^Z+ >ڗRY8Œ={˕׍aggHm:v~Sأ̣VNB䇇޵^[-C [P֦pg>裔2 $~H=ҔIh?l$,}',?)|/uhzC#c!)༒BCfd3~š .Y@s0N>܏8_J[71KTuXc5N7aggH(.YuUQk-n:^I4xbaHD`S$y'Ss:XT7(m$1Q?Ò%KSt55zBO]I$~{{N񥊹|>zcf'>QrX-lmnAg+;N8OZ`e:"ݦsBN9]^/䎢';V){9/,Î1u?/΁vKeɇ>F/F 1[\nmn靋Z~= T LǘZ#>.ĔJ(O%sܛT}٠J?9l'?cP3m-i޽;/X&~&~a q&lhvҖ-[I1ۍAe]8 (:guVr9Il[²$EԮTpUxUTQVR2tϮ`ZTEmNC~[1G%%G1{|Xa{C]_2u8 Q89%3y&)&_=d[NLt}xږٓ8G:^r]{e׍aga4:ɢE*Xh橧"0ˆJNjH7|QH' &4p%qWWY\9~ Bu>p#b(%%CF{?w\Y_9LJ$ܦ}t9 .+iKTX;qo~X;,h}N1 Tj\Mn2Id3fKM g`) ;ax>nR^JH8*{>&_f1H&G|eBl tЁ,5a` iNd$mDi*]V4@/PԂ!8N12 z^-] w]uPrWPV Qg惘 ~Νw=lJGs+& .24@c" lu0 qo IEHH2l>lS$J( K.Lwޮs֪0L 0V@EzAh2rL0hI&y0ZTa]Ժ ^ldb^/ B9!`StA^,4Rd봗M3db1RF򻢏~}ߊ[Ԯ=~TZşxx`_Hy7HŹ'w/G(+G8^Ї3Y8~PEY~42*4EIE;_r 5 I5n ?0 j'.Yy Cy]'۬7]vM"8:KfSb(YE())Ч< L8 &"Ns{,ws],(۞{;Xg?.2frPJ)rZtP>N~Ҵ3L M}ШG$|bD%VÙ/OˤJf~Ol,YPIkB|Aã2]%O $[o38ï50 h<`&l v/u)!<-|=Iۃ'Nݷv/z% 2 <M^ A\?}^ɿ^pTn\?ߵO$FuNuAt"J)]+ҍvX1E w;OaEYJ^2V:Y]]zk6&~aF6sD'9ܯK%H%.G !JȠvk'aPX:H?_~)|eG ]ro}NuŎrjJfe.iRyBQ ~_Ӕ)SB hVY@J nvZrgyFٞwulax&ݻwC=1xjjH@>dK24r,6]tAHlj dqYg/p?XD%L5Ng,\a5.yEVloa w.SZԒ6}œ1 b6š`&\whay]y,9=W1??:(#ykЯgϞ]~&~aFLiϪs ~jS5S-BYK/CE HiS}gl,e)PMv(+$1껣'$R^!)<9J'ŻچM 7eMp ?Bn R|i~yaAαg߃>/:x /Pr.Wn%aQs׏dذauoIEgmal\L!COTGԞ/ٱ er$=Pik=a0S?lpq,dZq˔pb܏K xZd} `XY'S{rȰjˢC܃D GEpѠ$mN"e$p[eUxH03 02%%k <a1ž !`]B~Gv mw4qDam0GyDQ2%Rt,X E9]u>Ǭ2Qx_;\^pŎT]vYF O%^>x> /Iw{C+ Fx,g_$DS3>ظ#oW\=ܓWQEcaQY;# Bu'"'S&$GLSԮ<=XDAQt?בP`SSL1nOBϱD1kE6cie+֢]t's~ '83cO20UP Z`w 9TgmGmL`~xMD Aq>gN*¤^_ ´cպZk{agaDENV[mW^y>{$Q[a9L׉ݓO5]c(_P1 -j/#ƎMCRXbFN0{MZYo]wO9j},ԳpxjQtuo:%p 3eqHf$NFJ/R$c#-$ Xc ޮ1L 0(¬M7ݔ>{|G򗿬Q\9Q; O$͏$PeJ2:wn۷ndu^{9FFZqY) +|(FG믿 'w>! $sSpƾMzI_l}w7kxriEcaQdʴx6p +Fޡ)A_@9s++@Ց?#:HUy FNZ&ENHEnduW<ߢv;O ߀};ɢG..N<p.r5/\VQz.nR _e{j rYwѢE Y3" (y|w0/603 0*]w](DvWSMR z{_%4^H4,@Q~NwK1c*dd}|Vz`I Cwhsw닍ocFgP:A!]tN)KEX chMv't1~vo9}G`D1Z~)ɋq@|/wJ8-c{Xz)FoRo0J.nj[03 0> F+sϭ ?xi)֗}K~Tі`(χߗCw4tbYϥӌ~rҡ:CM_\_v}A*v6tbS贴;^ Bď\PI|A.77> TF>#COFꫯFD1ۓ jurGagaDHJK/6=+‰%ࣨO{2٭N c *~E!'TUa eXh~h zՊEsF>v~>_ιZs&*3*8@Ll{w؞|IV%_{VZW+ -caQr5s튊d*\N,͵)*A+ Ǒ=$SˇWCN}^)agaĂ!C 4;"}:JHyQ|;;X K&yI7JzJ:O6eQжN"r䇧`>UDoA3:sq{;]j%T> GڑˁI(!Xqs']rvl ʢ|ڤItL(b{"{lSz1zASk=O<ױ/1cOk N@7xc3D匝_|EW.&~aF ܹ3 aw?l6|>IQ#X  TYAbT٦sW\nbPyA1 DdHW$oA/ÏܧAQe`[< /[c0 &D#B|w46sɅu{\n&~aF,*?|Mwxbb&L\tiJ3n{ث t 1?:CTE]kj-Z~AUMjikޫC)W|g%‡/ cN!Ef^z|ragg4@_jQ 5у>H̴dɒ͢S_Y t #_UATGz.׏wE"-lGe#]w?m:%LMJS&KgkAo!zWu~%$gUٞo0 ÈZov3ms:dTpBsJ<PQ>\V !~fwm;(u?OtAʿկmyTKKNNEBhؓ‡YR4&fYF㽊b ?0 #Pn/o&۰N:餂8 (ŸF=LB5~x{2iP`…Pv]v#鑲N[Ԯۜ8} {s%C}< 0 (gq1o\O?4X* c|r%1k ]2!-8UW (*gR2bGGgP[2+ܯĝ8[6Nz\G89$?;N6VB۸:,vA裏F{gTFeRgP?\㥺j0 Èx譱)"^4Yy[=x#p<+uQm١a(~BY@&&1䫮*7;Bg>ݷE]1p~~1\~%5Ҙ:uj욛D7& GN'Rou0Ή6*uVL MɅ Kg/a\_ij:?裏0}m(ѡW;:GNsC( RGC$'KaI9 ѹtBqDSLQ(KoY(l臷gi-~ W<9{^yߣ.x~f7kw'"r0kgg(, 0;3G)J3).|6l37u0L 00|p”^zh/ 7&3<''8 ~!%PRu'C~Hs0_ }/c3w(,$ۤZcYKlu3P}r%q1Ar҆)@fBg",B+FXl!s!ZJ03 XqAd)db>:y!u~*#S !;PwM7ݔsd,i!yL6-IK`.:Y\/Kgp?8P WdAGN2vѓBjl^{Moت SS*%kx2/0 È qo1 tB㛢U-^B7Ccyfy?0= I%,P"0VǦ wG]CbGՊ&* b)0jdtDGne7v1R:=;! skA9 |!ٞ$?']l j/$4Nf&~aFt8)w]ٛ$s /4R1 jtHCDKXA0"5#A.Y4֗,:QU?!e$.ZHbpNzq#_6KA]fa~;rdS: Pm$W[Y䍽_^Awt'!qƌ||~߂vxgf̙X h3~!(xjbfy9BvwD uGN١0Cc#Naܞ"URCWو#xrbaсD&, V%q,I'i c> ڡu0 _ "(Bt.W0/E0 (Ա?-'7n0q%%_sHIX݄&螗M%/j:8%cVH.(ap׿uޥ&~aFu] VzꕴB! Ͼ!?d.1RqTQ1#Lޢ+z7G\i5>%>yO3`AH n4eU~fvw_$o~;z<3{pVLRuvb cKA5|ASX*"*4zL{ƃQ= 30~P;b%.姿V ?0 #F I6mԅtyVRYR@y@Iv^;⇁>(f/2Z__2G!LO$ٳ=Wҳ)|RUXɤîg6C]_4ٞeF&8خdȐ1O=ME^1T 0 sft bܟrosvU;G|guz'e[G8%DE2?0 È(;v$^>|x l bS3t0sQ܌v,ߌ9"ii52(Z]~okZ$TqheOcN+ 3^Yũ7CF Mj( &$'39נ yNw JsJ&Df& Ԯ!2Usx&@qT/O5 T{ChL03 0bQ ʪZENw}wn by,=ud!>QJ1QS\Y;|xu~q: o B|Awh hyG U>7T ?s^+dOEpg/BH32IQ=tj8#6CJKNɻWV ?0 #F(^Z$P)}6#PK YM~ pGFv~zlKR>)ugBBG W`hT* ")vFHţ)=2qnvmTE$"V ?0 #RQ:xxQ[&5ȧ*R gJ$ B["|2<%Z/<}Py+V?3;;a'D9>N58蜦 dOiL&y!A+跨S![)Ē&.t*uo$zߨ{WU8y{GYlD^AS}`r6e) qTM1=%DG-ڽ{w&~aF`>[*~kDJŵa>PI{>t~sKU#C@Ҡr'MTÏڧ/ ~3:yҐg+LO/Iu7H"hn:bz@2n~f~ͧ 3˗6WjL7 ?0 #:^D-}m>C1"IamzhN_b%A$ rQ}&\DzDgsΑ$8^n*VX_2ZVɋ.Hďɋ{gըP PKRJ+-P ?0 #^)я Vnqykmѵbɒ% ɖD71MƍKBZɒ4|ðv~E>g'ZY<_b)e%jPqgK[^>~oJ^{gM73.?|%wqG^'Oaga Z=ZF1}WSGneБ-yYg%l>.h^[)Iz%`k"z.+vu] \[9fR&MQ}4Uiؚ-9CN/!dU*{[834Cjw%~ 03 0ԅeWf꽕"_K=VSG|OWnL;KhQ .L ZPO|jp]22 u>nXvZ!D?]u݊4O+H8NȂ_xM`\-w;tg @ [1_{j8bd뮦yqs2ѣD"eꉷ~[uɪ6a;PZ\s7|5au$zGP4zܔJ Ok`AFX?ifw[Кl@=r=zB$|o7`l>kQ>O>?6qD~dW.[QbKke`9\m ?0 9FmD2zlO?hf#: TGx쎐l$:ҫd)`|bƎ Z7o ~;_ec*vSaƋ/ 31[`h‡%*%1AR 7E+py߮g83<3n<;ؕW^ C B{#f=6Y4`_~*1L 0[o}K_"v5۹뮻 F4M6M.NxM\zCPWl9y/-ZLIbA5LF¤=REypb`iF\;]jJ"kֳaga4=Xbm79{:uEDBvU}w8ѥt ,Tg4IGI= cf]0•~P}U{bCu__F{Gҵz[Fq瞓 *1L 0ABSb_׭ }7*&Hs`;<`P#`R.TQaSQa[FvӱN gR}~W1gƠS(s''%>ڊh6!t .͙[ /T.y"yxh駨/DKۗ'1L 0& xʶO#mt6)E*~ᡃ? i>~*Am yaU]0ѯۜÎݣIPR3jמ~N~"AT' 0tdN9k,| ދၢHʗ;*zs1L 0&fw$|!Rl O>$qom,Q'Or(+;"H?OǓjJdr][Dv$Sfz[/ o!rJ:P>N,@sZ_.]w…y(e`LD>ԓꫯ/g91yaga4jUI #k-0y8FZ2E$mp%ChZ~KMG>|]!5:۳܂nQDCё-ߣ#i (("S)4"W>IEgUNxs2/0 h@f6tS"" VQQup[Re?i~ig}nCJ]w}ھ7.Sz3@'nUxHh}J?vZ>|`6C3<õtRFu=Dg MND ?0 i-!3U!9*߈jCC_n%Ufy睧,ھ跠]>vXYq0SIKw!A02[@T} #Q9=>{oRm^;찃A ?0 iúKCHe;-O<8?_o6I=?#+5@""Xv2}xY >}P Sz)~hɘsN&1eTW\qSE鬞O?4>d^7I3W^yO>_"a4: f7>HDtB^|EY9N0_pezGr+=fvG3~t4_3@tN݄ ZU U>P7GӁΨxxUIYƞWƅiW#" ;Y{"LXd z衇J.٧Oޙg}> ?0 @’2MDGdx*F%m3Љ3 'P=.w=Oأ-[ph|a= RSc{&ťJ6ю튒a%@zvޤ03 0C !8p`+J"#_N<Ϫ;})i$R]-r'grWv5~ ׾]= 9s8o'N5 D|&>xzKoJIޝSPEЫΝ8gϞ]vC.h߆=o缝UF"ᬏɅУ?FEn)5ƞ%eo&~aF&G*dz'H9["(NN}'oA Ynb*4kޞ-m܇OAޥFn ےt Ozᇣ7ߜ"暼0uwaga4 jeH˾;#A1wNw?Șkda*<(G ֵFB`̙WU~gVy`F8`t:Y4_;-opcG#U1z^/ NlJ'uh^xD‘ըT뛥gǎy[N:a|Pfnpjuh~}r-P)ihHB,2^}Տ?ή2{CtߣC:5kVM=Z 9"a[Q{!eon瘽1c8is61_ 0=&2L1?Qٍ>c/K%Ɋm8 ?0 l@43vtʄQ9Z7"@Sc@F.ŝ9W\qŏ~#sϨXRO՟f#~8۬msQ;=w$+}sn->IwxI's{7y0ۢsE@J+D@\d=l9 q⍩Ūndf,USwyDdQ $CV[YV%CNޭuNn|֚:0Q{aY{mY\)*e7dIXyO5 ?0 Y1|poSP5$b<) dݕ/Rpt̯`3HA?!QZ'/DMO92]u^Mi>-?|ճx$ r ~Ǩa=DLZfdS\rN:aga47PՔ˲ŋ`̓A,#DԊpԒ;u%hhp?z1k,) OpYk޹Nns; wÇ;36~z*~Gq.)`*˨(p=Sb t!eyiNWaga41zML>dYX̔gl|yAM{FWJsJф zVТ`i۲ˢv]v@~NId~Z+aA;)!e$K0L 0H 1 ~Yj]dACz@-ɟD򒀇dTW#Կ="H:Бh޸lns:k# (~*WGZ5TeaZ6=#)YnR%3Ҽ$;t 0 hbEa )d%&SRAځ Ogli&gjWD<$>t+d\pAzXOTќlϝ/fߜ}E֟|6B_ _sZ5d}.W1$jJuuFud$]mxC~_b&~aFy?>akQJ~I?ȏ!O3 QŻt)$GLSP~GFhwK{]]QP##\*+’*F3U߹uReҗ䵣ZwyaĀmƄ54/pWh7|B[1e s2yQhGPy8FH`*DY\Y ke~]|ڠbǾh"Ng]Kph J.9K$y 7^xp%J4 ?0 {5OsJ?xGK2)OqY'GER;Ӥճ[vH+0,x?a잯Ήt)9*^"~9_̙èQ2+7pCސUNaQ6HaZ{lM-I\l-[L-3eq]uU xҤI)Ĩh_ J]+[qNI>䓡?ϘD749ɓe㐱`0nga+" n:u/B]IJ~I\F՘Szꩧ"?dBGF_QRp)H,klS~ܥÀq}yH B}yj~nR,PD'EG?я1s1znxLnUU+PyX,y7_aCɼ.=ILF2?OJfHv+m2fj0+F;v؈,v 'r.۷AiI )gP.%Ts=z*Ih|1Y"HFrG/+(&җ˕ 0 h#u]ov}e^B%|52B$Gx +k`Xty%\RS2PF7 2},[nY! 3'sE,_zvmIk:(q@Jaʔ)hi8a+N_.5;uw0 h#G_ SzG%IvV~ez)k1g*}ѱ? ؂ >c$~ۑźGr@zZ_k?>’|csX }*p6B,b1 ?_O_z10L 0Uǎo2-O~?Pd + xb9[3èS> d,ޓz5Gsyۿfb . 4@ƜB9ǒK$"[zB'1 0 h#+oV]u_|0-",giTy)3f WUl/V~ R}!d{6vvxv7؁cùf3㉂A~؂ kA^ ?x $Cx`.vܹ?%';b YȋaFh[`^pQU_d񤐯#%"2“ ͑G)rg`&L(#]ⷨvg4pL?P-ZĹf\ LA5Cr aHX@/*)Ðy䑒Kn)wagalgu)V"{~>A"E2d2Q #ˆM6MLlgKYk:kE:d{.o]w=c&犢*+k#H R-CB% Hn{MDʏCv[YA&~aF|zwy3;0R7_J{gZ %GF>MTO.QFS׼_MߢĿ]9v1\͜9!WJ%Hq-uC}ݑ^L +.0L 06r8[aZug]5vG-,٧HT!Fn^T.2?XDoy;;E]0P6^xVvHR EM8gΜ=G,JP>oun<]LIuГ:xT!?]XF.  Zp> O~a~'^ү(8V~=}IRyyv_ aR*<<*%2!. |MJN!GQ%@K >Q?0k>EH 4/sAoNn;٩y;G2*:'@wj}k+=#/@Ww3z(>V7Nе裏җDU ?0 b"۷o@GZV4gtc/9_loMvptvۍr)vsY0hL.۳ld z}NuM_]z{rcb㉡ˁǜ(,bI&]լ/|~?JwL2!ճ{;&JCLWwT% ußuYdz07^ѭFG[& U@Y˒9z '_|1ACNA[lٲňEҕO03 0jn-fs¸'O2mȊT6bTH$jy"~RFuWсޓ{R[LvK #is/8|AOpf'tcRxdʪL]xtNČYMOTnw*NSAfPJn 2Q2+&׳ATdQbK;<PApK: =agaMP F=:Kt@ T䤵4aIpbC`:\1w=.Sz`u~ʹ_0 ]g7Pi"d w@-փA)Z;&M1JzN, rA>᧘'_rd6Re@_ɵ*DIa< 3M $KnaR0L 06 ňx6pÿ'|DVnO?:;{M26{ NDBdv/Qe{RŨ,k-,>Qޢ̭>c{}^{;l#?3H\!B +@~&7̹K!(Wх)/x3ďl2OF$S5V70uŐ,YRrvA ?0 ⭷җDЃ{|-&&3}O~x^`PQ @*ƞ2!˨[+v;c Տ F,ovӮcIݥ CDZT'LVۘ--H0ϒd2$y+ːheQr޽{aіAo~3b\wۃ=u6DQ3l-b88vS%!'>=绉%?ߏ~0 *k+!J* Wp B\"T~8֧e3G&O)o% ;JH&~aF[V^yemG_ō~iE^u;,߳SSGrnON\C@ *vqÆEZ<' rbh^jZ[2y> {HT a 鮻ͦ4/$' w ?0  ֭[+ V] 1ysajɉIݽ.yWGCz-E+t94djG3\52P`HhĦ`j](~ɱiN @%9<@&~aFdxb}D oOO #*NR.h&7HaGLWϐh^Y1d0yd++cFR'NȐ={L>PrIoС.0L 038$E6 0 1Պ-eTß?P/MDnשS'+&~aFM Qڶmt<+f(~7xUW]U0 YwV{aM_=1K/e]|!ǒsRe)j凟FEG<\تT*F ϐV5U'ŘG.4uOF49@ZT B%t's2)@/X`YR|aϟ7c/ȫovaga@Xò'LQ,GPRr n߾=} 묳RKlX@kC`=#1Xh3As]eT\x8C-!~ !]~ O1]T\GwީYpSg O~P2xa øAVۄR.]i&ڜ% ylQs̨4$ޫ)9ؼzk+&~aF@h/Iꫳ$ - zY)gb/̠dw 裏 j%> {c /N.("B ,;9CAy @u7pC/H׿IEKz`<0TwݴiӨ#' #1O>g $?ʜ Ϧ k=F*-6 7J+aaN,%RP"<"z%tw Jʊ8Kdkތ6!wqd$'|$H[+т DfH 3*#٨` ckܨs,&X4OɟA˧ HV⢔.83Ru|1ke01{mx,LqW ?0 c9φ`-ܒc2\;裏*  Ͻ( ď<!TE*"_H p~؂#["Nk!AӔ,d@tUWòc*@{~q@ ]LlfB3.I3C"2itef  Gdt`H?ݼx5#e2)C;gw+RXML 0beL)un;l7,(S:t([ѣaSS嫇N~2yȒڞiFlL*B^BQ]0kIx wȧiLt>R VFad ڠw0+"K7wέ!*HK=& E6){G[f.$TgaYHI. reQ7zݑPQnT24z!Uǎa2hggvo|4(Shҥ ӧFQ ;PɒRjsR@2)L>:L"oCQCLc3 9 UNCmyٲe9b-⢁j`kI/0A؛-(Ya-9?ڵ}^IƱW^H9iѮn~ɾXB$Up]tEP2ѣGITBЌ%l M̙3L- $<KDck( D姌U.zY#0 hdpF I^#ɨJG-dS L՚3bSEK9EpyK,S6. bϟWag4F(kOk^ϕauJ1$O$%Ӓ؋뭷9BUkX'' y c3hY?F ϐD'!Oj 7-8dpJ ^)c,{a#XP2(o͔ۀf܎y })Kf߬'GۛxDPunRegmLב@aw|عsg->c,w(/ 0{ꩧ,+0L g K̟X3f&~amaf7p?){&~Fd0$#H>cȒ!I_NtS=s#XVb !F9Sv '0`~dj2KSWV'fTlڐz뭼jB:h25}GdT!d;03BL_ ~,3fM.,fs1syU1wug뷓c|2bk͟?5,_7{x/gH p?H>oп/i -rf W]6Ζuhyag4 h? 㦅ϘaďӧO'Rw;s"!~> QCɦm 瘒.ƩU ;?ŕ~"o3Kq-Nl"R`!=cڵs3e8;w|/h>uY0:^鋱-1È6p'8/Ă'٧{;/ "5Ƚŋ1RŎmZ<:KN<5b, Qܨye)="E *(~UhB?~JC I7| O 7@Z+rCR4@+x ldT4m4Z?S]W|\CШ]V%۷&~&~F WamSSqŖ!{X̉ ?tBtҧO"'(X_"0']w]hU^?Xm\ 8q"D=̙3gN47t0b ZAɨx#CBam +Hi++4i%- rf$?J&q)KQL@NgB [*0{zO?]`_6I*<`D w} jFA"?Rv/+b)zI/Z()ʈ@qՐK9Q|e٧ ҳOJB %"~>eJM4ld`}]4 9!~o6.JI,Yu,X7L ߰rqS_VYeCÈC% ѣa>?#?Y$[n)Q5 <cFU$iv#Xw ؈_ճO.ݺ4V>m*$BËb2NÒ'^æ෢y7L>$TRcyKHF٧,*$Pu./~&~oXuF JaԩS uYyϔ'ʳ>TFD-G@b++!,CB"&BT"IH.EJ ?y$:5@(Y 'L|T \$a<,=?\{Rɣ'dqrWbLsh1 9HN 'x"?zdExdwHVcG /Dk=񱴡 5!2}.p ܳx 4C"^gH}J6BkHH+06{X#=ʸ&Ov+ӣN O`:o"'2k&{9\&K&"#sݝaga@&5Rw `dɒ?\fD{,!kl}]Ls>eyViZA6 >ys̡nwS ѿb,%F i޼y,xC*!AM&E7uL?\I'~eeCS"mRv/Q_)d˔anO>DCE7CU5!xdhzV95xHXfᙂajbZ&}zsJF(M6_aBc6یܭ[^("e 9W d9BW2nUJRD%UR Lq'v촠T5)~|^G! pE~j^C%ȃZN1b9pB&&?kT&cԐ )k0z-`a+"YmH"IR= uYeJ^1ֲe{u(+ D $VfbH~ iĉ xJ8L471]}etTXO [(.ǭ IK2"'_wxOJs*؛`z;#UC'2'VS&mM1#6UV173$}x${[u55mGv=+5BS5>d{yG/"㩗oG@%%!3<+ ~!xiWn8˓',%>Jf}vTt/fHgHjJHe`'Gte܂3S9,ϕ@4TT:Hn~!y|M%\Z~Cwagaqp=ݺu uq|3UW_}Uvu.ځ}ݑXo"{aQ) n2ECDA] 4LI8d /?V!Q_uHUA/C絆I}K." wagaeJ=|hmSu;Mg0H-Rlo(jYeAaJ' ?xFm 8j'fm$7O;n(]0 &?{ ?\Y@ [n[^u:0L 0 Jo~󛸳ВIEkVU&##/*Ȗ FTzQ=Kr=Sy@CpW2$1>f ~eCݫk@4@734"tqtxl:1$:F\rذaI[ׁagaY[_җz]"9gk5wqۡ-*2;tlWqaPgώ2dF}4 ٞ]vYEH6 ~'ꪫx!tbN'ĺ Cb`%ݻ7@d`amD<n𡺱ާO?% L&^{$疼SHIɞWԂ<jNx}d<Sɐ4w'PL}lg2M GXcH !!E\]v7agaMɹFp'?ǭZY[h}tfk!qD9AѷGg4*,:/!L"w0|)+@S?,v™!Tgx@䒛o9k_ a6AΚk\\94^B|B T9UbhRiQuM#!M81!'?aHd{3$(S %~a:ٱV\#f%\gux҃_ aPؓVڗ\+Zst0" YsQyɨ⊨F&~guV/ >]$>X|Xi<*I=ːo40 Ȅ&"~{۷oըއ~Hr(iR1 ӦMC>}:Û9sfTr_p?QAQ"lӣ3ҹ瞫bt>hν$&/LG1:Ӭ`k  Br~9r JPGKaPջxH@R 2N -+&~aF[#~5P_%?b> 029S$GQ-ZؚZEkQ"uFjTrs *;Q`Ο?CZ1I"0#BDA@c]$nJwANT< =1e_aaQs⇍![1=_qv!߆Nj7x#KDT򟷸iCjd4JF%u .gH#s2Q":$jU-ϻ7&\1Iݤ/>`1ɟ&pM7RHkW2Aafa5'~9֭[fꫯJ(rXWA2y]w4Ν+ƨFue!&~>?rcdHxCM ILMx<_aaQCG aF 0&ډ}o6 VA?g4 0N%k}`k k-חu{ ͏2UUFQ + $ZT]H8~Ǩ(gT,K|:Qdc, p#EUĸI\^ 3L 0V0}}6خV[#e9?A_B[oO*NI4}T0QbDEB3PIuS9Qy$QTQ R=I.B҅W"'_aƊ [ !7ͪ#ɇlyu)IozVƘ_<#7ҥK![KoÇ1bĴ< Urɮ]V݅ ?0 #+E;vī`V[l[ru!2K*/2ϒz8)3Dg$_y9`ݺ{{wbN:)6'k ?|ujTX2*3. wI'jz+1!%o[ꫯWagaԛQv6p+`/Xp"EVG&>e1+5Æf_Sߛrg.ޱso.OXk?.xJTwRdT}qjTҤ`I^  0 1!ۨ`ŏ.;> hUlBFyC5^NGx ]>wWW߳/sen7}[=Km;x~~?9?E ,JX ha5] 9?vf伕lRWP[??!K:b,Vl&~a_@DNc9&Z]|q<7$n 0`W6&.箻ʖw}eDRaMA+k$dw2d{5Mj<Oz}^Y]?6l n^߲M7t˭oޱ]zJ]"~%`TYE5*/Qq?wi #fy-2K2Ƌq5ֈaaD¯bK'~QxW[m^{v ,!AY˖t-@J˖-kɤT8e CU 罱&J/l$'f[ܽv/to8Ng̨kBl/BFniNЦFYLO#_oī={-g}]6)/on-\6m۵?s!UڀLę(M/iQ LFEx1aL$FJæo^c~ {oKͿQ sml-nj~n?oD9_QPYZۨ~Qٟ&~4qMHyH1qo 0 hnQ:HdS-BuQoS()QRZ8uˉ.}^٩3I뗧7[nܵݷg2_nA?OQ ga.䒨FE(VT&LP1Gy駧q_f9RvإK^, ?0 #a fÎP8Z4I7~.ֻAWLW L۱*j`tqPY5˺>GHeѨm ""[e(8*KR03 0բdc(_qb'DS@g}6R#fK՜}W\t۔U?%b߹CǓzSz9}$*~EmJJ@!1N:)ieŤ6O`BC=K&&~52}I^;"~1C4 ?0 g'!F,0utϫ -7p"o=?%^_o`:ٿ]O~j_K ֧~|vo,m}ev?:tM-sܸq Q^~3Yi{eŌJ1{&&~}._IE1!/{aga41`q4+RBD6n#|wg)~'-/ 9~tUoz./wV F/п6olE]m?v> k&t¼y ܣ չV?$6zph-{WSN't 0 hJ;{ Ͷh lǶmuӎojw:Ľ.X?뗿e*ͪ T,?aٞ?If<0*:Derwd,;)Iaga4%:(IJc:2F:j8^ j h5D/ ֥\gCvsŖ5s}`0HcӫKBdP:XSҷ%`6V[k?~9Ň.ˇSeTh^Zn駟yK4wpAkFG##<rz"yZȨo %SUF[03 0t.` fƎyPT2R+YL ):*x c^ŊRNvϷ~ma/mhY_1*f[=vyĈ5ro~̏[_"tGN5*(D=x Z9NBɄO֢?b̙ۆ`+*oU^cƌUٷo_q&~aFAZk-3CS 5xr %9h ߕ/ # #gImiO>]r{S#J0ͿA[o3w^c~bW?ϸp؍2&Zھg:묜>(ܒ×XlkblA|ZʌBhңJu/ P嬯wߗߡ $oSg6?BRi[f3w 3;M@<#c=>(Q=zr ofT%߁L]暼0waga4<]t!!0, o9??7{ﵽyN/me{<7бC֓_tI\,zWTM7RF'GIA-?!8Oby`'uZ@FElTutY(V=A`N [Q0L 0ҠNq˨n֨^򛡳h%Q_yarzaaT:noM_wI$-/[&mUNB]]8G#GV_a7o^lrZ&PyӔOr(O~W3+{ duB!=bɳFJ9z,YQ=C%ӧLRaaM4W]uU ]WemlPqDj`7?k#/{qEg[_ءO "ꪫ:G7-Z(̓8nRD&DK"yФ?3Q=GF$ a46o^%д=iV寖{oy6WhW]rA~G_?2Q'ٌ?DHF:e2SLOwa͐HRU%^uMekAP73{W_ 5u$oσFE"Cʍ'I ax]w]"+X}IO=T;-+\Nj7F,RnRAnܱ' t>Tiwe-$=6oذ }ر$eKR@!$g̏TOq?fH"UߕW^b[iF̆nKR;sWO%baX=6l&lr]. 5;i$F/w2!?'X]PSb{}6&~-7vө{Y.R䪕ѫ颸a bd)EV f!`=qHԹr٣nP=ȉ3f``ӦM~//u,J>K.I$v}a [;"~xՋ|UtGXa67tSv4Q~&?wMɈ H`|X^w yfw;@z9uٹ͘1cTU*eaӢF .$OV (~0C| _q߃w=|SCg߃Z_w_U,]Qs=Q=GkKO0 c[ǏguoߞVuYj7ljVYgg"NXhB5o月_~!wڙUt;{fr|k0['Cv-r6=>T8&V^Y%_c16 H:F=,ZkjMK [$O' ;SN9IwagB\;מּ?99a|سg; Xke[ ByAُY:e?{&ܾr'meJ P./,FH=UOXr]w1Z@'ȑ( P Q l2{36#)G98ɤe˖1FEg @&kfXGΦ+VU P`9.i-x8NH !__-Xý?'1!.]Zj;Va{,/ﶾQ g M,5f>ǑOzsQ\Ԭ,=E6uT /#hqeKO7tP6 Gxz $Ϸm pB/qFqf섄B6DT?~ ?B.F t?*>y}l*.B$Dv<)N%p:}f2F)&xvjHQ :p@BN) @[lb;)GhW &X NΣÇ <  +(jjΝ9 Wr$ǫK!' 1Q P|' V{wn'y."־%Tt{~3gKR~ȳ;Nf:z0 3Y-gYA`{6a1 aS ~ nq)Gϭ,$B@n[ٛ`}cTI'jyz<;=pziw I`QLnJH]:yA1ܚ.iӽI F͸U9җ|; l]gɁ vze*=UGb2SOBO!D_9).Yc̫]g]2%<(KX\̪Շ6o~o_<,`La`jͲv 2ݗ#<ҍK8Ƈa6;0Sj?$#l=%i+ru3ʁY ?!^@.Y$CB{#N߾}:k.l1t_)+?v۵y'g8;XV4S>ʒn{e~GXٌ4PPPBҾ}{B*H*r޼sBN0ԋ)ѣG?ټRI(퐾b I]\:֮K獡GO Kp&@#Tş<\Lk ݔhxtٶEyZx1Fv G}t~B!x7)#cj2v%bDDԲWR\tgW;tOq&=tMz)hy?J@)ڇS{`X$6l@Dh%-\ tFc zdW!!1M/&Lg4wu2aV_M/W(Iߧ*m^O=Iކ@АNiCK9/x.m{g2@t$Nlk˖-.3#-[*g!'£DdKHVHXI,EP;HgI͚/(SvMR i'Rdʧ+\/|U+\rͼ ?`B5Sq>cn؊3m,dU$¶ypt-ȣa~Ij%BO!w|aÆdL7iG|"ɓ-~p?ȻOhrJZ4%XU"Êު^g4?*9Hx1OG/&Xqv׆tS#lP@*B=ux׮]01`K[NۅBO3j("Ec::^k"9`5affFp/+\rf W_N㳛[|IlN l. s?dkW'\X&3^+u>ެUY4b B+ӄPzL#/z^&@΅7k7mĆ}^۰slCs!ԩVB/B"JB9ZypJKf|dʪi*Uܪ~1J@Ҹ9! n_?!sE@ټT>[g*dCG?oԨ*^R.)UzW=ccB"oƍ3 iq2/_ƞ7x=2CZ$>Pذٳgdh7-4Or!='$Bx͛ԯ_<`gpn[$6]Aҿe(GD8jd[0حq#گnES7Om/czfc <ݨ> 0$GpR4ezdȑ#͝bȮ{mÈٰ7$;k)kEH !_|۝񮴻ȃ60V$鵛+ehw"-sj{lޒ~\ecbҖ2zGGgR5koWVHwZ\l>(_ɱ$|?;Ƚv2r٠<*=a\]zrr-EEH !DD*ժUscCdI#(n$ٺKH! ӛz]s,Ňo  `Vy|C/D)h Z^x"5~SNx٤~ BD0;oɄg.ͬ 0;ݺ mq1>̅[\rCb6%se5j>נaw?πA2V]c;B>tOIOqۂ%?k֬~kעŋ{a0 ê*%}ZBρC JRRRfQNOҀ"增X`ڰa ϟm۶#`>}Jwk$W%7jC_VA}~##GhGhDq0ՇsHDOA2<&Lx8qڅ.zR>.g} ?!ނ… 5%槖}{0m$Grp1QJ$ę<^xx8ه/s&Ȋ<{MnyxZd唜!'Pإe(a駟?Tro"(SKI 5 b2 H׭[cEF"y3=5kRzVx\^Um)@o^jm>(elU>7p@\sHfz(Z="ѫW/&@p.а)kl՞^00U%M./!'C0 h`Hq~͚5|Sd şK,a1 #l=[m*dTF,rCtT825x-^(a.s0_G]>U_>E/׮C3v_W_\jKʤ~R'GA!d`>/8rr(f5?kYP ON FyZ{p'6l9.9zh.)))r~B!ec)'p¾}B}-lTYiȬ%5(CdhxSaAݧOu)\L?֬e7$qyO K*& Vubvk`[nrnώ Fx-]'S\Ur\}\Qr~B!ФPK/]h ?}@2{d޶lB 2u4Y.!c O߉*R{졔42}A~r$HԟMwU~\O;3.KX"emJ*͹5zqZ]ݭ[ =)pg7x`=2x6 w73ϙ6ZͧL hL#±~I6a,{IDE $!_1}o} rP/LF{wH:~wkt;M*NX¶%֕*L%S1|^:WoW_I,?#&M®Ӝ`2~dgΜ)ꫯz&ٳ6d׶ m^r\L,WTFP!'C\xᅄ)]vY8VB|k~5r~M^խۿTecW{u~JJv>G`J̪ZM_~y@Ҙ!>SNFomh lTmf9$(wq ԗBxK/图Yfut1Ǹ LqQf]q4*6;+%:$00}7*TbneemTO~/J,O)fÚ65h^zQ? L/߅mCzmre9Փ}QH !Dn:ԻcnݺyVA… *0/|E l5W4qk>'٣f?l?IBŗݸmROQd$AC&BqFlrۨe?"~ >S_)<9z -l{-F G7,hGn;ukTZZRJ~vX_Dߗ%-V,ʅ=?]zfFZm%Dž6luq&~Bt1{sSaT~IviEc+W@6Eb&f#!{喕,^Vy()O=Ċ+\߰U 5A?e.`_%V*QcN1FK/^UbѢE<uΜ9nGͥ5G ? ?!D&2;~?C6##PBW_!`r98xW3j>].d߾ .nYKڮf?]iύeR7ԻL9k LgZ?gɤ}- ?!OH\vθ)Lwʕ 2u&$$4\Jꫯ*Gq{p1c;KfTA+:zn="$&o-[~s[[\u=Ӈy䬡t+ءCr\E\Z;QH !D< _͛7wzT{=1zj%ja9sG˩ܪ^|n~[hQV;r% xGH % g=pJƏ%;v|† ʤSϗC%V֊3Z㻳p8)9; ֢GXA={B37o?k׮M41SO,i#<2zh9s[lI݊<΅v;x V{ꩧ~RM>l>}¡7xTߨQ-[+XBOܚD oK{[O_e8`sh C%JRM/lx0dB8+r>gq2~L#oSvmD#=餓RRRh $(n6^r/fu$i%柸'-D6mCG2]/T޼2e&&o)~%&cBŭ+k#o/,˖-=x"; nnoߞK+g BK/wqw+] fj-_5%W>JdTVd֭4S|9.vG#>6|!wM׮KSJm7GiU\rWL;x=?/^S#--Cwh"7lb!#B\CSq #kGbݻ |8e3eHI>m~>fJl+V|_)Q0_<3NY\Lvyk%/l5ȓer{q?=P"{LtQ.]]#$BD!CuY`QUoSɆ''cгžoСCw~EMK-eR t{b(4u~nD,Ys[zuK:Y6(~B! bG\Koܸѳ}_xiso̘16ϲ|fbP˖0?ۥgԭc2~ 8۷/W5j*'$BDlBK?x:j1!{08^~yט`nnV꫒Kn- EHլY S}|8?s 0қg+,66%Is9\`GBMrЅu֦7̱q%$y2# B3׿.]֬TmI)n}vKuډ~h9͛ t? _!'"W1s_.׋/~azzz8h9}2 ngg;Ν;O'Nٳ'/}}sP6 80¡T}m9^wpv8NGk3_|EK~7'x"ɓ'FH !rcvs?O?1Q?W&0Z=x &:1H R~[9i7jڐC)_FB.__~Ƿ#F O&G}jw߹w;v,WN;+nBhҮ];ƍg%TzX¿E;&3M!GNM[ 7VZ~B! G\2iҤ<<߼ys@Ea7n AY,;L1' ԝ[0"$>hѣGKhzUb~C%ߡ}05G[lᔡWo Q7le\! .6]} ?!ф%>M5YE*H$G[E,ot@ZB1Tz_*?_~@!n ?mp=#WzPy)9of N w \`~(@} ?!QXSO%.y'BNnSEͧ-|B&D ]d 10߽{7Ůt'ZM?|2r:|R5/4f̘|Zύ ޫPi{]XzmDMˆqijfMWa.nBD#G-Z4Len@O?,=i(]v!+=y"|7Tf刿^:|V^b[%&gTfGl]G5V`uͫ7l wr\3fBՃnBD3IHH (뮻"r?3ډHPF5Dpן%UȂbBT}}5RND2m/2dlmŎ$$bTXyo_7x"$։_޼lZ>}peC !'"LSz:+V$Gˤߑ]ߨZ=M/쟳"I%-9/LqL\[t7lUs-qa5~~Br%tEq3PP&~!Ň#ㇾ"!/DLٯ$>t)Ez@/w5N(v67i| ɿ\R*Uߥϟ/E۹xɥ}Bp 7Bza0$n*XVա -v~̍Ԝ 3V$@$?2hz.}OO9mhu'%/#owwoب˹)˦mE\Eur%9!''Ѥy%Zf syx.6C0DFҞ~˰$b2FFg/|yIv2?ܗ I~/T<1T}.u6/ۜvS֛g3rn"~>}zƍi8䓩d4˗/ܹ9CEPrrmrJ J>Dl&8N2M1/*opKSJo*(bb/ġɖKyCX ? ?!D<7?O6}ʔ)ψB磌Kf}˖S_]F}I%vתu푃>Ξ=|6ŁROWu~~B88?uЁ?Kh $jpl>O?t7s ЉpHױ+Ȕӄ aw7KOO*U^Zr6ӌhV{m'9i㘡ۍ f#sp͚5>U ?!1cǎ jժ۷/®3LW7͜9:묐o0 i^؁6 c){&/X9[EhXU}݇M%!ChTrVA3 L*QNõpڜq9ѝ&=t +O]ؗ-[˔B= "3.XF; x?|$< c)`ѧO=zd·  _ _{޽{OUnjjSQ:u_?}cI<{j&ׯ__(!''&555|݃C;:xq 0]ыeL}Ôի׫j"o^-_| \iuQwKNUW{ I&φ ލ:ڎQ6mQBOOWV' =7m䅽gT0AR޴S)˼^y$s 4hƌ|oGk%~a/VXW}SOd*xNfc}"amڷr_ZA2̭Kg]=gg)Nqh_7oȹO4h>!ʅuֱLpӄ/v@tE~~B|\oժUN,_Gn~ra5&%%mY04F>y[g4|RI%_FxJH-=1㌙xOOOСC=pg={6 jr^ ? ?!Do4jSN9餓*V8l0*J1zǎ>l7B;dQtW$l~oǜ#v~41KΝ42njFi1:){,c!z_vً/Y:___^.!\߮r}_?Hт56)&7of#6Ɵ1L\ !$"1qli*p ?#hɳҀDR^_P@MڗuKn_X:uKDSgU_)۬_g 8 "\sڵ9.̭urZW!!$"jPHMKWa($׽?3X *x F^vm 8[/0Hүbd"OUEC ejQʽ'xf}l*I9.+3LvkB(`# .|AOm6[P8s?I`ⒽJ>gAhs˖ۘ ,`Y:;9-/0Rj=>],ߺuk]tBBH !D JNN&(ySKn]SHX~}oԨQmXq'S}d]`Uɔ#OI| xx&go6CÓ@uuEWի-;BH !hH!1_ ~~-@w_ :,^ ,O^cd&=Mܱge|S/W?[e,Z|{=gƩtwR,95jժ2BO!H Jm;w$=Beڃ>ykR"r7 ?|>'Olhn˛'s@_,>1i\/\z Sްfo̓>1O"qR݆L6M!$"j|'VD_׶ < c -vŽ O?4 ^q$q7_ { &kh>T{/݃~{Ϟ=s-Z(Mr.M} I;$WUw7hە?/^;xӅtvTR^nYBO!4k̃ۆsi.IFFƈ#t|dqƑ $_̀ 0SnZw;wK)Ͱ=MW^Pz=4J=)biIݷeK+!'Qñ=y69==]TX~aj> @v7q>}~C˳Kg>K7$&GQzeDs,ݗVtFjצ\'c;L;Wl^=^Sw[]^2&%%oO*Վm%ZuoՍ73hwOmv 3tc3BH !D:PB%vxp}*uSLq$?f{jF+pcǎ ?ВKn/#-PmwF<5ݟe˖>y6YYn73ϲe"&BD .f5 Pb7sLG=Y˃ P|#23!)͓~&Ջs]7wd۷ώ O֠_MrG}<B(CdѢE J裏nr)\kҥ7uQ¬YY/_V0Cg8߈MW {tok~C ]\qޞ{=>4QN7_~\`+T]pB/?AI"En?gO>̽p 餲5 ]bpmf}ߨW{O4n1"/#3?kV{2t*nld GÇ=&l*mtߛoˎ~B5“#Zf#[hݻ{}bg}^!?SOx]wus@" n[k|/3S0 5jqov=t͛7{DZ,߹sg.+WVO ?!&/A*TʠUwEe xkfmdSis{9sqy?Ӂ$ng}%̰&mHeF?ќd|;)Z}m/ܙ;w.G#!7GTG]jɡ{V!$"jc/_޽{/^l@yˠ>O5jCZ9| u@ba:) QJ`I0_ͮRu[j>C򰞀WOOHJ/Znwڴ;p5|'rvX~7?af:pZ._BH !D4!јe cHh4-.@ eh3ͩ+1!l̴Aj?&z z&kd/ƮJYgϞT?xCǎWA7ɼi-N}/=(Rԙ{']h rL#!$"4nܘ]vn"@13~ᇰnyV1 _)8_1D_oz!L8ӳCΑͻ[J;,ߞ_.]WԨʐȿ<#I__Z{_cWuV9N>;r<]tE\`kժt~BM`wsL#SD?O #ah8p @yrqU^v2~C1݁_΄.Çg&LC`=VD,u0U{MRE}BRv~>7SJg׮nWޟ?oeG)?o[3nG8VW'!$"t҅vySk$֭[ـC5Zm]ޣu*o39z;~sj0 ;FW^y_6~zM)"/o}CBbz(5ώJH*Lٝm|ѡLJjOq3%KxDnF98+TM6F !'Ip1[Y 6?/_z\8#E$I{xyrkdGC2qرv 1tMY1E[oͪlTZu{9Wuȑ6ˠnBh2d⒳:+VyRI'>oCUMaa"h$j41Pofkv9Gg'KO&1Pqa-72cGTn/%1yCJu%S?ogXwUJomyݸ?9lh@5'Onyd%uQq?>Ӹڪ!$"pG\dD!(JC?k?I-LJzL1#ZoH\x쐀] Pu@#/H9@elTv+_me1!o≛&&t%d3V]?iUoYk 3{v펉`;9Ƽ݉jꙹqF/B9wPEL'ZkA HԢԬXTz ͣT?ѿ2d)jdu$w7#%yM$_t3{Yu >cfӣ$KH:S<1S՗CmC%$m/Z.rm+o,Svkm+jxO^l{Fodgߍuͷ{`LDtvxtI.(grqq3fkBhBq`JJ q $ 0,qT!u~瘛L+{!I#L> d؞#F |l3;+IyP&1/X~߸U{UT"×{L&KKHt_ZRG_jkZ[{vm/~K=3%rŖ\wo4ǘZU]v5V.BH !Dy7KyR@`H/SR$8Q6FN7hays-ÌzDEl& GK$UӧOgϚ5T[ԪӢ'K/$L+Vowؓv0'vUSsk\su=}S߸x;kxح(&Y~ѢE6ˬkBD7O\O<^:* =qcbۘ/QJ GL6YM61l.mR2J01W*⹬#: V INjbbMq#'N47`gB,C|ISNׯtYRzkP-2~y j7{~40t]$\E&HHQ&yQ;4&ɨQLQT^嘬{<Ƽ[|@3fBR,e=` gzر|:k+_ zꩧhZv-~0FGI-z7~\ZO=T8!'FMA<𧄄2ex *J>J(SÌ!o3a+ѣU{Zeɼ+@db[$%=S}?7uWv6q%RGeWJU8y֩;u?p tYIO5̝&Ju9pŎ=XpBO|?uЁ?s=9 /;9$#h.+nCΛz7p@FY}Mr rΈ_|쐝ƍrV H2Nە _|i~'%gxd~C4ذϙ>;*EL+%?ň)3Es}ϙ3G!$ƣ>j)?'|>y:l0oNwYx"k*4~N~f;CuRIgn9xqƗW ?Jy g̙39<sc)=;w~Tyg?R|TRevתY=o)SW^yp,&3yQ`.nG'%%qiԩBH !$\1rH3qy9+/Zh33$C.V rMrq?8"n*~cAqk?㯾 woJsWؘ#bq| {ؚ|'1ic/pl75mwGL3O!+Vmtݨ!CXet !'CYꙕ9묳x.Amhq΄@Zp}MMFy^z˦&+2dLIs1I6 $xugyqlEmnr:ʔӏGߦ)+*V^\&[߸ɮc+8JNJ.%vqҐ|\Z:߅B'3 jիs[ bxh橝sN"0bu"ׯGq'O>pf|3*qz7ް|ҤI4L@f 6Mxu4n&DZ#'$/Ufi媨kk/wyCÞvb䥵!|w1>4K+?`>V ?!p *?L2<'F`+Wf׮]KXm&p%4SI7ɇxfa6@o[VȈm6h{^ԱvDP}IK,RQsmFVk=1P>lbWҳlذvJZ[w!$L~gʊ+,%j׬Y[{q1U ݑMF2PnG{C=dop2KfHPԺeY^ʲP}\7$?ߍñ;S㍕ =(޸qg.ZҔQ!$qF|)RfpTR@+M6u駟L0s4hSD'1a:brthlgCq~qƲO>"l ]4~"]zNC,M t붲d4`jvKV_^򼆍wKGb&ĕw#7oیrckf2.s]=c()Bq9U4{vifa#7<ӝe֭۵k׎;VXђrК;YDbkh?KGZ,;b6~ёy3/Nm1cЍfN'Yf3{4k>y UbJ ?1i//|/#!)\}vhw |˻]|ˁGW}ek&GGe y2bG1u'ԙ.DFN9唓N: |x`>`֭K(|͛O0!Չtyի\1c+~"Çg#Wh9\]̷$#GӔ}RM@RsIQ(9SkTl; #MhV9h GD|ZenqJZ4m+Z=6h۞ݯ׭_=xb%G#%ڿC lnǭF:ӅB#K~mm۬JchѢl/3LQ#MwsJCс}| ^GI?@FAКG5j5msݵdTfZ׏ P}ߍ[ >;tbn)J޸y %U2~K.9.BxPhL|&ShӐK.;UӸlʅT!}4_Ы|H8q+~="' 1x]oFHv5Vbu]mP}_+3n0,Oq…rBO!<1 @a|,ny-t{ӑ|Lܞy i#r∰Y;_3d?7oS+v~Y\.≤v~CQN*T^W}Vx+>"4đ8+6ykm۶#~Bv"@93ǎ eƀQ'If}||T=LvKʡoӌ[ /GP7Ж}4:Ӑ y9C-9! O/XoGrɍlU{o֙pxnGXb}?*.p:ߝw H ’=^]6"?G8/^E~߾۶wkJl)Xɿ,*u2·~D",[*iKc;n n{i喖*d 0;Jm>}=&>\Zp#k AbjE:B@@CrG#b-,[X-e($cQO< 1@j7r\=~$UŢj2tm2el'T{njJu{oUtM[ݹH'vլe /'K>իW}%}X6lX !'LQ:toe[nէ?7t$2d +V8^2u|d35 G}&/tw@Vh[zQJ@QnzFZ\Xr[}IB9(^4l͠!&~^0>Y泛bK f@vZOt+*&ɺ" !'bVp} đ%<B֦ `ƌ=0{l$G aY5!ۤP}ToCa jy?=GƏoF f|ϫWiq+W\["O|_2C%&eN]&oE͜y}C| έTmC%fĿ.F,S}i%SP}Ykwz_J𻧟=Ao޼9Rҹl95.pŠ<_|EBH !`dS0'v>;ꔈlBL}..iw9lfÇH=lg컍QIIg+&L4~$ %Y>:Y~!Py~3YتnFӯ߼YPҊN]YJRr>OJZ T/WʞzQ};vnέ w5`ńƲty 1M6&*Bx zSVl1[7ocЭM Vmh2Tu:|@\!2'z뭀JKEiϳPV@'⍼¬\7 [z\:m=\D)YL79 ^%G2~>|9 Ry+.TeS[+VZ҆ҩȿ)wXw_*{ߡW7SccC'k4{ !~.ҭ[7.vƵ !'ނƤO?}SPthyH"vU۶m#QNqqib<䥗^ X~MȰ쀁H8}< 3h?Cs;;hР0w%1GJE|pIkZ\7j V2\knV}sj[5<8cgddǽx8oS@bE'OY/Bx \S .LW}ׯLcpGKzBJ+\= VBLV~=T ߟT:ːfHO>9tБ#GyZ=Bsw sGQyuϹ9$ )Hh\Vhj`Oo_c/;u^wQSTߚ ,^uS/n>9cb‰=sg%*8xHOVrC<BoA𝜜LBf#}$+0.(_G洴4ğt\nT9v}7GEN7z6矷Q{fxC㍠T)"ŵeҤI˽apSud6D}v7myÁ޷osλ,?~v>8?#Fftcbܹٸx SxnXPBfy֭yB~B!>Frbfyd^@ݲeːQÍ0?R}QÀʕ)>B\0aBd ])DYb\Zlz;}}|-gޓ?'Ňt{Hʅ RȟHoZyɐ|c|pʘ"ghaEO?~;qOa@5:<{bkPT0ѣ]BիW'R!C`G'e06lCO6ZzsHP!i&X[Em*^C,uF,45A C=3n GQJp /4=F*!H:O|;=ڲS*s2u4lY)g/C&T+Y({pB2F_Ks<-݋Mn@Q,ګWk|Piy|C=J xY ?^ف(wz<1C.3r_\>ř߀.BH !!%XRJ8ԓv;>՘ZVR2'޳-8x vE82 go UM?I9:5S鬦4@Of*x?}XخcALSsP!Vѵ+,JUi" jBڮ];rBO!JB"3ey@k>… \Cd* $hCcU?8=G2S1t |CL{+5Wedf?&7"SRO+L?A5uI,^d2>wMY: ~.g9G5jU!B}:DN4Z >O /oieLB2ebtZÄ.~#cJϡCr=裩lC ?!"-ZeԨQQٳ\@Edqw/ -+yDv8=@][̐@xРAkRTT P}1mƌz3g(m6*@gD 舀DR!--$Is= A 0dw:4ȸrRqL'& t%?*J!BH !G"xSOE\EwKR"~!f dr Aʞ>_7}k1`#$剀ch6Ւx8mLGaډ<4qjn.Y8N֟!~Z(bXYIY|)q{c'4U =HtV]>,qRRWњ5kxz!$"wpxرD+V[?p@ݺu3<OLMM%p@҈89ed5Tr} HPa;e/ҡ4klv{zzz,۶)nWAG\8Z*8-э3KbmڴZH͛7""+G`ХK;"pfO4:)P j$`6SJOIV3_Yect6C->/ozIҒkf I9P> jG9n0ХjAB<˩vY@/yEY BӠnT9@_Q1`=ḗ򱎸؝ܻZS7EBH !$r vi2,ñ;wfuԉ?;杘26+ 1|)>#5+}w}/yx!F0j`w-Do[x0Ǽ_tx k[oE]q?zSn?lYMw!_"7WQ|٣>3K ?![ +V,O?<*SLȷ5ηѤ\c"ZzìqET!E+%Y3^>(qhy _[G[H:u*54=tZw#G(f0Qz;Њ}xDX͙n[Ik0Ak=<8zǙ#*brvEe^BO! gͥJ`?[>1[ *32"È(=mί|MDΑgO V|D*3m; /d{9WcG$2U XXb Tu 999Wg~B ?Oz:N:$ٞ'̵vWl {cǎy9q?,2%M}MpEpGsRw$Y8fΜ9|p}z{yy[!&h"Ci1eڊtL° B淔جvu"BEc=6ܟyI'\yP,A(F4YtVpC ߴi,G](nY:c!/Z2%lX_>|8 g! }E8 BDxB?o$y#5kJ:u NJJ P-.X\6bF ? ?!D@,^7x`Ըq〧 I&]v=/1$ ޶m[\~ 6[ {S3ftxmvwAg07Q OɈ 5DžI~Nyeك׊͛7Ypa\@uZ !'G,~BV8 ,J.MYh{i{j׮ͫSY&tW^80ZD:Iq0qʔ)/^v[KY#7tYȆXooNӦe]do)>C o.z$B ƃ!_ b=5!" f=a]8HsYr{ϲe{;tv͌K+,!$"ni׮ Nj D^VTr>WM'`g8̤/7pnA }Zན̟?㎳ x !'1U[v¶8~v ,-^|L'L4`PNm'&y|+89†OΟȅ۴-;3)e)̪Uƺ~~BE=b*TǷqq$/q ;2F3W^q\pn@9?}9sH$ :#(muߦȳTsΉ+!,aeo?̭sW4wby73cN<dD?,[o]m.A&#?ad}6mZr$ISI ?!%Osan[JF gׯSׯ+a@ {fGm3t_x\_[M7dUdu !'z*qmraȵjD6`x='W_}O?M-5i'!* 2رragMqQXqS}?N"!$"9r$qLѢEgiyB'.M#ݽ{GF&؛+2cBlg޽{?_7׳gOx !'11\ enwJPoɒ%Ѕg|s$_iiiqlڙ# 9p\H!T~w"`1s8?qܹ<ʖ-[G~~BV2 ¯/y6l%] <]~M0|8v"ubWY)7H9V.μիWr|zmvi6x;BH !DA*U"!wjjs:tĉǼ|ٳZ.]j;^@vNEQ.7ntDAN&$$"B$XVXJ޿wϏ?h_|XSkTY/]wٿӧ= 5O֬Yd-[䘅Co /z\1WESNP!$"Vi֬ a q\={>|eP g#?C;waȹP`<: u7f}&ɢJݣy;+_H~mF1A~B9g͛ҷ`ӦMAU}_~?Б|d'[S }8=o2{L""?/^^Zg~B\q45jo؈PC|ϽJ)9|+WTѯ Ed;=fs2[c;s:`S}4 BO!b긎=XbiӦ#]P L9ںu>g1ti:99Եsx,(z8W q 0[>|>jU.B0}%INN/_L0sbe 2 gX&R`h\7xrylԨJB;s=tXS$oa&4PDmݺuԧgcϷ RzD~x43 j!~MȳqI'لBBH !Dc+'xb/[w6Dr'̢ET _̨C%b9bwN^DBrYC!hoT4WBSO=kcjjByVLo9A:ʩ3k޴7BT8{$s<!3Od&E G67C: *yV}XXDBB:ZBg֬Y7XDFRUe4*J@JgF{;gGSId#dAiM":BGKj<8)) g$#B'СChW caR1ѵznۣ"{Te w&R+EO^B{>Ar)c0BHI ! p*4)J<>#u\OQQ4% R{mOr!{dN@ lPU+\>O%6`:fB ]v!N "qRWIΟ;=S{$,@CA-d@^nc!@,=pv wjrUKGbٲe$IcF ? ?!D@)M% * ܉Ib3F>Hѩon ,'έ9sϞ=j/ô`⼶Iqn'ʗ/oS:fB7tQw5wx*2Vga$ۍDS~zn 혁wRptFFנ} y)+t.[,W5&TPڐa#"~ '@ /D}cH@f́0&P*[7Lө6*?|>?p e^ȗ:wN\+fT%rbŊ+"s0(;v^Kp:<.;xࣲ U</Ġ*Od~Ppm]&|*^,hK. IktGqw/mR`ɉ˅*HrFN\rNI5W\!7˜l#G ? ?!D/@VUR q f:yG}ԛ; ?A/ VXuTDnY Mc荤ٵkz7`=O;v8#ȟ;3kSF}=x`>mޣEЎ+>z u!''$_W_ǿ짞z.\pLL0@ ND 5&~' Gư89_ H)SKa:*6f>A=\=YjU6S(=kB!q;}ȭW{ .kшl2^ .x)0"RBO ꫯ>co|Oڵkg^ Flْ7o޼@}4%DɄz6!Ӕ0ECCA>z2yś";Dnڴ B鷳Ɣ՛B)(1eGn1-!goT^ sVڻfDń>묳y=EB ?!D_Ӈ}ע˄%32y HHjQh)AfZAHp@F~ ^hG^H2|9F <&cwz,sfpLrq_y_Y޸3w[?By)I!'#:AX5| m^vYVbSE@+V5 ! Tdx:0GeLCn D}Ca%0BduH22)EvƐ!Aifp+=GÑx|Xq1/M;,ΈvܞDܵ9䓹%%%q@BH !/g=SL #*T(uh\M/$A e.Bב: t#b܏}R} Y=aBg#oP'A[=A9܉81Ҏw #Y[˳pWL.:~BO?nl۝鐛y睬6!!!o#.;DX mCRF&3!F$ZΩ A"D"f^kG6L+#ȦjB!~Tsu"lrIn86yUPT\TNB ?!W̙3YGM}` "EQFĎiT"jEKԎ#ŘDDn Z/>b%iÇ .Fܗbd:Ks#, Ǎǵ^:uX- !$~9O<9q֬Ygq+޽{hmذa6+"s 9"Ht N.ɮ;,Y\}uE]T{B ?!DhߘQxŝGHb6q)p 6֭ۥ^Zti'L My!0`@Al5:ndqpVuv(c*vUnB ?!DpT`ꘕҿM&&&.\C1Oן4i#D f,cBD|_it2]XHA 6ZU@l~E)+wܡV!@S|yb={Yőq0Lĥȱ_N{vz:UT17rHWB ?!DS V)ftS is\+.wQ F U܌ꆩSBH ! 5"j߾}S'|bCBx ƙU`.Ӧ]鮍bOf 3gSN9@]B ?!DKKQ9VwbƣBx*99+M x󌿁.Arbڮ`%Ut5.ex\.fBH ! ;w&[Im!3F\ r)5X+;øe5K^J@:<36ffBH ! c9xhƌ|W8Zu`hI>:֜9 yykM}zfstbLw@%׊B ?!D[o%*UTw s;LH 9d<<}ƍ|v7 awM0sM#ײeKgXNm!BXyI'=sċv$wطŋ Ͼh"!pld-u?k֬rʙ2dJم~B? &֭#XOBD&H>rY6}@_ۆ.GNܮئMS}:uRU!'F r0a(w ?}-tD8͛ llWȴ$f/#*T`om B ?!D|Yj">_{EnO1Sȗ=:3뱕ێ>}z…m ͫ*B@F#Zzs\,ߞ={9 @7oEYqfHo>z<NlZ> i̴;4C ݕ;mwu%J,tԎBH !D 'Z*VXʢé¿ŇRݙ=LgwCCR/{:\Cc?cV=ƍcdn !$"0B8Sȇjus'A"΄qxw1j6nhչbmܴ vG2DB ?!Ȝ#F3-ZR+=gDC]hboٲE:W\>Dj_M')3O;LBO!@xMw}wC:_<`ቭb՜9߯D=z7)>:yߤ@CNvc]vVYJZ+o~B%6q '=x..&J@gh^iiiJ>g.(Tm=d /S}z€WV!'م*U"rk*'%=`,bP6uM{zzfrVݙdG y̘ FUnݺ[o~B3͚5#~jݺu$_P(H͏>?&u#N0UL`qR@ F9>rs538 sljBO!S 9zъذaI8k>C8Gut)&:T Eϟ?@-7ѹo/ϮWsBO!ҭ[75jx$U?i7a_dm&A+|cPixNstzZhevBO!ԬYB {۷J3<SSSXP|uHڥh<z pe>fЎh+x_ʌׯ_٬]LFB5os=WpaT\ :pc*Ǽ ,Y͖sؐΘ1Clj~BXƢ;m۶]vX=rM7ɤ5ZVp,ҥ ?Ӱny߾}y%JxVذaT{RIg )i@Ŭ:cNC/\غ,X+EbN[bm"+ xVrwaـ\'*T,9}t!/2WBO3 B:uӦMd+ӿ $ Slf0 WXm6tE*ƍN,=Bo?Yv y˱~:cèKTr#HpPԎ !a~ᇶ|y^ԩSO?tH7p7bs\+0` HBB0(BS'xe^~e6BH !b={w|⳰A mDU5E!GaEr2$lӦMdiR7@-}D;˺ p@vxX8ϖ"{m.sM&y3|Tγȿga2e_~?~ xyrʷr!$O?m@6Z-TaÆo K :=rc:H7,0Ra97nt:r9vg?|S}T==¯y,9hРǹ_lăv;+,t !'`/cf˜vi2B4[ Rl@%K7w%H IȦh̜?VѓBH Qpx#r\8ܷX$xTAJ>1:--E`zH D1wͧxv8F7)S80's:KǏ~~Bsu82'HCD+EX c+665D b,`!] 7 d' va ˑŔyp\7ϑ4.<[n&-PܮݏsBH !,s[AQhcJ:˖-za81.şR40PFfڊ!d1˒!Fl3S.p-P]~= ;M bmN@{Q<9z>z8,6ϥN82xS}[&[d̙~BO<Vᙽ7n\jjW_ 0G\eqg /FrN`/>A"TIJAd )>!.~\ Є,,dy)2*!uDތ.8IY?Z"Z#-y ia08^P'PIvm !}{!M[S)l,QgFrPn}A>UY)I`}d8(un1 9`KLq? = !$"1o]{#G{*%n([1B8.2C a46m(%BH@y1a]3|)Ԯq m3|gAC,Y$_rr2jBHI !"QWj®* 7fi#+D\B*4x͛7?M*]pNm'-ͪBHI !":a۠U׮q ==|眆?}<Ƿ}BOOQZlI(֢E 7 %BL93V0Z"O֬-BD- c>]XdqBD z q|Xw22`4y 7^vm`CdB!'{dUVKy`X! T,;-|!黣EcǎδRe%BD F*T^p .\+D삺c.3DD2cFI .]T;\!$;$2KLL̿yCa+VЮ?rJ(ú:ϐ\Ѝ#F8L.]z֬Y*%B ?!D9tP"EFՄTiW;-[07i2}tg:ߙg9v!BaÆ.\O>ꫯtc Gм~ pjٳf1wjRիg㏿{PB!'ej:uXKPFF K'O]2'އέ["iPYx1c`nݺm߾]]!$bĉv{q2 heAE)A*̏?wc K,!qj*;.ZhvBH !ir4 jFKT ·v : 4~Lh8|pO]:nݺԊk !‹nAۆ vZYNH#jԶ[+$T;ws~Gyа'kcGjfϞԷBH !KF.䒬"t)nݺuu!E-_ݻC(ɐ7t˿[B ?! ̢7o4KM?ԁ$ S]NUNVh#C3]4RRR^{5!"ܹuo2'3gS %z@HF 7h (ƷS2L<|qǙKHHxBX8*~~B0Bth-:3fI/0 ? o{-8_iii:IDN4 8,X2=Ix~!ƍ }B!'zE$WT<k1ȎlD#<{Ձ$"w}pґ##r1pv9eʔ4i!" 3p'ύ?> ș$ mbgcIDt)>lkCI2ϑ|UV>}zBO׏?'~dؔY_|EGE{A5t,ܢ3 چ;8vڎ_4!"ꫯ>hN:ܡFP}N ix GZUZET,j*LRݐD?#r9e˖B ?!Dlӿ'+^O>߆UړϷ Yx5LN7ͭjpl pg8;bB!$q´i.Bg sѢE0"'@ 觟~a+r{ :D^9BmaSA]uUओN퉛2,BOddd{ァr_ve$T!!n( %6 WX!rnjN0ei|뭷4h+6l0*Kq!"ΡQmIIIN,XlYurJ֬YoIo B҃cfHOޣ6-|#GLNNvNZjM2Es؅BOQ%o֬Y͛7w"Ez֭ +2UbʕVBIq7%hOps'%+V#nݛIw҅nB ?!DէOشi3fo(R c|xz*B%]c "PZo>mmm V+ԫW9)뮻+BO o}>#D{Q% Od0BMRDdCWj2OwNJ*=Ӛ>"~Brĸc="Hl1|w×40 cCk{Z^őDlC,sΝ;7h5SLPZÆ w'^{ .M !BW9ps9lj)?m&MKK#x:ad"JWxOGsΝd /_pԨMƱy41cƐ?$~Bk)F_۶mpBL2 /Ç)4ũor,$lF`:"ZTC-w^ŀd99#d-fĉ5rN?|SO!$"xJ*oz 7DH%KPtv_<$s!B{Vp{#d՛Gym۶E *9L2#F IBH !DADt'ט > HXl?jYO\3=[>wlZ(xXfqS#ԆL'(-2{J*Eu˖-_uRȄBO!" 728*.[~W#l%O{ne[P'xr`T b 鳎888AKpq7ÉHn[5eNS*VO1BO! ֍7?I~Ν;Ϝ9o:9fٳ)w  BId)4ofS搈|M/zTe]‡wܱrJt !Bx5\(O9뮻?J/J5- me˘E8` )Í!~'?Jgϝ sd^zNpNN:k׮ǨT!$"pԩ\rqrBTPJgpC PI AqY#\*ɢRz7==I?^zFI.]OSC!B )4m/kFOСC۷og.|^4bk#%yj"df:_ïŔr+*i=a {|MY!B tOi&M3o!@| _f*yZՁƷ~˘5}a\ȸ˼LS86ZS`xRp/ޒB ? ?!DBOIەW^yꩧGjz!Y!' ǀ)hE I "qyRyxLgfϑyse-[Q9rH 8گ_#|ݺu1cBOOQP >&ի4̈EbtLA_ROz"Gr)S kcjCf48xnDCLC`p4fgzV9!/d[hQdɒ@ꄅBOOQ+[L[`ڵ `'*+)$^""Vdҥ37!эkTn۶ D!BVʐǑrwgBɞA1BTv69Hk E^1xspXZ^=.Vj6lH']BH !D {EiF._02[3<+Y]CFqB3J02Q!5$ɮ4)/2h/$P$u:Y3g7xG(2CRɆjRI5,MAlN2(P~,eݺuO=TURL1'C:BH !DΠ7(# HQB }RpVBM"Ff !şhBJgLYEl0TĪn$>g>ʍPK^.ǼD"ЊF2(zl6ݻwL*Uu]F B!'y\+̙g,o7x$Xh_ D[NB:"3BeVBT%_[NB>=FZwgƜ ޽{xIII7pW !~BkȘ!y-Z0- ':馛LkQΡ ћHUaCA$IWRh #UԈnmڴ9s EWq !B7r;mT \]vo6- @v`*=>J|YxVZs9J )Gy]!~B?УWK/ԫWի*T(XP)+r o,Fiׯ?guV'[p ۗ6d߄BH !DY%j駟ƯN:6,S `n: ]]p1]G"7xQH ~'OFK !Bq$Ȑ4.+QQY@gϞ=RR;0k֬5kȑ#جYd=%%wTp !B3XYzgo&M\pYIc9dɒQD#fR<7=z=nAQSL{'fO=Tz2iŋ^#LWGB ?!ϟ?'u֭~ŋ'xT%*U|/}^xR3Tᅬt፹y ⏊cѺ=z`:o38*''M6HiOB ?!^iii}(ou q(א0<ӓNIJI<(; „_|u;|~)4fZí[Rko>ޤ$zɕ_T%.5+C/dSoƜCTԩSѴƍ{'p!M)rcǎ]tQժUjdܿ)}z5jԠZr K6RGB ?!1I!BH !DxK/^F޽Of &#dwn:?pjժW_dx)Rd޽MMMR ~W^yq8d}.B!B0xl=_7o?pꩧ|<8{l9͚5yoYf9\r%Zg!B ?!+U /qz<~M79p  /5Zx>!BH !D߂ LG83G;8_m% .B!BH !BOO!r]wu?ɕ _B!B2J}wٛԬYyzٛyB!BqB!~B!wg5lß pB!B ݻw|,#_tCgI~V%X0a)Rd޽B!BȱgϞ:SX1g!B!B ?!BH !B!B ?!BOO!B!''BO!E׸qc_~߼ys޽K*uI'/gϞgySNO>/K.dڴi{V/gծ]hѢrJj՞|_~EO!$Bo.\(?l11sTh~~-lذaիW/$BO!~%J(Rg=lذ ,^xܸq_} oMVX1wI&իWv5֥^j%7u˗Ϟ=K.`w9}ƬGyd>34‹Od\/<|MzGyMJ{߂\te=_}<4;X瞿ƛG3ʛ ɧ<=9=yrN]6iC: Zvwگk#ڶʗ^F.<<<>v͞gNy﷝uQǎ53Ϫ@;uxE.!| =;hȈQ-uzw+:‹*ox7|법fVJleN 7cd+7rm*^~g2uu!`A%;窲;o .-OA׀/l?I66,m6z$}tܤ7rK4#|:Q3,;^|驧yࡇ_xiɬg39'zG|7|Pv\M,[qk67׾edOk8ȳνǞx#i.{_yGWu Vޜf\d4o>nuYOTe7tӍ7X[g}vi[^\`Hi;Ban]v|Ugyː%]N~N ?^й;_Qď>8-isyvc'^|W^uu_n~yiv.h[_M-ʍ+\۶qu7{Ӻw&O=c:N{GٱO˃wkpDæK/7:Um1|+Xؒ8ꫯ~륗^..3]~z;d\WzIT&c>D{5BqN]lp_zx}X2>䈆48 >61Wd`2r X\%m+q< MQWO~B%rUl 6uis-p}~nfR@l;>:1df͚|˳fY}U_[lRC$2nAy ۷6w37†41r '_J^M\fS巈%1sR2fA"cuB0Z/l֢urm*lrGWmmֽedOkyvO;L.}{/RMJqiFQ}9'~n<< ݨh<;u 쎫 Xh֢-T})Y߲MG}c~ĨqhxJQx|Xv;f/^{K.yW^ Wx'1_wued\Cx򚂌 /?:caf:#6vy#[u=Y(#㴲 qY`wfP۴/J𣏭mK[cUq< YUF^i6\ѱ\8.5S9=,\qIƨMpC)n>LdSv>ӟ|..@?=|5|3.!|vj{}WT.k,?h=K^mV޶?2qI7A}nt(`}9mN_u6 Q~'2މ5^?gɓhبd/'ۄ_Mm22uh(^g5ѫ/W 6+.4ΏN5,ag̚Ďy*lSw;s}~͏<N 2 7ᶕ8օׂ`;{i+ːdYWTضY㼂Gsۑ9{vvUs _@9>!`d+xE^믗& 2,ȸI֛pdOk/![flDO;w@|Q,ZBncV|fA ̏vwB.|ŗr칉w]7nB;13DXB#2յgo^7W^y'\9rdɯV6"xZ~B8ח/*s?q׋pj|^,1!"7/t3fqWq^r}w>w?Ue:|k޼y=Z5ua[}dO·0g SF;`vZfz/e?f}=TYܯV.{l7xw܋p˝5xٕ]tU]zչ5mnWxA[!tjҢq;vtc&-6m6>ZM:F;t9q]v,>c{8k=q]Nm"&޽eΓL7vu7~7/]yְQO] /; Ãi]< u'vصW8u6o҅޸y#6=G7j1 ?ql<z1M׺~^~}bC׺a4j&#L6j㇎'>H%r f*{x',}eW8 =`Ȁ#{ڽ4lr}mS}ҙ=>\Nzw;+!wSz֢MG3hs} 禂"pJ⻆Y,ghoQp&!L÷HӺi]e'//AFKV`6MLp>uGb"_rŹ]>w2F[psS/{wFi#x@<#=F|d== 7x|'O?nXl)a|_:#c!/S dܤM\[,Ӻi]egxx q$Ù?DbIu ;q^=p~^qA8?~a>1~ ;vL!oS?&DKܽ7;:Fu>s7O9c9 8E?pA(n1qD5N;';Eٜɸ:ȸ d xۥW\uF 滺Aȑ:uصW.ybnҼ^y)kϾl1o'\3`qRJjxÇ,Z?䈆.G5m{\Ǟ І=a,-iVP7MZnm;Yܮm|&xaG5m՞K~[u>t>G/U|v ztSȯ0 -M‘|7Jho5^jNd=0BJ5 ,VSovDM2qb{ɮs&1v֢egpF'{/˟M* a*'޼MG>k~5yG /[rLk#=sJ^{A7K篒&Ȩq8].y. 3({]rr=:Mfi4'TMm17{7su V -i?Qu_蜨\\mctNim >o#ڞ8vO<o sz$?xKB~PYw9W7m&Y<|NhYp_}3 |jk=!|Rc: ֎/d}fMս>7l9.t\GO(tn'i$NR7|ȱ\9c sy𱧉z}]~u\,XawՒ?';wY̟a3T3Y93GFmTܦq6,4lڢƯF+'ԍ\nٛhYRGY/n\B>.U:_-}m 8zm۩+bܶsq0o.C L gKV^qm31`)\xVv#a=;j[va } 1LƇfi7>F-;svr1]o֡GO\b ns~Z''e/o$3S)4سWEĆuղ]gƬ#z3dkڲm{>FPOF2/-f54@Tȸ,/.MLJy1:[Ԕ X5E&G$~$~l#> 6;YM^M)J 9Q,ta\_>t8o dy%tFD˶mN}!o)lʭc/{4BGMqgg<7lއH2fn )m;w/ Bm>#ӯ|FDy vsVfݶ\A ]( sC N)(}\{AY`-EMO6u1y1`,\|@6k`)_WYΞHj ?Uu/L ᗒq~(K|6l{-1*κR^ SdLϙ Gaq#iZ8 feX!7Q< kLjC4)nR)^|se#gO3o|y&b <>p9ؽ78fM|!)~8:u/r1 X/_i.IΡ5:[0"wZW̘w?<bmdLJD8OcM="7fz 03y:<|3VSӺid\T˧S%@ƵLd~1xz+p;[+1[pe+β`|}JO+c'C"]06OB8oƓq㏅D\ gy7nýv<ɺc3mبAl[@nxZ?p\aM%eT *umθ Zk:OCkXQFpwEgO89,-wd9orc  g[>tr'3fj U1q)Esu{o˳ geyq.Q;bv1dM*2'~x} j^ \J݈RxJҡG{QH"λĕ3 L\ɮȗ-_wF& OB1Szuĕ hۙ]8l\>xq)D8WIs?,R1Bnޘ p0DE =5(tXD@ E A?ǶoWIX4)CojxZY?nYLV!`<m3LƦI?&P;] dpɘ{ǵٞձ9_,-EМ'x\8?Mjk~VmR58q=qUquzYwG,2GkՖAռ>,xE]OEg)|(N RaO,\jZ g1Q]64s_):xQ߲mg?<3`azLck5_c/.I\j53pz{_#ޣquD7B-))L7!Gi,ÎmQJrLs4>ƥ)*Nn֎wL2n3~Z'Î\^c* JY6k`i\oi QFp9,=RroFa<(OPSUcqn5;GquzYwXd+] yq %XYyH"ćhp Q`\x´*17-ƽ8&c>hn"Ϥ!f:ܣ@p$}YcmU!cUWc)MqxxPSӺit 9^\mܖUbƓq>TG m&`GWKNJ-k>ր:xGBAe@vCB{öpOHTf͞5KF8 #ȳbcȘAw|"G-vkԬxRGgw N]ɘOϘ 2(Ƈc6Ybc!g[JƤn>*rl(ExBGRL$FnfTYl"B;KU5E<۬A2UE%k+8?3C5L4` fJ;*9Y[xȘ[Nw8m֚_Do{1_*jJ)/E:,+@23j6kd> /x26Aav_~+mJM+̐&#u>L1ڽ>3?Uk8U=0{pϷ01U2FWПB$kÃi򴎑sV_L<yX>J_]3wñBPAd}p*X5[52+bm֗Sԃt`qQl d&O㏅mv \6q_O'Ɵ3\,\ =59c)%czx.{O켬JieSO;#ـcsP>FlҲ1 ؊k g'<Ve8];ot\[+gTxⵘ"mOHгmCZgd]vۻtzy#NN>&314㔊7~lmd}E'Žu.NQ $cdqbf2 732~ p;N䂀W'6<׺3q#Ұ9/3R2ht |Ξ0ib1O`>} ;NE\j-2%rNx \e'NTo}w_H/-&TЃd\C'*<|MO6uHZ*TqĞ r~5=c6=\Pd>~dL4s|p;_8muzTH;%Xy:疏@gٙ|ks s|{U'<1B579i+ meGR"sǷ%5<:h֪ߍꐫꦵ9]vg^!^UC~@i yuV2u[:jP.Ēj'Lw 8߽Z:NW^DWM4{D[ꕬotVs*- /ξc&"kݭ͡s |5HƟe=Ը[BP7_nɸkfqxxq<*G}?L<ȪiF {Oo>oN}<|p5oV^ζEKW8 (l:ߦ|tYo"-iV|lC]J|22չoT3^t}A1Ӻ-i]< _72/~^SdqACkʃ7c2 ,,,,ZdAaaaa#^<8,,,lӓq`q} 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2X{衇vqǟgn=S?^~Jϝ;?O~֭[ 2 2lw_` ~:p\֫z5לdc}{{:hƌ 2ޒoaaǰl2~;,Cw~ m֬YSJƍk׮\p;OUXXXmO?>)aǰl2[~mիMƝ;w5j{ݾ+/>_׿mY5{/xW^]!kg>:_WW]uUdT^Îd\xȐ! :.3`x -Z2M7U nPa 2A%5Ei[Xf^׌YgW&se]k_~yɒ%^{5a^x!4ߺYf:81M2l.\4tld3f *1҈xӧO_w|mO{k'뮻 :j޼yUy<]nӦMjb|_< +c5?vM[ZV/W=rmXǨ*[rI7r5!rȗu_ܷ<; o.ִjsao}sco߾n2gΜ$kٲ> 2 22xzz '|F9*֐ljPb{&cad,Ʒ-K[%cC[Vr]v/׮zwЭ]L^x)'Lvh~-55ytǎ'-nfL=;ݰ6.=qZ ;=0 x0ӨܡѣW 89ٳ&[l^'LK6v hdD&;S5j1ԩSg͚uR2LƚaÆ =Ϙdh<+و#h= 2Xԭ޽_vB%={G'):|NW>}Ojv3]tJǎ8hApKnM1`qvaKwuN9Yܽ#F{&-ZԳgOeSx4%zyP.` qKDzKF0'C7OS|k-[qO8;:9rQIƛogXXXhWߗ_+ '\YC Yd1ɎNvd2KmlШ܎.ƍӾq2:4~$oa+]>l2m*mn0—v|)O'.OY9iIf5 \'`;eN:w&>b\2o]>٣iV㗍YvΙVO?g9mþD|1o=3r| /XsK3k~WNra2,/>=7ܭ[k: .MdVZ|v/dW$+= G/5k8sӯ$W:_ٿsNY.u֛fϾ|]ؒpuNW_}y&ܽx1ιgo8 ?{>ih|i'{Pλo|?,)'C7OS|/J_??k 2ő˅ڌob#V:i˓lʕk3TA t[%c}dn&M$cCuwHqm۶ą#⛗i,%i{;Xy@2ڷoh0+ʻ`ȅ#pAvA8髦';K&Y/a~<=eazʐыFΞJțmSGl(.IMqJW2~%@T)d).Ͽx.|yN2hOMƂ\{iA~HIȻ,|n2B ے@. 3#ǼS>eT>cewzXlҤIe] - {=e^ to9Y'/%Kqu< :+0dDp͉rn2TMq H*m3fڈlh4y-pkƅ8k 2vW% \2f,HXd 23TB'퉪q2oW *] w 2])_ hH_XO &[`b%v@ rd[5 qf҈˥3FSq{gM˦(N0U;(N3B¶Xm9pV2 삪B0^62 |" ќ> eّqV2ǡynup$1* %&B{"_^$W(h4`}a;5v8?8W*1 p< p F=8(ٻ/Vr8=^n-  % J.ipxK~8JZ~L M=4F)n֎ r4/V'6>m(2 ĿoNv5Ϲ>P]0flpۼLX N߹ps~dtC(22S[oS/l+{IAƛ}'=cfDU`{7%odB4kdiYJY6놁׃͒&c+e6θiJ.7:أl P,|g EҀ#ȾLsRy9/x g1;e\2o;2bBr'ZB1 OƃaV|[N.4ƏaqB X @;˫ѿhLnE5eډw3f̐5 HU1 %穄D[$˱mhltX5\W>y!_d!@g$ n19Kgik Nf1o2?5'ٗ&Ctz{v8I5zZu2l <88DPbЃR2ld&•*@0a`"<pޖ]ڡSޛݩahU#@dnAGAaD0(h!H>ϖqAFvNAX7' X "j- /`Z2H-Dgpe,Èg&]s|poX#~ ˣj'+`uus" MXט4Q[pn/(?= tVL#*4$xCȃ >'(DÖ Ag< WA3qd e2Ϯuu3@g@p8/Z3>|ѹA),8<-.' "cƺGspsp\9N%" UWy‚w#AAau+RVq98,8> 㭌eߣ Eَdr*7x` $iv w<{_"WGrݺukђ>|9> ͛q/{饗R M$/QW77y$" *h36Tu, |9ki nY3!޵2ψÏA4 <הqMǪMtQ˸G-dљVFȬښj0uLϦ1?h[7j5f5j f)>33 LRAcc1v [Zޅظ~3Hyqz8QXH8/=:JFgL>iyo@/&N|]%@㇛'I@ҩ9TB>ch7 sNq":s1.]<{8|? Ή5A,*11'},^'fM>ߥb":u?Zn1Z- 22222222pT>׎suȸTgL*4o} 22ހHK4"1sŖRr.%'RvBR-Ck .!wzYa!5'^;Im7P5(+ D6A2m,Š,!x uXv<saJGа 2hUT1ZI&2Z!z|HBgyWu2bP!wIhA d8F$CyγxQGĸsEL%1$C q=@ II9Gd8_LKVc7Of)B(F1K )f8iK10Cͅ1@8و9j䁼+fv'6ה@21epN01>ݩM{ AAa30׾֣G-ߠ 222^?wc`%Ć\>wεBr V39sykGvu"dhMq:q`hȳiȂhss dY[L2"xKʄkWQUATD^|BA@^`aESY.]T^Jјs0:H!b\:RKX/'EݺM y%g#1/![B<^@끈wR2h8%.`FTIn1wpY!sy*zJozpwºBtVrz ^VnisY `6mt-џ4q1w9|>WNy-Zd>8sp:L"p)8*m*+ƨ//\c]ݾ}{ l{(O֒ߎO68ȸ \CHƉ]΅ȉLƹ]E8KM2WfeNd6$^i̘ms: 'Yz c[4- $db\p$1"q[:Da_ɷ|,$zD^)NÅcwpex26{W$` H42 sMa ie JT bsf0 v*MaV38JrG| 5 1 N܌`("5Àxzyi*vB`Lx޷4L9-37 go߾}s ^2H^EHtb[.+2S!q81h gs2sAY/jtrNN8UXT07\ޫ&%smp##?1*o֫WKTY郋s|AAAAAAAAa1.Hf% \Yj 7[J2ԛs6`YpicUR/}`~,+KӺm{WW$ NB[gIYPT^9Y&8Ti K=s>c1O"jy(gs018gig2{!BZ3tFua8#?qdhH([mDm1~82ۖU`Z@ndJ31i6uC`16[V99 dD )*dn$2cY6et(l4ZF`Wٌ.˂<}:ɨi2n0F Yas\3[ي"ٻǂ'N3w!s)L|d9&8@hn'MXԥгgf2{w M@jY ;-,`bx3JfȵLS[Ή&W&-)/V8omOĸc BL37abHg>14]ֻ Ɖ.+p!|COA-LvJfsW0wy` ͡Us;01ЃjΣ;*{` =7SG__TJHTGC.ɣ +SAxiB#=Yf.3Z}ד8C=oOEQ:؅^L$Ȳ9}3fwg&&jWfC9%fQ.7*Yf S1U(`$` |Y ΂tnlrQ3 AY_ Э_d*4\W/Lwx0 nq*ޱngs>)O:q$s_ 隸GmJq$AAaAaAazX˹V,zIXb9K?'xG.Ĝ%ƒ"p^t35 11̻qe\+39@<% mL3\zEl~@0 CN˃AIZlHi̲ =`SxTXc&/U rE睧xnGs1SV\ n8yhs2\5#Ӥidhv8A=eH421]TncLF,{=z043j 8p04KXC`XI1,F]8[Gg=9qVYOcr3Q[^9mgOl7gH+o`8o&6ٜ\lx]sO- WAe瘃ÂÂ6; TQaK1?4.ӿD%͐"}ˈ(9*IX` ]Խ;,EyA&f%eɸsp1 fF`"6\oj!ZY-NXFDD9ڼ1@Lb8X PShL*̆,]i$Zd\wm[== \Kd%o/%S& QqRXq[n:wue.k.b Aߌ\7o.6 ʸs{m檍4c]vLWrS>,{キJՠ/U3t.p n2v.bU&EU\Cfi9x ѭ{'s0LY x&96(xqT$'ReC@[ќ6+r`+hk̛iXEY1]ƀ3| 9*fjZI<,9~;lȐ!}ѩS:h3`P%ܛtUӊ R&3fO99+Xæ"lđ+ٌ9s<p/]1;ye3ZG ]9|46Ml݄ Kz%C85XWvo.׸ {wX|4Q]滷QW;O8AƁJaAAh:WғXuy 1&k?/(aԨQ.{??Hj |_̙|ת|6r{駃Y//?i۟qNEuY4|2;6l*XrgfXaY4 m`:%•+:g qdqli8shؗաNE$h7-+fD_R欯K ,v\lMm()Fы>XtFsbw|}abB* gl@ea .Odwa#@ 0EJ hЕߙ JFت҃1+@GjmȑIOJR֐ѣ+]+"S*EbQӝǶCu]$Cf S*0)M!:,+re U.'" 97Ydzy8eŒ r{lîM ,9U<*)fHh?tN'/776Eq24Ⱥ/AƙI{ 21cEq>jAAƵj/R~瞍mga:_~3sA[;%2o~ن1_ W$cirt p"f˹4k$eiۘqNWL y=ɒKt31 8 5괼"-ڙe`ұ*ɼV>} Sz Tϴ0w7kKKVH`7doru]?cOpj1=XfۏK[oUfA[ AOTF6G̸6|p ୯~l׿Y<~>1>6Ry<ρk#|$Y; OPÁ? -M嵭8Sa̾.'Vu®6qiT*Խ˥ZXZ8a2\5URde;1^̽Kr3"[Cy{} jygW8AiTuu+`pes ϑeYeu^9pr[*w蓉c1j5̀N4Ŕ  ιh@2 NՒqTͥyos;u&ݛ@IEy \F' 4ef3SԬ8%,I_ -ɂ?Gŝv TEMWM2kK)+US!J2Jdg?+ǃA H 8յ'c8W j͕|G2+.R,Ĝ3T:cs[,pl1 c0BD&3jMcXHELH0sޙXf:92(h냠7;(Hs?-p Rġ"ƒ7'RP3U-.(!KcLƈ2G5pN&<; a OVn*MN5>~-*g8LLKCAOCl5qCI/V800y%Z;.eCy\ 7a4=@Υ#a69q2P3>d\ yu9^-K xy8<0!v7YԭO6~4 3K \9EqqX%~^ z*FB ]D.6Q:!~j#g?vW c~?}hS$xU1Oy\pP]tY[@:8,888888n1Iky}dž"G?ѓO>w+_Q2&ROLfy&\2Fa/5Ӓ"|7$cxU1=Kgu2]2.BU(S".v%˥՛f).M֖eJ,]fY,;b)սJ~3E3Pu c 5. +Lûp`RihK67!2K AjgIgx5Mf-셺rqp m J1ր*dm>_y3঺IWm&7sScrLBhj|c"s7 OqN Ǧe=פu(LyB2.N G p$1ΙP\N?ywZ^٩‘*n$:.(>S'/T8-'gw[?FDs2%$n 22y-TwϩM1:FT.Ug&TĤ2{߿;2&65Eqq":r8A1Ĭγ񲶘}K9e+MVZ2ʴYhb`sDm_DpQ-5/y.$-/s[Zs UqG9}̗n=ʽiLA,+n!?SΫ%/r X'aX6-lJ&\hr1mʰ053NSalr4;2'qn20d'.(FRM[њf98-|R#:uƌGq!S1 ziKs6s7r\8gzSgLzܱpV3v%`%ŦC1M2LmEV< /88l Mhd@iy1E t˄%1N2WPռ:dLYG"SVŘ wkA4x3yuX0(+I wC=D% 22Cd\҇vKx`+[&c0.vg4a%VpW|@P%ʿA/ v6!ۂ^]N("yl+nf?fԩ룺 pdθ3QUOU 䘹xOޓeEd40!ۜA Lj 05jNZ Ok0];Pj cyTvt2Z2}!KiL5JV#,Dj3#awy+qGQq੆H1{r2G2&2(R#=9l<1'xs̽ V ,Lln H6gZ*:*e穅][*2tqqXqe#%E߾}Wr88p2Fk3~Wy%L̮-gme7VrUa4)׋?HEИIǛ%88888888,8l"2>&k"z Wʈ{Bf@%uѦF+HVO~90a.Dðb hӨWX:qYB┺XJ.3H.:I7p/tkuhNe+8sgKdle!PWϩcYp)@||b2۠[fHy).]u9NfV#+dΖsjz>}V4U#8`% Э`V7g2nGNSg'q_ k쪸 Ko g&)ʢv9)CF l?d!: ErǎN+dᵡ-. M@9<÷oVeWq4k@rs]/iFI{Y:4q1Y@43Zw22x:`r~n;^teNx v^1co#8=8wL'Z"38!XXІEͭmaaHOW&dD`LҿS?\Q9_/+UEMKlA4.Bes#M%Kq2 yq~.`] 2s\gj2#e d(-M2㜥j f} ;Vd G̑rN)Z'zՕPPHIBhgg]w[`?Ò껞t M@+rL`d 8Jз8d,j.Xll:]"N|!ЉG[q\!]ǵتQa UFL'R4k[Ψ4+B F!Hfjp[mIY(NqzؤIZ"gv5`NE" Yf3?)+ȤfTΝrD .nHS_VyZSPĉjz[&H~AAaAaAaAAAAAAaAaAa]Os黼B K9S8 ' VU[4s)TwQT):`xeܬm]SsLcҺRMĢaS$V,^9eEVNR¡C Ú Zd;{y682.?g&GƵ("k Hp3jr\\F$Pkne&PbA]ٓ0yɀo30X?Y9:$"PHV 4C−e*s'l6=|&@d͹ZJN6Fi0hyQ'.Юx@AgN5'CȤ!޽x1Km82D9C3œЃASWe 22 2 2d,l2h _EXrwhs+̓X\ؚ t</Kic-Vsɘ-3-8δk@܌x_cZ2+./.˘9qOl>c$@M'",;_MY v^ %cTV~*^A)S/Z;OtfLAFcƄx‚r4L8}bX [D퓭7"8s $?0%krN8K'je 3`͎-FL"t. Wy#0Tv!µ稡&iz*}":OAileմnl=cι,ാV+ak6@TVG.79o8 ͽ xOg71sqqXqXq& cenTbYAeUsXIFKL ys:Cp"7`; " /5T8=3!jS'b9N9@ة-Z'\)g/Jej^25-ԙڂ8Pw7&C 22 2+sߟA^XZѵzf632~饗H1w~sUoS gׯ_# 78xYۀT YXκR D`6c87VkS4`օ6O\Zd2FLp$$j ݢv(y!g&FMzY^:o YE` 9f"L5W,"@@Kj3lп'k>vZ!Hmea@dd@3WXyGlDaءˉJ N_O嵝g{3˸9s0ZɻrkZVS+^ܽ>uZVz(G:KWqCV|LcК]vx2e#qruk>e|Â]w#r'xi}QM{XfۏKqK9p.\ZnVCl 9G1bB%̅l9'ȍ_W vZP 2.Y`TuWoI3NwXQL "aP:1j`%.`S$@uYg8Wq'TLx1E6\K37rΣ;p%!CE+-<0!O\ffd x_c!-f%L彬L Dž :eW}H5<&Or?R蒩mFg[dY3T Ñ reY%*R.2qpyp0悩~:状9X͐+b'Xm8ƌW{,ɝν1lɘ5tyu.yA O?}a2>-H9|N[N9&mW60??JRڌR) ?/׿#hiK:gwۯ|+DE~嗫/ȯh)3_}[׾ƬzJN>d$ _d{q@tm>l_[K ~3ndmd;|_֭ۛod[o=,zy3pgG}*QgB `;id.n#*h[I N;f2kAGCQ櫯*fe$_H0"/dQobb0?ݔ͂iH^:'W6Pl'ErǒLCf}gU\T1|3=LQ$XGKN/˼uqq&o]bi؜n\2 BgfVZAڂY=Y:'zs[BfY.F(C2nG97~ nʩN_peiRڌ&t3=o `Z1#r93 ;-?NvT1Qa}3O 9vI-øU <'nhn7b.HmaX;U9g!hx.Ñ",BUBKB~؇+=TSq]?D]c4@y%z'p E1g{6|e:뀉.;TXYq&Bg(6a_kyH1f_ܷx0NPta1epX;ljCҞF<88Β;?^ j&{م)lpt6Q~WI^sJ?1D^~>xH'o&޽{K?&}b$=o_w 2 y%Xh\v[>܀IՒq_kUN,ڲoivnn?ȣAFtE;w]--\K'ؑqDM'˻, sKyus -=pm isoIS '8ޞo 6j Ś[!&}Yu!)w³JUʕ$+Uqଐ"SXC۶mMdžيZganv9u1ڶ}idYٮxɣ[1 r통3 ЭPf DpRj7Jeoc$7U>}͔ؖD3!* lSR: CO*Nn>ڜtdӸ̧5B1FV @/O f8 %ߜ*lC2eʄMpDKuDBZe q[@Q$MhJ>Wp(d]"8p+d^{G?"ZU͋tŜ SMc`1dob+6wU?_sYd fwO^O 22d #{K1 $tc\<ŬwޝȐ%[np&cmv,a]le?P#< %b#3o]v3 ^k#ݲShxc~k3=Ч;]ڋY*h)"aĽ*e`%^drHIX=$*d+b NE"wd_ 8Zb"p2#XDX_;qR۶ XġL!+ v,1=8pTU$h3 Z,"}, +#Vlv Wn>"od_!k;HtaIhHspոIu2a{'`5O}j\g9}VLf4 ֹ7D9ͅ,0Õ{$co5x}'i$qRA[YˆN#;, -Tn ` ӺpLaK$ŒMq;kJ/lA-y'Kd,F$+Nz:͹9)!wMP1;~f_&gu;{G2^*4e1XAZ*nN}]FC$97JTw!=^`sl@qr3](S+4G[c! r޸slօ,%=΂y sNė;luB2Nť?sy+g(z'C,.sZ|*W[,n-KKVHrc֮];v޹8jpLj 5l="2nXl6|+b!WwؕκAe$N˽IȗnXɲDgJDg\΂fD;vd&Q&MEpZ>3rpYq)`'-ļqVg5Ł]G 牀11H@LB]WN"+{8T 5& M2g+M{숖Xa"ڍ7dJl5sɠgmO3fh*XB@8r'ǒ!ATa KmV =z`L,6Je7ÃXV><+YN3ń+s"G3\ %3Os.w<=1i5΢ÙULL|d ļߐ`c'C,<}wmZ [ Q0]Vn{MOaca@0@2κМt@8X/DŽ9)fNCUͧA҃iVo&eZ[xZ_Mr"P^pUw"Q9gE[OVdLY+z` ~pTe2ƃd>ȣ!c$խq`0~m"JsX9L\صN>ȱ 5r=餓غ7,]~%.'xvxe LtqqqqqD|d/0WJS*$dwJUTl F9> uL"2}[+Y U2֗hUnah7\-) AyH@9A[懔 3#SWl䲙XΙ 1qy\͛ MLtvE }"Wc 6.P!y\reVNg껟٠P'/E9ҐUΗ'˅!Wٻ OOJF0֫p05o{ڈ4b}pAr-4)oO] J&@pС8ɤ` NqV@smOTdT1Ӏ xib ap 򨣎Ɓ{VTH,ZRPBu=ɘ/CuZ3|_vID0W֜CdrLEP\$DA4^z[#8l O<0 rhXLZ2d\!W`kv"a\$O#ktƹ<.*dV%,RSp,9܊HzML\t3GMaZɨmJ-<*ΖUHP; w UǗi5dt}Y>wIIN] *:Y1o35nR9{"+OꌡIxH-逵`5mZdXi1·fG ʀBX⚀NԤc@^Q?9ҖDY+48g6vڕ;34hBusa:3ɔspzքYN8I-C l gՄI^6m>2.TI1YVSಢ!)c1~lBGa\ak&li[. P;j 1ɛ%ִEROs\rUb$Xˈռc҄ ۩sYkQ؀6O:$CMEԕ-1 ͩ쾥Kax!H;p`=\TnL:]S9ǏW\l@6 R1bkGx4M)Vb'&׊k(Fa·΃x&tdT0 vmVqXXXqEeV0V֖Dy ]dJj9#,;t݊ճaV:N*Ēs-@2ĩκ#:KN:zsW6`|i)Xt fErM=0NKCi2!sfa6a=3eW-%WL$ؘ1(}QT;gQgAFwm0Z.Ci` ͈ @@]Cno R. k=crRɓɠmilT';J{Η'ٔl<а 0@0d)rJ9MNs`଴r&][N*d [eJceV 4 QsxV {PS7r!F"}s Xf(2{$d6'|zVVqXXXq%'N6|qqqLce 㰰 Z1]}ԩSb,C|DY Zx$\i6 SF1qmU'9V!+R,&Smd5>u$?ȕ(9jcqV>䁐tYUS-̶%Iq^Z8ڼ"2 r !eT*IrF h[n#/r*f5᭜!qΦlqiG M9y$gS0r5^z9MUiQ0L>#r(29$/҅ GQpM;!$>c`E54 VV+>ەsp裏1XF X)ܼ w2^ZXY)HV2Tw6J Eu"J1X,]$K@Ĥr176~($v*84~F~q@_ >܄i`<ɘ:j@gdDϺn00k5B$+i,AK#A_yqLf[1 n x5Zwh'fM.1N pBγ1>} gc`1E@ *hEg+y+v '͔s24XI@i2%'cSڦs29?VFT[ (\:2^+_ V% 㰰 jsErCmȍ*AeX2f+U2d*Rg9sT83fG3JM݅dLIBqX^4+3ℌ8p,LiLT p%ft0X\k8u:[:Qq\˶ I 1DP\Rɦ.YVhX0pi%e9PYe8U[ω]v95=c8jÕ&\>ޝ s׭N](wcT9uy+8B>Ȓop J6)g*QPp`8zxZx5Ck2 GJa-hڤ!ax&/ TVr g2( UKHUZ-O<7 [/|Gv9/b1˲]Ѡ*_8-TZ95KќKsZ ~;?$2 2aq^!l%,O__\T#*!4/;2BdӷeuB 7 dܗޜ 3&JaJMSRWvB{t#Bb0n7^U-@5/1&ǰ ϙiЗJc+A*ΚM8N˷9jz3jc#ٌ.,Yb~ނA sd,v6A_s!aÆy|l NS= 隋q߅b:/dqk鐝Z%+s2c Hhk|!$:);j(P63 ڨ9}wJtivnsx˘:b!s:T 38`;BKƧtFe 2sO;Zp'M|yo~ 㰰 㚴*1rdX%*w;찃d5~#Y~k[ɓqf\W2g&We&cu3$lcלy"] rwfc YQ dK$ 8l7b0L$ؕ%8d7P /|#K%K4αkڧ'މFu+#@d NU&3C Gu=vhD$1hkkxBh$ٰ+1g|}C0@dd,~7{u2¢P X@3jU`(G^xc6L3r.vQ^m Ivah^8]V?emwsBepEp J I 1["'<"[vG)Cf!}tND|Gh(茜{&ٶO*7IH eKi2cpL{饗‚kŘZ;?da]w%V{=ܓX%c]DrVW6RxwuJ[n!-3,,,ȸO~sOTssS<W^^~ܜ7o^+?I2bgv6dp]NJ cX͉h5BvNfDIub 2˔USd1?~m.Fnc j@%kҭ-[q0+f[CE>z )`)2r^9W6j^Hp'5,s]6hˇbsGD >Ǐt'Զsدafrnh 8T'SA\Ͱ/bCD;#-ʔلÒq&xZ.]x;'?3)@3#mjq2ґ/#뾸ze.#]Wdd\F\r1?3_u-K928|_,re֟͗9S"NCף`6Ⱥ}衇lm{*<*BW:}_58,,xk'cfryL|OӼ$uƈd^|\Ō+Hkkd $rJ XDþ1rfb9Z8 'Jѹt}NXѼysR!L0@dLi([$H PSHppӅjyT5* kpEiTw Fb ]ta릿(Upth{VF<_@5ݔgϞ~j}ݨǶާ%h%ܹ ЇͨJSZ i@gԢpIސ&8:sBO6#/)欦(6Ibt/=z_ȣ%W 拠ysP]Ǜ׿p#a9nAEN!L{~H@Bm֛)չX̩]KFl54YČf۷n9o=z |znuo c/dƏ==p0w3fo,‚ ! /|2/U\ո8J ˸z6`F-9Sr?6qVv3B3,0"Ts4E-6[ jJMFr,H89Ӯ]ΩQxJe8||q0Nmm:diy~0ա|i sPIdϻ0L(e_bƉQ-WYk3"ݏ%QO@ +΄6b'YRwƤ'OlQ 5 :iƅg%#P* ÝEd$38q!a+).Z&J`جj!U, 0d0 {Bq3sr2DX9N +R)D&h 6#9s =2h+xm/3׾5Z%N j;c;?E#ޮ];y˄#<ddd\sΖcy2'މČ]KC2)+{msnZˋ4A\=^JnR.88(b@a}qhO%/zH+ ",Z<:sE9N [D)s ;m]d4*E8k,:ơy+Ѐp"+@8@5Ek0qx@ V293EdnX_U8 \CՈk"AX"lg$(Qf)ɈU`8'IN-Q~G"0_{/)f![6&.]\tfY$d@hD9-'de?8`dEC8N4!ǎQ@87j IKgO39ޮpPL.LFdn_J5jWi|?Kk|v%OKF1Ir#ddq 6y@1Z 2 㰰 -w[Tے8ˋ8rrI@CC@dh" iX\.F9ҬmS!A]('ʂ8ZB\ГƮ"{LyQ!aSU/ #{Q̆ v_8;B SЮָ!džztXl# `dHl.DlID38l'FnKT `VNB|/" PK*1S|nmA-ZH࠳<Es=x.$W뇏oܘyfnu(nM¸hw]%kB Ⱦ,C`Tc12I5*Fsgˑ]ObNFDE.‰qN1$H#2ReLEǂ/sqq쭾)cvcYIq)ve&1L7X\F$baaA[>eq2|NVdJ_56 06h7?G3;+vYuWZ"(Aʨ'⌳NJY6Kxj FdwuXduVA< b7x")5YAg;h% .iVpc":P(Ev)óx5) A2=fIU>eV L2P/hi!M %)>uxcy68܈z2]ɔ0b߾3*Q*% Jil0p U!܉ȘsR&"M4d[ԗ>dGiĄdhMmqьP 1拉z]fdI.Yʴ<+Pz~.$ЉcFL(,茕tC7?6tqmS> m٦G}tK]G̸~;‚떡0g>CZ -cJydycMVx~⻨}~ar;ز8g(e\%+W@E~q.wLbe]7K AJ,2Ygluh%0!CF<$A59HHxX Pb=jx>sj$h?8ۤO#qFatX^g $-ٓM2fGu;2gpd8fxbHӞy1b3 $hf [F vOB.9A"Xr+qtW\qťɀK*' ! Y$|g8Z%JYT#+ %GSp%Y82CKלxprrhN 4(&!,NFRxS\f% OC[(EJrݒzzqr2ΰ :gh a2MF7ùnjTø\igπq8nh8._zۧEƹԬqHoі*ƙ#hc۸.9()= ǹpIqҘ 'ysk_Z2V^ Lаqzvfq5zqP1Eݻ*&3 8‘Si-Z:+P2niFl%R!ɌnmGV$"~K3.*$yeP /S1$)pռq6s˓Aq0XofeF1:8dZ].8Ga<ûě%clZ^ F 2srTcFؖDƪ)Sc,[N]nzx&3y6*ܖ4 aA嬗{sA29q" =t\29>:taxC #pfH'ts ļ$.ӏ E0{qeqǻwQp/̊w8*8G0B9Gw!#Ju$I\121I~M+ lDɐ@=)rG ,* ၆F7g)fR[AasS!Cp&AfpG  R=woYh-+2w* (gEYuEW}B.ٳ QKcƌDrmytaVF3,n}BqXjc$م "И-O#KVE 2s6t-dddddd[A=5Eա[$S6oBtka\o,,K5Q(#FCz0߾ |Qe4-ЦNCi͛i '-m>SOɼUSfԹSVҰًퟑ(fԓ\}קs+mR9+4thgO.O]Fi.6d0JBT]8Rŵh'vFcʰ@`pU2``"R#*8>r9i̭L"[2H,7S=qNIIK3Uk*ؖt >:K_aif# 3sH- pP̻Vn 5FA8é̐ᑗq7&cleq^V0*0]Yz e+;ƭäfLdaaaAuԐ':QR'crpմ(vB=v% 'cW^.m۶[ٳW:$>!m}4̗־:$#([sM%kݺ5+&Q5sl|ysg>i˧:}˙ʓWU 8m9'OpF>䉋'+X>d_xE ;9'b9:}&J3ަ~ /\-K/hd_wݭ׸8Zs˖مq 8{83h`ϟ;"'1[N@ 7n0_*7ë̴cڻ%IDɛܮݠ­,#ؑH0ʔ!{8Ά-ȍ 2sFfxzXc=*gz dLɓ'؍78RgHfB *$cDA:b*:A%de"2̴<)$\FĊAO^¤or듌!Ѳ2AsԘ&.@db`8z ·@ b"US-`zg©~H!0r÷y8DЍCߟO?x:Y [rw\s'T3'4Ѻ(Ad\߼m%%Cy01XO>i_{m ޭ~MqqqqXXX'j_u/]OiudL SB`q|w# U ٚ҅"+\Ma9Ő Թ,H BME# b*zʕ2$g` dp6])iHMM/I&0\sɋ2b2EQKrn:N4I{QV(l@`69؄r*8|~L #?]{|bK{O28VOhЁfefz,ڍË$$ ɞڼdaaaAuHo[~ο52F *M`:7H !N4ԲĽjdRq-I*c㻬4 CUF M|ʝYnJF,jBvu2x&#ZlƬ *f 'CJ:o|L3HdPt{VNە vT^T8Th {/1oiy;% Og֭d֣9IQ̴8XSU'CM]Sd>γ>3,,,ȸY^g}֗>$ x駃69vn)Ȁm fʝ dֽ{w0 l1ԕ8C՝ gBe[ÀU{P? ["~0-t;9<&S20NLl M6GZ7xqvE?ɯ5wu/| P/ j1}`+8Ik> *II1^gaܡ8J؉k7N 0$ǰfg,ӂJ\a`:4<|l6/%H86&pNJ#";;5*5qYH01Zs_\v;{$r=d1p/.lmd0i׮AHQ0%O\aXvÊb^:$4KLH4tN2 .+W@GK\Y\)!/UwJuy]A <*d#[dѹS<*3c)C!Lcq!zff*#alkj8T'Ri h'ehtɊ9- mQ0$03G,xf2zqv!#—_W1ײ<(o[ER?ppW啛5 FYٟo}[ 2}m6 [Dfj1T'nRš|fb6ҭH BabÄH\} _]Ȕ>`wa'qS6[)Q}ZmӦ~xS2v*lht2ߥSrj֡*6GdfT{Vu.8oXDgT0 sMxL-O5`ƏC{a}R&zN3,,,ȸg?Y2*W^ye#{|T~w>HirD(9@jUnI@:gAFƫ>H*QU/r|9N:wEQ:{|c .G Ɍ'`Pc*5Mϩ@#|K;g0H0a A D1}7jÑt^Q-[^LqιjްE&)6,fe1W=8Aóa1Ic:cP>d\?^9iY9<#tͩg V05wM`p8M;!2˜+~!jvA@QKW"YfrwCf@,zBxY00:׭+A8nBs|Lf0ȫYH8u5$LOH"`(n݆Ý2.K/ܽ ~O.<v!Boz4~[=7.C9ű9˜3%{_g%w}_+nQv2f>8L2;N21\K.t^wd|HfSbF4!ds`\S}4hPwJ^k5x欳o=kȮ]ݿ~]}7|u׭yޱN=u#&mI?mRf-88qO穧OBӏf9}q@sW֍2߸s=0Y'毯::jAasW, NQH&,'f=5lP?ϝwmê{>kڭCH=렃z1h >2ܩ6Zqȡj΃}y1HL$h1&1 '&M~UO0A)>{ɪ7d4W_}5foҥkf̼i:¨ѥҲw<}XpKZ&eNm"x =\۹zr޸$o^=4'Ϯ.zu范Â;Sx.wW؜ĝ|჌ê|WȀϔC|oqaZڶЈ g<|gי^7®r/95n9jԨ*!`ōyC&Isnrq? f$fǝyACe{@ͩxz谲ɳ?HĀxym~p+%xZ WN6oi<[:rp ^]孬4S@wzo01 mK,>aJOZ e%Ϸz5NhS@5 2Jh"3 $ceDгlRY.%[0" $Dn͡mY1f9ĸd(ڦ!xCs)c1W(DoQO s'rBeŷiÔAň H)AfV\d\;w\{r]-dHL`duZ y9? a0"eD[MprR] QG[*~nٞq8'LY!1_7O?DkW0.\tF^H: 9gϛ5 837uT0UGÉZw agρʳ)(ȑ# O iXۭEqqX&cCC$;hGb],.Ɉo^5x̕is&m B10Y4uoV4&kUeCؕR6 \sgeʖ].-p6d+yEQfmNb'L(͆I֑ɬ̱"̜ qYƁ"9%3mhpt`ka2>$TG@aڐ)eW-5 52ĉTtAu\w_3^M|:WjXU2kA5y="q;9v?Vn@,0#LhB8Γ1όF˽0}]ISs!ܹsl}ײԻ.X`l#s¦262â\{r?Ȳ=D: "2 D aJcr5DŽSql+VUГΈ$TQZ Jڤ)LKWR& <%]1:`P- wq9ӎp3:HyU.AG_3˳uZW.6_~Kv^~k4 ;6!4~h%qt{ʕ+O'$2K.O=L X4wٵc(ZzZvH͎!5 Jւ!92:ceD^(JpSHDY8.Z̒TdS-2{7DܹH4"gMyTwTVU"PA }y ل]Ul Lw8u8!jZ\pA2>Ӳw:ii6O>UT7ymB21.^u/d*KXXX-1_x7_܋#l2Ye#F[˿9ԃ0&Qߎ,Vx&IN\:#ݺ \HV2d i2pކGs7>t-b[eoybRT)jt?AUXšCr /u 2z{wXqG?ҥK'Uy:o+jՒ'1KfEM!TSu4BdeQJ.329 c Z9c*aPY:ݡC2zLV@1<-Y%qvh+$ Xo2J1Ei7X3~GL24+!1 "&퐤rCܩH1HDL R [EI UHŮrT%bČm9 p3-}%cI_IvW43so5~ٸ/HIHsXם;C U:q4PTD0VbpLQ pu++G1_~{1ݠ*}$cF4)24;oi焧?ȘmD1kŋ/Yd;IHlUav̗c*Jv.IYv72Ptj#^%2oTq!` O36Ș#,$ܹSF;8:QcQ8SdL@cpKУŴFgrGC#In!$u?M%TLe޽;#olmLH,WKJc\X;#d2L.ďסFa7YrW Q矎Tp_1$yΧN~_aS1fI"fV_qFEu]4F& q#+V駟~a5fT!csgr T'O^za3FETd᷿z2?>Bcٲ?0\bШ0q(2c:/;2vI$HNAf2-^}UL!'?1c3΅Q8!~#y+q c*y$cZ#.[q2dP/ƴ;'b+" 3\RcpX% =XtRMF 5qf"%B J>`:&3|gЀhC cm`+ 1.8+ V,B߾2䕙=;8^GLQABlW$"Lp$%a39hox(16..Bgb^!ؐ!Uq$"Do֬( S2Έmmø8\HB|~)بɒ_tϿ/=7r{xT2V2$sҀ(/b92B$'%1y2eJ_ȸaÆEXJ* 3)RGcFࡩ=ꀹ *테</#N<5ԌS2V2V2Vdlc>M4vp#2$H rµ16ShcVJEQn5 p|I eLdSV3N ieu$٩6x*e$eQw20UCȋE@V5e ġ^CmAK4+@lwBے,YȞvZE-pq6 mV hnA}Z_-^dU U# LJGf rAp?۷#'>u$|N·k'nn<֔"I1XO-0dL[A(Wx}gp8D?nڃLIO-ampJ A7f{*!d;hO@eDx4aGS"8ݶ"%Z4"8$xr%[k8^tiy6*  gիK)ʂЪ$<]9:0|Sr33rNY= Z maV"Be%lAn++J"5Gۅ]>:t<.4 %h2?xh|!QZX]8V% ]I rc1SOJI JǮ[HFVș@ظm.$0F;JTx\z{mS$L(TڔH2)4 4SHr}wC0-['C`*goHӁ86Ʒv 5W/T8$!˕F:t( ǎ3 £E@]4!G2YkCբDjJJJJ]8dLB[r*w)06Hà p01@, e\*ń%p eWd|#7ἝWAXXX-Xqm bb+5ے@,9=)sE1¢`ԗ0%X4 J$<ȂȴO}h$چ?l7 4B(Pd8-&ɱ$C.:JOh I0{(fUTfy_!`?rI Y4Ъd؟h<8+>M♉X$"̗ROh^֒&,rp;IuÀLE.JJ>V-Z&_[Ba LS:0wdL%?HCocb)~*:6ݏݸ~[Zvy+o?Q3++=dlFa2*lDmE }VC",v:XpQi6Brӕ4 M|A{l#q2D.¦3j £lH2BXI1,՛ /BRc.*+'.!ɯ$ DAU68\ KygP 4i4XQpXmZ=z:!٠4bE Kql%c%cȕip ϭ4O>< N#AoX v߱k;̓ȏ壟Xc2ӛ/mc?|\w~ 3O84vSɸ"(W2QϳeF GuzΜ9{O>R/L%c5%c%c%c%I~6޻ +yF,FK+T ǙJjxR!CM7L$TLF0f؂!CƧmꑉ845M8 6T fm6Y#eXZCu!})MH ˔iVD݁Ñ`&*y$X^u\Ql_p0a0YٔE4L#&K1,c6$M/\.,D0o%aڵܙdI 8KmRCX8d|*.=?rSL|w‡ӎL{bCTL/<9~_2smG!Kf#eSwNHZb9yٵ?\`ݣ[f7ae-aIVs>G,j̜}lo]+Koe+fd-Hnq.۵4L ׮ Qϰf*y'ͣǀSjŽRQ$'^8>ztj]n#nФd=#&Jn7$֭t%Kq&3W?^7n;{#[&@FE [R̒z:p>'|%YED V2H,cb.էi9hK?%cW4GMXXX8Bd4E}7I<"8<@ h \!kuCB1jU^bhsrb0MepI0X\*`5$2KL[.mm A3s4D{pԣ;lMĠ&q&r拾v>@!W~O*=}|mo#;y%*ѹWy*D1m%c|%K&1 '<Όz̘w~t2guh ywQ9'ȥXtDͥpt2^9h( I4)4)i>9Q#hswĸBF& z Z:vu/(K9Q;pDlb0-XiD0RX|T(HHjaTʛ7K3ak:&vB:T:Dk5-RfI6`:˳9Z 9 G4v»d҈!oɲhHh(jxۨ㌌\ZV+̕ טq51X+BxP/O U8B#ȧLTЏ{rAXZnA'i9ČAcIr83lSq$MTDG.gs$x?^9{̎#WlX57/wf/QO" 84tDRy]Ww $ #`%'Ghe,K~;`E7"!^%za>np#dg/k5stvu_?c)JQ53ĉa[ʔ)o͞=; ڒ$IB)(1ˀ]}4E._)>}TR#ϟxjLGcd%c%c%c%c%c!WJ߯I&׭߾^kдM+U|de߹;wn4ml 6zik0sEήwE I CB'NCH=Sl _{\4AG0S8[ T&/0w+L#2=4kx1D)_{u}u)b\ \pL-"RJG?SGKҸu|P\HAԙ!7ozNH|=s &qCʕ⋙3g&R"soӼ[-Yd_4K$J͎cȁL2 [j@*T*WHc0 mQVDۄ.T~=)nc-Mq.8m^fۀ`bЭIXA LhU}%:|ƶAP8-iMH#bY5${]f[ڜB6ԩSEr J7T㳍Xh*_j 3Eml ϗ28]6̝";Kr dLd-:"`>Bd\ ފV.צ F׭9z!J)Z_rVTupz#kz~r5㧬4e4gs7͙q3-JS:ղ{׬?nu]_͚}W[|2J ᴺkEeN>l/ܳLPUUNS:ڽ>'i=%UrUzTԽK]"tel5}ÜDpE+4gCgV % s%$ (DbhVd3"G"p.Oǹc3@E ddI2&8A.}!cF13,iԱyKwuO>$KVLSN \ k-[$|B4<汇ssX4i ]Cdɟ??)N<ٸqW_}Bw,Y BtL2dL0&[g&NktU̿x"Ӎ5:~8Ԏ{1ašm#BJ6E1#-t`y;u&Kiv25k2zN22 "% J 2WKLG.deI#͆譒A\,%O_˧TCӛgSMMMX-&HGt21O4P&I 0.RȩGݻ$0oݻ煌I.KinD Î2ӹ:w2dȘ% Ů>pnm.QP"x1y2Ldf8`-C=t 8Y{mI1*m,xe m!ZDw!D[ Ñx1=jN`Gb"&:uqhȗ6t’֍m)w7Re" 1-F#X [cmx=m͛'*ptF3A~ ˙bkmS2Cdō:|cGgϞ}7yDUaT2VSS2V2V2<J6LID2f\Jd%c%q:uVe~ᇨwSM!omb  8a$Gd+&^DM'-ZBtdD(LAd薙!ibZc=8Zp^|om%Kl%~OƤ@5m2_ň4%0%6 g„TYd*lǹ!cT$rD2Ҹ7C߄ѡyd[0.M׫C'OoRF2fHkxm`„ tx,@'`H9鐼,Id$M4QGsSB^-bhxZXgDȫ(@Ye(f" HO𡋐< µ»R8gR$WHW}]GX.-Ѽ;FW% 3MZmDje߉;2 8{6bGB.EI4D R3E ^ءd(v6qN"`z%۲2fBr}!cbܺ i۔nj\@3of\LSȍ+OX3]LC-T%J8q'A._bq6m0#$4iRcƤ;IvA"6)WPҁڴf ,\0fr|ȏqT,^TDmxufm#[n]!/lٲ"rm16[d2v9sji0>m^,5="ƍdddɘ=+U2xτcOC25aUg0 ?H1d>F/RKbMVlk,JCsL[']@* Ԋd@l!Dae\Le-h@*~m#@4L6Ȏsz6a8'h`݌գQVm׮]/Q"$7D|"̠{r|@hNx\PX VT8pEpffg(JqEMˀ c*Si=4b1J1@DŽx'Z2^?r$6–dx P[D" ni (:+*[x0(AC&z f0'ɉM :Nv,#vX@FC,ݐYF$Om"e(`5`"ʣd!nG}D4ǖ:T~8 72V-eJ}vTДE˶M ;'BM)o扈;PPBd4/' rIsa,Ɖ Jȟrcd]3/YdΏH.lG?S"Ơ2m]n:.q̐Pb,/X|vSLbayĠ;\XXX-v7ƃ\£SaYI] 8gGضbi# i,YpN8Ǔ}NȒOYMMPY/D 2B &:]0_ڇQ#0X 488X$&Ve[Y!tJ#e%Z*۱dRC3<Ip&x}5raWh6;vX6Q!@jYn "ɢҦFZɾ*"g7mc[dR+LBC-r[-ܢg۔)uǕb׏Xjժ961JY Rn2Sܽ(R*tcHSºO>fE½ŽAR@!N^38p ʶJSKO`bňYҽѾ0:8k'bhMȄU}z׮-"M;hUXה)՛)c6qZpۏV6#NR/spL5R@qndut/Qr$8JGTaU. Z8.c19Ki>~lw\v̕b5pe)zBxxm# I$l9dP0ÆY:i CmI]'Rf?Y]YD0>;K3U eQb&|DkE@Vƥ 3ݓtlk 18ļ8N=?)e CJY>cF!A_vP $+ ɐ*zs(JSMAx]d$q4+8#Ѵ>%c555jJJjJJJjjjjjJJjC0,d 1h?-&P@hXX2@m-ی$ ,C6VG X aF\ ϱ!3"`FYC![۱ `;k]0Q1=6=(V@#x;W״iSIl̊3d'/|Gw(ٚQW [ 13(/lj Hm Ҕ% 0wfR *Bj @!CX#|$92 F-l2AOPFHddd+ J `Jʢ3fJyB.s&hpjMEKhT C{2 4ezLbԽ,,ILr8#,--fh#_!orV˖/,/kڡSZ ć‰F{\-X@WMH f9cW"P8:ÖG[!flKeg@ml[6 XHɿ+)))++j_|I?R.2"'|bJZzu˞=g}F*I U|hW(]J<Çe13bR_J wz06rUvmvO?6j 7}֭/B|IJs/4mr JjERb?fwyW)PobJ񓌡nˎ6ߏN>1-Oٚ[0Qq i0cG:Tdq shȾr:=ؘx)RpL?ƥck殺#m͚5+.d{.m:;r 9{J >{lr+F~bJ ²m_hf}$eȖʕ+1H1s  { D7CX͍WOp8%KXy$sT *')Fah:#ζZO&Q^CL[~e\WvP$܀MM]C+qRK${Ʀi2U( 2ږJbzACTŭr}7v;d!ӒY4#I^A ,C FV0=E 9ԔիWG!x闰%Yur!cg7@!n޼9p 7 چy̹ $Mئ{]fk#9s'do_|$~ & kɒ%v*35?8pr-d-0L荶!޽믿ΆJr2d\|y#؛o0aBy.ÿK/vsʅ#Oy؏_iҤ9q}CT1d,o?wC:d|^zfui~"%Oӑ硭<193Fo|;\ 3d w$I+[./<f?1_MX&hȑ#ӧ8 E kժݻReM oV&]!3haA)f-0WG ,œHِ%m7h:- AȒpF"TRO e:w,2zu88|2}EDױBo1PHw22NrX챀- M]gI ޺OdtFz!9ځKh|QeB8wV/7Ou==sJ`0;q~טz*In޼8`5ě;/gJO71[̘)tS~'cwabgXa o"ʗ[L׬Y؀LS"qK0ld%(`CPF mEƌe$~1U4&6hCa\׈m$ 屹;fϜ9å[t<$UB gʔI9pAv2k`l\Ney"MWR?3!7E۴q_XND|ޓC(b酌M?DŽruB{&/B}!c^%.Pvq B2,hnapL{ܽEQ 2 !aNW*1_p66($! %7HY@ͥ,@sg˦n^;5 :K;d0IY9zNC)d>1!^?zౘp/){gʂ2)"I>cɸ.N \ 86fM\*3 M$ RJ `2#$HE6M |*0ےf:6 h9BtFo2D/AgDV!% +u͸mЮL{gID¢&ÇSڍOٜɘ@?r=>Q(-|i2Bf`h[J")_.,1[KFY9wmeuS4tM֕HmO"QgC"nD*t{uaG m[ tCDLe6Β})'![F>FiMpPAgZV;iq@b˒`Ѓ}Yx@ 8MAr%81NVEK ؜\ʸEkvlݢR[$@&%g" 1jE<=xa^۶8H QSwVlnΧFv#hb9ԤmvK9@a)mE/7hBD m D.B$v(iL͔c$hcE(mެABUt6șR2VSܩJYkԨA{g64i&M*Q'$dU5 \n޽{ɁBƹs熛b4iȣdd3d_nD)YlRaȋSs$> ʧaE*P-*MJ8'j2Bq-|:v$fIB!dOiӐ1eYbرc•+++GM?"blF xT3ԪW^6>px6&6lȶ 0]'ԤMVj6)P-ԘnەajՒmcb²'GoJ?|f?>nasd^qnjC|aك_{]SWOgNk㗌G/gRo|A# e2A;Cf'Ķp;iivQ; 8v KO\>~YW"0iYd3 4&EXQAP &ҲD MZ&m%;Ot½˔ >*{-2-߷!$`+9EKܷ㶣ApXu@z{)!`z2no"s؉~bkvj^s /2M64"S9#`u9k&=T2VS>;u?jti$IsBH۶m8qbY!+Kܐ {Ώ seSYKXM8&8Vʶ%K (+VpQfm%JJ1Ql`x>tP6Y V$_ Ԙ.hەaEylcb¡mat{ה='w8>xޠ^S{ge^sxi]A11G׌fo; mB{2LJw`[8h4;`V.; {4gT|V)8̺&0K1 UB)QLM2CU2V2V2VS1k׮zH1ډ*UB%Ք-Xm~@ҢFm,Paemd# .a8k e& 2+.FO W!N{O v <#3_:s1piihq5ZM7O)cǙ)Xxlq lW{aK2\^0dm@h=a5Yxx%oe[ A:"NNR@Ѝ@pŗA[6$F,aեۼYYv#eQ TI,Y]tƈn4T[A@)hRrɓ65冁uڔe$cne/D 8}#1uMjD&rTI~&[ƶq0^x!3Klvl#~ZŒncB Iq]8<%c5h29ٳgO<9&CP~g()*s<)Py'c}aVXM8eK.- B¸U旴̈́Yd  m&*&f83e!漡6d ]u=X땸,ްO.<-A_14L,YZ% *fv3He-YBYY:b+l/BƋY^ K5D/"1c(jm"n'%1BX ^o$p+iLUMX=7n&KKѥ5\: $Qgi%C{ޒ^Gɀ#$>;IgqA!1M떶!)C.~:3ΒcY5#뮴/_ ?NY9m#I;M+e!I{`K1$F_r?2iAY&KcJjjd\Gpפ 4@ ~&W^9'#ҧO cdޗ_~AFmmʙ@桧FXJJJJJjjsߨsy|żgm e"+$,}p^y#``?!O2ٖa期ӶY [&O %g ,eMS!fVgl#MmjUn8uj1ޠWX|`/LMp&Z i3Ѥ_#CMmHZVPE`Cępa[}]\Hܹ{ۆzuˇ*3Ht;mDf4NK4,]BrCms`Arq4#kԬe)Z>?WB6((V7~lcCëUc>.m"n)eZ!b=l#jPXL,G̺7k&M}?rEKq|3bNAҡdd͛3SG\{#rEK7tjzJjjjjJJ s=m%c5555%c%cp?W\v[XMMMMXMMMMMXMMMMXMMMMMXMMMMXMMMMMXMMMMXMMMMMXMMMMXMMMMMXMMMMXi'OL:￟:uX~UFf'|_w#tp䕌ՕԔՕԔՕԔՕԔՕԔՕԔՕ|"~_W2VSSScd,T鳤͜S]]]]_uՅbYL.zc%c55կ_G#߿"5X2]fqmm۶m7oUR?|ɒ%=qwǞuBl?|p"E):+LIfovC+W._|˳gjgbVWWWWÝUkYib|]\XM;~8$*-[쭷z~Ӯ] #G;wlҤx %Sd\;'NSt!|3*{=Z|򤓌-G[xZnDWI:C k_ov]5kVv?Pɐ!Ce…  x^|ŋ2}_|ѣ&LOm)SUSB8E?s OO6]XxۦM{~}<8z&:]tbp]JjjX2er!)R={v5޽{> ?&M.]ݻw#L,]@r\#o2Ν;7 b4iy%c5dŋe-@?Ӭh4d| vرpeJJSg0.^ttdR!c,|r -[]7o ߹svZ ,(Oooݮs6s/RxivΙPæ-?xruN7hH9uVR#dO2Κ3Gu_؝n=ڮZ={ɓϜ9STT P~gN*s<)v坌%QS[%c5dUtQG\d߷oǏxlA=~{-9&Ӑq6GJݾ]PŪ.A\{SĒ7mz3gu֓95ۻ'OuK۵g-0woo SӬ9[܅'NѻҦ}k㙃Պ*9p_I]fYؘ +eS``]n`0 (QQ5#ҧOgϞ;v 1F} cAFmmʙ@桧$/6>-?IMXdLC[w~za9US/\Bns|*`i3e+Q3aEKKWdfμ߸eV1kNϾmbtLXs8pPzٺ]'bL'MFZsp]%gނ뾰̰5 ]\uMXM-Ν;磏>2s |żgm ]7o^E4iRX!aӜG[2FzӦMoܸ{ whSMݻCoY1q]t9a:nCG޺}gu9lJصǚulkzNup]2FKe<D=جd78)qP2&v_4O\_n+VGlA=zΝ\h޹AbƽByWZ=3=|ԘS,P:ª5돟84Q}Æff.2B2]v7o8tDKWLj+c7+Vaކ5eEJx\׽}unv+n2Nڸ)]~}ʛ7o26v'ļu2v/l`qj53-2n޼-R *zin&/\dBb.[*UZ'O9t&r-`q2fζ8t.^/YBZnܸm۶mͼۥK.Yd'?nY]]]]=;cd]%cuuuXFi3¤g 2Ӓʕ+/_wٳWٳ&G=cUkYizl!c8ELƅwN4NBƷfT{ZI';[PWWW=ni]%E \1$cT2+x34i~-KW_r+f-X2}I3攭XMI羉26mEM~?p֭qFuuuX6։*_uݯ_}>>kly Ϝ3_X1{4Gnx-:EJfɕ?'O\֭)ex?/-u޵m߲cl\bE˃+WWWGh̝;o5 :jhBG;gν{gͶ~ݺ>{߾}U-PԨUfuBPvPŪ,\lr}eU\zr+MIR^e,VIv3ZPUk/Wf4b3WQ6 p2ޱ֝6~k/YIuͨu([)9!ғ99u!vE2v*ZB2fU]Xt qG܅"LJUqGH`*㟯p UjAl S/R"dp%gu6cCQdUtQG\d߷oǏHulA= Θemu.`k׻ؾjDμQS9skiWӖm}{嫬;e|Y9iҢ>K6}?hǮ=_;s-$nxs.ت5(":scZ<6lFWZ֊WVV+S}]X}aak6Y1DDpw 5EO9 ̘#VQףKbRp&,Ab4,[*oKUDA7B8_ŝFSh0!>a#,6kӱbZg'Nfv⦂o0uX&bx PTy|,#Ax U*W[Z-&8cI!` 36XLW"okpaɒ/-fzB)3 GqAe%#=Oa`D4UV3GP:j<ͭ&,*P_ |C{+pS?yOs;vxh㟏?qm8\aopY}1e"[obH2'NFtμg11c֜}*|E?0xHg\m2OޔCwffEx <)po֪C.Xj ۰9c\l¥[(Z,WՍvGOwr8nZNF(q[nG&rr98:~h4\p 9S { 񁌑FܦM߸qA|2ݻ^u3ǜ+VISBƷ|G8o]ȸ`R/\3cfE'+t5.L~&@dpVfvI0.ū׮2L/Zl12ݩ[sV2f0D=.p.6-dq>f-{´YcM`1D:X69g23[v]n3* 1אh"c]_$Obk7UIzMH7!DPYs5!Ko܂5#!'#o:$"ccC'$&L(+A|\}(Wgҳ2L}C,#2ߢBBƒ~BY{$,_yj9i2^BbrFy[OM=d[ _] m.dǯ~v+#Vq| 4od\Q(\bdϕCGhGm2Śj/,$ %cF6m2g3d)3kkcn&,J;fR2l$eqiVI&CR,+ˀ]:-YBOwf}E ?ua1Ҧ@[ Ov'0B"iv! 'clgs_$ qth lp;[sC 5;uIg4jЦ(awA|#6妢ϡ\!t2Bd/֩ɘM4ΓǏ7%1QS'Oݿ +c0h.9f y6BTEJu_2.Y{ pie\p{Pbμ Geu0B;7e6w2N!ǼjZ'++J \)IWIeG_(]Hb$_s]"/& "E[<}'$ddZяR$H5 .:/[iج\K7ŝa^H<Q xnC=r\JB; JrHaGɗVӀm Lm$3ٗ&g_jMap4)5Ҳ! : 7f",ِi.Y.';r'dLiʨ)8V}sOhD걗ytd,p<{֬ǏO8Id2{䇒z:H-\AݏdTJ X-l9r!XlA,\Cؿ1c )HA {Ć; h,a1br{p 4  9 `HnjzDmIgEk7*#ݖWc KOD+n[:%`,&/D"%$3` GJn 624RTjfC|$% Z ';G9ʖ9auufq0a] ygG82r9/x@jknfhV"v:BJ]<,2nݺu]gdNxXD;ѰYK=&^h5B \=OMaKl-ZDuƙ1LnvFI!KVjvd1a!|e+e2Sc~^x9mzfD iM6dLgAX"@u29=`,yYHF Dž&ِЪ(U#M&o,&42.d\s|y:Ij) ҔDejpRPcsX3$QFdމŅT2dA_Lc|̘1=@/^c:[PW(|1$0xh1Qˉ>: ~]), .2p2JxA(Tte(˧ĕ G_>cC00%yUuTِqcB ǀ} w'c cŸH),I%' S.Y&# jF-0{ hfw8V* "cz3_\"9b-ΈQJנ]#d,y-'MT>ۣGtӌDP:4#g!D"*DjWkI2v"Ǫ)ʔ"Hg2fCFŽ WEƥH*0#DgY$c %f\lH! ģivíNPޢ B B@8dliBgɑp3D+" nֺPS;mC$ȘJL.Sy*W d&& GC4ƑSW2VWWWW2Cn |e1LlG#!dL6fĂY 1c";8C֨ʕ0ِLpTyXO01|#ѾqMUptwoдTqXȑ9><_d\*I[/I!Gނ$W5>nQ]]]]=`!Bu

    nX23^qww'n': ~ilj$0&H ϓQugȒM0/s|:W#u]rDXddNіReM3L9`F=\X]]]=q$b<g\,LrgȚO1W,΄̠+rqЪ DF&chTrk2_ rEJ%uƬ%^{.YåֆMƱWRWEz\X]]]q!5fɭ .{Q 2VWW2VWWWW2VWWWWW2VWWWW2VWWWWW2VWWWW2VWWWWW2VWWWW2VWWWWW2*OB>8z|ݤU/]~ʵ` ED'͘C"Euu%cuuuǂI O5W<gΙ/,Ϙ=7&SmO:M.35}[ēH YsGVDQN6o߳~ˎ._qs-\=fK]=u?SWWWW)XjUTDY*!ۼdj-2uG_INxdllҲV8dZI `1T"@*afZrԝ7kݾuZw y)%g~n Yr䋦]ssnlInچe#T!c00cH *V(rkؕ B.wӔйG.=2S>T.=dܮSw\KT[l kkTNCqAd\jm*FtYrrxԬ_v5s( :qx = d%Qޕ6 e{RlHSatzt0MHrs,Ld8CD wPS)\2aO`ʌy0bu=:$++UgcQwB`1צcuRc%*"p/bySh8w3d6͢Ϩ]i6+VqFB4jּm'n*X SgߕP1|f-&L=fd m@DѮsjs.?eU6 DȕHŪnX~nBl ">k ï ߸kF% @0؍|1 cfB} d|RQWW2VSSSS5qSgΟ8m2e8~Nq`q]|VZ ,F ;|^q<KW z9D$~F(Z*RPaCFWd֫.=dǹq3FdsqPSa M"w &1>fL ?ua1NՕLrC06X<&&=tx+{nAR>DrEB'':=ş["X y1UI.sw[% %+" Lȕ&Dr$PPN8wyJfZ!,ԭ ?DRs@:F>r\ɘLvh$i0vfj#`L(ʨ+"L2.AEK[΄\>.k m,o8}\01 rx^6aNKX+ZFq.C[ɑlqXɀmq˶$"ٵt݀n˫1ѥ'HĞH ^: ƈ%7ac%cuu%c5555USxWSXv3Q#`wqlyD d SۼlRMxu#dD )f]QlE0BZ t39{Cy w 3"Gn &1 1ۆ 9(P "#E4u26:cȘ1KXƌc)*{8ʄ%*Q͠P0W6X} CAU! A 2f\$6Aإ^q||~gi6&b:uA$͎v= ,q\3v1(L8b,Fg +K6Euu%c5555USxtw؉8hLrXvRN2F.Rd3tG2&EDTfF5oݑ0=F*M> ㄍIFDHƈ(od`1 KXJjjjjGManjQ*:sXt2rZ >qo;OƄ?%R 12xӴu{F*^{ :IؘC3N(r+ܼMw)E(EGB(+{nrc10zL baϠd6dL)7Ô$!ct:}5Q6$S}#)cqTȘceQ0g)(SWg#OaȘb 02FsfhZtߔTÌcb)ϘIƔsK̸D.))88)Rgt$hڪjfC#d EMA> Ƒ cZgCB΁w΀)MϘl2 78`d\9FhX6kNhI$ &; * ]xdbq0w2nд⠊Յ ++=FdetK:0ܨ SS w3+TH H-O\֗9ɸV&01˜\ȘwwϜ3EK͠JQHSNҤtXbT}D\8\3f&B~)uuuq%c555XL)E%p \GIltUj)P0oSHvZΝ_x%Jܿ9u֭|zȑ~۽Yt?w}w̙55e/]3%%cuuuu%ǂ~駂 N>G2n߾=++?Fj ,Η/j gŋߩ65xfu9y2~x^ƌdɒ9?e: Zbis'RM-Q"Vm;1?~믿&kX|AT\y…L9O>AgLֶu2OV5jTb*Un6%c55%h'c=jjjj4ǒN555%c%c5555%cTSSS2V2VSSSS2/jKMXXMMMM8~w2իGC.ɰՔԔ`[ݻQxl'ҟww",8n91QS2V2VSSSS2~; %JD<ƉtjJJjjjjJJƊJV2V2VSSSS2VS2Vӭdddwȸ|jJΜ98 mذ!s/?{+WNr,_܌z׭dd8W\l۷o_޼y~m9rkڴCN>|*U6ZĉY{׭ddɸ`͛70aBwz~׭ddI?SN>}>qF%c%c5%c55555%Dž|lo={I+T7xPB6{!dÒ$IZXJjjjjJj#cry/g˖m;w_ujɧ/_4iRڵ!z}{*jJJJjjjjJj cxȑ:cBĀ[n؝;wk)ՔԔ:dM1c'0~5jg}ָqc%c%c5%c5555%c<G9ѫZj=#jԨrJR>|Yfp0m_V2_Z&cShq)̙|dAAA%"Cū!CE*+}l۶MOԔ0)))+)))+)))+)))+=d Ԕ㼑ҥKjjjq:@XX!( d獿SSSSjdd1c5%ڈ?[;dJJj)E9SSS{lzV2VS2VS2sׁxN'OL:￟$I4mTΝHFd%c5%c*+)˜9㙘={6pgΜI6,_Ө$;y;w}`o>/P`JjJjJjjjJׯ<`?s)K֮]}oܸQF~gȑc˖-믿޼y+W^ʘ1_v횜2%c5%c%c3/wb.5vo2޽{|`ޢ@A(2ڵK"_vؑ LӧO*պuΟ?cǎnݺ-\G2駟fp2/))EzQ?^=1۷^"Lܾ};[l={toKּXK.۰a|т,;X&֭[JӦM3\ƙyС\r Dn޼鱇NNӎKǐ͛KNdBƽ{~7^yZjJjJja3b.aӨj<Gāҟ^-\5 u_|3޽.#Ô 4%cO8ܹs (k4hГO>IBXjkhg^{e˖ǎCϝ={va?>}16)Rp>=7n!cVA"B/>j(%c5%c5K`CtlDM86Odɒ9?B*&M$.0-?(PXbm۶*?x~ןWu̙/?s,zemܸ9N57[n=qB<";M( 2f-#t)^xɒ%Քռ]/Wd({1_5Y6nV\Y]tARlG??Tnuq F'b9'VB@d@/ #c`FQ$8zPG2駟~alٲ)S96m2#Nƹs.W@y1oիV@ZXMXϙD/ӭd?È(b\ A.\d&+|!ݻ꫓'O޿?bf$ 0FqQ{.V"8q=1-ExluXMXMXMOq<Gki5nRtRID2Fu]RxC( j?|M w!,5^Քu8:tƌ9Ai@yٳgg͚0D[D&$tdH2/dܰaC"oݺT08C #\K njCSsNխXB Sy!c:ZbjI}cwLnPAjK2ŋLRP;Z%m.m%c%c5%c5%cuح#c8L|嗉z|$c7o^MMѹsgKF]Z޹sg8vBƛ7o?zJP(e :ɘ7fuϥK~&$+I$f>zk%m3+q',gڏczjժN:م) F : PSO=#+V駟~al2"fΦM,X@#̊/!C]g ,1*}bĐ15\Lҝ,YP+ [%cu+++2P5%c]Q,0 \pnswaH~O:EH\q"EN=jw%y{=/dܣG.-X2޾}t:,i2"4^lM GdJjJƏu8ZcN۷/t mL"ʾ1C*U$,C%q:uVZjrN52Ke0Dr}G"1o[h#lj~d|l%c%c[ʣɃB/JjJjt17ȋϞ=;k,.B_5 'RqÆ CUJtz)RufیCS>}xPX]/ 2/]tfޟ[\f(h#GI|@ Kt֭0aسgCf>'xiHKeI"yծ] M:7ИqtCq?_q(&î}Y,^cO81" ?F^F󫯾׿83mڴ!`G1$4iRcƤH S\9s=d,Mk&k % .:d~ ?C2ڢ pFLnݺ$C -[VDmذa|BΝ;h67`v9sB>B- ǒ˒1 ٸqTgݐdd8188d1ep#]K0]qU}WjժwXXMXRL80.uX-@8фyاOӶ9;_F*@'d*HDh|`Fdd_Q)^qt\ռXf(+_Mh 3#[ǃ!c2j92,YRXMX}ls>e5fda,Σ)dL#dLY&$tɓǼW^X8D:,O8_Qr!BɑH9?Zv->c94mqS*! *:-\;dd-dXGS p!cZ#.[W2VS2ƽ+7L̞=8v~UuҦM+1 ȥ&)A&M"t֘^]}XG;w!,5q\ׯ3V a7yFyHM`)6d밚cJƱ?2@i,<%c5%b%٭y˅b $r[Z5?SB6Q?dx@-:̗TѱQ%c*+o?2zFsbNpٸq,q_xWs.VXs_dFR0Ax2X$'.ß%ǙJjJjJJjj1x+,iCawǙN1cguh[RR"غK舚Kh=2JjJJjqqh6>1dFg Hv%qfY"7nԨQ&N]9rlٲw{כ7o~Ry?~AqXddjc9ƌ#O~mIЖ$IuɧgFG_qgӧO*U*v֭(tAXXLX8[,?XJ{{6lpO:۷Ϲ?0[DإJ"ӦM3<\\r胨sPP͛7={&L8e'J~3gμ~OD*#Fx뭷 ZpSL'eʔdeB Ql*)+)e %c%h!cM 6:I _ȸ{H&OԩSČtQ)Rԣv]\B=z4K "Vo.3p'c/K*)+|ǔcJJN}nR2ID2f\Jdeh!cu1[iժՇ~{g:VȘ.ޖ|.&j [j IWߺu+[a4$jO>ļmѢEt;%))+>*e[X?d 1(/gΚ5adBT$$\x2nذ!y!K֪R :`C * KXh[=}4ZB=$_e^t? [r%ͲQhxCG1>5>Қs^֭0aسgCf>'xi"夥$u@7id:u*b"G`%cv++|TTS2Vcd2"_}տ/ƙmӦ c>Ȏ!ѤI3&5I Nr 2Ԧ5 FL2?u!^TDmxufm#[n]!˖-+"F C>bd!}w2fs `YLϙ3',#`.|,Yۼ,xbr_="ƍ2KXq,ď׭d?X_&d{dddxZ`8ߪ_cJ JƱsgJÇtZ`QUXXMXM8 mP"1ƀQH$!wQ2VS|팒Z3%cLJp[F2ހH3d@]I%cdQ8Uw錒q3T-2ѫJj2T72++))Gv ҙ]ԝ7Jj2TiKT~JJjJjJ4jۛ 2zd9jJj36V NjQ2p,Y$nddd0κu똸~:L/.;JƊ]]v=zmWSaŃ6:6f)))W2q={c-b>مcaKdQPyJjJjJƁ1jg.\ʔ.;Ljm*{l/;bP!N|딌}:uP3YdժUk0%[ԳǶ|JjJQ7*#cs5k!Cx_G|Ԣg^>bdzhP_W2E Gded3gV2h|;FBݴY+ǓD4pI\rʕ$+++Q2~饗.\>|eE1PLN:岌 +icY(l+pD9Oi8+ 5A\%cO?$/Yd׮]^f pIyq( 'N+8  ݻlRJܝ˳X.]|̈́ 2Ν;\r.<ݿ:`zKPH&界Քlw>|eE[~k׮ i/$$cZƜ |ǴL'>{|1&ۤIӧOsT'L1pdd[uG%cgqqo+=jJ? C2w.1ѯG6e5bPcs8*:}5H u #ڕ*U*>aFr_Lpν=/ bxm2Ũssr/hE4xրTs2EjȘL`]/{2#FcJ!N۷ooIP1](M] z'ctƾ1߫W/vrKp3m۶8AƆk?Sysx_ ,>h?gX0W&~n9>2XfدH1B3fLܸq'N 2Iā`3hFqxP8#^~e.w>HLJ+W0q UҥK'vdk헝^-Jxѩ0:FT3e_R1 1pa+:+G@4r*&~L>_͛'=>n1\;v??oPB0테%k&3dmf˕+GoE#Iqpq;w&wŒ4hP,IdҴyy8Mӕ;.\guΘb9F8rgGG_i_QG㍑//Քi >pn۶-#=cui.g_Jۍv|@OV8QiV/RkX =ݻm}!/k|6Fx̲KpW2ng) dwΙ3*2'SHH8 EDA,>N$<ײGO/%>#1(E:Qh%pdmܸl\O׭1!8:ib/M].\+j~)c3Pjժ1x5rj6fdؒ1eM <~J*L&g_m{yOg\MOJ+xuJK> gΊE{sY)M@3f<%H^pSEM#b| *=2k]^Va8&i3_MJ9S2V2#++iӆtdR2ݝ;F7i3=:o HyÏ|叒ܬ]v6O3VN tHN׫ߥY뻤 &=&  HID_0%ElBܬdFU$9G#dȚ5+z?L%c5%cǙCi<"irڐF8F >%)xeZ"U$~ i\ KRuUR:uo(\- )}vYuo=lc_O FZI26l(#߲eի/d$;G9;NΕñF]^77gݘ=gJ|JddɘK>)׬Y# X8w۶`_Hzldܭ)\ӟ'6_Hiu%Km+UN6Νgn\K^隵b Hgs97G5n3NTBưuoP[ M$AH@jԳ1H+tdJ$Fx" Xr=JxSbxڜnWdd_ɸF@ڴiSFD)S]ؿFt~{>Y厏J ~̨;>Y:;KC?>ռŦNK͚@C3Lro409f=_? n9ڼZ1,PKPK \F=[\ kDGP_c·gLc_wԵV2V2d|`2|#;V2YկS_}?O1MڥEnPBަv*|6Wi}+7AA2u~ݹ*׳lyUlw{|׮=C2BƻRZ-%jeD BN.e(s>&EjUʩ^4_pcPAc̕?"$p a)u OHLR 1r{+GĎ"Eex(m _&g/b "ՙ"E#Y 꽻x L:ud %_/Rp[Gd|u&L 1+ΥK4Ē+0Z5Iol.jȗ^({9ϙq9Bx.^GŬ\![zpqD3gw8a_FU㽅UZX8q<>]֨&dDa+~6cRK>e+_%!ӵLϥM7g.Bd;l2f:-j\/]JTHʯ)(M٣GRXAGJ{;^"!&m9>vWD_/(jdU{gM퉄(Q""S2V2S2pT̙iX 1)ҕ3Aҥ?䓣~|Q$oV:ߵ$`+R"MQiO~䔽;rLc V>pQ3 "q#?"pMK 갚 W֢úsFRw }5;S +VWd5%c5%c=q<pn Da݅ Qā_cp }${$<߷.-^s޽;ϝNivgbgdŗ>x'<$N[ ξϾd)*rg=p 4jFn!e>ʗ/OE>rJgYܐsZqj#L6ˆFo":E<࿰Xǘq|ug2~WN>Wd5%c5%cxyvQM˔Y0A\ A1w} ='>;h1;WgUHc|4}n{.4HjV\=>v\ľcǎ+WL3,ČX&| @JEnBR;#FHBh/c~E%noJ(jM6޽^2V8V2V{&f$'?|WScPdktU\/]JtW]30 "`nf;~$zQ*IϿ`MǾH$Yy͉\v J!`R  #3POV/sxH+]Z}Pl`}{D6n{?+jl˕+eԩazEVS2VS2?LT kŻSʳ4GUyKFDLYv6O^bƇHz(W?ǟ\H2s/%H^b{f+tν|tω%ɝ8TdY nFP85K '&fy樔w©∐eգV{_rR7dܹ3i5?#8SeΜYjJjJzY).^D>AH)%Ȕ &S¼(Vwn=~Gf86="S$:0>8Gg&>0NwGPq'KޙDNޅ]dGCɘ+I:>pDZNxI4`6#k4,H6ߕN.QI/|벤);]Zpc'EW{饗HOWd5%c5%ct6i\Y::ufH;nom>Oz4a"='zWӝEGa޳[MRP cc[L ͜8Q"FPvmب) à`1 0eXs]sx'Hwu5qnݐ+Y%cee%cxv6C#/[fȱQ?y͚{6TyO{SBq^X/n/lDv/,} U9s6]1( gPr5v"ŰQ!v,#`u]N^;D(pX9ݳE`u җqX # }!cvlzf%B G| ;LjJjJz㢙4m=D 7. ׮o3R:@3%K]ICxm'90_ {!wNHr|*Afm)uR1hMwhs-DZiT] D~#R0MsS*azEVS2VS24m20nWD|m;tV" 1ļ?Uvws&\sD#oseʼ3e/!CJ82Fݛ pe"K8,!RR^"\u,F'3>+YM'>V2V2VaH;7U'Wو/$Kd)m7&JuFϿ,juJ߭6\ 4jv"hܸ1̄╏aSB½c^Yʰ%רsr$eP_8G߹|c%c"aV&V2V2Vgagg|kg>$ ;]J=OP ,|)ڽz* ?.c[ҥ?e!T Lkj=I(8̩pksgM_TM]e9}"7-J>Y„ y2"))q={D>U[ ^MY;Znṯ)qΑ|Hobg_,ϓFRB։X&xGl1X\lYҩe1,CYIj3+B>̥Cߢg:-vt^W +]0 %ۙ3g^|E%c5%c%c%Xeׯ_!]4:ue;ɸW^i0B^yqL(|u:Em,] JF~'ѡ ~7e;EĆ%HZpV&C$nr,"A{,C>N"Tu>Gw6ڴ}0ѻ?Nҷh͚~(JV, :AČ ;+?KhCh2!c ?K #dddx2n߾=jq1flki%~,J`It64WqtlI>]KU0,t`B惿DF_P_8+gVUKy DeED4z@LMIb.$cqMj:u>|c9SS2V2V2V5/jx3~hgmcoseٷ>cljELl tgM׹x 3Ny 1 E#ByIQfzuWsz 3sΙ0 A@I$+Q cϐ$It5((]5uG>?+uu9穧ꯊo{@x.!+]t=n`u1pV2Z drXci= 4cU7?L\L\.q1' 0 2ƼySf@߇yg=sRW-rIwܹW Ay`\ VK"|U"@Od^`qE_:v "L'Ә ̐qP- &c?_W)*;6`ÇuV|7~7|ce֎6RDcӳ_*Cӻ >Nd@CF;  1ڨ6xA%AQ@Z2T]$Q8f<^rGhF;iBQVӧf,PfG 8y^_v"r&cowoa]~t X|41X| N󳲡 9/`1 &E) #_ēL,^Ѵ|8Ew 1Xd*|7IrsANqVHυʬhI8foj>rK<]la}U͸3=]O>  c^µv +|Xٿ?ȣ472s1333QІ1pxoVGvCISP1͟#LBi"2!b((/xLAe6E¥~Ai#g&C bOĩ{jfbom?*/_i)5XLQp@XP/"JAĸb=%؟`Y%%eWAE-Bhrh>bYьHi^Uwlm&d3'?O;vp}oo~#6.QUd<2dddܰ.w9y': |$*䃣.sic!]N#gL|mۏ +Vx D|(N2hv&V!CHrՉO5i~Vۂ;#;B̙úPʄ ޅ+ +/P}[rl$E 0YŏX,lʚ-cqiX.\ a\ނQI&n%k&Tӗ1b._L? ͍syNJf2f2?ww_LL>ݺ-WlfkڶAA8 LimLl RRuxyY`E@O2&̤=ф :4clTuΩXfnјZ""J3Ef)4cq.9iw_DSkR~_&cּyBCCKd&Md;͍Up1330/NgP6Z6kLX9(D]?G;ScO4 "EdY(c7.(ArB2T/0\ &FւD֛ ^2ɓ!S+SײV8| l1C^PX:֌HEn|"##>@@˯ ɘm2V01q&cbP(*>wW'I#MVX aXJs HxG vܡ7J8&يwj`@zNʱØ0Z/¼USC! m|A/N3vxj0QŚ 1}S޿%ڶ?2=CKLKd ؅,K[4,;!"%1a3Pvu owHЊѲfGբ4 &&szN-揩ItMZe`Qmb/6k|Gb2f2qüGqeh V )z()ajG{C 4ItNMDA2(2BOpX55YDZ?(!Q%>E/a |j);T؁6k)_UMptCʹ(^dwd.&c&c&c}<^+8ݺmx\I C]G2H ꥖PK#R k 9Ҁhh'2 Rw k2O^ =R )x"kB2"L~ox©y{rW c?qس~1ߑ=ɘɘN4T7[9 Naa7fQTZnx=z4yEW KJ|,ǥ7LYr4 WzR36ggx|щAO:\e͘|)}LL|6(2B> 1厜rg4Nt1zRkx Mj(26hN6 RNx- w,d5.[3UMcAh>mInz-W̚U%%{'ip+|*ٱ=br-dL |!tQ8})LWt0!9z8Fc u}@H@ lrDAA큲嘴dXG LBxFL*$n0t{ 9+֌> Ygrrvxus>knVR0ܶC؎EB 4,d,rL  ^Qc+A}!'S儁2Iz?W.aFXq9Msq[J_Ǔc3G2Ɲ>d[233}굎Y~S[61­3`1sk5d<OcSRو}!"hf^NerDhRd7-ܧUw\uI6ٿuSl4>nmhWɘ\>"cLL\yZ|iCg3ߏ;׹Vm݉ͷwDGf 2cl ,/RLE?9JI W,s,{|=Z, X^> (oy^[:,qս)y©ނ`'z\y4VoI[#1ߑ#|dda?c1q:avZд/tsRh6p)X wg &F:(@ [^%㋰R5xh0,)O,En(K6x3޵18qzfh&i^/CUb|=`… @X.1}S΅E|Цf-6m~]<h579"2QԪ,CX1MhC쫈(R1H: g*%iʖ45PLSi2`A>_  qӔ.\"Z JX3;211>Wf9%TVg}#:{wc(ͧ +cUk 2j I|w`,B#Bf4T`]AWI=\4 ў ؚg4aNq7le>c#s{Dd&〸C-ޝ9ݢM.LL2bJ 9Vo7?@& 1 uP=iB%BhJ2SҠ~#e0"B:e;C?b*Kh.Au_e3ɘɘɘxFWO>x4ik|Gb&f2qC-|xށOIByκFvf2(2Q gf"( d#g)ObC^w^?ht~b c