isoband/0000755000176200001440000000000014073215605011674 5ustar liggesusersisoband/NAMESPACE0000644000176200001440000000127714073124476013127 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(iso_to_sfg,default) S3method(iso_to_sfg,isobands) S3method(iso_to_sfg,isolines) S3method(makeContent,isobands_grob) S3method(makeContent,isolines_grob) S3method(makeContext,isolines_grob) export(angle_fixed) export(angle_halfcircle_bottom) export(angle_halfcircle_right) export(angle_identity) export(clip_lines) export(iso_to_sfg) export(isobands) export(isobands_grob) export(isolines) export(isolines_grob) export(label_placer_manual) export(label_placer_middle) export(label_placer_minmax) export(label_placer_none) export(label_placer_simple) export(plot_iso) import(grid) importFrom(utils,modifyList) useDynLib(isoband, .registration = TRUE) isoband/LICENSE0000644000176200001440000000005413501223536012675 0ustar liggesusersYEAR: 2019 COPYRIGHT HOLDER: Claus O. Wilke isoband/README.md0000644000176200001440000000726214017542230013155 0ustar liggesusers # isoband isoband logo [![R build status](https://github.com/wilkelab/isoband/workflows/R-CMD-check/badge.svg)](https://github.com/wilkelab/isoband/actions) [![Coverage Status](https://img.shields.io/codecov/c/github/wilkelab/isoband/master.svg)](https://codecov.io/github/wilkelab/isoband?branch=master) [![CRAN status](https://www.r-pkg.org/badges/version/isoband)](https://cran.r-project.org/package=isoband) [![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-green.svg)](https://lifecycle.r-lib.org/articles/stages.html) Generate contour lines (isolines) and contour polygons (isobands) from regularly spaced grids containing elevation data. ## Installation Install the latest official release from CRAN via: ``` r install.packages("isoband") ``` Install the current development from github via: ``` r remotes::install_github("wilkelab/isoband") ``` ## Examples The two main workhorses of the package are the functions `isolines()` and `isobands()`, respectively. They return a list of isolines/isobands for each isolevel specified. Each isoline/isoband consists of vectors of x and y coordinates, as well as a vector of ids specifying which sets of coordinates should be connected. This format can be handed directly to `grid.polyline()`/`grid.path()` for drawing. However, we can also convert the output to spatial features and draw with ggplot2 (see below). ``` r library(isoband) m <- matrix(c(0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 1, 2, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0), 5, 5, byrow = TRUE) isolines(1:ncol(m), 1:nrow(m), m, 0.5) #> $`0.5` #> $`0.5`$x #> [1] 4.00 3.50 3.00 2.50 2.00 1.50 1.50 1.50 2.00 3.00 4.00 4.50 4.00 3.75 4.00 #> [16] 4.50 4.00 #> #> $`0.5`$y #> [1] 4.50 4.00 3.75 4.00 4.50 4.00 3.00 2.00 1.50 1.25 1.50 2.00 2.50 3.00 3.50 #> [16] 4.00 4.50 #> #> $`0.5`$id #> [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 #> #> #> attr(,"class") #> [1] "isolines" "iso" isobands(1:ncol(m), 1:nrow(m), m, 0.5, 1.5) #> $`0.5:1.5` #> $`0.5:1.5`$x #> [1] 2.50 2.00 1.50 1.50 1.50 2.00 3.00 4.00 4.50 4.00 3.75 4.00 4.50 4.00 3.50 #> [16] 3.00 3.00 3.25 3.50 3.00 2.50 2.50 #> #> $`0.5:1.5`$y #> [1] 4.00 4.50 4.00 3.00 2.00 1.50 1.25 1.50 2.00 2.50 3.00 3.50 4.00 4.50 4.00 #> [16] 3.75 3.25 3.00 2.00 1.75 2.00 3.00 #> #> $`0.5:1.5`$id #> [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 #> #> #> attr(,"class") #> [1] "isobands" "iso" ``` The function `plot_iso()` is a convenience function for debugging and testing. ``` r plot_iso(m, 0.5, 1.5) ``` The isolining and isobanding algorithms have no problem with larger datasets. Let’s calculate isolines and isobands for the volcano dataset, convert to sf, and plot with ggplot2. ``` r library(ggplot2) suppressWarnings(library(sf)) #> Linking to GEOS 3.8.1, GDAL 3.1.4, PROJ 6.3.1 m <- volcano b <- isobands((1:ncol(m))/(ncol(m)+1), (nrow(m):1)/(nrow(m)+1), m, 10*(9:19), 10*(10:20)) l <- isolines((1:ncol(m))/(ncol(m)+1), (nrow(m):1)/(nrow(m)+1), m, 10*(10:19)) bands <- iso_to_sfg(b) data_bands <- st_sf( level = 1:length(bands), geometry = st_sfc(bands) ) lines <- iso_to_sfg(l) data_lines <- st_sf( level = 2:(length(lines)+1), geometry = st_sfc(lines) ) ggplot() + geom_sf(data = data_bands, aes(fill = level), color = NA, alpha = 0.7) + geom_sf(data = data_lines, color = "black") + scale_fill_viridis_c(guide = "none") + coord_sf(expand = FALSE) ``` isoband/man/0000755000176200001440000000000014070653241012446 5ustar liggesusersisoband/man/clip_lines.Rd0000644000176200001440000000204113501223537015052 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clip-lines.R \name{clip_lines} \alias{clip_lines} \title{Clip lines so they don't run into a set of boxes.} \usage{ clip_lines(x, y, id, clip_boxes, asp = 1) } \arguments{ \item{x}{Numeric vector of x coordinates} \item{y}{Numeric vector of y coordinates} \item{id}{Integer vector of id numbers indicating which lines are connected} \item{clip_boxes}{Data frame specifying the locations of boxes to clip to. Should have five columns, named \code{x}, \code{y}, \code{width}, \code{height}, \code{theta}, which specify the x and y positions of each box midpoint, as well as the box width, box height, and box angle in radians. Each box is specified by one data row.} \item{asp}{Aspect ratio (width/height) of the target canvas. This is used to convert widths to heights and vice versa for rotated boxes} } \description{ Clip lines so they don't run into a set of boxes. Useful for labeling isolines, as it allows removal of line segments that would run into any text labels. } isoband/man/label_placer.Rd0000644000176200001440000000314314064403653015346 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/label-placer.R \name{label_placer_minmax} \alias{label_placer_minmax} \alias{label_placer_none} \alias{label_placer_manual} \alias{label_placer_middle} \title{Set up a label placement strategy} \usage{ label_placer_minmax( placement = "tb", rot_adjuster = angle_halfcircle_bottom(), n = 2 ) label_placer_none() label_placer_manual(breaks, x, y, theta) label_placer_middle(rot_adjuster = angle_halfcircle_bottom()) } \arguments{ \item{placement}{String consisting of any combination of the letters "t", "r", "b", "l" indicating the placement of labels at the top, to the right, at the bottom, to the left of the isoline.} \item{rot_adjuster}{Function that standardizes the rotation angles of the labels. See e.g. \code{\link[=angle_halfcircle_bottom]{angle_halfcircle_bottom()}}.} \item{n}{Size of the point neighborhood over which the rotation angle should be calculated.} \item{breaks}{Character vector specifying the isolines to be labeled, as in \code{\link[=isolines_grob]{isolines_grob()}}.} \item{x, y, theta}{Numeric vectors specifying the x and y positions and angles (in radians) for each label corresponding to each break.} } \description{ These functions set up various label placement strategies. } \details{ \code{label_placer_minmax()} places labels at the horizontal or vertical minima or maxima of the respective isolines. \code{label_placer_none()} places no labels at all. \code{label_placer_manual()} places labels at manually defined locations. \code{label_placer_middle()} places labels at the middle of each isoline. } isoband/man/isolines_grob.Rd0000644000176200001440000000550213644532420015576 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/isolines-grob.R \name{isolines_grob} \alias{isolines_grob} \title{Render labeled isolines} \usage{ isolines_grob( lines, gp = gpar(), breaks = NULL, labels = NULL, margin = unit(c(1, 1, 1, 1), "pt"), label_col = NULL, label_alpha = NULL, label_placer = label_placer_minmax(), units = "npc" ) } \arguments{ \item{lines}{Isolines, as produced by the \code{\link[=isolines]{isolines()}} function.} \item{gp}{Grid graphical parameters. Parameters applying to lines (such as \code{col}, \code{lwd}, \code{lty}, etc.) are recycled among the total number of lines drawn. Parameters applying only to labels (such as \code{fontfamily}, \code{fontsize}) are recycled among the specified breaks only. The two parameters \code{col} and \code{alpha} are also applied to labels, unless overridden (see \code{label_col} and \code{label_alpha}), but are matched to the corresponding lines.} \item{breaks}{Character vector specifying the isolines that should be labeled. If \code{NULL}, labels all isolines.} \item{labels}{Character vector specifying the labels for each break. If \code{NULL}, uses the breaks as labels. The number of labels provided must match the number of breaks provided.} \item{margin}{Unit object of length 4 specifying the top, right, bottom, and left margins around each text label. The same margins are applied to all labels.} \item{label_col}{Color applied to labels. Can be used to override the color provided in \code{gp}, in case labels and lines should have different colors.} \item{label_alpha}{Alpha applied to labels. Can be used to override the alpha value provided in \code{gp}, in case labels and lines should have different alpha values.} \item{label_placer}{Function that controls how labels are placed along the isolines. Uses \code{\link[=label_placer_minmax]{label_placer_minmax()}} by default.} \item{units}{A character string specifying the units in which to interpret the isolines coordinates. Defaults to \code{"npc"}.} } \description{ This function generates a grid grob that represents labeled isolines. } \examples{ library(grid) viridis_pal <- colorRampPalette( c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"), space = "Lab" ) x <- (1:ncol(volcano))/(ncol(volcano)+1) y <- (nrow(volcano):1)/(nrow(volcano)+1) lines <- isolines(x, y, volcano, 5*(19:38)) bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39)) b <- isobands_grob( bands, gp = gpar(col = NA, fill = viridis_pal(21), alpha = 0.4) ) l <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( lwd = c(.3, 1, .3, .3) ) ) grid.newpage() grid.draw(b) grid.draw(l) } \seealso{ See \code{\link[=isobands_grob]{isobands_grob()}} for drawing of isobands. See \code{\link[=label_placer_minmax]{label_placer_minmax()}} for label placement strategies. } isoband/man/figures/0000755000176200001440000000000014017542227014114 5ustar liggesusersisoband/man/figures/README-volcano-1.png0000644000176200001440000102114514017542227017360 0ustar liggesusersPNG  IHDR_iCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*i8 @IDATxgp\ٙ%x`{=G޳ŲR9VKUDYNvDΟ?;ӽ;ݚkRT%A{= n"  \2ܗ|}9NO@    @uH    (hAA@A@A@, R*A@A@A@!A@A@A@! DŠA@A@A@A@A@A@A@Ab`1("A@A@A@A@AA@A@A@A@B4X JHA@A@A@h{@A@A@A@,jBE?Kpe 8nꢳ|q `{ X_Hw|'pquş~Cؑbnnnprr?ybS]ߣ4prvj.oW?3mhX_MuXlTӧm fA`WpI0GNRy|%P@{bxɎ Y;k_J‡+:EThN{i " www?A@Ao%6 y*^H;u^(.~4:10ҍ~QuΔ݂ E@"/ VAEV^fUCDf!G.=QTx1Y8S/$yuxyj"A@hA. >8#V]R$CXXzdpa Ǜo3z ͝AA@ ѰXA@AXXœ t|^)/NNN(7֚-,k!掣Gl0/^'ٵ_!3i/垶s,?. 5^ZA@A`]YAYkZ>5sFc}E:6abre5W=#L) 4B44R  0=;!VUӐ2 \(:!>)MBu YQՠB 88]݇N gzs;=\]@vA4Pn;v|mغ6'瘚@Q^1L$hn=JM!q*"+<=׮P 6SA@y3 CL*(rzzVqu!22YE.$$cCe `Qf]GyUho(ݻ;U<=K(x {UvIe_ED<~(,xAMd" 4ML gMM>Al8MC;WvarA@php  l&IЃz[Uo R6F J_ςnRFv6]]2޾Ĭ_Pqw&3;;ym/,MsF"|m~|Ĥ5255[]ϸFL@E"2"YU YxFZzN O9*:A1WRSZA@B4X{ 7eWYjKY1(8("  _-ٸ0oTa"l"+fHM۸fNۧʹI"8B48ڈKA@Ejg_X~艌L]$ lG?O8İ _9;vx'ŠuA@48Y_a5R'D 2.1D.Dŧ}gB_Maщ1OJBO z0Gc9Sm-U"phBH[l&'&Z$ۯ5 uc>kcfAq)CbfC@r jp*DSG"L' IQRL fIɃqʁiPP봬׏n-:! 8!W#@%6A|f?{}mo@OM4Q--ƽ\]C`xϦV#!FBl˓=;=g` &8>-RČA@=(myl]{W_]T h~V.ZoO:rvmy@pW1:1h:666f& 9p;(H&H` o}"ge-gE*l/};;qW H^"WiDTxv"pPZ"\:9)%Ǚ>*EiffE. CӦZ%Dæ Dup.!ffPx709mpZRR! Ӥ86?ЎnzNB7M xs&|q/P koZ#V0=Qݭ*dq!f:ɤ!H+Ar޾\8SJ|d:N|^>BAm'' B4I DFPc%E4Lʑȧ?dɡi"R}}$tD G'/0tTX$8J։wtzu6avfja :IJ\BCYA2ʎ7|oQSsCғ &2k! h`'C@rV#Ôq ͥRas#ؓ{BCkzb!bV(e]Ǧ9 VlhX?< w-yЏ D*&A`nhSiz4/wH,|,  d7 6Foã= +}fztM:zx1X_ "hRQ s`Q%ܺ{n3Y`$+̣4 I#h`, f(gVE*QcCJ9FށJ.!azRlFX.`!l< sQƠ( >\j[k# +Y`"&Y`F{-!D69B4K!w =KM)FttEلh0GK !JaǏ +hF [ÜC[R)9'G7.28wq;% .釱/F56]#2wB4: 8OA@m! P["RSbK?LW~-ܜjMWd"HtrX f_",Py*: E u.hGvdfeь;L]3tw7A5HnsdK D7O+iiyn<~d `HD tA!07?j]xz<~!RRTBHHmN~BcUhj*{S*uu! ^Yv`q꡴J"Zj(gAUQ IVqቖfqo_T ;wwwcDk-@PE$aK  l Ӡ"d'rca$ͩ$Ȥ"ߦȅ{$8jj ?$$g#9aQ 2# r"bHɉQT]*]HHqHI+>ˡt!LSҠŵOJħIDDif!D@"49,(A`9ѰY>gUE.4?ŒpKߣȅlf=:%NKk?!8" H>(8J-m V=pn2_w8!"6)9^.Y+$ag16WQyxxS!J8 _}Mʂh7?@eU7QY/0T=D,GWWzPYzUw0ob>Fo3V?QZֶ \ LMdS4=/w;. pxhp[@Whב~$Q1<$\O"077AJ0>.?ZEGx6#d!O4M*k@CL@F``] utޒL? &]5E@?>Bd FB>- z-#@> _8S)r&8H 1X --Up"#-I3UaUXd ``cho=֓c#E0 -^``r`_evkEKD˿GᷫVt~4'YƆI" ?\nʹ2*ruaXM X(_mپ~o\ L(?tR=A o0=K o1"o1>gxV'0D? ]݅ҫ縭rl6uI*! @Lh`ujkow?;EYDZ&2I, T* Goo"1Glm%HI٫)lE D Q 5F-8E&.Z?2o}-f>O#݋C[7w$PZD 3,zݝwV|n:ÃN_{33!zKMύ:c2a'=yE*, >A!4^")‚IO PdNeBEA"#H}P;\∥K{tcY rZE||Bvsf׉!Js1,<8FE7Xd- 4ZVA`Ѱ P"0;7ξfUĚ9V5~OM 0 ]gPIP-l@o)~N/y87!7;g %3RazҐٺCR x|yru1i$P]uaRaH9o%-RDz}1fQcNWC+1HAZnG9`H&X823ihd3FI+ee_h 7Ww{۳i.w$/tB@;E@;X u-(G($/OL,k(QGwjqd4AB?8sO*Aoޗ FV! w`o<$t=Aԃ,(򀮭4O@s>vI xDs/J[P]C@*E4<LޘR0(b" :-7D8$e/NY}֥ljST޻rSZNcI6{i903cTią ?ކ_'/R DvГs # D6N".L(Q^Y*yP@5laaN6gAᡗ΂9֎CVar̟.m4o_x1`܌Lv[v- ko/#!Ԙ/BLNVj Y볤VU.VAl Wz3oKe71ew^ Bl ]o+7Uȅ˄ u^>SĂ%na-G{1Ӊ|z) o-]O<}} t=jhx Rgmj*]_Q'Be l޲ + H.ˏ>>侵5܈ =•+6iǁ3puq,՝G]üNSʃ\]N&B  "h[Dۛ]KLOިfSvr祬k.=R[Y>Ebr\؇,Ez [DpOFzǏ " ,ŖzL:ڄ."|h5߭9FE;PCDlny P]vV l³(*|.7zDVگ׏Cggjk6D9}θDG3?݆9["0 v*!S߀W k" DÚAo3G}#,GP窧& L0x;A4pHvك/p'J|E'5hbГ#}mql@4Ė,>ZWꐄ -k*;ԇ' KK/X`t "]gbl.n8V*R)F ,hx:Oah@Ymٷ Oh>޻,zLZ =Mq8_ |Muj3d=C/–iG?Ud(oK˶J4QSZ| a"B4X{-ݵ*r 7i,,Ș\$EE܈"8Mhn.N'{Fb.k͖{W?BU%]WR֏vщU'SVM鍢# m7yk}9G*U5z^y? j)C։FbzZ\) 9>Bqz KPR D tDX յ'OވsG0LQbbb"3D5|]pDg<7'0$|qErxޮ){)^@d4J^“W.D@AyLDpxxcpzryq)Kjx_!Gx{?" \-`D]s GP{ .#E0" D v =D.S6#ggWe#%)ڸS#9r 6S¢wħIʊ o0c yg^CځB-%!I=~!jo^\FPX'hlK_W3>*Y4X6cODW] ccz _ &aJ!>!vw`I,=,xg%pv0*rBTT*RK(<\6! |cS)=hrcOD0(n_|  ]j#(x$:d*D3߃ҏ*NℌC(:fX/wSQSٳȈdC7-#`oDHtQ"'eߖq!g,'2hXǎ, 1֑CSGe#!%){)+U9@`=CD[Z.:hyg-1HE0˛ g}QvVCQo#(:nSd DP{vuHz+9鈴~|cbtP?@ll#Dcgbt_mLÉ%VFr.DCtaFw] 07]ȅ4edڸzh PCfϖ㷁jD‚"9X-9`kvNB{cn{C}j~(|M$߅%v!6"%Qu_J)% GLJ)02أD-O>fvCaDcdCů#48&B48K!,;sB}C稨td+i |kcann }}4b`ZZaƜLruGBZJKi"0ћC[Cucy9z n:C[9#DƊy*/|_batc/Wӓ76u؟"#:.G+h03G]"kE}bt 7XX-u h`OW4=000ҽxRD~ְ XȄn uapSyyrܼ NNΤEo)qG}60UvSeO};'Sevf _hP;xM=ŦjMB4 LNC{{`1kJU R6# G#A߄wZ!wlg 85r釘V'{U"VYx)C`B?f*b,FZq'KEB䂻ǒUjb%ۖ?SHb>%t&FU[VS&M\u:埛+ݍj4!z'=}^퍋s";ʝNTн/QjVFA;m 9*а{h 5pedeE@[陃# D74HIp[HHvf(GcBpj'"@1*‡"+B4~ypH?`fzRUva)KZau!ZUSAC oѬ>%D@ O_O=| sa;BB4XƵ_Cd 셶Y:[eYU%&@GpdJWW.\9&&զ<1bdOs!i4/B4bW_3~2%XG UM+Uf T AfBÌâղޤ ѰOonÍP^G/| Jo_'I = ՘Y٢FxZ*ElO|ans3|mqqs'aAr^A` - Ѱ1QL!*.}N/~/E|[Hۣ=>FՏT||qğ+ב0wpOmé}o &<ّn 1IGD@G}Vtso =}'AFIqѱ>E,@# S^E9BA NT|[l'ƆIwh68dGޙנ]{jG.,k<-$)9وFHB܎ w~vDDs&fM&0561F 1LӶ'%Q~! U4<%h3unX!6Əmw~ vHG^r]z%a@0 D.ZZ = Rhǐ!]au8 ᝯg`y.Ii(ڷI;uGE-p:Doc]Q9YATvtz 3oěO &GF1d" þIC:u>5LBXu/NM~Uv":!F#Oѻhӧmj Tz3?"Fp@C@2i 1h0U&QGvmIh^V-WAX!5;W<QN`u ^7pARae[xщ~#9:LDpM4"135 D+cW;,K?ƈh0/N.KI&b![E.aގ^>~88-4T 0QśRHX$,!Uͽw vd/)mYp{R^lwYt vnrqqC^i.^a N{P~w~dw]?yI=@,eN# DN#, VBщ^Qo+GC#LRοi K0n65r"Ƴzo@r"aXZ taT<0 l$k?:B39yvל8UQ [;8@,7ON4a'sD: ">%׶vL%2` £,FXo|}NP4OjK D:VJw .(\t:o}lc 'Dóc`]`Hel`pb`&ECU4ȉRZ,+]]'gVC.˞&4EcS)&8!>e"8,>A#Ŵ_ F4pDIKmڛH@E'"5݇'vs9-XP߃_E`Ln4.092BZ&Mq7Ә7h ohŒ~o^7 MH6 )M ז x4ܹ~Iκ5"45Nzt$;EE/"#|'XP!Gt~~7o}+`E/c?YB4hnHAep$o~e)4)C`pSE.RĄAk䨅Ld !=_YmJ9^vԖ_CC.ZOD \&&OoMY+u oFzGCo}O ]\Cz/D>XRb7|GݸhJ[I*Fɉ7(mwwfJ[__ArRÅ ѰpL[[%.^ v8Rx-r# vG Xsh4ȩ) "&de ʂuE4uE'!%I{kls[&fPh$b:{Lc)[&kDS#iaU 6n{Ed> gWۈ1,khX PW#rQљ܃X{?C,u[d,r.kܘp]earbTi7'NW8ohXxƦ)|h(U.~ށ$"%@@'i ih! m)3xdͅ}%!::}X /wNN0/!QH.V akDnhozcM)%T^Cq}{NfնCQ.RVNM }u*blj^<KDfMg;-wQo19:bN<.Z}Ef]UwLmLUIm DFni@*2(8d+i[}%D}Bx {$H 2*nފ}V a&&?l8l7BD.wT3R79CPXn2E/S>[!#tSE/s !u`ڡ yIOD XB4l75+Hk!fYy!ӌ^ggPq3T^I3KLR6o# 8BSHK@{#S22:4^poza/)[չaA1x}mf i!48($A0Kj:/#-DiX3Gc::POB Q<}QR#cm.x+ceÍ) S$3<9]E/ل]OC 5F!0o?W)>W߁dz%d%rr" D+ VCyPZuӳ KOOz()xYYG4ehU}w.c}pj87>5_ 1IYEadGFpi=\|){Vn~x(QYe>NZ i,B4#=յ*ҁ!x TDe)⁵<|OUWE``Gb#5gq14ޙ)YҎmq3a;-ӈ~qr)NpyaOI \CC)N+HdvHWdÊSfU+DF4U#׈Yv䱊ڣslG8W^\u/&Ȭ &0=v4LB*ځR,JZcY&8e)6,bk073 Zj*TOo!4NE9؏ fgq{*WvkvNZ.<[$;H"SBq99PV?sW0QRj᭖񐈘^>=&,] 0av{>%8ن醴A(&=|h0(h.1XP(up!1}>XlQ@IDAT4-W?ƒS;UÇ7CȑW΀*X!?A`G5?lvrjqQT"v#{tbbײA$vCR#D:[k\ Φ GcGB~1衅-l<$+ԯЕ 9MHj 8}]U[wvgfL%.IEiښ`{jEG^AV{p`btPŞ6lmR\#pY.HZ!$.ź`]!` D;4uV>i0 $9H9Ik_{p+UV L05iXD4:<ʄ]#ð#H5 l~NCt`l4G`8j# 渰Ch&ҡQ"׌cs,Vk3͑l/?(O\c>/ACm//8dZm[m [En~n&rҘڗ{H6KAc$-~8# d\\JJ^EHZ(ni0W4:ڏ*"VyK¢_dIYv1,E4bbl Qq7 XF[f֬h׶Zfh}Uue= o0r[WQt{ehyu7>+6= T`baqZ^Uo_& ,hGVy.+uƄ&f' }MNľ?&l/! DólmCŧ_RTcLֱ瑺eUMqB7{ ^DZkL ?%Z՜@ ?}{B4ݴ0"8= !LPȂ `_hh)?j)+!?9H@ ]dGɡ7HE.D'dl#0;;V u`􃗽7R8:'(!Ib!!Eȹr`G+P@{jEo~)G e+v Ѱ1zPٗhq Y!Ir8 ¡gpWꔛy^@ns`Bѝs{o0G{5B4lb::jpϡ4DH+БA@Y":Z IA5T7bf.nn:de!S$hdAYUu -nfAmvvq;E&稨PwXr T , xya}qG/kȅu.RDC(|k`Ac DyrdU_G+6GOscWoԸ̛K] k[}cxGN airhj"*>Bqz MP" D+ VCDh_'$ȅբۛ鼬.oǛ%u4oo@)"*>I0 c'RX0yE( ^ȳ^;*O~G~2 gDHbN]V(B4lm`f''Qs2}9XHsOS/i"j*/~K_ba 4*HA ?Ԧ;?@xw/>6RS5 D>/8&_ 80sNٶS-N!DN+u VDDC@;W_FKb_[m]zrr PYua}`LXJ׍ ]muL`BaX|D?4#F—7kBWoiTݽPkH;vXj(~#;G6Xiyz}JBڙVh*r8ʿ-=m׏ WI{Uo@:o;p㟑n!u, n*hG)88#n}ꡃML<{]f h_u1Z>!0bsB4X_KT,8{U,{F5O(@Db7ŲKO{3Eo!SSQeY7Ck˯z 7nXy針(~-# DÖm#A?5fTSyo"ABdD2&&[ۼmZXfKhm}hSJE!ZhMR!z:=r|C~Gdl&ш3`[ʎ|ޮe;"3]s*U{p.S▵+sS3XDԐ!\VEyTC:bju aF,;ϊ+B4 -w"p DlYқ(C߾Q"z!֏ hkx"X ?gfFQYxk!liyKiqVrJp][!Hv F)Sqm= _a}g}xĂ䭼/dp5702cJSp3#4B?ner7SF/yL؄'lFwm{nDC Pg;( `^KDf ,4ݸ CP `᪳,&z1Odv ; L "*0FWl^@&!Bs&NXZ6l(B4\]eWf+sBͣݸ{誩PgoMG)(!ƆUX:/4 R~:ѰlNaK~vL6K-`*6] ǔYX'XHQ aa z,.!0C\_]]igtb#.%bw~.gTf zg7 s붖#s@4<& ȰʡP^ uabC{%q&xy +\k717e bsw9Vm00ݷH> H Jl@aNEBDL愧#"1+̺ Akr՗p?1,[ |e;|FW]JI0ӯ@F5]D}/1!!q8}{=F -P=kiݼE){ spn9'Q{)肦AS!,"XFh0XVt(*:4h#>oYJ rbcrV12" x~onIC`v 8z<@i,0[?mhtɱE  i+ӤirtؔX&bPQC:  n;NÞon&736A05263ǴOo4%h&#i\G hІ,Kh>h __R)|Cq/Vv+/}^3o59SxXJ[ [_`p([pg ѰIt0#A%E7yZE{Y!lw3Xh¹fLL&NAL6, d,l@֔)%Ow-H, t!Y6[E_H'B f0N,ȡC b[8E`EœgLCl$DK7(1QaH(N탻uVVY @Da07.yDXV&GQ,EۙmM-D6g@ 6\ŭBc!8/u5?t>5XusgM!v泱{'pshHO(؉mB4JD\ÝGx;x߾!7JptVoo\hh,5qD.%R(bݭrax`p FoB)&9aI &wCoF|k- h(S1y5\EH Qs9{ks=ѩo육Kԇl| jj6>ubc(/ų7E;0qL&%`X&d,=5u8Xغ'r`Fʞŵ_X +oD.k>Эsj2X ZR/<(XѰ>Fr ` ~ߢSoi-^H>}"'ܺvsFɂBSZ&MhG"#[%v!&'ITʪHYfc \T%Կ!\ݬ+V:k(]7 /~?Z*-up_`f8Þ='ɮtAK#Lw7S@Lx+q#hpЁn?F>z{¾[yGac=/R08:4*QR&e텗*ѣdsM)9zChE,L,haNo8I&(TמD 8vT+rzWOCZX6-hki"k)Tz)3 nH۶܂"֠maeUH"8! *R+ۮ7p㧿P+| զn+ i5B#4^Nw1;=J9o M7h||HR7PEM7ڊ'`E҂N!׬R%&&G% q) k.iΑtkCE.WyN1&cFDY 059cƄb?8&\{9D?dO'0E -読@[2\؂|nfpY廰273)-r0z#`>!<;f4:v6J1ȅ9 68⡿ o;Fڱ|4'G;EW|o6~.ɢ_V(:("v.W]U}l̅ $-U&&xY͙HͶ ɫaꂨ,K$c!q `D7]Dϒ6Ltn6r_:CCe>i\N|v4o36AQRdn6xj<9ZkLgD R*RRIpm7 !LB[[%.^ <I|%#!llt_{yX7L c}o"1C& zc# -.#44N# ]*eȅG'+r!1^G4U:|C} P-Jtdžhba3u6+ ?x2y`6] #]kh"ׇzSUkwNQ[ؙ)' =&ruBܪ@Ӂfha2?dz›"-0.$yhwv1ՠ@ly]>_*F;6ur3߃Д;gJo^~嵷֭MX!ՏSQMhlD- .44"@H'Wo.0}RhF}UƧP^HXlL4 ƍHP U"ac ۀD൚`E 0N4j EUՠL ,0a --6Xvomk[X-雤(oWD@@f, I4MvZFZ!8xi&)?+b_\$Q_x0KXI[t6I0n]Ez VƦ"6v$nh=|{ZSp?wXb,ZQhH>::L6QTyCЊDF6m<G#R'G˗A,tW8RSDGB;ͪk.'G.!I㷓n-ffst W\WW`-߯+_(c NKEX`P3<5p 0C}0=3  ~p k8;M"Hl;Av (VY=ܢC ͤ ,{F}G0эV) rY| 4([]ۄ|4(@K~ϸ 1|ɭ% )v8ݍM8"]q۳R r2E3cVbݽ$S(ʹ1b[rYjvؒŸ 4N{~@%忙טC-:lȝjp:}L<\̖-O4DIKEE"V66Dw!îk^F)#Ħ#,L7"H 4 +tlnۓ 1\/kJDn(v5L;)~U¢J]Lf+B֐ f * P@<!Ea ME%*EL+^r `4m?[:U݀NZxUUIn JS36rbͯ'וY:  ualX휳1e] ;Ot4hklhMywPz3rXl50O" tCyM.60d߆o*Iwg~J3zaky3>7T,a\X24 +=q`T;9XT\!eZZXk4()AFR(R-M* JBX{|\TLzf)(:*m$# zyǿD}^*,v6ALJps?YIͷSFtVP8bbne`y=Sԝ}\0:4a -ʺz;mg'F\ 3"TKu K 0( @8y"桝ܼzAc4`5WrP[ZQYY谊@DxD-9a=߈*k7kO3Ǒ'm>QoS*AY~&8|sPH$S1x}\khP 4c!ZAu\&=Q1ːϽU C#nGF$6e[ Qu` L3.ThF~ھԌ- $bdd`|")191c'MhcCu1ytJЦ 3,R 2,QJ`B[MJcvP[*0[`Ck!%&i܎RJEe5>C..$xZC<$~C.ykEws7zZԅwS=]\\Z ww=(ĖXCWke3u% Ua&Fٱn^T 3Bj3r tAb|ESoJRUYkvv8?W|}!fnmZ(N?̏kc߃q|MM;߬.a gt:eA=x2РAkQ\h6gin &>s VkqOuLfh`&DwOk\ f|,7ե“RuT2\4R:v.U"o5#f;*d`1ȶp(AS6¥*QL.hP 4cG\yزIKAZA 64ק [I͙ !DpyBf=Fo h`m(+.,7 k7%M Qb @ΔvjT ힱM6.4  sؠn50203Pp,{2S$#{T8ՠ\84,!Itve_G'|\P Δ!F`{?2y1(.bk{O׉2A3CƠCR1,ll#۷C 2~,oAkгZ]HtLci!ο[0@kC){!]2Р 4c1ZwQIx9vbܒtz8'$y֮y+WJ6rktm M!w ZTJ!D@KJ"/jUCmmM`;*cWuD˕zLWv37 kt驕WZU>)<: n!T==LJ@ϾUTJ&3hJ#nNy r?)FsQ{5O SS!30tEak ^t$a#iB|]p6 ;OL~t֔ ^aÕєJ/pãK"mi;pϿP_/ILq>>HNGʲҵ5 x㫯n#7 **n|t{}6zRH!D@C9#$^ ]);d}zǟ-)IE#4Y=n+BSHl@^3jo堧IxM{X2&uzzZZRZFB9 eiO§gQL?ވ)Fn]ׄ QI&seD Bp0g  yӔTݨ kj"|&8;T0V919ejq <ݰEn_uE5\yM +w$=q ùÿEgUɬ=;hP3 #7Kt>o08E+w 4AԱVE'DF'Ge%%oR4pJZ(."R[=X)uUm4T| xM (TV rHܭ{R3xK40SV0\h&pag"w]#hώn!p[Dѳxs+eBa72BC²Q]^~kېqNdcx@Kν洊M@Gb م"-1h*kְEf@REժM4^A{T/f7ͲT89| .ZBfҖ x@\bPAyn4)NTG_aLR\.eAzhPbRmll'p%XZGI/H5qkUcޯTv1{-PGfJf4=YR6<^Ѱ׆(ݝ.-ydUPW%~.rM hAg]Z+hYM-vpbP!v L5ꖝJG޻'0ةU\ \X%N\"Wa,\}? mUj{6!t:"䇼!tʂm%[fTrJ_*E19ﰆLp̾K1ē'io CHt "6V 4h 9W@myj+VދmrEueA U3GoR#j"G@X(}Bqafu;FwYk`,P:DU)Bye@&}ou=ߋfbp_,RU5u7.t54pO .˰BSۧ0Ŵ#UffH8؝ 5gS9dR@iZ| oMB-"T`ɺ̛IAcN*c7.V:fCi!}K>6Y/{iXHw(n|*@)̱vr2AMAybi* Vb떧 flR]@zeA K;T_! [~hcB=s- xҥPjkL;OLKjovZGoxE@yXA1Z*JT :J]I7K^A^So ,4PZK<1 K)6un!OU :,'$aөuiqFhPNt7w'9~j J؂ FW2uF t#![ZS>+cU+I\R⨣C;sU}>I2:4Le^$r̢>AР6ܸt 2fe[ih@D,z;X}"׌, /#=1{DGڔKg[ ,Dy~UgpD: vп8δV*ՌlIƬ"dI7uCGM]s;<*w }[vWlk,2Z4g-lZ[d} gy#ӄ_;9>q]2Р֒+pTXMZ&G#0PSOB_eqvAӝl@IDATdRk,C{ڢ-0{J7L}(Teg2*YC%9ӥ^mYOl! ;f==k&X}c` ] Bb}E\Yq"89l8o-aWKP{J< !`njQ^Q}jޜt0F VM}oJg]d.!sq4.a~o,-ԊK35TEM$*.mhmO_FĽW(~vv5nZԣ䀆\ 9i`N37BxuT&000,dD;(o(J&ZMmIժOJn##TPa,.VMX&#n.m]]&g/Xt#Y霁֊I~{;>8~T`psa)(1 ý$_Tŗm*[b$ 31V 4h YVqT.NõOR`9xų45ۭ:W0n/ مj\&~ $Y|SgES,!0>M[Qq#C]{#+rJDfXjOG@ .|Dgf㶠A2Р*J\<=C(gQqSv"*+o7;a}]xLY4/W}1b^?m;9 of6|,g?I@WX()<4~[T\L-u1vY m]mlU/&|C"|vnl*V;>ϔ_@2FC< YXՠf2y#`C|SV%,A>!"ɠCKu1rD]Y_ Y q0H\ 0'XKkU9ϢfE7X 1nD+}3Р"\0{Ħ زIZKu ?WW7Z _f/kP59ڍyyS1db0Jj/BJp䍈޺vnzv{]5rO~*n2lx=L:XefIV`pmB9\p'I4D1o[:@Bcܦڛ?+kj V?+#|6 kӋ6et0+Z***2KŚ+|b7Pyr췄bA;׏BMVh)s*?sAi2أRlɭbg忸hqH_AEu1L>>.p tGԶ6݇b!omt4hR*5 5Bp p*MVHGϠUW\B$xR`\=.8fk0nbL7ɠnt\?F}fNr[[~ۿD3Db+7k-D@A\@:rM E૯n#'4],|H=/E$Gqu,@ooi1FȠ/v?mzhHm8wiŝhY h* IC-wU1uKF@3ofc79w Ӵai*8x 4X`'[W;MY o>5)n iBws7zZMuZӃ`l]`Gػ;PQUq5q'Р9ΣC#ͫAMN%+XTGy5w1׈@x@HփˤFE{i58"q'xxFޱ=`]-73GҁGnܷ[g?(IFMJ@Z*];_(OdA=V2Р\#`(-KȈPX^l=kk'SxV~G4YQ1[vm6NxP}v\7pN#h)c'C?W5rb(DSۮXfPI]YYY0l Z v`@&%ޣ/œ‚Dػ$&^v3KZ' hڙU С: xaSxr3݋Nm6_Wh_)Y\xǏG/m^bj¥^}G{ >XۦHK }fnRJrp?Pcs"  @:rME`xx.r# =1+7\L h` _C}e=37= u4&O!8c1f00@kh&fpsp y̜ S] uX*@kQŴ O' sYKA=k4!t. 桿oN-[~lƎ{=mlAWc/{AkܳlqVV#QkNYH 0#d3(͢4m#ye֋4 f< J2\?& OXapvc4 yD;,mgvic#ø{(zQ7+[l47 EQHT10pA}0$5ü 57r'԰CoN y`.;-xZ/4pzJWU PbpT\]E*DȚpHqSg꣱omA0/{#.N=95} 1:8ao5iiK?w8=-mXB-/pn^ø1Pm tRb}藛gֶu4!gT=x8xa`謫i߮~e{Hϸ6r[ĉRYJBKI˪ka|dtƳ綳`nrnFGQV$ANbO) #\baƲ6:TTۧpl<ý.I!|ƒ(@-xRV('Y')l+¬߽39[ϋOC"V4-0 xb򑿀mx`}Q7 c#x?@/w#%>"  @:rM$#00@toV!|p;"X ląiLdڥJOk3n>Fy8z{ C@R^>*32QA ] ʾA[1esL `Lh/AGy-Xq6Ps`)l՜f@CMYNGJ 6>܉^2:4~Ol+އzr:n|pC wkW'tL@]vd=7 *JB{Y D촭 >b_l1J\1Juhm#;N Os0diQ@ /clhh[H!2xčIh?,BTkedpAeUk[Gb>8I H%aSu2R*ʻ=+\D`6%Ks}v<-U8K=Z+`ec?k#P^p &Qq8zxcNj +{$ *O6Z^oj1f걽طYy}d>gd@ irbǰ@# ee W3AN?EWA[u+ڪZhu5[@3SghHv8y+''&&V [&%Р)i}Q+>T*'|w5'Fs 4y_f_}Ԥ)aD?qO7P/#`gX@x77%lBoAE:N}ɦ  W?zV un#-)#ipcr{sc58؇ B18aڇz^G@GHnߒm׳X6c E'nYP h(̹'ޢ~%CP(Qқ/)mڰxd}1b]|3cv@^C y-]ӎ\ATm(_ P!=TP'}P_%4} 5`h)G%x(6{W0=g^4K2jP8vjR0x唲m%S5̾9o MS6e.1{~f!D+T ]a4&9y F8S)8/xn[nƗ&QLl@gzzphnp. 1 73N" %$gK"{Qcb?1=Yݾ9=NTܰcc)m%ՂVrPO3` {/ NOנu}Ǔ;x3.}@kZKۂIf{N)oBK~V잝H|t['N"cb Fmib>O!0NHz%ٷ싸Wpwľ߅Bn-Xnp3}-hC.#`_AZ{@J`^|qyq41rؐJrMiz|CsO-fp?6SGCOB<Id"s9' 5 mŤN 嬯 TYK,>" ޶!WSE*hiND vV * Lro,hPvblt9_9JSx_I6GpJ| VN-efa_7$asOwwk? C^sul0Ͻ{t5)\|eac'gtVf:;{cZSf 9%8+nd+(ec^D$Bf 4 9;IpVee QH/L+:yAAyl8ܢ>9C_okClE=˗!@*V3;C˩=ʹ(EҿccVgPul]k) ~ ,J`c1JwP  ^´P݆ !88Qq weHrxfSgӿߜO,l{O0 Qƫ3N~>sp6Dsf!]ź7J{QrB D-,i)Uę$p`agb d*W<zG"5 ,u7e@@!/_{Fν84f 6zVZ֣x5 [kG[6|jͿ8w@򡇴wƆGw ? 3Yx>Lz͖ $4(^+qϝ%zO.atРx_ 2("4rp r"x̕ݚ)c(0C]$MXaa>/(Q#UR,Q|B7 ^h`-n!8#.r|foq qGb6|NN OMm!\N{hbeim)"+Ժ h0!;$"'SboBO*0>6G#fnla '?_j]ٺ23K tZ>[OW$=|SV,iC}tGKW/yͯ>:w2O`y?W8" ѧ]darYQwՔ1BV+@P?r1hhPF5 n+,Zfs=XMj'+Ţ_>ɱ9QaG˿xUD&(lxpJT 鯾ڜI.np JԎ@4ߋ+ᅫ|=w<=XX7Nhm‰wX҃1QD"Gt{Ɔ$4Mko@B)H#^g^Agg?BJ;+EaO~{4T^9. ;,7PzG>Snl•׏T4gbEf]5d'GckoYXEbGF(`@A@ [l]Dp\D2{:TvV4-MToLl' d`AgKIXW@fY<ґl\?VAQ GpJ1qr:] 5ufq*E4 JSX0uW.9I"rZܱ+[b;?Р]ZםNdA ,^[;Ml~82SXKhiV L``&ggb#c=wr>e&3|\~'ՓX%8%L,Aɡ^X_x4^?/NC߳I9qo q=RxfinBUբ_`>~j; ܦ\\x}<{'7}G#.)Vn3v~~~RjѵnUb<+Rb#!r#Kg2f!DM>F#HP_H:z Jͤ4 w~:A]賔ývQnley?@|VbY p7YaX: tPn'8{9o}&4eR_N$ˈ0$(lT4<@ }Cߖ aXbthv^~cA'Рl ߔs:E>VUm"EM> zG"$$^|ܗ o=Hh؇6Ne扐ڹPp VkfݱM4 q WAfS!ٻ |b;q?N 6,\W BW{<۱9Kl1;:|nع1XSh0֑-K{Iچ{%@_bW` 12K;;O{yd;yg߃~f|QxrߟOnv IhNyͫF-5U1f+$k)x `[g>y#`aj;Qx E:dLJ0#(Y̱QWP_e+VSܜ4z]edHUҲGzo,l)-ǥ?F_[ћw"lfIEhV̟EeCJp% _hN;IGp;(&޶ֿzgyoB5%,[r>_?헯&-[-*X+ 3}͘˫/B-kP .:.sLȻ9R4[֘(Ṭ' .ok!wRS3 o;h/mq Ħo< {)!OPz1MpCͭEjfѮ be@_t^%{ 4G O߻,S';M$5>H4$G˓/2acş@g^iFgϾ^/-O{24\+%zŏ~7ʥWif*. lXl褔 m%xZ! > ¿ -،/VC\HhN`B*4 |N-NAph٩5׽(ϪCz!n ZhQ-m cOhK hЌ Gf|t _^[ᛢ+T壿Dnv}-pϑIbQRa7vAvw#!Er3slDu2|bo[:ۈ k}KVwLf4}8Vڎ!s/V%yLKK?FGJfk C 6X;Ъ=-3/Vb?)1 `M8r&s) ,p?+9{̋D]"6G(Qqn"5>M U֜^Xz)_Ri8qM@2.h6f jW M{80J6%kJ`mG;XZp@gd#`:lBii&nؘ9a>?&.*q? vlG,J3/w162,~W߃jEg! 십?yQo|͆16DxO eu~O.ډbil(+iXh1a[Y7;8y>n"|cms*f.uO;\ k@KX+O^D<B>kx糁]`4+; 3AkQT۔0Pſ ,4h4 #=='FnPm[=x>{;`a)j1Bi|\*:>8&*:n}~u 5>=( j|N} 4EGq,mXQ&o_T{yp_O>|gcy%w.h0!#84*ˮjUst#sџT 4]㯉t'o?.aix=YH6?GluuV\ڙz eOZv*nN.x?MU`"ـvtkL 2sa#(Cyv6JD  vpT'6V" EHpejIdM<Ⓗ B\0<<D at]u/mzo>ek|C X+XUku'~SZߍ\)Z+R ). 4*_?i A[*:cA$O-h2Sk@C-č:進+_^Mܐ-[DTtX<  Tv)+*{ER5kPe3sKv)T4057KU~=}!8mڻ1#.D<3+E[e OߏU=BzGfQ$j"l  !XmL h 6r]8'f#YV úistD|2 .idžQ  @ *zhѻu0_N?b4) 4p?; t %/tM m*i]nn?~~ ¿EC LJe˗#kƊϿD]n-[78|ViXs{| LqEհv#^E)M-@"J&/EAy=f#ػADp5Aw?R(LBeb.dTdO;{s$mE:, 1LFVZ=Fh9!:P.]^h3f[qۃX+A3,w7u:ګ$ߣcmti$U N/<ggm^gT['Nu EOE+]4pX'7uY41|zp7t![^z"2>qFL"@DB`爺"QUQu^9yE$"$&te/P@Cy5\-M;!,_[BB 1/H'ǻdNw?Dic7꫰ş9E⒫j\gH "hD PHAȒm.ŜhtO[ 4;|K.}5}A"J04OBnXk6#:}FI-CdxrT#l5:0ػِJ?_O vS=Cx>FՄHGPHb=;~򖰷E?x/`ٯ}cN3++rxA)Ij‚چ׏!Px.ڝLРc)2(r59J ]y؛)Riv#%>C7/ F7d@T2\(#ڡ>Օ0*b./$nRdv`EW@ i_ i{l8uK7eI|p}XFFC z6h _BsBl}-1Oun\#xyKzh0ᗁ#8-5H:HZ MMhh(Ep% 00^.VVLڇ&11jWƒ|1V>VXtM'c#{y?L ޟ)!DoF.:ȝn_CKyQDF`;i Bp^;6]A.; @Ke*Qqb/LuXNv XZ4 WHnCh?m"###b8yŷBg`tqΰx v|gkf"ǜn5"|.!3`ý•&]!l설CQzjf~o^Agw }Ku4p /!׭wDG=}Ca>`VCK}lmCkOYߧmeaHhhq$ZFXw .Է򵶁c8-VXX DB7<6,|tߡH1 f2,7ӮVAaw$m3kKlgM@boDzqASw1`y~~lf#T A6~ 45NxuE-Ѡ;'Sun6X`Bؒգ\tһzVXdA7e(o;v}s=ĥIuyOڧ?n#KR田O`,l)]FΖ?A-4)?נ@m^g`Fg*:ݍ8 i@Խ}-B[RZ(xq'\čxgf4@۽WfΜ{w{p6]<=q&YZIKolI ^FgMlrj׺OIE6L rX:&IH"" ?T20΢X:ZM}.IQYV~*[3 CmZCʑC/pɃe9ڏOnn~LBg wwN;Z)w;fM0XhD&" ?72BfL Hh47_m`_49lIB( 0-ŀo~Jd/K5p890w>EX}gmjk, D$}a,WGb:+3l;g“E#rǞ[v茛N2ŝWS&lˎ4fb\Sz&kPהok8祪)_QY" = @C=q]]]FE*p`! eef{gu/qPA .hI u8]> ]ܾixS~4 (f._г-aFAGs}zƿ0侩Aʢw*D^rmQ+=@x 4K3#LܿiT-kbql_2Mj/FϏ6K.#?:+o[Llh)NWGԯPM򼬨icc`KQNTv$9Ȣ q44^rp"ؤ+aP<@Mׂf[^RNm&hhc>VR[CC]50iR?\t@Cg@ @BMMQ~{"@IDAT']~7#m441E+0`*ji, -2ɸ/zeK{ܒ9^S'I8/cavЇ hš [B~>f]uanzJ+{^E%@*zÐ"tN<+d^N6 Wneh(!`7F,zra,E8 oLinlF薳@[V-d\kܐVog"^EVhig48 QhV&-32w#" ⪒ +Hʼnux_Ӂ'-| z"(-r"]V ܙQxpOzhPn D uu 3:n`w L{Bݽ ׯ7(e%_w @ QGv#>rOMy:pTway{?EŰ⍑=J$x4%P[&RzYBE]~й:e`B ׮E*0 L` \p4٭fV_uU'JjXޔ&J}0x;F?9{[.faˇGIZYqM jHE77- h*FhKS3;t-`L(uR]D̾+඲RQw?#yd40_WOM HpfN>]%jD8 rꈋ4"fG(0PE,*D{ ptՇc8^6MĞByp<hN0a{Jxg fΊەP[Ys1RZ:J1{mRT)LUa<ΓGc(i(FI}Eb6D K,<䂶T[Hj)j4&wYQTIp@)aoyf."FAUnc/"u=&Styxk1D‘H FJu8xO u,h]d 4kKqQѫY2X_ "YZ.9 M6Lw @C'kge"SQD7nܷ2AL偁ۀC*pПH9B#ǵۆ$7G^x0g: 8 N79: 7I=i8e~}0|G4&` ַW-E%8RtD _/A&B/+7XQz"zJ>憎}Hҍ$hw#w܆hjg Xy( }rILnj chԔQʷY"c<fL}uQ@E(`ppoAsS#wL4xt 0 >wh؉( L&2G/u jZ !}Yl{uhx|;Clu(V1f@5^ SthhmPؙl\%2[gin < HaմNد"ord҉!T;:60ЁΊ3F΂vrbBc(%AȐSXȆ'ez`Dُ_;yrP]^@Oc]-Nz[/ >#UB!{~CVJ4{?T"f%Mph,T4W@j*ڱO_%")9? |  g!A~lc1p|xD WC{[{5H[7Drk3LZEN( !8bkD,E2U=T&Z5܂* *AU%3eMf$ك$KaaM Nz|@KoIA@E5(W>_T&w}<0tMGVdwqf&; zBTSJH9/Mfck(jp̯Ύ0Rڊ}UR3CU(2 C? =(L8Tidi2.,? UtF̈́ 2Xt7T xŲ& [K ґDB*Dd-UL.W!6NcC>!5740j:}jKws-6!߮`6iLpG# @ѕpV=ȹ(f=|J&כm'OdSVyO_n@EٸDAgMr+ xOq8IdoJ~X430 ,r!##QGQ\}d &_$or>Šぎ W)[63v Q&1``k-U}>EE^>Hr^g+g'3Gb *QUX焹٫G?uhq& #*sy "=Hj007$\d}"JZlً&@M^h['(=t tlIŷPUq q,];<\Oa뿃O$Tզ{5L Ib t1i1Paf R0rREW--|ў7mNKDFrazd\΁ f-OAü!5VnȻhP̘Wy_ >LIDC0*+nYolupy 5 y-{bX d`':ˌ Q fS^#>3kCpqYݮ>Ġn=x,\: .dDBNu $0A>=O}Lg pVZŭT٨(kv`l0pmw=#b6BVcLB {ME ¶C]I9ί؀0pln8*bƾO:y߷z_F '[G=0q{G ~f0Lǡ¦CNfss#NljkEŇdP*P^d%7ab=/Ԙ׉?FR?,4/v!ގp_h[(Va:6KdLV2JVlLq-𲶲í64 .(*ݧ/?zf ;JWp|%+;Z1~RQKv\Y;ehcw:U0!zf}Mv"ĝHErh6Z(HGO\'`/<|ME+EzZ+E%60>E)LEcїyw\a ee: űZ\Q ˴p;NrLsoG?:WIc+ja#]  u$;A.XO e:?l38PQϹ[z{@d5XSS\z׉셕~Jp0 Ft̏^'zȿN :uKs2yP4 f'R԰ Qhwʊ=ug̍j㭦܅dJѠ]"E0qn{4 Cy^m2%Wo#L>FܶEx)X?,ڇklxi|h* GqT5prC$ 3#p_Ӧ.xgptNBEE~9wCZEx- q a#dُVu".󉓎^,`Ǡ}##=K;Ɓ>f!bv$F;zdb3>8~pO9Bjj8n+L1o \}GAYEu ;>()?zs 1'47;F)<ّD3ShM4[$RhpRI1R1?3ޡe詥ȱpHR) 3xP:SO`gC,m| q6HDHe[#C?GҙDlo#"UR$3۴e:#|E"e)mH:xKfǵdIq57%#18W1"$r9ꡌÉ߾ǨKOYEn 4MG~r"ΐ짗AU] `w!<|Lz1>SͿn&KyvWii.'K1jbN"\  9g,\g|8ih\B߂LmjXѽDWa&Gf`z֌ٚ'hmB|κSޝ3] M_onlH/{xeq Wb{p\t583?a#nDCw q F5-kQ/ NJJ/鑹0љGK:*?IeȎ΀3=UR;an LQ_PQ(NʀTd8FIs2E}E%R^veʍ VlH[Lr|zV>t/=2qJ恵ASC=3~%qR~ V[Oӣ&-ae$˦%V?7}hNK;3Ո\QQQ509>q% u hG zݩ[2b^)<&O&OHd_Րo/rCu^̑F4]oVD*gx~,X< ցr| [o@K[8A6\V'U8PCE-"OIGy~5r =YAkS"BtTV"=<NbE`w !Or_+EMA R/yg$[큢TACQj'G PZ 3rb(BW(p\"X4:%Ơ8^à$ h]Р.Г"nhAqq.#:\`Y&>V1l#2vիgu 1PSU,GmU f dxF%eײv76*`EيXU[92qзҕ{.l<}@=JS¬FSg^҇$bϗP&Ecɫch[\X{@QfS0 ِR '0E*F0u4 4TW!eYP̼-j:`i~VOE]y 򣯢81 ƞNr#TՂ4֣4% 5-#Cwz'ֱ=+KdGF9 QpKZ9]^> v(vU C{WdHJˇq6$$AεD\(&o7ňZ&< SoPa=x@ f74h{,+yгo@\siNB4G5>qvX\!vgVx4E3Cnxur2UESԹG]s=xO򥔳&%^CMa)CBab+<(1WX]˨)) $l h`~61RdK)͎TeҋƼ•e! _ 4(~2Y[ i(OҜ6sL`b zRK Ƥ&n^<(aoi+NZK՞" !б6j[]\62ʝX'gHqF/ä*\T Gv|]]({0|v2R x@@<0k;k0DIj~4KuI Vxwغ, s(Y00wfq <NNԒ"pmt<+FR9!͸(LNgN>De&.Nk$O8ӗ2r<q"O'`$'!xxԴHddD tԃ7oUUwҫwX,&[D.='xO(|xvC&Sz-TU),;W`Vlj:CQ&w [,ninAYwj8̂ Cū% Ej2|v.KCO O8lNs?^°G)Ľ.;?G<zW8ID8`({lREJyDRkxLi̐"'}=*VQ j^ݔ趾01{q".N (thhhh@#jjgKYYi50ms#=5SRG,ϳm,cx;00fz H@CfL8n\/r {z/^_}1x</ж{ewlbF˼`[QP"3ȮsUbBW,x r #c/ܩc<`1FԤ ظ: '[[2W9izT.6eU TネȉKc+6pJOKoO ]KȆd _ag?nTo?NaRa .ewCgDWU`u͝{NڦDw%ǎ%Hx:AdIV-ӺU7;ft~BDD֬YgggXXX 00۶mCuu5Fn6l+ <~~~믿(ʠAAA{熎NU\^d$'.| fwV)|< 7U#a>R%hv/|&YxD{hbuC mp)'C $ 1V 5Yz/]_.v㕽!ϐ#{ 2CHk?B^QtńC1AjJ"a3oCUAPPt[l(NtmMuS[bg6#j)ji6g҉pOk3PYTbfs#{Jя"z4%E(bRpk4.`A}E_UP a\Sب˨Shb,}1Rq [W"AYE iCߟ'2.?|> >&Dۧ;^h谫ezq>3u3K-uߝ;"0ͪi ;y""}NO3Fݐt [`}m`ATԒҵx0H%5/-Ma;yJAݐ #G{v^v6u],€E6 ?*i5+&u`bHⳮR.E]u7jjBj*/&L( ȥ9gT&6>mѣG?|  xsqΆa$Kh],,,|7xFF QH @ti<<sM}CU*ׯ߭ETqoQ2v!x`&n޷쳢Yko[t $CPX}4p+~2zIfT@$ 6(. TxE_m, kS u=\`򌧏dˡȵ.K1g:c0Hl몇6'ܼMą"D*pxZbEzxeBXXV# px ptMu㭨wA'/CVy&kHƈw2%|̀EI={Jpփxt$UzF60:Ll8()ag~z7<ѳt'G8Gi((Hm6= _z=ݧgG'IgK1 KȺP{l֖0!::&L@{gϞEiimպzڭu͍8sf+E1fU41vSupkyy 3%g"/+ zV;<ֆ{|`GPKpEoiS$aWaj"@}X+ :2ω&&֘Ñ8nnG<4Kf ug< ?,يԒ[eM~-df"*<"Gv't45 Yӆ*;e w$D7 )8^~f2d\O.~ìϊEӗ9CxDHĈ>R1 Z<9!p oC^J1xIlI&'127ס c?z>r7sxX·ĵx 4(e[R ]L|'qR=Kq0`t LIMO|HᨫI2v\f=ljaY)I_=+ŠTwy%-ttD~Ly"^Ǣ+{2&&Wh233[+ؼys>cm" JXJ FO]L$b Ew[/ج?#¹|ʊ꯱qppˎ 2x $Jyx 5ljf?r"q5Gjq̘ (%p$ʲsr<.Af4.1<|`ḷ&oŌ'޻/ywSHKL cd=ۅcN[`&e>3?˗/[׿,pV*\YSNĉ;κ;0rp#I1vaGp|gURUEς-:d.0c DcwjJJa8fw:_Cr^g\KI@RkK\qcm}gG [hc麹Dĥ!Ea GZ=fGs4) \m"tmIį\^ RdD_Gj w^u@ChjJ>Ȑ$`$~e>ʮE}C2ع-/mO_S{sY_pJRJa~׮Ä$U:RToDDȎ/ ){ 0q2C?GHTgi >(ˡQʾsd uɌc=72p_dSp-&G6hv*h` I-]9DraI K3"sR"ϗ"S`)ŵݫ'-Kr,oًn&7X|'(,,yxG'ObٲeXx1,Y׳߮]vZԐn,+L.cx'go6t)"x@:`f'ڪ2ހ@ %hnhHt6S{Uޭ7:xr;q"[_MKO{wR%}@1q"Ϧrqf=&@mbfEtS:\5ͨ#Ϯ2{ToE+Ć#/GOL7xa2NQS; ;k #}MN. xsex{Y$D__yg#s2*=Q}(b`}8狮i%U% ^Am9BVFsC3MǼp7"B΅v#v!>u=plhf%V1]P]C~t1hӕ<&)ķ =O+CCBS&OZ KKOHqZEq|&w:־٩ 4[a@S(d?|Naffv~ǵ. !|nKę fo f,R1C[q\ͨ` 5fD)LN/VyCI]1cFE-䵒q0Y7AMC{ϒ{GTߌD>KH}Aۗl5Mn{tײR ji~S e,'j|v/HHBEҬ/)5Y(` 2$`~ 8w+ giHzq>p#/Y瑛%"<6Fye ߅m18k`Դ0Q>} ͪI)Ȇ}R \ޞ 4M e)IPWw5pð%CU} QV@32HO/}H%2)gx:y/DHܸтW '&1g{;h#=4Y^`]Q`h5'=(@CYn6})G-A;1NYbQ)b)IV!g>؈d kOS,b" ,%H˿1ynVNw$)VSW%@?+s0n8&hkALUyP?C.סKbfJ%ޜJXa7)^>i:-uNfbBIɹz\pYh`aG~%9гeI&|woQ@w,iKu77k8H9ѕ'=ЃHSӬێOC`i! ūe|_O  Օm^Y7)2@K̿}Kw-ڦ6 ~|HFf}} *QEQU}|]mUM}h}ڷ$ Ѧ,=R Ũь) s*:g4t_]C^O܀ u@IDATk Mc i[tN@í<>`(f:6:Z1ga9سޑͮ߈PpÒߖҹ^K8gPt(?lzV.I䇠W#R#Y@>fbM,}8;hQʣ@nB>74zs;0kt)xH0N԰_bM!%d$^!:;bGť=4-`@8qz+Ou⎊ԉTu0E0Q4hlbp^@;;{ fh|0p|>Vb'ZOY;z!h RS۸#G>.$Ea:(':)R;<d%ȶQV;?xLӋlXN. >~'jt=ΣKԄ $>y=?M+.4c6}T9J+K_d8?fZO*GWKZs ًͮ?(E5Hdu{6%g7 y5D|)zpsIVx|>|3&[[Ɔ*,i_/q;E`vanC2ѥͫC l 3hJgS\mfjd dL k~h& eDT֡ 3JLX kj`՗5!32 8ƚZx8]^P AU! ` #ޤ:Ѻ=g'TZ.G FBRc˨,+O3kgq]6ʋsVVnPWWl֮:aV^5qqIMPQt9n~Y9A@CdBEK#Y%bOе5Gi}u8<)?Ƽ0AնYGaj>1ǓmJ+giYO{'Uz:̔r9~ÏuEt\+撄̷JJ1g͗\3?lLkQR""x#h 4OWG ͳ.J@2\<;xauH/AUI-E$8֞&`*L{GۣٯV@PXՓgy,EiD0|(=ɌJfc w+8 돬tԖ e(kIaʳ 6u%tGrU--0, /*)9(夺FaO@W?~>H"+& `oNCϒOYSQՀEL_ ssk}' 4;QR.2H 8ivG)(MuQB>J*;?}FJCCCwԱqIr/9m*t+n܍_Eei98ל oS"1x%]:hC{&Dؙl\~W[3QDhF(@k2{?o]B))T2y = e ߟRzbioƟ?Ea4++PRHP1A 6)wUYu*0xAE8(FCu=I;oBNl&uX xb\g*qQ[T u}=L]=ǨFoA/C/_Ia%H83 GqI8rԺxnĄOzul'㫭b諏QtínseN|hC{8ot 7o >1ID6 c! ;`C ۑ7Yr^U8p '( I~~RBD)BSp I Y&*)CӠ{3"H3{ 9IZ@Caz2B~K[[ao ^F6t9<2'a;b`##Wp jD軧QQסהWьLS2Zkƾ[G1rM"0l),$rY0 '5ɇ"C”]_'~>m" HZIC~Q^p_F/}R=oػ`tu?Z'~O n1s1hY.(MoQZ#ꇉ=> MM}#AP{ !GjJ yBZB8j*nYܫw;g0,]8pkA@Cqv:B֬DscL07)RCd!߭Bd^K!hDJe)GꭸIzX1sIzm}{nBW#f\CC/1WbfJ3ů9 446\Ǫ/bFLOW ?.L=3-F| D ~c">[G O7K?Yzft/5퓜w3c'јE:ݎ.*pp!fbE{nFE EmKE0Iy3"$tt` ,ԉ~H:un2Eӯɦz]E c5rF΀Љ]~h{vv~T0bBNycu 4zU v p&D_<̹+FpaP+L4N؃+)Oq$>[֒b34 07PS͔*&1Ep;D]Tћ#j=EkY"d$wA%YJan~cÅY8{PU\ {}|M %O%]G$LN(O@iѬ̘&ط%Y^دgzcؽȒ)[a|^,ℓSzt-Haї+lL,#;1hXCnR1+(]!$2`k'B[)箐"E'4tR$d a3铴5Vg9rӧdiR,"Jo`cF3wݻ-l]aj刼̫{RhdnXL) Jd⁺J$\c[q.JHq]3KyOз3aM 4V*^:u-$+n5PD d`9g߈gx_GbgBY!m9d,rVmRXv._Kڗcku$ }`h"藮+m%y=J0!ԣE8N@{ DlQZֈ2fUȮ$Xxdnw 6euH\8Y}b2n#;0@<;L"x,W6ёymkC> z<={+hܶ~j*C$QWPNv tŸ1g񛞕%#Q_Ukٰ?{^EE{RH( B*D,ۻb TDEE!Z $Cz7L|1` m_ٙ;w7ힹ} {jʊQd?'DpR7 R{X?ѓOz-+[*)M 8۞cd6~L3iȗCң.Bn߀cI? Z)fF>N{!Nl7"'p@RN1/nAQZ!3rVqeޕb!jt Lۦ?O@Ca^5^]yw=m l'sEty $U;1m5 q p| Jj1h5W Nw]Ls:a6Եp9̲*3t1鹲:)`ҘPG ч/Qc+l:p>YY?zfp)a$5Y^361Mgե9bxEif\F1Gb4Uoa#J GMy O+f}-lD6YWMFn4'K[g/UHө`jEq~JKsPX41|hЇ4H2j{e&G#^J؊K(ky( uX Еeٍ4:(|1 ģp+4ě셞 %i2܉.;c]i&21+&JR}D*=$1e3g-9tVc?)NFxcWYY A{C^ɯ+=eHXˏLn(`{@\x#^8O?s=\ҏ,׌(I @\TrP2J+(N!?=̈ٱ~mL1u]:{nAJl*J)T*'qǠ)_>%m ?ގQZ 4J| Jab?d&> BS]>%dr8$x?=i"3Ab^o%0<8"wf}e< W ܟؖ24Y hjG//#)V}a /"L:6T >*s]\z$<1`Lƶ=B1c?dD lPՔ>Ş55HB.<;n1Q,u!xd؆)L-zsvz{gU DX@\ pD`t~uyFfJ qؐ}}S {Sz4E8E*kCmu)FHORƆZ^ޅ1j<*뽛ohH:w~Gb)q_H'߲@7@y!|R>(7_]-9``i(^Dضg7,BLȆ9$3<F9GKa0f3lQ@ s~a:]3|)>w*q" ̑L&pƎ4ɐ"kę _)#Lk(e,,*c*R0|=ćaL*b5ΙMq%," <0ȉfTvg>`sI`KYo5?4ǞQĀ .'26vLS8#'WCuQ1 `*y0Iumv9laq J <>-I¹;yh˅Pc_ZilCjIۑGI#Ͽ"H}v=@C=ʝ[$\_y30Tj,]ǟ"' c_}6Cz¿߁8Xq pŽ/#finh/OJhcՏ c(>9vaZ~~k?⋩s #8vmUGQ\PK4w` >0HLm4H췳 uM<GJưv00R(]3 +Yd|TEtdQ=W)n[Ҭgh!3 %Y(HC8JWK9[۞/eE/{ADzN)GϢnɖklsaQC$@hlPΑH1 ) I`3JDΞDFEdMDKKllJ4Z=&5y~oQ TYs1ZH1GE gJۤQYʍՇKh }/M#)y7@Cves;᝻qO:^%0WI[:w`kzt4Ѹ]G d8m0f^@MhS|p GЬ ~>̝W,(o9I!S&$@(tQ9Y{Y>Iעe4a4[b^H 3n*/@EYĕ8{Ø佥ݱkaȌ6 e5>tfINnߋ,Bel փ,P:HGAE[[P+)ŕ(SYcH?bԖrJ!c^36Cv|jI"/S`C?e L(E,5^ܜDd<u 56sD?)' XW`/LV8M)qa+Fjh‘Q&E?{gW^ VO Ԉ w_uu0f xMa/ -c7B/p2Goz3+4 T%8Gf!aPb!E`lq(oȷa[G a@Ùt< R˸I+pU ;<m( hx.#$0}[bظT)Ma0&K",oեpJIsmm .HCUq-2/!{?'h'DPc=t"klzK%# N$C .G(9 i^z fhi}sKd^"A *Rl`*ND:D$42D(X(+#I۪b"-Cc[hkJ4H˳qELK9b#Ն#0J ]cS!ٷ%.Аsl<'ޞ03"h`p6oO)IN|o#&B$aPf KS9n/Zt2p`by6鷵*6=;"<1XvH_f|f6|La -\^ xėeپ' @䎳w6>eVEiL[7S Qd+f\&`R2 D&i9a;ju dh` fp, $'TK }~Ԅ4fgjK ,] 70"wkd\B]u9Oop6U/TVr|6uVUU?յzjHL( I,Ml@Cw$% iR`\1e=q'ut2bۨBW@c=wH8s Mu4 J.8շE1<~\AB+4sfEk?줸s+} Ж]ҸgT)"dn}C it#h/9dyL``["РxTK[r;WzV`IĘ~rxIq3Ia&Ϧ:\9" UQbd'WFF~P%@kWC_ZȻH \gnNDecٟI02R8,-єQQB @+4:^KB5ae%X:ӗm&苂4U"( i1w@X_ѹ"p#ψ@ 7%^:yz/ DD:sd.0rG#k;eb47`R/L&OOV|E|0*)TYS־3=ZM9@œEhȑRhf@s؋QX_gT M=^b>f{, )DQ`K,q0uj0jq4 w40ڣJ&̾-#krKH4WW`B"<ғQ#3H'ڠ(7 %YТt''?!!h[pNO(,LGM].BGK&Ɏ j?@4z X_dZ;?8C}m1e2/WT;b—Ch,GخܷJy 0i<ԥ$[yrwP'4& i8F Mu6ualg*%x~?˓H+zH``d АU9#{P'G {)˭hb{@0Ei=Y*I3fu`.߹μȏNr!Y$Y#Qi{_ĐmK!m 1W`?z[{81K^fGECMSN=i^u~>B.Mcd#+[A6wrc٩1pp?U<Sd=t B7ƞOlIÒ"/rsXx3Tofc Ṇ"L Iի3 cx \H 72'G ?ivئG{ɏJ'^& W4 ik˺W!/!wL|!lhD|Y6S2ܪxyAP&TV5!-fT6[jL/l:"VtUM _kYOWUOHb;Z?<0{}UWmF4,X=cBR8ƯdoAzD fꓩy*T%bxdb[lN5-MsN"N[;kFg嶏y! O|R[8;QCMy ܀^DJTTrz 3g~GJjRc_ y: NQL>ؤj&'R.K޻a0, c;q g9q ~sgo\ N >|̞ ;XY:dfM+E0`ѧˡg&Ȋ8 ?!FB X'sp6qaV|$%涴}fg]UEּv'cZش~F(7qz(#yC<&pPK4 iA [[)~|s()M)K{I >YS݈O8c{Sx[,aS8aL>p>_J_9C 9#7,J}|+{IWSS>ǰg~BKӡΗػMк/QU)?+wN,#NI]Uf} x;ӓ{W  MM t_Q~zƎ5YSc/`WbBZBkڛG36|x䂕%;7z쁦z8ACcb 34| $%o;x+Ȑ|殖w2:!|2oʍȱd`VtJ2tu-C 'PZCs\|Pj(JpmB **4Djx$KC} jv]] _3P [Ѻ@{SƗo6%uA>b:l")sډq FAE@nM|#__'SL)?ڍK 'HLisܩ_NH :`×-Třc^|LLxi wcgDzçMJKaK,@tx>b/陔kK˯;yWƤ x`S/%Rrsb Fԅ|9WB㩰tQY]&aߧeF/1+j78_/#"!$ňjbIL"`YP}Gb?%ޭq=&|_a!s#N S {A( L,PI@COZ+cO#**PZQG7`v}PWv縘:/I Od&@3XZ[[`%ńH!xHV@\Ԉs\E[J_S~U֢,+8M]"M bNøm?q G?2lWCo= C ٪]tfSOgc킭0ւ'=mB!_:Xw=2nߘf.F45 Y<k `H FV@TǢ#ǠCۣl}uԏdMաOG}-=M`jYy-J*{Ξ/R'7. NWB|31IEz;p&VA Iu3mtiI_[b٭'qc۴1_PrN\XCv'VJ+󹮼>5Hɏ $:;o?E1jkjc׈I>6#96 n;U:Y8s[ mRWM4j?RyA7$΅8P􂮑mXeQBL>~8&,C +ɯCQ&LˇE9r$y}~car-yC9S0l1^Zm%u>K tܕHo.לGsU>Kԣ >hssUs 5_ 3;F%gsk]od!2*80Ёm4lgQf03֦&0Ti O :ag1M?KZT"fkZVQU𬿢#7;ĕg!3M}=?VSVD'=PUgٌuIY?M%SHxO [@ T `AUT''XFƨɳnF@s1:0!1_6zrҝ6XJhi!N'4)t(ca\)iM{C80Ol=oMDWM:FZxhlAkN" [#è$=R2Ά=|, J0#(7hbD Y8so(sؤ E4.XzQݰRвp;cYgdhZOdKơѣBM]%ZDv%ZfM׀ѽGE.F> f4N>J=>2O_;'_L_O?%b8~ rՕm/p{~7%.YL-Em># i\ &669}62tV7)9z9ޞ/VSR_J  e(GYIҶ]rWNؚkl/~'Qtj*L`Hm #4mc 52B\5GZ5`xќ(O{`3?s_$U$5IUo-'̎+DO' 07N~I8\ԥYEpť rL})cV$t3@ κlÕPZ9b} ^re$8:=E9ٌ"4rrm _ 4FCm5E1 ]4eՀHĺFǙD@ Ƭpr;=ޅC&zaa*+_m3r? ûޠෟ`8+&jIE>CsyBoФ}zQ'Xr L  TS!{3FYTtP` 1Q͔3:!QkBD:z*``Sr{`8;|h!'qx&!5<K> hjo~Y#Bq K~yN, 0⾱01w*_X1< sOt&G4dGEW&(=9А 6!*tqtE"4h;}ƹ0 9iqIe^;DN LeľH;Krh$ud7PȐ x^N%spo9:j*7(yыnVU!'xgKrH\UU8$&RzhK7j)_[`+Ie5V-xNF΅\l|&Y~ QGE~.*+:ܨj[e؞u;;8hS]#e@,6/zJ3pK n%>rW .,_ f)aتErIhAYJc:1bC#wn&pbޮqo8}-İl'R G+)4Sdέ\](\>kYax-`b= zE;'~b CBHn0S G_+k)voRx\9ܸl<8w#l7>?^ي|b+pnh?9ᱨCZX8GHk&kGQ .O6asrȺJ߽{{7A`D0wE[zs:G0P#=`21Wqw&> ͍8W 'G_ wC7QH1Gۢ1$T=O5J+=dBҁaAS>>놑G .L]p_|2ZhX| k7OǼ" '/z@u4s:fVLachյ]khaS3ڲ8~4J4f| #=Q;'ߦk蓁%۔RGEa^xmd}j'`}A[9);5mhb m6,[jaR!F4Hbj(,M\3yΖ(Z)'TQK!|w`V|lz 潷m; @IDAT}]>~m'9O45㯵m)<vbv7P"c/?퉗% ASS+.^.É6ȨRt$fAd,j&ϢDߏLtX;qj ĝJ#ecsw%v+ i(EzOjjO!:2{m%>x:2NGIJ^9y'*]j剴 OV"~]AWrو:q0#rHղ/LvexG<*zO`7Q#^S&oLH7% ~܂;(`K8fHOnEV>*-t#LnIc0 jBw!?hR/j_B6X%f]G{~ٍ\۪lBXD1B)%ED^.Ecklb\ clyJD;ιpMnvTdEo&c Βns]<|rTBPQ+Ÿ'"QRKi`3c{t/<{Q9 mjI@Cp5uMzb iPOči0s^WT9dv\ CS+Ny:='А{O8ik<  ׸C z@4_62ĨCf**qvz0v=h49s24URJ%),{J"I*gubyvr9W{``ZtuB=_:ژAKušwCHd"4g3pID,r:&Aڏ@A= z7QkkH5FUI-6>gǐ _/q p;" _.rfbx9 r>?^Ji8wl6NZk` {X|E)> 1:Gfih ׿ n”Op}P(ʣi^6. 7u,-N>db$>ij7UM HiP޳.Z?lH 9O%>%"Ł8G\ +#,^G}#ϖf#="V6ؓ?&#^0= ?~YiX`ai.;.:eq] ,%꿤f,33P ަ mx\j[ekΣ0 -چOB2٧Qv54q0Ƒs7TϷ@ʹ$L%U*UuDۚ`نص7dE=-˜gh 5?kFce b{U!e}u4u\on oTe2ffhM< h Y=:W,DA8_*Tz+N@}M2/G ydmS%t)cٴ:' .M͸HFHhhEɠc,{[z:c_⧘;Q~0٧^I}vkGǑ9!䘢[{dX0"ea/>6|M2WFT` .2= z ]g@ i&TFY^0O➥1\JFle#bқmԣtOЧ!E$= =KS.&AUSN$FIYH1!:R*V h);yR?-4\:{%%HL psh;u` ꠮Cd~rD'hnh@VE.Xx;~hӏB1` "? ,or1,-g޿`gysE/x~(s3v/Æw8a^F+&x5?hhHZ0ƛГ +lad 95{7iL{wD= 8{᳷`&,z.RPgm}M,[w77cߺ3| .>M~*0㥹pq^XuvQHL[e~\;IΝJƩS^AqRR{سhӕ<6 zyt˴c8 {V<:yB9\6z  F/pTa#wfr:xa>"~e(.>z7,ͮ|pާH )ď_tRl3OޞpX<|ԥ n(ý6\ET7k׍ٱF0IQm"U#qQF3{q5_7F!b_<뚰i\vsoHH7?ɤБ<Le~*x}g9y '{=&@CUQr&ҵONU]p]DYR1`n5a8g(MCdA\3tهwlc4Kʪ76a9${6_V]yכz]iё\Gr: #z/5]aI T>Yiޔ+8 l= ^}DQ8$!:c `lћ!&ɤR ``X5M 98.wgV̈́K哂d?W7EOϣ*TU/ē+;LEEUU3qtTV5W#}O?E&w (/Ga^5-5Kqa- @EjMU6AL{/=詁{EOzl}{ưߚ&"6M/cϕn SX,5euxogܻv<1+"f8O["pٳ>D%I}` Ү}%W(EUB4٭6L|ҹkbw} 1#." 5?F,<`l,\iJ}s^:E "qN;92 u N2hj`J 2{bǐ~{ G=CZ[-톥Hh%a`Yr#40Ra +~?6e+Y7e7s(QC! 1&Is*OkX8Rۙb3H=4hvwL|#L| :6΢"{kJc$ )\|CM5¸wd"TQ0 W!BWHN [lmw$X[wMb=z!Xnl_gPz7+]zQK"RWHi証YX~wT &fB%k3L 5>sܥbx`/*';_-߁G]B#F/Ҝ'; G}v9Ðt6Igo0qwqy|$`$ݎ"d8 ]3S  :&). cg^Z RpR&X뉩R ǎSXf5M x 9AM}!ٜhֽ?FJHJĊUv2yҦ=6?^~ӓˑp@ZiIRvSiP`h >s#=u9k=f`D%u*AUQ-I*'7C)ߘưWȞU3Ax fjoǾg?͍-XwϯD X o{,aҖ(_/^ڲXa;|cXTz~SWC]6y׀˪ٰElhbN1}LMO@Tae$AON0zPn?ʜ:! _M)}nŮ! ;ad.)uyIq8}38 ό)зT,΀ g܂Pm$X M8q fǺ@a׵p f[%=*_;7e8/%wPBH,~gޏj^U58Z@W-}6g_'6)\d^UQW[8>7WREGxPSST*98# 7|;LdAEF%jhނbhJ(v˭Dwi 憪iiaz"Z!a\Πs0` >eټ?kO;Z=f&:/]3~S?cՖ#`-8M@}D|Y6x/,cϞ;[}xfKOB!) wVXt-8`CFD יͷ󾞐A280, yI|ɥ5Q5$|`LU,,@54 kw\:} CT|,pGA |g($ )k^0U8L`KC5vÔ0YSBWNs5~-NC=PǂG&2\b$rOCJ:pR,@C8}p %aP)xCl(FȶPI(+1e "q1hS͹NݨiAg"oP_V;hVxdnte*Ab&3`Szڄ\3^Đ N0\ۥeUgaR!pB8ǟ?F~|ká!'baϩ)ė`*p`]n=n eCޕb&2%YtI5ɀ]:0mix ĢHM*ųKaX#t𵒻 h!`4H7FpZ*Bֽ$ Ps )[R9{ܧw=7h{?YMf9aܕ7uդRq'(:--SH%'M|Gޝ) [R|-͍p<#]q ,*QD$Afa]ݝh(NׁEmX 7&C${47&l}3ڛ&z}NV MmϟrϓTz@!<&(Trȅ{?@Ȭ$oW)Md`0{Pܒb$RyVr &ܤ| m-0e2D% mgY &%dI2%NU ${?=>k%q,|Qj/6T۬:Xʄš04l g# R6QstMM7\O,c̋BUK):mʟ~i_8(fF$% ,tX,z}%1|xKRZ9fqź$`Ľ ܌<@C`T#&/_m tpIUҦfh7s4R!#l]lJĝ8 ?j* k<PP]_|t{beѠ1GPSRMDHI>y-ͭ|eVXDtK'PV ?2_ hŗqjlq>/"\&bO"xO ޱs$i-I 5 RgR hȌU3YuAPĝԈX8 ھX<  }_Q0'e!cF=XHTlAgkhka?dmÁQkjhcKjЀ;W3TR48!TLϓ8}\sTm w+t Ra3TX613D=Cn}eJN;yi[jBN\OKEA#L9v2,hnjk+2.vgr ko}Fq1v`5APB ]8Pb2sjNvx.DqŒ~5|Uġ4Mdxl~(LK9E$##:Um(z=:,kĖ|t6#3Ն1}_bbgƱLJ97 2ߎ/nK3S`d# 8k`SْSr 8 I` KPb7+SQVc$=~*`5YQ(h`yp\v͵H> q$BF7J]ڳO{t"+3!+%-MPQqt֧zP{7уUYwP[}HSGiv`n!r`~( l-1Ǡcf$K:Rյ34&N|п*vAgP+%j1&IA#|tU|E60d0KBHF1fk{Xr~نԖU56T_fk𸕏X3g0G:0i۔}Eccٻ*5z; %! iҬ>ӧ(^P;[!Bz=Yn@DHٽٽmSgCk]%zN!1 (߳yvSsq`(nsM' nj*ZCcx_:E~nI39/]a5`a" 4~5ChhH;y3b"qjBo-iz\4.lYMRN1'b>d7`(H28ꂀwR墚re ѠAu)T$L`+оQAʩ-dxHc.CQC m5xwegME\?+8ލԇgtko?78ws H{,+*c:3y)9u}NgJ b"I}~#^A;?Bjշ ;ӢܔGB/ 4r2Kq j!w~+G[Ctu~`GYbK~(ۓ22"b53C.H8{ QN6@׎@Cv=gFKpˀh4CU j&\؅C'ܻmBO-iMu5nًdǞ|ԵЖlKk[6_^XpBAuPA$3-HC~Z.%rF+mˇK nZE=pv6NEf֔κIGA\ k(-"FU/&/$͠\`7TQʴrz9IDi T]z8k7h6sxkRg=eeԉOR͑wmsO'h @]ES MT MJM9ɑ0ԒbNTF[ IqY]] ֪^la2LKf;^Zn3t;R,\{ދ͖Xl+'^+>+ 3O'͈\D3͝O'V"ܹtANPPeƵݗ1 ,] >7 Pw378An|2Ϣ̩!w({1xq0kiKgF2!Z v*Y5v5ezBsMA  0>|Mo b1D@h@Q1zcܼvkw"lF 0[s% =[kýW[.tl1F~Q;/c[cC}. Ue$%Z< ]yŧ I+MrrEc8kr'\ARg[q_;3k{@By٩H g-ClRVX"5:9}^SG"#:lepuc__4'1 TId{ ˘tGX.*\A@0V2h:nڳ>.ߝ'Փuwg(Ƞ%ӌOh:>mり ^R]%B; W[iDnt3F~R,y+2~|;m+R"~( #sz?|/~ 8{@XN &"{=  XVFVm۱=&ID D}M޽_oN5Mѳ!MEao!!ay[yR؅T$v=W>4\- ^S:CPh*C_u:~/ 9EOIvAE&78p0w4f;CX!;"tʙ$%Fuc'˻n!x1xnDW^0s0܈}=N{ii©/bC2hh׊ rZyf@D]mݫ癝Z9{::ua퓧}7ڮ[Xuh3=c_h8WK$gprC*2=—EY$(CFlpK^w f, EJ3PaѼMS`]w*:M]scǡhKNAV=8>ܡ쁆x@-Rc^ =38tm`U=+)cQN8z̚Fb+s p~FDa l-DeX}"l>׽7$qChpp ^O=pT"~E2[Z=&vĠy~eR(Ź8U\ʠŌc;w )d}sO;Kty‚BQ.䜥j%ӽ)*PKzcۇq5t~@GY}$|}$ Axwa= hJ }koC ZD]9MxT "go׻{B)/J㪘ɆVF3kwI ~ w@tWw^-Q_}_!aZjGJb[;ٞzoi^ ¬U~޶"(Sz7{&u$wxX@_WD~*BNg7ol9U){O+~, {2)PUQ6"_Ƞi~^߫% 2(HĈhs6ՕՊEgV.Q6Ju=fĂ)Au W ˇHdq .|2w2tu1h0q: A=f^3mooR/blVYbOd4as,$(tA4+ttQSYáWhVY. 3A ;UIȌz.keEajxv7Bn!7 ,e%M]IQ\<@V|E\H/ -#$̡7BVm'I9^gI򃴖N6֊hWI K߉équsm'QguYߏӿG3h7f )[k9ws hA(OUlzؠ w~l2TWTĺʊ|z55@"M (/A ]u?z3z)Ta[O 4&v-$GfwjYfәR'O}EzKm}m{fO1MgQYRFB(3I4={1 -ѱpkJwr[:QO%ݽjvEzj2c@~2dxV t2ЯU)A1eJۣ%1-Q>$e3T%lBMc%+[QյDZwS,Xudvx2۰v~2a&m0?0 IW7ܖЪepo$L$Ern3alNӥhibҾl4Jx?ZzZJZDuE%ݳ ,a}bHKeES*RK6=U+bRK{X( z)5)U[G5ܗ[CW0^$Y9N?*M}Nx+b.ΜS1Zۺ dՎAv'.Zvra=@s@;{f wa"lzVNڄ+ zy|kɈ!g*ۍ &1S;S姰(";r :ŭt8l쎖kf-􁽳 (*< [7.om6eMI6-Q^PCT\ka(/%+c!bDL8<օjIS-7h~%y9l!W= {@hсz_f6mahZl]^vrEa=s8/ 2^0ۗw5s!fPYV#?]kqm?zOa7B+p9_gL:;bDǩ0 z5ad*|4 Eʽakb5cTN)R"d!~@o_6hBNbl;ϻDglwo.wx?u>j.ALo@C=g`jq9L&{@H=Ц#,I.mgvem 2 s8;>/B[2ITWC#Nj68NԵ4fi1AI^V/܇ݟJo3tW.H$a K>/~6J Y#ib,LE.Q0~{1܊b99Hku53|UT^('f`dQ塞hx|khȗzL&{@{pTQR U ב\k;s2^6n+ދ(S6 ![EaV I>sM5E (!zzbpBm4`jkbY@IDAT"i(򋠢R);`9'@Bx$@CYBww)3DUi9c=ߓYۼ2ϓSWÅN@#xzPVT@8Mz6= B = 2ISG}qbh(+jP{2?ر~_:c՟%+)؇gdWwnZ^3d=@'A(?_FA =!n,ZB|t6(_>B9 ݌mTD+5V'Y,ewCxV}?,*!Ϫ,`ݖ.QԵN(P`4ˌes}ǐzzJߐ!KeVC=&W= n(h4Po<0R uww&i&j="NqqΘkXsȫj3c6lT7D@Edf@މkn* }_g7AЃuu*K/}?Lqy[hRdmm- Yf! \m](ҥ@C=&W= n(!zNؑ9BVWIj+q=1П[4b+t'0|:t q50өlXZdp,\PC~߀*s9Zz.vӋX22 q2;`]sHB)P;<ek =6mBOh%e/=oh8YmCFB'ܵhhV|W@Oij]c֙ 4SXS ۾3Cf^i}79/''sW[fI%\bz<";j ü ދ6r;&&~>$$eCصۧjB"P#+ae[ o k;}6[?<5Ⱥχ;Ll Ahr9->}ho"p;*KC<k"?Jؐ`4pi[V;kdS4ș'4{tu>ahnڱym%L%j4BؕT,{9n~ #]L%%:?݁ !+!1-|٫[>V("&ɶ C6M{y,Se8ʸcoò }@~{>{@gN譓wY;NVD򁹃c / ,z9")CMa$Jh(~40瞓om>?ҟ = f(OćV\pl_vaP3G%sIEgy)^^5ZO,QM+Մl̅2h\rQ_=`aQ&0AثɿV_1儷hk)_APj+*%Niz]f]0zIKZ? "g532Z`QH5dI F6YO_jt2zQ0rSP[SMvde+IxMGGDR926F<aGp.H%(1{k!TCP<7IŰ4+-nz \$f!3>YȌOgjRF; tB./Ʃ_27s0f6|7 vHM.ak/M^ޖti0B/яIEjyw8?[whh*+W_@YA)ߍB޿z )"Q} a3( Ud4r*o 2`m!dgrkn-lnJN }gJEmtxkd,-EFL,,x[HeCd'1Z$6PBS+R!.Zfa ˔a`ƅ1VC(ob {`tx3aDƧ =(l L\sI^ :)I:۶kÀ칆673O>9$>`(|}F8Pxu$&eW$49[ ñPr\cB@ETFCqv|~>`):#z@ Isgelwĵ:ƜFBaf:ˌB5.5;L!/x_GZ*Ǿ5 @}$&"oW9u_x\9)wn `raT,]<`b@VXgcdʼnR8?v*3mQ ҇Q`&pJݧ}7DD.*PQ\9C!.er&mcF0$&|ȏN~R2u;)*BxvPC#Bn@@z8j %pH1_la'Z3EMH55u0vR899q $b?bBX'>; ""_8wh#0U2Ӓx e@CjDrɏW*/{@T3cxt2/PQ9ZŌٹ˪6yjvcpuo$iЂ*2Pi ʊc챜#\ 0k:j%DǥgٍgS *6P`(S<׳}_Pm;~V]QdEQ.PUV\v<|uaHXY74TҎ*O\1^Ҵo1:-/E~<߯oaYQNDBX>#!(U翑bFJztiCD`pb*д*G\Ak0j1^8/HLXr_|%-7~izꅸvk$3a֪WxӿQh%@ź'vc5 ]ށUvra4 1o1*QZR]q2&ac ǹs[q7&Ba8 ;K.tC\6.^3kV#;=ǂA.<@߬d{yY8t =fMkw妲KN(/MuiTUYÙ='ux4zׇe{c$qJfTݗI-NԯڍAK)t&.m9v \ G$c=T wA+XX1e3'N=cr nخpB$?P+>Qט F,na緢AעG/u~CbD#m02Ĩ ~_>x1|GOoF*f}@\ھq/PJsF-@:RV<;9P#B}wi|G䌻?&B][V@ѸJTuڷh  MQMrқ"TōT/;M5yTd[  e Sju}7Xnۋ&e%@RYT_~_,|7~!4ccOuXdxʲ0t[C&䁠\Ya"ž㛑ǂpiY>Wq}{VČڳ4c7Bٚ䁑@KOӼLʝByRxI !V3TQ- Wf_y\BhhiE_#G,_7RF6}sӱϸsmN@޷jqs>%Ga 5Sq(vՊPu9sV0 < Oǿ{x$s$xDw5)D¶Y1}\ Tjgxct4z:_7#-:M_qT&4?kDDz LI"21,' 5XHaZ:c/Spmjӂ) B U,؛u3vyݕG3O(xgZy6PS[aqzV|׶2k?b6F#J:(+,߿AȾ$Yͯ7Q#}2XsnYPUWxB.ey@m}^ϑkaRh ߎG!B#"YQZ^' I!5SeO!asQX=Gl}k=1C<1ܿ(Ze.m>sa/2 ƅMg/ ;7]abBUe8t.KL}iQ̤KWqeV1,]^=؉BqȈPN2'llRT^t53s."-V56-AZ~: !5 ֱ )-ħKO󏣨?X[3>9Nocg/ fDXPaUb7"`QlVLXVWAst2ĩ,ZYX/t'H/pɐ of]w){ߋ])QMf)2Pjaojixc_Ns(;>%[C/ >oPg{y0BFVlL3&'8U(ӡ${N'Q[+fGGGP7"eL-0u IR1E-`Զ x e_೸cwW9gIզ<_-6V')ۓ IΥR4d{шb?٩7پ,tGHF<W/a\t X:s"]Oo-q9,{/{v@z<+XO>^;гBg#0raNr%b|[_ta}/@MirG{  Z )DVJńlђNKO>QYVJ2T*haDָ5ai"# u;k׮Əe~-2O3[@[MM]ɣxU 1E%"8j%]?4 ;p h-~~rb쎑-3ȷ8Y9tQ3נqAw;n^s7TrxcG[6PHZo~@ɛ QB}A-ZIF)[^XDtՅ/tFPP}8OE0j0gHKcWx#C -t3rc;   :Z@ 1 blfIo@]CffW(/i#)pڝ 'O4=~Mv 9&: rqh Dv)[}քGĨT _o.'tvuܹ ӟi&;]rl`qFv\ gN  {IH(i>Czoϳt~4TwV*-,#||T)roxSP͇ accb*^з#,=lmi9p erN"\hZ_CK,.eWii#[9r MQU]=8|~+gHhNpPٺ:jHm3 [;`\Lf:N qoPNE/vxuvĞ(ӚՆVZӈR[ !?,Ymk44G LKMo6!eGU*ݥ&-<;g^dRZ'm #vLF?n s{cjtїqfu־/(0&r?" ;Hbsk`Dv=umM1,Xwރ}f>$']#7%MOYP`l㡥ro𘲆V8@ @˶57Ҙ]U b(/ t_T OB=ZM<"O0wv?\hVml@I9h`kdW\SmݲmO. y AEjzLمLՅzΓ'!+{@`( 4sK]i*'ryf4PX TJ{ %0ߙjRb!N] ®?%2:(-/ƁqTT2xx}{ 0;vLҹUг0]9Cyެ8^# cqzY| NnLkpfD:U$hS+CEa1N W~BqRPAHM" 'Ix@}seM8K;ژ5zzCG@wb;$!-\>nDžhtt8vSP;11,JkDlys-c e/Vse`47.m:B~Z^z@Nɻ%RxO¬*5B BRgtc)*ʑvW?GP o-=ZӧHh3?.]0`ފ~Fv^uU hqvF~әREEiN;EfoZm+/>P! abGTO EfDCT@>8G 8sJ.qŅD!N?Ū1O1Q"T$#-/Є4{FdAVѨN%Ш+?@zNOsgkXGqϿFHJ*Qyadi'd'4mho$(5IV$'rׇSj}J?x0٘ jl$)/i*[O }*zMb@$<}$^9#wbH67x{ t]q‡~H*kDfSqNKXU B h khLwIC0ń"wE4&ф[O.k9D)7oز7C4fŚ X>da|h(/ԉ !]0:ܩ@KP[[KGtz KD%,/QF\[ۢ-o1̾{aWpefʇa4⩩9+pw6>lm><È6 x ]eR5t)kIĤV4ȅ&9rQ?]m:ƽ~C:,g yt4Ħ_H';1~xyX=ZUTbqyzg@¸} c(a'"ݽ@9zz0p XV2rlZD6_~yA\ĝ*%zgH4#/%G2U# l,If:с/AuBH1H:+;PN4 LMmСCV3%1 #VYzyxw~9v\;ĞOCй=\%`C5&GmC |MB[6FeI);dߓ^@SVx ̐挨8?ѨV,9X nB4~mgr?V_7&!Dvg«?'xthg4f ߁gZQQMk/_^!`-ͰdTօ!B}4:iOn}kԕS7RNE (=O T"x[Z681!Z=qeeVU<~)sc44P(R]&ܸJ2CPAzr Ff8T@׉ɱg5l!tt QYUCgi@u(o߇>ō.E߾Ѿ+wS}1 i~UglA//> qhyJ4t[τA=SG_V_&bkOϗ$s_QEnJVzL ;/{lXzYRB3ƜFaAC[ Ex}>&^j%V_JefKN觑KI΁E+vӧU}5^n,țnK``gI2hU^ߞftkr:w " >mVRPuK# Ogq{pq"*ϓ}{mYV?kMFLy4d*/,R S7_D}7582Y [n3&+y4yb"tv!c<@ѳD뛑Zܼ|!iMOQdh˃_tPb`n c"@"vȊ$SܠæƯ/ȍ;)IHOlS4|cmƲϊiZxN! ))79B뀷vLk~tWM AH5Fj$sʹ͜M[;8v.jfwMu l=r6]ډD%y@. қO*+gL12B3?jCkiby;vIC7`*r΃XPVR؀ʯJ8{ŨĂa'W"qXoZX!p{hߏg!ߟ v4I>/Ff8hm俿GLXSޏ_d9J4zib=|=*?{ae훙,bۢWU&Žp}>E;(,e3 Z;eÔ] 6PAFr|(e &FhXvk1(oïBK[]2ӎ ;Ү =D{j`DrDjj2lmJ&h`QDo;o~IoaXb3Ϳ{lvCʩ:+A>Zn wd~mL=`$dv ?W]F-vbYNh jU]|ԯRdt͍وTZɣsÕq! 4Pؕ7̆llD !.XCث2 K#bLilLVu]JhO&mN5g…Ee vn@th).%OHǚZt8,)5|,C]CaJGnqzDY0#|ݘ] !!v˸bP.BcЪuk2z :h^}nl=L( $fRRe>I\cH9z;u[;wqxf4$)ȏžtXu3y@׌$?`4{gW6 j~vXI\IM*amv,i9"l GW!:4th/̄҆ -ƛF /.nP1a[ @]ծ]ohhh12PAJm\gqh7d'7о)jzO^SB]Ky8ٶRgD97HXH1t۠*jR8bh|h = !3>1o64i{alGEA(/VMI +C@ eX:C;E(2,1E‰ zW54L\Q3ueg#P 屲S?npvgF=&q J'ɗn`ݘA,ں`ЧiJݸw޾ߘi#+1m;sNIpnص!sGBlTaxsXמ}C@akwv)k?@jRrcuLo8iUY%%BUc6 ?2v~vϜa~9vsUov"ѫNPQW~<`́t(MȻdlڷ( k4M'撖ГE 9FnJ~Ev]ae0$~tr!jT{X#y)8g:.𕚆M!/h{5]M zs r0thf݁>hlS!zxe8~/XL_o,ʲCIDu{+fȋ965a(p`*C}"jz}{]snw0@* Rh(/$ J ab/ 􂦦8é!1؝91powMKWi։o}"-1?@MC/Y*{ ~6/ "jx{y_,eL|Nމkoշ a1̀5e AH!٩%Y(HNgJ\NY.) O#fL 1~\H~WEuk{wo\8w#"f(2 áٰvvՓLtlv`JyqN+\{@&HՈ|kD<ӰҎmzF|? 1 t'*K+eT7eZ|c>5sDI^ .@np_RoK]ogl~⓸$at]ZJ"V\ mC !+!1o3TPSYd $ a@A-Wmͩ[WvBIN.#hI=@+6mjˀUc£J!AlCE6v]FRw׮>X9v9=hfmkn]AUe-kD̮[18NR 6LۈaMd(ʀЂFóE -;&@Ftb;թ>m⫡A239d G70'~] qG|`gx#\ڱqEdK,&l A^ZumY:9֫ʀAFD!9" YQV[+4QVV /z VGt%D^;vnbΉMyھڸ"8h ғcqeFkhfoPŢ惦 ٷ\ХWcۋA. [r9>ZRW[gxQW y/Hè&Ƭwrb;#`M6]Cmp\ݟ5wq(s(}2uqJ\f}*sQ8/iH~`~֦sG.v"Ͼd6$nxTFee!cƔ)콢L\u;z.0C򏊬U&8% i R*4Eǂ%axŅ!) 4ܽ޶f}=|Q4-ɲάTp5K@Ax6CMU+k ULl8-DW H}-aN܇Hgהn|Sadhky{~J4ơOI_@oMv֐1Mincd~@NbN~-Y@!Y5tc#t3W7mCpF٢3fŝ`m ,}7~{Nbӽ}~0WY#t1O\Q%$2#U]#U)ttoaH Q>riYY33m4;|8 i v#_mpݵcm)YKȍ2+&gOG16Pƛۧ+ع>.ï-Bޝ5cEE i uy%'i45`lkB²rY(@Cuy;c}Х) ('Tp⮻*skGAjM3M,l$hxzsϵ3mYNlmߡX4Ɖ<*rk[O/za2c"U,;̯v<!;gR(b~2S?e ͑!>d$8&0޶!}Z]ݠgCz&07"Xl bB d"z Z 1#I^@ĭwOwfU%YC5Ni6;_<}{%]u#K}x|.86 |9CZ:?v<2OTp $EЅjB|K$=.?3+.;TVV_ޗ_ph@$07qBvAʽ(#܋ST# 2i!AHjO`CDW@IDATrq'Ȧf)UF3t:U˸ןD4;2sdfSҍ{mn!ڛ?YIk#:̳_{)J@Y~!Nڐ_w7Uq0qD`TǧcT3Doz?^G/-%M7dqW82!B/ >VMߏ46RlV0*' $ qE .FטbSl hx@iw |y裢'v7'{Ojj`( 04dqQ'Wܔ$ Xx]|zwAfC[#Vnpa\_BlqXt+h(Ϝq|h>ގOv,q~7HL3onĽxQVQ>cӖЃwؔ]Hy6f/MIy`rQ޷F}9qS2JSOȆˌa*kl!QO45F!6]&vlH3Qֵ0ǛۦTr.awgE ebkXݤXXe P!c46phȸ/w̚QTr T_( O;|`m+'Nu :뛺ocrO&RAs]m^B袺߳?[Z(%a"E*4t«&p~07]> 8*{@ޱ ŏAWߐ)>kw ;Fߟnٿsӧƚ&d~{N+?ZuG)XBK!X 8 rasqpzd~tfM@2^ ҮFV">]:b҃ )JepKgNnS[Pgb92O&<;AGOG,r:(.Ê[N&h miXۈꫲ .oO/NUGt;#}8Ovxj H;%K ԨMHԝG \BUb`,&9Kz{5M(Չ"HqnW3Mqt\CEz1x-TE#UD<@.BYyCq+5h]@4plܽ4Xk';Oh+m0` F󟀱1k/ܨXe&5̸yH a}|VnP,άDCJB9 [%^4>{Cⲁ"}% ; UànHz hj}S 4`<3Jzd0_E^흽H*98cRP_F|x} cZdס#/")=-˹+|,o}26e={?Gܳa 7B|,oQd e3UcPD c\͹_#H8ulĒER5%z} lk09K D\:T(l#mф,͓/ݭ3 ţkoR-$KG?QߏFL.)2X S_':!k JH^~2N R8SAMdNs{'JIHj0@-YʖAl.U?Pwbڴ5@SC vm{7]V=1"%,;MWi=$"cxՈ 5"ڷ͓A3_THjbË1kbD#O6R[Q1Ǣ7mj&~t~}e/ZU^xh],u(c^ {sikP\aYTfRT85A[WW9م&&|\(& 2ۻ8\)7x^7)bX.PͮA"b &`.~R"Qy\!0PKx1|/{)EKd\8hX޷.6&X|'#KYԜ9s}pPY[Y/^3N.>(-͖ S1#pOXD22PQZwߡ4n 6 @YN|klH9p_~rUg=7?hL8iR Y5Qsp i3%&@=g5shĦxh-rdi[_7Eǃ4T)_Ogq qsOQvO"'ݽUw5l {L`4uאXq(vA =>_N`r[^~~خnU7I FVXa B QN~ڈu \B&蝩= adDԹ!{I7-ͥLe{3K["##Jժ|]@C s< (:"l]&NU w+p$O]Ca|e>GiO+1|-"}6IB't*=BCr$w{Hؤ~`")/=-@mTWv|FK1yBƪ<n{PJ{y^u-~} X݀omV W ;J+Q6`K%߹G ӸiW F f:-4CZ.,!+^#d+(rt $FV<Ρ*~ĽR\g q)8ԣI!w׽3ETSaE{y"KQ1d v2bHiz-TVJՌի 4qȘh0$$ <ss{?'l?D}M}4󪧻 L&bmzU7apkGoڊиG]a1]p?v;> wDNը.RGo7K;7"_:#UcUʐf.0H]Jo4h싀 Slݎ4;?.Ʉ2d|9*W2412h }MQi{wYL^5_ +4Dn? ^0TыiP y; S]'km#4NqM(VwAkYi[01gja+̊ڝ*S& tM EPdQcQn `G7^8iP [%v6!i3ݬ}3x^Cfĩn֠9c΋ y(Sz6<2z<ݧ@ַ|{߭?VX]2AEvl]#NmKTMӋK<0h'BP>߅K+AKqOm:r`ߑr 7w?.ۅol>!O(7R4$)-@r\Pض7 NMvXv:_^-e_sާ:0)v Yzx)AcE52ogOC V!S@'،Mz&|O1 mͲn lh*+ PSj x7Q 2p (OyIViiyw< # BP::s/\*NFu'J<׿Fs,;V8 o9M-`dGg(X1V 0,D7 .XԈ\WcT 9 ;Ji ߳P˵:F':IdhoiC߹4irȿ+iK:6yh"{bEJѤJՙp0 ds"sv{'q_]G:!K/ wUL?}NEm]p4f l<)Ƞ]w_كxYYe]A4(`duu 3Z 7,}$C0ŔQ_[ʏ֯1Bb$3 MH?m}X{(vh1 ?-HS\U}'UaQm{C}xL -]mZL'ő:oug/L6prK`[w W:{5NG G{vɭDH 02).C[Z1i2yE  ~@m0s'n++v#8/ l/'PH\Gd:uvt&vňtE/,Wt! Hr$?L8 /J[B\5 {ŤNp ͑|ZaddA D& u~v@:8x9Ehjx͏=U7)0BZW]ke3Uq?$3`l(ODyf6|iEO1 4SG)$)l ²hoJq맪fIdK4lh3=$m'=+wu r?fsSzH3Qաݪh`QSc87LtbqSJwΣa@T)\g+~SOo)JJ|mT#sh84Q :d0xͮ*³1tvܵMsQb3! Ax-&CJxMUˮ{-X 㞼y؀62=NC< 26R}r踹xFWu?1]r{h"gKṢ|̄oBRqq)qt)xZ*mń:H{ IGZ}&f[ޙ89pMHܵ٧"I(E0/6o:F]U}A4$\#ܬ\1imyNw򴓹B@#0BO, &l^;{"6w/Sv[1H!.ЖGa9É1/̉U?v]D^ĉٲMpſt#&РA4t2ʾE^Ya-&Ͽ;>UDY ?ao^ݱWc*w!zƘ0tr pmBlL6;a+=b;x\SO#ڸٛc 4HZ_ok!jIz MzXm#c4UFۺU՛U]: D$dԥck }q'4Ir医>G +mC 5xmj;>͡pOAh8ws0u.s`,9YN#xП |4! ]37GYP \Oǫ@CSGgz _s{cߖ;54x XV? (w'qsRh(.24>N~Rq*Ɵ]N!H0Gm8u,3#xl #Jp3$khW[oS'N~ ˫BWjOb~?N:QN'jN2m4='y :CA- Z^q r Rigޜ82H%mb|NK7nNԗ n7KYsvRƳ5vb픏/k_DZ]D#$$ VV=UM&РDêo ?PP\%RahbK'%Sh#n>ŗRHpjA~R"/[,%e*Ž7"Fx/?Y)m0ZّM q=##x %y_=g싀 ,64T5"$"5Yv%`ARB[sMZ`FxEq6yÁ!׿&6v@n~T||HPk\(J/ǎyfm=7@MYyVR慙~U"yaѭ7Ѓ;Q[^wߡp ;]OJ1 C趡8PhӔcKnDeMBzIX8L9'K}!t ȐX.'VZGVIJhCA9qQh"dCp"хI_3U 4tuT]ԒR֡S"GYY:l髡9o$r ]YMW;W} d*+ِLvc8\Tݯ.}m ,Z0vMvڗw tB4[jD.ȧ/iUzhf2VNy`lYo}W_?Zr[ShglDE~ tpq2xv6 .ze1/7Ak٩xj;n ,ύAX]8=;%d-0nʹHʁ&9(*J#XN RcS>W+8?~! }cO-ϡQ_Q g_J(΀D9(JOBK=lt~ZT@RcFQSQ]+|K2f}9W@ #Z(%"Ec}7RnyAh$!'ASF9%;{3>hHRQAW{CAӥ]ꍿ;aD3{s`CmA)}ݠR}SRw%߄+Xeb37Hvml8Xhhm7{6n}wù) 4dJ)/=S'sp߅X NeR[R?w]Vk]uq\9]b#v..M V4u75{{/qs2QBG7 : Ys ҝ.6 n/hSC-ˁy8Ur):kbec&P*stX 7~U46^$7KHEgQA$>A]qPD7k?w8 BR4U"+26RCRZ:A^xdOѥW@+FL<7Uz=lK[YX02`J!bHRˠ(h$O׋Nxus`]RW^r1|^ޣ~v)ghilŗoFZEoݪG{*o%'ɤGwPVc=8(~.T}ѤngܘX䝉hS u7Љݒ~tc !ig\h1~~cDAYYːssI>4iZQB80Cc0!:E!6>x$ uuH?l 6{Ɠ@.rQѕ雚{HƵ9G<*,#ʅeqX!o"*S(#$u0 ICjz@ҝi"w+t^w{ݭ~+K.R&4)渫{9\ܚ5OQ t+5'}DO 'R͂c( ]zD|26u?>*$P~"m]]e9ySˋIh2.c4oC`Mq$QK) )QUPסJ'fa $e]yL IP[94ηG<Ƅ}nt=)k'25!3>ه#Fkٮ 58=#\d7q[|UA1>.p#|gU=iNV<3E wsDjJA;KoWL|v$hS_lZ1˜`wI{ܮVs蜛(h(m9AJI #&Jwl:VAS:Lk *=|3ԆF55CFRm021]JpuX>cPSI}*"̎= +gw4`*8iBHDMOFr//h蟱5(_1PױCE^Hs4˅|if ɳqeꟙ`Ά܄bYtK aN `yq9"Q)ZYڲ"@CYqD"=n MQ\h`>#o}+Hxw]S0$7r7J anOb^yD=G|M;9Ks{w L&^{$R2[aM.U8H9x?ODv1zxKj 7NlD*wnEf!kaj|2@V+HG LPX:ƅ x+SPU<F 9hn#8O70w['Wcn1%ki`rJEndG诅+^7u 4Xpi"Ȣd:"KqCahc!Vs+wT4It6/*lwI oRV-!_ܐ%NqIchaDJ'!pIJԖTi^ݝ]Hř7hwV# NbN+Ŏ{''dTSx ;?g'~}8ES%9 {ѝ@CiF1?=Z`DLHun^J,x{bbV+jnj;kjDH6ORL}c'&SJ!O(Zqb?ðai묨E;vDݥqQ!u5="_w8:ҟ,d0sbQY/d09AU ,YKD{JF 4ANG moɣU@?EU]uJ6X4Tnq|*֒rU~3v7$>`bE݆yd!ؘ"@`Lŭ\>u4/xB¼)J2EuH9J5B ;vOɦϊ9(hQ9M( ('&n#4zzi ݿTfc# B7ACc5** P]Q0&2:TbdbA膱hAPjˊu$zW򽡙áV@jKN h`0 ,}F+ e$m64gr-iۯ46g`iPQ 5Yɫ^ë9g4gajBo"KzNR@wu߃y#%<5$O|0{av(JCq*Ip q8\WF5\#H6Ahf=~==:Xe֭k; >Uyɚ7{{gDVL0ss1yt7@CL?*rWVKkL2b~&|*ҵ1%ؿsdE,vp 8DJx a muSGQ6@IDATmGc@Cx]Ȟ`_LOs 6pP^rL4GQ^k`CĉKܙ$NM/VA"{ JLHU|@c42PK)nC=hUƘcG , SBڨ^`Z4=@ ِx(Sȓ&/[N\1-xEP^xn!9{qQM(blǝt*+Dn)@Xlq]+}M[61?<)'rH:<=a3 p#蚖qye??=)a& l=%s칶v|K5!nA71K6co|哎SgՖ"zFqZ[`l8D_yq.N|LH<6jf`'k =B-,= @*F* s+{d&⤨""M^LmIҎK (ׯ eʟ/S]~3`w\Tr : "Ñ} &+}N05E+*ɇHr[9%DI&e/^+l۟F9 P_uv)6x0"kCT4 /"V1ѶSY^4?qEp:\d3 R8' lH%7 <~e\L*C%{}4 gTF$)@M^1lAħPEHQO[5cp!o@CʼnT;ߛKJRM5Ge3k(Qm-Ko|VR(#pH7g4= .%uW}I@$lМp>TX m_Xh IgdÉ FN%y/CRLffF\h+^E܂Q\ZvP7?ޤ&?e"gRεIQ|qsk0Egӯc . fOi%N IABȂ6VU 2=XhMf=[ JYr̉DvT'YtX[&v9љK(j:ٵhkD+ UEOA5Tq3҇/K-'Ej (F,% bH(&M)= v M2J>aGw"զ~XJhfmg>:Ѽj< <\߂?S hm1NZb#"cEeoӋ/;zxL% J&>p$؉ܰ 'm %B?`{4uB'ĢԳH=q.S Ƅ.jܹ644T&?_ң3ΝQJdb``?P45HMy9'T6 PN B́T< '}b!E,RE**Q믪vkQ ƒh4&%Zkh[JJ[ۻ:O(WIhr7QDK:'VHU4"U\#s_2!H$S@<N  >*$*Son*tdr$Q_9{*8Р7jY_Ck>|vzvUo$S[P@Dg仱/WA+'V$̺X}hn8ZqUذ[ + okkSvsn_fL7Rni'N;v`EgɊLDZ"/.n3B..ױCjj9]JQs>J #YN4c^5=cw@0)kR4ǮSoU֦6A ]p/4׷^SNĨ[ǁ%+tBdo%Y2qτ/Yv$m=`^FcUCC47!\{ۓ6őHJw}-Mpq˷%m]Jonwj&k W5CVc#bn4.; fgCuĞ%v9`hg( lgXqe_!)=\3J/IdEyTs{}R;?p zTrx|X[Xb;cw ?eڗuGH')Bpc<ҨKalTkmˊ`N\&QLHb1"3)WG()U#G{Im.V+HgvD6n{".<}zN\C+g31V{SHĵ}$ "m#Rz ě|y,)wx”z;1[H2ȺZqox`4٬ALUSzrD;mFLV|/ jyCI e3 ٱ bԕ#\@IO VVG*j223TU&GcŰ0 G6-kPR):le: )ZO+gwLYssӺB4tԝq~׊0{OS@*-.>N*ZW إn;R~yG>݆Y+`nCO.V~wBg![gy}P@r-͢4At N`( y)DU}k7؋heVRMQ|Zt,KD$&K- MVTrozkW_΋sIT42z4 R~q)|_KjQA;^yS5ֻz_|&sOn^'& q)K<V"E溺 a?&zLMAžP20x9p&GMmX%/#f/ ]mGBLo Ibiک*?`t'w;ҏ bȑRobBd+R~o>S'@Z ;N͐9d@Sgñ5)Z0FR~9y`!hnh"a 79H19jXbiOԯ$Y]DŽ*F2q4sm/ ,4VTwS;3L+]vD@If.$%|'$CG X`䂅s:kѳ` .v!wSo`O,QdA3ظ?,>TK !>aB 쓴d碰w]s11DL> .T XxF$'r$ *œZ[M}Fy y9;"#{1y2`CI`匷ǩBm*Wmw[s+CÈ6i l~ 'cn&e*E8"$8*_ZC'F9iSB+צ}a`{6=+  P`KJ2?Dg}nd k;bOT04وiu\cl$zC)8"%t:~}v-N WV@KO੣#:~菩DmJUr"qoD3z6^R5իz}Ranw /: dDƯ_FMe `;vkuM)brYY菍.Q 4TӉlR88X =u [Tk0;7BKs#t DsM= 4p'6Go^lVWUPݔF )`@AU(.)%(ضrCjr?h {yٝȉ+aw `b{?YR84Y߻w濏ĉ4 e#k"R;(Bh 8*c S@ .d)ٛ 1~p⡴zw\#R)ݒ.98E@z}Ajlc9/>CsUxXk ԥ HDZf,PDā[o/LmҮ"9{Ol"VA<,`߀]#u4E5Fcc Z(^෕ePCir#(04c?FS+KgxeVS=~DB0u7I(^!<;ȃ`wܾꞫ"lBt#kn{PhpWwR2?'޼lTE844SjIiۅ ]S8Tjj ]5r @9`9ʋ \0S8[-&NbgOʢTr*TBsfҢn$M[٦~ Fʓ ?xp ŒE+.%}ʋsI|4h8''^ta*@:\*҇CB m"5t\ IQ1`]R BOA8>@77?|8rkK~.ʳv b샹hڞ&pUc "F|"9$+<wL.)t gKڙVwhx/z%BhJ=p_K(Zȍ'.=G .ntET],ahcIi쒆4~d~{a"]y`-W, < Cf/w}/xX%)g_V oZM.~C1~hkWOh4WG=^['/Lw͔'ڝ7U dCuE du\Eӛ@vM͊ӓ3")Cxy;8"(Oa>r!J"7 ,p?]&U &nS @CG[:oO#FAX[9RQ@C_9@kH@$vpHDϚk>)ljq5uƈa9[.9@>dGgbk^p9MNjh`$W>G|m)8 E -T&0e*RuJz9sgh vd)q0414l]ҧ:œd4U#7>fP&4!;s\(,kP Ec&60 ՗T: ;S[~juk7T7Q(~^ OS=XWS@ +[shMuMPGM0ʵc{B@C<})|ٱEٕGk2 X,Qz}iB-͌.CW<ߙ[!mqQ{}'܃F֮ܰW=GQ4?{Vձ}Tz S41'/yy+/y/$v{GT@*H=sŠA gr/眙ٳ{ٳZ u8 wӻ|Rjpt ?{>/Hn ߑUu%ҫb<ucafss'e8\ylfdMZ(V Bzci1k׼ʎCNZʋ0k飰76=,aߠ('qww)隘qɕs'x)G\ی_\(cFgM=|6q(:gG}E!pI`[-Uu\2ӏv_()+P (Ng #IHkSX`>0Ey^ =H.EQjtEed @~f{"pSgsցV"[؃WpIK'ʯE{K'+CR錨|Y^IR8z-Œu3`lo"kycOc;:g`7w@{J~J/C5?Á=5T6/!;*k~3Rhk&Osߪ@.k¢CK}ʲ]HNtcifJcFtΉyR.'DЃ3q[kIk>-# v c_h(IF}sC>>gh-{U:%נ^;얯PMdq؎u?6U!دur2!g;Ra@ dCMJUT) e(SFii&Zq5[gotvqGImI!i L"3=C!c}s+"Lb 1d~%56${ʸT4RYBajw!3%:hn ;ٯr Qw߂S7+a\ٙ7Ζ {Hv4܎AϹ-&.CAp t`NpV;Zo[nb .f"rg'L9++kǩip)HVGV <:U8Rp g}n1潰V#R9v'}5]mLm#ܖ:m& a ݔ:a)dmc_~lr)!``a--oQة\0ܥ/{n?*KcQDzr4 H1cbrͨ+GT.d'c֒+nf@n@|A_6Ԙo:/GI.WE%x$Q6M^XݐՀ=C鋨-A$N'm[|89N8 D%URbJ~wxS%H&MqCG \ᯞc`uAʳ(2 >3~ע "@ qI*N-āwh,6g~|nw6b/| j6AU[ A>$qbwEe1ԵI}Y-+Sއ^*cZ(k{0Nh0f $&WVVuu!M0heRASSSނ|4Uj@HރKirFb-Ͼ%#I {GDW]G$h&ٹp%၀akaFzhnrkf.,9EI˧$|HB5PKm!A򧭮&.p/d_UMAmȉ<]\SYUza4E4 du8~ eU5Dp}CHsgrE;9 /;Q/_I=x޻@fcF5RC)aT˱,}b2>>_GaRbm4bboߙr VZ߂aH,4} ICY`@& C2{9ABkKkIB$!')*XV]w20^qD4twv/×hfmzgÞ`ߡ|Aҙ]0sߧf>$,&B5} A*ےcBQ ~ #Y@V Cr4|Ww}ٳk.4̙3xbݻΝ {$$$`&`C.͗掃v܂*IBCC%hkiD:W`?ZX=9.. ݋{%{,nG×NkC=yz.Us^IGm~ 8en3 V ah&UXgJ"LH|@K_򎸾*8BE:ۘgg'?éNl8RZO8N>0aVm%Dơ_>@Cm|x+&`/R*:I]UWEmYn`eV Elk,cjlnEyAzZOư)bi=bh/u~-&ueX>ߝSk<񯸆}u|+*L|Ƈ<K>{&n2#TvL\ eSAӵh`)uuu}8N ¦&TUULJ:`툈.,--'Yޯli {p(}W(T_CSQ~72H S#{jDՀ_}\y6OیaI0DP!Z=K~rGS|6"HKeÆ>Vi7q%"4$z a9n/:::|S1gN VX“O>lA GGG3}Ν[0a\q9# EEiس] !UI'#̘NqhhB8.b=B]C>VXMjpۏ0B,O(CQx )9WCa"w=d 7/~-n%#,QW3)uTW;a'xlp5xId:=ZGh{`-{%ϑRE#T"Wa!$AHv =%m-u-|\1/i$/B=BAe\x+2ws|J[(ƢVC}v>T\+O)o6ii̭nnŇV=&55G#:>H';_tDS`J^45E7$¢Xo(?ۇ{ 999W X!E)8|K LDwN=apuoÍ0YI/dugoA]8wZӗ.z`x2Nf8u./n@ t`,I CB# .R~#xg?I85|͹0q"-o;o;!k?Hp$Tn;c/H8WDKi#$3-ZD܌=~`lneY;ؼ; x. R&gysSZѠmcmwxxəW+j& dm۶E>.*7-xa1#$(,LCH#03u]E,ŬYz΅oG .CxL ٫H UHT(jOPSQS?|F1i2y'Ω!gCN|5^ݔ[Sُ"uFWwORۭ|o^O񱗉SQYYDB5E6 #c &,Dw 9$"jZq懓'A$RPQR|Vέbva)׈W(|o/rz6#зxҰ+Q4sxZ]гu#{8F`f#=`zX{NH_a҄H0hN?!L(N)-zQmkw7&BK ' 5+Hptko;Iuo9eH/#t8D"_qf Is|Gt]t(ui0&nXvyg6".cmc{yАmȍ.^ )3$W_3I$z:Z!z㽦|ڒ6qWލ\]J+INӲP ͢?C_lsFFFdcĎN ݝv&X|z?ĝ -M=,Y<VyڪR>._<9/$G5r{m9O657FH.o7SN< l5EtNdaЧ!yOdRQBce=|FM~Y0W@@!`To/; t&L~lآGL zStj>E.܋p%p5_P-Nv/=9?.fX'h`͟?qqq`kk+ùĥ?\|ee2={㡯ȅm 䳞VX`6$U)l NKtA_{Z$+x0<[w>>PO6R5XBt%:=2W.ShPtOH[gV6"~7s|p3-"::ooMc߿/^p`"?3 /縁M[q̘2u#+rބˤ\Q++W 9A@CCDvvGMո|ں0zy+:pp1n/Iv6V#+<4 o6x{図"|sURȊ@k]=534͉AN~QRUAMV+ ,~Ύvd^HGCU3_f籴 s ͥxQR9G.t eYhnyTՀvr1LU-5 ^ FWkþl(! -a@?)O߇veaߕ&I{6dxIN4x@*)([dFb0a,]l0NH8K kݧz999V5?/i="60h0Ko;7s^^^`> <)2//uSĻヒ7|of 6m=gnl{?CKDn^ń݌(IFtFߑfbfvFWt}'lc[}<#dIDcy`+STU``_&f7M#_= #P]P&_Ɋ If@IDATQ_SN(\pEaE: iSӡP1RIVq NGt= -?;Je`yU=(?jp׏0%x% A P#r9+Qpjk8͇Ա3y("GCcE%*sNl,:gbz*I&f6j\N7UnAq؋[S@*?UUrWGuj-r{:;FЕmz-ωx2@Q5J+Cmq5j{IZME/L{a;DZq_jgRx/]$Q;. -<=3|Jbm͍ٷIq7da +`lawsMO@zhћߛP| E@@]C+]v|Wg7s#T:Ր9Y/8nΚa$"Jk#9!zA}ySA_iT(&ȶr5ʫ=R0C>oǓȳS(NOƅ[56߬$G" 2lHdu9جi05ás[QZKNw6#8v+\#y/:zFXU$FCBaZ+OIKej͟^TQDCH^X=Q fG.j|؃2. E@@Z(A  n%ۚ{皒̝RXL@@B0%ҌΝH`Nz_Tmn|e 0r鯊U Kt46CY] s_cg'""܊fi Bt}gnj ^d\8ÛeRA*8t[D~bϗDn & HQh ]RzWLLl{v C ;ˆ]Ե@.$*&NY̹8R(+ݯ$؃&&rM3j<'Bhp@! #u&iAESjZPwf¶wg*Td@+10pSM%erezgMgYSSӎDD~(JC]O_6ho )L^IPb-zEl em/6sV HuE]Tc%,{$ϣpAaYJyUo@#" XG{xYIz }Oڰ _VkZHEUa.¿ y *!ilut`+;'8$ о`jg;y PgLGM[=f S=zg;_?COuB0Ugp=[ ژss,蘛pԹ7:I遢 ,1a`n&q)ؚ"$ܐt UHs'tb?taO $v|hjcɒ!=>@IIO~I*8af\s*خ`(bqDџ܄?!g3tA*:K`?y5u'?!;|s0:د;d= -n pPM xNI*=-hn[>߬HXjZ[S1.;{}fӗBL!e8 k#Bw,gS*0qEOtzci !@M3~)*5I=$H=3K.^%֎k'T\["`Xkpt'hmi6.y8cw4ԧ E? %{{3@XiH~2?z^d2m#-QD.{1EƩlGW[ /~4' nrc:{`=p>JNrL픳\U4Wҋ&1J;9#*؋~*cɠuRfQlt#%:8ς(j!UyX 7ƣ`Cg;tjc>.=PƇ0NI:O~C}I᫄i~EGCͭz&Jwǭ=Jz!'Csc-TT4xЕ%ki]:+sۈBnFWVaԛ;?Cu9-:l4`d$Ǔn;9Ҫ+y1q>+8dr  ?͊)|/M:R] koj`Z[L}8k09(o8 H6uEgTVP_-MjS^PѺeH9"}͇p|W/Z:;f  ڂ}ېw)78f.yD%zcߙ*/CCއeh|J<9ZۛqV,&vA4P’u//PV# 3g4͸WQUZVڌ1Df9Z K`Kihk Qp5ZVEѻ#zyϬhŰ/UxU-:}>,,;L-[xD= #9(ZK\ɮƮ0``=u_ݽp=S\޵f<8` (v^mÝ5TO_8]nm 1Yrp8TAf>~O8e񅟋kƉeb_m7}}&¤%h*,%"5yȋLtVJTƌ'7fԠ$ӟ~FS5xL<~HOp2Ke5Lw{Jm\qJ}%ѕ gk>HμtonHW:()`M\27=GG;,\9`G` 3 ]7ُ)رJ`狫g#{6Vp4 he?%Dl#zޣܖφ=!P&yőRIB*,<^S$r.Dģjژ30vMD7?20RpuqחrM>m=#IGH WVIُS} fQTچ ތU+J9?laDt, Ԑt)guX:|,GiUI^.ߠ:rn;}2=M25paDvՕ7)hAIyp ܆@T1|Xe%," Ʋ*r.!XzDb2i?G3'ό()wH\&2|EwW'%\Wհ+_#L؁x/3eupr qGCS4NK^=5jkKq -Ha!¨*K`la'o ٞ1cbƢN Q' >e!9ey:h(| 3zLjCY\uLI$L3?2扫=+r90![`{ 8|aw 4d)ndwgWB" Ѓȣw޹8T],ɹ)eFx0N$4=,ä[l?C飼 m`?s3DSWu)O6lLL wH vq%'u;&l}}s:QAWW_N_ *ϛsp5, cb!Ry51=o ! aMOBIUE*˪\=bYꚉn!/v 9 Jp-iFcXt T7նb?ORtUcI e %iE?{kwPu5o#R9BAƅ|!ǎA6NŎNZDP`" DKeۚڰ뵟QZ=. 0:AܹPrB]`K,5„e]:pQ*qskWzitSr2Ğݏ3o94ipkTHǨY~`5Ac R[LawPQ|Sޏ^ͬo?֖F\صVF_:9I-s͊l}9~mi zdBs ,mی\rt 5d{ E< S_/31uX͇dPituܑP!UWuk-*\EϯChdTE-Te<% )*ȅ?tTI5‡;'ɢ|DsuB>G"a/dD2$Fc #0`>|eek] WAS]肣)0e9lvf+jpd]ƴ롦Z=7pIҫW s{vKqT"TCĶ.4 Ff^q4Z[(/7+Rat4a'B8f Wzܸxɘ2t $;ޙmá911=BU "PV_^?\Q$dG,yct$m_ނ**"ޠ<I7rg#hjűPSxKJϖ'kO(Wr]I)B?>1[%KGz,:HN%&eƕs=MhB+[pe* 05Jua  gY&"#w"3+ƔSì%Mmq6N0iğE^9A\yWNO_?ûq%<ʝ 3zk;qfK<1 Ձ2SP(o͜ _̥+/̿=sכ#fTfLOmddo3grGedAG 9(2tÛ`"c= "Od&_0*nT棱LcH\s@WィAR3.+sTU" ؁:) aY u韫4Y⇫k(gal[s'Oi^ՙFtP[ڀm;e S,Zw 2|e3EE5CES|.72jh(*FTH(T?ua .̱`/?v 1qa+uuAC!c/)?.܌K<*Yje銹s2ot-V;:ڨ1q 9<%Mp4aCi2%4&K6%r̾g#oyu%H8~?;'{US54?Ɯ$Nǒlw4W %͈me*s'c]$DLc_;B\}1x?`.< Gӱhkl ¬Rp5+N-$ǭ`jyY){8zL y%* uV/b=^` ̹``p)O!~|X,u{BSϝmxO*r)ʹ߃Y:`YX=-` |.fOY+K^p哗(ZZX$%FLAVcߖ?k<,GVK5h a{~> tnCo`i^éo?FK}-N~f?F%#lA@9sDEC\q%,O[!dDn^[/2߁ ?⌹\!J``Voa?QT4ct7551ABr t`0Uob>6Wppǁ Ec0Go$؀=[ȳ2^Syb҈6nClWZ&hpwkmhE LO>/xk;WObD$QXlL^*] lld~qg6Rx/v濰 &`4'.o}{KYƁ.+TL^RbQzG#lԷT9Bwƶ56!P˫xY Z}P][wAՕkJplg$mm#,]<յDRS[JFښ 2=ee76<}B@L[N"Y4t []I" s8SVJ?Ns }mK $5oH\TBhjc8{"@ q ~&_Ye~=$a(mIt1vvsleqz%?Z=sg8r+nE04TWy$l/&BQL2Ada-iȑo-r*hHRewW6VP*EfLYp7:{b ʠJ)[˝ i, RSq!j/XDXtW =yݝ%Rd'`횷p.|=9 fpRaٻ+#"c]CE](^,L_wB̔8#$i$+-u~D>Nj;rKP]TC+ٝt#TxL4o`؉rt->GBe!%, U< ޏ]pl;qptwa̸lAЊ-k|r~f0J&aɤ,"[!_uf>w,0JTa1 >^bK̈پ]D>IY芷ya}JI6X#;>Aks=Դ"tt$٥BTȝf=`P j"~v):,,k'g{`H,dKQKܵ#e:e~9~ *r3Ii T53T~rLAVέ蝑|ޘ#$Y(ܦjʻ;PΝ ű)RP'nkoO\0IJ 6K">-}#x)rJ۴SH:yJJ&'H **jX9K 3Elg? r(J~)scя t\ kX?0H$eg]kl05Yq,M8hC4I[H !^N7i hpfqs&> E'!` (s9Z.Ǖbӽҡ1Z1I֝`{?vF˒j4h c35 \fjHLF":y!rp'm9?^wEtGQJ'4 ޘ1wF QK X l'>LH2jϼ]kG^Q/:;ڹDuEٮDOQX p-]ҲlNn#<s҃U N 8*`: &!)LP]ƣ;v̡tvG)7}Qg 04HxZ]2NXU dq ϝ Z*w245U|^kh%AsNLh9:E5FIM[xXJ'ݠ*.FD{5'9"G+Yg S#)^bsVI3YhF^#q/6ܦ>@`ĊWƲJLh0*440JPRV q3{4FCJ**c Մ~$FJ ~;@o7X+6cq+JǹsvYkJߺF%?{;~v)2szx* 9X0O@@UϺJ"/; Ye:5<_\$:˗.#x¾9&)ܢ2}&pXjZ'd+{5`J(bl la{r}Y-^^[-гo+ 9z `hlVH5  3WXr~.;kj9uYUkn O|H> g6Ad]7Ă{"շYׯ_CQy0}}h4AJ::PFRC- /IO 8M̍3wG"$Ac'!/O#etLtam<>䧸P3Jx.NǥJY"s &RcZ V䡎_O&'B vlJXW%ݷg+ 7 ̩PK΅;V,̡oe s}H IeE @|GnL'lMvd-)BF;tMx]Ƕ!J,* `'L$lBrh$;fF6(,",pԅȸ|m$t2 ,EiT"R(&T6!Nѐx*ޜCԥ׻x{6_]I<8džg'M7nx;Zv(jas8Y-uܡ uܡPTW4+Fз9-eemS#s|#tc9݉>Bu]({J!PY?FGk 4u xOAzR$j"xϕ$lBrhD0h(!򃀊:&Xېy1삡g&{wB3pwTUM]`TpF[c;X$לK7Jtt7W",L-OqPt` }ՋqW4qBMKBurp载hM␦v>,APS :!cTֺH` "ǂT+^^+iU~5oAsu sV?U D4թo?F[S#iEhHkb'>cDH_7$80AY!x!p*g k>Yf؄c) dzWk,9ro7PvY"}á@Σ@\6+8LM"l82ϝkQ# s 1>)F^lYb@]y 90o&X(_ #}d䏬x`vt$]9gK< ]p4 ]/@hNR p| `j Ua +X4>ɖ``cKrN_$q ||\h(*c Kp=H]L>d\ɓCSS |ar.i/LN҆~]S&'MbR6ZSL⏤q'@79do/.dƦ5ASG4 oC`b|R?RG”a6' <1zÉ0:O[XS2~fV2f3Ϻ2=71$h$w/8w4c#]|##({a&5xr(sx(VA[WqS;:kp!_M(Ve#2;׉ėpUg}s &s.T4oJzgÈ #NcjsPqV Y0S2!>FtR9JK:005 yHOލ-Y9.V-瘊KÂD4 H6?ZXH1䎆y @ϥ)IA^YF1O<# M1$5lh/ ,)D_[/Mf]h L gPqGv[~%45>G ?| ac0jR{"Ï3|'gZߺFsg'EdU5h-+GKYy iCwVY2!$'ṊDs|GÔѽh,.`GmxFJdnNLw\_=6)M='_Dy1b{ K7Q(E__RRF.(]]GG16<<k$6HxUҁ*R3na6s!{??co?}ab/,x5w_O2Y?PIe03#8#(j]CyZ Ҿ@PjtFq0F)I$~>I8M·oAF/ҊƂ"IDi +i'ttD58 WއE6Ƃ_wZ#Ŧ .^  bcZhp?va}5E}mhy,_*zLlm8Y(F) GR5"I\: |-—Fl%Zb@@ 8JM$2ɓ(JG:q0L&؇"  cD҄Ř @IDAToE{qS٣/F5y](s+Xg|F}mY2]#@Iʓ,~%BGoiB)YO% eg0\vGG=K}'bzfjbm 5@̲8v4$ _x "8R!0`de /Od ep0yF hʹ0夃<MmSo9<)ZSK=|6 69zU@B0fɦ1d)=c h{F gZl wEG&|Ӧw(wJL[g:br+<Fq7PV +gN;K'٨ՈXv%AlfdEC[K_Rt@;d.vD^ide$S%*c3# k/DJs4W@k'Kp_w'OlE5aG~tϿK]DeWV~LMKR9=Y87}C@[e >x0^`\C;ya.&&a]l+ŤLu彩>|Q;{ oj\R'ۻXi䘗_}LT!41i:_q,7Du|8|OE?)#N}ΞAWl!W{֐zaoE .]:1h,,_M֐DJ 2_SSxvbߙw݌X{2.YqΤؠJևzyD5"7WyE/^W9}х{} >;?8>I.-XL;#aIHԭuֵW)?MJO0u 1 ~EAD\ř֯KCq֞KCTffғIi[# N~!QnՅo֪r$GCq>MJ{%9{}TgOVNN}Dd΍APWW13"B"Ag-EJ7(*W rftũtuֹ*@iy(y[J>k`f*vU(>_H|3 ,HB bU)ۊ+w160=cĿ !u(OIGcߪ;XfS&ԅ-򘃡bZMK[gȻXZ]E@>k7!PU?HF' ٝ;Ӓ#X˺2a``2}op8FC=$tܗ?íV:ɑ<}o@XV6%\N\~+V2k@";_OB @ev2]cJ`y­Z7wC:U*!Lm5U $֚rqa|dHU~\uF&TM\\ 0zMP'QYF*rCWSv[NTTR9oRqf.ذEhs7 066}V"zV97k6pGlPct:6Xgçظ9J9@Uef7 0SYÃ6>BL>|l-jڕmhŵIH<,.\:grZQ|n'Ά}xa\|c 3?h%!?ཐͭB;')T_K/H3$9H.L&v`h!'5H:kز-Є&7u{4>is\CJ Kioũ1۳{T^ltǓ'^I ZW{_d0S4A wCF^+, R"04xTJCS8O$O!ݖ,Baw[Eϒ8/|~4ԛGNXiJؚMQu[>C^śf w4G[M7^6QOO‡Ht]/TVtoA+e6!sԟs [-0O4X}d3ԙ0rǔ y X!ޘ$'4s2hij4 /"< Kh ) !r4p'`2C*O6 գMX|7LYJnMGCoܴ*zD8 ug?"auswb(zz'GT?H$!K0'IuhhaHع5 V a`bdPVydnBK$=F_4f45_ f~e;SB4`Aƒم>IwPVFFF0bDWM, M3219άXur2P9Nt{xyFFO"D=*YH(/(Ч(V;wT;sEMM096#Ɋ.i!QSYh)ܥw;๏_B`l3D1wۧ(R+hrsC|r/ʜ x"tFE]=x =s2,@#!w!i'C?xUTN뇾)w2_^iiEC_"񃷘AW[G̖g`l& [g͠4`P#GHGQl`vFBj'.jlǙz 5ٕ(Ml>[p࿿B]^5-b/MmMx-qBjr3'`t8z1xq zl/uH"H"o6|\Bݥ% kW`?U}6xZ_$ FE>h X0`8׷/} fvbQ_m^х9yDLM@x\f+Ċ L~L]1yvAc8ę|g^#PQ$q4xheb q\#~q` P]E'AK)9}iƵĉR rs\V^ wiG##7 4)4 Metqg9Ɔ%2D'Lٶ. |ۺ􅥫 T{cthgu9G%:FX#pZ,.d^)j.f2U㢱{&o>ilf*~nB^F]a6Jϡaan툘FD4ʵ,ٸ0\;Sa㹣A{ ]ۮاk ].JZ+D-T[i0Xz\x FzQA!}7F{YɧU(W\²+f5NȝhFE56I]3'7 n5ԓ8A8A[P3G?H@D҉c2 31: *dlހ7)`fM1>27Z4\k|>KLjcq>(xN#h$ ^K,,HuД# SAFp6"lXZ:H2$V0^ 5†ꕸv6^]J-XJo ˉN$ka`:7z:i{QZ*QNPTҟ{q ;^A,lDD6:W+j^h,_I/>jaG} s.Tdi544pjs]>C~&allORr7 qC#5Dt3(L!P_YDXY9K%%Iwqc'ݨͩB+q8PCke3#u^9Jמv:@ ͤ+eJKLBe,%F0>:T30r@Sq4Pý"$ p{ ޜWMGG--)9קqCSK GA׀:v>8f!&IRuSYE*qwBI K}SEЪA0[<-eM1u9O:NB8լS!l:xp)P;piEgOc`?rE;f /eZtEZZ:\xWR&$:;1::Zvވ4L-Z21m!GTqs44畢D6Xy  %0% .6AHb!C1{QwF8IB![5RZ%q0&e++$P.FM$GQepe^f,A+L2?hЪK(~e[c`nh1dU($\ Q цPVL.A•8-M8T\I$anz] (2 :#|fX8;MFuF&w}v ʇO弈c#(tERr<"D0Xى/FΐMK.fVB_hhEW}c$ϝzbCa!00Ѓs^G 7?NSCKL`ՐQw)=0ui%L"v#K^u):pD͛RE"Ͱާ}} 2IlhyG,QOHJiy-@SPDR4Z ^e 0}Ea(gI Ww~# 37]Cjܔ["6Iى.l#d]i_Qyۅn@$P&9,fnl"Q gcSnȇzQe;W(U>9G@hP]I&NA3'147'$ FFꛢ:'xg  w IWcFCeXANrkll)_$o`jt 7`dhTІH`.d 0V>.ԥE(> > =8UC8M}WRqf ߽+&gΐs@ bPj$r;3[=:+ɩj27mU(K?+NBi#nAKbp t )EwGћDe%N[o|b 鹧_v5c-XhiBbmg*j7<lM8LP Pq_tt`"<Ԃ{bhh.m$`b-E#@oPD{.}~Iy^K3VE3.VK)󍏌2u2 1 LIZ28t7-HC`JNTh*#rU3T%Io 92υfpwJ7؍*YX8ǧ`i>9y6810'QX> Bݢ@w4#9h 6pw%/ p[< nvP>A ݍh(c4ry_Y (t(1!&[ݷt 3F4@cM M`cGG_M%ψ@cc)Hw5O GQZp1<''?u[{wF-m]l{U_!\[0bOdӭ:LKr35w&:0wrBkY9FnPM's4tIHV#0#-ĹymKo|HP`b۷:+vb#۝T(A\;, QOkPfPBpY fے4/YTd~G$5!_>#Y =D.qǾ||@գɡkqZtVƶj$e6pVUBO3 ܜJ4;nBW^x}p_vs'H:HO˷ZI!NPH խߒm75r!Lu.ug,qDwfgg]=.G$5⡧w{(]MH]|fScWRֈ#Mmo X5\"&~ZY".bL Տ{D- 1 Y.xS+vw\ޗ# *ښjtv_ Z<+kh-mtXrД3y?[VS>Ջ膣[;"ДSpWL1@% 4Lr%)8 _;t$!D2PRg M,"D<:.e7]d\IѠA,ބ0rB #c bo5[{ U*h*^m]"[[B$LLTrAQ),]`jaSw!`h% aW2vC$ FGPrE/Trbi۪EhV$ qm]ݹ x!'ǵIcښ:`q>֎r+Q.kHjDg`eɁgM.{p.WI̖ r{FH>)$kajd;ac88U?ҎH9qUŋ6ujURwZ*&n·Gf_/Ir^(ͥH}e$uE8K)B$JQQ߼Q"WHJᮠ$ O'4"Ɔ%:[B !8jҾψy \1ۡExxS,U%ٸx3 I8Vcmd-_+TW1B|qK"l=ekAWk&ֈ} y7: @Hh;au [&s < Ѡ 4,?}E\(kKt,9Hػ&GJ jqoclpHX چQzEjP(٣/q0j)ĩC&𗨸"'~vw.2F~f/JR%ko5kK\2@`lliqZd-t fV;S𪜣2bhD[ahPu 0<| S04At=]8}˫a(;"p{C8x60 '5Z܌`~H謭#OjZ蛘!p0xG&)!JL+Z]us Aπ΄<4TǏ&Y `zbhmF"!|!) z6[WBe cH9 fYhr|#RSUYNN˭)!$u4JY54tN((' ?l"!*s ƙ khH"^FR?I4eleXZ _Jw( 3 -<i?&& !kv7EizJq3!"f~C/&ȰuwM%e[;p&}/z;^8 Ld3Pij@C(sKBxxDrWY)ɿAQR>3 =`u@!v.m}=$U9@ބʪi,\.A 63m-HtU4L-8'hoW+[(9]!~M#1iZZ*l0');Ղ$L&*-؆;[rIڄS@?U F`eO3hP0j3]uV%~? iIJ a1G%-ts}j'69>(8q =ӪyzL3#PvCVGjX,\y$?*kJrSvk =_ Y<y%%a*, &)W2,-I%Bhn.N/ݗAUHjǍS^:17xY!I2#| }͒Yfpw=tV1RS,]LC쒨MnȜnb1Ez!}'/eY#zӰuPn O:*3%kal'aonP(^P^qbd`FvM麉AQ;llmH]%I&g9u#M5IKpL=:(!mᥱnGtbl -l7 gR3bЈ6XA!u]I@O*l,l uG_`=)]³p wRHT]CGGIF+/@u00.4#m. >!:*e +Wh.JY.1dv ꅛbk!jG.a ~pY/7"(ɕZS6kڬH.oF>u¸D}?Y=uH}s5ӫ  uuhhi"/_Ul,mQvJRO+~NwLOWWUv- >zDPzReIU2T(CIZ2NW? w_6[RVzWλP ^BSMp“g^35V>ONsEii7?E\D=Kn?s@m^5cʝ sFoe5H'kleH)Kә hKi9s0UH4"CKv -xT\wV ?{|XfBWS=S֛޺^4$ 455Cv[aJA+ F7xϫa[Y"}:Z $&q65Br:k뙃!pzP s;}|cvi:$ي%,fvRx/G#poh&OHIrw Is NfʻG-Ь"㽯s,+q4^8mA&+ҁԿkrl.XS0s>qvoK+`שf `QS})_}R6[}#h5\G#0+Z%in. ۬A'ZY-^{-GޞP.c_:\9a#THJ3%9ӽU|cCäIwZ9~Ga$r-]N"|CX)O#*2.c$qXu(c/(hPp 8j@k`㭖oNCpn_KWcdWA@WqD@ ~3Wa$HJ!8@fbm%d /P}I Q=G7yJ8{q82D`b|uUL"g lsq)+%Hy@[>Wpi #D|o  FRN4p}]\.#30g!@/ID0-d(zUK"lU{j2sز-a\Q ԅҔk :9D&[R~њ'VO )hjoVZj2::ƪIй*#4a3 -J9׋=ejaMp8B;ĵ^\AOQ_/ 9EmcEQkLں[AYiC_kˣ҂ӲKT:rfZgm=(Pĺ9.{E_חNߘ(,޹pfȝOƞ}C0_~&/l턖91  bX% _`xxBD;U}r4\W$]& 4ەAyɓౄ,|^\ޓɉIXu{uWW?܏ v}˟;dbc#<e/0izFXqz@:pG;ǐKP0ccHM݇461q}.^ DX^>Mȥ'adi %RmZ-v3kALaG>召-\q#03c'U83x-3coP|$)#:'xYک>G@pGHNi#Q mezU\!ecS: P[ˍ,鼉~}LyZ]bl%8Wd`z]Ju %Uw0NXܓ-_)cӹT\MÕC1>2B,@hZ,^3#4 !%kZzѪM>4GGu2CG>Qj'1J],xuX-I.v5>q\+I&N#;.!$|Ś7jtZ ҾPɬ62O[Pr8bE;ĺr*p?_؍*RY6cSKjͼQgHr}WDC[OonܻFDOP02j!R_KaA)#\:!2}ւhu2}ֶ6>bVyhj 7bmֆB{>Po7+NG@pGWꊐ|S cALx:vJR1u2ÂL42lHP3 eKq:)ChuM,]nCE36f)6V\?L?zImgzr# n5YGq8w4 Ⱦqz!HJw`պGHDV(N ]:&^ z®Z-IO,]]IjKq J- PO+@WC'XZҒo#YY߼KGabg7^O-6Jnn`ڻtMOLpĆw4mTT߮&>;m~\E-ެ ҋuMD%H/PnMX{Cmىn@ү;Z{} &Gv/.0]W-0=-H?160CK $U#>fLe$">L Rw}q,'|FGeP3A\^LÜ ZXu?q:!I?zx,\C3?hfZ{y`zꑤ ih `rb)iY ;rF= X^݊֊f늅Q\A|~#75AO^8' 4So[ 2vY#z3u r5; h,X07fw4'K h&mfjrA?ȗ^`; (;MgEH=2wLAO]3qښ)S> `d +7PCgGM1ô|!&6SE~^@IDAT}r53[#kF$@T'~J8M,k< ե9p3 qprU_nRn3Thpr'Y5KFڎ]znXh.F[S5S?(F bZzsMcOZx 'T6o @HQ|5Ak8J_@CfXfIȀ,a`#Lߎ@kC|[䦰Cxp/@ySygPYSF3sGK}`䪞 mh.X+zyϪb9`VFuY@${2=}c;J 9wpINr G>zXĪH}}΅sWa*,=ޚzpG|8J@xIDmA1R8a$p,~}bVXH&x_fv'>s2 YO 6!E( X8y(_ vlW+M'>QM-!׈;ĸj\gE(f;O_A nhB]N3%hpJP%:Мz8@V0[膫(=qQpJFC=L Ѡ+Thmd-?ARZ |BoG7HBlkDŵL6'fP(r,2\Rf`y;o"x]#Ud~t0usQ[nMĚ''q.f3Y &I 'QNFlMM> .0}%wHv wϰ7uptD3%C兗 I:x#LL +֫# r; /Zc6 --ЧB dijfsz)E'mVT vH9KmYk}ʌ2f\L189SN'X y.^e0ܚgS[3<38iCp> Kc+%<&JFSӴH{4!'w4wfqr CL)._9h,`,t*OWB6G@eo}N\`?9 ~QJI /?p{ajh*'/4_$*TT] DڝPZ{[/{g/yWq\}z.$ $n؉/}c'(w$$zw}3sz3s{wgg޲gOilmz4xdCi>8a  # ?f נT#ڀ]8"`=;/LArbU^ phn^Qo)dhr0D(@Ku zzzhHd;s%| go2X(ċ<{kYBsʖGa9(TR iܸtDU 'zRU !ݞW4Zb`N}gKӡCDx Sk̝rװNc#@񣣉 "PJiha2}u9E(qƃsX i׍ni/m@;ED;#I%0,L% 4Epu꼮/M!\XUpmF~JaK0k|>yROށԋk'58 jCM Ⱦ2LBeW`e4ԅ_&p>ixfH"SeBr! w3<0I3g BI*ǧ/p~ZJ=\EiƖ;[w^Vq@JLYB#ې(1 I"@ |3m%OG~41-bW>%T٨FyToCH;BM ʐ(Y_L<+(]m]pCo<DKM3 Y^KT'+Kt'a 1GRsB̊ ~9|&4HyAW>jPS򺒲R{ǰtteBm| ERd!Ddha\ 7a!)y &\,9qU|<ޞ.3]!`VP*o3';;۫MǮ6vlXHvt[C~{U都~Cv38בNO%k.Bwk5j~>p JW qϲ7,SoO7|OrԈ R"@줴2%?(/$4 ^{ 2L} !1a`Bori \7;+S<6̀9dz!?Y;kdtcIܜS Tj nމJWCEv*J%!0bFڗY>ktwu`oFN*D(P &&~GOOM-av#-'=}Y4cv9*m+kTR'Oű5V  `?cӯ>GGsLԛwc^;4EQ]=]5)OpitkXlLyg49&_5HߵW(ro ԦLgc˝eߗB_'Dޥ4ӏ? KpOzH /!*L^:ek]3PzHt/7=FAk<̔R%oC# @v Cvj?i\G< tvv^Zi[u[MƍvaVq{?؊<.Vj_?Gyv0PGȽlnMA5/v,hFL& lbѩ %W^"@|A>kE" ɟ~"'zܻ[U#scI g>z~B_gџb䔂0ux->ظ1/]/bߠtw”u\߅^K﬙0bf}Ie($ DF"@nG كiϮ9+kӦ*ǽlߟ@z)l|Cfp)!_|O}H,_ix9U?7'>2 2uj WDu.sʒ#DH:$ !Ƽ?B߅35"G4B'0i$L_;Ox|8~4VK\67/nC{cXGw$o!Gss3*Q 9`}=ݸt"AYD CRV D@cx cn@ogLl04&PN\>f<#v-;gwe]UK2"iSwfcXhjǎ?o8M@ :$n?C3xw~TAnytex0@W[>{wv*b@>%J>x%0=Y]-`s=fɓ@Т=`:B("@ AG"auEH۶SHr"𻪚jCKy;&^S|p-u%ߚOwvz_}Y3|ky$K7\C4UOmĎE_oݾ25|<<^vhJZ|k7n&zy>YVxO!e @_?+e}ݽ0c]^; XY/FH7#qP0 N"@ 4qh 4Qli̯ɂ@yz&rN}^X9i\!#s/Vx_OGqlCʳk?fw:m/Co,yi%˴o4^شb6TT`d {_%A ]GO%֊Xk^n>:EneO@?=,R|#$P+Ek5Bt#芧?*/3o{?xe~$th{g'??c%|(Ԉ SpV;NGG3BH4nBH߸BzN֢::pUHָNFݑ:\M`pprv'{b}̈]hUlGVQq@ѥ驿g{c>@fe'{Hk=P?;ct4V4`6qo!5<}݁w.8Y\]X cvv0^cX+SHz:ĸ᱘9M?`w&_pG~k<fE,ŝM`24L \% s ǎ v1&axDϿ})4uFFJ&xN llz|uƽ& jDΖ1H_z7fG*O>b?xVQF埫(Ac'@Ow'N &&X܂&d>zzp7sy-̰px8 ڣ6t6[[ y3ApB:;x70PjrTq!7 D@aJvi3[ ag^ƣTGV_9'l_`aFY+u #XUg:``LbOH`9{lҒ7Ă $8SYM8=.3nHg񉞸IiQ Cè) ^wpPTИ_te+/kɓ?ϋ׀4H 䱰 XG_~#="p*QZq =2XT߾n2" |u`$zmj~nL8*$nuU>[{;UCpA`&VС -:ظ!~ZXjS [ כ= b'w*$,x<.$Fφeo1P؄'t #ːq/D<!)2IK?o&Xrt(]}}y{&75dhЂ5C>t=u9FRW2]x'b,@4{ԤI~%Ks].neTug[҂ SP#~P.X+Cj7Ϡ$-4Cb؄sBKw':X{iF &[adaΌ ?-i"~"0^:[pd'(OCZR xMA @gg+ Ū0A̵psMSР)beeYB*+['QCBR*OU99vSPPG҄jDΉ,|ks)Op>&&ljFF?yNEvs:}S9"QŹpt'h`D^.9K2pނ`n Z ɭ"C͹hÇ?bd3>eȓժXЅ&r*{ 4xa qy|ԎnvQCRLJ,2Z-5M(Pb55" ^|.SBF;nm34DK?26&b1Xe6<-K.1@{ $V IdJ? o HGUIN040Ƃi-tcҁ'@3܌Q1+%M<61>aB_O||f ,+ԚC+܁o]+5I!x<0*0704ߠ1O1+Ӟf7Wʆގ.BsYaԢ/k3pzp\?^4Vnt!v=Ἦ,b ?d!!]LL(pgQJ 臅3|<@ j,)M ,"9;!58pa9 D|y3VmxI2aXFtqWn`f`!6~O݃?y%? aCGgS3N|!}tty՗B3Ù%6|<1}t%/3W#XR>P%d\$, Dfv't62?ZuwgBc[*yIPx&==.\ǂ/B<LlT@#T(8^O1`,^[WJXkU$bwgpOd%z{$DFaڴpDZ_n;%zZ1Ok155}"i~IzhBo0sͣOv4!y'BpL/gu&{yQ6܀&1o4}EXXJX24HyuH6"@8a%[<ֻY_#g1tש; mW as;Ws^:QnΓ5z-^\ahK n 5q3p^(&lO"N#<,3MWWaq',Z%|MBwOp,(z T!%|lnp/L(zWBAQ)ll\pߚ_ŋPb6*.{ ށtQ8ދf!xnx/#\Ȝl$H$%Å]BӞ Z+j.>1y )5lOg*J2K˶]߸W}B} k7rͿ^]V%32ČߜYjpXMH؃9=uOAzb7{boMd.g݇9 R 5uR> X0Pc64tuu]WWCCCqBVyysfI[:;[qp89>9LmdEߴ9ZQ(41% ]7v4, /~6|Diydl>7~d>cd5mmhfxƖFby1\] ̨sgk h.‰>FCAs]s{; HCSތ _&r=0ЬRҶZQPeoE,ᣕԔ.--u8 Tka">Z[,p!++ }N:% xW>"q/\^x> {KǟG_kf[E[EIK<$]}B- +E (^{~aw Pj/(LFeEș,"4.zK-E eC/ ܰZ²hH g` **\MHyʳrl="D%brߵLɓ$Sv}]_.?F MQQK=u3@u%ҥK8~k $Vr.+],5"0*CCGG~?ᅬ^z ?,|?wyIi[9d0MxǧCTrTnu>i8+rlG6TI[x8p #c~:v矷09k*ԓ[@WH3kxyck Y&6⿕ XFMljA򛟣<%SLo`y{v^$ ,q$u6VQS mp:8;I E+I= S>f&V̋>{)B?Rb|{o6$Xz5+ax:zznMαg ̅Kp-ƹ;QV)*YX;HW$/C&bpYf Kg'Y@BO<[O3h.o\g~3 ?ID3dLtw<DX吆˞ܰZrӰMnjx`" *#͍ °`klnQ%i> &m~13u'ZZ} +mI(e?X`@#8ʲ1::p7u% )z<*iQdQQW ̙Dg?J~ǟgr$J)RRv!5mx,~ 0"FIN.i'U!nrr4=Ae^JUW(?LDӼ0#f(" Y8:;plC}Ȓ!̛ O76p`Ȩ` l،Ò:@_w~sx,iu#*Bm2Dc#K.Yť] Fc8>Vtd$Y M0oIJƅ{Y6\HW7,̏^ZB)jkkY&x{ߘ!oFbR>7< ewlڴ < tj&35տ ǁ|Pk=DUs02*w^\Tnyc\UKh0|ʅ.[y#ZD<;#kG"܎a +i&C?3 pcnbkEw`u,t>D ›0}z-!\#Y]{щ#߷i|}}ub\Xu榵Ɨ6G}}9 傽gׂPY&s*/>7S'ӟ_tܬ !!!\u6z1q<<أc^+#+$ /į~~#K:qRJs䴓ng`IH}vFꭶSY 9<zCpT}+D?~"_:VJm? 3b7h)*8׻qB<ŋcwM8ۺU>Eˡ/lv RwꭘkTthe߽gŒ3%X8qr3D͘] DLJhRP뛩t | x?ջAAA“9}Xڽ,@IDATYJ$y5~g'5g}fl<=NӓED3~ڪ]#uӡ$rHoz~ͿpaHV1F|ͮ^7>HH*okU\Tyzy`YkϏ<|}jcNF8E3MktikiAP^-ںbg`gvۍ5ﲛ}LQx2f)m,lCqQ+y <\هyD++<*?C~~>y<0GҸaۏ_xZ <='c-+Ȓ_…Įz j@>S;[k {Q%@? /'#!O TZjQ|%lQ+=n&Coo~r+_j$ 4%%Ey%#)(?BqTq#j*.#׉d5ǸBV}W$+͘7663zz5z*K<9|!f̸KNgnd00Py(s#*ݭZpz\!z>qG'K2bCРM P\߁䖿zs&'' 5kְLJ%HeݵErNog1tџ1mj3S-Zs}xKZjCkU9벻J6~36v(:z%']'S[a h2_$gyZ47T'DΪT*`bb6PӛNDoK 'N|K9ɢN_g-8yS#64po'|m3 C(@MYڑsP*dr~S:xUeyUԢ6c{ZAQ:A|QҞ'}5 cCUht E`Ć.%KyfvQkkՅdgg's9*J\]ꫯz&p/+V`ժUxnOA HNފDT_be09Kop< )0RPS80Q"0˪3.Gkew1cS`0.xFGJ~GyϲXD_O M7}bC~IT)F'BDMȠGHM+< !]KFehXv-lقxD,~_t)[Xz5+2  cXȎf$l}%,b``HڥCPy1[x5A֟&pVK'x1=abey>zL z֡D\8*FQG[G[Z`ae'{ݺ:ڄ'CkS-0Ċ? K4HM}kpy$֪<l?s-,U꓄fR"Q&O,+w|<9dQQ "t[0g"i8%,+w) i٨gem˷.0Abpq2ZFY:jCÐv<#5"p+AAl`FB%AYEVIX:0N$O |R$J΃dž[g^1%Ա$9 }W76Ϸ: <,5&2MF*' ի:dhq>Ԓ'_h=<*H" c U#EwsMxpa\pq?@[(3'P*V F&fZ{^i.a|G3EV:UXG Ƣy亦r rX%xDO]wPt'}!`[8M-%MeKp⃏Qx4"^S;*M:~J*E)J^ZUZgN̸0S#;<%?(%5| ]D9]; CxKzH ˥rt(IB܏@HՕoj;pؙq$@qICݙ#K_ <CI^:6;_<< "ݶӶ@{}BIH:H@{mgXX~v>^aIGؒ 7HߵWls[+eoe勪,d[NBnFXV!Iq23g#eq![nVKt }"c03<^"5 %01Ċ?7 ة(5sϣd>jpQnR&!5\;  xBnކK duMn$PtNmW}Ξ 9O.ZyFҾ13Qv>]]8kK.Kf,n-O=B^gg?̙D2:uww C^~X.7G_Hhe~ k*B[[G 9#, /Yq) Wţ&b#c>q8B$0=8F&| 1O=00NSZaX_{PPss~k+cs VUhkie)ɨ8^EEhkka~0b%MfMѥy@E%V 7H- 66101FBU IG"p[5GbdU nKv-2]I:h(Ů7~ NLbULTښ$- ps r$ڊ; nd3kddd*HnhPb*"p]?^n-Gu*:|rˋ ]HBd?ιUWNM`ڍ]Abk?7Fzr%/'ߵ .!»g~ }z[XjVqm u£aסQ}N} -kle+D';OͼTjh GtAEYBٰ˵BUTɪBJrp?@=hj}5dl' XȠ=5%m0C hrwTŔ\Gi_ :,8^d< TeXsQv(ik [9G'{^ Ore1e:95"Nzp>Uo͘U@Us3dlMOr :J6>bX WuLOs CÈpQg"pgMU(IjEwFʛU3%eq3[ܴ \f:]MHzs{1 Ԉ& GNfyA~- eӑw/aCOC秩=O3>ҔM) f8Rq 񝆘e,/$eCn3(g-IPՋdٮ])&N 'ZvFL'vFSq._x]߇^tÇVddP+@c VGcƣB@u5_pNv1,76VRjYGXqՕ>Ǿ}#)} XU 22n9J`hЪ&e'Tq ]x)AcӦN4bɏo~4jp&yI@"0R}=8p L1{ݣ%)bq /.Pq)1=s[{)|KB'z#[U̔AAvkph[jP~/LcՑW.hPڒf p K|wQ]h:W,Oj P3$G>_#w!#!lj_Ϊ L^-i믲4zh೵J(OCoO7'~cٳu߅AOWĊya< pYHUNS]tgZ.uV+.T઎N%Q1otQD@B:ꛐ-;cUUEТZ% Q@@GOSCY̏^߀o>BMa.+n #P=Ґy1tܓS}fc`CͿMOu;ع_]DGk yyNGڧȣA;֙Ttp7FSOWRô9I!KXdMhL# RQ䷿gW wqddK* ^UՁWr6;e*U]Mt[f47W_12Dýqd$ @ t"q"0ݗN&VLx^8T0 oЇ>NQqNAںz'TjB}iP/|RIzE)$x%fjkХ;}V mDȘO>gN /4d$:P24(mEI24{}nuN8 JN4>ʴ,dlڏ+YP'1P#D@Yx nh,A{k#L)V!$@ybhn>]n'liFn)DҌ Nr"!3"0T O@S=K6(b"zU, #AUiNA2YhR}](ߣKX=r24 MGAݍ+Y>}v9I::"gI@_Fğ2h|[[#v'ΞUru)Ydh,23=z/ 6WTj~Jњ+J WcU$a抸~eڕ |D̃(Kdǫ.ȑnAsAGOIO$,o6n,!&@Y/r7¢Eٝ7U Μ5-SEHTy38oMi8WC]dޓח$ 8C3SY^xzߑ[ qxf>WjD/{/|W;PUѡ<v(x~|'Ѭ\)FL;ʱ4Pc y6 uu%"'ٴXXٌaT:H6Z-YtE ':I 7Pt2Q0YI#@SI%}U;OV7hF"'joE↷3\ia ,ɡwtvbXu^X0½ŴЅ3ԈR ٭ԕU^s<3a̽DK fP</d" 8 탅#X# H "pQχ[nH{8 ȫA+S\=JJ2 'ހgn+U?PF;G Wc_C} |C&>>@h !zp CC)5(XZ5.$J b73 Zdhڥ==]hh(;KFzٮJ%F!HCGV'9T)7Dڡ>N0D@PFhhGoȘ+3 Im6#G%077&@J \raa^v nJ) QkcD@} N? 8/? ڰRo"@nI+3i4 Ԉ  @ Ls@oo *ήG[{݂`(+sj|WY CgJӹDwO`an}!m] ۱J-u'DK|OLn\CM5D+"< '@3UHK` SsKZp719: K tج-A EOrk6t k'@Ϗ e2 =v_^ jDuXǂIibhXԈ $@W6Kc@'R8zLZf;]kZ#LL ég[&xKNDObyY!0P.:Ά6 bGmM !P|8ƆxP늍O + FJ r]9=RYV6񸯫I… fgߪS DQ#D@; \%0m'TNz#]*{5 h+)j>zB~Ae[C Vm-HPv\xFo֖_HGD@ PA].ON05*idL/śdZ!f6֫F}X 7Pa5܇g.>O?B V=GetWCO7!N6&r3٨rC#0hn)O|Ji%\000DrD%K\h4&Z Bc{ W_] (f{HY]i\"@T#7]s ҟ| vnZ7=(^1LMiH~QXH'aXog3ig[nT'*Ǡg"066g_C[{jo])ɞ*I7$ 4h6͵, $+sw2%veI#b+;GRp"E rqcdBXزq{gx4q7VY̫讪Ws`R$hQLa N*53.e4p1:t|) V[2=qaVT텑t[ċl?hп5P?&"){rWmBR "@ (&&pchVZc_Ty̬mWbcTnݵi 6vBO{ ^p bɿ63{zs'z:T/zDU)ʳt!7s|v"GJ} .,0Ѡ4j/Dװt[H*bFj\>D" AClmֹc/c304D<Ӓ F#oTT0[d8y+!z1;FrɟP \p=A/N{.LPfI/]#"@n@$C.c,PL.صh(f'ŏ6Ѱ &Py2nxF:V.vADmˎ~.n\CKA1z!u#WO,ETnWCOx:`xE&Җ-OLi?]Q>Bd%oEbVIH\:}& z:;E&Arލ?ҖTRr D"@V@`rx_7}=dajζ@ag~>ffOG=".Mj͌e(xZQנc8sxWl,CS׼4.%ѠK)c_K18)CŹ VC&^gjrrξHzI[HUȃ[vÚm|䷘]Sv*m4G=T1>:( ^j<4f# 4|i4]z4_.,̋!7[&":hX7ꥇo6NTN.9}- Bh+/aիLb9?ғ123EK hW9t@ #Gys !,rJzGCfvp 57< 4=m쪓|wڂED@ PAW|^GUf:]8)L\sB>jsnEu73BڍDC.4 pJ#pO0*B4="ݍ%FyY/gDv=6CW hd4Ll'D`%(аZtfiKn*635wX?/'%okL)fė"30>_<2P&u}Fڸaӓ/G6aD@nb=L m  V nq] b[HA'@_cP+NxZ'`lF⮠엪##zm XB)O&]D@ p;PO{CӶa2P#D@utTWyKyp!'pg*^f_.5"@VG`fr G]51 {aQz"pO|44aF1 c5xFɶOPB' @XFrBK[Ȩ@MiƤN+G,jjm> X$$S i"'">Q"[hvF"GmPdn,A dTA" -hz5%XqZ&'u0f&aag瞒(YԌ |?„ysG~Ɠϊr ~ :2NY操D@xWT sNCmutuɞ9@Pay,='uB.gP#V-C0M} Ms(s,=vHG#0#56 ~Hgr-!LJJ >E+191 s gO cd6֎rEv"LhX&(:M )֌ܳaxk~sZ{OV|0*u%D&# y~Y֭CԎyjA@ BZXaUjGd6[a3s+L < *GL@䖄 qLO)ϥXf*`NVR?'S`߄v>uǦl d0Z mVZ//PrA};?CC%%Q}Bݕ4pήzG BK"jTv訣TʾlZ;JeocF Kjߧel2X8aϾǶK] 2& ZzwZ]Կ':nӒ=r>:+\OzazO{v:O\hc966(Xk&@^~r~9z;iv.㾰0/ljv޿9Z\-:ND,2Vq8;nCH{y-7jvn-hiGSitvF|E]UWx BS_s%%Pс_hbY Rluu,/@RAKD6Ɉb!(&v׎4+ D@Ɛu50Q[#0?j>!".덷TPYE[M<Y^-J)!$J6~[u'P[ w[_ ~ؤKe d PF에P7ޛ'uOE"@t5vrho 뤣2t۟ma1Ҷ >g3*p%<Ӌs}vB|; 2,yPd8sjڢ|kzfSs1fg024FgW_X1 4u'ppى^F +#+L,?< ,V6vF&Hgݯ.揸vLO}~9LP˶H} KR}`&%cc@X 4bzz\AJ6OccmH)٢(РO߃>OY"@ t2M\~g:S| /s\‰4nT&~.ODgY!~CTudhzD|T$+Z!̡mRYC( $J`QӇ.(J'D@pAՌ?YeO蚋:鏹-v}$?$\d ,U)&'Ч^V?fHno ?&'KPh 7X%yMYCf D@H RGƫ $n4> Eojz OD&dią"_@{ >vv7+3wnf7>/\[oqپ)fgbBCRtƞ_6M&4F&uѠ+Lz:D'8jԑ#]SSg1;oAmYY}DncUB֭Co]E]5]hiÉ O)͸Vێ'.~_@*42ڏ1mPT0@o"p=PLMW D(&&`y"ΠD? ϰhd;d~meH}IZYɝ;o.T=>7wH/@Pԕ堽 #^9պ"VvpsY?ԉ@י3D@lfְbOԈ D.`djOJ75" r?y9h/`H{8y ]Ub!\=˰BOp[XWGLvy(JCA%M(РJ>b 6dbp5"*MJ΢ n6nx)X;8K^2lVAGocbhە$av o !L r&GU>ȀD߸Z 4cpKJ&bX9 4#S+GP֓%ntWYxŸrی>Or مbdoݼC:щ?@K?~PW1NN"$Rt7HĪanc_GHaFdYcAiĄ2bdh [WA]$@]\UI5'039c.K)rffg?wѩJqD2;C"RѽU.#"vDmۧ4X9`_d_xjkޖ\x135 +Ԉ)=mh)sȜ4RcdvnssTq sD`(аJpԍ!Ô3_=>Wr4RltIDcffQ )O?d٠&[!XZ&9$BAK]JZmDžXaz ֶGLv۠Y'3^Uhf,̭579Dtt|="@L+~ f L />=܉^!w!0҉_vqkH_~Vw%Z q1=:&NٌT~ΐD@ 4_avzvNV3ShVn'KLدUKO)&)аD Pa i"@dH`)}z`_ICp@IDATuy,ad[f t Rwfd *Oc+"Vk@"<\W_cV1 p2/ *:)FQf3h\T 6 \!`(@o"@@MF]d)ؗuzdM7; L گBgQ ^y.w;)נ,Sg"@$J FupAKk;Y;ہƪbͰN:X|~ LOg4P#D@u(Р:4 '0Ҽy E%R+WGlsp dT \+bt\m!xaD&..xAMۺ !Y[ɂ e­ :~ +[Sfppv7c'xa HT!D@KˑuML(˖l߀M'$H`fr}ׅuf6³k3i~v*XCvo_/_I.7~93Gdl6`_'* \A g!GzYFmXdBD@(Р 4 %G`.^6XY <xLI@ou#yc]*%q1H;4R{j "K[qXo=MAi=:"5Ue|w.k8Rf3,q_S[O,q9=M['4n@n+yE#0Ҋ &8%xņ!OQsQi!tRR J Gu8y'\wєws?WF|U?`~nΞ,CYhtP+8)h3ܺ"=A Rc"vhX;C (? >]\D؁mIՔ 22.z_sJ/6ظܥ_Cf*K:-YO@ky2;O@+A]YibVFbK:g PAi<˔֭Sm3S06O&zX->A8}>R˾V(poޅW"}xO  2O3--v͐'fZ67ʌk46>9*?"@TC@w{Bz㶊bz 8\[})C–4ńr1DA3N`{*=A2Ts@t}y?"S&G¬ɠ {8:z!0@Z >g?6Lc@:J "QOC3aAԭsX 9>gCccO#]+[y쮩/z cW&G`jfݏxԖ͵hR.aU CC#ϡA[h^"sۮs!e<…vr%lN2uY`2Hp:Ŷ,#SOAosQW`c|.j{k$\P O͵LqӶNVƗ *!@`At@PV|kYfH3APrX2&8Q#D,P{z.AF"eUQ%ذx+ohˬ0<C<-35w8vYIDܛ͈c~q";ZV\Z=Xg`>4*IvX6ae [{C",My?878*;O]'MM >*j+lm];R::1^uaq^@^-79R&f  rV]k[;?^Qz,x*45"@r*)a9"U8G`trEj s(&DEnϽ s8O܋NG Dg1d}Gu9dC"r^a+On+.d( %]S/ p%}.NB`_o׬T ÞG #Ӥ6߅+„055צ9˞{1t͌N$w#`t5"@(, 8i[eCkyxVw\,&#~L  HR{@3;Bљ\HP$m}.w_eRJ(||"Q^&*`D""zhX=;'  r 4ھ_zU] PRד-IVEV`ba UAP=?lEI$oyH뢏zP2TZLR>Ӽ/s9FyPy[זJ Pa|$O@CoS=`$ N>p CW}j8CYIGFγ֭[=Chf"@e)T_Y񜝣7?ī\]𾰉oCX )#b)݃^@2Pa"+ (,S@.-j>h(@K+|b:I$A LLIuXqM'w&0;@Wmv\i%IHѩAO1ɂHM֝+>̥2@e.%LdLPA& Efjڤ]cV8{Hl]1-OPat&0;zpg-(Lg$Lj L mLWffn}XE^|^*yȽm,bJ04T iO 4FPauܨ0ZaH&f<÷.J_"v@ЦU&n#={ad`b<}m0YGdN`rt4eͶ76C#xU^n[jϿFar(gM,֏ pMsD,lj"D=!DC 4fao{n域CƟ^ǬB!gxBOV$#096FzCZ32(#$\ /+_%aÆ`epK:>` O YA&cjY"@ZUbOY/i=R}ʬkE@S31u'_ʕ-٭FXp =_aljw tt]tӊ(ve17ts @j#pur{09'6r3%#@- D &0?;wNlтژiS4:2Qz꬘3fp 4:Dw2D uw|5;Xlpg'7oI[ qSikkGlxҪ|hJK10.߼t> @ "D!0څ_6Kp 6<ubb!+<>#& %7XƯW)061WV`Y n>q/&mLVMB81aކE{! W?08غdH$Dd  D@+(xS)fqG}{de`5|.`djO>!QL.g"sK/"D|e{T\e?^}+<<Izg]8Ewwp%!b.D>I@$ DM_ 1 6}5i檿ۆl 3= VVŒɦfK@Pd2^dåx051*HM}rt6)<]}"@G Dg"@h)߾1Qvۏ;&:G'&" !U"VKoS.4C11D!xp; .pǦ&PY`fzRj i, Zڪ 8l]]gK;(Р4+ Dh4sbf3kk=4b4ln,l33f&$0& EXp:x_HdKO.̃v߸ڲ*35̬taec6݂ۍ#D`(а Ht  Dț@_m3F;{#1Q30c+փI#abnt"{Fzc> 0bj"3 +pj Q ]_XX 2rv\'|C=o('m׈PaUب* /rlu)ggj:MVM`0?; Cc$>0¶_P{'?;)8FnU3D@6Q%Ny&♝>suvN G"Qh(nV (*>? p):q~CRF{w/4Za73Lzd?Gغ鞓?[Wy`=" JD_K# (BS-z1;=5ǬX@!0< AQp`]lUEYv=!f?#*r kp%m ѻu..9DJ Zů So{EIqwe%x0mlew67]h-,8NUXg`A.Xe"z~&^GUqE`jlT{k1ػRn^AZD]_ҹG^I"ԛh-Y;;tos22"5v#9M]ikںKk}<%4$7>4nǤ?_*I/&'6PAtiV.)+/,!xc_P5P 98DUpOS#D@ L.2r^Wr C"0>8,yݙ)\<Tp`7 LA_Q GqYxgjj-b%u幔%%1!Hޱt "@UaN+%Q˶JA?,>Ss˕4L5G/_D%, r[A@Źl,̭mAJ 1a^Y<*`" ,L)=:Qm RE[QU*lZ\Wl ]:݊KP@m۞ryLƍS¯plۯs>CD@j( 1{搗9`-iTYzS.yhuz}F_c;ԈQIT_" ߼S[tt[< Pao3sb+[?.c?[ck{a2dB(XP12|+ m#lxZ`^XDw4&=<4 N;//_E sRjß%1rGX9;'Qk5l_ Nv T Um"T+1%N_ ,X An~ѣv w#l8;w<$Yv].ț{(v<‚ d, 4HvimXY\1fz¦M`#&X/ {ڙS[V7~b ut]o6o$0LWmm΂ Fd$$ Up}LzJ̾];ƪfR3&f6$deå-nMM%x= CFfPA3f\ZZ˅϶.,E8 w@򊈅\v_zNnD܃ 6=pe.\' mM_A,KX+*_?Ɗ\qxys{{5}2/OFzxYz 4h>L#Ǘ1nX&| F"Wβ]=z.mr 9{ti">N'ZbrU) ]e!mY`!Y1=gPgSMYCogӒ.ξ 8`Tp6Y8ڹ-iP#D@(РY:94D]b&Û6,|cu_7V7:*~qI(>Pξlxq9O]prcN Glݣ .IƇUB{e8/gniD\pa>Ԉ*amy 0@ҐD|xz.=me2*`k[\߱D@+(Р3iOO.\8^w`2X-]m\ߝ@{, \<|.}z Wst.``hV G%ɯhlCޟއ5RSrz:+ XvUDpg0LO- iljxwP.TB`F1L~ѡcC\gg#ADGS1 ΁_fea۞#D@(Рy:1#*,<'7C&D$l 儱RTek(x8L--v $͙7[Y? KO&~Y!:1gO^Ѻm^cM궗[丨cSqӻYi`hX >AT6t o\'ŐJIHEE~ ^B.P?/EW\5X`5"@G c/ۙGFn>p()=4qA 4yёy/ "܁اgxѷ:x07kz]%l| ="MtM2:vbS qA&h(Cod^Θ](LMb|`>%,0m\C詭C'YT?<;*멋MY1('{w XpI?ʼuVVI}..Z|D#: /i̷EF$֗ߊ+Hԕ0CeKچ-058.2#ԗm% ;k' BjPA5u~ទy N3f〭g)]΋oivvn@nv+'G?@s(s 6 ?Ibbyrv/="Fg--=fpGh)!kR1Ux?FAQ<6. oe؝KnUZrrcc() &:Eyl"溫 BTt: 4Hj9iL[[.]:aa ڸIJr*+}:.uo/<#Ñ#_Jʘv309OE=}F(> ³ë/U]fUm,*^1VY $3bFEBhVnۻ# Jd%ʙ v Ly aTDP5 A#%@鮍-ANqEƘ0eO 1GRA |q'?B tV)})Yuw]|"%9ωFH~1ow[3b|(Rx0RZ 0;=4 t}Ykt@TI`aaՅ(z-KCFSM*r XĉW  0L%.t@L@Ĥ.@o[% jCf;Ѡ/)<(dSŕAҐ023s[d1 )/؝a,kVkpgZX ޢ[0:կ^lh.oJށQ0d$uEmu02س4w`-PxmAtt,>{& >1D<_WyƬK/zΧ,MmV|'ny1vѝ& FÝhRꑷV\/L()Ny 99 }`߻Ѣ+ij Mb`һu+.DBYD t@TL@1= Ęr)G|89jLPXx}}dz8zfyQjR" h"Hń!\&ە{|ݱtrd p&G k_ v|o6>XW/^D=US/*~dQ\;h/@aufԶ}zwQ>>؏FV10m_y+x# 'ܒX~P ɉQ]Dye(&|+Fx<02Id/V[pС$17tcll͈ *ʗbff Y(fU$&nL@( '&I @XU{PL!;}T\c;>[{N[]ԑ}As3eX`u ںb?/?^"_`h`kd6p[o{ܪpvR)gjeCOg}xScc˸+Yȭ{Xng%ⳟZlʊ,{У&BޕPw압ͭYpa;""6Ô Rc(- (ʛZF?J[+e%"D@wPAwrŞtu3ǣUc jc[^ro%[vgW wwna-JŷDGDe j='ߐ?OZjYQY{VY'{DE ganw[Pu.1?;+VAI^696'QsRIJS3n޼bĺMB g1yvb#vm9:ʂ,{ezjf"*(!i`AjD& 4իyvƍL4/;GX^*^H؊btTc>E]bQ|8H(-[~c7ߑ՚352Ji~}^fnB/`Qy !~ƬKPLъ  H&{_6,`Aq ז}4S|_`vF!,sB¦Lw!I38$d-`Ƚ |)"&f^ނHT-%*0D .Dmj"=h{<4܃Ȳx.ZGzG}]-(x\]_h_]Xoq!=XeZT9)aNO^A*10!˸G=2_}xWEԒ^,]h2rffP}}bRfͷx o-•oRXmo; gs ]] (,:ҥU#6 #CZr  4|nֲ~YH1KۏQ]U.{\>Oh.naCi $PAafmרa}~U-xu U쩟_B兯 ӛpU34FLZ. 0dcU'6dg1 I g4=&C]^6J#Jm=6ev { A/h*@0}DT|yddKm]*HyGS: C z֓crm45oy)bp4dU,_ha(|>a2hhʽ' 5BȖt+[56ӬEtWo)uVqzt\XuF&jTtRxpF+7^t}dbr|}= " n[ =KI@1=kCMK m}>>'RXlCcpY#T' 49.:,fcڣ_YPxˡ$N=eS`:HPA5=E__+γ.1_8v6E_c6)3>Z3?>YZ 0;{5{]p7r}і[W}xQYUaa M۾ϧ'E`uLرZ2dτ LSnz+ |Oh$ny.kk~-&ᚋvl?[[]suLN߂_sҲے{FX$@E:0I[ZT_\} ˂d4¨ʺ Po69͂ n$MF# fH9xG(.3z+Qˣn}}} >ںM @cNlm[)"DGHW@SQ sHpnHfH3hAZ&PYT}HHGٷ N5-J} ^DtH#@y⥣T:8{b/?RS/#]l{z'U輜Wdd'(Рw7q=uWTTJCpF{Uu$JVҕFw&q4*.5u_1CR`fitq@ .4|=GW$myށQ_y~ "051SoX`m$tj92'NcT' :L :uu1(›A+\%ϊz| E]R2oٶ\Scc0^J~"n|)Fںq6nn}-tױE ,%Y`\ pY|Z'|KY! rQ]^rYX!0"0$M9,L'0 [ː}&Ǖ١H(ͤ-og &}0A;aym _ϖ++6 +F&==Lua`Z \f p-(9K!׻j )bv͂xnpC柏`Ӄ ߀ l}t\~m5118=I׀yEuyY(;ui):-wynC(;}N<./ZZ!<}'B7n[mdΠi0,[!*q;"R[ G6-;=,P+zYͭ j,@f p]C駏2qgH:[k02؋w_þ=xJZ=pgZ ''V"2 M,vF,ʃqLPr:oN?6i _-mö^d8dzbBWHKt[[3rsL##[ ԃ \ m[+3k{/=.ϝWdaqf {X2-g7FG3+MWz )e,p]U5K~ٱҔ qwq`]uUK-L1P#"0<нT!+8;y=fB x;V!3U0ҏDs?8L--v0MڎE}M _5F  ?3_9 ϰ(Bc`"Q^ Hf4 ,7 Yp,ZZ,u dՋmDCKY`8# Zl^EQ3Ypkq{Z .,WpazYt; u^bpu;dMc˓+|Jj L,&O`5$ om(TY~*}܎^pDgMEz_*_ b#/Omnvi/\c g1׽W@$登~pXrJ`F1xZXub $@p}GʤH, ⥣l;b}fUOAYlo7*Vf6KSADi<"pv|tg槿#+>K4tUUUwd,)T_\'Gĩ֭ol`p[wymfzJT8ed?,AL\,D^V^bǭ&ٹ) 73lln}5@Jƻ ٙ:ii " y3g0LV&#7Q/2W@hX)1:#6~?F`b FjoVv ى LET]I1܈H&hFQuAULN(90ڐT2 {ࠂ (5 \ NN>_n434Y@̸% -TT&061"ں녕l}+}3 4|3z@p֗" `X;>j~\>[pWhX_?|̫%x363GHoskHyGŕsb%mxfD'&:A_WRYgfpu_ ,I[O!;}1}o,?jDeĴBys";a$r^K 4h_W4:yRA@;aKߗ^Wx PwJNW^P"`[_h:w V`>18aQI;^8ZkG"͵B_a@YZ~'I?/kɎZ\1ffama- $` g-σܿ(f$"_K1)Р1ԪIqY|U9""5xki_36>,D^mՐu54^σ/"P%@WM- ?;جMxi`e*KTĤ;AZ*&X]\t4W2h#/7icimAm]Pr\׵&s΃D(HID,Z_^~ֳHHB@9&cr9's{'`f0\p}k=gI}ep$)RWzk)kxI%GGWڨ5Ähڈ,fxźt*4#WpbNB"8SoggOa]mFmaҙ]YS-̉@|8f"'$i$nѦ9DމcJptvŶ/#BD_wr>S!ʉ\0ǔM_\\<aɔ"n](]-(+nZ,uYA`)u6R6w٣dl A^r: IKĦ†/=upqb ̍OیX\:))PW^"f,F}qn889!f˦(s B"rp*/2mad7̲ {NF%a'^MĎSCu"*KSQ#yĬCB&%J( /QV~<nBI\LS*Q)Rܒ;7[v"3rdcX Vr*J1͂:6::oC x'qIJX'm9ݽFL!{$]H.ox%$m;MD/\&f=x7%X;6s(M #$;>6ik 5%b7h+^t,@>Ԧ]BƖ X]ߘ ^#6z M=ȅtI SK}P_|tUBBT&|<-GZKPGJ:L0\zp11T0@QyȽeM %!yf{3p"/<U@kC.~twCӱמR ¹'VŽR7l4L!A`! eNWɅɯ赛ã8aY7~&ʋ&>!؆@IDAT?_`, 34e(/=-%&Qy089نhL|,3TЈYBiC"2T& ?>X ѠC!.=L `ZuYB4XXz: ׮a5falF`<.s8cᅫXCǴD4Q1j=͂B@ܤeE02(:9&lhzpB=m>l{4=h t\ \;Kr2ȅ pRP?Fa5"V' 0# NuއxT[vsrǾ2"ҔAKL[p;Zr'/>#ٲU8Eؕ!=⊃fF40=8m$)$Z,ݮV:k71>7JگUN- o7^W:YqQA#XԱR']齼 [\pIm ?|xȄ$G&*υ$ sK[&Z*q.(dKl`vS6gzZ{M+m ѠXxTC(nDVuw[t V,Zh iPY4=Vm4 y<U ۔>a,+N4pg?D~{r瑴f,h&[BɄ"株$c4/2V \A6,Q}H{GIځIYP )AֈNWi+9}%`P8 O 2g`1Xu8wMtNK%Ci'v÷x7 @7sf Φz0 a"\g.d smCE0oH߰(x< JE )L46;?FG]Srh(|/ްcs!Gi/pp($1+ zĴPڜjR^m-ʋMF[UD4ezIWSdXW-؊o 8`ydӏ4d?vIL6- 8 I(RDwP1JCF.Fǔ26 gՠ-%#m&.^kZG7/·b\WC޺G>~b{t6(^օ0 M^KiJ/D6zFZZaW 3/c#(vC<܀٢ut/X#ѐ;( c/| aKhyE|8%{/tM33F$$lV#\\<{!_+^1\3Hl8O^2b$*po18o=}԰LlOڣt+[l "Ek;\Y_xMҐyt97UmK!zZpA%E& 0م3UbWe,~.zn޾1®2Ӆ\YI+ba=sqc\,1^ Z>ݏUX-ۘ Yײbhf[O-Y#|eܝ=ċ|`WP:` R'ww?ACI>cl\oX$'M{fa qkl+ڸL8f<z \]ݐcN="evXƯ@?\\=Wg?PZQG ե1b2Fq`؄x^1cѧ-VǏq @`퉻j(]hݱ[wQ~!\=6j{?öهM+KčwT// _y'!sg ^U<x{Rn?Q{9e+{/bSCciھ_UdSF$('r#'9cD Hݴh-q}ek\D:nf-PC)JeeG PH7I Jڭ1߇;W~;7`O/j2~\]Rkղbh,*Fǧ,=gבl:poԁi"".'@; 92PVx=bM$"2^85/% .8S`O}V$8^FOs#0>:tNSrlٹ=@oWʉX(+Fgd*W@pP\ 'AeG7UCQL _NFpq+XaQ֊FK,Z.B4,A9_0[c(_A* !}3XK&rnGww˄RWDȎW`oދiKE)TW5$^KKT8B4h|_M]YS a^$T,:Q)EG߀<\o"ekN4G*FEJMo/ :nT+b)BdGD\=7npsn&D`2[KQ tڻpVvt<8DbJ)t5"lahh4h|:;;IEhAi/~7g_PX 2f-D__*qJd}UeYBl:+1F\읜u7Rv>W/ob%;zݟ'7y5m\I6k)e(!MӬeLtudqGbyHcy0TU?4Ib[3OaS𚇤qi%<_6gamF^ [5K&[o()222 o]/| Lʶ_D\K ՆE3#>aJ//Q(^tVq)/d/r_Si0os ?+wg!4-.p'l7(7wDsQq+O+ܑuI;՜fZյKn\D]efWxߪpg(KD D0@ߤՔ1P&oRi)(ͯD?@imY-? [3Ch@evJ6 =p ͗V>N_)E%VäsJE7J&ډ'wwHOw/wwwׯe&02:>BI}mGWK;HkAfC,hXv9[@20왐/OO1ظ8U ۶mFcc#X21q .p沲;(-MJȵlkHؔp8b:"6 5eQy/"&UwS~L3C}=D:ta<{''E9ý}1 .1A.D`ϴmsr7L&g Frd`hlQ^ -shv?!a e8|'ƸfZ1eGi,GoIzPBf`x7۩̉ۀ];H#:1 #4 U$%> Eck4Uӯ~ǞdAX4ڊ.<ۘ` )i2&ovV^$Ӭ=c#ϬYs@ծfSXFCwO+ښUu}G1I,_π"ws  u0Jk XraoW/U1wOg`o{&ZR82x!Q^N|X}SY)~Ђ l $0gJ2_yBDT`POxzCw0 a HYy4cGb^?8r/.WX1x0,S3F X!r#066']2wd+B28}=zYW$v$AjxewL*u.el.cjٳgQO׿B.KQOS;>NTRG$f40ЭD'yf_"*![bmKӒ $ma4?DFq3 /:aD: br}gW2/(4+t9kL䒋itXA!@ N0%|-woU4_Ճ;70<ЯI߸;~^&tB[svhh>3- ZaB\}w"m "褉&"k_7 ?J%t.ׄSYrPōڸBKNNp??󴂂C A,(Y ݻ@(.E!wiQY|OU~1KـIU4%48Wib~m'֮]t8U|_~j7g8uҲ[>)lX5gCEemh(`oW;D:I,̎Ta!?d\^jfoX Vދ_7c,8EkUn*砣zZa3k!aV ;mg)ѤЬƙ`fr[rhY]Q(iKMN_6) IkmbMp;Y%j|o0HYI$c8.:- !f-&[Xqfm<5s״?_w9u$VGG$'mSU#\J?­#Պ7NAfrwOO"`jQ*s^xh؈)ևUkzfa8և{4J7ϽK^`הػ,}yO0PO)S=5<)>!*SU_yۑOxp e 'ݝBHOS\E ̡Cp1%{䰆+Wy(_wMemwuBf>U٭VpLbNbX\/CpDzhI@{/*gCL4V Ծ݂`BJ#KD^}qif90g ohPPJhJdd"\\`WĤ Gi2,_=䓇,#3(O*~tϡz]B'?֨r"P_X +<.My:p̱=JY \O Qʅ_矫^\ir `8WOXJ\l;ySi ɿ qd>I\آ"cK{}L3U%HK]\ GY>aw s!<0ngOީb9~5䟧qȌ\o eQƯQ:aQkW|_-Kh~_"-33a  .eg6{{ 6 @@@$n"ˈPAEE.N*M&Ť(n" .vپcTP&,Al_=5aggLc;IlF+C,{T߷^ ?J</$>!Q!s!*4 G/6((Aa<&`e҃T@A@XKhzfC([uhXin]kj Re`WE}qvq7 7r,գ/ԌBҖa*Xh裌-Ϡ,Js=Vy2X"k%Ԕ塬 IP P*[w7|BK.B[GJ] J]hϰ'Ҡb; HŜPXbNgFnYx4 fiBXGòsb#D9џ6(VU=P2닧 {tN:y-  |c%㒜~v2ã u(x Un sԿSƈpu)F]\Fգ`G}qttFL:Ri+m}iIDC%e˺|m QA.iq1mD[&o㪆bE2 4y-, {.I t6h 9n`#kAU8jaŘE ݽ|#[DC E+_Oh" O衡לwփH&T\_r\(/!J/*̅7Q6 sWz=4% ~(*fqǽ_Dthb;F)N;;`OAƯ8B48r! DieR]M W챜Ut3ˇ $ ˿%1uG^Dʮ F\Z6 b:"/DkU~X08?PJ.m *,6en3ߛa 1"lf[:Lο]$E2Jن귵tܭիV!\|2E(5 Z emuJϡ3WL,H\8wji c z[pw??s K/7o2c$KGYDWV ]Ms޵D0D"&{Ӧh2ֵJ4p 7ŽFz#q*nsFZgm ;A!LYh0%r-- DFAlUoj*W++SJ ]<1*!ȇvveK;I40nccȹܹ`ttqŖ~ k6Y<)!Q ™lpߤs 㣣(} Πsr;2!# =,#Cyx49cES O>, - yO\:[_FպLnƶj1?͌ fuuD:FUo V"_mhcYWQ}jSm/j:2HR rmmh}^DG7< ~ D(G`&Iɍ*M>EUf!XG-6ו񟠷]5H^[P\rׯ e|/X LB퉒\\!Ft®k6bs0Q}âRv̀hlG/B4]` "&%QCeoJ zJ\ǍƘ=m'W78yL,i*o#O')&>Ov&Zt@O+T/? Nd y%;gwrA_wZX H `a|ppa*F>ՁKU%~ʶ=a/7Z f\ / }#-chxy%F /A-N2u x>}Fビ+y?4p?`q,!,cE ێ2,-x@Oیؔpqoʥ)?|-"nŞ=0a|m6d'iIy'X4):F䃞lk|p&!"m-W6%XfL\ݽR$Yya(*n{幰~ljKTvhŭ"/.HI$Dfr !0N!䚲ʑv+hP!&B@-1L4C}#8:,&+6#&e=ڊ=nL8 Rz`>U vm D$]̫ /1Z3eW;ӽ-ػ#,9k "5G{a#DFnsbwϕ?GMCe?y1者Zi.R 9zY(brnYJ[>=;B%ż 3$DLD䳵# DO!2W;9WPHJ\MM4,Vc%x&&H &$xX`?%bmxE>4עG%1I<"Cxj&<|Eup m5^8QX(7S'$&7jK@<?":m4*X)&adg1婲%&eE91[4~IMq۱.z&!3Ҋ D匕Xj$x&XVp{@]e4 WP("Ӂ#cjj+EBzLnsP@Cwn- J^ Ѡq &/CK0''7 bcw3"$Hg/Q= s'Ĉ=,^\9ڦ2e/< vABѰBJ /AO_|,7} ڹI硡]CӌcLE:$4tald9Px XQWV!:q"B WښjTʮ&ekxx ynn:o3P^~Wսض&Moy4oa.%-| ' Dy5 ^[#(]󔭞4[^ +j4M(g/Rz8{b? ӦJE%Z6G45 p ǚ5!9i+Ki3`h NUXxc`G/)famh0yMA<@}Tt"sI!;"w/"1:φ +Õ`1ooK%8<ߠEe /S3׋14Jo{'N}qA֥"D 9Iۯ&+m3#V\_= ɘGU?z+'~AtjJm[1b[%8}4BF="vn=([yg)MWք,dFnA[mB4Ls cTl/prc!::Sb`&#P[[K+xv%KݤHV_"DJ*mZ3B4ntYֹwQ({|QQ3BlK4p8Pm4`y t|oT@0J#)DJ#,k !ffY[!k1*/Ҳhj*v_"ھ|a9ɹ J||=:WiMeE\M3FbN }6,((g+wcxTv*kGCkaLhȌUK*R`9}t#ҁL>putty|~bҡ~"aOLBHyi2h| τhYWWw#dom[?]jWN6 %wFeh O_4re 0B•tb6=渨 @]iNhXy}k݌0)w_w&g.Mb&ᶊY_IT^N'hУ)KA`aѰ0rTOWJ[R_NāX. !X2|g2!v`/sG@tl`*`[ sKOuѰ;gg('}w 8E{7fuROfmSySL۬>>!E#2||UXBϗ㴅47W(/P }YLphg_H`ڛPVrh҅rQfx/Ap%q+wQi(`5ތl>G)Si*[{7~LgQh,hwrnWGxPѮ/ i AbJa6|af5Rh30|<TH34̞L>׃"(+eݙB/d"t!//8z4,'\wl#PYccè(!sEH$g׾N6VPȅ N`j"|E| —>HHք@o*jCo"$$ްMVj!wy.kdֻNJ}Vv6A^!8~k6 ( aO`5 pU*HQzy\N 2<#Mr1>>:s!-%22M1"*ҁJ;WWwoC Hud @ba2\6ϜǬ`8@ucMi+4V&7(x RB'h%Yt^hxPHtٻ ^j1MxώvkY9e ҡnVEՏp)O yDZ((Z.>N`B,0@"\`8k痴r-J@RfC0mɡ3AD*0A@3%괋!=MPUTNwAw11\؄Dx6\DCee.N߮ / =~U:0?U Ŋd0a.)aؑW2vZzC@0O 튀 "oB x68H7ĐS3VȐ@ggi:Rer ?Wq!CBE&jYu%raj\/@U"ێ֚ZֶZ O<Ă#E:# "ɛay07H͔-"X{IB}tE!_ {B/"46g%0۪ϜIbOA̛@gkG@k,t^= ppps/e͈߈ıZaӆFroBy<*V瀻}puZ@>6Z_71q*@IDATc2^IÃxDF"DMh6=[L$2!1iKJ:6jmD{ܾJo)Wݥ"z.GQXh6!6f.,X+8o6GMM>\߂2lYLVۅ`G}iJc/ihxb` ރ.m 3˜|#(sFEeјd[2\X_:8 +{>7< HLtSm*H)o M:r{"}HTE.!CK+9022𓐨$"52>Cښ^[Nr#T:iO!oQSNbxmժ~$<|l B40R,=TZ 4!XLJnL.Vтbhr>g Me&xHE@t=!1E)[G(&IH^K!^,s5%yd/6x<8 WDg88<`Lg\ɘtGoRzI⻏ڲIc(mY7M:!/B& pKrqk6bs|y4,= a. 2aɩO#bb^ɕnW,li6? u׏D''W p/H؋%**CU&c6 `QUdvXCCX4T PN"NزӔT-gԬƦ2թ"XCuzqN9y`FXO'' h0X,0VA"G=kknp噺"(y+HV}˦@ypo(1ԖʗԨaD8q"W7ۦXYĸ?|b ``h"U" ^݄."!!|ԜNc3"!xwu'  I@8Ds )5!|xx Dx N&ªGU徻{#<&kv $b&քJe:"z8eÆ#X+ L8dB'32w!j'=]})mQxi^1J!s\WN )7@ iQ"xSӯC cHi%gV!00 iHU2 E&XEf꧔,&x#.<ʻo@R7EB;;R\9sԔ鼳<`ݿʛa)9hk%aլAݕooa.%-| '=MHA`JZFC\Auj81"&y~<>6;*[*(ZZTsctSY^W֭6DJO UMTGaewvJGk=| 9H^B+di$2F"L.pV!JEʅuvlO>AfB@6bpJkO2{S!* 2: V ! D mwHC,38d4QzM#4*ωaJY~GջwObTܻmm=g F0h"wӁ]/%g> GppB8-/\-:: Js0By9gUB|fĥn0 /[eA}CsKue5Vp/>òv=pf )-T&Dk4gjq{;qؓzL6HA\"'r;:2omFEckpL E(6n8u$5,tCCCBhcrE,4Snq zMOEQ:I@CHa L Ã()+aUJB|f$.3ۓ=؎=Ph4L.QQص puRf&>ټo x{ʐw~QI5J>/L-;U!ؑ';k9::*܇g{~X5A@KzjQ& Ѱ(`[GUv6((L/DA"t 4L^ 4eWa?d^zzmUh4242ycK:@5^)6yj;{De(!">,X$Ѡ<`Z4A*r-f΀/ 3ς&`&|ïk.1bvHs{:PJo7OFE@)YB4X Vο{EW3OT|/#9iۊuNA^ ,`ׁJ:Z3 {PHD³fthlJKY8j@@zDlϴk>wC#Fe}Lz8 auG'ݒt :}u޷h&.h> +Q^RIB"orԗ3uxvbCS0 RK@`x?ٟ)/!!j%m6U%=*^b&B4ljhi *Mx]4H)DQ: L$d"z;&>wR9o]0x,:g5Yn@^D>SșitDQuwYe5{>VW+ҁ)B*z)w$ E>0#$xS!nx,D2NyF T aq/vp(c.C9^;ܸ>O7r;Ҫ1X©9De}2;p!L\E;ѠKlzir#N%1$;q$a Zf(y+wc.{u˧agF4,3{%t+v"d]]sag qT: L*x@Zgg'(m3PKY,KPP^:52"Uiev[^&p%AN?jv:}ԵOf ~\M1 3>L+u6QvF N.h[ٿEny b3ZY6pz[aMu}7N}qA",Vr +EB4HzK-dq$Df*4PR+Jnq1CCD"+/&x9Wqu o@N :R nngAqGW&XZ:&TkpeBE%"3(;A:|)~HsrrQu%XA,<*,2\֧"~c?KĈRPo$;FJA@H gQdn5d督qrǖ͟Df~XGvR>00H`B+*䑠u%^PKIx()Fe `-N'9Wa~JK/Ύ.s"y.؎5Y4BYd򡽻 tT pB/uA@M黤µVgm^?DY!/Z։ , !%,| =Fcc*BJ82b`y N'X~D"{b:X"EN-N)A1ABtp@n:* &oB|NmFAJ`"MD,4R^r:K}Y412wRW LPY%7PA(k?&ѿMn!l3WJ 69>2RsfRZDB0|dFU{8c7U^$YV޶i ql3"9m !ib Bss%=B1!QHYhsrrRKg,s;qƏ'p /Ȭ}HK_ KFA@ 4bA,E R `1:`sNu( ɻ<*&zz9P3|]NØ"R Nj?k"({:DEe*:.DU tbpf V;07L,o/9x`Zo0<<|r|A}"? 9,>eiZfP ߇Eӡ<^&c KAhX"BC}^!lw {l^?d$nELh s!Vj(V$pHJXv$U +wUiy%a%ѕ ZIX u*30e8S5_y'$x݉cItJ;:t28_O e${:4nKy7< PF`(<L/`kc 2! !XE:Be_ԿgqA!\t9 B~P ^FK#N*'#r8$,S-Kq*/}~)HXhCG P_` $|&# ^Կt̎^t(G('gtvGTӊydŋ:~IIY-YH|\3]Aߊ|;1aX yܴ# ֜,<V8!3n=EQn,@G}(02+L@|}=" U"TPB$>kGFPx EuU+߄Ӈ0{A̙(uVqsR1?j8nsU-e1pr0m $[K L SʃRqTA3_* " Qt.棱{~KoGL̢g'(`n9.::UdrǕ){<_@8_Nڋe]KE!* ;Y P:`AGͮRК\芴U,E5Y5QYu˗}yn0WY#t2 bMf#ވ{T@Tbܘ9j|У zu|gc? >ʫ(]+^Gp0$jpH-, W(+}Jk񝷰 QQY];aKNX٩,;jacP`8< (@ LX gElW!V"-UQnZSPgq *rPHR$3e+dj([~P 0`FP'D$bۧiEMm)2 o'O\f +m|Cl>…acב)D00У:TZ{TEt7GwdZ K PhvCQE+)ո|EU_N@`@7ŕ+DN^sZXOѝzohRV[W1;(@ Wzh(`~عpA_Bee>/  7㖳i-A#E*ujdo!M[Bȭ3((:B ,Ѕdk)@ 0РbS)@ PpyWzefU3kybEFVq>͛8WCCQk$[sW 9j77Wa)@ hG[J PF  ŧP[{ Y|}Cx&J+ZR#*(e+ /<>hBH#ƚ>o)@ LK;%<((@ hAYfش ܵhooMu7[ ` hdApda`y|[vUuZgQVb=(@ Z]?;O PVVVH@_H-pqW(&ܼdj"7G{g&,prVB݋Ξ3#UU G;'4{F P h0Aa(@ P`PVy2%(@ P3ñsݟSUw/K߄M=ʈ"7OGivxs\87\٫14,+X!9,IiBQ4yۃ6OdoO?I(`QNX'm(wk,z`?~ VHYԸLެE#v^؏[Nl (@ P4  uK{# kTB/F P0@()ڦr)ܴ)0,x9cdw.uD#w+XReз {O P|4hԯ !v(@ Y('NEeۅϙ@Sk- Cl D P@ hzFڊFKf"7'nA@b’4q#F_ʯ(@ H ?wsP8cZ̛k2Pt(]񽍵-2"">$Ic(@ XEF''LE󊏡]_FXS>!/W Pt p>Q=M])?.A-"kv^7WNym9dO(@ XE9;cWQ\sgnC|H?]y+`mmcCnQ(0*PXxׯ29j@VU_C B:g40|l"(@X:18%`֌`DM (,EUCV ne(@ P pM /T]ln}/<;Տrj8!3n=uvh>e6| Ei+4ã?Dgk5/(@ P@{%qN,ۜxHy,{qiEB $ 1|l%(@ X҉N`mm--Fl10:zۑW*"U89< (@ P@wB&[h@42ӷ ES(}C8_N'm4&Q,AE->B P@h@lOKOVMז[52~(@ P@בs◢83 k508^-Tkkq䮁ֳ(,]e|U:Ix §޳|8(@ P@} =ߘ&d)ę^Aذ5WÇ~{[̓4 2ﰱe-@èJ_*ٲO+(@ P >b]440@$>pQ^ش|LEݽF{8} G-_ARXiSЈnN<=.psFOOg P!0:fXp=&Nj~ ɱ+h4-p_TۼX3P-RІn E 6"l.B)s՛i.];];a@Pd|_+WKNXZI P҉ (@n>ؒbs.K&65U%%gL@ȵ4j z ?N\-ʋ COZ Äb PM K P\Jnߠ/#899QQd[+<2Q-EP%&'@ܸ(@ \ @$ܱpUqX+^GHqn,]Q V0.$'?Dym:;27 3pGn(U:rl7(@ ;^Z&/ؠJ^=hGWfX7䏶/Mp3@UC>8? QؑA [@ P$`v,Q@VA~8^(8U"QU醎L0(7r K ܵ(*+PGqKYǮF_G(uh(`vޞ~ؾnbն2Q^~ڪݹ[hb5ߢV=~d Qdafg(@ LJ3&Ɲ(@ P%r7Ewo'rGX؂g^9! p 1ڭ3((:aX[ =r BTǧ(@`Lj-(@ eiq pOLV}bfܼ=t Dn 8m*W ^.Ȋo7_( Og PEn(sN}r9Jzj#ܦW:ŌGcȥ*)g(@s৴G PС@/N)TU=2ӶD:0iD`!ҧ](Lj V[`&mON P+@ [F PХ@]mTM?"8˓_.=L.؋Ξ3#UU G;)Dž(` 4}(@ d|V!DĥIDDO7ENj7NrIh ;,^8i$[B Pf+@ F PЏ@(.ZZjT}B .3`&=mC ~[jG6LZfP 0`#Q,\ƍ8WEDk$ǭBR2X[[[xͯ{Eeq! IL2ZQ 0`#vQ,\yX3v`WۅcvD5S9(@ P` 4LT(@ P`;kucF`"/&&PQW܋׭v+U^(@ <`W(@ P&08؏g:("e3c <#z7^TȪ+lQ `AvnivHXyR <|8_7n۟ Q>)wlj@H. -4 +SՔ幛ZkUͪX%YcddD&4(@ O@ׁq>DD_3߃g'w%aiF9)3:}fd'.&f1E}sW$|/(@ PVfNMߙSNٹk]y+Di/);'YbL>yO(<[[[|K5\Y5  !!i/F0֭>~4+$,Ej|϶[W/!9dYx,g3tbGӚ,koo/fh=DR`"nNDvg4LHj_Y3s:PX2qg;mU*(r79HÃ8^tBWVTZeD&Lk;x2%CC8_N? Qd0DĒ(0-0-̓;ɀqnTp2EQ8A_. O* RGpyF|!<<ɗM--"hmSpK&M@k{#A˃u@Tbܜܟhx.8sA@.q8%%2U*4|6 4Е [U9>Bo_z(::mI˾8U~tCDhr022gaAb|ڐCs ȱ(*+kG1,BD,Bq-aa|h 40.({‰GPZ~iʃ2a(@ PR@xҺ?G+ŭ[PWWUo?ܨH]D.Q[[+Uʲ\2*ۉz([~ڟ/(@ [3-:Sw.F_6)7CsF  hxrp^+MNJ\ l{UUuJδ{R~zWS܋/Km^|,Z>Y0Ƴi[3=~l8afC9?t!C]t]jdemH^ 6"f![ojťˇP]}IIa%Ov{n~ YZṲ$qA_VGpwص5̕I6Q8A\YPv׫.0Fr挆qBeL3p)җorIșq0'xj@nU{5n0Y9GhϜѠG@O%LVe0^vFY^( /x%6.硰8Wl+ƶ(@ P0&4{e;2@~I6duەPwYi[ՔY7 (@ N(6ڻZ,Ɩ1Ktfj Ph,`x휰&%q>.w2a#Fr(@ P@%.Ğ184+İT/='kCl5(@ hE83kfBN!4׉j(Sd1n(@ hGOT%kKTݑ)Vxjl)(@ Rp vw')!6cpΣ ]v(`FGq!* V, E6;E P8VHOGld8v"'%ʐm,Gƍ(@T TlP,J@BsmB,,j P f;;u'$D+>_#6"(Sƍ(@ <|Wol 6H\jZ3S&'@4W9@nQT5E6CVvyi?l0(@ Pzڑs~/jUw\nLl >PЫNh\1q Ĭ%/m!krS;(#:`W)@ P(q!!$ _cCP` wNDX-`6w6႘)7 .3%34f.: 2ǒb۠pg P ˲*'區U!X$qF P,a'чbjc[ZIDATԯݳRq Ul&DNc`aw$q#/}_)@ P-gF\JvNF82A P0cL klj)dȜ%@2˓_=1LZc^vN)KEzVV<^^t}[E])Vl^(@ P`"vˣ+(@ Pbh|GfŽ]8URܭ!f8T"3m"&`9Xvn7sƪput3)pQ^[ƖjJۊH˝)@ P(@`2HqAeΊ>ř1<<4cE'0Z mUABˈ\n78 {!LX rDO_>=N_>9(@ P(@g pFódtxd\{"aԷڭ3kwO*G,TæZ_(: 'nAmkZQxEPP(@ Pf! f1 7'wp`meF;ntʯ~V Q!^Bۚ62rD[GfWKOz0ݥ3S(@ Pl8lƴ g2"t̕C"3Z}6}}"1G({ItKadQzM\E݂׎RE׃aL(@ PY pFYy\՘1m#? #oq:iA e>d9;ENS[vŶ gRM.P\wt2&(@ P("hxd"'\ѳCLX$nH$Y\(؉qXsfd8-DCzq܇,겅8(@ P pF'Da3cf>*yYZ~P¬³:QOp 2y_G\ȣ%"gT!|棒+eBF P(@ Wzd ^L6?.`D܁f;D-o̍ͩ_ n#89a2v-䌋Ξz9 (@ PПNo̍ؠ̚(Źj(Se=ܼr=ܵRzJuYDyp0g8q=ġ,fbT!KkMg(@ L@K{ô'(@ Lg4LKaߜe, K_Sk >K)ZXO;MdryʎUanؙܝ=jʗHF䌌=~b7 Pwc,t" (@ P"haNY[[#ubN;y`hxPuo+r-bgk?M5#R32 =}]&lOM P-5jYh0^=z%yiųSH)a== DS]/-Q%o덂rǎ73+NyTRe0+nчFFF<|Sw@V6&n,Y PЏ )墳*n$[G;؟濏S?Uw3~>мDK?u=AuKn޽ڦ{*QL@n<[G P`WDǁ٩HX$f4L܍(hXi.ؔ Vm7ǪM_rz𦤭XReO|`o)@Kѳą}*&r1DzR,y7 Pg4|?S C";E Y62ӷMT)/$z=Z|q<Y&p(e9{/^tvnDU-(0ah0wr_.e'z(\U\ BUo+EVU @ Q`xxgƁwUYſ `AWL Pk`Zdb|Op]8^$Z+gBfz:`O>M P\hܱ5˞eL)kQTneV +;0/ZNu*ZxojỌS0PÇv,dZaX`@[N P`AcRy K v<~!7Ȱ&` @Y"CL?^=9(@s?9Kz#skZ8;2?7 Ph`̚'f7R0"E>B7Y mtꭵQ Ŏ]|TӪRɨ 0OWͲuq"qie(@ P&`<ܝu=ȥ6izpCI?z{M@kk k\]]6ݧ ; )9J ]vxprrh=b(|d/zYhrKQopdžfmFoK}SNP;6B(YHo؍ TVLll ߗ(`LKN>6l۾ ʘW e}2lG}2NCǠ(@ P(@ P@ 0 (@ P(@ 0`4J(@ P(@x P(@ P(`4W0D3aU ͸lC`hh̒looob+(`3F~|p-022U]/3б :|v(@ P(`l-Q(@ Pt,@](@ P(@ [cx@__'w[[I;Q@߇\'k܌NC}) C>cd[ZZ ?kQ@/gZ[[!?_Q@z2N΄GW Xۋk~?֭S?>|s?:voY/~ȃ*0 "?cч#`{Fn&r7$\:fZ ;Bq 00N(/qaaa_8|lpp_xN> g0|Uwp~ST~д@YYj9UrwΝO?C{ hUg9M$,?VQd[>Ǎ$`{F.13~ߪMQ@o\:gF`„t?F_㨭Uי YJ|\`mm=8=ÇUn֬YW ,U`0G׌3  ?dPOGltN }ȥDr=|ꦐ| g4"WɥOo...ꡉ!ɹ_2#PFKgg1̋/w4!sLBY?/`gN-kȥI21dTT~իJ,ZPP>d@_,wɍz8k6-cU}l4}ۿh,o-JG謟߆\:qm5Gnt%nݺUݭ}18$`g,䖐zUUUO>)yC3*ȰhѢA "gƲeyyy4P 4<S`vv1Ew^e_WXf oViv0h&o} 9[y3gǞF&r&uMSfjuJ- 3~'7^?0 y444@hY'1"##ՏdOSFєrB|Ua/|5ea*?~XL%sMI"WXß|~39-\>r}# `gJ|ܹsj6 O{K{FO!>^~^0{ XXOZL2ŋ셖5[dbNnϟw.;5\ٕ Q(2899\\?.\{aΝjzhW~ό>WygV0ɵ(`i[lQ rƝb'\^RR*oypKd!0ӟ1rL2, ݃zș2K/a@d/)NR dF??Z\*r=&ɄBrd@K}g[|9~?S@7UwQI/srF׾Љx?c$\$?gd[~2cJd ?"0X1 ?W3S صkHpܯ )@)hllTH˜?e')19!!!jلƚR` Skjj0sLșE(ɾgVIe.YNYW 8 c1 P(@ P&%dbN(@ P(@ %@X*|(@ P(@I 00)6D P(@ PX 4(@ P(@ P bN(@ P(@ %@X*|(@ P(@I 00)6D P(@ PX 4(@ P(@ P bN(@ P(@ %@X*|(@ P(@I 00)6D P(@ PX 4(@ P(@ P bN(@ P(@ %4/@IENDB`isoband/man/figures/isoband-logo.png0000644000176200001440000011256413501223537017205 0ustar liggesusersPNG  IHDR >nbKGD pHYs\F\FCAtIME93 IDATxw|TUƿ铞B'f)JfAw,kb];J/{!M["%LBq$a99=oy^I!O$)6"bCaLvs{1"4m[weYTz<QNuha ))fu}E|T~JڬHA$c14jj Z1Lp2gR^{SV܄nECiC#(v=J/˗g9 IX,c~_>}s߃n4JO f1h0t3|>n"8Z$I\6zt1D\#61p"`7d+n!^S!4JO.f1s>F=i73b%:M5]s\4r Z7 z14t0qnZ/oZ-}=/DBb\lbD>l*̘;ڬrB )ԋ+ΰ ΉMF1D;'0cB֮AV$E9. eJ)w뺎l!}/ѩKvlrbDP>+Vnb2Վ⠑ 1fv-1r w{e̜=H1ηXJdY[~CbGD6'YOj5GdkNBv-Q\u݈ؤňCMcN^}fZUaB`$%꤉ՄM dEYXL&9"AJj]oc#z G׾&SPXj&!@iFqsZHR{=-INJxWYl;N36!,yCEzXv]1ig/[sizD *4,=lXd WA=d]<ɤ`f"~]\kdx!&ԴDns4XM1l۰y_f.CHEaNF <>z,gtŠEEż|$VНɤ&9={C|pW>~@3D%uIdY̦rp:|a^'y`D^.- gmɏFzWSyw((.u 2b⚌xz%ф5 ߽Кwyg Y#욦ϭw涻F[1zU.ʢ%Z͵ @\FصQ4T3:qKcNJѿ)--/̏aݻ2:vj#O}q:#)@J^n>o8>l2":MOp,+ΘNhyݥd~J-%֭]ϾȤ_ԏ?zfK't]UX·_L՗>։)Uc%\O'= r+>,$%2S|)7l^ '$v[57\L6M1c21$p%'^\x6Y"jbp̒Z@68hUyxO4~9[vYMK-4-Z6-O\+cD,[/?3ml6KDIbPmw!0*w]C.Ed&c,[゙7_Nċau8Nj$2`po\u>Cb1G+⃷ ٸnHRD*/A!`eH+f_A}`o~:e6w w }=M#JЄ ŤpyxYɰB Obtm$uxB޽!cD#<osr*.t=2x~ZncHIMP5a2*7mr9poۍA$$l}!Ba tII> ͯAjQl'CPEyٺ1!PL2KcARr̜=gm.:$hŦp㞫l ajh/yxwÛ_!B2r7NjP5b-=g,v gWlMґ 嘓a/yOyBqU'ɤ[#'feeBwE{Կk]_~pWHOB xW_0):},Ynׂ^+# E`<~ ΣU'Q6Y<-tIwXb%EVY9 \}^5όTq{:i.=wE@RJW]w>}&bJ _|-/#\E^t݈XUV N;wjn{,q?Bퟂ^r08nC*On<ϻh۾u ^xm|>1C}m6g`؅F$# ?Hnn%"G"fkq_8 ƾ 7gaux4M!`0T_ĒdIͦPńl6LH&b_i)g.W?c岭XMV,2(\\:t۶`vbD)^c6V''MQ*W\5{6q:1m)[ ֝xv硖ch:BCpJ:_5 ) r⎢4$Iޯ#I$v;>}XT㫂 \jci:gs/ϙ:'zH >xJoJi޲1xVF^64I6{ψǀ "7? #:B4Mg5w8#A!x2q/Co D6`,Ǖ]}@bRBlE_NXN`$ _V܏."ki:vrYe'}Vl}Ȭ_cX":ūN ãOAMc+1@Oh)"jwo *S[}0Ү]Kz i?9 lø~~$TZ-\aCyߕ\|ت3V )1?F]NwpyP Q.[i:σDN-^s^O/ʮ]y9#SPUZv挼<*l6[lE9{u ̾ QK VwY,`$JP8nIU($q:r\s4o$Q%zb/\{o|yK;" q@ж}K>;w*ZickUI~+L' ý¿;ՋUU'Ϭ;U. 't Ƶ$O9ZB@Fb7TueRȑW5oAڴkΝ^Υik1Bt˲k|4t#twJsO綻ҫOS!]'/dHEyQ=dӎⰣج(v+Ղl [̡/Ŷ$S"U܀0F*O 9DB_>LJUrqX$,3fon2N ZnЃoE=pSg/[If'?Q\Tnh1Aqv>t.<\}Q, +kolyms -ɘⰦ`IIĒ9)kJ$,)0'awbs8Q~h/ZZ^A `i9R2lIÜ.U2K*lфT N$&%pɨA3sM*boټ)W?n6kĂ|\2>PQ]a~ڴmΥs.Wf2 ^:vlͨC1r NcgƧNdjxAIIMbvķoeM|WM-;(_5dj5fIB +|,(Q׾`FU5 ̞q F_9̬!?o3ץWF)񓒖ecpɨ;gLd4)FxN@nQu:Zl~DIaپq-¹K^HE( 6x/P+sB 8;}ΥW a5`uFD_F>GfXL^n,#G"P`gqoP/c꣍ҕXkԻȃj !sx۷Ɩvʏq;q&s [GPe@%GnޅHE Q"y=֭rSWDZ̷ i~4SAi JkH7%^fxYfZg7cĥk&D_h /CVHeI"qRu$F\ڟ{٬16۽\gSƧa!0 0w^MQC1 73Ƨ>"U3 v5&U2rVNvqX.3D/*,OÔszUԥOn+F@E{U M70mﻁ=d[(hR32EZ"Cx=zu'o3BrVab|cx<^4-)]ӉKpc7ríW`Zb@ɲ`=`h*ٷ_E.mظI׳qoQJTi.xFB,K'8Ws߃ׅqwlϥ]7"cune{nH kzV=P$􀟬ϣH~$u@bvhDT5 <*q6Y5 )ɂ}ؖ z 9׽G;^~!uD%}?!z$j$vnGKb9aixH&VM19Rğ̣R_J Z?U,bBh&zi(/=> h|`@2}5j) 2G$~4#XRF/^I^?na`IMjVV18OÎ%aZ i;dyT%Yo#%fNT#' IDAT(—W{^m.M@Ls{+ ){U=J4 bhzuO H&%e:fHr gXH޲&@!/6iA-$] hׇU WղEUDOՇ"4PfV̈<˾l$kt ׫=([u[ܲ<E%!?)\8#!SGZr!#& /<\<*9V:q!}^L]z֐\CYeQsTjK' ѵJ,pBJWn@s{OSIT$KKǖѨ~LꔸDx\%k)_AqTס™ U [,({I'Cw)VrA6([)is%z1WOD߽a֊#ŧ#=˳c cDQhtn͛`mR}PjJkʹވnnEsIkwFP%ĵlJԣn>y/(Yy醆i\7TbS,aURhs=s#2bi;ǍG_j8!/bsR˂z}/% [q0"7^0Y1wsauz)_M"P@'ҡQ%y]*7op&#(]C }BfJsWCZ z$$]cS<SFօ ?AB6RzWԃ*{z?춪/Co/y;X_8Lc}%M!q0@1a9D]yl~u"AIB/sbQ]e@!ج19PN fAXi'HFPyJtjzW%KVi{dR?4YAJ~N`W;j/ǔ]wVa ,+8&&^6ާ+DABgMSLe KrbW& lak5-kjR7J/t/ƗW͵3@qYu+`g$ gso:?mbN hy'_(/VzJkyhvpc_B GrVo9aPyΖY2aINt"[LȺ =e] ]V%f}fM[F8[6%Ms۵"{:Mгk/3Q49r04ֱ#bII'bTݑ[/aNid-# G&xv쉨\y}XRHz&h%5 sB- D<^J7j@ νbB7j}ɬv<<;PrM3g5٪]چ:ģup-]MٚMxFBRۦړܖш6_I~'dfTVgڭ;M#G2*zz< ? &3ŹgJ33TÖ;gWjA4|8 My%9)W?*R~+e7RfșQWқ$aٙ{n\KWc5Kr"ִd͛lGLlӰ$4JU/\*7odzcB@j8[dH;6**˩!CUI>S{#D[3[7"ՏЃaa#l*]f0Y,v:PZyJFz}^jtַ݂!㢴!909g5&s[R:=Drlµx%֠Vs脬 % I 7 jVLN;݆bzәj !B U^ۋV72\ع-mNa4^_I8[7PU}HpDDžU^_.?\ځ4AЇV5˿Ci K08^;ԭ%: F0HYHк^q$~|dZ^I)ekн>pyp)CZT(-?d)^ oz+)CȒToam2(މzF`zFEڞU]ǣT*tC{K"j}{VF.(jb`IM,a,)xJMG`$):f$5l q]WKOyk~̉r~?UMFf! 0\{p^:XBAJOp M'Nt|RYHsٷ$? (n`II%gד )*D}55}FEA=*ږx?ckѳF<^cBqjJrI~8ǚbRnIi}I3) N|CH؛N-d ^j)Q4Dts(p r\ cC 7BKj2nMd"ܣ =}YLP1SB9Y*+Ihn/FP\a愼PQYn:+(&*ARb[qv\4& ěsyn( *swӯsi8/@"d˒Is8o8*f{>?.(C߳iu/qoJ:h8hIW}.[Csq-@Zvx$ցۯ:d)O#%eBEP6yL/ݫ5nV'9hb5= )Yl wz 1Q (rZ]1a&vH`:d qdyM07{v#g7Q0k13Q~ /Tʇ"fRDĮZłbإͯQc \SqAd2ʊ͍rPBU./a$pc_c7Ȋ ?76ID-s&)FHT ha!WYXB~m wX83޶PPhԯsB2zCA(^עTnގZKsAQ±vI *֔${tEkӼ?ϨfG|DFɼ#L鵊Ԃ@P|Wg"CטTQx/pE蘬o5а[Λٴ:|_R>v@9=ފ ks-j#rؐCxy F̯KTm13:ԭCIH?a`ێ!gO.F-ڵ~0C9^1&Kl؝GueJkѶV0I(( ՌMQp %d/Ib$p^.Բ 4/(V 8I3EوcKu9Yk3Ξѹ=9$R7( 8gvQӱOO!* lBNkܴN ݔR腨NķkEˆ :A T~ jlP\DNeLuD֝ר̝ax# ~GБcva^FsmOq znmqgSWddBa=MhҨ} K rrV:pF1anw.R=( b[AJ}Fl8 {FF(tB+P\CT=}0*l. LY]nFA Ⱦɳ@6_cIKtqމ3މW7E6)C[:ɴßU6˦>ijkoԞ~K6HD]T#BWQ2:`rJB1|F[x4MF&Sv11w?w'cLdZB` p*ܞ rZ([Ͼggn>\6Xޅ7},!Gr:"q/4;n)B;4SC ɲ̈H>k(%TB)DÝLͬ}:>EӤdv|%h~!f*F60Yds(pB 7j߭_wy)]4 zd JG :IY:`J÷}MC1IPy'k|ַɅ7dLBiK7G۵`+H4)1S31w)lfDEMwPkCPUILDBi}y?h^+3SRd|yl}S*77UϷ;raT+H3Vpm5W%[<-QkL!>P} [ZlGNL{#,%_n>zy%he!k%%kF#iXj@pb* '2b$2ZzHP6H/&tlq#Ekٺe',:#clPLȹl$jy%{'B$wlMٲ5V\30@$R +N+ɍSIЋǒx7&=-P"ܥ]B媍UxW :BDxL:ghp_R>WW>-/_}6 V~ݶE$Mԓ!bEOhfN_Ț՛Yz+neR%vأ9;2gRvtlMnۂ`W,u"&MuؗWT6voGݸx@ZnݝϏ¼ٹ=׏$IAY݅W_H!jVYl}􍳪D$/cޜ塉 ? Snq6H ˪T{OD TMۘ|l{h~]C'(*,eb r߮F`_ʋXf+a&d$QXP,K|$22ӸxeG:'Oכ`POxة53~ϋQUgR~KF^>Gq y7uB4MG 8e]?#;ҢuSu ;D4`@ށ*~8ld\^9Z ֐dwyeP ǖѨFޱ{Kվ2vǰ@gO@%Pera]2fNOM'Ѽ %KV7Ò\ë~|s:J/GK]8Bt]^dc4Ψria=.?|3޻D5sڙ3OgJ.5`P=b9"fϾo};Y}3U?e/Zn~wn x0ÐX+f/jNVamho9J7= Xfܚl|գԗ-_̇*D^UJ۰`qI/SwO~įa7{%*k޻k^y-~[f#o|:bfu2cat8V &'xl L_XH!AAUsixc_2G4Y,ědŕC 7ͯofGgxmZVc!_GMYK` vhqx<v￞8&ӏpG{iXjaIt{U]Se,?rF~?j@Qz܉.n_GI>*<_WOa)>7Vi Kut5jw1~G P5~3/g`u X$DY"MHU$e ,1aAΖڡ?0,X8vVya/G~( 0 iyD`<͌|0NWq 5~ 2e,ZguYr]INI$ic6ol!w>RXvnZR5J÷?7붡#c q׈ IDATmׂ=8֯9ܞ:M1ub| Ol}5ӃI R -lf-E $9(U *r)d'deP4GY\$ϴ[&;(aiL2*uqX߂ B7f%q-3c׃bsg-~Ȳ%k(V3<[ܛ[ q4j;2^#Oۯ~qؽlauʏ@'Ul҈{ᅱoqk4~\ťG®yB{o~u4Fqq1wlÜ*ɖMT-MَwwB7{'S4H1+wXhgtxocc̝c{;mX#$x4  a$>hgVDgc^3Wru$XPB7;E#5F\:A$?% ff{y{ ٴs;Ug㪮ۧLF]{c$J%@H\r@HЋinq"]r8 43u]؅?M6'NO\3(gs|nCAgMvanŠ5j&$0*c =[~:8 @MiZ\ɒno2($3;-ό?˯:OO ul7W]v?3'2hH?aƉ8QN8V_0l@Up)l$m?'iZ̜=C?Ϝ{IbϼpMbq k+@vNW_wn o{!] vxF倰!A\H%%35FXcT-\B!3ș1nU뷰-jH,#%s3ΛP=7`\|m(es /*woG;3&ezɽ߮M8d[E[Jغ*ƙ[nƆH@q'yYde)k^[FuK h DZ\k\sGZPշdNEx6,[K"/WNn+MVC~N雟U0 #1e-w%YcGsp#qA"WnhsbҔ;뺻57!a{9iz8N.tmLu]5eU7&HI2i L2>Yڃ !8e4eUmj3dPb"NO_c_ctǔt?3fYKIݷh\rKR-O**j iv-nFoj¨#HWj-cۥqIS.Kä 2> k{S35bz6x̠1Bmu='nؚ0զ%4ׯ+JK钝ɠ!q~ۀ.:>$,*LRfY gA@cRóBb+&13NzZ ,M иf#v4NƭDٲ k~RB;u W|YF$VP(@~yI{Iy6Qf((k]^ymTVP_ׄeY8ҬnsrLUTMkớ|kn?l\_fŢgf jd֡)Z?GoK8rq$/!CwsEF2-Wwr>h ii9ᵬFMMQ};s-񵔘n5 [dsQHt(޸'9ƞ{‡-"Z=)%dn>(zraJNNfrk]M-bb8/>\1M3Yp"K_m9Qʦ2ɬeKQPU/;)]ΰ_B$~=)hEy57^g~c!ini(隂'Qruz+)=B؇_JN>mF$3")iެPvZk!h)<48.qMhAtkHD=:d<"od* EK{Ϯy! MV&ےA]CWIzz3sCɒ7W0zP 4EuU|ͻSc4jH"ci'/K&–eFJWy]65E۴ v;(ុ棩jBhp8a#1xH"E{h$f'.RLJg;KuB'eCL[=ȩ;P(TKcܕlYrLM!g'I6 N%ǗYUu^Y xjxDO0538o9SI}>rs36b i);Iއ(Vjeab4L֮{\ɸbd_)c G\gqu{g̿͞=IjZeMy $@Mi'ŧe'Î8pd$3ڴb2t@itm45F[Ce8NL?"a2Xr^}6?wߟ\W;.<uX[Ri9J`tGm%9ϮՕTڶ,j%Ǣ]GKණBV8\ r_?`g˱-$E<hM˶z|=MUl`1mg4-jj Z )%o17]gJw?.Z5"jGJuƧ6qS[k)1mئqx/۳)o*BFՐ1*|r/oV\AyY6/rj.{U*E(PZNB!@]~߬1B$#x=)x]Xly#>If2?ÒuV,_W3Y-v7x'wV.A*#>3'ɾ!+?m6…ޜ)/޿=EIqyfww=;3"`U 80`p@Gc&k&U*%xYHr,2"${u]cg˘w7,6P0n&>}:F6T9Wr摑F"Yb.X5^#L`rFH$No$T?%xhFFf:u u[1%'D2˽znSYQb!to^gNMu<f}J,,{Yplv[*+YPm @.Ӹ c @Hh o6SUU[w޷ [nz1q3&&[UY={j^躶o;{&{ ~y1o1 M i!Ģq -#gZwۿkp ƶ]b8iN)K~|LRTE%='ns5]IqQ Q;2њIuYM8#jFtubĄ HK ]RbY^Ĥ='97dl\7tKUbzt.pXuOk%(JL˲__ܫ~@ASw tsFt| ܻDGz ҹ&3{RJBiA6Q$r33ӹᦋ8[298j !7~8}`aI~y1(Llw՟7=,LnM$mGӯ>XԶb8_͹矄H)pZ^e2+#&FsLR/m),MȜWbf?)+ |L:NU&Y˸絹A !=g*7GЗH$P]2m}Mpa4D1{ 84-+qF~,>{S{n0'nwJ厍" "zӅ7~wZMUQTu7ݻ%Ʊ b{:Df4iJ6@|̚]zGM4'3ȰM&& g&eg7yĔR>Ft&'MUjx1]!vKOKdeep oRQQϧiINv&ǟt(gD=b_]8 ч_aul˲+Nb!&PԄv)*ƌޥݴlF j={% bLN3䫕,f nhK)tHD3덊B gA?SF3cD5pƮn6tFen@o+ ?6UCiq$=+lM%KSSi kU 67GJ4OvR&˦Icqd 9oCϗa˕df3n&dΡә{t 姤qG0qH>^X՛XjەZJI`m$ 4GDzSC}ZUh?*dQD^[}M@PU*`MimʠYsu!Mws,;p8N99Ngj1VJ}s/?96d؈A 1N9j*˫LaiieycYy|c1& M^GxDFfjfϙ¸ RB~zㅘk]efu^l .Ha -]7=%W(JF׺I*=%:"vi**iK +Qhi OaHhpz+~iEwNٓnдa b/h'cGbP ET}ů?A?L9Yht j0}:C*!4ES=,UC#P4iN5 uR:67|oхq&6k^F݋J334]-e KϤ/AJ;]w״([4$~ o] .A:~d\B 4j0_vUh*k Yzwи~sBr}FU-+w7erZ^-OQUδ4qbq!"Z׾_ c7EXY;cXH%q/N:.[J P_[wEAK QRW2ⲳ{!z\nNNJ5Zj !z͠PxSr)=&]8w\^Rȱ/TjɖVWC]c{Mwszuw1V~?u߮ey5ylҔe]zA /4ivwjh pj0iz%MQFX|GVsiOT)A6bGK)>Ci]o.vN^oWwt\om\5,\d׎qQH 1oĪkJzBUj(Hl[p/M[ 뜲\w]W;n* v[s鸸&sD&&Ɵ/' _vzx=H'kxV}fmfMFufe *林1TϣؾtԼ|c/ٸL^/KuhapigHWg)*i=Jʼn 'ș5 #I:poї)shʈٴRUװÂ5wmoHitwNH'8k] Kp@[֭O QCA}v31QUQUCxIM\BK!P~>2u"~pB uTt ƵXw#4ݘz88,r$sh|vzVzV=xibʉUbTbF3yη.Qt =3쵥u>YF IDATLٙK Hc5*/qfꖯ~Z&/дN-W|:2 _}9БJϊD6neݏR|M\״F1#ɜ<=xyyh؄Dqq~ Rh׋}^y(BKU\dNEnw떯/_)IPUhm/Kl[9/=)c#wa Zʺ?F)\.i1nC/d]QD,Y7W>ةH#1}9YLf\1Zzh?[1v8SSn =+%5;Õ%+Xv|moWh56gܛ=IKJOw7{~o!P(=X|6e7N; 8;`n7E l~*B03v~}]]W[P8JQB]tCn($ >t&z-ysد-JŻຠ=}iGUIG3w<@kzBUYaTT33i=7E v$FWYϓhin:Bx`&K/{8X&nCe8۾ŭ؀[o9C2QE-:p @B3H:ã2_Rx|J^4;<,Fe c~v)̞ tUUQȢEO?uQ|:s3u_X:h-NFUb~|Hh TP@ {Xo(-_-k#|iCgO8m~`V3+ _ƬeZZ>k`o~L 'kѝh]&cq(z;IDH#fiẍ?0"Ofb-1S@"4璧"T@U0#%/.@e?gws>d}(3D6nm;՟}_~ڥ^gѝX-_aK؄6_z&wGR7V q+7b/-][iZֳBE,@:`"ڐ(cP{Nz{ ^B6YQumb.ykKh?\!]>fb?n3:HekX~_2g@U:N`/m7eHk; G#N1.0p7)vgŞ^H{#egb_`|jpǢ9unWv}*E1?)߸ҼHo^߬s=ASu-cܯd?jOqVz7o~= J'r9qM?m{J3{IΚ͋q ?Z.NJlvKIv);*TݺbeSNB|2͟bz7-BӻdExG޸O9KuOa+X P4Wg1uzkJN`ϲ~@Jk. sҘ1Saz|=B|!X=/7Lg3_'_ @nD(@t5oƭ؀oe(yp/ޯ@\2Tm/P;VRշۣNm-N٢wtԉ4EOvlOo.jȝ=Q\@x$]5XK_,xjz:\Q4ʍ߽x9ïB4ǂ M= =u%%gPݎfu!4Ͻ=B*>ܞclo>Ͻ[ҕysg0ڋi'eQTb-YSЧ@A# ed)'ƨ=zR k;l{]£;R^Hw}??o{5ǭry sA@l[A1޻gbPu+P=]-c=+&_,Ƃ{ĞL (hx3lHmԐGˈ(~;{*؄G6T}D k;eH;D-_K?{& eR)8pΊY]Yװ?bI74o1G bƬI/fݝQƂ w-~6}%cG-@TrV 4-@ ZɤK5&?vS7!-k-" 毈lmt)'9%- y 8Ҍ"#5>\$Nea:NG|:}?ϖҸ~sihξ7b54yvw"^}Cg|2.TN?0USm+gEh`Kng3 șv [+ũޜ_/,wv =3Bz!Qvՠ#uA nԇtD^!xY3!DJ1C鹠H#[S[d E*J D0GZ1ϝeBN4}g۰~3)*˛`(rsi=U_,E j NCڌ3d[z+!=;E"]R${-BώeıҰꂸqD[5)qKWlvG.BjK.uzmN葷L*?]BMBXŶ)7>=_C|)<%2lڸ_W6\[VJ4y q(>k,뛗 r-+/ 5dzwSY+@H|(!2z$Z@; "=5u$)'O9i4ӴLB8/oʛ/|w.nkhƻkI,ɴp߽^bܙ c|B=7o#Bs< :5 s0+ұjC엽_\\irPҲ柴p>"jްA HL곯yAS1tMemE-V|]Ǖy000^RU6ʆɪܬ #C> ץekdN)] I_ &L즧|MbVWuhv .>C9PD^Ĺ9ֲ+F(Fw )e ;cՄ>df?*~5@MS9"(5^jbmࠌ ebM ,2O_ PJjQt>}/ĬH˱ BA"JU苒݂k"T]1[ |hAM:AKqŰ8#r'eϑ3w׵ MKka:5vVufyx?wq,c 6|(9(<..XIn#k7"Z+^jؼVaicrF RGbMiοT~p L< _s.l|x7kxWy1&`j/3WWyL =V[DIj.ifH f7]v,6`oMz>1Ŧ읅{s )%oWGXPԴ0]⑜m;X'UמÔƒ)n@o&}8}>;ݍhN sڄ!=H}w8#]<+D_nɆKW`U=7^.-vGM>m)ý?%>UװL]/XָM#QR,ՐR3n8 =|} UW+?>ƍyo!>pQ ,rK_"ʭ1B 2%`T:'.OJfEKЉ1Ծ<}qpnMU|}lچP`a3Uxjc&iiAnŜ{givq)+_xh4b!@K p}0C.;畫qֶϚoT fY:mߵR"(jmܑ(,mֲ׼@m$ԡvws[ەPGpq0 s/kчGx+7:bBo 3gOۮb^%;-hW{XvvTz˅,MY1N@*rcuhv5t 8MZ$Lȣ։ɏri z9#~TMkZ4x.ΒҎ)%1l@~|ݹsI ygϲh@QLˁibڠ(>s[V\JKD(W;}"P)nxYy#qTP||PV;Qŗqs])ɧ7]HVvUv *k+ (+Ʋmx,pxz##L. RH{8[.,8h˒HLH뻫wyJƮYy/{o7zaK!Z-eS/dѝYx%ۓ|7׀"PS2a pdEmS%ZzH -.`Wu_ؗx{x2^ Įoiz=2 DD-|BJضCvvOul>o<.O<_~Ft]K)~w$X`r)Qb0lL 80K30kBTmΔriGpgt~Qew_?`k0,/ ;0oÙ3k9bVfr^x!1+XiH[AIA97Խ.'RD,n0q(:`.4؅j'JpۼZUSQy 1[P&7qڬXp'X5!tҽ7v(I)Q$>ϻ낼*.a99|3wZ7]E sS^݀߯Λ>'M猙Sqzu.Y׺K6ѧF蜻W%ϰDq~- *8#HEb849ny'tO&uKko+c~_jT8`I8gN59p-3_;YvQӻR"4׼g> [c||R@#aahN颫S,q]=wǝ8>{QնOEJE<45|㬙|on%Mg7_8M~0Ne`V!<}o69%jϨ [C<2GE:EMuOMF3 .9N>Rq6ϻ@J+v3\fra5: l=>̮R[(;{\2zPp@||9]3x^.'#[Z?O.KB˕hJ*) 23ÜsuL:f;~[g|=2A8)kq8_NuqǺv4Kuf66gI?t8[ʅ]XqaydBSQ>|YdNE#g'˵W{M*>y74ih"+& 'pٖ1胸1cD=]GEXt w/`0%wTkq\~\jF P{3[A=g7]i.]EsoҰzVc;CZvs< !Pc3Ț6㝗x }8oIDATHTK6WAW MDTѣ[9ncNZGz2[o--&-Ew^6#Z\tGͩܞ`T*;莅6b6EydS1yoa74ڶny?R"DT0; ch*TL,FlI'e 2~KŶrj"~_>ɑtZ)Giķe4:]/̝]7j<|o ǔ?Hڐ+&iX%x,_QcHv8ĶmN9~s 6Mz|zwVpow Ei%n㷑9]ص!Ҍ:!˪X[x}Y΅]*w`%c%ċfW8vO_zkt?ME][ɚ<&kTֲGXΧ/ca]ŒRJ!~q\rQ{1T6녺\G1q~mڈbU( .%6iW>6lV.Qa%.Px--āiCsh-ON/ ^weN;[n )+埼R ˁCrhyRVU G<㏭)|º;nuPgHp` R}ɺ{y*BTҲ r 0u}[d; ?E46@rD+8 6ʞ:.,߁oۏ; ^Q'!ȟ;)dY4cه_|E#_G,/͑"+%>ΐ!Ol7~w<'W(*.'3S&f3g2~Da  BXȽslA~xjԈ?_E D ^eZķ7ywL"Lq\t]oA.}HdwQ~w4>*OUi # BU^6T`EQ^GqqEcEYE(ۏ $V˝ ΨEAR]I}=9SwW^|5:[h&!k&v04Vu^.p+;}O\v7-:$cxrnB8kU+3E8y'55jFӽg{1mUm/tx#v}GVvbʯHqIyţq]J)+xɉBX;:-ט Ԝ{O?% kՈ+^/[-TmGdaW{\_ECB4MZ*t&mLgmx=,]Ǯp؍׿+2)L}-=Qe] 9oB벢pd%Z%P"Ix5"/ },U UQڬpcs ? ZTJ1+ |d Vg!|F ^Ftɧ{ 1˟a' THD0 Ѕאgw%Ή+9:w)6OJ:åAV#74hi:мe֝Qب2 E8F |I*DZ +Dl`p"tN͊84YI &xjXqBak אk4 /h`IHz~.I).qԧqX>ZnM~3CGBoý^~"m{YVnt..Ӵ vE\@BKvJ!V=!]Ry7;׿F,b$]r,qN a)bY8 IpvWnU)bǖݤ,]wWu0I8eh`H"Fu;GTM߯ uf50#?&]bHjϦ`W:#]zYaqqԬt  CS$v5]ǎ"?K:Vɨ۪s zډjU!n}mvdjeǸ7^clBu>M1A '=`5?:\8<甐.9|Wn k% ! DJW;j)yx=̹{b۱Ze,* hX$]OJ$\ߐ&]Iloj_1'bÊMrrp:t.QFx?uل,F|lw#WCg~j2_YPUcBz}ԥް~tj3ꑿ+b9b ,socL6v8_ x}d=7`Z4قj`$jG) nぁg^;wZIs"ѽvca#q:Mc/Q<ņ j1s[/\q8Cg>œ'$}~|A (Jߤ8wlqU8T)&sA>k>kpa18"5"lP/JcrB&${t&UQX5o=-T>v8'N\-ݶw'DSTWcGT4$IپObx~|"}w%&&iqe<|2q[AY Й90"0R?i1^߹Zf kI@S0͉D-+N`iЎ*,k!{^j/) )bV N n!5{z..Z _3̄4#i`s*_3O/`Sa; !$R瑮nI7HUks98ErrC:Q}>|717]UY8zB%l??(syq#1j:+'Q1X[Pޤ!@BWƃKHG 1t.}_2N)*aó|{(7TK'DL$໾c3΁Ym$Ino`w}q:Wo ߼C6-F-vEJ`kDV1ʁYoyǏi"[5̩T˓GPTif?;brH姕N3Hd%7ATͼW>ͻ9z<M`V!32Aedk*8vNZߗnNETZ53w=o}ȍu!bcxxZϲreEQ Z0n`n&מ.[Cr&vlO缧@+B ^]~Dunޏ,.i(;諛ԓG'$Hq}Vs\2тANv>$* MЯaҵ^;CvYHH5E]Hߝ*ڠr߹Ґ낸᷒0}||}z>$jo4cА^1v A`^yt@&|U?p i*L.uKGl@B` LCh>')rc\ΰ`@!fБ}ֳM+lŗZS.Ct@XL{n4vO?I] tFhV򴝻Ip_qi\.WX,6'#GGT3GMMS̶]$/Y ;h:NNȒuN7Tu+:8]uxh䰓?I pۣXdkX鼚ԥ & kP|&[yٹn·>fϾLlNp^,iS^DZe46F o-㓔TSz]v>nZ;} oNḟinP\:vO7[)9wa8NHĻlri;"6|1V% @ ,L4wNfp:C4A72D>?G>$yf4! nUSͪ Ξ)J}>'G'ЪMԯm{yoٛ~j{"B BkBPZ#iC{rvfh!,ἮIY=ls}v0v2A7 Zl=<5ܳ{GrהL>Ӽ&覮J<=S>?^WAxi4g\tSW23NSז=DvVmn'6OLwN4A7ukͧ[dsg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*i8 @IDATx mUy' \F1H D%XDOư:RŌX6ҲE&hDć% bhEф;sþ{jfc=g[s3w< @ @- @ @@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4& hh@ @ @=@ @4&PDpW}h}33~ @ -o9o zԣ;<9 .HP 0?>N; ?y @ZƯklZƯ%Pϰ[ܹH|j/;,W p@c=_ B3<3x hZA=C·?<< 7pC8M7N8Ah J\wqGxsN=԰aÆ=nppG~aݺus`x^~o .|'<nōJ\5 uh? Gnl???? /֍4> Ћ@49믿>uQs ԧS򔍯~^W-/~qxk^fA!v[xwxOJFOO}j8蠃{ް;n|N /zы׾[l|t/PϰY #7:14d.1l7I1 @?38#<PrG)o9# :kp衇SN9eu<~6? ;c_) Ŀnm?zo2K.*C.I up5 dشq?{6% @_u]v|35>'0;8;9kƎfC5J Ŀï㎉5!3n8_Xy Я@z--d9A@ïÿ*6a뭷=kx 54~-Ї>4'?y˞#@CZ~:h]>!~u" @_6yc^wu"}ʽMo% Ѓ@e/{Y8묳_ibkAeI?3~ _KMkn;~ ~|L@ Nۢ wE 9o{ִqn!wyᦛn 'pB;z пw1\SO,~7(G1u_ ^>xavwG<"|sb}B ~>hJo~%2d#p7N:)|c ?я;do^W}7BUB@;'>w;\tE}'s1Yz! Ab= ys±nĪUJ[  @ @@:YѐJ @ @( hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @ @ 4 @ hp @SA&n^'@O~=ܻ/~ƒ~AOxB8蠃{X @s1/ @ ]M}A>z`py_Nj-6\Pa @`nAdN @ ,pa֯ >n] 뗄:61   P244! @ ul  @6iiB^'@e  @@%} ӈӄN4O!@JR@0M @ oACS=T"s0EiB^'@y j  @i- }6U n aA8 @CAC.E@o# xhըXF@аs  0A@0KhI@a  0a, @``a ЏwW%@nAC7{,,T$ x e J@PUMe 9i 싪[@АwTO@q MD6FYd% hȪ]%@MB&@@AC}=7c bsV@ؐmNwAC-P H @ aC$P! K9d/ lȾ&@sAC.H !C>R)hS@Ц ' h(fD!C#!@ih]@: O@Ȑ_TL6t4C3 ШQN @ ŵԄ4. hhԀ2; @. ]j LZ2jP P֚ K@B{h @aCI }AC=R!V / l(&H`nAdN P^ S@ЧkHO@А^OTD!C'.B6Tj%0U@02S3"@)R/ h* ЩSn#@ k X# hXC   @ )uC-4toz2 @ZaC7qA& P&"HP@ؐ`SDAC.AO!CM4s3H@PQM$, lH9J#ЂTCHA@ȐB@  C (_@P~ͰB!CM7e !&)@ A %!CJP  Z@ذZ4S3X@PqMd$ lȨYJ%a4HQ@ȐbWD6< !Q@ؐcL`a#$- dH=#@y@ dC!PŸ @@†vk kM/G @uY/ hȿfPBnA }w L4L7r Q3 r˭p㡇 ?y @OaCM`w<2-l|a»{j8mW]}]38‹. nW sam3c  ШW\:+Y8#@`1+ssF r%+ס᥯<>lppgo^x\|-cN%@`i+&4Vhhՠf2nH;찑'?ް#y }&&!кub @`@V ]&!ЪU^ 0FaeE|N@†.]ZAZhM@ #u j Е+i!V@а3Z2jP0.KZt! lB545  1 89m 6>&!ШQN 0F au">'@MaC&V@а324Fi 0a hK@Жq 45 F 01 %pX-s6jLk kM'@IaC"V@а32,LD0f89M 4&!a!6' 0Fa5a hB@Є145  s9110w?-pU6o  ӄN`a I@0ƃ)|@@† A`A&>$0a^1 Z@Zdy9†6^!0a^1[@V @`2zk85  7sQQ*#0E@00iC#!P|C S^)& MjNK06 Y (9B kxCF#PnkEW֚V .]3uACR_B։]@,ڔLdZIh"4$ t/ d & `H#y#pȫ_%ЦM]c& hȭcmL@d) `Ȳm-pH5 #ЩSnKX@Аps֞=[#H]@zOwTO aC]@А{?an2'(B@PD!V)@+†VX !f)uy!F !U~ y h94ACi5B4^ Pȶf;)CS8 K99cACSB٭I wC,~C5; T#<2yMj4z4o~eWV>ik=vyNQ@ؐbWag>3|_nme]5\-a vZmݚ3؛7/|N=߸t0]Aa%+ v#Y v]:HS@f_&Uu'o[n_a뭷oË^I{GC=]G]{¿+bؕO֭jK_@ؐ~aaϽ!.pLJ.l/.yחz<Σ !C}4d3=PƛISk~$UF  xׇÃ:4ǯ:837/pl HBp8c!C,( Z<#M3߹w/I54~-u2c{#s?놝3(Cawo!G=Xo!~tIeܕBȐ ]baL7Ew~G8쯎zs 2l%o^Lø")#OTuax?j58~-G@ؐO橴ao&<~F| j\qOiIf6* )wg9_b]}fxL<#}C=Z5߿"KGkڵkWϰY ?#ȉwC|G?cu]7X2%d? \,/- x|"Q#Y*qCi~k_& Æ[?o*(Pϰ68}{nab{^b?7lʗ?_Ȑv&Vw;J-OLL~O}j8/V#Y*q1px3pu[e`Z=T~tz(%8gC/V hj6 x`8¸T/>/})<W V+>ߎ#oy~P{eV?~s̃:֥W^uU8Gwr=iG ~͌_;=#?q{kSw+嘆V=$.Sq-?f47tS8ꨣVvٽ|?Mq+G=twZ>h~b^!@`Y=aiO{Ŀqv<&둧;(\АgUǿkGF:~-K 9O_YM^PZ~zܾN8𲗽,< }s{.я~4? 'xby Я@Չ;ɨ>oa]b_3kLFi-D9 lAO88]=*׾6| ~ßɟ `?A ?[׿3Kν(ۿ]!C ]6y 9 2jR2d2w$ he @}؟u׬A@PCqQAâr#@d `  ٵL :w9 @@WgJuj2ms]T@а @ ؟!)/;!Cv-SpO] ж6~MBm t> @ Q3$ee' dȮe Y@s\ ІP5fBn t> @ A3$%e' dȮe ND@АH#AhR MjF!C]7 MI jR2d2'& hH!!@,+`e_{S$CHD 4B k4$e @?ârΫY@PsͽiACӢ#@,` k4$  @? }62 e, @PP33!)@D, @^3>g.dSߵ % h(fC ` 5L4$  @u ؟~0[!C ]P el @ϐy3+_ȐYÔK AC&R& Ps 25(S@Pf_͊T 6. 5L24d0 @ ؟ަ43!CJP 2 eլ @ ϐa2+YȐYÔK SACS6 PiJ32 ([@Pv͎H 5+R 5L24d@ @e؟>8 !C]Q e @LϐI2+SȐYÔKAC!4  @ o3ݿ25C@PG͒H\ 7( 5L 4P!@?C~=K~5ǽ3<)O ~zfmR.Wm( h(D%`rrkx_B6 +|~GH^ ɷ(SUW_+B '`ECy=5# @ 33dְDˍgpa%ZA@PC͑HV ɶ&†^qծ`4O!@2?Cf K\mT*`Jo @@gHW1\f @@V4'@?CodV34h44i( @<gG˱0Nt- hZ @w ؟ :6І1  @3؟a$Lf7D&/ б  @`(`?a4<@IDAT5 Ѕ e @?*-0\`o@@ O%`Q*UjYG@hC5  @P-p5bt2- Xa  @0Ikf5R45 @U ؟v7>j{34Nk@44i @gUqfX-sRGC]Q Pnof7C'@`I+t: @`^3+(`5\ tJ @@g(Lb (sr( @`Y3,+XV3w&=r휺  @,ϐez/z B `E H!@4%`$jzzmJ4I @?C-Jj{3$E 0B@0S @6ІjcZPv͎@h(E$'`Z|A fHU $@`+6! @M3[V3S3"PN' Ыz fȲ}&P< Е.:V3G P=jy @ ؟S/6\`o[iʶ4 еzV3; p@hY -6p5 kH@PQMG xUr욚 X-`">'@4,`A n 7T `ECM6E @_3խfȥS$@`a  @K؟a Nf7Ce7]  l) @؟!^\ )wGm+`yOC s`U|p5* L@AV4LS!@?Cz=I"RzXV@а  @c0ӛ W3؛a3 !)H[ i'fH j @i{44-j< @gp+Lf74)  9uK @@VgȪ]k5C.H@G]K u{W3؛a= !V ٶ­fEI =, Pﲳf7ò'@ E+R슚 @ϐ} [ D 4B @@9g(md m )tA  @@Qg(NjF9 F@hH1"@?Ckj{3-m|O}&@?Cm]zRV3,Mh24d(e @y؟!>Qp5wM4tZ @@g( Mj؜D@hȴq&@?C}黪j{3 '@ +Pv  @3T'j5T$@AC!4  @3߃+f7CQmP5& PlI[0Nj*`BkZ @@g<+W3؛!N&hhRX @@gk&o5O PF& Юqj{3=5 a= @?[aS61 أ/ ЊZavj{3dB  K9 0?PŸV3]@P` @gXPT[M9 s`9 0J T|j:nl.`=|F[ s{p5m 0 3 9 0I tyjzzmL4L* @`&Tp5j 0B@0S @Y0TYPv͎0  @ ؟a3j?f7C  ` 0+x a9?g @ ؟o2j{3lC4T~'@?2zek5C}4 GCF#@*?CE3j{34U XPeMhB M(; N+ hh @@g(sLk s9* U$  @дh^Y͐WTK@h @BPH#p5tE XPt{MhK mɦ? H+ h  @ ϐa,y  Ed @.Ѕrװ!;ѐV?TCd ` R fh ذ!`ECm4  @K3tεfH*!@ mACQ kH W3؛Ct"@ KACmS4 ЗV3%`Z @g3܀.?\`od%`ECVR, з@׷{sW$@ oACS= С:NR fH)J!@ iACQ RF7XЍ P= @- ؟EDf7C RI Xѐd[E(`^MV3gkd4_#@?CC 3\`oT4$E @ ؟!5[ zPW͖XP ezp52m U^]E tj:fX- hfO ` :d 5TT@) @9 ؟!Ǯ-V 9 أaS  @F؟aJO W3؛X  @9 ؟!^ [9 tFT/`znj{3s3%@AC;F%@ ?C!2 ysأa, @ ؟W3؛~% XЮ  @ϐyg(jB9 s`9K u{ u, h_@о+ @ ؟!Q s`93 أaF( @ ؟W3؛^! XН+ @ ؟!Y s93 fr Pp5 Э[oW#@2?C&ZLsfG H!@?C=f7C}6;wW%@?C Z<s*f4̀ @.3j{3g#@?b~uk sNÆ İ;';g2lavy{~_~ᢋ.>?leVa\j}{_rÞ{9xk, Pϰ wqGxk^N >y~[7GG~޾ 'z |;U?<|C fϗJsf(mo뮻./g}fq'?yy П@ ?f}sÛp衇:+\r%?io? buwni"UA>2^KL>>?>cJ}؟ΖEx>φ%K_R)vupGO>̦ j6SO /~!oX~J&P/npڙ_8]pg>/ о@u!~n1^_}|n۟!^p5Ci{3 ?G>pqDž;< O?p V^qc9Vj6=y`?_ûQ/{~XVn?V?s:{2_"1g(wͧ zk?g'=Ikۆp0C 3lACܘ*.fmF-pyN~za-zW6~98-//yӮRu9ϱ㮗'ö =fJSi᪫ fk7'< cuk.xt'PϰY D ~o|#;bU7Q Oӎ |ncE_Ɠ'7&;/ ~9w!C_rw}2d™'7|^^8yr ]vY8cva[\Ʒ{ַN:(@5 }pw'xmw[o=สaݺuç!æM(w 6Eq6xׇK_y|83aM ^pgov;8>y{fj6ax{\{sD|K'>awό 7oL鷭gr_E{>hװ;9% j|w[]bүU2;+rTua}oo {{}I %-&hHQKf=F %CJݨ!C]^=RW5  З]u  @FV&*dHsQG`(FFx8t]Մ 5wGW@Я @&M0|ظqҬ!) 7HyQ@Pc۝]G!Ǯ\ tJP@Pa[BF k @`ag2*UqV5w&@}ACƮ@ d!C%^rV5, t4@- pH?)T'dH y`UC}R%y j  @`&>( dHjaQ@0# @ ]C2!Ce\Ϫ2h# hH*!@%Kf~!\U =7 (J@PT;M@P_ϛU M 5w  P==!C}bV5t hHBr{̬jC5 (Q@PbW͉F Fd!%]U IGqd" hȤQ$@Y5/kUC=6C4o  !L)K0K Xհ4\@P ` ]*r 9u+Zjȷw*'@ AC}P$ `CMPaqR Pf*& pͩˣ ]jV}@ 9 8`!C} j9 Є Ec @@+dޕjp @`QAâr#@ݶ\Эag  0I@0Ik @`aJO 5jXΉT, hN wa/y%@ WACS7$# phB,ԜU Y: u,  @rBݮU  el @ M2onj[@АwTO fka6'G/`UC=Py*  @ c x%=뉊HS@Аf_TE 6oas!`UC}R% wuP@)[PH#MV 5p2'U yI' hޕ  @@A/Ah) hhS @`!7C!) =4%Pb di,`UCS;m 6>XPAȰ jHE $@'ACO.Kf9p2e(`UC]S3]Pv  Ѐ@n"yo A@K @er tع9 XՐSJ@W]4,j dhцK^[@:4t r @i!C5^V5%5 ХKm"@- 8Zl!) bBAEg]#eRZ@е @  5e!) A KAWW]7ER슚C@Їk @ =4%!)  HIAȐRGՒU )uC-% hKu  @@B XՐ|H@ OR'p2M `UC ]P} wm AȐPU ɷH( hh @ GQx@x[˳>jf X  w<KI6 \za ο/go| ЍE_Ǜv~߅ ݰJrkx^ y 0 99T+W8+ &U 9" E$ @RWC]Qm 6> @@V5Tz'P< @@V5-l|R4 @%`UCQ4f4̀ @Xհs M@А[K U ٵL,! hXϩ @faV) ! @ h" h@@! @"`U,J!@ wACT? @@6V5d* a < @WyO@n:^ @jȺ}'@`A H!@ ФU Mj uD= @ XP|M@o @} XЗ жma @!`UO P6 @@V555 0M@0M @Z%X ЫW~'@]'P @@FV5d, 0a&& @hOlL@] @fV5l24d@ @!`UC}4 B4  @$ `UCMPa4 @jX/ h* @ 0@@ h @XPL+M@j[o @) XՐbWD<yK @:@v Z4Fk` @ XհHC@АFTA @`36  2jR  @^) u| @V Ud  @r!na(O @ XՐ`SDDAD/ @W~] 9 @@V5tb,) hX @ma ФIMc @hI` K@I H @y75"q5* @jhԀ hhՐ @ Uc дiQ @hQq M@#F B @qv4,nL @X  0aF( @HET:F Fx @@V5$ X@PqM @ _ . h(G PU Ŷd- hȺ}'@Yo4 @*`UT" бcp#@ ФU Mj& M( @@V54! @jȫ_%P @@V5Tf$!6) @d&x Y @V^ׅnk dia;^ ( @6kdy @@†feY @X# lXC :4t @6%4{3'@D@PIM@"D  @m 56 61 @ 7$$ hHJ!@ жma hp @L@PYM@] @@ †e Y @* lJ4, @JyHG@АN/TB @aC/.JXAC51 @ fr$ }J @aC56Q Z58 @ yKR45 @Q@#K(@@P@M @@†EGAC=6S @s r0w   @6c c`sno4$]#@ maCku4VM @ aC20*a  @fӖ@Za6 @z+ lm-cr @, lȹzN`@ @@R†a24LM @u ?  @ 3iJEAC&@ @`ia>HQ@АbỦ @a;4dQ&$@ oaCoy  @譀 t  @Y@[{.2Q$@xg߮[nv&bTlaC~EaUaW-b^ жw4] @Db_qh5mqΆ6M`iA> @ l0焕+_x?n8`I İp 7 ٰ~$iR& h[ŭ0|G=A0f8?~8u† H`a= @ 1t8xL@~M}c( & @^L0FE<&@MaC&0, ht^`րaH0*1m ڒ7.aAðG @U PQ hC@І1 =<"@;`8xL@†ōG`X@0@(aTc64m,a @@m Q hJ@Дq =<"@ 0B FE<&@ aC 0, hd'z0 *p u a# @n(aTc6ԩoa @@ Q K@P~ =<"@ t-`8xL@†:TI`X@0HN(aTc6T-?a @@2} F" PJM}4 {xDZ{0ZèT% lJR?  @ K r†rnZXJ@а} @tM†A`A4Z%@ f85†a" hEO[ PB@Pm&%p"@`*aT\&0V@0 PZ" PF@PFMa @@IP$L l!4,cE@0m@ }* hUP{ 0" `i@†g Z78tI@v5i* lH2敲!d! `ȢL 8,PC† F`@J J%LH!L@&†L eI(I @@N\9 g 99COCku6T%. \]k#@J 0fӉ!R( 8AC&2Mh^@мyJ# RHO@ؐ^M(AC:0HD@H!!B Ŕ4$Q @ )T!9ҭhS@ЦS4Z"@ Qwb C'h*6Tʩ:PDK @rrnZm88T@ذ}4 ~P@â׸dC&!nmhuL RH#pV=@YaCY9$ hR50,bcM`uK #aCF2Z R0PA[9( lpY@[;:* `ha3]!™6   "KACe3iXL@mRyhV@Ь4Q @ 3iڸqrh]@z LaAC#@ YyCF$ЦM}c7- hhZx 0afB$$ pHBfaCOF@АL)L0,'dg&6Ln|  CoJms _㾯P3  !Zډ @UaCY@P3  @`zfZtW@ZasVֺ !ârcƨ DQaCkH@a @`a=F"_@ؐ `X@04( `hP8tsaCO-SAã>v#@ m=y'^xᅴ'kv *'g}6?[Zl55^[56^k6U*fװcO7pC'>ナZ^~6wя~c?.O^x_?sÆͯxp{h{±tO  py3<ÿ{pƙgί{Eկ'?pezj:guVvm_dWկ~5{ ^y [n֒^}pmu͛]RV? +wnPg Ϲ$~Eሹ2k:lk샆 /0za]w w\mC=# . r)ܱ + y𧏅~ %o f0N!ʌ~'ᨣ z׻^{mxWj|'* phc7_9ƍ/<#{ua ҭͬ3kج+W\qE2rLJo=|; .g& hȬ`c߅3]Ne]e( `Ȱh|Gnip}}=k졇}' pH&U~݂lzOIx/w ,ʌSkج? [mUKBX:|ǹ Юwνs%&5ny3.}-tIa4d+xL<-mv}f]fkRxx-vKg6UIfۗװY wqG;hMwqx s.?-Œ6᷿D?jNV1HZ!>xa^-`ȧlk-a\c |IolpovSgufaZ<ɱIM op뭷kצ6=B/a^ׇ{wɲؙ@k~ǟ g†5vva~%"۽bl?v+_|zCZvfoK]c.˙1p ɖjɉm+&z`n h?3ws緲~θ/a=5k,zq<-o>Xؐw ÿ{ km)R~Nts=7<3ⶸ/uW ~e^dC3[}y u?j= 'xbK~?Gqn n)m0v N% dȫ^vŊ~ -_u[@ؐwW54^KmB|j5L6믷}GêU_a'/Kweijnp׮zᶛ^Z xsχ|3w4W8D8񽠕!Cu*;ˋ/8zs׽~p}kl|az }k@깟Νu!~[>p7]bC¯,Ɛ6%,32tk샆x 9}7|SW;l^tD yWw|sV; 2+X>/ܳ{p '̅-٣f) Rs2~D %ay2Tװ+S6\s뺀> \}ko[@v_@֟P~ yͬ2_C+ lXJ> s͍XBL]@@0Ct@@Ё"ZBVBe&@rH†cv2tVB`a$J!0avC=' hH&fTB@PMSr( :TLKIR@ȐdYLACHC@ؐF̢{BԊL# lF˱&2Ln ̌6,cBh蠀EV ACȆhV@ЬѺ+ dnm@aC5m E[' h^MhN@4 0a6? tU@ZWSBӶ 6aCm:<3 fԼBޖ 4Y?j 8@ ]U- dZT R, lP=2ЖI"aCE鼀%E =aCjjE Ri,+ d(+]+h &r` =+X@P1:# dL)-& 򭝙# dU& l[ŭw9!rBw]@ [_A@P B޲ $ l V + hU ү+ dW* lk{^@0/ ~x†K2|5 j}BdKcb-Z@7d:†tja&q6 ~oB܊4,co =(% N64m6 m;UAC1F r!C $@ ]2tWV@PVN :WR ( dp* ЦM}c) dSW߹ rW* lSg () ΁ :WR X@P16_C+ dp& w6T sE@0}49a*.'( dH(Dw682d_B hH@a6W33 dp& w6\s[J@Ȱ} 6 qx!""N@Ȑ]LeAC0|†kd rΆ ]/' h(U =+x2dX4S&@; / &* hH0!!3!u{Bn4 :V,GЁ"ZHN@Ȑ\IL(3ACf3 6 tI;Tͼ"dȻ~f!:Ef† :XTK"@; Z/ tD@БBZF†͍A@L @ꦽ6 IDAT!C1 yl6$VLGЃ"["h\@8;. hx-~aCF dp& 'lS]]wS@ͺZU†{8Ed9PvbT@[v†MA@L @׻v!Czﷀ .$\@P9 8< 0avC=l28 w6hlB&0a%R@0% B B u@`"ADL"0az3-6 / mYZ@Ȱ4T/#†2,Kxgs`j!dI@06,o Bg&Άɭ~g! hhCݘ6S/X05Be@AC-:%P6Ml dp& @w6zK!C+l}) RuN@йμ !̄: @w68 B 44m0!By  @`vlݰ+=RIY@Аs=[aClB(uDaEoz[z OL@АXAL?†ztBQ  P:z2V1յ ɗ *'! †I7:_b L@АYL{†t܊ dl'@@†MSQȐje̫>Wړ6$S&"dV+ lKәBΔB:& hXA-'_aC[nB'@@}†lYvO`a=64N^Bډ @e ew!p =+/ lHFP0 P~F24%m $P6:24Fm L, l* ɖ 8< !ZL;!ôb'@@s†欫IP' hVf6LxB H S@z LTLyaCeG2ӎ 7/;v4god &j@!Ck&@@iaCi 6J r@}†lgY0hO@Оr# @tkcf †I  0af;2TNC 6 VՃ*I @}aC5a^4[;3ﱀ گ @jaCբ'dL ) R9@@0RMj-6W!C{F&PjQhP@ ơ ͛M Aм ) hSW64q!CsF"@@†* dhH44%m5 jص~c# @ 5aC2olmP7& 5nRP  3 mW*6T+!Cz$@@n†+&dTR4T s!Pč]r6TWA!Cuz"!ʘ 3ml*dP蚀a f7 9T 6@DPNKt]@PBvZM@А[̗†)6*dL M@0}Ņ ӛiA gAC3w&@xar+G @†!V$ACW*i6,qay#G @acGBTl#}ACkl†'a= aa=. hz&†M062Ml!@ E/!C}4J`N@i dx=M@𢟐E U@[w !zx" lAP˩S + FasH/ Ps d(諀ns}  N}[aJ4U/%P@!C姏  @`@!Øf=4N`^aa&@6:C /AC^2[ t9l2vr dXv豀ŷt]  Ub dh,2 yl .ХAPbPKaa¢;@ =.'ЅA0 @@[] m=%!z-r &"@)r Sz, hq-r9 Bj?-c dh1> yl 4.S dh0 )l2,fz, hq-9 BI8HE AȐb4U/%К@a 0@aajN L+b d'@R  %C /AC^2[ 6Z?L*H)l2TTT豀ŷteR e*B dH0/y HFͰAȐi`" P@ab@ =.UA0kմ'@ G /AC^2[ 46+  @@MM B[=4N*&!CU"D dl0Oy HVΰAȐlMj3l2\<豀ŷtU 6 @ 7:!CngK@АW̖@U Bm А@a@ =.KAPWuK T6ryK@АW̖@6 Bll а,ab@ =.[L d*'@ʄ Bܫn4U/%4a!0$0M dhH%cACo$l24U  @@W&  ]uK@АW̖@K Blj вRa@6{a[: r-# ^c//u3/ G~xXs!FL'~zժp 7<9:7 |;nzo}k:\{aSG P@C xӛ򖷄k׆;#< Sxqꪰrʰ~ ysL+o>U&@ /0via{7<a= 7|se]\L%ЇװY EK|W2\qo~B?>׿w"3wz.zymp/sv>OO~zb-Y~}8묳E]1sN;4* 7pC8#.Ұ;,[fM8f-s6!zDA'&B K 8I1u#@=k&ZjCPa"O= ~aDhLg?Yu]á.E=M7y䑰N;-z4#!_2 a뭷. z%ꫯy{ m @93|_X,dJǔ׍vp- bQ|}U@rK~ cnM&@ qJ7xc 34$ 5lAO<1LM 7z?~fC]R7םwyθkchO`98\l&\cӱ@s<_cH 5t1q׬fǥnq+73<3v"x _7矓d~c _3矓mkج7%g1,v~,mθ?~͖h tPxK_u}x '`oz:C>MwO@ O=puׅ38#oq[A2&О@vkhkꦷx͍ٷ탯t4/ЗװYD<-͟F$P@C9$vpw~:!~ = 3.8.0 if (sf_extSoftVersion()["GEOS"] >= "3.8.0") { iso2 <- st_make_valid(iso) st_is_valid(iso2, reason=TRUE) # the plot should be unchanged ggplot(iso2, aes(fill = id)) + geom_sf() } # alternatively, if we shift all data values by a tiny # amount (here, 1e-10) so they don't coincide with the band # limits, no invalid geometries are generated. raw <- isobands(1:5, 5:1, m + 1e-10, levels_low = 0:1, levels_high = 1:2) bands <- iso_to_sfg(raw) iso <- st_sf(id = factor(1:length(bands)), geometry = st_sfc(bands)) st_is_valid(iso, reason = TRUE) } } isoband/man/isoband-package.Rd0000644000176200001440000000167214073123757015762 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/isoband.R \docType{package} \name{isoband-package} \alias{isoband} \alias{isoband-package} \title{isoband: Generate Isolines and Isobands from Regularly Spaced Elevation Grids} \description{ A fast C++ implementation to generate contour lines (isolines) and contour polygons (isobands) from regularly spaced grids containing elevation data. } \seealso{ Useful links: \itemize{ \item \url{https://wilkelab.org/isoband/} \item Report bugs at \url{https://github.com/wilkelab/isoband/issues} } } \author{ \strong{Maintainer}: Claus O. Wilke \email{wilke@austin.utexas.edu} (\href{https://orcid.org/0000-0002-7470-9261}{ORCID}) Authors: \itemize{ \item Thomas Lin Pedersen \email{thomasp85@gmail.com} (\href{https://orcid.org/0000-0002-5147-4711}{ORCID}) } Other contributors: \itemize{ \item testthat and Catch authors (testthat C++ testing code) [contributor] } } isoband/man/isobands.Rd0000644000176200001440000000537313501223537014546 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/isobands.R \name{isobands} \alias{isobands} \alias{isolines} \title{Efficient calculation of isolines and isobands from elevation grid} \usage{ isobands(x, y, z, levels_low, levels_high) isolines(x, y, z, levels) } \arguments{ \item{x}{Numeric vector specifying the x locations of the grid points.} \item{y}{Numeric vector specifying the y locations of the grid points.} \item{z}{Numeric matrix specifying the elevation values for each grid point.} \item{levels_low, levels_high}{Numeric vectors of minimum/maximum z values for which isobands should be generated. Any z values that are exactly equal to a value in \code{levels_low} are considered part of the corresponding isoband, but any z values that are exactly equal to a value in \code{levels_high} are not considered part of the corresponding isoband. In other words, the intervals specifying isobands are closed at their lower boundary and open at their upper boundary.} \item{levels}{Numeric vector of z values for which isolines should be generated.} } \description{ Efficient calculation of isolines and isobands from elevation grid } \examples{ library(grid) #' # one simple connected shape m <- matrix(c(0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) df_bands <- isobands((1:ncol(m))/(ncol(m)+1), (nrow(m):1)/(nrow(m)+1), m, 0.5, 1.5)[[1]] df_lines <- isolines((1:ncol(m))/(ncol(m)+1), (nrow(m):1)/(nrow(m)+1), m, 0.5)[[1]] g <- expand.grid(x = (1:ncol(m))/(ncol(m)+1), y = (nrow(m):1)/(nrow(m)+1)) grid.newpage() grid.points(g$x, g$y, default.units = "npc", pch = 19, size = unit(0.5, "char")) grid.path(df_bands$x, df_bands$y, df_bands$id, gp = gpar(fill = "cornsilk", col = NA)) grid.polyline(df_lines$x, df_lines$y, df_lines$id) # a similar plot can be generated with the plot_iso() function, # which is useful for exploring how the algorithm works plot_iso(m, 0.5, 1.5) # NAs are ignored m <- matrix(c(NA, NA, NA, 0, 0, 0, NA, NA, NA, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) plot_iso(m, 0.5, 1.5) # two separate shapes m <- matrix(c(0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0.8, 0), 4, 4, byrow = TRUE) plot_iso(m, 0.5, 1.5) # shape with hole m <- matrix(c(0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) plot_iso(m, 0.5, 1.5) } \seealso{ \code{\link{plot_iso}} } isoband/man/isobands_grob.Rd0000644000176200001440000000215413501223537015551 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/isobands-grob.R \name{isobands_grob} \alias{isobands_grob} \title{Render isobands} \usage{ isobands_grob(bands, gp = gpar(), units = "npc") } \arguments{ \item{bands}{Isobands, as produced by the \code{\link[=isobands]{isobands()}} function.} \item{gp}{Grid graphical parameters. Parameters are recycled among the total number of bands drawn.} \item{units}{A character string specifying the units in which to interpret the isobands coordinates. Defaults to \code{"npc"}.} } \description{ This function generates a grid grob that represents isobands. } \examples{ library(grid) viridis_pal <- colorRampPalette( c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"), space = "Lab" ) x <- (1:ncol(volcano))/(ncol(volcano)+1) y <- (nrow(volcano):1)/(nrow(volcano)+1) bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39)) b <- isobands_grob( bands, gp = gpar(col = "black", fill = viridis_pal(21), alpha = 0.5) ) grid.newpage() grid.draw(b) } \seealso{ See \code{\link[=isolines_grob]{isolines_grob()}} for drawing of isolines. } isoband/man/plot_iso.Rd0000644000176200001440000000250613644532420014571 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/plot-iso.R \name{plot_iso} \alias{plot_iso} \title{Visualize a single isoband} \usage{ plot_iso( m, vlo, vhi, fill_lo = "gray95", fill_mid = "gray50", fill_hi = "black", fill_band = "cornsilk", col_lo = "black", col_hi = "black", newpage = TRUE ) } \arguments{ \item{m}{input matrix} \item{vlo}{lower cutoff for isobanding} \item{vhi}{higher cutoff for isobanding} \item{fill_lo}{fill color for points below the lower cutoff} \item{fill_mid}{fill color for points between the two cutoffs} \item{fill_hi}{fill color for points above the higher cutoff} \item{fill_band}{fill color for the isoband} \item{col_lo}{line color for lower cutoff} \item{col_hi}{line color for higher cutoff} \item{newpage}{boolean, indicating whether \code{grid.newpage()} should be called or not} } \description{ This function visualizes a single isoband calculated from a matrix. It is mainly useful for debugging and visualizing the isobanding algorithm. See \code{\link[=isobands]{isobands()}} for more examples. } \examples{ m <- matrix(c(0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) plot_iso(m, 0.5, 1.5) } isoband/man/angle_halfcircle_bottom.Rd0000644000176200001440000000144613501223537017567 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/label-placer.R \name{angle_halfcircle_bottom} \alias{angle_halfcircle_bottom} \alias{angle_halfcircle_right} \alias{angle_fixed} \alias{angle_identity} \title{Standardize label angles} \usage{ angle_halfcircle_bottom() angle_halfcircle_right() angle_fixed(theta = 0) angle_identity() } \arguments{ \item{theta}{Fixed angle, in radians.} } \description{ Function factories that return functions to standardize rotation angles to specific angle ranges. } \details{ \code{angle_halfcircle_bottom()} standardizes angles to (-pi/2, pi/2]. \code{angle_halfcircle_right()} standardizes angles to (0, pi]. \code{angle_fixed()} sets all angles to a fixed value (0 by default). \code{angle_identity()} does not modify any angles. } isoband/man/label_placer_simple.Rd0000644000176200001440000000234713501223537016720 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/label-placer.R \name{label_placer_simple} \alias{label_placer_simple} \title{Generic label placement function} \usage{ label_placer_simple(lines, labels_data, placer_fun) } \arguments{ \item{lines}{Isolines object for which labels should be placed.} \item{labels_data}{A data frame containing information about which labels should be placed.} \item{placer_fun}{A function that takes an individual isoline plus its associated break id as input and returns a data frame specifying label positions. The data frame should have three columns called \code{x}, \code{y}, and \code{theta}. \code{x} and \code{y} specify the label position, and \code{theta} specifies the label angle in radians. The data frame can have multiple rows, which results in the same label being placed in multiple locations.} } \description{ The simple label placer processes separate isolines independently and places labels for each line using a placer function that does the actual placement work. This label placer is not meant to be used by end users, but rather facilitates the development of new label placers, such as \code{\link[=label_placer_minmax]{label_placer_minmax()}}. } \keyword{internal} isoband/DESCRIPTION0000644000176200001440000000303414073215605013402 0ustar liggesusersPackage: isoband Title: Generate Isolines and Isobands from Regularly Spaced Elevation Grids Version: 0.2.5 Authors@R: c(person(given = "Claus O.", family = "Wilke", role = c("aut", "cre"), email = "wilke@austin.utexas.edu", comment = c(ORCID = "0000-0002-7470-9261")), person(given = "Thomas Lin", family = "Pedersen", role = "aut", email = "thomasp85@gmail.com", comment = c(ORCID = "0000-0002-5147-4711")), person(given = "testthat and Catch authors", role = "ctb", comment = "testthat C++ testing code")) Description: A fast C++ implementation to generate contour lines (isolines) and contour polygons (isobands) from regularly spaced grids containing elevation data. URL: https://wilkelab.org/isoband/ BugReports: https://github.com/wilkelab/isoband/issues License: MIT + file LICENSE Encoding: UTF-8 Imports: grid, utils RoxygenNote: 7.1.1 Config/testthat/edition: 3 Suggests: covr, ggplot2, knitr, magick, microbenchmark, rmarkdown, sf, testthat, xml2 SystemRequirements: C++11 VignetteBuilder: knitr NeedsCompilation: yes Packaged: 2021-07-12 20:44:13 UTC; clauswilke Author: Claus O. Wilke [aut, cre] (), Thomas Lin Pedersen [aut] (), testthat and Catch authors [ctb] (testthat C++ testing code) Maintainer: Claus O. Wilke Repository: CRAN Date/Publication: 2021-07-13 04:50:13 UTC isoband/build/0000755000176200001440000000000014073124635012775 5ustar liggesusersisoband/build/vignette.rds0000644000176200001440000000034414073124635015335 0ustar liggesusersu @׏,%A|!|C":t]su [Oaf{!&VHb^HV14|TmA@Ik&n8PEL%u!D{s T#ΐ=3䦙`ё0JpbXF2_/vl!,X{g/ ]׵ rњk+V!isoband/tests/0000755000176200001440000000000013501223537013034 5ustar liggesusersisoband/tests/testthat/0000755000176200001440000000000014073215605014676 5ustar liggesusersisoband/tests/testthat/test-isolines-grob.R0000644000176200001440000000252013757762552020571 0ustar liggesuserstest_that("basic functions", { m <- matrix(c(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0), 5, 5, byrow = TRUE) l <- isolines((1:5)/6, (5:1)/6, m, c(.5, 1.5)) # incorrect number of labels expect_error( isolines_grob(l, labels = c("a", "b", "c")), "Number of labels must match the number of breaks." ) # incorrect margin specification expect_error( isolines_grob(l, margin = 1:4), "must be a unit object of length four" ) expect_error( isolines_grob(l, margin = grid::unit(1:3, "pt")), "must be a unit object of length four" ) # default settings create two labels per line for this dataset g <- isolines_grob(l, label_placer = label_placer_minmax(n = 0)) expect_equal(g$labels_data$break_id, c("0.5", "0.5", "1.5", "1.5")) expect_equal(g$labels_data$label, c("0.5", "0.5", "1.5", "1.5")) expect_equal(g$labels_data$x, rep(0.5, 4)) expect_equal(g$labels_data$theta, rep(0, 4)) expect_true(all(abs(g$labels_data$y - c(0.75, 0.25, 0.5833333, 0.4166667)) < 1e-7)) # extra breaks are ignored g <- isolines_grob(l, breaks = c("0.5", "1.5", "2.5"), labels = c("a", "b", "c")) expect_equal(g$labels_data$break_id, c("0.5", "0.5", "1.5", "1.5")) expect_equal(g$labels_data$label, c("a", "a", "b", "b")) }) isoband/tests/testthat/test-cpp.R0000644000176200001440000000003113760132231016545 0ustar liggesusersrun_cpp_tests("isoband") isoband/tests/testthat/test-label-placer.R0000644000176200001440000001032014064403653020317 0ustar liggesuserstest_that("minmax label placer", { lines <- list( "1" = list(x = c(.5, .75, .5, .25), y = c(.25, .5, .75, .5), id = rep(1, 4)), "2" = list(x = c(.5, 1, .5, 0), y = c(0, .5, 1, .5), id = rep(1, 4)) ) labels_data <- data.frame( index = 1:2, break_index = c(3, 7), break_id = c("1", "2"), label = c("a", "b"), stringsAsFactors = FALSE ) lp <- label_placer_minmax(n = 0) out <- lp(lines, labels_data) expect_equal(out$index, rep(1:2, each = 2)) expect_equal(out$break_index, rep(c(3, 7), each = 2)) expect_equal(out$break_id, rep(c("1", "2"), each = 2)) expect_equal(out$label, rep(c("a", "b"), each = 2)) expect_equal(out$x, rep(0.5, 4)) expect_equal(out$y, c(0.75, 0.25, 1, 0)) expect_equal(out$theta, rep(0, 4)) lp <- label_placer_minmax(placement = "rl", rot_adjuster = angle_fixed(1.5), n = 0) out <- lp(lines, labels_data) expect_equal(out$x, c(0.25, 0.75, 0, 1)) expect_equal(out$y, rep(0.5, 4)) expect_equal(out$theta, rep(1.5, 4)) lp <- label_placer_minmax(placement = NULL) out <- lp(lines, labels_data) expect_equal(nrow(out), 0) }) test_that("angle adjustments", { theta <- c(-3, -2, -1, 0, 1, 2, 3) expect_equal( angle_halfcircle_bottom()(theta), c(-3 + pi, -2 + pi, -1, 0, 1, 2 - pi, 3 - pi) ) expect_equal( angle_halfcircle_right()(theta + 2), c(-1 + pi, 0 + pi, 1, 2, 3, 4 - pi, 5 - pi) ) expect_equal( angle_fixed()(theta), rep(0, length(theta)) ) expect_equal( angle_fixed(2)(theta), rep(2, length(theta)) ) expect_equal( angle_identity()(theta), theta ) }) test_that("none label placer", { lines <- list( "1" = list(x = c(.5, .75, .5, .25), y = c(.25, .5, .75, .5), id = rep(1, 4)), "2" = list(x = c(.5, 1, .5, 0), y = c(0, .5, 1, .5), id = rep(1, 4)) ) labels_data <- data.frame( index = 1:2, break_index = c(3, 7), break_id = c("1", "2"), label = c("a", "b"), stringsAsFactors = FALSE ) lp <- label_placer_none() out <- lp(lines, labels_data) expect_equal(nrow(out), 0) }) test_that("manual label placer", { lines <- list( "1" = list(x = c(.5, .75, .5, .25), y = c(.25, .5, .75, .5), id = rep(1, 4)), "2" = list(x = c(.5, 1, .5, 0), y = c(0, .5, 1, .5), id = rep(1, 4)) ) labels_data <- data.frame( index = 1:2, break_index = c(3, 7), break_id = c("1", "2"), label = c("a", "b"), stringsAsFactors = FALSE ) lp <- label_placer_manual( breaks = c("1", "2", "3", "2"), x = 1:4, y = 4:1, theta = (1:4)-2 ) out <- lp(lines, labels_data) expect_equal(out$index, c(1, 2, 2)) expect_equal(out$break_index, c(3, 7, 7)) expect_equal(out$break_id, c("1", "2", "2")) expect_equal(out$label, c("a", "b", "b")) expect_equal(out$x, c(1, 2, 4)) expect_equal(out$y, c(4, 3, 1)) expect_equal(out$theta, c(-1, 0, 2)) }) # Two isolines id=1 test_that("middle label placer", { lines <- list( "1" = list(x = c(.5, .75, .5, .25), y = c(.25, .5, .75, .5), id = rep(1, 4)), "2" = list(x = c(.5, 1, .5, 0), y = c(0, .5, 1, .5), id = rep(1, 4)) ) labels_data <- data.frame( index = 1:2, break_index = c(3, 7), break_id = c("1", "2"), label = c("a", "b"), stringsAsFactors = FALSE ) lp <- label_placer_middle() out <- lp(lines, labels_data) expect_equal(out$index, 1:2) expect_equal(out$break_index, c(3, 7)) expect_equal(out$break_id, c("1", "2")) expect_equal(out$label, c("a", "b")) expect_equal(out$x, c(0.75, 1)) expect_equal(out$y, c(0.5, 0.5)) expect_equal(out$theta, c(pi / 2, pi / 2)) }) # One isoline with two id values (1 and 2) test_that("middle label placer", { lines <- list( "1" = list(x = c(.5, .75, .5, .25, .5, 1, .5, 0), y = c(.25, .5, .75, .5, 0, .5, 1, .5), id = c(rep(1, 4), rep(2, 4))) ) labels_data <- data.frame( index = 1, break_index = 3, break_id = "1", label = "a", stringsAsFactors = FALSE ) lp <- label_placer_middle() out <- lp(lines, labels_data) expect_equal(out$index, c(1, 1)) expect_equal(out$break_index, c(3, 3)) expect_equal(out$break_id, c("1", "1")) expect_equal(out$label, c("a", "a")) expect_equal(out$x, c(0.75, 1)) expect_equal(out$y, c(0.5, 0.5)) expect_equal(out$theta, c(pi / 2, pi / 2)) }) isoband/tests/testthat/test-clip-lines.R0000644000176200001440000001203313757762532020052 0ustar liggesuserstest_that("basic clipping", { x <- c(0, 0, 1, 1, 0) y <- c(0, 1, 1, 0, 0) id <- rep(1L, 5) # clip box entirely inside line out <- clip_lines_impl(x, y, id, .5, .5, .8, .8, 0) expect_identical(out$x, x) expect_identical(out$y, y) expect_identical(out$id, id) # clip box entirely outside line out <- clip_lines_impl(x, y, id, .5, .5, 1.2, 1.2, 0) expect_identical(out$x, numeric(0)) expect_identical(out$y, numeric(0)) expect_identical(out$id, integer(0)) # clip top right corner out <- clip_lines_impl(x, y, id, 1, 1, 1, 1, 0) expect_equal(out$x, c(0.0, 0.0, 0.5, 1.0, 1.0, 0.0)) expect_equal(out$y, c(0.0, 1.0, 1.0, 0.5, 0.0, 0.0)) expect_identical(out$id, c(rep(1L, 3), rep(2L, 3))) # clip bottom left corner out <- clip_lines_impl(x, y, id, 0, 0, 1, 1, 0) expect_equal(out$x, c(0.0, 0.0, 1.0, 1.0, 0.5)) expect_equal(out$y, c(0.5, 1.0, 1.0, 0.0, 0.0)) expect_identical(out$id, rep(1L, 5)) # clip right half out <- clip_lines_impl(x, y, id, 1, .5, 1, 2, 0) expect_equal(out$x, c(0.0, 0.0, 0.5, 0.5, 0.0)) expect_equal(out$y, c(0, 1, 1, 0, 0)) expect_identical(out$id, c(rep(1L, 3), 2L, 2L)) # clip left half out <- clip_lines_impl(x, y, id, 0, .5, 1, 2, 0) expect_equal(out$x, c(0.5, 1.0, 1.0, 0.5)) expect_equal(out$y, c(1, 1, 0, 0)) expect_identical(out$id, rep(1L, 4)) # clip in middle out <- clip_lines_impl(x, y, id, .5, .5, 2, .5, 0) expect_equal(out$x, c(0, 0, 0, 0, 1, 1, 1, 1, 0)) expect_equal(out$y, c(0.00, 0.25, 0.75, 1.00, 1.00, 0.75, 0.25, 0.00, 0.00)) expect_identical(out$id, c(rep(1L, 2), rep(2L, 4), rep(3L, 3))) out <- clip_lines_impl(x, y, id, .5, .5, .5, 2, 0) expect_equal(out$x, c(0.00, 0.00, 0.25, 0.75, 1.00, 1.00, 0.75, 0.25, 0.00)) expect_equal(out$y, c(0, 1, 1, 1, 1, 0, 0, 0, 0)) expect_identical(out$id, c(rep(1L, 3), rep(2L, 4), rep(3L, 2))) }) test_that("clip multiple line segments", { x <- c(0, 0, 1, 1, 2, 2, 3, 3) y <- c(0, 1, 1, 0, 2, 3, 3, 2) id <- c(rep(1L, 4), rep(2L, 4)) out <- clip_lines_impl(x, y, id, 0, 0, 1, 3, 0) expect_equal(out$x, c(0.5, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0)) expect_equal(out$y, c(1, 1, 0, 2, 3, 3, 2)) expect_identical(out$id, c(rep(1L, 3), rep(2L, 4))) out <- clip_lines_impl(x, y, id, 1.5, 1.5, 2, 4, 0) expect_equal(out$x, c(0.0, 0.0, 0.5, 2.5, 3.0, 3.0)) expect_equal(out$y, c(0, 1, 1, 3, 3, 2)) expect_identical(out$id, c(rep(1L, 3), rep(2L, 3))) }) test_that("rotated clip box", { x <- c(0, 0, 1, 1, 0) y <- c(0, 1, 1, 0, 0) id <- rep(1L, 5) out <- clip_lines_impl(x, y, id, .5, .5, 5, sin(2*pi*45/360), 2*pi*45/360) expect_equal(out$x, c(0.0, 0.0, 0.5, 1.0, 1.0, 0.5)) expect_equal(out$y, c(0.5, 1, 1, 0.5, 0, 0)) expect_identical(out$id, c(rep(1L, 3), rep(2L, 3))) out <- clip_lines_impl(x, y, id, .5, .5, 5, sin(2*pi*45/360), -2*pi*45/360) expect_equal(out$x, c(0.0, 0.0, 0.5, 1.0, 1.0, 0.5, 0.0)) expect_equal(out$y, c(0.0, 0.5, 1, 1, 0.5, 0, 0)) expect_identical(out$id, c(rep(1L, 2), rep(2L, 3), rep(3L, 2))) out <- clip_lines_impl(x, y, id, .5, .5, sin(2*pi*45/360), 5, 2*pi*45/360) expect_equal(out$x, c(0.0, 0.0, 0.5, 1.0, 1.0, 0.5, 0.0)) expect_equal(out$y, c(0.0, 0.5, 1, 1, 0.5, 0, 0)) expect_identical(out$id, c(rep(1L, 2), rep(2L, 3), rep(3L, 2))) }) test_that("singletons are carried over or clipped", { x <- c(0, 0, 1, 1, 2, 2, 3, 3) y <- c(0, 1, 1, 0, 2, 3, 3, 2) id <- c(1L, rep(2L, 3), rep(3L, 4)) out <- clip_lines_impl(x, y, id, 10, 10, 1, 1, 0) expect_identical(out$x, x) expect_identical(out$y, y) expect_identical(out$id, id) out <- clip_lines_impl(x, y, id, 0, 0, .1, .1, 0) expect_identical(out$x, x[2:8]) expect_identical(out$y, y[2:8]) expect_identical(out$id, id[2:8] - 1L) x <- c(0, 0, 1, 1, 2, 2, 3, 3) y <- c(0, 1, 1, 0, 2, 3, 3, 2) id <- c(rep(1L, 3), 2L, rep(3L, 4)) out <- clip_lines_impl(x, y, id, 10, 10, 1, 1, 0) expect_equal(out$x, x) expect_equal(out$y, y) expect_identical(out$id, id) out <- clip_lines_impl(x, y, id, 1, 0, .1, .1, 0) expect_identical(out$x, x[c(1:3, 5:8)]) expect_identical(out$y, y[c(1:3, 5:8)]) expect_identical(out$id, c(rep(1L, 3), rep(2L, 4))) x <- c(0, 0, 1, 1, 2, 2, 3, 3) y <- c(0, 1, 1, 0, 2, 3, 3, 2) id <- c(rep(1L, 3), rep(2L, 4), 3L) out <- clip_lines_impl(x, y, id, 10, 10, 1, 1, 0) expect_equal(out$x, x) expect_equal(out$y, y) expect_identical(out$id, id) out <- clip_lines_impl(x, y, id, 3, 2, .1, .1, 0) expect_identical(out$x, x[1:7]) expect_identical(out$y, y[1:7]) expect_identical(out$id, id[1:7]) }) test_that("empty or incorrect input", { out <- clip_lines_impl(numeric(0), numeric(0), integer(0), 3, 2, .1, .1, 0) expect_identical(out$x, numeric(0)) expect_identical(out$y, numeric(0)) expect_identical(out$id, integer(0)) expect_error( clip_lines_impl(numeric(0), numeric(1), integer(0), 3, 2, .1, .1, 0), "must match" ) expect_error( clip_lines_impl(numeric(0), numeric(0), integer(1), 3, 2, .1, .1, 0), "must match" ) expect_error( clip_lines_impl(numeric(1), numeric(0), integer(0), 3, 2, .1, .1, 0), "must match" ) }) isoband/tests/testthat/test-isolines.R0000644000176200001440000000744013757762555017653 0ustar liggesuserstest_that("line segments get merged", { # two connected line segments get merged z <- matrix(c(0, 0, 1, 1, 1, 1), ncol = 3, nrow = 2, byrow = TRUE) out <- isolines(x = 1:3, y = 2:1, z, levels = 0.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(1, 2, 2.5) + c(1.5, 1.5, 2.0)) expect_equal(out[[1]]$id, rep(1, 3)) # two unconnected line segments don't get merged z <- matrix(c(0, 1, 0, 0, 1, 1), ncol = 3, nrow = 2, byrow = TRUE) out <- isolines(x = 1:3, y = 2:1, z, levels = 0.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(2.5, 3.0, 1.5, 1.5) + c(2.0, 1.5, 2.0, 1.0)) expect_setequal(out[[1]]$id, c(1:2)) expect_equal(length(out[[1]]$id), 4) # two separate lines get merged in second row z <- matrix(c(0, 1, 0, 0, 1, 0, 0, 0, 0), ncol = 3, nrow = 3, byrow = TRUE) out <- isolines(x = 1:3, y = 3:1, z, levels = 0.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(2.5, 2.5, 2.0, 1.5, 1.5) + c(3.0, 2.0, 1.5, 2.0, 3.0)) expect_equal(out[[1]]$id, rep(1, 5)) # circle gets closed z <- matrix(c(0, 0, 0, 0, 1, 0, 0, 0, 0), ncol = 3, nrow = 3, byrow = TRUE) out <- isolines(x = 1:3, y = 3:1, z, levels = 0.5) # circle is closed expect_equal(out[[1]]$x[1], out[[1]]$x[5]) expect_equal(out[[1]]$y[1], out[[1]]$y[5]) # coords are correct expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(2.5, 2.0, 1.5, 2.0, 2.5) + c(2.0, 2.5, 2.0, 1.5, 2.0)) expect_equal(out[[1]]$id, rep(1, 5)) }) test_that("NAs are handled correctly", { z <- matrix(c(NA, 0, 0, 0, 1, 1, 0, 1, 1), ncol = 3, nrow = 3, byrow = TRUE) out <- isolines(x = 1:3, y = 3:1, z, levels = 0.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(1.5, 1.5, 2.0, 3.0) + c(2.0, 1.0, 2.5, 2.5)) expect_setequal(out[[1]]$id, c(1:2)) expect_equal(length(out[[1]]$id), 4) }) test_that("All elementary segments are calculated correctly", { # a matrix that requires all elementary segments for isolines z <- matrix(c(0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1 ), ncol = 8, nrow = 5, byrow = TRUE) out <- isolines(x = 1:8, y = 5:1, z, levels = 0.5) expect_setequal( 10000*out[[1]]$x + out[[1]]$y, 10000*c(7.5, 7.0, 6.0, 5.5, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5, 3.0, 4.0, 4.5, 2.5, 2.0, 1.5, 8.0, 7.0, 6.5, 7.0, 7.5, 8.0, 6.5, 6.5, 6.0, 5.5, 5.5, 3.5, 3.5, 3.0, 2.0, 1.5, 1.0) + c(1.0, 1.5, 1.5, 2.0, 2.5, 3.0, 3.5, 3.0, 2.5, 2.0, 1.5, 1.5, 1.0, 1.0, 1.5, 1.0, 3.5, 3.5, 3.0, 2.5, 2.0, 1.5, 5.0, 4.0, 3.5, 4.0, 5.0, 5.0, 4.0, 3.5, 3.5, 3.0, 2.5) ) expect_setequal(out[[1]]$id, c(1:5)) expect_equal(length(out[[1]]$id), 33) }) test_that("Saddles", { # a matrix that contains all saddles (there are only two) z <- matrix(c(0, 1, 0, 1, 0, 1), ncol = 3, nrow = 2, byrow = TRUE) out <- isolines(x = 1:3, y = 2:1, z, levels = 0.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(2.5, 3.0, 2.5, 2.0, 1.5, 1.5, 1.0) + c(2.0, 1.5, 1.0, 1.5, 1.0, 2.0, 1.5)) expect_setequal(out[[1]]$id, c(1:3)) expect_equal(length(out[[1]]$id), 7) out <- isolines(x = 1:3, y = 2:1, z, levels = 0.6) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(1.6, 2.0, 2.4, 3.0, 2.6, 1.0, 1.4) + c(2.0, 1.6, 2.0, 1.4, 1.0, 1.4, 1.0)) expect_setequal(out[[1]]$id, c(1:3)) expect_equal(length(out[[1]]$id), 7) }) isoband/tests/testthat/test-isobands.R0000644000176200001440000003525313757764151017625 0ustar liggesuserstest_that("elementary polygons get merged", { # two connected polygons get merged z <- matrix(c(0, 0, 1, 1, 1, 1), ncol = 3, nrow = 2, byrow = TRUE) out <- isobands(x = 1:3, y = 2:1, z, levels_low = 0.5, levels_high = 1.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(3.0, 2.0, 1.0, 1.0, 2.0, 2.5, 3.0) + c(1.0, 1.0, 1.0, 1.5, 1.5, 2.0, 2.0)) expect_equal(out[[1]]$id, rep(1, 7)) # # two unconnected polygons don't get merged z <- matrix(c(1, 2, 1, 1, 2, 2), ncol = 3, nrow = 2, byrow = TRUE) out <- isobands(x = 1:3, y = 2:1, z, levels_low = 0.5, levels_high = 1.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(3.0, 2.5, 3.0, 1.0, 1.5, 1.5, 1.0) + c(1.5, 2.0, 2.0, 2.0, 2.0, 1.0, 1.0)) expect_setequal(out[[1]]$id, c(1:2)) expect_equal(length(out[[1]]$id), 7) # two separate bands get merged in second row z <- matrix(c(1, 2, 1, 1, 2, 1, 0, 0, 0), ncol = 3, nrow = 3, byrow = TRUE) out <- isobands(x = 1:3, y = 3:1, z, levels_low = 0.5, levels_high = 1.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(3.0, 2.0, 1.0, 1.0, 1.0, 1.5, 1.5, 2.0, 2.5, 2.5, 3.0, 3.0) + c(1.50, 1.25, 1.50, 2.00, 3.00, 3.00, 2.00, 1.75, 2.00, 3.00, 3.00, 2.00)) expect_equal(out[[1]]$id, rep(1, 12)) # circle gets closed z <- matrix(c(1, 1, 1, 1, 2, 1, 1, 1, 1), ncol = 3, nrow = 3, byrow = TRUE) out <- isobands(x = 1:3, y = 3:1, z, levels_low = 0.5, levels_high = 1.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(3.0, 2.0, 1.0, 1.0, 1.0, 2.0, 3.0, 3.0, 2.0, 2.5, 2.0, 1.5) + c(1.0, 1.0, 1.0, 2.0, 3.0, 3.0, 3.0, 2.0, 1.5, 2.0, 2.5, 2.0)) expect_setequal(out[[1]]$id, c(1:2)) expect_equal(length(out[[1]]$id), 12) }) test_that("NAs are handled correctly", { z <- matrix(c(NA, 1, 1, 1, 1, 1, 1, 1, 1), ncol = 3, nrow = 3, byrow = TRUE) out <- isobands(x = 1:3, y = 3:1, z, levels_low = 0.5, levels_high = 1.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(3, 2, 1, 1, 2, 2, 3, 3) + c(1, 1, 1, 2, 2, 3, 3, 2)) expect_equal(out[[1]]$id, rep(1, 8)) z <- matrix(c(NA, 1, 1, 1, 1, 1, 1, 1, NA), ncol = 3, nrow = 3, byrow = TRUE) out <- isobands(x = 1:3, y = 3:1, z, levels_low = 0.5, levels_high = 1.5) expect_setequal(10000*out[[1]]$x + out[[1]]$y, 10000*c(1, 1, 2, 2, 2, 2, 3, 3) + c(1, 2, 2, 1, 2, 3, 3, 2)) expect_setequal(out[[1]]$id, c(1:2)) expect_equal(length(out[[1]]$id), 8) }) test_that("All elementary shapes are calculated correctly", { # a matrix that requires all elementary shapes for isobanding z <- matrix(c(0, 0, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2, 0, 0, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2, 0, 0, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2, 0, 0, 0, 1, 0, 2, 2, 0, 2, 1, 2, 2, 0, 0, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2, 0, 0, 2, 0, 2, 1, 2, 2, 0, 0, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2, 0, 0, 0, 1, 0, 2, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2, 0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2, 0, 0, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2), ncol = 18, nrow = 11, byrow = TRUE) out <- isobands(x = 1:18, y = 11:1, z, levels_low = 0.5, levels_high = 1.5) expect_setequal( 10000*out[[1]]$x + out[[1]]$y, 10000*c(16.00, 15.50, 15.50, 16.00, 16.25, 16.00, 15.00, 14.50, 14.75, 14.75, 14.25, 14.25, 14.00, 13.75, 13.75, 13.25, 13.25, 13.50, 13.00, 12.75, 12.00, 11.50, 11.50, 11.00, 10.00, 9.00, 8.50, 8.50, 8.00, 7.50, 7.50, 7.00, 6.50, 6.50, 6.00, 5.75, 5.75, 5.25, 5.25, 5.00, 4.50, 4.50, 4.00, 3.50, 3.50, 3.25, 3.00, 2.50, 2.00, 1.50, 1.00, 1.00, 1.00, 1.25, 1.00, 1.00, 1.50, 1.00, 1.00, 1.25, 1.00, 1.00, 1.00, 1.50, 2.00, 2.50, 3.00, 3.25, 3.50, 3.50, 4.00, 4.50, 4.50, 5.00, 5.25, 5.25, 5.75, 5.75, 6.00, 6.50, 6.50, 7.00, 7.50, 7.50, 8.00, 8.50, 8.50, 9.00, 10.00, 11.00, 11.50, 11.50, 12.00, 12.75, 13.00, 13.50, 13.25, 13.25, 13.75, 13.75, 14.00, 14.25, 14.25, 14.75, 14.75, 14.50, 15.00, 16.00, 16.25, 16.00, 15.50, 15.50, 16.00, 16.50, 16.50, 17.00, 18.00, 18.00, 17.00, 16.75, 16.50, 17.00, 17.25, 18.00, 18.00, 17.75, 17.50, 18.00, 18.00, 17.00, 16.75, 17.00, 18.00, 18.00, 17.50, 17.75, 18.00, 18.00, 17.25, 17.00, 16.50, 16.75, 17.00, 18.00, 18.00, 17.00, 16.50, 16.50, 12.00, 12.25, 12.00, 11.75, 11.00, 11.25, 12.00, 12.25, 12.50, 12.00, 11.75, 11.00, 10.50, 10.00, 9.50, 9.00, 8.75, 9.00, 9.50, 10.00, 6.00, 6.50, 6.00, 5.50, 4.00, 4.50, 4.00, 3.75, 13.00, 14.00, 15.00, 15.50, 15.00, 14.50, 14.00, 13.50, 13.00, 12.75, 8.00, 8.25, 9.00, 9.50, 9.00, 8.00, 7.00, 6.75, 7.00, 7.75, 7.00, 7.25, 7.00, 6.25, 6.00, 5.00, 4.50, 5.00, 6.00, 6.25, 7.00, 7.25, 7.00, 6.00, 5.50, 5.00, 4.50, 4.00, 3.75, 4.00, 4.50, 5.00, 5.50, 6.00, 2.00, 2.50, 2.25, 2.00, 1.75, 1.50, 11.00, 11.25, 11.00, 10.50, 13.00, 13.50, 13.00, 12.75, 12.00, 12.25, 12.00, 11.75, 11.00, 11.25, 11.00, 10.00, 9.50, 10.00, 9.00, 8.25, 8.00, 7.75, 7.00, 6.75, 7.00, 8.00, 9.00, 9.50, 16.25, 16.00, 15.00, 14.50, 15.00, 16.00, 15.00, 14.00, 13.00, 12.75, 13.00, 13.50, 14.00, 14.50, 15.00, 15.50, 12.00, 12.50, 12.25, 12.00, 11.25, 11.00, 10.00, 9.50, 9.00, 8.75, 9.00, 9.50, 10.00, 10.50, 11.00, 11.75, 11.00, 11.25, 11.00, 10.50, 6.00, 5.50, 6.00, 6.50, 3.00, 2.50, 3.00, 3.25, 3.00, 3.50, 3.00, 2.75, 2.00, 2.25, 2.50, 2.00, 1.50, 1.75, 3.00, 2.75, 3.00, 3.50, 4.00, 3.75, 4.00, 4.50, 12.00, 12.25, 12.00, 11.75, 6.00, 6.50, 6.00, 5.50) + c( 1.00, 1.00, 2.00, 2.50, 3.00, 3.50, 3.25, 3.00, 2.00, 1.00, 1.00, 2.00, 2.50, 2.00, 1.00, 1.00, 2.00, 3.00, 3.25, 3.00, 2.25, 2.00, 1.00, 1.00, 1.00, 1.00, 1.00, 2.00, 2.50, 2.00, 1.00, 1.00, 1.00, 2.00, 2.25, 2.00, 1.00, 1.00, 2.00, 2.50, 2.00, 1.00, 1.00, 1.00, 2.00, 3.00, 3.50, 3.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 5.25, 5.75, 6.00, 6.25, 6.75, 7.00, 7.50, 8.00, 8.50, 9.00, 9.50, 9.00, 8.50, 9.00, 10.00, 11.00, 11.00, 11.00, 10.00, 9.50, 10.00, 11.00, 11.00, 10.00, 9.75, 10.00, 11.00, 11.00, 11.00, 10.00, 9.50, 10.00, 11.00, 11.00, 11.00, 11.00, 11.00, 10.00, 9.75, 9.00, 8.75, 9.00, 10.00, 11.00, 11.00, 10.00, 9.50, 10.00, 11.00, 11.00, 10.00, 9.00, 8.75, 8.50, 9.00, 9.50, 10.00, 11.00, 11.00, 11.00, 10.00, 9.75, 9.75, 9.25, 9.25, 9.00, 8.00, 7.50, 8.00, 8.75, 8.25, 8.00, 7.00, 6.75, 6.25, 6.50, 6.00, 5.50, 5.75, 5.25, 5.00, 4.00, 3.75, 3.25, 4.00, 4.50, 4.00, 3.00, 2.75, 2.75, 2.25, 2.25, 2.00, 1.00, 2.75, 3.00, 3.25, 3.00, 2.50, 3.00, 3.75, 4.00, 5.00, 5.25, 5.00, 4.25, 4.00, 3.50, 4.00, 4.25, 4.00, 3.50, 3.00, 2.50, 2.75, 3.00, 3.25, 3.00, 2.50, 3.00, 3.50, 3.00, 3.75, 3.50, 3.75, 4.00, 4.50, 5.00, 5.50, 5.00, 4.50, 4.00, 3.50, 4.00, 4.75, 5.00, 5.50, 5.50, 5.50, 5.00, 4.75, 4.00, 3.50, 4.00, 4.25, 5.00, 5.25, 5.50, 6.00, 6.50, 6.75, 7.00, 7.75, 8.00, 8.50, 8.25, 8.00, 7.50, 7.00, 6.50, 6.00, 5.50, 5.00, 4.50, 4.00, 3.75, 3.50, 4.00, 5.00, 5.50, 5.00, 4.00, 4.75, 5.00, 5.25, 5.00, 5.50, 6.00, 6.50, 6.00, 5.75, 6.00, 6.25, 6.00, 5.75, 6.00, 6.25, 6.50, 6.00, 5.50, 7.25, 8.00, 8.50, 8.00, 7.25, 7.00, 6.50, 6.50, 6.50, 7.00, 6.00, 6.50, 6.50, 6.00, 5.50, 5.50, 8.25, 8.50, 8.25, 8.00, 7.50, 7.00, 6.50, 7.00, 7.50, 8.00, 6.75, 7.00, 8.00, 8.25, 9.00, 9.50, 9.50, 9.00, 8.50, 8.00, 7.75, 8.00, 8.50, 8.00, 7.75, 7.00, 6.75, 7.00, 7.25, 7.00, 6.25, 6.00, 5.75, 6.00, 6.25, 6.00, 5.75, 6.00, 6.75, 7.00, 7.50, 7.00, 6.50, 7.00, 8.00, 8.50, 8.00, 7.00, 5.25, 5.00, 4.50, 5.00, 9.50, 9.00, 8.50, 9.00, 8.75, 9.00, 9.25, 9.00, 8.75, 9.00, 9.25, 9.00) ) expect_setequal(out[[1]]$id, c(1:26)) expect_equal(length(out[[1]]$id), 324) }) test_that("Six-sided saddles", { # a matrix that contains all six-sided saddles z <- matrix(c(0, 1, 1, 2, 1, 0, 2, 1, 0, 1, 1, 2), ncol = 4, nrow = 3, byrow = TRUE) # midpoint outside the band out <- isobands(x = 1:4, y = 3:1, z, levels_low = 0.6, levels_high = 1.4) expect_setequal( 10000*out[[1]]$x + out[[1]]$y, 10000*c(3.0, 2.0, 1.6, 2.0, 2.3, 2.0, 1.6, 2.0, 3.0, 3.4, 3.0, 2.7, 3.0, 3.4, 1.0, 1.0, 1.0, 1.4, 4.0, 4.0, 4.0, 3.6) + c(1.0, 1.0, 1.0, 1.4, 2.0, 2.6, 3.0, 3.0, 3.0, 3.0, 2.6, 2.0, 1.4, 1.0, 1.6, 2.0, 2.4, 2.0, 2.4, 2.0, 1.6, 2.0) ) expect_setequal(out[[1]]$id, c(1:3)) expect_equal(length(out[[1]]$id), 22) # midpoint inside the band out <- isobands(x = 1:4, y = 3:1, z, levels_low = 0.4, levels_high = 1.6) expect_setequal( 10000*out[[1]]$x + out[[1]]$y, 10000*c(3.0, 2.0, 1.4, 1.0, 1.0, 1.0, 1.4, 2.0, 3.0, 3.6, 4.0, 4.0, 4.0, 3.6, 3.0, 3.4, 3.0, 2.8, 2.0, 2.2, 2.0, 1.6) + c(1.0, 1.0, 1.0, 1.4, 2.0, 2.6, 3.0, 3.0, 3.0, 3.0, 2.6, 2.0, 1.4, 1.0, 1.6, 2.0, 2.4, 2.0, 1.6, 2.0, 2.4, 2.0) ) expect_setequal(out[[1]]$id, c(1:3)) expect_equal(length(out[[1]]$id), 22) }) test_that("Seven-sided saddles", { # a matrix that contains all seven-sided saddles z <- matrix(c(0, 1, 0, 1, 2, 1, 2, 0, 2, 2, 0, 2, 0, 1, 0, 1, 2, 1), ncol = 6, nrow = 3, byrow = TRUE) # midpoint inside the band out <- isobands(x = 1:6, y = 3:1, z, levels_low = 0.5, levels_high = 1.5) expect_setequal( 10000*out[[1]]$x + out[[1]]$y, 10000*c(6.00, 6.00, 5.50, 5.00, 4.50, 4.00, 3.50, 3.00, 2.50, 2.00, 1.50, 1.00, 1.00, 1.25, 1.00, 1.00, 1.50, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 5.50, 6.00, 6.00, 5.75, 4.00, 4.25, 4.00, 3.00, 2.75, 3.00, 2.00, 2.25, 2.00, 1.75, 5.00, 5.25, 5.00, 4.75) + c(1.50, 1.00, 1.00, 1.25, 1.00, 1.00, 1.00, 1.25, 1.00, 1.00, 1.00, 1.25, 1.75, 2.00, 2.25, 2.75, 3.00, 3.00, 3.00, 2.75, 3.00, 3.00, 3.00, 2.75, 3.00, 3.00, 2.50, 2.00, 1.50, 2.00, 2.50, 2.25, 2.00, 1.75, 1.50, 2.00, 2.50, 2.00, 1.75, 2.00, 2.25, 2.00) ) expect_setequal(out[[1]]$id, c(1:4)) expect_equal(length(out[[1]]$id), 42) # midpoint outside the band out <- isobands(x = 1:6, y = 3:1, z, levels_low = 0.8, levels_high = 1.2) expect_setequal( 10000*out[[1]]$x + out[[1]]$y, 10000*c(6.0, 6.0, 5.8, 5.0, 4.4, 5.0, 5.6, 4.2, 4.0, 3.8, 3.0, 2.4, 3.0, 3.8, 4.0, 4.2, 4.0, 3.0, 2.6, 3.0, 4.0, 2.2, 2.0, 1.8, 2.0, 1.0, 1.4, 1.0, 1.0, 1.6, 1.0, 5.0, 5.4, 5.0, 4.6, 6.0, 5.8, 6.0, 2.2, 2.0, 1.8, 2.0) + c(1.2, 1.0, 1.0, 1.4, 2.0, 2.6, 2.0, 1.0, 1.0, 1.0, 1.4, 2.0, 2.6, 3.0, 3.0, 3.0, 2.8, 2.4, 2.0, 1.6, 1.2, 1.0, 1.0, 1.0, 1.2, 1.6, 2.0, 2.4, 2.6, 2.0, 1.4, 1.6, 2.0, 2.4, 2.0, 2.8, 3.0, 3.0, 3.0, 2.8, 3.0, 3.0) ) expect_setequal(out[[1]]$id, c(1:8)) expect_equal(length(out[[1]]$id), 42) }) test_that("Eight-sided saddles", { # a matrix that contains all eight-sided saddles z <- matrix(c(0, 2, 0, 2, 0, 2), ncol = 3, nrow = 2, byrow = TRUE) # midpoint above the band out <- isobands(x = 1:3, y = 2:1, z, levels_low = 0.5, levels_high = 0.8) expect_setequal( 10000*out[[1]]$x + out[[1]]$y, 10000*c(3.00, 3.00, 2.60, 2.75, 2.00, 2.40, 2.25, 2.00, 1.75, 1.60, 1.00, 1.00, 1.25, 1.40) + c(1.75, 1.60, 2.00, 2.00, 1.40, 1.00, 1.00, 1.25, 1.00, 1.00, 1.60, 1.75, 2.00, 2.00) ) expect_setequal(out[[1]]$id, c(1:3)) expect_equal(length(out[[1]]$id), 14) # midpoint inside the band out <- isobands(x = 1:3, y = 2:1, z, levels_low = 0.5, levels_high = 1.5) expect_setequal( 10000*out[[1]]$x + out[[1]]$y, 10000*c(2.75, 2.25, 2.00, 1.75, 1.25, 1.00, 1.00, 1.25, 1.75, 2.00, 2.25, 2.75, 3.00, 3.00) + c(1.00, 1.00, 1.25, 1.00, 1.00, 1.25, 1.75, 2.00, 2.00, 1.75, 2.00, 2.00, 1.75, 1.25) ) expect_equal(out[[1]]$id, rep(1, 14)) # midpoint below the band out <- isobands(x = 1:3, y = 2:1, z, levels_low = 1.2, levels_high = 1.5) expect_setequal( 10000*out[[1]]$x + out[[1]]$y, 10000*c(3.00, 3.00, 2.75, 2.60, 2.40, 2.00, 1.60, 1.75, 2.00, 2.25, 1.40, 1.25, 1.00, 1.00) + c(1.40, 1.25, 1.00, 1.00, 2.00, 1.60, 2.00, 2.00, 1.75, 2.00, 1.00, 1.00, 1.25, 1.40) ) expect_setequal(out[[1]]$id, c(1:3)) expect_equal(length(out[[1]]$id), 14) }) test_that("Inconsistent numbers of isoband levels cause an error", { m <- matrix(c(0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0), 4, 4, byrow = TRUE) # single values are recycled expect_silent( isobands(1:4, 1:4, m, 0.5, c(0.5, 1.5)) ) expect_silent( isobands(1:4, 1:4, m, c(0.5, 1.5), 0.5) ) # error, multiple values are not recycled expect_error( isobands(1:4, 1:4, m, c(0.5, 1.5, 2.5), c(0.5, 1.5)), "must be of equal length or of length 1" ) expect_error( isobands(1:4, 1:4, m, c(0.5, 1.5), c(0.5, 1.5, 2.5)), "must be of equal length or of length 1" ) }) test_that("Swap isoband levels if given in the wrong order", { m <- matrix(c(0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0), 4, 4, byrow = TRUE) out1 <- isobands(1:4, 1:4, m, c(-.5, 0.5), c(0.5, 1.5)) out2 <- isobands(1:4, 1:4, m, c(0.5, 1.5), c(-.5, 0.5)) expect_equal(out1, out2) }) isoband/tests/testthat/test-iso-to-sfg.R0000644000176200001440000000707513757762543020016 0ustar liggesuserstest_that("conversion of isolines to sfg", { #m <- matrix(c(0, 0, 0, 2, # 0, 1, 0, 2, # 0, 0, 0, 0), 3, 4, byrow = TRUE) #lines <- isolines(1:4, 3:1, m, c(0.5, 1.5)) lines <- structure( list( `0.5` = list( x = c(2.00, 2.50, 2.00, 1.50, 2.00, 3.25, 3.25, 4.00), y = c(1.50, 2.00, 2.50, 2.00, 1.50, 3.00, 2.00, 1.25), id = c(1, 1, 1, 1, 1, 2, 2, 2) ), `1.5` = list( x = c(3.75, 3.75, 4.00), y = c(3.00, 2.00, 1.75), id = c(1, 1, 1) ) ), class = c("isolines", "iso") ) out <- iso_to_sfg(lines) expect_equal( out[["0.5"]][[1]], cbind( c(2.00, 2.50, 2.00, 1.50, 2.00), c(1.50, 2.00, 2.50, 2.00, 1.50) ) ) expect_equal( out[["0.5"]][[2]], cbind( c(3.25, 3.25, 4.00), c(3.00, 2.00, 1.25) ) ) expect_equal( class(out[["0.5"]]), c("XY", "MULTILINESTRING", "sfg") ) expect_equal( out[["1.5"]][[1]], cbind( c(3.75, 3.75, 4.00), c(3.00, 2.00, 1.75) ) ) expect_equal( class(out[["1.5"]]), c("XY", "MULTILINESTRING", "sfg") ) }) test_that("conversion of isobands to sfg", { #m <- matrix(c(0, 0, 0, 0, 0, 0, # 0, 2, 2, 2, 2, 0, # 0, 2, 0, 0, 2, 0, # 0, 2, 0, 0, 2, 0, # 0, 2, 2, 2, 2, 0, # 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) #bands <- isobands(1:6, 1:6, m, 0.5, 1.5) bands <- structure( list( `0.5:1.5` = list( x = c(5.00, 4.00, 3.00, 2.00, 1.25, 1.25, 1.25, 1.25, 2.00, 3.00, 4.00, 5.00, 5.75, 5.75, 5.75, 5.75, 5.25, 5.25, 5.25, 5.25, 5.00, 4.00, 3.00, 2.00, 1.75, 1.75, 1.75, 1.75, 2.00, 3.00, 4.00, 5.00, 4.25, 4.25, 4.00, 3.00, 2.75, 2.75, 3.00, 4.00, 4.75, 4.00, 3.00, 2.25, 2.25, 3.00, 4.00, 4.75), y = c(5.75, 5.75, 5.75, 5.75, 5.00, 4.00, 3.00, 2.00, 1.25, 1.25, 1.25, 1.25, 2.00, 3.00, 4.00, 5.00, 5.00, 4.00, 3.00, 2.00, 1.75, 1.75, 1.75, 1.75, 2.00, 3.00, 4.00, 5.00, 5.25, 5.25, 5.25, 5.25, 4.00, 3.00, 2.75, 2.75, 3.00, 4.00, 4.25, 4.25, 4.00, 4.75, 4.75, 4.00, 3.00, 2.25, 2.25, 3.00), id = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4) ) ), class = c("isobands", "iso") ) out <- iso_to_sfg(bands) expect_equal( out[["0.5:1.5"]][[1]][[1]], cbind( c(5.00, 4.00, 3.00, 2.00, 1.25, 1.25, 1.25, 1.25, 2.00, 3.00, 4.00, 5.00, 5.75, 5.75, 5.75, 5.75, 5.00), c(5.75, 5.75, 5.75, 5.75, 5.00, 4.00, 3.00, 2.00, 1.25, 1.25, 1.25, 1.25, 2.00, 3.00, 4.00, 5.00, 5.75) ) ) expect_equal( out[["0.5:1.5"]][[1]][[2]], cbind( c(5.25, 5.00, 4.00, 3.00, 2.00, 1.75, 1.75, 1.75, 1.75, 2.00, 3.00, 4.00, 5.00, 5.25, 5.25, 5.25, 5.25), c(5.00, 5.25, 5.25, 5.25, 5.25, 5.00, 4.00, 3.00, 2.00, 1.75, 1.75, 1.75, 1.75, 2.00, 3.00, 4.00, 5.00) ) ) expect_equal( out[["0.5:1.5"]][[2]][[1]], cbind( c(4.75, 4.00, 3.00, 2.25, 2.25, 3.00, 4.00, 4.75, 4.75), c(4.00, 4.75, 4.75, 4.00, 3.00, 2.25, 2.25, 3.00, 4.00) ) ) expect_equal( out[["0.5:1.5"]][[2]][[2]], cbind( c(4.25, 4.00, 3.00, 2.75, 2.75, 3.00, 4.00, 4.25, 4.25), c(4.00, 4.25, 4.25, 4.00, 3.00, 2.75, 2.75, 3.00, 4.00) ) ) expect_equal( class(out[["0.5:1.5"]]), c("XY", "MULTIPOLYGON", "sfg") ) }) isoband/tests/testthat.R0000644000176200001440000000007213501223537015016 0ustar liggesuserslibrary(testthat) library(isoband) test_check("isoband") isoband/src/0000755000176200001440000000000014073124635012465 5ustar liggesusersisoband/src/isoband.cpp0000644000176200001440000016154413673436547014640 0ustar liggesusers// This file implements the 2D isoline and isoband algorithms described // here: https://en.wikipedia.org/wiki/Marching_squares // Includes merging of line segments and polygons. // Written by Claus O. Wilke #define R_NO_REMAP #include #include #include #include #include using namespace std; #include "polygon.h" // for point #include "utils.h" // point in abstract grid space enum point_type { grid, // point on the original data grid hintersect_lo, // intersection with horizontal edge, low value hintersect_hi, // intersection with horizontal edge, high value vintersect_lo, // intersection with vertical edge, low value vintersect_hi // intersection with vertical edge, high value }; struct grid_point { int r, c; // row and column point_type type; // point type // default constructor; negative values indicate non-existing point off grid grid_point(double r_in = -1, double c_in = -1, point_type type_in = grid) : r(r_in), c(c_in), type(type_in) {} // copy constructor grid_point(const grid_point &p) : r(p.r), c(p.c), type(p.type) {} }; // hash function for grid_point struct grid_point_hasher { size_t operator()(const grid_point& p) const { // this should work up to about 100,000,000 rows/columns return hash()( (static_cast(p.r) << 30) ^ (static_cast(p.c) << 3) ^ static_cast(p.type)); } }; bool operator==(const grid_point &p1, const grid_point &p2) { return (p1.r == p2.r) && (p1.c == p2.c) && (p1.type == p2.type); } ostream & operator<<(ostream &out, const grid_point &p) { out << "(" << p.c << ", " << p.r << ", " << p.type << ")"; return out; } // connection between points in grid space struct point_connect { grid_point prev, next; // previous and next points in polygon grid_point prev2, next2; // alternative previous and next, when two separate polygons have vertices on the same grid point bool altpoint; // does this connection hold an alternative point? bool collected, collected2; // has this connection been collected into a final polygon? point_connect() : altpoint(false), collected(false), collected2(false) {}; }; ostream & operator<<(ostream &out, const point_connect &pc) { out << "prev: " << pc.prev << "; next: " << pc.next << " "; if (pc.altpoint) { out << "AP prev: " << pc.prev2 << "; next2: " << pc.next2 << " "; } return out; } class isobander { protected: int nrow, ncol; // numbers of rows and columns SEXP grid_x, grid_y, grid_z; double *grid_x_p, *grid_y_p, *grid_z_p; double vlo, vhi; // low and high cutoff values grid_point tmp_poly[8]; // temp storage for elementary polygons; none has more than 8 vertices point_connect tmp_point_connect[8]; int tmp_poly_size; // current number of elements in tmp_poly typedef unordered_map gridmap; gridmap polygon_grid; bool interrupted; void reset_grid() { polygon_grid.clear(); for (int i=0; i<8; i++) { tmp_point_connect[i] = point_connect(); } } // internal member functions double central_value(int r, int c) {// calculates the central value of a given cell return (grid_z_p[r + c * nrow] + grid_z_p[r + (c + 1) * nrow] + grid_z_p[r + 1 + c * nrow] + grid_z_p[r + 1 + (c + 1) * nrow])/4; } void poly_start(int r, int c, point_type type) { // start a new elementary polygon tmp_poly[0].r = r; tmp_poly[0].c = c; tmp_poly[0].type = type; tmp_poly_size = 1; } void poly_add(int r, int c, point_type type) { // add point to elementary polygon tmp_poly[tmp_poly_size].r = r; tmp_poly[tmp_poly_size].c = c; tmp_poly[tmp_poly_size].type = type; tmp_poly_size++; } void poly_merge() { // merge current elementary polygon to prior polygons //cout << "before merging:" << endl; bool to_delete[] = {false, false, false, false, false, false, false, false}; // first, we figure out the right connections for current polygon for (int i = 0; i < tmp_poly_size; i++) { // create defined state in tmp_point_connect[] // for each point, find previous and next point in polygon tmp_point_connect[i].altpoint = false; tmp_point_connect[i].next = tmp_poly[(i+1=0) ? i-1 : tmp_poly_size-1]; //cout << tmp_poly[i] << ": " << tmp_point_connect[i] << endl; // now merge with existing polygons if needed const grid_point &p = tmp_poly[i]; if (polygon_grid.count(p) > 0) { // point has been used before, need to merge polygons if (!polygon_grid[p].altpoint) { // basic scenario, no alternative point at this location int score = 2 * (tmp_point_connect[i].next == polygon_grid[p].prev) + (tmp_point_connect[i].prev == polygon_grid[p].next); switch (score) { case 3: // 11 // both prev and next cancel, point can be deleted to_delete[i] = true; break; case 2: // 10 // merge in "next" direction tmp_point_connect[i].next = polygon_grid[p].next; break; case 1: // 01 // merge in "prev" direction tmp_point_connect[i].prev = polygon_grid[p].prev; break; default: // 00 // if we get here, we have two polygon vertices sharing the same grid location // in an unmergable configuration; need to store both tmp_point_connect[i].prev2 = polygon_grid[p].prev; tmp_point_connect[i].next2 = polygon_grid[p].next; tmp_point_connect[i].altpoint = true; } } else { // case with alternative point at this location int score = 8 * (tmp_point_connect[i].next == polygon_grid[p].prev2) + 4 * (tmp_point_connect[i].prev == polygon_grid[p].next2) + 2 * (tmp_point_connect[i].next == polygon_grid[p].prev) + (tmp_point_connect[i].prev == polygon_grid[p].next); switch (score) { case 9: // 1001 // three-way merge tmp_point_connect[i].next = polygon_grid[p].next2; tmp_point_connect[i].prev = polygon_grid[p].prev; break; case 6: // 0110 // three-way merge tmp_point_connect[i].next = polygon_grid[p].next; tmp_point_connect[i].prev = polygon_grid[p].prev2; break; case 8: // 1000 // two-way merge with alt point only // set up merged alt point tmp_point_connect[i].next2 = polygon_grid[p].next2; tmp_point_connect[i].prev2 = tmp_point_connect[i].prev; // copy over existing point as is tmp_point_connect[i].prev = polygon_grid[p].prev; tmp_point_connect[i].next = polygon_grid[p].next; tmp_point_connect[i].altpoint = true; break; case 4: // 0100 // two-way merge with alt point only // set up merged alt point tmp_point_connect[i].prev2 = polygon_grid[p].prev2; tmp_point_connect[i].next2 = tmp_point_connect[i].next; // copy over existing point as is tmp_point_connect[i].prev = polygon_grid[p].prev; tmp_point_connect[i].next = polygon_grid[p].next; tmp_point_connect[i].altpoint = true; break; case 2: // 0010 // two-way merge with original point only // merge point tmp_point_connect[i].next = polygon_grid[p].next; // copy over existing alt point as is tmp_point_connect[i].prev2 = polygon_grid[p].prev2; tmp_point_connect[i].next2 = polygon_grid[p].next2; tmp_point_connect[i].altpoint = true; break; case 1: // 0100 // two-way merge with original point only // merge point tmp_point_connect[i].prev = polygon_grid[p].prev; // copy over existing alt point as is tmp_point_connect[i].prev2 = polygon_grid[p].prev2; tmp_point_connect[i].next2 = polygon_grid[p].next2; tmp_point_connect[i].altpoint = true; break; default: Rf_error("undefined merging configuration: %i\n", score); } } } } //cout << "after merging:" << endl; // then we copy the connections into the polygon matrix for (int i = 0; i < tmp_poly_size; i++) { const grid_point &p = tmp_poly[i]; if (to_delete[i]) { // delete point if needed polygon_grid.erase(p); } else { // otherwise, copy polygon_grid[p] = tmp_point_connect[i]; } //cout << p << ": " << tmp_point_connect[i] << endl; } //cout << "new grid:" << endl; //print_polygons_state(); } void print_polygons_state() { for (auto it = polygon_grid.begin(); it != polygon_grid.end(); it++) { cout << it->first << ": " << it->second << endl; } cout << endl; } // linear interpolation of boundary intersections double interpolate(double x0, double x1, double z0, double z1, double value) { double d = (value - z0) / (z1 - z0); double x = x0 + d * (x1 - x0); return x; } // calculate output coordinates for a given grid point point calc_point_coords(const grid_point &p) { switch(p.type) { case grid: return point(grid_x_p[p.c], grid_y_p[p.r]); case hintersect_lo: // intersection with horizontal edge, low value return point(interpolate(grid_x_p[p.c], grid_x_p[p.c+1], grid_z_p[p.r + p.c * nrow], grid_z_p[p.r + (p.c + 1) * nrow], vlo), grid_y_p[p.r]); case hintersect_hi: // intersection with horizontal edge, high value return point(interpolate(grid_x_p[p.c], grid_x_p[p.c+1], grid_z_p[p.r + p.c * nrow], grid_z_p[p.r + (p.c + 1) * nrow], vhi), grid_y_p[p.r]); case vintersect_lo: // intersection with vertical edge, low value return point(grid_x_p[p.c], interpolate(grid_y_p[p.r], grid_y_p[p.r+1], grid_z_p[p.r + p.c * nrow], grid_z_p[p.r + 1 + p.c * nrow], vlo)); case vintersect_hi: // intersection with vertical edge, high value return point(grid_x_p[p.c], interpolate(grid_y_p[p.r], grid_y_p[p.r+1], grid_z_p[p.r + p.c * nrow], grid_z_p[p.r + 1 + p.c * nrow], vhi)); default: return point(0, 0); // should never get here } } public: isobander(SEXP x, SEXP y, SEXP z, double value_low = 0, double value_high = 0) : grid_x(x), grid_y(y), grid_z(z), grid_x_p(REAL(x)), grid_y_p(REAL(y)), grid_z_p(REAL(z)), vlo(value_low), vhi(value_high), interrupted(false) { nrow = Rf_nrows(grid_z); ncol = Rf_ncols(grid_z); if (Rf_length(grid_x) != ncol) {Rf_error("Number of x coordinates must match number of columns in density matrix.");} if (Rf_length(grid_y) != nrow) {Rf_error("Number of y coordinates must match number of rows in density matrix.");} } virtual ~isobander() {} bool was_interrupted() {return interrupted;} void set_value(double value_low, double value_high) { vlo = value_low; vhi = value_high; } virtual void calculate_contour() { // clear polygon grid and associated internal variables reset_grid(); // setup matrix of ternarized cell representations vector ternarized(nrow*ncol); vector::iterator iv = ternarized.begin(); for (int i = 0; i < nrow * ncol; ++i) { *iv = (grid_z_p[i] >= vlo && grid_z_p[i] < vhi) + 2*(grid_z_p[i] >= vhi); iv++; } vector cells((nrow - 1) * (ncol - 1)); for (int r = 0; r < nrow-1; r++) { for (int c = 0; c < ncol-1; c++) { int index; if (!R_finite(grid_z_p[r + c * nrow]) || !R_finite(grid_z_p[r + (c + 1) * nrow]) || !R_finite(grid_z_p[r + 1 + c * nrow]) || !R_finite(grid_z_p[r + 1 + (c + 1) * nrow])) { // we don't draw any contours if at least one of the corners is NA index = 0; } else { index = 27*ternarized[r + c * nrow] + 9*ternarized[r + (c + 1) * nrow] + 3*ternarized[r + 1 + (c + 1) * nrow] + ternarized[r + 1 + c * nrow]; } cells[r + c * (nrow - 1)] = index; //cout << index << " "; } //cout << endl; } if (checkInterrupt()) { interrupted = true; return; } // all polygons must be drawn clockwise for proper merging for (int r = 0; r < nrow-1; r++) { for (int c = 0; c < ncol-1; c++) { //cout << r << " " << c << " " << cells(r, c) << endl; switch(cells[r + c * (nrow - 1)]) { // doing cases out of order, sorted by type, is easier to keep track of // no contour case 0: break; case 80: break; // single triangle case 1: // 0001 poly_start(r, c, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, grid); poly_merge(); break; case 3: // 0010 poly_start(r, c+1, vintersect_lo); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_merge(); break; case 9: // 0100 poly_start(r, c, hintersect_lo); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_merge(); break; case 27: // 1000 poly_start(r, c, vintersect_lo); poly_add(r, c, grid); poly_add(r, c, hintersect_lo); poly_merge(); break; case 79: // 2221 poly_start(r, c, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, grid); poly_merge(); break; case 77: // 2212 poly_start(r, c+1, vintersect_hi); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_merge(); break; case 71: // 2122 poly_start(r, c, hintersect_hi); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_merge(); break; case 53: // 1222 poly_start(r, c, vintersect_hi); poly_add(r, c, grid); poly_add(r, c, hintersect_hi); poly_merge(); break; // single trapezoid case 78: // 2220 poly_start(r, c, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); break; case 74: // 2202 poly_start(r+1, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_merge(); break; case 62: // 2022 poly_start(r, c+1, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_merge(); break; case 26: // 0222 poly_start(r, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_merge(); break; case 2: // 0002 poly_start(r, c, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); break; case 6: // 0020 poly_start(r+1, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_merge(); break; case 18: // 0200 poly_start(r, c+1, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_merge(); break; case 54: // 2000 poly_start(r, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_merge(); break; // single rectangle case 4: // 0011 poly_start(r, c, vintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c+1, grid); poly_add(r+1, c, grid); poly_merge(); break; case 12: // 0110 poly_start(r, c, hintersect_lo); poly_add(r, c+1, grid); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_merge(); break; case 36: // 1100 poly_start(r, c, grid); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); break; case 28: // 1001 poly_start(r, c, hintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, grid); poly_add(r, c, grid); poly_merge(); break; case 76: // 2211 poly_start(r, c, vintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c+1, grid); poly_add(r+1, c, grid); poly_merge(); break; case 68: // 2112 poly_start(r, c, hintersect_hi); poly_add(r, c+1, grid); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_merge(); break; case 44: // 1122 poly_start(r, c, grid); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); break; case 52: // 1221 poly_start(r, c, hintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, grid); poly_add(r, c, grid); poly_merge(); break; case 72: // 2200 poly_start(r, c, vintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r, c+1, vintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); break; case 56: // 2002 poly_start(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_merge(); break; case 8: // 0022 poly_start(r, c, vintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r, c+1, vintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); break; case 24: // 0220 poly_start(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_merge(); break; // single square case 40: // 1111 poly_start(r, c, grid); poly_add(r, c+1, grid); poly_add(r+1, c+1, grid); poly_add(r+1, c, grid); poly_merge(); break; // single pentagon case 49: // 1211 poly_start(r, c, grid); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c+1, grid); poly_add(r+1, c, grid); poly_merge(); break; case 67: // 2111 poly_start(r+1, c, grid); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c+1, grid); poly_add(r+1, c+1, grid); poly_merge(); break; case 41: // 1112 poly_start(r, c, grid); poly_add(r, c+1, grid); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); break; case 43: // 1121 poly_start(r, c, grid); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, grid); poly_merge(); break; case 31: // 1011 poly_start(r, c, grid); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c+1, grid); poly_add(r+1, c, grid); poly_merge(); break; case 13: // 0111 poly_start(r+1, c, grid); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c+1, grid); poly_add(r+1, c+1, grid); poly_merge(); break; case 39: // 1110 poly_start(r, c, grid); poly_add(r, c+1, grid); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); break; case 37: // 1101 poly_start(r, c, grid); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, grid); poly_merge(); break; case 45: // 1200 poly_start(r, c, grid); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r, c+1, vintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); break; case 15: // 0120 poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c, hintersect_lo); poly_merge(); break; case 5: // 0012 poly_start(r, c, vintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); break; case 55: // 2001 poly_start(r+1, c, grid); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r+1, c, hintersect_lo); poly_merge(); break; case 35: // 1022 poly_start(r, c, grid); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r, c+1, vintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); break; case 65: // 2102 poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c, hintersect_hi); poly_merge(); break; case 75: // 2210 poly_start(r, c, vintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); break; case 25: // 0221 poly_start(r+1, c, grid); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r+1, c, hintersect_hi); poly_merge(); break; case 29: // 1002 poly_start(r, c, grid); poly_add(r, c, hintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); break; case 63: // 2100 poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r, c, vintersect_lo); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_merge(); break; case 21: // 0210 poly_start(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_merge(); break; case 7: // 0021 poly_start(r+1, c, grid); poly_add(r, c, vintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_merge(); break; case 51: // 1220 poly_start(r, c, grid); poly_add(r, c, hintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); break; case 17: // 0122 poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r, c, vintersect_hi); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_merge(); break; case 59: // 2012 poly_start(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_merge(); break; case 73: // 2201 poly_start(r+1, c, grid); poly_add(r, c, vintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_merge(); break; // single hexagon case 22: // 0211 poly_start(r+1, c, grid); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c+1, grid); poly_merge(); break; case 66: // 2110 poly_start(r, c+1, grid); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_merge(); break; case 38: // 1102 poly_start(r, c, grid); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); break; case 34: // 1021 poly_start(r, c, grid); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, grid); poly_merge(); break; case 58: // 2011 poly_start(r+1, c, grid); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c+1, grid); poly_merge(); break; case 14: // 0112 poly_start(r, c+1, grid); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_merge(); break; case 42: // 1120 poly_start(r, c, grid); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); break; case 46: // 1201 poly_start(r, c, grid); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, grid); poly_merge(); break; case 64: // 2101 poly_start(r+1, c, grid); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_merge(); break; case 16: // 0121 poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, grid); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_merge(); break; case 32: // 1012 poly_start(r, c, grid); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); break; case 48: // 1210 poly_start(r, c, grid); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); break; // 6-sided saddle case 10: // 0101 { double vc = central_value(r, c); if (vc < vlo) { poly_start(r+1, c, grid); poly_add(r, c, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_merge(); poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r, c, hintersect_lo); poly_merge(); } else { poly_start(r+1, c, grid); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_merge(); } } break; case 30: // 1010 { double vc = central_value(r, c); if (vc < vlo) { poly_start(r, c, grid); poly_add(r, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); poly_start(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_merge(); } else { poly_start(r, c, grid); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); } } break; case 70: // 2121 { double vc = central_value(r, c); if (vc >= vhi) { poly_start(r+1, c, grid); poly_add(r, c, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_merge(); poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r, c, hintersect_hi); poly_merge(); } else { poly_start(r+1, c, grid); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_merge(); } } break; case 50: // 1212 { double vc = central_value(r, c); if (vc >= vhi) { poly_start(r, c, grid); poly_add(r, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); poly_start(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_merge(); } else { poly_start(r, c, grid); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); } } break; // 7-sided saddle case 69: // 2120 { double vc = central_value(r, c); if (vc >= vhi) { poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r, c, hintersect_hi); poly_merge(); poly_start(r, c, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); } else { poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_merge(); } } break; case 61: // 2021 { double vc = central_value(r, c); if (vc >= vhi) { poly_start(r+1, c, grid); poly_add(r, c, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_merge(); poly_start(r, c+1, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_merge(); } else { poly_start(r+1, c, grid); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_merge(); } } break; case 47: // 1202 { double vc = central_value(r, c); if (vc >= vhi) { poly_start(r, c, grid); poly_add(r, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); poly_start(r+1, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_merge(); } else { poly_start(r, c, grid); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); } } break; case 23: // 0212 { double vc = central_value(r, c); if (vc >= vhi) { poly_start(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_merge(); poly_start(r, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_merge(); } else { poly_start(r+1, c+1, grid); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_merge(); } } break; case 11: // 0102 { double vc = central_value(r, c); if (vc < vlo) { poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r, c, hintersect_lo); poly_merge(); poly_start(r, c, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); } else { poly_start(r, c+1, grid); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_merge(); } } break; case 19: // 0201 { double vc = central_value(r, c); if (vc < vlo) { poly_start(r+1, c, grid); poly_add(r, c, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_merge(); poly_start(r, c+1, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_merge(); } else { poly_start(r+1, c, grid); poly_add(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_merge(); } } break; case 33: // 1020 { double vc = central_value(r, c); if (vc < vlo) { poly_start(r, c, grid); poly_add(r, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); poly_start(r+1, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_merge(); } else { poly_start(r, c, grid); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); } } break; case 57: // 2010 { double vc = central_value(r, c); if (vc < vlo) { poly_start(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_merge(); poly_start(r, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_merge(); } else { poly_start(r+1, c+1, grid); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_add(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_merge(); } } break; // 8-sided saddle case 60: // 2020 { double vc = central_value(r, c); if (vc < vlo) { poly_start(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); poly_start(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_merge(); } else if (vc >= vhi) { poly_start(r, c, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); poly_start(r, c+1, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_merge(); } else { poly_start(r, c, vintersect_hi); poly_add(r, c, hintersect_hi); poly_add(r, c, hintersect_lo); poly_add(r, c+1, vintersect_lo); poly_add(r, c+1, vintersect_hi); poly_add(r+1, c, hintersect_hi); poly_add(r+1, c, hintersect_lo); poly_add(r, c, vintersect_lo); poly_merge(); } } break; case 20: // 0202 { double vc = central_value(r, c); if (vc < vlo) { poly_start(r, c, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); poly_start(r, c+1, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_merge(); } else if (vc >= vhi) { poly_start(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); poly_start(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_merge(); } else { poly_start(r, c, vintersect_lo); poly_add(r, c, hintersect_lo); poly_add(r, c, hintersect_hi); poly_add(r, c+1, vintersect_hi); poly_add(r, c+1, vintersect_lo); poly_add(r+1, c, hintersect_lo); poly_add(r+1, c, hintersect_hi); poly_add(r, c, vintersect_hi); poly_merge(); } } break; } } } } virtual SEXP collect() { // Early exit if calculate_contour was interrupted if (was_interrupted()) { return R_NilValue; } // make polygons vector x_out, y_out; vector id; // vectors holding resulting polygon paths int cur_id = 0; // id counter for the polygon lines // iterate over all locations in the polygon grid for (auto it = polygon_grid.begin(); it != polygon_grid.end(); it++) { if (((it->second).collected && !(it->second).altpoint) || ((it->second).collected && (it->second).collected2 && (it->second).altpoint)) { continue; // skip any grid points that are already fully collected } // we have found a new polygon line; process it cur_id++; grid_point start = it->first; grid_point cur = start; grid_point prev = (it->second).prev; // if this point has an alternative and it hasn't been collected yet then we start there if ((it->second).altpoint && !(it->second).collected2) prev = (it->second).prev2; int i = 0; do { point p = calc_point_coords(cur); x_out.push_back(p.x); y_out.push_back(p.y); id.push_back(cur_id); // record that we have processed this point and proceed to next if (polygon_grid[cur].altpoint && polygon_grid[cur].prev2 == prev) { // if an alternative point exists and its previous point in the polygon // corresponds to the recorded previous point, then that's the point // we're working with here // mark current point as collected and advance polygon_grid[cur].collected2 = true; grid_point newcur = polygon_grid[cur].next2; prev = cur; cur = newcur; } else { // mark current point as collected and advance polygon_grid[cur].collected = true; grid_point newcur = polygon_grid[cur].next; prev = cur; cur = newcur; } i++; if (i % 100000 == 0 && checkInterrupt()) { interrupted = true; return R_NilValue; } } while (!(cur == start)); // keep going until we reach the start point again } // output variable SEXP res = PROTECT(Rf_allocVector(VECSXP, 3)); SEXP names = PROTECT(Rf_allocVector(STRSXP, 3)); SET_STRING_ELT(names, 0, Rf_mkChar("x")); SET_STRING_ELT(names, 1, Rf_mkChar("y")); SET_STRING_ELT(names, 2, Rf_mkChar("id")); Rf_setAttrib(res, Rf_install("names"), names); int final_size = x_out.size(); SEXP x_final = SET_VECTOR_ELT(res, 0, Rf_allocVector(REALSXP, final_size)); double* x_final_p = REAL(x_final); SEXP y_final = SET_VECTOR_ELT(res, 1, Rf_allocVector(REALSXP, final_size)); double* y_final_p = REAL(y_final); SEXP id_final = SET_VECTOR_ELT(res, 2, Rf_allocVector(INTSXP, final_size)); int* id_final_p = INTEGER(id_final); for (int i = 0; i < final_size; ++i) { x_final_p[i] = x_out[i]; y_final_p[i] = y_out[i]; id_final_p[i] = id[i]; } UNPROTECT(2); return res; } }; class isoliner : public isobander { protected: void line_start(int r, int c, point_type type) { // start a new line segment tmp_poly[0].r = r; tmp_poly[0].c = c; tmp_poly[0].type = type; tmp_poly_size = 1; } void line_add(int r, int c, point_type type) { // add point to line tmp_poly[tmp_poly_size].r = r; tmp_poly[tmp_poly_size].c = c; tmp_poly[tmp_poly_size].type = type; tmp_poly_size++; } void line_merge() { // merge current elementary polygon to prior polygons //cout << "merging points: " << tmp_poly[0] << " " << tmp_poly[1] << endl; int score = 2*polygon_grid.count(tmp_poly[1]) + polygon_grid.count(tmp_poly[0]); switch(score) { case 0: // completely unconnected line segment polygon_grid[tmp_poly[0]].next = tmp_poly[1]; polygon_grid[tmp_poly[1]].prev = tmp_poly[0]; break; case 1: // only first point connects if (polygon_grid[tmp_poly[0]].next == grid_point()) { polygon_grid[tmp_poly[0]].next = tmp_poly[1]; polygon_grid[tmp_poly[1]].prev = tmp_poly[0]; } else if (polygon_grid[tmp_poly[0]].prev == grid_point()) { polygon_grid[tmp_poly[0]].prev = tmp_poly[1]; polygon_grid[tmp_poly[1]].next = tmp_poly[0]; } else { // should never go here Rf_error("cannot merge line segment at interior of existing line segment"); } break; case 2: // only second point connects if (polygon_grid[tmp_poly[1]].next == grid_point()) { polygon_grid[tmp_poly[1]].next = tmp_poly[0]; polygon_grid[tmp_poly[0]].prev = tmp_poly[1]; } else if (polygon_grid[tmp_poly[1]].prev == grid_point()) { polygon_grid[tmp_poly[1]].prev = tmp_poly[0]; polygon_grid[tmp_poly[0]].next = tmp_poly[1]; } else { // should never go here Rf_error("cannot merge line segment at interior of existing line segment"); } break; case 3: // two-way merge //cout << "two-way merge not implemented" << endl; //break; // two-way merge doesn't work yet { int score2 = 8*(polygon_grid[tmp_poly[0]].next == grid_point()) + 4*(polygon_grid[tmp_poly[0]].prev == grid_point()) + 2*(polygon_grid[tmp_poly[1]].next == grid_point()) + (polygon_grid[tmp_poly[1]].prev == grid_point()); switch(score2) { case 9: // 1001 polygon_grid[tmp_poly[0]].next = tmp_poly[1]; polygon_grid[tmp_poly[1]].prev = tmp_poly[0]; break; case 6: // 0110 polygon_grid[tmp_poly[0]].prev = tmp_poly[1]; polygon_grid[tmp_poly[1]].next = tmp_poly[0]; break; case 10: // 1010 { polygon_grid[tmp_poly[0]].next = tmp_poly[1]; polygon_grid[tmp_poly[1]].next = tmp_poly[0]; // need to reverse connections grid_point cur = tmp_poly[1]; int i = 0; do { grid_point tmp = polygon_grid[cur].prev; polygon_grid[cur].prev = polygon_grid[cur].next; polygon_grid[cur].next = tmp; cur = tmp; i++; if (i % 100000 == 0 && checkInterrupt()) { interrupted = true; return; } } while (!(cur == grid_point())); } break; case 5: // 0101 { polygon_grid[tmp_poly[0]].prev = tmp_poly[1]; polygon_grid[tmp_poly[1]].prev = tmp_poly[0]; // need to reverse connections grid_point cur = tmp_poly[0]; int i = 0; do { grid_point tmp = polygon_grid[cur].next; polygon_grid[cur].next = polygon_grid[cur].prev; polygon_grid[cur].prev = tmp; cur = tmp; i++; if (i % 100000 == 0 && checkInterrupt()) { interrupted = true; return; } } while (!(cur == grid_point())); } break; default: // should never go here Rf_error("cannot merge line segment at interior of existing line segment"); } } break; default: Rf_error("unknown merge state"); } //cout << "new grid:" << endl; //print_polygons_state(); } public: isoliner(SEXP x, SEXP y, SEXP z, double value = 0) : isobander(x, y, z, value, 0) {} void set_value(double value) { vlo = value; } virtual void calculate_contour() { // clear polygon grid and associated internal variables reset_grid(); // setup matrix of binarized cell representations vector binarized(nrow*ncol); vector::iterator iv = binarized.begin(); for (int i = 0; i < nrow * ncol; ++i) { *iv = (grid_z_p[i] >= vlo); iv++; } vector cells((nrow - 1) * (ncol - 1)); for (int r = 0; r < nrow-1; r++) { for (int c = 0; c < ncol-1; c++) { int index; if (!R_finite(grid_z_p[r + c * nrow]) || !R_finite(grid_z_p[r + (c + 1) * nrow]) || !R_finite(grid_z_p[r + 1 + c * nrow]) || !R_finite(grid_z_p[r + 1 + (c + 1) * nrow])) { // we don't draw any contours if at least one of the corners is NA index = 0; } else { index = 8*binarized[r + c * nrow] + 4*binarized[r + (c + 1) * nrow] + 2*binarized[r + 1 + (c + 1) * nrow] + 1*binarized[r + 1 + c * nrow]; } // two-segment saddles if (index == 5 && (central_value(r, c) < vlo)) { index = 10; } else if (index == 10 && (central_value(r, c) < vlo)) { index = 5; } cells[r + c * (nrow - 1)] = index; } } if (checkInterrupt()) { interrupted = true; return; } for (int r = 0; r < nrow-1; r++) { for (int c = 0; c < ncol-1; c++) { switch(cells[r + c * (nrow - 1)]) { case 0: break; case 1: line_start(r, c, vintersect_lo); line_add(r+1, c, hintersect_lo); line_merge(); break; case 2: line_start(r, c+1, vintersect_lo); line_add(r+1, c, hintersect_lo); line_merge(); break; case 3: line_start(r, c, vintersect_lo); line_add(r, c+1, vintersect_lo); line_merge(); break; case 4: line_start(r, c, hintersect_lo); line_add(r, c+1, vintersect_lo); line_merge(); break; case 5: // like case 2 line_start(r, c+1, vintersect_lo); line_add(r+1, c, hintersect_lo); line_merge(); // like case 7 line_start(r, c, hintersect_lo); line_add(r, c, vintersect_lo); line_merge(); break; case 6: line_start(r, c, hintersect_lo); line_add(r+1, c, hintersect_lo); line_merge(); break; case 7: line_start(r, c, hintersect_lo); line_add(r, c, vintersect_lo); line_merge(); break; case 8: line_start(r, c, hintersect_lo); line_add(r, c, vintersect_lo); line_merge(); break; case 9: line_start(r, c, hintersect_lo); line_add(r+1, c, hintersect_lo); line_merge(); break; case 10: // like case 1 line_start(r, c, vintersect_lo); line_add(r+1, c, hintersect_lo); line_merge(); // like case 4 line_start(r, c, hintersect_lo); line_add(r, c+1, vintersect_lo); line_merge(); break; case 11: line_start(r, c, hintersect_lo); line_add(r, c+1, vintersect_lo); line_merge(); break; case 12: line_start(r, c, vintersect_lo); line_add(r, c+1, vintersect_lo); line_merge(); break; case 13: line_start(r, c+1, vintersect_lo); line_add(r+1, c, hintersect_lo); line_merge(); break; case 14: line_start(r, c, vintersect_lo); line_add(r+1, c, hintersect_lo); line_merge(); break; default: break; // catch everything, just in case } } } } virtual SEXP collect() { // Early exit if calculate_contour was interrupted if (was_interrupted()) { return R_NilValue; } // make line segments vector x_out, y_out; vector id; // vectors holding resulting polygon paths int cur_id = 0; // id counter for individual line segments // iterate over all locations in the polygon grid for (auto it = polygon_grid.begin(); it != polygon_grid.end(); it++) { //cout << it->first << " " << (it->second).collected << endl; if ((it->second).collected) { continue; // skip any grid points that are already collected } // we have found a new polygon line; process it cur_id++; grid_point start = it->first; grid_point cur = start; int i = 0; if (!(polygon_grid[cur].prev == grid_point())) { // back-track until we find the beginning of the line or circle around once do { cur = polygon_grid[cur].prev; i++; if (i % 100000 == 0 && checkInterrupt()) { interrupted = true; return R_NilValue; } } while (!(cur == start || polygon_grid[cur].prev == grid_point())); } start = cur; // reset starting point i = 0; do { //cout << cur << endl; point p = calc_point_coords(cur); x_out.push_back(p.x); y_out.push_back(p.y); id.push_back(cur_id); // record that we have processed this point and proceed to next polygon_grid[cur].collected = true; cur = polygon_grid[cur].next; i++; if (i % 100000 == 0 && checkInterrupt()) { interrupted = true; return R_NilValue; } } while (!(cur == start || cur == grid_point())); // keep going until we reach the start point again // if we're back to start, need to output that point one more time if (cur == start) { point p = calc_point_coords(cur); x_out.push_back(p.x); y_out.push_back(p.y); id.push_back(cur_id); } } // output variable SEXP res = PROTECT(Rf_allocVector(VECSXP, 3)); SEXP names = PROTECT(Rf_allocVector(STRSXP, 3)); SET_STRING_ELT(names, 0, Rf_mkChar("x")); SET_STRING_ELT(names, 1, Rf_mkChar("y")); SET_STRING_ELT(names, 2, Rf_mkChar("id")); Rf_setAttrib(res, Rf_install("names"), names); int final_size = x_out.size(); SEXP x_final = SET_VECTOR_ELT(res, 0, Rf_allocVector(REALSXP, final_size)); double* x_final_p = REAL(x_final); SEXP y_final = SET_VECTOR_ELT(res, 1, Rf_allocVector(REALSXP, final_size)); double* y_final_p = REAL(y_final); SEXP id_final = SET_VECTOR_ELT(res, 2, Rf_allocVector(INTSXP, final_size)); int* id_final_p = INTEGER(id_final); for (int i = 0; i < final_size; ++i) { x_final_p[i] = x_out[i]; y_final_p[i] = y_out[i]; id_final_p[i] = id[i]; } UNPROTECT(2); return res; } }; extern "C" SEXP isobands_impl(SEXP x, SEXP y, SEXP z, SEXP value_low, SEXP value_high) { BEGIN_CPP isobander ib(x, y, z); int n_bands = Rf_length(value_low); if (n_bands != Rf_length(value_high)) { Rf_error("Vectors of low and high values must have the same number of elements."); } ib.calculate_contour(); SEXP out = PROTECT(Rf_allocVector(VECSXP, n_bands)); for (int i = 0; i < n_bands; ++i) { ib.set_value(REAL(value_low)[i], REAL(value_high)[i]); ib.calculate_contour(); SET_VECTOR_ELT(out, i, ib.collect()); if (ib.was_interrupted()) { longjump_interrupt(); } } UNPROTECT(1); return out; END_CPP } extern "C" SEXP isolines_impl(SEXP x, SEXP y, SEXP z, SEXP value) { BEGIN_CPP isoliner il(x, y, z); int n_lines = Rf_length(value); SEXP out = PROTECT(Rf_allocVector(VECSXP, n_lines)); for (int i = 0; i < n_lines; ++i) { il.set_value(REAL(value)[i]); il.calculate_contour(); SET_VECTOR_ELT(out, i, il.collect()); if (il.was_interrupted()) { longjump_interrupt(); } } UNPROTECT(1); return out; END_CPP } isoband/src/test-clip-lines.cpp0000644000176200001440000003322613760133076016214 0ustar liggesusers#include #include "polygon.h" #include "clip-lines.h" #include bool near_equal(double x1, double x2) { return (std::fabs(x1 - x2) < 1e-9); } context("Crop to unit box") { test_that("Both points inside") { point crop1, crop2; segment_crop_type result; result = crop_to_unit_box(point(.2, .3), point(.7, .6), crop1, crop2); expect_true(result == complete); result = crop_to_unit_box(point(.7, .6), point(.2, .3), crop1, crop2); expect_true(result == complete); } test_that("Both points trivially outside") { point crop1, crop2; segment_crop_type result; // to the left result = crop_to_unit_box(point(-.2, .3), point(-.5, 1.6), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(-.2, .3), point(-.5, .6), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(-.5, 1.6), point(-.2, -.3), crop1, crop2); expect_true(result == none); // to the right result = crop_to_unit_box(point(1.2, .3), point(1.5, 1.6), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(1.2, .3), point(1.5, .6), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(1.5, 1.6), point(1.2, -.3), crop1, crop2); expect_true(result == none); // above result = crop_to_unit_box(point(.3, 1.2), point(1.6, 1.5), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(.3, 1.2), point(.6, 1.5), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(1.6, 1.5), point(-.3, 1.2), crop1, crop2); expect_true(result == none); // below result = crop_to_unit_box(point(.3, -.2), point(1.6, -.5), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(.3, -.2), point(.6, -.5), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(1.6, -.5), point(-.3, -.2), crop1, crop2); expect_true(result == none); } test_that("One point inside") { point crop1, crop2; segment_crop_type result; // horizontal lines result = crop_to_unit_box(point(-.2, .5), point(.5, .5), crop1, crop2); expect_true(result == at_end); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, .5)); result = crop_to_unit_box(point(1.2, .5), point(.5, .5), crop1, crop2); expect_true(result == at_end); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, .5)); // vertical lines result = crop_to_unit_box(point(.5, .5), point(.5, -.2), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, .5)); expect_true(near_equal(crop1.y, 0)); result = crop_to_unit_box(point(.5, .5), point(.5, 1.2), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, .5)); expect_true(near_equal(crop1.y, 1)); // diagonal lines through corners result = crop_to_unit_box(point(.5, .5), point(1.2, 1.2), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, 1)); result = crop_to_unit_box(point(.5, .5), point(-.2, 1.2), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, 1)); result = crop_to_unit_box(point(-.2, -.2), point(.5, .5), crop1, crop2); expect_true(result == at_end); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, 0)); result = crop_to_unit_box(point(1.2, -.2), point(.5, .5), crop1, crop2); expect_true(result == at_end); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, 0)); // top result = crop_to_unit_box(point(.2, .8), point(.5, 1.4), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, .3)); expect_true(near_equal(crop1.y, 1)); // top right result = crop_to_unit_box(point(.8, .8), point(1.1, 1.4), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, .9)); expect_true(near_equal(crop1.y, 1)); result = crop_to_unit_box(point(.8, .8), point(1.4, 1.1), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, .9)); // right result = crop_to_unit_box(point(1.4, .5), point(.8, .2), crop1, crop2); expect_true(result == at_end); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, .3)); // bottom right result = crop_to_unit_box(point(1.4, -.1), point(.8, .2), crop1, crop2); expect_true(result == at_end); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, .1)); result = crop_to_unit_box(point(1.4, -.35), point(.8, .05), crop1, crop2); expect_true(result == at_end); expect_true(near_equal(crop1.x, .875)); expect_true(near_equal(crop1.y, 0)); // bottom result = crop_to_unit_box(point(.2, .2), point(.5, -.4), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, .3)); expect_true(near_equal(crop1.y, 0)); // bottom left result = crop_to_unit_box(point(.2, .2), point(-.4, -.1), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, .1)); result = crop_to_unit_box(point(.2, .05), point(-.4, -.35), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, .125)); expect_true(near_equal(crop1.y, 0)); // left result = crop_to_unit_box(point(-.4, .5), point(.2, .2), crop1, crop2); expect_true(result == at_end); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, .3)); // top left result = crop_to_unit_box(point(.2, .8), point(-.1, 1.4), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, .1)); expect_true(near_equal(crop1.y, 1)); result = crop_to_unit_box(point(.2, .8), point(-.4, 1.1), crop1, crop2); expect_true(result == at_beginning); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, .9)); } test_that("Double intersections") { point crop1, crop2; segment_crop_type result; // horizontal lines result = crop_to_unit_box(point(-1, .5), point(2, .5), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, .5)); expect_true(near_equal(crop2.x, 1)); expect_true(near_equal(crop2.y, .5)); result = crop_to_unit_box(point(2, .5), point(-1, .5), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, .5)); expect_true(near_equal(crop2.x, 0)); expect_true(near_equal(crop2.y, .5)); // vertical lines result = crop_to_unit_box(point(.5, -1), point(.5, 2), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, .5)); expect_true(near_equal(crop1.y, 0)); expect_true(near_equal(crop2.x, .5)); expect_true(near_equal(crop2.y, 1)); result = crop_to_unit_box(point(.5, 2), point(.5, -1), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, .5)); expect_true(near_equal(crop1.y, 1)); expect_true(near_equal(crop2.x, .5)); expect_true(near_equal(crop2.y, 0)); // diagonals through corner points result = crop_to_unit_box(point(-3, -3), point(2, 2), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, 0)); expect_true(near_equal(crop2.x, 1)); expect_true(near_equal(crop2.y, 1)); result = crop_to_unit_box(point(-1, 2), point(3, -2), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, 1)); expect_true(near_equal(crop2.x, 1)); expect_true(near_equal(crop2.y, 0)); // top left corner result = crop_to_unit_box(point(-.4, .4), point(.4, 1.2), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, .8)); expect_true(near_equal(crop2.x, .2)); expect_true(near_equal(crop2.y, 1)); result = crop_to_unit_box(point(.4, 1.2), point(-.4, .4), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, .2)); expect_true(near_equal(crop1.y, 1)); expect_true(near_equal(crop2.x, 0)); expect_true(near_equal(crop2.y, .8)); // top right corner result = crop_to_unit_box(point(1.4, .4), point(.6, 1.2), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, .8)); expect_true(near_equal(crop2.x, .8)); expect_true(near_equal(crop2.y, 1)); result = crop_to_unit_box(point(.6, 1.2), point(1.4, .4), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, .8)); expect_true(near_equal(crop1.y, 1)); expect_true(near_equal(crop2.x, 1)); expect_true(near_equal(crop2.y, .8)); // bottom left corner result = crop_to_unit_box(point(-.4, .6), point(.4, -.2), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, .2)); expect_true(near_equal(crop2.x, .2)); expect_true(near_equal(crop2.y, 0)); result = crop_to_unit_box(point(.4, -.2), point(-.4, .6), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, .2)); expect_true(near_equal(crop1.y, 0)); expect_true(near_equal(crop2.x, 0)); expect_true(near_equal(crop2.y, .2)); // bottom right corner result = crop_to_unit_box(point(.4, -.4), point(1.2, .4), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, .8)); expect_true(near_equal(crop1.y, 0)); expect_true(near_equal(crop2.x, 1)); expect_true(near_equal(crop2.y, .2)); result = crop_to_unit_box(point(1.2, .4), point(.4, -.4), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, .2)); expect_true(near_equal(crop2.x, .8)); expect_true(near_equal(crop2.y, 0)); // horizontally across result = crop_to_unit_box(point(-1, -.2), point(3, 1.4), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 0)); expect_true(near_equal(crop1.y, .2)); expect_true(near_equal(crop2.x, 1)); expect_true(near_equal(crop2.y, .6)); result = crop_to_unit_box(point(3, 1.4), point(-1, -.2), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, 1)); expect_true(near_equal(crop1.y, .6)); expect_true(near_equal(crop2.x, 0)); expect_true(near_equal(crop2.y, .2)); // vertically across result = crop_to_unit_box(point(-.2, -1), point(1.4, 3), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, .2)); expect_true(near_equal(crop1.y, 0)); expect_true(near_equal(crop2.x, .6)); expect_true(near_equal(crop2.y, 1)); result = crop_to_unit_box(point(1.4, 3), point(-.2, -1), crop1, crop2); expect_true(result == in_middle); expect_true(near_equal(crop1.x, .6)); expect_true(near_equal(crop1.y, 1)); expect_true(near_equal(crop2.x, .2)); expect_true(near_equal(crop2.y, 0)); } test_that("Points non-trivially outside") { point crop1, crop2; segment_crop_type result; result = crop_to_unit_box(point(-.2, .9), point(.1, 1.2), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(1.2, .9), point(.9, 1.2), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(-.2, .1), point(.1, -.2), crop1, crop2); expect_true(result == none); result = crop_to_unit_box(point(1.2, .1), point(.9, -.2), crop1, crop2); expect_true(result == none); } } context("Transform to unit box") { test_that("Simple transformations work") { unitbox_transformer t(point(1, 1), point(2, 2), point(0, 2)); point p = t.transform(point(1, 2)); point p2 = t.inv_transform(p); expect_true(near_equal(p.x, .5)); expect_true(near_equal(p.y, .5)); expect_true(near_equal(p2.x, 1)); expect_true(near_equal(p2.y, 2)); p = t.transform(point(1, 3)); p2 = t.inv_transform(p); expect_true(near_equal(p.x, 1)); expect_true(near_equal(p.y, 1)); expect_true(near_equal(p2.x, 1)); expect_true(near_equal(p2.y, 3)); } test_that("Transformations from/to rhomboid work") { unitbox_transformer t(point(1, 1), point(2, 1), point(2, 2)); point p = t.transform(point(2, 2)); point p2 = t.inv_transform(p); expect_true(near_equal(p.x, 0)); expect_true(near_equal(p.y, 1)); expect_true(near_equal(p2.x, 2)); expect_true(near_equal(p2.y, 2)); p = t.transform(point(3, 2)); p2 = t.inv_transform(p); expect_true(near_equal(p.x, 1)); expect_true(near_equal(p.y, 1)); expect_true(near_equal(p2.x, 3)); expect_true(near_equal(p2.y, 2)); } /* // the following tests don't work properly, because `expect_error()` // doesn't catch calls to Rf_error(), it only catches exceptions. test_that("Singular transformations are caught") { expect_error( // box without width unitbox_transformer(point(1, 1), point(1, 1), point(0, 2)) ); expect_error( // box without height unitbox_transformer(point(1, 1), point(2, 2), point(1, 1)) ); expect_error( // singular inverse transform unitbox_transformer(point(1, 1), point(2, 2), point(2, 2)) ); } */ } isoband/src/testthat/0000755000176200001440000000000014017470642014325 5ustar liggesusersisoband/src/testthat/testthat.h0000644000176200001440000001126514017470642016343 0ustar liggesusers#ifndef TESTTHAT_HPP #define TESTTHAT_HPP #define TESTTHAT_TOKEN_PASTE_IMPL(__X__, __Y__) __X__ ## __Y__ #define TESTTHAT_TOKEN_PASTE(__X__, __Y__) TESTTHAT_TOKEN_PASTE_IMPL(__X__, __Y__) #define TESTTHAT_DISABLED_FUNCTION \ static void TESTTHAT_TOKEN_PASTE(testthat_disabled_test_, __LINE__) () /** * Conditionally enable or disable 'testthat' + 'Catch'. * Force 'testthat' to be enabled by defining TESTTHAT_ENABLED. * Force 'testthat' to be disabled by defining TESTTHAT_DISABLED. * TESTTHAT_DISABLED takes precedence. * 'testthat' is disabled on Solaris by default. * * Hide symbols containing static members on gcc, to work around issues * with DLL unload due to static members in inline functions. * https://github.com/r-lib/devtools/issues/1832 */ #if defined(__GNUC__) || defined(__clang__) # define TESTTHAT_ENABLED # define TESTTHAT_ATTRIBUTE_HIDDEN __attribute__ ((visibility("hidden"))) #else # define TESTTHAT_ATTRIBUTE_HIDDEN #endif #if defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__sun) || defined(__SVR4) # define TESTTHAT_DISABLED #endif #ifndef TESTTHAT_ENABLED # define TESTTHAT_DISABLED #endif #ifndef TESTTHAT_DISABLED # define CATCH_CONFIG_PREFIX_ALL # define CATCH_CONFIG_NOSTDOUT # ifdef TESTTHAT_TEST_RUNNER # define CATCH_CONFIG_RUNNER # endif # include // CHAR_MAX # include // EOF # ifdef __GNUC__ # pragma GCC diagnostic ignored "-Wparentheses" # endif namespace Catch { // Avoid 'R CMD check' warnings related to the use of 'std::rand()' and // 'std::srand()'. Since we don't call any Catch APIs that use these // functions, it suffices to just override them in the Catch namespace. inline void srand(unsigned) {} inline int rand() { return 42; } // Catch has calls to 'exit' on failure, which upsets R CMD check. // We won't bump into them during normal test execution so just override // it in the Catch namespace before we include 'catch'. inline void exit(int) throw() {} } # include "vendor/catch.h" // Implement an output stream that avoids writing to stdout / stderr. extern "C" void Rprintf(const char*, ...); extern "C" void R_FlushConsole(); namespace testthat { class r_streambuf : public std::streambuf { public: r_streambuf() {} protected: virtual std::streamsize xsputn(const char* s, std::streamsize n) { if (n == 1) Rprintf("%c", *s); else Rprintf("%.*s", n, s); return n; } virtual int overflow(int c = EOF) { if (c == EOF) return c; if (c > CHAR_MAX) return c; Rprintf("%c", (char) c); return c; } virtual int sync() { R_FlushConsole(); return 0; } }; class r_ostream : public std::ostream { public: r_ostream() : std::ostream(new r_streambuf) {} ~r_ostream() { delete rdbuf(); } }; // Allow client packages to access the Catch::Session // exported by testthat. # ifdef CATCH_CONFIG_RUNNER TESTTHAT_ATTRIBUTE_HIDDEN inline Catch::Session& catchSession() { static Catch::Session instance; return instance; } inline bool run_tests(bool use_xml) { if (use_xml) { const char* argv[] = {"catch", "-r", "xml"}; return catchSession().run(3, argv) == 0; } else { return catchSession().run() == 0; } } # endif // CATCH_CONFIG_RUNNER } // namespace testthat namespace Catch { TESTTHAT_ATTRIBUTE_HIDDEN inline std::ostream& cout() { static testthat::r_ostream instance; return instance; } TESTTHAT_ATTRIBUTE_HIDDEN inline std::ostream& cerr() { static testthat::r_ostream instance; return instance; } } // namespace Catch # ifdef TESTTHAT_TEST_RUNNER // ERROR will be redefined by R; avoid compiler warnings # ifdef ERROR # undef ERROR # endif # include # include extern "C" SEXP run_testthat_tests(SEXP use_xml_sxp) { bool use_xml = LOGICAL(use_xml_sxp)[0]; bool success = testthat::run_tests(use_xml); return ScalarLogical(success); } # endif // TESTTHAT_TEST_RUNNER # define context(__X__) CATCH_TEST_CASE(__X__ " | " __FILE__) # define test_that CATCH_SECTION # define expect_true CATCH_CHECK # define expect_false CATCH_CHECK_FALSE # define expect_error CATCH_CHECK_THROWS # define expect_error_as CATCH_CHECK_THROWS_AS #else // TESTTHAT_DISABLED # define context(__X__) TESTTHAT_DISABLED_FUNCTION # define test_that(__X__) if (false) # define expect_true(__X__) (void) (__X__) # define expect_false(__X__) (void) (__X__) # define expect_error(__X__) (void) (__X__) # define expect_error_as(__X__, __Y__) (void) (__X__) # ifdef TESTTHAT_TEST_RUNNER # include # include extern "C" SEXP run_testthat_tests() { return ScalarLogical(true); } # endif // TESTTHAT_TEST_RUNNER #endif // TESTTHAT_DISABLED #endif /* TESTTHAT_HPP */ isoband/src/testthat/vendor/0000755000176200001440000000000014017470642015622 5ustar liggesusersisoband/src/testthat/vendor/catch.h0000644000176200001440000146576614070641312017077 0ustar liggesusers/* * Catch v1.9.6 * Generated: 2017-06-27 12:19:54.557875 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_CATCH_HPP_INCLUDED #ifdef __clang__ # pragma clang system_header #elif defined __GNUC__ # pragma GCC system_header #endif // #included from: internal/catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC # pragma clang diagnostic ignored "-Wglobal-constructors" # pragma clang diagnostic ignored "-Wvariadic-macros" # pragma clang diagnostic ignored "-Wc99-extensions" # pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wc++98-compat" # pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wparentheses" # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL #endif #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED # define CLARA_CONFIG_MAIN # endif #endif // #included from: internal/catch_notimplemented_exception.h #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED // #included from: catch_common.h #define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED // #included from: catch_compiler_capabilities.h #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED // Detect a number of compiler features - mostly C++11/14 conformance - by compiler // The following features are defined: // // CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? // CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? // CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods // CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? // CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported // CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? // CATCH_CONFIG_CPP11_OVERRIDE : is override supported? // CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) // CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? // CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form // (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. // All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 #ifdef __cplusplus # if __cplusplus >= 201103L # define CATCH_CPP11_OR_GREATER # endif # if __cplusplus >= 201402L # define CATCH_CPP14_OR_GREATER # endif #endif #ifdef __clang__ # if __has_feature(cxx_nullptr) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif # if __has_feature(cxx_noexcept) # define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # endif # if defined(CATCH_CPP11_OR_GREATER) # define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) # define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ _Pragma( "clang diagnostic pop" ) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic pop" ) # endif #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals #if defined(__CYGWIN__) || defined(__QNX__) # if !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS # endif #endif //////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin # define _BSD_SOURCE #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Borland #ifdef __BORLANDC__ #endif // __BORLANDC__ //////////////////////////////////////////////////////////////////////////////// // EDG #ifdef __EDG_VERSION__ #endif // __EDG_VERSION__ //////////////////////////////////////////////////////////////////////////////// // Digital Mars #ifdef __DMC__ #endif // __DMC__ //////////////////////////////////////////////////////////////////////////////// // GCC #ifdef __GNUC__ # if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below #endif // __GNUC__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER #define CATCH_INTERNAL_CONFIG_WINDOWS_SEH #if (_MSC_VER >= 1600) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR #endif #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE #define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS #endif #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Use variadic macros if the compiler supports them #if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ ( defined __GNUC__ && __GNUC__ >= 3 ) || \ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) #define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS #endif // Use __COUNTER__ if the compiler supports it #if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \ ( defined __clang__ && __clang_major__ >= 3 ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif //////////////////////////////////////////////////////////////////////////////// // C++ language feature support // catch all support for C++11 #if defined(CATCH_CPP11_OR_GREATER) # if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS # define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM # define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE # define CATCH_INTERNAL_CONFIG_CPP11_TUPLE # endif # ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS # define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) # define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) # define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) # define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) # define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS # endif #endif // __cplusplus >= 201103L // Now set the actual defines based on the above + anything the user has configured #if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_NULLPTR #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_NOEXCEPT #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_GENERATED_METHODS #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_IS_ENUM #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_TUPLE #endif #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) # define CATCH_CONFIG_VARIADIC_MACROS #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_LONG_LONG #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_OVERRIDE #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_UNIQUE_PTR #endif // Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for // analytics) because, at time of writing, __COUNTER__ is not properly handled by it. // This does not affect compilation #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__) # define CATCH_CONFIG_COUNTER #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_SHUFFLE #endif # if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_TYPE_TRAITS # endif #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. #if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_CONFIG_POSIX_SIGNALS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #endif // noexcept support: #if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) # define CATCH_NOEXCEPT noexcept # define CATCH_NOEXCEPT_IS(x) noexcept(x) #else # define CATCH_NOEXCEPT throw() # define CATCH_NOEXCEPT_IS(x) #endif // nullptr support #ifdef CATCH_CONFIG_CPP11_NULLPTR # define CATCH_NULL nullptr #else # define CATCH_NULL NULL #endif // override support #ifdef CATCH_CONFIG_CPP11_OVERRIDE # define CATCH_OVERRIDE override #else # define CATCH_OVERRIDE #endif // unique_ptr support #ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR # define CATCH_AUTO_PTR( T ) std::unique_ptr #else # define CATCH_AUTO_PTR( T ) std::auto_ptr #endif #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) #else # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif #define INTERNAL_CATCH_STRINGIFY2( expr ) #expr #define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) #include #include namespace Catch { struct IConfig; struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { #ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; #else NonCopyable( NonCopyable const& info ); NonCopyable& operator = ( NonCopyable const& ); #endif protected: NonCopyable() {} virtual ~NonCopyable(); }; class SafeBool { public: typedef void (SafeBool::*type)() const; static type makeSafe( bool value ) { return value ? &SafeBool::trueValue : 0; } private: void trueValue() const {} }; template inline void deleteAll( ContainerT& container ) { typename ContainerT::const_iterator it = container.begin(); typename ContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) delete *it; } template inline void deleteAllValues( AssociativeContainerT& container ) { typename AssociativeContainerT::const_iterator it = container.begin(); typename AssociativeContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) delete it->second; } bool startsWith( std::string const& s, std::string const& prefix ); bool startsWith( std::string const& s, char prefix ); bool endsWith( std::string const& s, std::string const& suffix ); bool endsWith( std::string const& s, char suffix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); std::string trim( std::string const& str ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); struct pluralise { pluralise( std::size_t count, std::string const& label ); friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); std::size_t m_count; std::string m_label; }; struct SourceLineInfo { SourceLineInfo(); SourceLineInfo( char const* _file, std::size_t _line ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS SourceLineInfo(SourceLineInfo const& other) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; # endif bool empty() const; bool operator == ( SourceLineInfo const& other ) const; bool operator < ( SourceLineInfo const& other ) const; char const* file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // This is just here to avoid compiler warnings with macro constants and boolean literals inline bool isTrue( bool value ){ return value; } inline bool alwaysTrue() { return true; } inline bool alwaysFalse() { return false; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); void seedRng( IConfig const& config ); unsigned int rngSeed(); // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { std::string operator+() { return std::string(); } }; template T const& operator + ( T const& value, StreamEndStop ) { return value; } } #define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) #define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); namespace Catch { class NotImplementedException : public std::exception { public: NotImplementedException( SourceLineInfo const& lineInfo ); NotImplementedException( NotImplementedException const& ) {} virtual ~NotImplementedException() CATCH_NOEXCEPT {} virtual const char* what() const CATCH_NOEXCEPT; private: std::string m_what; SourceLineInfo m_lineInfo; }; } // end namespace Catch /////////////////////////////////////////////////////////////////////////////// #define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) // #included from: internal/catch_context.h #define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED // #included from: catch_interfaces_generators.h #define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED #include namespace Catch { struct IGeneratorInfo { virtual ~IGeneratorInfo(); virtual bool moveNext() = 0; virtual std::size_t getCurrentIndex() const = 0; }; struct IGeneratorsForTest { virtual ~IGeneratorsForTest(); virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; virtual bool moveNext() = 0; }; IGeneratorsForTest* createGeneratorsForTest(); } // end namespace Catch // #included from: catch_ptr.hpp #define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" #endif namespace Catch { // An intrusive reference counting smart pointer. // T must implement addRef() and release() methods // typically implementing the IShared interface template class Ptr { public: Ptr() : m_p( CATCH_NULL ){} Ptr( T* p ) : m_p( p ){ if( m_p ) m_p->addRef(); } Ptr( Ptr const& other ) : m_p( other.m_p ){ if( m_p ) m_p->addRef(); } ~Ptr(){ if( m_p ) m_p->release(); } void reset() { if( m_p ) m_p->release(); m_p = CATCH_NULL; } Ptr& operator = ( T* p ){ Ptr temp( p ); swap( temp ); return *this; } Ptr& operator = ( Ptr const& other ){ Ptr temp( other ); swap( temp ); return *this; } void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } T* get() const{ return m_p; } T& operator*() const { return *m_p; } T* operator->() const { return m_p; } bool operator !() const { return m_p == CATCH_NULL; } operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } private: T* m_p; }; struct IShared : NonCopyable { virtual ~IShared(); virtual void addRef() const = 0; virtual void release() const = 0; }; template struct SharedImpl : T { SharedImpl() : m_rc( 0 ){} virtual void addRef() const { ++m_rc; } virtual void release() const { if( --m_rc == 0 ) delete this; } mutable unsigned int m_rc; }; } // end namespace Catch #ifdef __clang__ # pragma clang diagnostic pop #endif namespace Catch { class TestCase; class Stream; struct IResultCapture; struct IRunner; struct IGeneratorsForTest; struct IConfig; struct IContext { virtual ~IContext(); virtual IResultCapture* getResultCapture() = 0; virtual IRunner* getRunner() = 0; virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; virtual bool advanceGeneratorsForCurrentTest() = 0; virtual Ptr getConfig() const = 0; }; struct IMutableContext : IContext { virtual ~IMutableContext(); virtual void setResultCapture( IResultCapture* resultCapture ) = 0; virtual void setRunner( IRunner* runner ) = 0; virtual void setConfig( Ptr const& config ) = 0; }; IContext& getCurrentContext(); IMutableContext& getCurrentMutableContext(); void cleanUpContext(); Stream createStream( std::string const& streamName ); } // #included from: internal/catch_test_registry.hpp #define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED // #included from: catch_interfaces_testcase.h #define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED #include namespace Catch { class TestSpec; struct ITestCase : IShared { virtual void invoke () const = 0; protected: virtual ~ITestCase(); }; class TestCase; struct IConfig; struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); virtual std::vector const& getAllTests() const = 0; virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); } namespace Catch { template class MethodTestCase : public SharedImpl { public: MethodTestCase( void (C::*method)() ) : m_method( method ) {} virtual void invoke() const { C obj; (obj.*m_method)(); } private: virtual ~MethodTestCase() {} void (C::*m_method)(); }; typedef void(*TestFunction)(); struct NameAndDesc { NameAndDesc( const char* _name = "", const char* _description= "" ) : name( _name ), description( _description ) {} const char* name; const char* description; }; void registerTestCase ( ITestCase* testCase, char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ); struct AutoReg { AutoReg ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ); template AutoReg ( void (C::*method)(), char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { registerTestCase ( new MethodTestCase( method ), className, nameAndDesc, lineInfo ); } ~AutoReg(); private: AutoReg( AutoReg const& ); void operator= ( AutoReg const& ); }; void registerTestCaseFunction ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ); } // end namespace Catch #ifdef CATCH_CONFIG_VARIADIC_MACROS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } \ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } \ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestName : ClassName{ \ void test(); \ }; \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ } \ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); \ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #else /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ static void TestName(); \ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } \ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestCaseName : ClassName{ \ void test(); \ }; \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ } \ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestCaseName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); \ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #endif // #included from: internal/catch_capture.hpp #define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED // #included from: catch_result_builder.h #define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED // #included from: catch_result_type.h #define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED namespace Catch { // ResultWas::OfType enum struct ResultWas { enum OfType { Unknown = -1, Ok = 0, Info = 1, Warning = 2, FailureBit = 0x10, ExpressionFailed = FailureBit | 1, ExplicitFailure = FailureBit | 2, Exception = 0x100 | FailureBit, ThrewException = Exception | 1, DidntThrowException = Exception | 2, FatalErrorCondition = 0x200 | FailureBit }; }; inline bool isOk( ResultWas::OfType resultType ) { return ( resultType & ResultWas::FailureBit ) == 0; } inline bool isJustInfo( int flags ) { return flags == ResultWas::Info; } // ResultDisposition::Flags enum struct ResultDisposition { enum Flags { Normal = 0x01, ContinueOnFailure = 0x02, // Failures fail test, but execution continues FalseTest = 0x04, // Prefix expression with ! SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { return static_cast( static_cast( lhs ) | static_cast( rhs ) ); } inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } } // end namespace Catch // #included from: catch_assertionresult.h #define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED #include namespace Catch { struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; struct DecomposedExpression { virtual ~DecomposedExpression() {} virtual bool isBinaryExpression() const { return false; } virtual void reconstructExpression( std::string& dest ) const = 0; // Only simple binary comparisons can be decomposed. // If more complex check is required then wrap sub-expressions in parentheses. template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); private: DecomposedExpression& operator = (DecomposedExpression const&); }; struct AssertionInfo { AssertionInfo() {} AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, char const * _capturedExpression, ResultDisposition::Flags _resultDisposition, char const * _secondArg = ""); char const * macroName; SourceLineInfo lineInfo; char const * capturedExpression; ResultDisposition::Flags resultDisposition; char const * secondArg; }; struct AssertionResultData { AssertionResultData() : decomposedExpression( CATCH_NULL ) , resultType( ResultWas::Unknown ) , negated( false ) , parenthesized( false ) {} void negate( bool parenthesize ) { negated = !negated; parenthesized = parenthesize; if( resultType == ResultWas::Ok ) resultType = ResultWas::ExpressionFailed; else if( resultType == ResultWas::ExpressionFailed ) resultType = ResultWas::Ok; } std::string const& reconstructExpression() const { if( decomposedExpression != CATCH_NULL ) { decomposedExpression->reconstructExpression( reconstructedExpression ); if( parenthesized ) { reconstructedExpression.insert( 0, 1, '(' ); reconstructedExpression.append( 1, ')' ); } if( negated ) { reconstructedExpression.insert( 0, 1, '!' ); } decomposedExpression = CATCH_NULL; } return reconstructedExpression; } mutable DecomposedExpression const* decomposedExpression; mutable std::string reconstructedExpression; std::string message; ResultWas::OfType resultType; bool negated; bool parenthesized; }; class AssertionResult { public: AssertionResult(); AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); ~AssertionResult(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS AssertionResult( AssertionResult const& ) = default; AssertionResult( AssertionResult && ) = default; AssertionResult& operator = ( AssertionResult const& ) = default; AssertionResult& operator = ( AssertionResult && ) = default; # endif bool isOk() const; bool succeeded() const; ResultWas::OfType getResultType() const; bool hasExpression() const; bool hasMessage() const; std::string getExpression() const; std::string getExpressionInMacro() const; bool hasExpandedExpression() const; std::string getExpandedExpression() const; std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; void discardDecomposedExpression() const; void expandDecomposedExpression() const; protected: AssertionInfo m_info; AssertionResultData m_resultData; }; } // end namespace Catch // #included from: catch_matchers.hpp #define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED namespace Catch { namespace Matchers { namespace Impl { template struct MatchAllOf; template struct MatchAnyOf; template struct MatchNotOf; class MatcherUntypedBase { public: std::string toString() const { if( m_cachedToString.empty() ) m_cachedToString = describe(); return m_cachedToString; } protected: virtual ~MatcherUntypedBase(); virtual std::string describe() const = 0; mutable std::string m_cachedToString; private: MatcherUntypedBase& operator = ( MatcherUntypedBase const& ); }; template struct MatcherMethod { virtual bool match( ObjectT const& arg ) const = 0; }; template struct MatcherMethod { virtual bool match( PtrT* arg ) const = 0; }; template struct MatcherBase : MatcherUntypedBase, MatcherMethod { MatchAllOf operator && ( MatcherBase const& other ) const; MatchAnyOf operator || ( MatcherBase const& other ) const; MatchNotOf operator ! () const; }; template struct MatchAllOf : MatcherBase { virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if (!m_matchers[i]->match(arg)) return false; } return true; } virtual std::string describe() const CATCH_OVERRIDE { std::string description; description.reserve( 4 + m_matchers.size()*32 ); description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) description += " and "; description += m_matchers[i]->toString(); } description += " )"; return description; } MatchAllOf& operator && ( MatcherBase const& other ) { m_matchers.push_back( &other ); return *this; } std::vector const*> m_matchers; }; template struct MatchAnyOf : MatcherBase { virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if (m_matchers[i]->match(arg)) return true; } return false; } virtual std::string describe() const CATCH_OVERRIDE { std::string description; description.reserve( 4 + m_matchers.size()*32 ); description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) description += " or "; description += m_matchers[i]->toString(); } description += " )"; return description; } MatchAnyOf& operator || ( MatcherBase const& other ) { m_matchers.push_back( &other ); return *this; } std::vector const*> m_matchers; }; template struct MatchNotOf : MatcherBase { MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { return !m_underlyingMatcher.match( arg ); } virtual std::string describe() const CATCH_OVERRIDE { return "not " + m_underlyingMatcher.toString(); } MatcherBase const& m_underlyingMatcher; }; template MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { return MatchAllOf() && *this && other; } template MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { return MatchAnyOf() || *this || other; } template MatchNotOf MatcherBase::operator ! () const { return MatchNotOf( *this ); } } // namespace Impl // The following functions create the actual matcher objects. // This allows the types to be inferred // - deprecated: prefer ||, && and ! template inline Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) { return Impl::MatchNotOf( underlyingMatcher ); } template inline Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { return Impl::MatchAllOf() && m1 && m2; } template inline Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { return Impl::MatchAllOf() && m1 && m2 && m3; } template inline Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { return Impl::MatchAnyOf() || m1 || m2; } template inline Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { return Impl::MatchAnyOf() || m1 || m2 || m3; } } // namespace Matchers using namespace Matchers; using Matchers::Impl::MatcherBase; } // namespace Catch namespace Catch { struct TestFailureException{}; template class ExpressionLhs; struct CopyableStream { CopyableStream() {} CopyableStream( CopyableStream const& other ) { oss << other.oss.str(); } CopyableStream& operator=( CopyableStream const& other ) { oss.str(std::string()); oss << other.oss.str(); return *this; } std::ostringstream oss; }; class ResultBuilder : public DecomposedExpression { public: ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg = "" ); ~ResultBuilder(); template ExpressionLhs operator <= ( T const& operand ); ExpressionLhs operator <= ( bool value ); template ResultBuilder& operator << ( T const& value ) { m_stream().oss << value; return *this; } ResultBuilder& setResultType( ResultWas::OfType result ); ResultBuilder& setResultType( bool result ); void endExpression( DecomposedExpression const& expr ); virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; AssertionResult build() const; AssertionResult build( DecomposedExpression const& expr ) const; void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); void captureResult( ResultWas::OfType resultType ); void captureExpression(); void captureExpectedException( std::string const& expectedMessage ); void captureExpectedException( Matchers::Impl::MatcherBase const& matcher ); void handleResult( AssertionResult const& result ); void react(); bool shouldDebugBreak() const; bool allowThrows() const; template void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); void setExceptionGuard(); void unsetExceptionGuard(); private: AssertionInfo m_assertionInfo; AssertionResultData m_data; static CopyableStream &m_stream() { static CopyableStream s; return s; } bool m_shouldDebugBreak; bool m_shouldThrow; bool m_guardException; }; } // namespace Catch // Include after due to circular dependency: // #included from: catch_expression_lhs.hpp #define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED // #included from: catch_evaluate.hpp #define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4389) // '==' : signed/unsigned mismatch # pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) #endif #include namespace Catch { namespace Internal { enum Operator { IsEqualTo, IsNotEqualTo, IsLessThan, IsGreaterThan, IsLessThanOrEqualTo, IsGreaterThanOrEqualTo }; template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; template inline T& opCast(T const& t) { return const_cast(t); } // nullptr_t support based on pull request #154 from Konstantin Baumann #ifdef CATCH_CONFIG_CPP11_NULLPTR inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } #endif // CATCH_CONFIG_CPP11_NULLPTR // So the compare overloads can be operator agnostic we convey the operator as a template // enum, which is used to specialise an Evaluator for doing the comparison. template class Evaluator{}; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs) { return bool( opCast( lhs ) == opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) != opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) < opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) > opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) >= opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) <= opCast( rhs ) ); } }; template bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { return Evaluator::evaluate( lhs, rhs ); } // This level of indirection allows us to specialise for integer types // to avoid signed/ unsigned warnings // "base" overload template bool compare( T1 const& lhs, T2 const& rhs ) { return Evaluator::evaluate( lhs, rhs ); } // unsigned X to int template bool compare( unsigned int lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned long lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned char lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } // unsigned X to long template bool compare( unsigned int lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned long lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned char lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } // int to unsigned X template bool compare( int lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( int lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( int lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // long to unsigned X template bool compare( long lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // pointer to long (when comparing against NULL) template bool compare( long lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, long rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } // pointer to int (when comparing against NULL) template bool compare( int lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, int rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } #ifdef CATCH_CONFIG_CPP11_LONG_LONG // long long to unsigned X template bool compare( long long lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long long lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long long lhs, unsigned long long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long long lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // unsigned long long to X template bool compare( unsigned long long lhs, int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( unsigned long long lhs, long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( unsigned long long lhs, long long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( unsigned long long lhs, char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // pointer to long long (when comparing against NULL) template bool compare( long long lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, long long rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } #endif // CATCH_CONFIG_CPP11_LONG_LONG #ifdef CATCH_CONFIG_CPP11_NULLPTR // pointer to nullptr_t (when comparing against nullptr) template bool compare( std::nullptr_t, T* rhs ) { return Evaluator::evaluate( nullptr, rhs ); } template bool compare( T* lhs, std::nullptr_t ) { return Evaluator::evaluate( lhs, nullptr ); } #endif // CATCH_CONFIG_CPP11_NULLPTR } // end of namespace Internal } // end of namespace Catch #ifdef _MSC_VER # pragma warning(pop) #endif // #included from: catch_tostring.h #define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED #include #include #include #include #include #ifdef __OBJC__ // #included from: catch_objc_arc.hpp #define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED #import #ifdef __has_feature #define CATCH_ARC_ENABLED __has_feature(objc_arc) #else #define CATCH_ARC_ENABLED 0 #endif void arcSafeRelease( NSObject* obj ); id performOptionalSelector( id obj, SEL sel ); #if !CATCH_ARC_ENABLED inline void arcSafeRelease( NSObject* obj ) { [obj release]; } inline id performOptionalSelector( id obj, SEL sel ) { if( [obj respondsToSelector: sel] ) return [obj performSelector: sel]; return nil; } #define CATCH_UNSAFE_UNRETAINED #define CATCH_ARC_STRONG #else inline void arcSafeRelease( NSObject* ){} inline id performOptionalSelector( id obj, SEL sel ) { #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Warc-performSelector-leaks" #endif if( [obj respondsToSelector: sel] ) return [obj performSelector: sel]; #ifdef __clang__ # pragma clang diagnostic pop #endif return nil; } #define CATCH_UNSAFE_UNRETAINED __unsafe_unretained #define CATCH_ARC_STRONG __strong #endif #endif #ifdef CATCH_CONFIG_CPP11_TUPLE #include #endif #ifdef CATCH_CONFIG_CPP11_IS_ENUM #include #endif namespace Catch { // Why we're here. template std::string toString( T const& value ); // Built in overloads std::string toString( std::string const& value ); std::string toString( std::wstring const& value ); std::string toString( const char* const value ); std::string toString( char* const value ); std::string toString( const wchar_t* const value ); std::string toString( wchar_t* const value ); std::string toString( int value ); std::string toString( unsigned long value ); std::string toString( unsigned int value ); std::string toString( const double value ); std::string toString( const float value ); std::string toString( bool value ); std::string toString( char value ); std::string toString( signed char value ); std::string toString( unsigned char value ); #ifdef CATCH_CONFIG_CPP11_LONG_LONG std::string toString( long long value ); std::string toString( unsigned long long value ); #endif #ifdef CATCH_CONFIG_CPP11_NULLPTR std::string toString( std::nullptr_t ); #endif #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ); std::string toString( NSString * CATCH_ARC_STRONG & nsstring ); std::string toString( NSObject* const& nsObject ); #endif namespace Detail { extern const std::string unprintableString; #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) struct BorgType { template BorgType( T const& ); }; struct TrueType { char sizer[1]; }; struct FalseType { char sizer[2]; }; TrueType& testStreamable( std::ostream& ); FalseType testStreamable( FalseType ); FalseType operator<<( std::ostream const&, BorgType const& ); template struct IsStreamInsertable { static std::ostream &s; static T const&t; enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; }; #else template class IsStreamInsertable { template static auto test(int) -> decltype( std::declval() << std::declval(), std::true_type() ); template static auto test(...) -> std::false_type; public: static const bool value = decltype(test(0))::value; }; #endif #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template::value > struct EnumStringMaker { static std::string convert( T const& ) { return unprintableString; } }; template struct EnumStringMaker { static std::string convert( T const& v ) { return ::Catch::toString( static_cast::type>(v) ); } }; #endif template struct StringMakerBase { #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template static std::string convert( T const& v ) { return EnumStringMaker::convert( v ); } #else template static std::string convert( T const& ) { return unprintableString; } #endif }; template<> struct StringMakerBase { template static std::string convert( T const& _value ) { std::ostringstream oss; oss << _value; return oss.str(); } }; std::string rawMemoryToString( const void *object, std::size_t size ); template inline std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } } // end namespace Detail template struct StringMaker : Detail::StringMakerBase::value> {}; template struct StringMaker { template static std::string convert( U* p ) { if( !p ) return "NULL"; else return Detail::rawMemoryToString( p ); } }; template struct StringMaker { static std::string convert( R C::* p ) { if( !p ) return "NULL"; else return Detail::rawMemoryToString( p ); } }; namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ); } //template //struct StringMaker > { // static std::string convert( std::vector const& v ) { // return Detail::rangeToString( v.begin(), v.end() ); // } //}; template std::string toString( std::vector const& v ) { return Detail::rangeToString( v.begin(), v.end() ); } #ifdef CATCH_CONFIG_CPP11_TUPLE // toString for tuples namespace TupleDetail { template< typename Tuple, std::size_t N = 0, bool = (N < std::tuple_size::value) > struct ElementPrinter { static void print( const Tuple& tuple, std::ostream& os ) { os << ( N ? ", " : " " ) << Catch::toString(std::get(tuple)); ElementPrinter::print(tuple,os); } }; template< typename Tuple, std::size_t N > struct ElementPrinter { static void print( const Tuple&, std::ostream& ) {} }; } template struct StringMaker> { static std::string convert( const std::tuple& tuple ) { std::ostringstream os; os << '{'; TupleDetail::ElementPrinter>::print( tuple, os ); os << " }"; return os.str(); } }; #endif // CATCH_CONFIG_CPP11_TUPLE namespace Detail { template std::string makeString( T const& value ) { return StringMaker::convert( value ); } } // end namespace Detail /// \brief converts any type to a string /// /// The default template forwards on to ostringstream - except when an /// ostringstream overload does not exist - in which case it attempts to detect /// that and writes {?}. /// Overload (not specialise) this template for custom typs that you don't want /// to provide an ostream overload for. template std::string toString( T const& value ) { return StringMaker::convert( value ); } namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ) { std::ostringstream oss; oss << "{ "; if( first != last ) { oss << Catch::toString( *first ); for( ++first ; first != last ; ++first ) oss << ", " << Catch::toString( *first ); } oss << " }"; return oss.str(); } } } // end namespace Catch namespace Catch { template class BinaryExpression; template class MatchExpression; // Wraps the LHS of an expression and overloads comparison operators // for also capturing those and RHS (if any) template class ExpressionLhs : public DecomposedExpression { public: ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} ExpressionLhs& operator = ( const ExpressionLhs& ); template BinaryExpression operator == ( RhsT const& rhs ) { return captureExpression( rhs ); } template BinaryExpression operator != ( RhsT const& rhs ) { return captureExpression( rhs ); } template BinaryExpression operator < ( RhsT const& rhs ) { return captureExpression( rhs ); } template BinaryExpression operator > ( RhsT const& rhs ) { return captureExpression( rhs ); } template BinaryExpression operator <= ( RhsT const& rhs ) { return captureExpression( rhs ); } template BinaryExpression operator >= ( RhsT const& rhs ) { return captureExpression( rhs ); } BinaryExpression operator == ( bool rhs ) { return captureExpression( rhs ); } BinaryExpression operator != ( bool rhs ) { return captureExpression( rhs ); } void endExpression() { m_truthy = m_lhs ? true : false; m_rb .setResultType( m_truthy ) .endExpression( *this ); } virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { dest = Catch::toString( m_lhs ); } private: template BinaryExpression captureExpression( RhsT& rhs ) const { return BinaryExpression( m_rb, m_lhs, rhs ); } template BinaryExpression captureExpression( bool rhs ) const { return BinaryExpression( m_rb, m_lhs, rhs ); } private: ResultBuilder& m_rb; T m_lhs; bool m_truthy; }; template class BinaryExpression : public DecomposedExpression { public: BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} BinaryExpression& operator = ( BinaryExpression& ); void endExpression() const { m_rb .setResultType( Internal::compare( m_lhs, m_rhs ) ) .endExpression( *this ); } virtual bool isBinaryExpression() const CATCH_OVERRIDE { return true; } virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { std::string lhs = Catch::toString( m_lhs ); std::string rhs = Catch::toString( m_rhs ); char delim = lhs.size() + rhs.size() < 40 && lhs.find('\n') == std::string::npos && rhs.find('\n') == std::string::npos ? ' ' : '\n'; dest.reserve( 7 + lhs.size() + rhs.size() ); // 2 for spaces around operator // 2 for operator // 2 for parentheses (conditionally added later) // 1 for negation (conditionally added later) dest = lhs; dest += delim; dest += Internal::OperatorTraits::getName(); dest += delim; dest += rhs; } private: ResultBuilder& m_rb; LhsT m_lhs; RhsT m_rhs; }; template class MatchExpression : public DecomposedExpression { public: MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} virtual bool isBinaryExpression() const CATCH_OVERRIDE { return true; } virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { std::string matcherAsString = m_matcher.toString(); dest = Catch::toString( m_arg ); dest += ' '; if( matcherAsString == Detail::unprintableString ) dest += m_matcherString; else dest += matcherAsString; } private: ArgT m_arg; MatcherT m_matcher; char const* m_matcherString; }; } // end namespace Catch namespace Catch { template inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { return ExpressionLhs( *this, operand ); } inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { return ExpressionLhs( *this, value ); } template inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ) { MatchExpression expr( arg, matcher, matcherString ); setResultType( matcher.match( arg ) ); endExpression( expr ); } } // namespace Catch // #included from: catch_message.h #define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED #include namespace Catch { struct MessageInfo { MessageInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ); std::string macroName; SourceLineInfo lineInfo; ResultWas::OfType type; std::string message; unsigned int sequence; bool operator == ( MessageInfo const& other ) const { return sequence == other.sequence; } bool operator < ( MessageInfo const& other ) const { return sequence < other.sequence; } private: static unsigned int globalCount; }; struct MessageBuilder { MessageBuilder( std::string const& macroName, SourceLineInfo const& lineInfo, ResultWas::OfType type ) : m_info( macroName, lineInfo, type ) {} template MessageBuilder& operator << ( T const& value ) { m_stream << value; return *this; } MessageInfo m_info; std::ostringstream m_stream; }; class ScopedMessage { public: ScopedMessage( MessageBuilder const& builder ); ScopedMessage( ScopedMessage const& other ); ~ScopedMessage(); MessageInfo m_info; }; } // end namespace Catch // #included from: catch_interfaces_capture.h #define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED #include namespace Catch { class TestCase; class AssertionResult; struct AssertionInfo; struct SectionInfo; struct SectionEndInfo; struct MessageInfo; class ScopedMessageBuilder; struct Counts; struct IResultCapture { virtual ~IResultCapture(); virtual void assertionEnded( AssertionResult const& result ) = 0; virtual bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) = 0; virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0; virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; virtual void exceptionEarlyReported() = 0; virtual void handleFatalErrorCondition( std::string const& message ) = 0; }; IResultCapture& getResultCapture(); } // #included from: catch_debugger.h #define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED // #included from: catch_platform.h #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) # define CATCH_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) # define CATCH_PLATFORM_IPHONE #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) # define CATCH_PLATFORM_WINDOWS # if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) # define CATCH_DEFINES_NOMINMAX # endif # if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) # define CATCH_DEFINES_WIN32_LEAN_AND_MEAN # endif #endif #include namespace Catch{ bool isDebuggerActive(); void writeToDebugConsole( std::string const& text ); } #ifdef CATCH_PLATFORM_MAC // The following code snippet based on: // http://cocoawithlove.com/2008/03/break-into-debugger.html #if defined(__ppc64__) || defined(__ppc__) #define CATCH_TRAP() \ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ : : : "memory","r0","r3","r4" ) // backported from Catch2 // revision b9853b4b356b83bb580c746c3a1f11101f9af54f // src/catch2/internal/catch_debugger.hpp #elif defined(__i386__) || defined(__x86_64__) #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ #elif defined(__aarch64__) #define CATCH_TRAP() __asm__(".inst 0xd4200000") #endif #elif defined(CATCH_PLATFORM_LINUX) // If we can use inline assembler, do it because this allows us to break // directly at the location of the failing check instead of breaking inside // raise() called from it, i.e. one stack frame below. #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) #define CATCH_TRAP() asm volatile ("int $3") #else // Fall back to the generic way. #include #define CATCH_TRAP() raise(SIGTRAP) #endif #elif defined(_MSC_VER) #define CATCH_TRAP() __debugbreak() #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); #define CATCH_TRAP() DebugBreak() #endif #ifdef CATCH_TRAP #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } #else #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif // #included from: catch_interfaces_runner.h #define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED namespace Catch { class TestCase; struct IRunner { virtual ~IRunner(); virtual bool aborting() const = 0; }; } #if defined(CATCH_CONFIG_FAST_COMPILE) /////////////////////////////////////////////////////////////////////////////// // We can speedup compilation significantly by breaking into debugger lower in // the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER // macro in each assertion #define INTERNAL_CATCH_REACT( resultBuilder ) \ resultBuilder.react(); /////////////////////////////////////////////////////////////////////////////// // Another way to speed-up compilation is to omit local try-catch for REQUIRE* // macros. // This can potentially cause false negative, if the test code catches // the exception before it propagates back up to the runner. #define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ __catchResult.setExceptionGuard(); \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ( __catchResult <= expr ).endExpression(); \ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ __catchResult.unsetExceptionGuard(); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. #define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ __catchResult.setExceptionGuard(); \ __catchResult.captureMatch( arg, matcher, #matcher ); \ __catchResult.unsetExceptionGuard(); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #else /////////////////////////////////////////////////////////////////////////////// // In the event of a failure works out if the debugger needs to be invoked // and/or an exception thrown and takes appropriate action. // This needs to be done as a macro so the debugger will stop in the user // source code rather than in Catch library code #define INTERNAL_CATCH_REACT( resultBuilder ) \ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ resultBuilder.react(); #endif /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ( __catchResult <= expr ).endExpression(); \ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ } \ catch( ... ) { \ __catchResult.useActiveException( resultDisposition ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ if( Catch::getResultCapture().getLastResult()->succeeded() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ if( !Catch::getResultCapture().getLastResult()->succeeded() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ __catchResult.useActiveException( resultDisposition ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ if( __catchResult.allowThrows() ) \ try { \ static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ __catchResult.captureExpectedException( matcher ); \ } \ else \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \ if( __catchResult.allowThrows() ) \ try { \ static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( const exceptionType& ) { \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ __catchResult.useActiveException( resultDisposition ); \ } \ else \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #ifdef CATCH_CONFIG_VARIADIC_MACROS #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ __catchResult.captureResult( messageType ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #else #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << log + ::Catch::StreamEndStop(); \ __catchResult.captureResult( messageType ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #endif /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( macroName, log ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ try { \ __catchResult.captureMatch( arg, matcher, #matcher ); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) // #included from: internal/catch_section.h #define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED // #included from: catch_section_info.h #define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED // #included from: catch_totals.hpp #define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED #include namespace Catch { struct Counts { Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} Counts operator - ( Counts const& other ) const { Counts diff; diff.passed = passed - other.passed; diff.failed = failed - other.failed; diff.failedButOk = failedButOk - other.failedButOk; return diff; } Counts& operator += ( Counts const& other ) { passed += other.passed; failed += other.failed; failedButOk += other.failedButOk; return *this; } std::size_t total() const { return passed + failed + failedButOk; } bool allPassed() const { return failed == 0 && failedButOk == 0; } bool allOk() const { return failed == 0; } std::size_t passed; std::size_t failed; std::size_t failedButOk; }; struct Totals { Totals operator - ( Totals const& other ) const { Totals diff; diff.assertions = assertions - other.assertions; diff.testCases = testCases - other.testCases; return diff; } Totals delta( Totals const& prevTotals ) const { Totals diff = *this - prevTotals; if( diff.assertions.failed > 0 ) ++diff.testCases.failed; else if( diff.assertions.failedButOk > 0 ) ++diff.testCases.failedButOk; else ++diff.testCases.passed; return diff; } Totals& operator += ( Totals const& other ) { assertions += other.assertions; testCases += other.testCases; return *this; } Counts assertions; Counts testCases; }; } #include namespace Catch { struct SectionInfo { SectionInfo ( SourceLineInfo const& _lineInfo, std::string const& _name, std::string const& _description = std::string() ); std::string name; std::string description; SourceLineInfo lineInfo; }; struct SectionEndInfo { SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) {} SectionInfo sectionInfo; Counts prevAssertions; double durationInSeconds; }; } // end namespace Catch // #included from: catch_timer.h #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED #ifdef _MSC_VER namespace Catch { typedef unsigned long long UInt64; } #else #include namespace Catch { typedef uint64_t UInt64; } #endif namespace Catch { class Timer { public: Timer() : m_ticks( 0 ) {} void start(); unsigned int getElapsedMicroseconds() const; unsigned int getElapsedMilliseconds() const; double getElapsedSeconds() const; private: UInt64 m_ticks; }; } // namespace Catch #include namespace Catch { class Section : NonCopyable { public: Section( SectionInfo const& info ); ~Section(); // This indicates whether the section should be executed or not operator bool() const; private: SectionInfo m_info; std::string m_name; Counts m_assertions; bool m_sectionIncluded; Timer m_timer; }; } // end namespace Catch #ifdef CATCH_CONFIG_VARIADIC_MACROS #define INTERNAL_CATCH_SECTION( ... ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) #else #define INTERNAL_CATCH_SECTION( name, desc ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) #endif // #included from: internal/catch_generators.hpp #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED #include #include #include namespace Catch { template struct IGenerator { virtual ~IGenerator() {} virtual T getValue( std::size_t index ) const = 0; virtual std::size_t size () const = 0; }; template class BetweenGenerator : public IGenerator { public: BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} virtual T getValue( std::size_t index ) const { return m_from+static_cast( index ); } virtual std::size_t size() const { return static_cast( 1+m_to-m_from ); } private: T m_from; T m_to; }; template class ValuesGenerator : public IGenerator { public: ValuesGenerator(){} void add( T value ) { m_values.push_back( value ); } virtual T getValue( std::size_t index ) const { return m_values[index]; } virtual std::size_t size() const { return m_values.size(); } private: std::vector m_values; }; template class CompositeGenerator { public: CompositeGenerator() : m_totalSize( 0 ) {} // *** Move semantics, similar to auto_ptr *** CompositeGenerator( CompositeGenerator& other ) : m_fileInfo( other.m_fileInfo ), m_totalSize( 0 ) { move( other ); } CompositeGenerator& setFileInfo( const char* fileInfo ) { m_fileInfo = fileInfo; return *this; } ~CompositeGenerator() { deleteAll( m_composed ); } operator T () const { size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); typename std::vector*>::const_iterator it = m_composed.begin(); typename std::vector*>::const_iterator itEnd = m_composed.end(); for( size_t index = 0; it != itEnd; ++it ) { const IGenerator* generator = *it; if( overallIndex >= index && overallIndex < index + generator->size() ) { return generator->getValue( overallIndex-index ); } index += generator->size(); } CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so } void add( const IGenerator* generator ) { m_totalSize += generator->size(); m_composed.push_back( generator ); } CompositeGenerator& then( CompositeGenerator& other ) { move( other ); return *this; } CompositeGenerator& then( T value ) { ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( value ); add( valuesGen ); return *this; } private: void move( CompositeGenerator& other ) { m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() ); m_totalSize += other.m_totalSize; other.m_composed.clear(); } std::vector*> m_composed; std::string m_fileInfo; size_t m_totalSize; }; namespace Generators { template CompositeGenerator between( T from, T to ) { CompositeGenerator generators; generators.add( new BetweenGenerator( from, to ) ); return generators; } template CompositeGenerator values( T val1, T val2 ) { CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); generators.add( valuesGen ); return generators; } template CompositeGenerator values( T val1, T val2, T val3 ){ CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); valuesGen->add( val3 ); generators.add( valuesGen ); return generators; } template CompositeGenerator values( T val1, T val2, T val3, T val4 ) { CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); valuesGen->add( val3 ); valuesGen->add( val4 ); generators.add( valuesGen ); return generators; } } // end namespace Generators using namespace Generators; } // end namespace Catch #define INTERNAL_CATCH_LINESTR2( line ) #line #define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) #define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) // #included from: internal/catch_interfaces_exception.h #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED #include #include // #included from: catch_interfaces_registry_hub.h #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED #include namespace Catch { class TestCase; struct ITestCaseRegistry; struct IExceptionTranslatorRegistry; struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; struct ITagAliasRegistry; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; }; struct IMutableRegistryHub { virtual ~IMutableRegistryHub(); virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; virtual void registerListener( Ptr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; }; IRegistryHub& getRegistryHub(); IMutableRegistryHub& getMutableRegistryHub(); void cleanUp(); std::string translateActiveException(); } namespace Catch { typedef std::string(*exceptionTranslateFunction)(); struct IExceptionTranslator; typedef std::vector ExceptionTranslators; struct IExceptionTranslator { virtual ~IExceptionTranslator(); virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; }; struct IExceptionTranslatorRegistry { virtual ~IExceptionTranslatorRegistry(); virtual std::string translateActiveException() const = 0; }; class ExceptionTranslatorRegistrar { template class ExceptionTranslator : public IExceptionTranslator { public: ExceptionTranslator( std::string(*translateFunction)( T& ) ) : m_translateFunction( translateFunction ) {} virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { try { if( it == itEnd ) throw; else return (*it)->translate( it+1, itEnd ); } catch( T& ex ) { return m_translateFunction( ex ); } } protected: std::string(*m_translateFunction)( T& ); }; public: template ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { getMutableRegistryHub().registerTranslator ( new ExceptionTranslator( translateFunction ) ); } }; } /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ static std::string translatorName( signature ); \ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ static std::string translatorName( signature ) #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) // #included from: internal/catch_approx.hpp #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED #include #include #if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) #include #endif namespace Catch { namespace Detail { class Approx { public: explicit Approx ( double value ) : m_epsilon( std::numeric_limits::epsilon()*100 ), m_margin( 0.0 ), m_scale( 1.0 ), m_value( value ) {} Approx( Approx const& other ) : m_epsilon( other.m_epsilon ), m_margin( other.m_margin ), m_scale( other.m_scale ), m_value( other.m_value ) {} static Approx custom() { return Approx( 0 ); } #if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) template ::value>::type> Approx operator()( T value ) { Approx approx( static_cast(value) ); approx.epsilon( m_epsilon ); approx.margin( m_margin ); approx.scale( m_scale ); return approx; } template ::value>::type> explicit Approx( T value ): Approx(static_cast(value)) {} template ::value>::type> friend bool operator == ( const T& lhs, Approx const& rhs ) { // Thanks to Richard Harris for his help refining this formula auto lhs_v = double(lhs); bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value))); if (relativeOK) { return true; } return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin; } template ::value>::type> friend bool operator == ( Approx const& lhs, const T& rhs ) { return operator==( rhs, lhs ); } template ::value>::type> friend bool operator != ( T lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } template ::value>::type> friend bool operator != ( Approx const& lhs, T rhs ) { return !operator==( rhs, lhs ); } template ::value>::type> friend bool operator <= ( T lhs, Approx const& rhs ) { return double(lhs) < rhs.m_value || lhs == rhs; } template ::value>::type> friend bool operator <= ( Approx const& lhs, T rhs ) { return lhs.m_value < double(rhs) || lhs == rhs; } template ::value>::type> friend bool operator >= ( T lhs, Approx const& rhs ) { return double(lhs) > rhs.m_value || lhs == rhs; } template ::value>::type> friend bool operator >= ( Approx const& lhs, T rhs ) { return lhs.m_value > double(rhs) || lhs == rhs; } template ::value>::type> Approx& epsilon( T newEpsilon ) { m_epsilon = double(newEpsilon); return *this; } template ::value>::type> Approx& margin( T newMargin ) { m_margin = double(newMargin); return *this; } template ::value>::type> Approx& scale( T newScale ) { m_scale = double(newScale); return *this; } #else Approx operator()( double value ) { Approx approx( value ); approx.epsilon( m_epsilon ); approx.margin( m_margin ); approx.scale( m_scale ); return approx; } friend bool operator == ( double lhs, Approx const& rhs ) { // Thanks to Richard Harris for his help refining this formula bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); if (relativeOK) { return true; } return std::fabs(lhs - rhs.m_value) < rhs.m_margin; } friend bool operator == ( Approx const& lhs, double rhs ) { return operator==( rhs, lhs ); } friend bool operator != ( double lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } friend bool operator != ( Approx const& lhs, double rhs ) { return !operator==( rhs, lhs ); } friend bool operator <= ( double lhs, Approx const& rhs ) { return lhs < rhs.m_value || lhs == rhs; } friend bool operator <= ( Approx const& lhs, double rhs ) { return lhs.m_value < rhs || lhs == rhs; } friend bool operator >= ( double lhs, Approx const& rhs ) { return lhs > rhs.m_value || lhs == rhs; } friend bool operator >= ( Approx const& lhs, double rhs ) { return lhs.m_value > rhs || lhs == rhs; } Approx& epsilon( double newEpsilon ) { m_epsilon = newEpsilon; return *this; } Approx& margin( double newMargin ) { m_margin = newMargin; return *this; } Approx& scale( double newScale ) { m_scale = newScale; return *this; } #endif std::string toString() const { std::ostringstream oss; oss << "Approx( " << Catch::toString( m_value ) << " )"; return oss.str(); } private: double m_epsilon; double m_margin; double m_scale; double m_value; }; } template<> inline std::string toString( Detail::Approx const& value ) { return value.toString(); } } // end namespace Catch // #included from: internal/catch_matchers_string.h #define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED namespace Catch { namespace Matchers { namespace StdString { struct CasedString { CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); std::string adjustString( std::string const& str ) const; std::string caseSensitivitySuffix() const; CaseSensitive::Choice m_caseSensitivity; std::string m_str; }; struct StringMatcherBase : MatcherBase { StringMatcherBase( std::string const& operation, CasedString const& comparator ); virtual std::string describe() const CATCH_OVERRIDE; CasedString m_comparator; std::string m_operation; }; struct EqualsMatcher : StringMatcherBase { EqualsMatcher( CasedString const& comparator ); virtual bool match( std::string const& source ) const CATCH_OVERRIDE; }; struct ContainsMatcher : StringMatcherBase { ContainsMatcher( CasedString const& comparator ); virtual bool match( std::string const& source ) const CATCH_OVERRIDE; }; struct StartsWithMatcher : StringMatcherBase { StartsWithMatcher( CasedString const& comparator ); virtual bool match( std::string const& source ) const CATCH_OVERRIDE; }; struct EndsWithMatcher : StringMatcherBase { EndsWithMatcher( CasedString const& comparator ); virtual bool match( std::string const& source ) const CATCH_OVERRIDE; }; } // namespace StdString // The following functions create the actual matcher objects. // This allows the types to be inferred StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); } // namespace Matchers } // namespace Catch // #included from: internal/catch_matchers_vector.h #define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED namespace Catch { namespace Matchers { namespace Vector { template struct ContainsElementMatcher : MatcherBase, T> { ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} bool match(std::vector const &v) const CATCH_OVERRIDE { return std::find(v.begin(), v.end(), m_comparator) != v.end(); } virtual std::string describe() const CATCH_OVERRIDE { return "Contains: " + Catch::toString( m_comparator ); } T const& m_comparator; }; template struct ContainsMatcher : MatcherBase, std::vector > { ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} bool match(std::vector const &v) const CATCH_OVERRIDE { // !TBD: see note in EqualsMatcher if (m_comparator.size() > v.size()) return false; for (size_t i = 0; i < m_comparator.size(); ++i) if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end()) return false; return true; } virtual std::string describe() const CATCH_OVERRIDE { return "Contains: " + Catch::toString( m_comparator ); } std::vector const& m_comparator; }; template struct EqualsMatcher : MatcherBase, std::vector > { EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} bool match(std::vector const &v) const CATCH_OVERRIDE { // !TBD: This currently works if all elements can be compared using != // - a more general approach would be via a compare template that defaults // to using !=. but could be specialised for, e.g. std::vector etc // - then just call that directly if (m_comparator.size() != v.size()) return false; for (size_t i = 0; i < v.size(); ++i) if (m_comparator[i] != v[i]) return false; return true; } virtual std::string describe() const CATCH_OVERRIDE { return "Equals: " + Catch::toString( m_comparator ); } std::vector const& m_comparator; }; } // namespace Vector // The following functions create the actual matcher objects. // This allows the types to be inferred template Vector::ContainsMatcher Contains( std::vector const& comparator ) { return Vector::ContainsMatcher( comparator ); } template Vector::ContainsElementMatcher VectorContains( T const& comparator ) { return Vector::ContainsElementMatcher( comparator ); } template Vector::EqualsMatcher Equals( std::vector const& comparator ) { return Vector::EqualsMatcher( comparator ); } } // namespace Matchers } // namespace Catch // #included from: internal/catch_interfaces_tag_alias_registry.h #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED // #included from: catch_tag_alias.h #define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED #include namespace Catch { struct TagAlias { TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} std::string tag; SourceLineInfo lineInfo; }; struct RegistrarForTagAliases { RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } // #included from: catch_option.hpp #define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED namespace Catch { // An optional type template class Option { public: Option() : nullableValue( CATCH_NULL ) {} Option( T const& _value ) : nullableValue( new( storage ) T( _value ) ) {} Option( Option const& _other ) : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) {} ~Option() { reset(); } Option& operator= ( Option const& _other ) { if( &_other != this ) { reset(); if( _other ) nullableValue = new( storage ) T( *_other ); } return *this; } Option& operator = ( T const& _value ) { reset(); nullableValue = new( storage ) T( _value ); return *this; } void reset() { if( nullableValue ) nullableValue->~T(); nullableValue = CATCH_NULL; } T& operator*() { return *nullableValue; } T const& operator*() const { return *nullableValue; } T* operator->() { return nullableValue; } const T* operator->() const { return nullableValue; } T valueOr( T const& defaultValue ) const { return nullableValue ? *nullableValue : defaultValue; } bool some() const { return nullableValue != CATCH_NULL; } bool none() const { return nullableValue == CATCH_NULL; } bool operator !() const { return nullableValue == CATCH_NULL; } operator SafeBool::type() const { return SafeBool::makeSafe( some() ); } private: T *nullableValue; union { char storage[sizeof(T)]; // These are here to force alignment for the storage long double dummy1; void (*dummy2)(); long double dummy3; #ifdef CATCH_CONFIG_CPP11_LONG_LONG long long dummy4; #endif }; }; } // end namespace Catch namespace Catch { struct ITagAliasRegistry { virtual ~ITagAliasRegistry(); virtual Option find( std::string const& alias ) const = 0; virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; static ITagAliasRegistry const& get(); }; } // end namespace Catch // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections // #included from: internal/catch_test_case_info.h #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED #include #include #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" #endif namespace Catch { struct ITestCase; struct TestCaseInfo { enum SpecialProperties{ None = 0, IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, Throws = 1 << 4, NonPortable = 1 << 5 }; TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ); TestCaseInfo( TestCaseInfo const& other ); friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); bool isHidden() const; bool throws() const; bool okToFail() const; bool expectedToFail() const; std::string name; std::string className; std::string description; std::set tags; std::set lcaseTags; std::string tagsAsString; SourceLineInfo lineInfo; SpecialProperties properties; }; class TestCase : public TestCaseInfo { public: TestCase( ITestCase* testCase, TestCaseInfo const& info ); TestCase( TestCase const& other ); TestCase withName( std::string const& _newName ) const; void invoke() const; TestCaseInfo const& getTestCaseInfo() const; void swap( TestCase& other ); bool operator == ( TestCase const& other ) const; bool operator < ( TestCase const& other ) const; TestCase& operator = ( TestCase const& other ); private: Ptr test; }; TestCase makeTestCase( ITestCase* testCase, std::string const& className, std::string const& name, std::string const& description, SourceLineInfo const& lineInfo ); } #ifdef __clang__ # pragma clang diagnostic pop #endif #ifdef __OBJC__ // #included from: internal/catch_objc.hpp #define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED #import #include // NB. Any general catch headers included here must be included // in catch.hpp first to make sure they are included by the single // header for non obj-usage /////////////////////////////////////////////////////////////////////////////// // This protocol is really only here for (self) documenting purposes, since // all its methods are optional. @protocol OcFixture @optional -(void) setUp; -(void) tearDown; @end namespace Catch { class OcMethod : public SharedImpl { public: OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} virtual void invoke() const { id obj = [[m_cls alloc] init]; performOptionalSelector( obj, @selector(setUp) ); performOptionalSelector( obj, m_sel ); performOptionalSelector( obj, @selector(tearDown) ); arcSafeRelease( obj ); } private: virtual ~OcMethod() {} Class m_cls; SEL m_sel; }; namespace Detail{ inline std::string getAnnotation( Class cls, std::string const& annotationName, std::string const& testCaseName ) { NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; SEL sel = NSSelectorFromString( selStr ); arcSafeRelease( selStr ); id value = performOptionalSelector( cls, sel ); if( value ) return [(NSString*)value UTF8String]; return ""; } } inline size_t registerTestMethods() { size_t noTestMethods = 0; int noClasses = objc_getClassList( CATCH_NULL, 0 ); Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); objc_getClassList( classes, noClasses ); for( int c = 0; c < noClasses; c++ ) { Class cls = classes[c]; { u_int count; Method* methods = class_copyMethodList( cls, &count ); for( u_int m = 0; m < count ; m++ ) { SEL selector = method_getName(methods[m]); std::string methodName = sel_getName(selector); if( startsWith( methodName, "Catch_TestCase_" ) ) { std::string testCaseName = methodName.substr( 15 ); std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); const char* className = class_getName( cls ); getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); noTestMethods++; } } free(methods); } } return noTestMethods; } namespace Matchers { namespace Impl { namespace NSStringMatchers { struct StringHolder : MatcherBase{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } virtual bool match( NSString* arg ) const CATCH_OVERRIDE { return false; } NSString* m_substr; }; struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} virtual bool match( NSString* str ) const CATCH_OVERRIDE { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } virtual std::string describe() const CATCH_OVERRIDE { return "equals string: " + Catch::toString( m_substr ); } }; struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } virtual std::string describe() const CATCH_OVERRIDE { return "contains string: " + Catch::toString( m_substr ); } }; struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } virtual std::string describe() const CATCH_OVERRIDE { return "starts with: " + Catch::toString( m_substr ); } }; struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } virtual std::string describe() const CATCH_OVERRIDE { return "ends with: " + Catch::toString( m_substr ); } }; } // namespace NSStringMatchers } // namespace Impl inline Impl::NSStringMatchers::Equals Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } inline Impl::NSStringMatchers::Contains Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } inline Impl::NSStringMatchers::StartsWith StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } inline Impl::NSStringMatchers::EndsWith EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } } // namespace Matchers using namespace Matchers; } // namespace Catch /////////////////////////////////////////////////////////////////////////////// #define OC_TEST_CASE( name, desc )\ +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ {\ return @ name; \ }\ +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ { \ return @ desc; \ } \ -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) #endif #ifdef CATCH_IMPL // !TBD: Move the leak detector code into a separate header #ifdef CATCH_CONFIG_WINDOWS_CRTDBG #include class LeakDetector { public: LeakDetector() { int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); flag |= _CRTDBG_LEAK_CHECK_DF; flag |= _CRTDBG_ALLOC_MEM_DF; _CrtSetDbgFlag(flag); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); // Change this to leaking allocation's number to break there _CrtSetBreakAlloc(-1); } }; #else class LeakDetector {}; #endif LeakDetector leakDetector; // #included from: internal/catch_impl.hpp #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED // Collect all the implementation files together here // These are the equivalent of what would usually be cpp files #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wweak-vtables" #endif // #included from: ../catch_session.hpp #define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED // #included from: internal/catch_commandline.hpp #define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED // #included from: catch_config.hpp #define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED // #included from: catch_test_spec_parser.hpp #define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" #endif // #included from: catch_test_spec.hpp #define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" #endif // #included from: catch_wildcard_pattern.hpp #define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED #include namespace Catch { class WildcardPattern { enum WildcardPosition { NoWildcard = 0, WildcardAtStart = 1, WildcardAtEnd = 2, WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd }; public: WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) : m_caseSensitivity( caseSensitivity ), m_wildcard( NoWildcard ), m_pattern( adjustCase( pattern ) ) { if( startsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } if( endsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } } virtual ~WildcardPattern(); virtual bool matches( std::string const& str ) const { switch( m_wildcard ) { case NoWildcard: return m_pattern == adjustCase( str ); case WildcardAtStart: return endsWith( adjustCase( str ), m_pattern ); case WildcardAtEnd: return startsWith( adjustCase( str ), m_pattern ); case WildcardAtBothEnds: return contains( adjustCase( str ), m_pattern ); } #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunreachable-code" #endif throw std::logic_error( "Unknown enum" ); #ifdef __clang__ # pragma clang diagnostic pop #endif } private: std::string adjustCase( std::string const& str ) const { return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; } CaseSensitive::Choice m_caseSensitivity; WildcardPosition m_wildcard; std::string m_pattern; }; } #include #include namespace Catch { class TestSpec { struct Pattern : SharedImpl<> { virtual ~Pattern(); virtual bool matches( TestCaseInfo const& testCase ) const = 0; }; class NamePattern : public Pattern { public: NamePattern( std::string const& name ) : m_wildcardPattern( toLower( name ), CaseSensitive::No ) {} virtual ~NamePattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return m_wildcardPattern.matches( toLower( testCase.name ) ); } private: WildcardPattern m_wildcardPattern; }; class TagPattern : public Pattern { public: TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} virtual ~TagPattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); } private: std::string m_tag; }; class ExcludedPattern : public Pattern { public: ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} virtual ~ExcludedPattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } private: Ptr m_underlyingPattern; }; struct Filter { std::vector > m_patterns; bool matches( TestCaseInfo const& testCase ) const { // All patterns in a filter must match for the filter to be a match for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { if( !(*it)->matches( testCase ) ) return false; } return true; } }; public: bool hasFilters() const { return !m_filters.empty(); } bool matches( TestCaseInfo const& testCase ) const { // A TestSpec matches if any filter matches for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) if( it->matches( testCase ) ) return true; return false; } private: std::vector m_filters; friend class TestSpecParser; }; } #ifdef __clang__ # pragma clang diagnostic pop #endif namespace Catch { class TestSpecParser { enum Mode{ None, Name, QuotedName, Tag, EscapedName }; Mode m_mode; bool m_exclusion; std::size_t m_start, m_pos; std::string m_arg; std::vector m_escapeChars; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; ITagAliasRegistry const* m_tagAliases; public: TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} TestSpecParser& parse( std::string const& arg ) { m_mode = None; m_exclusion = false; m_start = std::string::npos; m_arg = m_tagAliases->expandAliases( arg ); m_escapeChars.clear(); for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) visitChar( m_arg[m_pos] ); if( m_mode == Name ) addPattern(); return *this; } TestSpec testSpec() { addFilter(); return m_testSpec; } private: void visitChar( char c ) { if( m_mode == None ) { switch( c ) { case ' ': return; case '~': m_exclusion = true; return; case '[': return startNewMode( Tag, ++m_pos ); case '"': return startNewMode( QuotedName, ++m_pos ); case '\\': return escape(); default: startNewMode( Name, m_pos ); break; } } if( m_mode == Name ) { if( c == ',' ) { addPattern(); addFilter(); } else if( c == '[' ) { if( subString() == "exclude:" ) m_exclusion = true; else addPattern(); startNewMode( Tag, ++m_pos ); } else if( c == '\\' ) escape(); } else if( m_mode == EscapedName ) m_mode = Name; else if( m_mode == QuotedName && c == '"' ) addPattern(); else if( m_mode == Tag && c == ']' ) addPattern(); } void startNewMode( Mode mode, std::size_t start ) { m_mode = mode; m_start = start; } void escape() { if( m_mode == None ) m_start = m_pos; m_mode = EscapedName; m_escapeChars.push_back( m_pos ); } std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } template void addPattern() { std::string token = subString(); for( size_t i = 0; i < m_escapeChars.size(); ++i ) token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); } if( !token.empty() ) { Ptr pattern = new T( token ); if( m_exclusion ) pattern = new TestSpec::ExcludedPattern( pattern ); m_currentFilter.m_patterns.push_back( pattern ); } m_exclusion = false; m_mode = None; } void addFilter() { if( !m_currentFilter.m_patterns.empty() ) { m_testSpec.m_filters.push_back( m_currentFilter ); m_currentFilter = TestSpec::Filter(); } } }; inline TestSpec parseTestSpec( std::string const& arg ) { return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); } } // namespace Catch #ifdef __clang__ # pragma clang diagnostic pop #endif // #included from: catch_interfaces_config.h #define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED #include #include #include namespace Catch { struct Verbosity { enum Level { NoOutput = 0, Quiet, Normal }; }; struct WarnAbout { enum What { Nothing = 0x00, NoAssertions = 0x01 }; }; struct ShowDurations { enum OrNot { DefaultForReporter, Always, Never }; }; struct RunTests { enum InWhatOrder { InDeclarationOrder, InLexicographicalOrder, InRandomOrder }; }; struct UseColour { enum YesOrNo { Auto, Yes, No }; }; class TestSpec; struct IConfig : IShared { virtual ~IConfig(); virtual bool allowThrows() const = 0; virtual std::ostream& stream() const = 0; virtual std::string name() const = 0; virtual bool includeSuccessfulResults() const = 0; virtual bool shouldDebugBreak() const = 0; virtual bool warnAboutMissingAssertions() const = 0; virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; virtual ShowDurations::OrNot showDurations() const = 0; virtual TestSpec const& testSpec() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual UseColour::YesOrNo useColour() const = 0; virtual std::vector const& getSectionsToRun() const = 0; }; } // #included from: catch_stream.h #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED // #included from: catch_streambuf.h #define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED #include namespace Catch { class StreamBufBase : public std::streambuf { public: virtual ~StreamBufBase() CATCH_NOEXCEPT; }; } #include #include #include #include namespace Catch { std::ostream& cout(); std::ostream& cerr(); struct IStream { virtual ~IStream() CATCH_NOEXCEPT; virtual std::ostream& stream() const = 0; }; class FileStream : public IStream { mutable std::ofstream m_ofs; public: FileStream( std::string const& filename ); virtual ~FileStream() CATCH_NOEXCEPT; public: // IStream virtual std::ostream& stream() const CATCH_OVERRIDE; }; class CoutStream : public IStream { mutable std::ostream m_os; public: CoutStream(); virtual ~CoutStream() CATCH_NOEXCEPT; public: // IStream virtual std::ostream& stream() const CATCH_OVERRIDE; }; class DebugOutStream : public IStream { CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; mutable std::ostream m_os; public: DebugOutStream(); virtual ~DebugOutStream() CATCH_NOEXCEPT; public: // IStream virtual std::ostream& stream() const CATCH_OVERRIDE; }; } #include #include #include #include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 #endif namespace Catch { struct ConfigData { ConfigData() : listTests( false ), listTags( false ), listReporters( false ), listTestNamesOnly( false ), listExtraInfo( false ), showSuccessfulTests( false ), shouldDebugBreak( false ), noThrow( false ), showHelp( false ), showInvisibles( false ), filenamesAsTags( false ), abortAfter( -1 ), rngSeed( 0 ), verbosity( Verbosity::Normal ), warnings( WarnAbout::Nothing ), showDurations( ShowDurations::DefaultForReporter ), runOrder( RunTests::InDeclarationOrder ), useColour( UseColour::Auto ) {} bool listTests; bool listTags; bool listReporters; bool listTestNamesOnly; bool listExtraInfo; bool showSuccessfulTests; bool shouldDebugBreak; bool noThrow; bool showHelp; bool showInvisibles; bool filenamesAsTags; int abortAfter; unsigned int rngSeed; Verbosity::Level verbosity; WarnAbout::What warnings; ShowDurations::OrNot showDurations; RunTests::InWhatOrder runOrder; UseColour::YesOrNo useColour; std::string outputFilename; std::string name; std::string processName; std::vector reporterNames; std::vector testsOrTags; std::vector sectionsToRun; }; class Config : public SharedImpl { private: Config( Config const& other ); Config& operator = ( Config const& other ); virtual void dummy(); public: Config() {} Config( ConfigData const& data ) : m_data( data ), m_stream( openStream() ) { if( !data.testsOrTags.empty() ) { TestSpecParser parser( ITagAliasRegistry::get() ); for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) parser.parse( data.testsOrTags[i] ); m_testSpec = parser.testSpec(); } } virtual ~Config() {} std::string const& getFilename() const { return m_data.outputFilename ; } bool listTests() const { return m_data.listTests; } bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } bool listTags() const { return m_data.listTags; } bool listReporters() const { return m_data.listReporters; } bool listExtraInfo() const { return m_data.listExtraInfo; } std::string getProcessName() const { return m_data.processName; } std::vector const& getReporterNames() const { return m_data.reporterNames; } std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } bool showHelp() const { return m_data.showHelp; } // IConfig interface virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } private: IStream const* openStream() { if( m_data.outputFilename.empty() ) return new CoutStream(); else if( m_data.outputFilename[0] == '%' ) { if( m_data.outputFilename == "%debug" ) return new DebugOutStream(); else throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); } else return new FileStream( m_data.outputFilename ); } ConfigData m_data; CATCH_AUTO_PTR( IStream const ) m_stream; TestSpec m_testSpec; }; } // end namespace Catch // #included from: catch_clara.h #define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED // Use Catch's value for console width (store Clara's off to the side, if present) #ifdef CLARA_CONFIG_CONSOLE_WIDTH #define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH #undef CLARA_CONFIG_CONSOLE_WIDTH #endif #define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH // Declare Clara inside the Catch namespace #define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { // #included from: ../external/clara.h // Version 0.0.2.4 // Only use header guard if we are not using an outer namespace #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) #ifndef STITCH_CLARA_OPEN_NAMESPACE #define TWOBLUECUBES_CLARA_H_INCLUDED #define STITCH_CLARA_OPEN_NAMESPACE #define STITCH_CLARA_CLOSE_NAMESPACE #else #define STITCH_CLARA_CLOSE_NAMESPACE } #endif #define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE // ----------- #included from tbc_text_format.h ----------- // Only use header guard if we are not using an outer namespace #if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) #ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE #define TBC_TEXT_FORMAT_H_INCLUDED #endif #include #include #include #include #include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { #endif namespace Tbc { #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; #else const unsigned int consoleWidth = 80; #endif struct TextAttributes { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), width( consoleWidth-1 ), tabChar( '\t' ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap char tabChar; // If this char is seen the indent is changed to current pos }; class Text { public: Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { std::string wrappableChars = " [({.,/|\\-"; std::size_t indent = _attr.initialIndent != std::string::npos ? _attr.initialIndent : _attr.indent; std::string remainder = _str; while( !remainder.empty() ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } std::size_t tabPos = std::string::npos; std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); std::size_t pos = remainder.find_first_of( '\n' ); if( pos <= width ) { width = pos; } pos = remainder.find_last_of( _attr.tabChar, width ); if( pos != std::string::npos ) { tabPos = pos; if( remainder[width] == '\n' ) width--; remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); } if( width == remainder.size() ) { spliceLine( indent, remainder, width ); } else if( remainder[width] == '\n' ) { spliceLine( indent, remainder, width ); if( width <= 1 || remainder.size() != 1 ) remainder = remainder.substr( 1 ); indent = _attr.indent; } else { pos = remainder.find_last_of( wrappableChars, width ); if( pos != std::string::npos && pos > 0 ) { spliceLine( indent, remainder, pos ); if( remainder[0] == ' ' ) remainder = remainder.substr( 1 ); } else { spliceLine( indent, remainder, width-1 ); lines.back() += "-"; } if( lines.size() == 1 ) indent = _attr.indent; if( tabPos != std::string::npos ) indent += tabPos; } } } void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); _remainder = _remainder.substr( _pos ); } typedef std::vector::const_iterator const_iterator; const_iterator begin() const { return lines.begin(); } const_iterator end() const { return lines.end(); } std::string const& last() const { return lines.back(); } std::size_t size() const { return lines.size(); } std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } std::string toString() const { std::ostringstream oss; oss << *this; return oss.str(); } inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); it != itEnd; ++it ) { if( it != _text.begin() ) _stream << "\n"; _stream << *it; } return _stream; } private: std::string str; TextAttributes attr; std::vector lines; }; } // end namespace Tbc #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE } // end outer namespace #endif #endif // TBC_TEXT_FORMAT_H_INCLUDED // ----------- end of #include from tbc_text_format.h ----------- // ........... back in clara.h #undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE // ----------- #included from clara_compilers.h ----------- #ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED #define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED // Detect a number of compiler features - mostly C++11/14 conformance - by compiler // The following features are defined: // // CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? // CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? // CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods // CLARA_CONFIG_CPP11_OVERRIDE : is override supported? // CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) // CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? // In general each macro has a _NO_ form // (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. // All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 #ifdef __clang__ #if __has_feature(cxx_nullptr) #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR #endif #if __has_feature(cxx_noexcept) #define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT #endif #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // GCC #ifdef __GNUC__ #if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR #endif // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below #endif // __GNUC__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER #if (_MSC_VER >= 1600) #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR #define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR #endif #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) #define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #endif #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // C++ language feature support // catch all support for C++11 #if defined(__cplusplus) && __cplusplus >= 201103L #define CLARA_CPP11_OR_GREATER #if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR #endif #ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT #endif #ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #endif #if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) #define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE #endif #if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) #define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR #endif #endif // __cplusplus >= 201103L // Now set the actual defines based on the above + anything the user has configured #if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_NULLPTR #endif #if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_NOEXCEPT #endif #if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_GENERATED_METHODS #endif #if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_OVERRIDE #endif #if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_UNIQUE_PTR #endif // noexcept support: #if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) #define CLARA_NOEXCEPT noexcept # define CLARA_NOEXCEPT_IS(x) noexcept(x) #else #define CLARA_NOEXCEPT throw() # define CLARA_NOEXCEPT_IS(x) #endif // nullptr support #ifdef CLARA_CONFIG_CPP11_NULLPTR #define CLARA_NULL nullptr #else #define CLARA_NULL NULL #endif // override support #ifdef CLARA_CONFIG_CPP11_OVERRIDE #define CLARA_OVERRIDE override #else #define CLARA_OVERRIDE #endif // unique_ptr support #ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR # define CLARA_AUTO_PTR( T ) std::unique_ptr #else # define CLARA_AUTO_PTR( T ) std::auto_ptr #endif #endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED // ----------- end of #include from clara_compilers.h ----------- // ........... back in clara.h #include #include #include #if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) #define CLARA_PLATFORM_WINDOWS #endif // Use optional outer namespace #ifdef STITCH_CLARA_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE #endif namespace Clara { struct UnpositionalTag {}; extern UnpositionalTag _; #ifdef CLARA_CONFIG_MAIN UnpositionalTag _; #endif namespace Detail { #ifdef CLARA_CONSOLE_WIDTH const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; #else const unsigned int consoleWidth = 80; #endif using namespace Tbc; inline bool startsWith( std::string const& str, std::string const& prefix ) { return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; } template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct IsBool { static const bool value = false; }; template<> struct IsBool { static const bool value = true; }; template void convertInto( std::string const& _source, T& _dest ) { std::stringstream ss; ss << _source; ss >> _dest; if( ss.fail() ) throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); } inline void convertInto( std::string const& _source, std::string& _dest ) { _dest = _source; } char toLowerCh(char c) { return static_cast( std::tolower( c ) ); } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) _dest = true; else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) _dest = false; else throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); } template struct IArgFunction { virtual ~IArgFunction() {} #ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS IArgFunction() = default; IArgFunction( IArgFunction const& ) = default; #endif virtual void set( ConfigT& config, std::string const& value ) const = 0; virtual bool takesArg() const = 0; virtual IArgFunction* clone() const = 0; }; template class BoundArgFunction { public: BoundArgFunction() : functionObj( CLARA_NULL ) {} BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} BoundArgFunction& operator = ( BoundArgFunction const& other ) { IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; delete functionObj; functionObj = newFunctionObj; return *this; } ~BoundArgFunction() { delete functionObj; } void set( ConfigT& config, std::string const& value ) const { functionObj->set( config, value ); } bool takesArg() const { return functionObj->takesArg(); } bool isSet() const { return functionObj != CLARA_NULL; } private: IArgFunction* functionObj; }; template struct NullBinder : IArgFunction{ virtual void set( C&, std::string const& ) const {} virtual bool takesArg() const { return true; } virtual IArgFunction* clone() const { return new NullBinder( *this ); } }; template struct BoundDataMember : IArgFunction{ BoundDataMember( M C::* _member ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { convertInto( stringValue, p.*member ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } M C::* member; }; template struct BoundUnaryMethod : IArgFunction{ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { typename RemoveConstRef::type value; convertInto( stringValue, value ); (p.*member)( value ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } void (C::*member)( M ); }; template struct BoundNullaryMethod : IArgFunction{ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { bool value; convertInto( stringValue, value ); if( value ) (p.*member)(); } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } void (C::*member)(); }; template struct BoundUnaryFunction : IArgFunction{ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} virtual void set( C& obj, std::string const& stringValue ) const { bool value; convertInto( stringValue, value ); if( value ) function( obj ); } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } void (*function)( C& ); }; template struct BoundBinaryFunction : IArgFunction{ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} virtual void set( C& obj, std::string const& stringValue ) const { typename RemoveConstRef::type value; convertInto( stringValue, value ); function( obj, value ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } void (*function)( C&, T ); }; } // namespace Detail inline std::vector argsToVector( int argc, char const* const* const argv ) { std::vector args( static_cast( argc ) ); for( std::size_t i = 0; i < static_cast( argc ); ++i ) args[i] = argv[i]; return args; } class Parser { enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; Mode mode; std::size_t from; bool inQuotes; public: struct Token { enum Type { Positional, ShortOpt, LongOpt }; Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} Type type; std::string data; }; Parser() : mode( None ), from( 0 ), inQuotes( false ){} void parseIntoTokens( std::vector const& args, std::vector& tokens ) { const std::string doubleDash = "--"; for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) parseIntoTokens( args[i], tokens); } void parseIntoTokens( std::string const& arg, std::vector& tokens ) { for( std::size_t i = 0; i < arg.size(); ++i ) { char c = arg[i]; if( c == '"' ) inQuotes = !inQuotes; mode = handleMode( i, c, arg, tokens ); } mode = handleMode( arg.size(), '\0', arg, tokens ); } Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { switch( mode ) { case None: return handleNone( i, c ); case MaybeShortOpt: return handleMaybeShortOpt( i, c ); case ShortOpt: case LongOpt: case SlashOpt: return handleOpt( i, c, arg, tokens ); case Positional: return handlePositional( i, c, arg, tokens ); default: throw std::logic_error( "Unknown mode" ); } } Mode handleNone( std::size_t i, char c ) { if( inQuotes ) { from = i; return Positional; } switch( c ) { case '-': return MaybeShortOpt; #ifdef CLARA_PLATFORM_WINDOWS case '/': from = i+1; return SlashOpt; #endif default: from = i; return Positional; } } Mode handleMaybeShortOpt( std::size_t i, char c ) { switch( c ) { case '-': from = i+1; return LongOpt; default: from = i; return ShortOpt; } } Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) return mode; std::string optName = arg.substr( from, i-from ); if( mode == ShortOpt ) for( std::size_t j = 0; j < optName.size(); ++j ) tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); else if( mode == SlashOpt && optName.size() == 1 ) tokens.push_back( Token( Token::ShortOpt, optName ) ); else tokens.push_back( Token( Token::LongOpt, optName ) ); return None; } Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) return mode; std::string data = arg.substr( from, i-from ); tokens.push_back( Token( Token::Positional, data ) ); return None; } }; template struct CommonArgProperties { CommonArgProperties() {} CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} Detail::BoundArgFunction boundField; std::string description; std::string detail; std::string placeholder; // Only value if boundField takes an arg bool takesArg() const { return !placeholder.empty(); } void validate() const { if( !boundField.isSet() ) throw std::logic_error( "option not bound" ); } }; struct OptionArgProperties { std::vector shortNames; std::string longName; bool hasShortName( std::string const& shortName ) const { return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); } bool hasLongName( std::string const& _longName ) const { return _longName == longName; } }; struct PositionalArgProperties { PositionalArgProperties() : position( -1 ) {} int position; // -1 means non-positional (floating) bool isFixedPositional() const { return position != -1; } }; template class CommandLine { struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { Arg() {} Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} using CommonArgProperties::placeholder; // !TBD std::string dbgName() const { if( !longName.empty() ) return "--" + longName; if( !shortNames.empty() ) return "-" + shortNames[0]; return "positional args"; } std::string commands() const { std::ostringstream oss; bool first = true; std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); for(; it != itEnd; ++it ) { if( first ) first = false; else oss << ", "; oss << "-" << *it; } if( !longName.empty() ) { if( !first ) oss << ", "; oss << "--" << longName; } if( !placeholder.empty() ) oss << " <" << placeholder << ">"; return oss.str(); } }; typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; friend void addOptName( Arg& arg, std::string const& optName ) { if( optName.empty() ) return; if( Detail::startsWith( optName, "--" ) ) { if( !arg.longName.empty() ) throw std::logic_error( "Only one long opt may be specified. '" + arg.longName + "' already specified, now attempting to add '" + optName + "'" ); arg.longName = optName.substr( 2 ); } else if( Detail::startsWith( optName, "-" ) ) arg.shortNames.push_back( optName.substr( 1 ) ); else throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); } friend void setPositionalArg( Arg& arg, int position ) { arg.position = position; } class ArgBuilder { public: ArgBuilder( Arg* arg ) : m_arg( arg ) {} // Bind a non-boolean data member (requires placeholder string) template void bind( M C::* field, std::string const& placeholder ) { m_arg->boundField = new Detail::BoundDataMember( field ); m_arg->placeholder = placeholder; } // Bind a boolean data member (no placeholder required) template void bind( bool C::* field ) { m_arg->boundField = new Detail::BoundDataMember( field ); } // Bind a method taking a single, non-boolean argument (requires a placeholder string) template void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); m_arg->placeholder = placeholder; } // Bind a method taking a single, boolean argument (no placeholder string required) template void bind( void (C::* unaryMethod)( bool ) ) { m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); } // Bind a method that takes no arguments (will be called if opt is present) template void bind( void (C::* nullaryMethod)() ) { m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); } // Bind a free function taking a single argument - the object to operate on (no placeholder string required) template void bind( void (* unaryFunction)( C& ) ) { m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); } // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) template void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); m_arg->placeholder = placeholder; } ArgBuilder& describe( std::string const& description ) { m_arg->description = description; return *this; } ArgBuilder& detail( std::string const& detail ) { m_arg->detail = detail; return *this; } protected: Arg* m_arg; }; class OptBuilder : public ArgBuilder { public: OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} OptBuilder& operator[]( std::string const& optName ) { addOptName( *ArgBuilder::m_arg, optName ); return *this; } }; public: CommandLine() : m_boundProcessName( new Detail::NullBinder() ), m_highestSpecifiedArgPosition( 0 ), m_throwOnUnrecognisedTokens( false ) {} CommandLine( CommandLine const& other ) : m_boundProcessName( other.m_boundProcessName ), m_options ( other.m_options ), m_positionalArgs( other.m_positionalArgs ), m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) { if( other.m_floatingArg.get() ) m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); } CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { m_throwOnUnrecognisedTokens = shouldThrow; return *this; } OptBuilder operator[]( std::string const& optName ) { m_options.push_back( Arg() ); addOptName( m_options.back(), optName ); OptBuilder builder( &m_options.back() ); return builder; } ArgBuilder operator[]( int position ) { m_positionalArgs.insert( std::make_pair( position, Arg() ) ); if( position > m_highestSpecifiedArgPosition ) m_highestSpecifiedArgPosition = position; setPositionalArg( m_positionalArgs[position], position ); ArgBuilder builder( &m_positionalArgs[position] ); return builder; } // Invoke this with the _ instance ArgBuilder operator[]( UnpositionalTag ) { if( m_floatingArg.get() ) throw std::logic_error( "Only one unpositional argument can be added" ); m_floatingArg.reset( new Arg() ); ArgBuilder builder( m_floatingArg.get() ); return builder; } template void bindProcessName( M C::* field ) { m_boundProcessName = new Detail::BoundDataMember( field ); } template void bindProcessName( void (C::*_unaryMethod)( M ) ) { m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); } void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; std::size_t maxWidth = 0; for( it = itBegin; it != itEnd; ++it ) maxWidth = (std::max)( maxWidth, it->commands().size() ); for( it = itBegin; it != itEnd; ++it ) { Detail::Text usage( it->commands(), Detail::TextAttributes() .setWidth( maxWidth+indent ) .setIndent( indent ) ); Detail::Text desc( it->description, Detail::TextAttributes() .setWidth( width - maxWidth - 3 ) ); for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { std::string usageCol = i < usage.size() ? usage[i] : ""; os << usageCol; if( i < desc.size() && !desc[i].empty() ) os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) << desc[i]; os << "\n"; } } } std::string optUsage() const { std::ostringstream oss; optUsage( oss ); return oss.str(); } void argSynopsis( std::ostream& os ) const { for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { if( i > 1 ) os << " "; typename std::map::const_iterator it = m_positionalArgs.find( i ); if( it != m_positionalArgs.end() ) os << "<" << it->second.placeholder << ">"; else if( m_floatingArg.get() ) os << "<" << m_floatingArg->placeholder << ">"; else throw std::logic_error( "non consecutive positional arguments with no floating args" ); } // !TBD No indication of mandatory args if( m_floatingArg.get() ) { if( m_highestSpecifiedArgPosition > 1 ) os << " "; os << "[<" << m_floatingArg->placeholder << "> ...]"; } } std::string argSynopsis() const { std::ostringstream oss; argSynopsis( oss ); return oss.str(); } void usage( std::ostream& os, std::string const& procName ) const { validate(); os << "usage:\n " << procName << " "; argSynopsis( os ); if( !m_options.empty() ) { os << " [options]\n\nwhere options are: \n"; optUsage( os, 2 ); } os << "\n"; } std::string usage( std::string const& procName ) const { std::ostringstream oss; usage( oss, procName ); return oss.str(); } ConfigT parse( std::vector const& args ) const { ConfigT config; parseInto( args, config ); return config; } std::vector parseInto( std::vector const& args, ConfigT& config ) const { std::string processName = args.empty() ? std::string() : args[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); m_boundProcessName.set( config, processName ); std::vector tokens; Parser parser; parser.parseIntoTokens( args, tokens ); return populate( tokens, config ); } std::vector populate( std::vector const& tokens, ConfigT& config ) const { validate(); std::vector unusedTokens = populateOptions( tokens, config ); unusedTokens = populateFixedArgs( unusedTokens, config ); unusedTokens = populateFloatingArgs( unusedTokens, config ); return unusedTokens; } std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { std::vector unusedTokens; std::vector errors; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); for(; it != itEnd; ++it ) { Arg const& arg = *it; try { if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { if( arg.takesArg() ) { if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) errors.push_back( "Expected argument to option: " + token.data ); else arg.boundField.set( config, tokens[++i].data ); } else { arg.boundField.set( config, "true" ); } break; } } catch( std::exception& ex ) { errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); } } if( it == itEnd ) { if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) unusedTokens.push_back( token ); else if( errors.empty() && m_throwOnUnrecognisedTokens ) errors.push_back( "unrecognised option: " + token.data ); } } if( !errors.empty() ) { std::ostringstream oss; for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); it != itEnd; ++it ) { if( it != errors.begin() ) oss << "\n"; oss << *it; } throw std::runtime_error( oss.str() ); } return unusedTokens; } std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { std::vector unusedTokens; int position = 1; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; typename std::map::const_iterator it = m_positionalArgs.find( position ); if( it != m_positionalArgs.end() ) it->second.boundField.set( config, token.data ); else unusedTokens.push_back( token ); if( token.type == Parser::Token::Positional ) position++; } return unusedTokens; } std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { if( !m_floatingArg.get() ) return tokens; std::vector unusedTokens; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; if( token.type == Parser::Token::Positional ) m_floatingArg->boundField.set( config, token.data ); else unusedTokens.push_back( token ); } return unusedTokens; } void validate() const { if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) throw std::logic_error( "No options or arguments specified" ); for( typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); it != itEnd; ++it ) it->validate(); } private: Detail::BoundArgFunction m_boundProcessName; std::vector m_options; std::map m_positionalArgs; ArgAutoPtr m_floatingArg; int m_highestSpecifiedArgPosition; bool m_throwOnUnrecognisedTokens; }; } // end namespace Clara STITCH_CLARA_CLOSE_NAMESPACE #undef STITCH_CLARA_OPEN_NAMESPACE #undef STITCH_CLARA_CLOSE_NAMESPACE #endif // TWOBLUECUBES_CLARA_H_INCLUDED #undef STITCH_CLARA_OPEN_NAMESPACE // Restore Clara's value for console width, if present #ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #endif #include #include namespace Catch { inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } inline void abortAfterX( ConfigData& config, int x ) { if( x < 1 ) throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); config.abortAfter = x; } inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } inline void addWarning( ConfigData& config, std::string const& _warning ) { if( _warning == "NoAssertions" ) config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); else throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); } inline void setOrder( ConfigData& config, std::string const& order ) { if( startsWith( "declared", order ) ) config.runOrder = RunTests::InDeclarationOrder; else if( startsWith( "lexical", order ) ) config.runOrder = RunTests::InLexicographicalOrder; else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); } inline void setRngSeed( ConfigData& config, std::string const& seed ) { if( seed == "time" ) { config.rngSeed = static_cast( std::time(0) ); } else { std::stringstream ss; ss << seed; ss >> config.rngSeed; if( ss.fail() ) throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); } } inline void setVerbosity( ConfigData& config, int level ) { // !TBD: accept strings? config.verbosity = static_cast( level ); } inline void setShowDurations( ConfigData& config, bool _showDurations ) { config.showDurations = _showDurations ? ShowDurations::Always : ShowDurations::Never; } inline void setUseColour( ConfigData& config, std::string const& value ) { std::string mode = toLower( value ); if( mode == "yes" ) config.useColour = UseColour::Yes; else if( mode == "no" ) config.useColour = UseColour::No; else if( mode == "auto" ) config.useColour = UseColour::Auto; else throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); } inline void forceColour( ConfigData& config ) { config.useColour = UseColour::Yes; } inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { std::ifstream f( _filename.c_str() ); if( !f.is_open() ) throw std::domain_error( "Unable to load input file: " + _filename ); std::string line; while( std::getline( f, line ) ) { line = trim(line); if( !line.empty() && !startsWith( line, '#' ) ) { if( !startsWith( line, '"' ) ) line = '"' + line + '"'; addTestOrTags( config, line + ',' ); } } } inline Clara::CommandLine makeCommandLineParser() { using namespace Clara; CommandLine cli; cli.bindProcessName( &ConfigData::processName ); cli["-?"]["-h"]["--help"] .describe( "display usage information" ) .bind( &ConfigData::showHelp ); cli["-l"]["--list-tests"] .describe( "list all/matching test cases" ) .bind( &ConfigData::listTests ); cli["-t"]["--list-tags"] .describe( "list all/matching tags" ) .bind( &ConfigData::listTags ); cli["-s"]["--success"] .describe( "include successful tests in output" ) .bind( &ConfigData::showSuccessfulTests ); cli["-b"]["--break"] .describe( "break into debugger on failure" ) .bind( &ConfigData::shouldDebugBreak ); cli["-e"]["--nothrow"] .describe( "skip exception tests" ) .bind( &ConfigData::noThrow ); cli["-i"]["--invisibles"] .describe( "show invisibles (tabs, newlines)" ) .bind( &ConfigData::showInvisibles ); cli["-o"]["--out"] .describe( "output filename" ) .bind( &ConfigData::outputFilename, "filename" ); cli["-r"]["--reporter"] // .placeholder( "name[:filename]" ) .describe( "reporter to use (defaults to console)" ) .bind( &addReporterName, "name" ); cli["-n"]["--name"] .describe( "suite name" ) .bind( &ConfigData::name, "name" ); cli["-a"]["--abort"] .describe( "abort at first failure" ) .bind( &abortAfterFirst ); cli["-x"]["--abortx"] .describe( "abort after x failures" ) .bind( &abortAfterX, "no. failures" ); cli["-w"]["--warn"] .describe( "enable warnings" ) .bind( &addWarning, "warning name" ); // - needs updating if reinstated // cli.into( &setVerbosity ) // .describe( "level of verbosity (0=no output)" ) // .shortOpt( "v") // .longOpt( "verbosity" ) // .placeholder( "level" ); cli[_] .describe( "which test or tests to use" ) .bind( &addTestOrTags, "test name, pattern or tags" ); cli["-d"]["--durations"] .describe( "show test durations" ) .bind( &setShowDurations, "yes|no" ); cli["-f"]["--input-file"] .describe( "load test names to run from a file" ) .bind( &loadTestNamesFromFile, "filename" ); cli["-#"]["--filenames-as-tags"] .describe( "adds a tag for the filename" ) .bind( &ConfigData::filenamesAsTags ); cli["-c"]["--section"] .describe( "specify section to run" ) .bind( &addSectionToRun, "section name" ); // Less common commands which don't have a short form cli["--list-test-names-only"] .describe( "list all/matching test cases names only" ) .bind( &ConfigData::listTestNamesOnly ); cli["--list-extra-info"] .describe( "list all/matching test cases with more info" ) .bind( &ConfigData::listExtraInfo ); cli["--list-reporters"] .describe( "list all reporters" ) .bind( &ConfigData::listReporters ); cli["--order"] .describe( "test case order (defaults to decl)" ) .bind( &setOrder, "decl|lex|rand" ); cli["--rng-seed"] .describe( "set a specific seed for random numbers" ) .bind( &setRngSeed, "'time'|number" ); cli["--force-colour"] .describe( "force colourised output (deprecated)" ) .bind( &forceColour ); cli["--use-colour"] .describe( "should output be colourised" ) .bind( &setUseColour, "yes|no" ); return cli; } } // end namespace Catch // #included from: internal/catch_list.hpp #define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED // #included from: catch_text.h #define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED #define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH #define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch // #included from: ../external/tbc_text_format.h // Only use header guard if we are not using an outer namespace #ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE # ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED # ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED # define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED # endif # else # define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED # endif #endif #ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED #include #include #include // Use optional outer namespace #ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { #endif namespace Tbc { #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; #else const unsigned int consoleWidth = 80; #endif struct TextAttributes { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), width( consoleWidth-1 ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap }; class Text { public: Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { const std::string wrappableBeforeChars = "[({<\t"; const std::string wrappableAfterChars = "])}>-,./|\\"; const std::string wrappableInsteadOfChars = " \n\r"; std::string indent = _attr.initialIndent != std::string::npos ? std::string( _attr.initialIndent, ' ' ) : std::string( _attr.indent, ' ' ); typedef std::string::const_iterator iterator; iterator it = _str.begin(); const iterator strEnd = _str.end(); while( it != strEnd ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } std::string suffix; std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); iterator itEnd = it+width; iterator itNext = _str.end(); iterator itNewLine = std::find( it, itEnd, '\n' ); if( itNewLine != itEnd ) itEnd = itNewLine; if( itEnd != strEnd ) { bool foundWrapPoint = false; iterator findIt = itEnd; do { if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { itEnd = findIt+1; itNext = findIt+1; foundWrapPoint = true; } else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { itEnd = findIt; itNext = findIt; foundWrapPoint = true; } else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { itNext = findIt+1; itEnd = findIt; foundWrapPoint = true; } if( findIt == it ) break; else --findIt; } while( !foundWrapPoint ); if( !foundWrapPoint ) { // No good wrap char, so we'll break mid word and add a hyphen --itEnd; itNext = itEnd; suffix = "-"; } else { while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) --itEnd; } } lines.push_back( indent + std::string( it, itEnd ) + suffix ); if( indent.size() != _attr.indent ) indent = std::string( _attr.indent, ' ' ); it = itNext; } } typedef std::vector::const_iterator const_iterator; const_iterator begin() const { return lines.begin(); } const_iterator end() const { return lines.end(); } std::string const& last() const { return lines.back(); } std::size_t size() const { return lines.size(); } std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } std::string toString() const { std::ostringstream oss; oss << *this; return oss.str(); } inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); it != itEnd; ++it ) { if( it != _text.begin() ) _stream << "\n"; _stream << *it; } return _stream; } private: std::string str; TextAttributes attr; std::vector lines; }; } // end namespace Tbc #ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE } // end outer namespace #endif #endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED #undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace Catch { using Tbc::Text; using Tbc::TextAttributes; } // #included from: catch_console_colour.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED namespace Catch { struct Colour { enum Code { None = 0, White, Red, Green, Blue, Cyan, Yellow, Grey, Bright = 0x10, BrightRed = Bright | Red, BrightGreen = Bright | Green, LightGrey = Bright | Grey, BrightWhite = Bright | White, // By intention FileName = LightGrey, Warning = Yellow, ResultError = BrightRed, ResultSuccess = BrightGreen, ResultExpectedFailure = Warning, Error = BrightRed, Success = Green, OriginalExpression = Cyan, ReconstructedExpression = Yellow, SecondaryText = LightGrey, Headers = White }; // Use constructed object for RAII guard Colour( Code _colourCode ); Colour( Colour const& other ); ~Colour(); // Use static method for one-shot changes static void use( Code _colourCode ); private: bool m_moved; }; inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } } // end namespace Catch // #included from: catch_interfaces_reporter.h #define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED #include #include #include namespace Catch { struct ReporterConfig { explicit ReporterConfig( Ptr const& _fullConfig ) : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} std::ostream& stream() const { return *m_stream; } Ptr fullConfig() const { return m_fullConfig; } private: std::ostream* m_stream; Ptr m_fullConfig; }; struct ReporterPreferences { ReporterPreferences() : shouldRedirectStdOut( false ) {} bool shouldRedirectStdOut; }; template struct LazyStat : Option { LazyStat() : used( false ) {} LazyStat& operator=( T const& _value ) { Option::operator=( _value ); used = false; return *this; } void reset() { Option::reset(); used = false; } bool used; }; struct TestRunInfo { TestRunInfo( std::string const& _name ) : name( _name ) {} std::string name; }; struct GroupInfo { GroupInfo( std::string const& _name, std::size_t _groupIndex, std::size_t _groupsCount ) : name( _name ), groupIndex( _groupIndex ), groupsCounts( _groupsCount ) {} std::string name; std::size_t groupIndex; std::size_t groupsCounts; }; struct AssertionStats { AssertionStats( AssertionResult const& _assertionResult, std::vector const& _infoMessages, Totals const& _totals ) : assertionResult( _assertionResult ), infoMessages( _infoMessages ), totals( _totals ) { if( assertionResult.hasMessage() ) { // Copy message into messages list. // !TBD This should have been done earlier, somewhere MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); builder << assertionResult.getMessage(); builder.m_info.message = builder.m_stream.str(); infoMessages.push_back( builder.m_info ); } } virtual ~AssertionStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS AssertionStats( AssertionStats const& ) = default; AssertionStats( AssertionStats && ) = default; AssertionStats& operator = ( AssertionStats const& ) = default; AssertionStats& operator = ( AssertionStats && ) = default; # endif AssertionResult assertionResult; std::vector infoMessages; Totals totals; }; struct SectionStats { SectionStats( SectionInfo const& _sectionInfo, Counts const& _assertions, double _durationInSeconds, bool _missingAssertions ) : sectionInfo( _sectionInfo ), assertions( _assertions ), durationInSeconds( _durationInSeconds ), missingAssertions( _missingAssertions ) {} virtual ~SectionStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS SectionStats( SectionStats const& ) = default; SectionStats( SectionStats && ) = default; SectionStats& operator = ( SectionStats const& ) = default; SectionStats& operator = ( SectionStats && ) = default; # endif SectionInfo sectionInfo; Counts assertions; double durationInSeconds; bool missingAssertions; }; struct TestCaseStats { TestCaseStats( TestCaseInfo const& _testInfo, Totals const& _totals, std::string const& _stdOut, std::string const& _stdErr, bool _aborting ) : testInfo( _testInfo ), totals( _totals ), stdOut( _stdOut ), stdErr( _stdErr ), aborting( _aborting ) {} virtual ~TestCaseStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS TestCaseStats( TestCaseStats const& ) = default; TestCaseStats( TestCaseStats && ) = default; TestCaseStats& operator = ( TestCaseStats const& ) = default; TestCaseStats& operator = ( TestCaseStats && ) = default; # endif TestCaseInfo testInfo; Totals totals; std::string stdOut; std::string stdErr; bool aborting; }; struct TestGroupStats { TestGroupStats( GroupInfo const& _groupInfo, Totals const& _totals, bool _aborting ) : groupInfo( _groupInfo ), totals( _totals ), aborting( _aborting ) {} TestGroupStats( GroupInfo const& _groupInfo ) : groupInfo( _groupInfo ), aborting( false ) {} virtual ~TestGroupStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS TestGroupStats( TestGroupStats const& ) = default; TestGroupStats( TestGroupStats && ) = default; TestGroupStats& operator = ( TestGroupStats const& ) = default; TestGroupStats& operator = ( TestGroupStats && ) = default; # endif GroupInfo groupInfo; Totals totals; bool aborting; }; struct TestRunStats { TestRunStats( TestRunInfo const& _runInfo, Totals const& _totals, bool _aborting ) : runInfo( _runInfo ), totals( _totals ), aborting( _aborting ) {} virtual ~TestRunStats(); # ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS TestRunStats( TestRunStats const& _other ) : runInfo( _other.runInfo ), totals( _other.totals ), aborting( _other.aborting ) {} # else TestRunStats( TestRunStats const& ) = default; TestRunStats( TestRunStats && ) = default; TestRunStats& operator = ( TestRunStats const& ) = default; TestRunStats& operator = ( TestRunStats && ) = default; # endif TestRunInfo runInfo; Totals totals; bool aborting; }; class MultipleReporters; struct IStreamingReporter : IShared { virtual ~IStreamingReporter(); // Implementing class must also provide the following static method: // static std::string getDescription(); virtual ReporterPreferences getPreferences() const = 0; virtual void noMatchingTestCases( std::string const& spec ) = 0; virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; // The return value indicates if the messages buffer should be cleared: virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; virtual void sectionEnded( SectionStats const& sectionStats ) = 0; virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; virtual void skipTest( TestCaseInfo const& testInfo ) = 0; virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } }; struct IReporterFactory : IShared { virtual ~IReporterFactory(); virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; virtual std::string getDescription() const = 0; }; struct IReporterRegistry { typedef std::map > FactoryMap; typedef std::vector > Listeners; virtual ~IReporterRegistry(); virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; virtual FactoryMap const& getFactories() const = 0; virtual Listeners const& getListeners() const = 0; }; Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); } #include #include namespace Catch { inline std::size_t listTests( Config const& config ) { TestSpec testSpec = config.testSpec(); if( config.testSpec().hasFilters() ) Catch::cout() << "Matching test cases:\n"; else { Catch::cout() << "All available test cases:\n"; testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } std::size_t matchedTests = 0; TextAttributes nameAttr, descAttr, tagsAttr; nameAttr.setInitialIndent( 2 ).setIndent( 4 ); descAttr.setIndent( 4 ); tagsAttr.setIndent( 6 ); std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); Colour::Code colour = testCaseInfo.isHidden() ? Colour::SecondaryText : Colour::None; Colour colourGuard( colour ); Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; if( config.listExtraInfo() ) { Catch::cout() << " " << testCaseInfo.lineInfo << std::endl; std::string description = testCaseInfo.description; if( description.empty() ) description = "(NO DESCRIPTION)"; Catch::cout() << Text( description, descAttr ) << std::endl; } if( !testCaseInfo.tags.empty() ) Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; } if( !config.testSpec().hasFilters() ) Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; else Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; return matchedTests; } inline std::size_t listTestsNamesOnly( Config const& config ) { TestSpec testSpec = config.testSpec(); if( !config.testSpec().hasFilters() ) testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); std::size_t matchedTests = 0; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); if( startsWith( testCaseInfo.name, '#' ) ) Catch::cout() << '"' << testCaseInfo.name << '"'; else Catch::cout() << testCaseInfo.name; if ( config.listExtraInfo() ) Catch::cout() << "\t@" << testCaseInfo.lineInfo; Catch::cout() << std::endl; } return matchedTests; } struct TagInfo { TagInfo() : count ( 0 ) {} void add( std::string const& spelling ) { ++count; spellings.insert( spelling ); } std::string all() const { std::string out; for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); it != itEnd; ++it ) out += "[" + *it + "]"; return out; } std::set spellings; std::size_t count; }; inline std::size_t listTags( Config const& config ) { TestSpec testSpec = config.testSpec(); if( config.testSpec().hasFilters() ) Catch::cout() << "Tags for matching test cases:\n"; else { Catch::cout() << "All available tags:\n"; testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } std::map tagCounts; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), tagItEnd = it->getTestCaseInfo().tags.end(); tagIt != tagItEnd; ++tagIt ) { std::string tagName = *tagIt; std::string lcaseTagName = toLower( tagName ); std::map::iterator countIt = tagCounts.find( lcaseTagName ); if( countIt == tagCounts.end() ) countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; countIt->second.add( tagName ); } } for( std::map::const_iterator countIt = tagCounts.begin(), countItEnd = tagCounts.end(); countIt != countItEnd; ++countIt ) { std::ostringstream oss; oss << " " << std::setw(2) << countIt->second.count << " "; Text wrapper( countIt->second.all(), TextAttributes() .setInitialIndent( 0 ) .setIndent( oss.str().size() ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); Catch::cout() << oss.str() << wrapper << '\n'; } Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; return tagCounts.size(); } inline std::size_t listReporters( Config const& /*config*/ ) { Catch::cout() << "Available reporters:\n"; IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; std::size_t maxNameLen = 0; for(it = itBegin; it != itEnd; ++it ) maxNameLen = (std::max)( maxNameLen, it->first.size() ); for(it = itBegin; it != itEnd; ++it ) { Text wrapper( it->second->getDescription(), TextAttributes() .setInitialIndent( 0 ) .setIndent( 7+maxNameLen ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); Catch::cout() << " " << it->first << ':' << std::string( maxNameLen - it->first.size() + 2, ' ' ) << wrapper << '\n'; } Catch::cout() << std::endl; return factories.size(); } inline Option list( Config const& config ) { Option listedCount; if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) ) listedCount = listedCount.valueOr(0) + listTests( config ); if( config.listTestNamesOnly() ) listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); if( config.listTags() ) listedCount = listedCount.valueOr(0) + listTags( config ); if( config.listReporters() ) listedCount = listedCount.valueOr(0) + listReporters( config ); return listedCount; } } // end namespace Catch // #included from: internal/catch_run_context.hpp #define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED // #included from: catch_test_case_tracker.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED #include #include #include #include #include CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS namespace Catch { namespace TestCaseTracking { struct NameAndLocation { std::string name; SourceLineInfo location; NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) : name( _name ), location( _location ) {} }; struct ITracker : SharedImpl<> { virtual ~ITracker(); // static queries virtual NameAndLocation const& nameAndLocation() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed virtual bool isSuccessfullyCompleted() const = 0; virtual bool isOpen() const = 0; // Started but not complete virtual bool hasChildren() const = 0; virtual ITracker& parent() = 0; // actions virtual void close() = 0; // Successfully complete virtual void fail() = 0; virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( Ptr const& child ) = 0; virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking virtual bool isSectionTracker() const = 0; virtual bool isIndexTracker() const = 0; }; class TrackerContext { enum RunState { NotStarted, Executing, CompletedCycle }; Ptr m_rootTracker; ITracker* m_currentTracker; RunState m_runState; public: static TrackerContext& instance() { static TrackerContext s_instance; return s_instance; } TrackerContext() : m_currentTracker( CATCH_NULL ), m_runState( NotStarted ) {} ITracker& startRun(); void endRun() { m_rootTracker.reset(); m_currentTracker = CATCH_NULL; m_runState = NotStarted; } void startCycle() { m_currentTracker = m_rootTracker.get(); m_runState = Executing; } void completeCycle() { m_runState = CompletedCycle; } bool completedCycle() const { return m_runState == CompletedCycle; } ITracker& currentTracker() { return *m_currentTracker; } void setCurrentTracker( ITracker* tracker ) { m_currentTracker = tracker; } }; class TrackerBase : public ITracker { protected: enum CycleState { NotStarted, Executing, ExecutingChildren, NeedsAnotherRun, CompletedSuccessfully, Failed }; class TrackerHasName { NameAndLocation m_nameAndLocation; public: TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} bool operator ()( Ptr const& tracker ) { return tracker->nameAndLocation().name == m_nameAndLocation.name && tracker->nameAndLocation().location == m_nameAndLocation.location; } }; typedef std::vector > Children; NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; CycleState m_runState; public: TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) : m_nameAndLocation( nameAndLocation ), m_ctx( ctx ), m_parent( parent ), m_runState( NotStarted ) {} virtual ~TrackerBase(); virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { return m_nameAndLocation; } virtual bool isComplete() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully || m_runState == Failed; } virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully; } virtual bool isOpen() const CATCH_OVERRIDE { return m_runState != NotStarted && !isComplete(); } virtual bool hasChildren() const CATCH_OVERRIDE { return !m_children.empty(); } virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { m_children.push_back( child ); } virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); return( it != m_children.end() ) ? it->get() : CATCH_NULL; } virtual ITracker& parent() CATCH_OVERRIDE { assert( m_parent ); // Should always be non-null except for root return *m_parent; } virtual void openChild() CATCH_OVERRIDE { if( m_runState != ExecutingChildren ) { m_runState = ExecutingChildren; if( m_parent ) m_parent->openChild(); } } virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } void open() { m_runState = Executing; moveToThis(); if( m_parent ) m_parent->openChild(); } virtual void close() CATCH_OVERRIDE { // Close any still open children (e.g. generators) while( &m_ctx.currentTracker() != this ) m_ctx.currentTracker().close(); switch( m_runState ) { case NotStarted: case CompletedSuccessfully: case Failed: throw std::logic_error( "Illogical state" ); case NeedsAnotherRun: break;; case Executing: m_runState = CompletedSuccessfully; break; case ExecutingChildren: if( m_children.empty() || m_children.back()->isComplete() ) m_runState = CompletedSuccessfully; break; default: throw std::logic_error( "Unexpected state" ); } moveToParent(); m_ctx.completeCycle(); } virtual void fail() CATCH_OVERRIDE { m_runState = Failed; if( m_parent ) m_parent->markAsNeedingAnotherRun(); moveToParent(); m_ctx.completeCycle(); } virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { m_runState = NeedsAnotherRun; } private: void moveToParent() { assert( m_parent ); m_ctx.setCurrentTracker( m_parent ); } void moveToThis() { m_ctx.setCurrentTracker( this ); } }; class SectionTracker : public TrackerBase { std::vector m_filters; public: SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) : TrackerBase( nameAndLocation, ctx, parent ) { if( parent ) { while( !parent->isSectionTracker() ) parent = &parent->parent(); SectionTracker& parentSection = static_cast( *parent ); addNextFilters( parentSection.m_filters ); } } virtual ~SectionTracker(); virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isSectionTracker() ); section = static_cast( childTracker ); } else { section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( section ); } if( !ctx.completedCycle() ) section->tryOpen(); return *section; } void tryOpen() { if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) open(); } void addInitialFilters( std::vector const& filters ) { if( !filters.empty() ) { m_filters.push_back(""); // Root - should never be consulted m_filters.push_back(""); // Test Case - not a section filter m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); } } void addNextFilters( std::vector const& filters ) { if( filters.size() > 1 ) m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); } }; class IndexTracker : public TrackerBase { int m_size; int m_index; public: IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) : TrackerBase( nameAndLocation, ctx, parent ), m_size( size ), m_index( -1 ) {} virtual ~IndexTracker(); virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isIndexTracker() ); tracker = static_cast( childTracker ); } else { tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } if( !ctx.completedCycle() && !tracker->isComplete() ) { if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) tracker->moveNext(); tracker->open(); } return *tracker; } int index() const { return m_index; } void moveNext() { m_index++; m_children.clear(); } virtual void close() CATCH_OVERRIDE { TrackerBase::close(); if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) m_runState = Executing; } }; inline ITracker& TrackerContext::startRun() { m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); m_currentTracker = CATCH_NULL; m_runState = Executing; return *m_rootTracker; } } // namespace TestCaseTracking using TestCaseTracking::ITracker; using TestCaseTracking::TrackerContext; using TestCaseTracking::SectionTracker; using TestCaseTracking::IndexTracker; } // namespace Catch CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS // #included from: catch_fatal_condition.hpp #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED namespace Catch { // Report the error condition inline void reportFatal( std::string const& message ) { IContext& context = Catch::getCurrentContext(); IResultCapture* resultCapture = context.getResultCapture(); resultCapture->handleFatalErrorCondition( message ); } } // namespace Catch #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// // #included from: catch_windows_h_proxy.h #define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED #ifdef CATCH_DEFINES_NOMINMAX # define NOMINMAX #endif #ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif #ifdef __AFXDLL #include #else #include #endif #ifdef CATCH_DEFINES_NOMINMAX # undef NOMINMAX #endif #ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN # undef WIN32_LEAN_AND_MEAN #endif # if !defined ( CATCH_CONFIG_WINDOWS_SEH ) namespace Catch { struct FatalConditionHandler { void reset() {} }; } # else // CATCH_CONFIG_WINDOWS_SEH is defined namespace Catch { struct SignalDefs { DWORD id; const char* name; }; extern SignalDefs signalDefs[]; // There is no 1-1 mapping between signals and windows exceptions. // Windows can easily distinguish between SO and SigSegV, // but SigInt, SigTerm, etc are handled differently. SignalDefs signalDefs[] = { { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, }; struct FatalConditionHandler { static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { reportFatal(signalDefs[i].name); } } // If its not an exception we care about, pass it along. // This stops us from eating debugger breaks etc. return EXCEPTION_CONTINUE_SEARCH; } FatalConditionHandler() { isSet = true; // 32k seems enough for Catch to handle stack overflow, // but the value was found experimentally, so there is no strong guarantee guaranteeSize = 32 * 1024; exceptionHandlerHandle = CATCH_NULL; // Register as first handler in current chain exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); // Pass in guarantee size to be filled SetThreadStackGuarantee(&guaranteeSize); } static void reset() { if (isSet) { // Unregister handler and restore the old guarantee RemoveVectoredExceptionHandler(exceptionHandlerHandle); SetThreadStackGuarantee(&guaranteeSize); exceptionHandlerHandle = CATCH_NULL; isSet = false; } } ~FatalConditionHandler() { reset(); } private: static bool isSet; static ULONG guaranteeSize; static PVOID exceptionHandlerHandle; }; bool FatalConditionHandler::isSet = false; ULONG FatalConditionHandler::guaranteeSize = 0; PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; } // namespace Catch # endif // CATCH_CONFIG_WINDOWS_SEH #else // Not Windows - assumed to be POSIX compatible ////////////////////////// # if !defined(CATCH_CONFIG_POSIX_SIGNALS) namespace Catch { struct FatalConditionHandler { void reset() {} }; } # else // CATCH_CONFIG_POSIX_SIGNALS is defined #include namespace Catch { struct SignalDefs { int id; const char* name; }; extern SignalDefs signalDefs[]; SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGILL, "SIGILL - Illegal instruction signal" }, { SIGFPE, "SIGFPE - Floating point error signal" }, { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, { SIGTERM, "SIGTERM - Termination request signal" }, { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } }; struct FatalConditionHandler { static bool isSet; static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; static stack_t oldSigStack; static char altStackMem[32768]; static void handleSignal( int sig ) { std::string name = ""; for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { SignalDefs &def = signalDefs[i]; if (sig == def.id) { name = def.name; break; } } reset(); reportFatal(name); raise( sig ); } FatalConditionHandler() { isSet = true; stack_t sigStack; sigStack.ss_sp = altStackMem; sigStack.ss_size = 32768; sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = { 0 }; sa.sa_handler = handleSignal; sa.sa_flags = SA_ONSTACK; for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); } } ~FatalConditionHandler() { reset(); } static void reset() { if( isSet ) { // Set signals back to previous values -- hopefully nobody overwrote them in the meantime for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); } // Return the old stack sigaltstack(&oldSigStack, CATCH_NULL); isSet = false; } } }; bool FatalConditionHandler::isSet = false; struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; stack_t FatalConditionHandler::oldSigStack = {}; char FatalConditionHandler::altStackMem[32768] = {}; } // namespace Catch # endif // CATCH_CONFIG_POSIX_SIGNALS #endif // not Windows #include #include namespace Catch { class StreamRedirect { public: StreamRedirect( std::ostream& stream, std::string& targetString ) : m_stream( stream ), m_prevBuf( stream.rdbuf() ), m_targetString( targetString ) { stream.rdbuf( m_oss.rdbuf() ); } ~StreamRedirect() { m_targetString += m_oss.str(); m_stream.rdbuf( m_prevBuf ); } private: std::ostream& m_stream; std::streambuf* m_prevBuf; std::ostringstream m_oss; std::string& m_targetString; }; /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { RunContext( RunContext const& ); void operator =( RunContext const& ); public: explicit RunContext( Ptr const& _config, Ptr const& reporter ) : m_runInfo( _config->name() ), m_context( getCurrentMutableContext() ), m_activeTestCase( CATCH_NULL ), m_config( _config ), m_reporter( reporter ), m_shouldReportUnexpected ( true ) { m_context.setRunner( this ); m_context.setConfig( m_config ); m_context.setResultCapture( this ); m_reporter->testRunStarting( m_runInfo ); } virtual ~RunContext() { m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); } void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); } void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); } Totals runTest( TestCase const& testCase ) { Totals prevTotals = m_totals; std::string redirectedCout; std::string redirectedCerr; TestCaseInfo testInfo = testCase.getTestCaseInfo(); m_reporter->testCaseStarting( testInfo ); m_activeTestCase = &testCase; do { ITracker& rootTracker = m_trackerContext.startRun(); assert( rootTracker.isSectionTracker() ); static_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); do { m_trackerContext.startCycle(); m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); runCurrentTest( redirectedCout, redirectedCerr ); } while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); } // !TBD: deprecated - this will be replaced by indexed trackers while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); Totals deltaTotals = m_totals.delta( prevTotals ); if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { deltaTotals.assertions.failed++; deltaTotals.testCases.passed--; deltaTotals.testCases.failed++; } m_totals.testCases += deltaTotals.testCases; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, redirectedCout, redirectedCerr, aborting() ) ); m_activeTestCase = CATCH_NULL; m_testCaseTracker = CATCH_NULL; return deltaTotals; } Ptr config() const { return m_config; } private: // IResultCapture virtual void assertionEnded( AssertionResult const& result ) { if( result.getResultType() == ResultWas::Ok ) { m_totals.assertions.passed++; } else if( !result.isOk() ) { m_totals.assertions.failed++; } // We have no use for the return value (whether messages should be cleared), because messages were made scoped // and should be let to clear themselves out. static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); // Reset working state m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastResult = result; } virtual bool sectionStarted ( SectionInfo const& sectionInfo, Counts& assertions ) { ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); if( !sectionTracker.isOpen() ) return false; m_activeSections.push_back( §ionTracker ); m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; m_reporter->sectionStarting( sectionInfo ); assertions = m_totals.assertions; return true; } bool testForMissingAssertions( Counts& assertions ) { if( assertions.total() != 0 ) return false; if( !m_config->warnAboutMissingAssertions() ) return false; if( m_trackerContext.currentTracker().hasChildren() ) return false; m_totals.assertions.failed++; assertions.failed++; return true; } virtual void sectionEnded( SectionEndInfo const& endInfo ) { Counts assertions = m_totals.assertions - endInfo.prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); if( !m_activeSections.empty() ) { m_activeSections.back()->close(); m_activeSections.pop_back(); } m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); m_messages.clear(); } virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { if( m_unfinishedSections.empty() ) m_activeSections.back()->fail(); else m_activeSections.back()->close(); m_activeSections.pop_back(); m_unfinishedSections.push_back( endInfo ); } virtual void pushScopedMessage( MessageInfo const& message ) { m_messages.push_back( message ); } virtual void popScopedMessage( MessageInfo const& message ) { m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); } virtual std::string getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name : std::string(); } virtual const AssertionResult* getLastResult() const { return &m_lastResult; } virtual void exceptionEarlyReported() { m_shouldReportUnexpected = false; } virtual void handleFatalErrorCondition( std::string const& message ) { // Don't rebuild the result -- the stringification itself can cause more fatal errors // Instead, fake a result data. AssertionResultData tempResult; tempResult.resultType = ResultWas::FatalErrorCondition; tempResult.message = message; AssertionResult result(m_lastAssertionInfo, tempResult); getResultCapture().assertionEnded(result); handleUnfinishedSections(); // Recreate section for test case (as we will lose the one that was in scope) TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); Counts assertions; assertions.failed = 1; SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); m_reporter->sectionEnded( testCaseSectionStats ); TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); Totals deltaTotals; deltaTotals.testCases.failed = 1; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, std::string(), std::string(), false ) ); m_totals.testCases.failed++; testGroupEnded( std::string(), m_totals, 1, 1 ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); } public: // !TBD We need to do this another way! bool aborting() const { return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); } private: void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); m_reporter->sectionStarting( testCaseSection ); Counts prevAssertions = m_totals.assertions; double duration = 0; m_shouldReportUnexpected = true; try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); seedRng( *m_config ); Timer timer; timer.start(); if( m_reporter->getPreferences().shouldRedirectStdOut ) { StreamRedirect coutRedir( Catch::cout(), redirectedCout ); StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); invokeActiveTestCase(); } else { invokeActiveTestCase(); } duration = timer.getElapsedSeconds(); } catch( TestFailureException& ) { // This just means the test was aborted due to failure } catch(...) { // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions // are reported without translation at the point of origin. if (m_shouldReportUnexpected) { makeUnexpectedResultBuilder().useActiveException(); } } m_testCaseTracker->close(); handleUnfinishedSections(); m_messages.clear(); Counts assertions = m_totals.assertions - prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); if( testCaseInfo.okToFail() ) { std::swap( assertions.failedButOk, assertions.failed ); m_totals.assertions.failed -= assertions.failedButOk; m_totals.assertions.failedButOk += assertions.failedButOk; } SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); m_reporter->sectionEnded( testCaseSectionStats ); } void invokeActiveTestCase() { FatalConditionHandler fatalConditionHandler; // Handle signals m_activeTestCase->invoke(); fatalConditionHandler.reset(); } private: ResultBuilder makeUnexpectedResultBuilder() const { return ResultBuilder( m_lastAssertionInfo.macroName, m_lastAssertionInfo.lineInfo, m_lastAssertionInfo.capturedExpression, m_lastAssertionInfo.resultDisposition ); } void handleUnfinishedSections() { // If sections ended prematurely due to an exception we stored their // infos here so we can tear them down outside the unwind process. for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), itEnd = m_unfinishedSections.rend(); it != itEnd; ++it ) sectionEnded( *it ); m_unfinishedSections.clear(); } TestRunInfo m_runInfo; IMutableContext& m_context; TestCase const* m_activeTestCase; ITracker* m_testCaseTracker; ITracker* m_currentSectionTracker; AssertionResult m_lastResult; Ptr m_config; Totals m_totals; Ptr m_reporter; std::vector m_messages; AssertionInfo m_lastAssertionInfo; std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; bool m_shouldReportUnexpected; }; IResultCapture& getResultCapture() { if( IResultCapture* capture = getCurrentContext().getResultCapture() ) return *capture; else throw std::logic_error( "No result capture instance" ); } } // end namespace Catch // #included from: internal/catch_version.h #define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED namespace Catch { // Versioning information struct Version { Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, char const * const _branchName, unsigned int _buildNumber ); unsigned int const majorVersion; unsigned int const minorVersion; unsigned int const patchNumber; // buildNumber is only used if branchName is not null char const * const branchName; unsigned int const buildNumber; friend std::ostream& operator << ( std::ostream& os, Version const& version ); private: void operator=( Version const& ); }; inline Version libraryVersion(); } #include #include #include namespace Catch { Ptr createReporter( std::string const& reporterName, Ptr const& config ) { Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); if( !reporter ) { std::ostringstream oss; oss << "No reporter registered with name: '" << reporterName << "'"; throw std::domain_error( oss.str() ); } return reporter; } Ptr makeReporter( Ptr const& config ) { std::vector reporters = config->getReporterNames(); if( reporters.empty() ) reporters.push_back( "console" ); Ptr reporter; for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); it != itEnd; ++it ) reporter = addReporter( reporter, createReporter( *it, config ) ); return reporter; } Ptr addListeners( Ptr const& config, Ptr reporters ) { IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); it != itEnd; ++it ) reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); return reporters; } Totals runTests( Ptr const& config ) { Ptr iconfig = config.get(); Ptr reporter = makeReporter( config ); reporter = addListeners( iconfig, reporter ); RunContext context( iconfig, reporter ); Totals totals; context.testGroupStarting( config->name(), 1, 1 ); TestSpec testSpec = config->testSpec(); if( !testSpec.hasFilters() ) testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); it != itEnd; ++it ) { if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) totals += context.runTest( *it ); else reporter->skipTest( *it ); } context.testGroupEnded( iconfig->name(), totals, 1, 1 ); return totals; } void applyFilenamesAsTags( IConfig const& config ) { std::vector const& tests = getAllTestCasesSorted( config ); for(std::size_t i = 0; i < tests.size(); ++i ) { TestCase& test = const_cast( tests[i] ); std::set tags = test.tags; std::string filename = test.lineInfo.file; std::string::size_type lastSlash = filename.find_last_of( "\\/" ); if( lastSlash != std::string::npos ) filename = filename.substr( lastSlash+1 ); std::string::size_type lastDot = filename.find_last_of( "." ); if( lastDot != std::string::npos ) filename = filename.substr( 0, lastDot ); tags.insert( "#" + filename ); setTags( test, tags ); } } class Session : NonCopyable { static bool alreadyInstantiated; public: struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; Session() : m_cli( makeCommandLineParser() ) { if( alreadyInstantiated ) { std::string msg = "Only one instance of Catch::Session can ever be used"; Catch::cerr() << msg << std::endl; throw std::logic_error( msg ); } alreadyInstantiated = true; } ~Session() { Catch::cleanUp(); } void showHelp( std::string const& processName ) { Catch::cout() << "\nCatch v" << libraryVersion() << "\n"; m_cli.usage( Catch::cout(), processName ); Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; } int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); if( m_configData.showHelp ) showHelp( m_configData.processName ); m_config.reset(); } catch( std::exception& ex ) { { Colour colourGuard( Colour::Red ); Catch::cerr() << "\nError(s) in input:\n" << Text( ex.what(), TextAttributes().setIndent(2) ) << "\n\n"; } m_cli.usage( Catch::cout(), m_configData.processName ); return (std::numeric_limits::max)(); } return 0; } void useConfigData( ConfigData const& _configData ) { m_configData = _configData; m_config.reset(); } int run( int argc, char const* const* const argv ) { int returnCode = applyCommandLine( argc, argv ); if( returnCode == 0 ) returnCode = run(); return returnCode; } #if defined(WIN32) && defined(UNICODE) int run( int argc, wchar_t const* const* const argv ) { char **utf8Argv = new char *[ argc ]; for ( int i = 0; i < argc; ++i ) { int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); utf8Argv[ i ] = new char[ bufSize ]; WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); } int returnCode = applyCommandLine( argc, utf8Argv ); if( returnCode == 0 ) returnCode = run(); for ( int i = 0; i < argc; ++i ) delete [] utf8Argv[ i ]; delete [] utf8Argv; return returnCode; } #endif int run() { if( m_configData.showHelp ) return 0; try { config(); // Force config to be constructed seedRng( *m_config ); if( m_configData.filenamesAsTags ) applyFilenamesAsTags( *m_config ); // Handle list request if( Option listed = list( config() ) ) return static_cast( *listed ); return static_cast( runTests( m_config ).assertions.failed ); } catch( std::exception& ex ) { Catch::cerr() << ex.what() << std::endl; return (std::numeric_limits::max)(); } } Clara::CommandLine const& cli() const { return m_cli; } std::vector const& unusedTokens() const { return m_unusedTokens; } ConfigData& configData() { return m_configData; } Config& config() { if( !m_config ) m_config = new Config( m_configData ); return *m_config; } private: Clara::CommandLine m_cli; std::vector m_unusedTokens; ConfigData m_configData; Ptr m_config; }; bool Session::alreadyInstantiated = false; } // end namespace Catch // #included from: catch_registry_hub.hpp #define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED // #included from: catch_test_case_registry_impl.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #include #include #include #include namespace Catch { struct RandomNumberGenerator { typedef std::ptrdiff_t result_type; result_type operator()( result_type n ) const { return rand() % n; } #ifdef CATCH_CONFIG_CPP11_SHUFFLE static constexpr result_type min() { return 0; } static constexpr result_type max() { return 1000000; } result_type operator()() const { return rand() % max(); } #endif template static void shuffle( V& vector ) { RandomNumberGenerator rng; #ifdef CATCH_CONFIG_CPP11_SHUFFLE std::shuffle( vector.begin(), vector.end(), rng ); #else random_shuffle( vector.begin(), vector.end(), rng ); #endif } }; inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { std::vector sorted = unsortedTestCases; switch( config.runOrder() ) { case RunTests::InLexicographicalOrder: std::sort( sorted.begin(), sorted.end() ); break; case RunTests::InRandomOrder: { seedRng( config ); RandomNumberGenerator::shuffle( sorted ); } break; case RunTests::InDeclarationOrder: // already in declaration order break; } return sorted; } bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); } void enforceNoDuplicateTestCases( std::vector const& functions ) { std::set seenFunctions; for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); it != itEnd; ++it ) { std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); if( !prev.second ) { std::ostringstream ss; ss << Colour( Colour::Red ) << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; throw std::runtime_error(ss.str()); } } } std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { std::vector filtered; filtered.reserve( testCases.size() ); for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); it != itEnd; ++it ) if( matchTest( *it, testSpec, config ) ) filtered.push_back( *it ); return filtered; } std::vector const& getAllTestCasesSorted( IConfig const& config ) { return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); } class TestRegistry : public ITestCaseRegistry { public: TestRegistry() : m_currentSortOrder( RunTests::InDeclarationOrder ), m_unnamedCount( 0 ) {} virtual ~TestRegistry(); virtual void registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; if( name.empty() ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); } m_functions.push_back( testCase ); } virtual std::vector const& getAllTests() const { return m_functions; } virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { if( m_sortedFunctions.empty() ) enforceNoDuplicateTestCases( m_functions ); if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { m_sortedFunctions = sortTests( config, m_functions ); m_currentSortOrder = config.runOrder(); } return m_sortedFunctions; } private: std::vector m_functions; mutable RunTests::InWhatOrder m_currentSortOrder; mutable std::vector m_sortedFunctions; size_t m_unnamedCount; std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised }; /////////////////////////////////////////////////////////////////////////// class FreeFunctionTestCase : public SharedImpl { public: FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} virtual void invoke() const { m_fun(); } private: virtual ~FreeFunctionTestCase(); TestFunction m_fun; }; inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; if( startsWith( className, '&' ) ) { std::size_t lastColons = className.rfind( "::" ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); if( penultimateColons == std::string::npos ) penultimateColons = 1; className = className.substr( penultimateColons, lastColons-penultimateColons ); } return className; } void registerTestCase ( ITestCase* testCase, char const* classOrQualifiedMethodName, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { getMutableRegistryHub().registerTest ( makeTestCase ( testCase, extractClassName( classOrQualifiedMethodName ), nameAndDesc.name, nameAndDesc.description, lineInfo ) ); } void registerTestCaseFunction ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ) { registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); } /////////////////////////////////////////////////////////////////////////// AutoReg::AutoReg ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ) { registerTestCaseFunction( function, lineInfo, nameAndDesc ); } AutoReg::~AutoReg() {} } // end namespace Catch // #included from: catch_reporter_registry.hpp #define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED #include namespace Catch { class ReporterRegistry : public IReporterRegistry { public: virtual ~ReporterRegistry() CATCH_OVERRIDE {} virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { FactoryMap::const_iterator it = m_factories.find( name ); if( it == m_factories.end() ) return CATCH_NULL; return it->second->create( ReporterConfig( config ) ); } void registerReporter( std::string const& name, Ptr const& factory ) { m_factories.insert( std::make_pair( name, factory ) ); } void registerListener( Ptr const& factory ) { m_listeners.push_back( factory ); } virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { return m_factories; } virtual Listeners const& getListeners() const CATCH_OVERRIDE { return m_listeners; } private: FactoryMap m_factories; Listeners m_listeners; }; } // #included from: catch_exception_translator_registry.hpp #define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED #ifdef __OBJC__ #import "Foundation/Foundation.h" #endif namespace Catch { class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { public: ~ExceptionTranslatorRegistry() { deleteAll( m_translators ); } virtual void registerTranslator( const IExceptionTranslator* translator ) { m_translators.push_back( translator ); } virtual std::string translateActiveException() const { try { #ifdef __OBJC__ // In Objective-C try objective-c exceptions first @try { return tryTranslators(); } @catch (NSException *exception) { return Catch::toString( [exception description] ); } #else return tryTranslators(); #endif } catch( TestFailureException& ) { throw; } catch( std::exception& ex ) { return ex.what(); } catch( std::string& msg ) { return msg; } catch( const char* msg ) { return msg; } catch(...) { return "Unknown exception"; } } std::string tryTranslators() const { if( m_translators.empty() ) throw; else return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); } private: std::vector m_translators; }; } // #included from: catch_tag_alias_registry.h #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED #include namespace Catch { class TagAliasRegistry : public ITagAliasRegistry { public: virtual ~TagAliasRegistry(); virtual Option find( std::string const& alias ) const; virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); private: std::map m_registry; }; } // end namespace Catch namespace Catch { namespace { class RegistryHub : public IRegistryHub, public IMutableRegistryHub { RegistryHub( RegistryHub const& ); void operator=( RegistryHub const& ); public: // IRegistryHub RegistryHub() { } virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { return m_reporterRegistry; } virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { return m_testCaseRegistry; } virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { return m_exceptionTranslatorRegistry; } virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE { return m_tagAliasRegistry; } public: // IMutableRegistryHub virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { m_reporterRegistry.registerReporter( name, factory ); } virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { m_reporterRegistry.registerListener( factory ); } virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { m_testCaseRegistry.registerTest( testInfo ); } virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { m_exceptionTranslatorRegistry.registerTranslator( translator ); } virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE { m_tagAliasRegistry.add( alias, tag, lineInfo ); } private: TestRegistry m_testCaseRegistry; ReporterRegistry m_reporterRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; TagAliasRegistry m_tagAliasRegistry; }; // Single, global, instance inline RegistryHub*& getTheRegistryHub() { static RegistryHub* theRegistryHub = CATCH_NULL; if( !theRegistryHub ) theRegistryHub = new RegistryHub(); return theRegistryHub; } } IRegistryHub& getRegistryHub() { return *getTheRegistryHub(); } IMutableRegistryHub& getMutableRegistryHub() { return *getTheRegistryHub(); } void cleanUp() { delete getTheRegistryHub(); getTheRegistryHub() = CATCH_NULL; cleanUpContext(); } std::string translateActiveException() { return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); } } // end namespace Catch // #included from: catch_notimplemented_exception.hpp #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED #include namespace Catch { NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) : m_lineInfo( lineInfo ) { std::ostringstream oss; oss << lineInfo << ": function "; oss << "not implemented"; m_what = oss.str(); } const char* NotImplementedException::what() const CATCH_NOEXCEPT { return m_what.c_str(); } } // end namespace Catch // #included from: catch_context_impl.hpp #define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED // #included from: catch_stream.hpp #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED #include #include #include namespace Catch { template class StreamBufImpl : public StreamBufBase { char data[bufferSize]; WriterF m_writer; public: StreamBufImpl() { setp( data, data + sizeof(data) ); } ~StreamBufImpl() CATCH_NOEXCEPT { sync(); } private: int overflow( int c ) { sync(); if( c != EOF ) { if( pbase() == epptr() ) m_writer( std::string( 1, static_cast( c ) ) ); else sputc( static_cast( c ) ); } return 0; } int sync() { if( pbase() != pptr() ) { m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); setp( pbase(), epptr() ); } return 0; } }; /////////////////////////////////////////////////////////////////////////// FileStream::FileStream( std::string const& filename ) { m_ofs.open( filename.c_str() ); if( m_ofs.fail() ) { std::ostringstream oss; oss << "Unable to open file: '" << filename << '\''; throw std::domain_error( oss.str() ); } } std::ostream& FileStream::stream() const { return m_ofs; } struct OutputDebugWriter { void operator()( std::string const&str ) { writeToDebugConsole( str ); } }; DebugOutStream::DebugOutStream() : m_streamBuf( new StreamBufImpl() ), m_os( m_streamBuf.get() ) {} std::ostream& DebugOutStream::stream() const { return m_os; } // Store the streambuf from cout up-front because // cout may get redirected when running tests CoutStream::CoutStream() : m_os( Catch::cout().rdbuf() ) {} std::ostream& CoutStream::stream() const { return m_os; } #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions std::ostream& cout() { return std::cout; } std::ostream& cerr() { return std::cerr; } #endif } namespace Catch { class Context : public IMutableContext { Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} Context( Context const& ); void operator=( Context const& ); public: virtual ~Context() { deleteAllValues( m_generatorsByTestName ); } public: // IContext virtual IResultCapture* getResultCapture() { return m_resultCapture; } virtual IRunner* getRunner() { return m_runner; } virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { return getGeneratorsForCurrentTest() .getGeneratorInfo( fileInfo, totalSize ) .getCurrentIndex(); } virtual bool advanceGeneratorsForCurrentTest() { IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); return generators && generators->moveNext(); } virtual Ptr getConfig() const { return m_config; } public: // IMutableContext virtual void setResultCapture( IResultCapture* resultCapture ) { m_resultCapture = resultCapture; } virtual void setRunner( IRunner* runner ) { m_runner = runner; } virtual void setConfig( Ptr const& config ) { m_config = config; } friend IMutableContext& getCurrentMutableContext(); private: IGeneratorsForTest* findGeneratorsForCurrentTest() { std::string testName = getResultCapture()->getCurrentTestName(); std::map::const_iterator it = m_generatorsByTestName.find( testName ); return it != m_generatorsByTestName.end() ? it->second : CATCH_NULL; } IGeneratorsForTest& getGeneratorsForCurrentTest() { IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); if( !generators ) { std::string testName = getResultCapture()->getCurrentTestName(); generators = createGeneratorsForTest(); m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); } return *generators; } private: Ptr m_config; IRunner* m_runner; IResultCapture* m_resultCapture; std::map m_generatorsByTestName; }; namespace { Context* currentContext = CATCH_NULL; } IMutableContext& getCurrentMutableContext() { if( !currentContext ) currentContext = new Context(); return *currentContext; } IContext& getCurrentContext() { return getCurrentMutableContext(); } void cleanUpContext() { delete currentContext; currentContext = CATCH_NULL; } } // #included from: catch_console_colour_impl.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED // #included from: catch_errno_guard.hpp #define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED #include namespace Catch { class ErrnoGuard { public: ErrnoGuard():m_oldErrno(errno){} ~ErrnoGuard() { errno = m_oldErrno; } private: int m_oldErrno; }; } namespace Catch { namespace { struct IColourImpl { virtual ~IColourImpl() {} virtual void use( Colour::Code _colourCode ) = 0; }; struct NoColourImpl : IColourImpl { void use( Colour::Code ) {} static IColourImpl* instance() { static NoColourImpl s_instance; return &s_instance; } }; } // anon namespace } // namespace Catch #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) # ifdef CATCH_PLATFORM_WINDOWS # define CATCH_CONFIG_COLOUR_WINDOWS # else # define CATCH_CONFIG_COLOUR_ANSI # endif #endif #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// namespace Catch { namespace { class Win32ColourImpl : public IColourImpl { public: Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) { CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } virtual void use( Colour::Code _colourCode ) { switch( _colourCode ) { case Colour::None: return setTextAttribute( originalForegroundAttributes ); case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::Red: return setTextAttribute( FOREGROUND_RED ); case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); case Colour::Grey: return setTextAttribute( 0 ); case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::Bright: throw std::logic_error( "not a colour" ); } } private: void setTextAttribute( WORD _textAttribute ) { SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); } HANDLE stdoutHandle; WORD originalForegroundAttributes; WORD originalBackgroundAttributes; }; IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; Ptr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) colourMode = !isDebuggerActive() ? UseColour::Yes : UseColour::No; return colourMode == UseColour::Yes ? &s_instance : NoColourImpl::instance(); } } // end anon namespace } // end namespace Catch #elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// #include namespace Catch { namespace { // use POSIX/ ANSI console terminal codes // Thanks to Adam Strzelecki for original contribution // (http://github.com/nanoant) // https://github.com/philsquared/Catch/pull/131 class PosixColourImpl : public IColourImpl { public: virtual void use( Colour::Code _colourCode ) { switch( _colourCode ) { case Colour::None: case Colour::White: return setColour( "[0m" ); case Colour::Red: return setColour( "[0;31m" ); case Colour::Green: return setColour( "[0;32m" ); case Colour::Blue: return setColour( "[0;34m" ); case Colour::Cyan: return setColour( "[0;36m" ); case Colour::Yellow: return setColour( "[0;33m" ); case Colour::Grey: return setColour( "[1;30m" ); case Colour::LightGrey: return setColour( "[0;37m" ); case Colour::BrightRed: return setColour( "[1;31m" ); case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); case Colour::Bright: throw std::logic_error( "not a colour" ); } } static IColourImpl* instance() { static PosixColourImpl s_instance; return &s_instance; } private: void setColour( const char* _escapeCode ) { Catch::cout() << '\033' << _escapeCode; } }; IColourImpl* platformColourInstance() { ErrnoGuard guard; Ptr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) ? UseColour::Yes : UseColour::No; return colourMode == UseColour::Yes ? PosixColourImpl::instance() : NoColourImpl::instance(); } } // end anon namespace } // end namespace Catch #else // not Windows or ANSI /////////////////////////////////////////////// namespace Catch { static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } } // end namespace Catch #endif // Windows/ ANSI/ None namespace Catch { Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } Colour::~Colour(){ if( !m_moved ) use( None ); } void Colour::use( Code _colourCode ) { static IColourImpl* impl = platformColourInstance(); impl->use( _colourCode ); } } // end namespace Catch // #included from: catch_generators_impl.hpp #define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED #include #include #include namespace Catch { struct GeneratorInfo : IGeneratorInfo { GeneratorInfo( std::size_t size ) : m_size( size ), m_currentIndex( 0 ) {} bool moveNext() { if( ++m_currentIndex == m_size ) { m_currentIndex = 0; return false; } return true; } std::size_t getCurrentIndex() const { return m_currentIndex; } std::size_t m_size; std::size_t m_currentIndex; }; /////////////////////////////////////////////////////////////////////////// class GeneratorsForTest : public IGeneratorsForTest { public: ~GeneratorsForTest() { deleteAll( m_generatorsInOrder ); } IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { std::map::const_iterator it = m_generatorsByName.find( fileInfo ); if( it == m_generatorsByName.end() ) { IGeneratorInfo* info = new GeneratorInfo( size ); m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); m_generatorsInOrder.push_back( info ); return *info; } return *it->second; } bool moveNext() { std::vector::const_iterator it = m_generatorsInOrder.begin(); std::vector::const_iterator itEnd = m_generatorsInOrder.end(); for(; it != itEnd; ++it ) { if( (*it)->moveNext() ) return true; } return false; } private: std::map m_generatorsByName; std::vector m_generatorsInOrder; }; IGeneratorsForTest* createGeneratorsForTest() { return new GeneratorsForTest(); } } // end namespace Catch // #included from: catch_assertionresult.hpp #define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED namespace Catch { AssertionInfo::AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, char const * _capturedExpression, ResultDisposition::Flags _resultDisposition, char const * _secondArg) : macroName( _macroName ), lineInfo( _lineInfo ), capturedExpression( _capturedExpression ), resultDisposition( _resultDisposition ), secondArg( _secondArg ) {} AssertionResult::AssertionResult() {} AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) : m_info( info ), m_resultData( data ) {} AssertionResult::~AssertionResult() {} // Result was a success bool AssertionResult::succeeded() const { return Catch::isOk( m_resultData.resultType ); } // Result was a success, or failure is suppressed bool AssertionResult::isOk() const { return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); } ResultWas::OfType AssertionResult::getResultType() const { return m_resultData.resultType; } bool AssertionResult::hasExpression() const { return m_info.capturedExpression[0] != 0; } bool AssertionResult::hasMessage() const { return !m_resultData.message.empty(); } std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) { return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"') ? capturedExpression : std::string(capturedExpression) + ", " + secondArg; } std::string AssertionResult::getExpression() const { if( isFalseTest( m_info.resultDisposition ) ) return '!' + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); else return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); } std::string AssertionResult::getExpressionInMacro() const { if( m_info.macroName[0] == 0 ) return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); else return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )"; } bool AssertionResult::hasExpandedExpression() const { return hasExpression() && getExpandedExpression() != getExpression(); } std::string AssertionResult::getExpandedExpression() const { return m_resultData.reconstructExpression(); } std::string AssertionResult::getMessage() const { return m_resultData.message; } SourceLineInfo AssertionResult::getSourceInfo() const { return m_info.lineInfo; } std::string AssertionResult::getTestMacroName() const { return m_info.macroName; } void AssertionResult::discardDecomposedExpression() const { m_resultData.decomposedExpression = CATCH_NULL; } void AssertionResult::expandDecomposedExpression() const { m_resultData.reconstructExpression(); } } // end namespace Catch // #included from: catch_test_case_info.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED #include namespace Catch { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { if( startsWith( tag, '.' ) || tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; else if( tag == "!throws" ) return TestCaseInfo::Throws; else if( tag == "!shouldfail" ) return TestCaseInfo::ShouldFail; else if( tag == "!mayfail" ) return TestCaseInfo::MayFail; else if( tag == "!nonportable" ) return TestCaseInfo::NonPortable; else return TestCaseInfo::None; } inline bool isReservedTag( std::string const& tag ) { return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); } inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { if( isReservedTag( tag ) ) { std::ostringstream ss; ss << Colour(Colour::Red) << "Tag name [" << tag << "] not allowed.\n" << "Tag names starting with non alpha-numeric characters are reserved\n" << Colour(Colour::FileName) << _lineInfo << '\n'; throw std::runtime_error(ss.str()); } } TestCase makeTestCase( ITestCase* _testCase, std::string const& _className, std::string const& _name, std::string const& _descOrTags, SourceLineInfo const& _lineInfo ) { bool isHidden( startsWith( _name, "./" ) ); // Legacy support // Parse out tags std::set tags; std::string desc, tag; bool inTag = false; for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { char c = _descOrTags[i]; if( !inTag ) { if( c == '[' ) inTag = true; else desc += c; } else { if( c == ']' ) { TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); if( prop == TestCaseInfo::IsHidden ) isHidden = true; else if( prop == TestCaseInfo::None ) enforceNotReservedTag( tag, _lineInfo ); tags.insert( tag ); tag.clear(); inTag = false; } else tag += c; } } if( isHidden ) { tags.insert( "hide" ); tags.insert( "." ); } TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); return TestCase( _testCase, info ); } void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) { testCaseInfo.tags = tags; testCaseInfo.lcaseTags.clear(); std::ostringstream oss; for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { oss << '[' << *it << ']'; std::string lcaseTag = toLower( *it ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); testCaseInfo.lcaseTags.insert( lcaseTag ); } testCaseInfo.tagsAsString = oss.str(); } TestCaseInfo::TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ) : name( _name ), className( _className ), description( _description ), lineInfo( _lineInfo ), properties( None ) { setTags( *this, _tags ); } TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) : name( other.name ), className( other.className ), description( other.description ), tags( other.tags ), lcaseTags( other.lcaseTags ), tagsAsString( other.tagsAsString ), lineInfo( other.lineInfo ), properties( other.properties ) {} bool TestCaseInfo::isHidden() const { return ( properties & IsHidden ) != 0; } bool TestCaseInfo::throws() const { return ( properties & Throws ) != 0; } bool TestCaseInfo::okToFail() const { return ( properties & (ShouldFail | MayFail ) ) != 0; } bool TestCaseInfo::expectedToFail() const { return ( properties & (ShouldFail ) ) != 0; } TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} TestCase::TestCase( TestCase const& other ) : TestCaseInfo( other ), test( other.test ) {} TestCase TestCase::withName( std::string const& _newName ) const { TestCase other( *this ); other.name = _newName; return other; } void TestCase::swap( TestCase& other ) { test.swap( other.test ); name.swap( other.name ); className.swap( other.className ); description.swap( other.description ); tags.swap( other.tags ); lcaseTags.swap( other.lcaseTags ); tagsAsString.swap( other.tagsAsString ); std::swap( TestCaseInfo::properties, static_cast( other ).properties ); std::swap( lineInfo, other.lineInfo ); } void TestCase::invoke() const { test->invoke(); } bool TestCase::operator == ( TestCase const& other ) const { return test.get() == other.test.get() && name == other.name && className == other.className; } bool TestCase::operator < ( TestCase const& other ) const { return name < other.name; } TestCase& TestCase::operator = ( TestCase const& other ) { TestCase temp( other ); swap( temp ); return *this; } TestCaseInfo const& TestCase::getTestCaseInfo() const { return *this; } } // end namespace Catch // #included from: catch_version.hpp #define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED namespace Catch { Version::Version ( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, char const * const _branchName, unsigned int _buildNumber ) : majorVersion( _majorVersion ), minorVersion( _minorVersion ), patchNumber( _patchNumber ), branchName( _branchName ), buildNumber( _buildNumber ) {} std::ostream& operator << ( std::ostream& os, Version const& version ) { os << version.majorVersion << '.' << version.minorVersion << '.' << version.patchNumber; // branchName is never null -> 0th char is \0 if it is empty if (version.branchName[0]) { os << '-' << version.branchName << '.' << version.buildNumber; } return os; } inline Version libraryVersion() { static Version version( 1, 9, 6, "", 0 ); return version; } } // #included from: catch_message.hpp #define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED namespace Catch { MessageInfo::MessageInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ) : macroName( _macroName ), lineInfo( _lineInfo ), type( _type ), sequence( ++globalCount ) {} // This may need protecting if threading support is added unsigned int MessageInfo::globalCount = 0; //////////////////////////////////////////////////////////////////////////// ScopedMessage::ScopedMessage( MessageBuilder const& builder ) : m_info( builder.m_info ) { m_info.message = builder.m_stream.str(); getResultCapture().pushScopedMessage( m_info ); } ScopedMessage::ScopedMessage( ScopedMessage const& other ) : m_info( other.m_info ) {} ScopedMessage::~ScopedMessage() { if ( !std::uncaught_exception() ){ getResultCapture().popScopedMessage(m_info); } } } // end namespace Catch // #included from: catch_legacy_reporter_adapter.hpp #define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED // #included from: catch_legacy_reporter_adapter.h #define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED namespace Catch { // Deprecated struct IReporter : IShared { virtual ~IReporter(); virtual bool shouldRedirectStdout() const = 0; virtual void StartTesting() = 0; virtual void EndTesting( Totals const& totals ) = 0; virtual void StartGroup( std::string const& groupName ) = 0; virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; virtual void Aborted() = 0; virtual void Result( AssertionResult const& result ) = 0; }; class LegacyReporterAdapter : public SharedImpl { public: LegacyReporterAdapter( Ptr const& legacyReporter ); virtual ~LegacyReporterAdapter(); virtual ReporterPreferences getPreferences() const; virtual void noMatchingTestCases( std::string const& ); virtual void testRunStarting( TestRunInfo const& ); virtual void testGroupStarting( GroupInfo const& groupInfo ); virtual void testCaseStarting( TestCaseInfo const& testInfo ); virtual void sectionStarting( SectionInfo const& sectionInfo ); virtual void assertionStarting( AssertionInfo const& ); virtual bool assertionEnded( AssertionStats const& assertionStats ); virtual void sectionEnded( SectionStats const& sectionStats ); virtual void testCaseEnded( TestCaseStats const& testCaseStats ); virtual void testGroupEnded( TestGroupStats const& testGroupStats ); virtual void testRunEnded( TestRunStats const& testRunStats ); virtual void skipTest( TestCaseInfo const& ); private: Ptr m_legacyReporter; }; } namespace Catch { LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) : m_legacyReporter( legacyReporter ) {} LegacyReporterAdapter::~LegacyReporterAdapter() {} ReporterPreferences LegacyReporterAdapter::getPreferences() const { ReporterPreferences prefs; prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); return prefs; } void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { m_legacyReporter->StartTesting(); } void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { m_legacyReporter->StartGroup( groupInfo.name ); } void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { m_legacyReporter->StartTestCase( testInfo ); } void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); } void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { // Not on legacy interface } bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); it != itEnd; ++it ) { if( it->type == ResultWas::Info ) { ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); rb << it->message; rb.setResultType( ResultWas::Info ); AssertionResult result = rb.build(); m_legacyReporter->Result( result ); } } } m_legacyReporter->Result( assertionStats.assertionResult ); return true; } void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { if( sectionStats.missingAssertions ) m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); } void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { m_legacyReporter->EndTestCase ( testCaseStats.testInfo, testCaseStats.totals, testCaseStats.stdOut, testCaseStats.stdErr ); } void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { if( testGroupStats.aborting ) m_legacyReporter->Aborted(); m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); } void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { m_legacyReporter->EndTesting( testRunStats.totals ); } void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { } } // #included from: catch_timer.hpp #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wc++11-long-long" #endif #ifdef CATCH_PLATFORM_WINDOWS #else #include #endif namespace Catch { namespace { #ifdef CATCH_PLATFORM_WINDOWS UInt64 getCurrentTicks() { static UInt64 hz=0, hzo=0; if (!hz) { QueryPerformanceFrequency( reinterpret_cast( &hz ) ); QueryPerformanceCounter( reinterpret_cast( &hzo ) ); } UInt64 t; QueryPerformanceCounter( reinterpret_cast( &t ) ); return ((t-hzo)*1000000)/hz; } #else UInt64 getCurrentTicks() { timeval t; gettimeofday(&t,CATCH_NULL); return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); } #endif } void Timer::start() { m_ticks = getCurrentTicks(); } unsigned int Timer::getElapsedMicroseconds() const { return static_cast(getCurrentTicks() - m_ticks); } unsigned int Timer::getElapsedMilliseconds() const { return static_cast(getElapsedMicroseconds()/1000); } double Timer::getElapsedSeconds() const { return getElapsedMicroseconds()/1000000.0; } } // namespace Catch #ifdef __clang__ # pragma clang diagnostic pop #endif // #included from: catch_common.hpp #define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED #include #include namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); } bool startsWith( std::string const& s, char prefix ) { return !s.empty() && s[0] == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); } bool endsWith( std::string const& s, char suffix ) { return !s.empty() && s[s.size()-1] == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } char toLowerCh(char c) { return static_cast( std::tolower( c ) ); } void toLowerInPlace( std::string& s ) { std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); } std::string toLower( std::string const& s ) { std::string lc = s; toLowerInPlace( lc ); return lc; } std::string trim( std::string const& str ) { static char const* whitespaceChars = "\n\r\t "; std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { bool replaced = false; std::size_t i = str.find( replaceThis ); while( i != std::string::npos ) { replaced = true; str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); if( i < str.size()-withThis.size() ) i = str.find( replaceThis, i+withThis.size() ); else i = std::string::npos; } return replaced; } pluralise::pluralise( std::size_t count, std::string const& label ) : m_count( count ), m_label( label ) {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { os << pluraliser.m_count << ' ' << pluraliser.m_label; if( pluraliser.m_count != 1 ) os << 's'; return os; } SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) : file( _file ), line( _line ) {} bool SourceLineInfo::empty() const { return file[0] == '\0'; } bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); } bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); } void seedRng( IConfig const& config ) { if( config.rngSeed() != 0 ) srand( config.rngSeed() ); } unsigned int rngSeed() { return getCurrentContext().getConfig()->rngSeed(); } std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ os << info.file << '(' << info.line << ')'; #else os << info.file << ':' << info.line; #endif return os; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { std::ostringstream oss; oss << locationInfo << ": Internal Catch error: '" << message << '\''; if( alwaysTrue() ) throw std::logic_error( oss.str() ); } } // #included from: catch_section.hpp #define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED namespace Catch { SectionInfo::SectionInfo ( SourceLineInfo const& _lineInfo, std::string const& _name, std::string const& _description ) : name( _name ), description( _description ), lineInfo( _lineInfo ) {} Section::Section( SectionInfo const& info ) : m_info( info ), m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) { m_timer.start(); } #if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 #endif Section::~Section() { if( m_sectionIncluded ) { SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); if( std::uncaught_exception() ) getResultCapture().sectionEndedEarly( endInfo ); else getResultCapture().sectionEnded( endInfo ); } } #if defined(_MSC_VER) # pragma warning(pop) #endif // This indicates whether the section should be executed or not Section::operator bool() const { return m_sectionIncluded; } } // end namespace Catch // #included from: catch_debugger.hpp #define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED #ifdef CATCH_PLATFORM_MAC #include #include #include #include #include namespace Catch{ // The following function is taken directly from the following technical note: // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html // Returns true if the current process is being debugged (either // running under the debugger or has a debugger attached post facto). bool isDebuggerActive(){ int mib[4]; struct kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); // Call sysctl. size = sizeof(info); if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; return false; } // We're being debugged if the P_TRACED flag is set. return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); } } // namespace Catch #elif defined(CATCH_PLATFORM_LINUX) #include #include namespace Catch{ // The standard POSIX way of detecting a debugger is to attempt to // ptrace() the process, but this needs to be done from a child and not // this process itself to still allow attaching to this process later // if wanted, so is rather heavy. Under Linux we have the PID of the // "debugger" (which doesn't need to be gdb, of course, it could also // be strace, for example) in /proc/$PID/status, so just get it from // there instead. bool isDebuggerActive(){ // Libstdc++ has a bug, where std::ifstream sets errno to 0 // This way our users can properly assert over errno values ErrnoGuard guard; std::ifstream in("/proc/self/status"); for( std::string line; std::getline(in, line); ) { static const int PREFIX_LEN = 11; if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { // We're traced if the PID is not 0 and no other PID starts // with 0 digit, so it's enough to check for just a single // character. return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; } } return false; } } // namespace Catch #elif defined(_MSC_VER) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { bool isDebuggerActive() { return IsDebuggerPresent() != 0; } } #elif defined(__MINGW32__) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { bool isDebuggerActive() { return IsDebuggerPresent() != 0; } } #else namespace Catch { inline bool isDebuggerActive() { return false; } } #endif // Platform #ifdef CATCH_PLATFORM_WINDOWS namespace Catch { void writeToDebugConsole( std::string const& text ) { ::OutputDebugStringA( text.c_str() ); } } #else namespace Catch { void writeToDebugConsole( std::string const& text ) { // !TBD: Need a version for Mac/ XCode and other IDEs Catch::cout() << text; } } #endif // Platform // #included from: catch_tostring.hpp #define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED namespace Catch { namespace Detail { const std::string unprintableString = "{?}"; namespace { const int hexThreshold = 255; struct Endianness { enum Arch { Big, Little }; static Arch which() { union _{ int asInt; char asChar[sizeof (int)]; } u; u.asInt = 1; return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; } }; } std::string rawMemoryToString( const void *object, std::size_t size ) { // Reverse order for little endian architectures int i = 0, end = static_cast( size ), inc = 1; if( Endianness::which() == Endianness::Little ) { i = end-1; end = inc = -1; } unsigned char const *bytes = static_cast(object); std::ostringstream os; os << "0x" << std::setfill('0') << std::hex; for( ; i != end; i += inc ) os << std::setw(2) << static_cast(bytes[i]); return os.str(); } } std::string toString( std::string const& value ) { std::string s = value; if( getCurrentContext().getConfig()->showInvisibles() ) { for(size_t i = 0; i < s.size(); ++i ) { std::string subs; switch( s[i] ) { case '\n': subs = "\\n"; break; case '\t': subs = "\\t"; break; default: break; } if( !subs.empty() ) { s = s.substr( 0, i ) + subs + s.substr( i+1 ); ++i; } } } return '"' + s + '"'; } std::string toString( std::wstring const& value ) { std::string s; s.reserve( value.size() ); for(size_t i = 0; i < value.size(); ++i ) s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; return Catch::toString( s ); } std::string toString( const char* const value ) { return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); } std::string toString( char* const value ) { return Catch::toString( static_cast( value ) ); } std::string toString( const wchar_t* const value ) { return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); } std::string toString( wchar_t* const value ) { return Catch::toString( static_cast( value ) ); } std::string toString( int value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ')'; return oss.str(); } std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ')'; return oss.str(); } std::string toString( unsigned int value ) { return Catch::toString( static_cast( value ) ); } template std::string fpToString( T value, int precision ) { std::ostringstream oss; oss << std::setprecision( precision ) << std::fixed << value; std::string d = oss.str(); std::size_t i = d.find_last_not_of( '0' ); if( i != std::string::npos && i != d.size()-1 ) { if( d[i] == '.' ) i++; d = d.substr( 0, i+1 ); } return d; } std::string toString( const double value ) { return fpToString( value, 10 ); } std::string toString( const float value ) { return fpToString( value, 5 ) + 'f'; } std::string toString( bool value ) { return value ? "true" : "false"; } std::string toString( char value ) { if ( value == '\r' ) return "'\\r'"; if ( value == '\f' ) return "'\\f'"; if ( value == '\n' ) return "'\\n'"; if ( value == '\t' ) return "'\\t'"; if ( '\0' <= value && value < ' ' ) return toString( static_cast( value ) ); char chstr[] = "' '"; chstr[1] = value; return chstr; } std::string toString( signed char value ) { return toString( static_cast( value ) ); } std::string toString( unsigned char value ) { return toString( static_cast( value ) ); } #ifdef CATCH_CONFIG_CPP11_LONG_LONG std::string toString( long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ')'; return oss.str(); } std::string toString( unsigned long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ')'; return oss.str(); } #endif #ifdef CATCH_CONFIG_CPP11_NULLPTR std::string toString( std::nullptr_t ) { return "nullptr"; } #endif #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); } std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); } std::string toString( NSObject* const& nsObject ) { return toString( [nsObject description] ); } #endif } // end namespace Catch // #included from: catch_result_builder.hpp #define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED namespace Catch { ResultBuilder::ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg ) : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ), m_shouldDebugBreak( false ), m_shouldThrow( false ), m_guardException( false ) { m_stream().oss.str(""); } ResultBuilder::~ResultBuilder() { #if defined(CATCH_CONFIG_FAST_COMPILE) if ( m_guardException ) { m_stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; captureResult( ResultWas::ThrewException ); getCurrentContext().getResultCapture()->exceptionEarlyReported(); } #endif } ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { m_data.resultType = result; return *this; } ResultBuilder& ResultBuilder::setResultType( bool result ) { m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; return *this; } void ResultBuilder::endExpression( DecomposedExpression const& expr ) { AssertionResult result = build( expr ); handleResult( result ); } void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { m_assertionInfo.resultDisposition = resultDisposition; m_stream().oss << Catch::translateActiveException(); captureResult( ResultWas::ThrewException ); } void ResultBuilder::captureResult( ResultWas::OfType resultType ) { setResultType( resultType ); captureExpression(); } void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { if( expectedMessage.empty() ) captureExpectedException( Matchers::Impl::MatchAllOf() ); else captureExpectedException( Matchers::Equals( expectedMessage ) ); } void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) { assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); AssertionResultData data = m_data; data.resultType = ResultWas::Ok; data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); std::string actualMessage = Catch::translateActiveException(); if( !matcher.match( actualMessage ) ) { data.resultType = ResultWas::ExpressionFailed; data.reconstructedExpression = actualMessage; } AssertionResult result( m_assertionInfo, data ); handleResult( result ); } void ResultBuilder::captureExpression() { AssertionResult result = build(); handleResult( result ); } void ResultBuilder::handleResult( AssertionResult const& result ) { getResultCapture().assertionEnded( result ); if( !result.isOk() ) { if( getCurrentContext().getConfig()->shouldDebugBreak() ) m_shouldDebugBreak = true; if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) m_shouldThrow = true; } } void ResultBuilder::react() { #if defined(CATCH_CONFIG_FAST_COMPILE) if (m_shouldDebugBreak) { /////////////////////////////////////////////////////////////////// // To inspect the state during test, you need to go one level up the callstack // To go back to the test and change execution, jump over the throw statement /////////////////////////////////////////////////////////////////// CATCH_BREAK_INTO_DEBUGGER(); } #endif if( m_shouldThrow ) throw Catch::TestFailureException(); } bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } AssertionResult ResultBuilder::build() const { return build( *this ); } // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, // a temporary DecomposedExpression, which in turn holds references to // operands, possibly temporary as well. // It should immediately be passed to handleResult; if the expression // needs to be reported, its string expansion must be composed before // the temporaries are destroyed. AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const { assert( m_data.resultType != ResultWas::Unknown ); AssertionResultData data = m_data; // Flip bool results if FalseTest flag is set if( isFalseTest( m_assertionInfo.resultDisposition ) ) { data.negate( expr.isBinaryExpression() ); } data.message = m_stream().oss.str(); data.decomposedExpression = &expr; // for lazy reconstruction return AssertionResult( m_assertionInfo, data ); } void ResultBuilder::reconstructExpression( std::string& dest ) const { dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); } void ResultBuilder::setExceptionGuard() { m_guardException = true; } void ResultBuilder::unsetExceptionGuard() { m_guardException = false; } } // end namespace Catch // #included from: catch_tag_alias_registry.hpp #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED namespace Catch { TagAliasRegistry::~TagAliasRegistry() {} Option TagAliasRegistry::find( std::string const& alias ) const { std::map::const_iterator it = m_registry.find( alias ); if( it != m_registry.end() ) return it->second; else return Option(); } std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { std::string expandedTestSpec = unexpandedTestSpec; for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); it != itEnd; ++it ) { std::size_t pos = expandedTestSpec.find( it->first ); if( pos != std::string::npos ) { expandedTestSpec = expandedTestSpec.substr( 0, pos ) + it->second.tag + expandedTestSpec.substr( pos + it->first.size() ); } } return expandedTestSpec; } void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { std::ostringstream oss; oss << Colour( Colour::Red ) << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << Colour( Colour::FileName ) << lineInfo << '\n'; throw std::domain_error( oss.str().c_str() ); } if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { std::ostringstream oss; oss << Colour( Colour::Red ) << "error: tag alias, \"" << alias << "\" already registered.\n" << "\tFirst seen at " << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' << Colour( Colour::Red ) << "\tRedefined at " << Colour( Colour::FileName) << lineInfo << '\n'; throw std::domain_error( oss.str().c_str() ); } } ITagAliasRegistry::~ITagAliasRegistry() {} ITagAliasRegistry const& ITagAliasRegistry::get() { return getRegistryHub().getTagAliasRegistry(); } RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo ); } } // end namespace Catch // #included from: catch_matchers_string.hpp namespace Catch { namespace Matchers { namespace StdString { CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) : m_caseSensitivity( caseSensitivity ), m_str( adjustString( str ) ) {} std::string CasedString::adjustString( std::string const& str ) const { return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; } std::string CasedString::caseSensitivitySuffix() const { return m_caseSensitivity == CaseSensitive::No ? " (case insensitive)" : std::string(); } StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) : m_comparator( comparator ), m_operation( operation ) { } std::string StringMatcherBase::describe() const { std::string description; description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + m_comparator.caseSensitivitySuffix().size()); description += m_operation; description += ": \""; description += m_comparator.m_str; description += "\""; description += m_comparator.caseSensitivitySuffix(); return description; } EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} bool EqualsMatcher::match( std::string const& source ) const { return m_comparator.adjustString( source ) == m_comparator.m_str; } ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} bool ContainsMatcher::match( std::string const& source ) const { return contains( m_comparator.adjustString( source ), m_comparator.m_str ); } StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} bool StartsWithMatcher::match( std::string const& source ) const { return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); } EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} bool EndsWithMatcher::match( std::string const& source ) const { return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); } } // namespace StdString StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); } StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); } StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); } StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); } } // namespace Matchers } // namespace Catch // #included from: ../reporters/catch_reporter_multi.hpp #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED namespace Catch { class MultipleReporters : public SharedImpl { typedef std::vector > Reporters; Reporters m_reporters; public: void add( Ptr const& reporter ) { m_reporters.push_back( reporter ); } public: // IStreamingReporter virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporters[0]->getPreferences(); } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->noMatchingTestCases( spec ); } virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testRunStarting( testRunInfo ); } virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testGroupStarting( groupInfo ); } virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testCaseStarting( testInfo ); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->sectionStarting( sectionInfo ); } virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->assertionStarting( assertionInfo ); } // The return value indicates if the messages buffer should be cleared: virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { bool clearBuffer = false; for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) clearBuffer |= (*it)->assertionEnded( assertionStats ); return clearBuffer; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->sectionEnded( sectionStats ); } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testCaseEnded( testCaseStats ); } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testGroupEnded( testGroupStats ); } virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testRunEnded( testRunStats ); } virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->skipTest( testInfo ); } virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { return this; } }; Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { Ptr resultingReporter; if( existingReporter ) { MultipleReporters* multi = existingReporter->tryAsMulti(); if( !multi ) { multi = new MultipleReporters; resultingReporter = Ptr( multi ); if( existingReporter ) multi->add( existingReporter ); } else resultingReporter = existingReporter; multi->add( additionalReporter ); } else resultingReporter = additionalReporter; return resultingReporter; } } // end namespace Catch // #included from: ../reporters/catch_reporter_xml.hpp #define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED // #included from: catch_reporter_bases.hpp #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #include #include #include #include namespace Catch { namespace { // Because formatting using c++ streams is stateful, drop down to C is required // Alternatively we could use stringstream, but its performance is... not good. std::string getFormattedDuration( double duration ) { // Max exponent + 1 is required to represent the whole part // + 1 for decimal point // + 3 for the 3 decimal places // + 1 for null terminator const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; char buffer[maxDoubleSize]; // Save previous errno, to prevent sprintf from overwriting it ErrnoGuard guard; #ifdef _MSC_VER sprintf_s(buffer, "%.3f", duration); #else sprintf(buffer, "%.3f", duration); #endif return std::string(buffer); } } struct StreamingReporterBase : SharedImpl { StreamingReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; } virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporterPrefs; } virtual ~StreamingReporterBase() CATCH_OVERRIDE; virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { currentTestRunInfo = _testRunInfo; } virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { currentGroupInfo = _groupInfo; } virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { currentTestCaseInfo = _testInfo; } virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { m_sectionStack.push_back( _sectionInfo ); } virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { m_sectionStack.pop_back(); } virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { currentTestCaseInfo.reset(); } virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { currentGroupInfo.reset(); } virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { currentTestCaseInfo.reset(); currentGroupInfo.reset(); currentTestRunInfo.reset(); } virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { // Don't do anything with this by default. // It can optionally be overridden in the derived class. } Ptr m_config; std::ostream& stream; LazyStat currentTestRunInfo; LazyStat currentGroupInfo; LazyStat currentTestCaseInfo; std::vector m_sectionStack; ReporterPreferences m_reporterPrefs; }; struct CumulativeReporterBase : SharedImpl { template struct Node : SharedImpl<> { explicit Node( T const& _value ) : value( _value ) {} virtual ~Node() {} typedef std::vector > ChildNodes; T value; ChildNodes children; }; struct SectionNode : SharedImpl<> { explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} virtual ~SectionNode(); bool operator == ( SectionNode const& other ) const { return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; } bool operator == ( Ptr const& other ) const { return operator==( *other ); } SectionStats stats; typedef std::vector > ChildSections; typedef std::vector Assertions; ChildSections childSections; Assertions assertions; std::string stdOut; std::string stdErr; }; struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() ( Ptr const& node ) const { return node->stats.sectionInfo.lineInfo == m_other.lineInfo; } private: void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; typedef Node TestCaseNode; typedef Node TestGroupNode; typedef Node TestRunNode; CumulativeReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; } ~CumulativeReporterBase(); virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporterPrefs; } virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); Ptr node; if( m_sectionStack.empty() ) { if( !m_rootSection ) m_rootSection = new SectionNode( incompleteStats ); node = m_rootSection; } else { SectionNode& parentNode = *m_sectionStack.back(); SectionNode::ChildSections::const_iterator it = std::find_if( parentNode.childSections.begin(), parentNode.childSections.end(), BySectionInfo( sectionInfo ) ); if( it == parentNode.childSections.end() ) { node = new SectionNode( incompleteStats ); parentNode.childSections.push_back( node ); } else node = *it; } m_sectionStack.push_back( node ); m_deepestSection = node; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); // AssertionResult holds a pointer to a temporary DecomposedExpression, // which getExpandedExpression() calls to build the expression string. // Our section stack copy of the assertionResult will likely outlive the // temporary, so it must be expanded or discarded now to avoid calling // a destroyed object later. prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { assert( !m_sectionStack.empty() ); SectionNode& node = *m_sectionStack.back(); node.stats = sectionStats; m_sectionStack.pop_back(); } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { Ptr node = new TestCaseNode( testCaseStats ); assert( m_sectionStack.size() == 0 ); node->children.push_back( m_rootSection ); m_testCases.push_back( node ); m_rootSection.reset(); assert( m_deepestSection ); m_deepestSection->stdOut = testCaseStats.stdOut; m_deepestSection->stdErr = testCaseStats.stdErr; } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { Ptr node = new TestGroupNode( testGroupStats ); node->children.swap( m_testCases ); m_testGroups.push_back( node ); } virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { Ptr node = new TestRunNode( testRunStats ); node->children.swap( m_testGroups ); m_testRuns.push_back( node ); testRunEndedCumulative(); } virtual void testRunEndedCumulative() = 0; virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} virtual void prepareExpandedExpression( AssertionResult& result ) const { if( result.isOk() ) result.discardDecomposedExpression(); else result.expandDecomposedExpression(); } Ptr m_config; std::ostream& stream; std::vector m_assertions; std::vector > > m_sections; std::vector > m_testCases; std::vector > m_testGroups; std::vector > m_testRuns; Ptr m_rootSection; Ptr m_deepestSection; std::vector > m_sectionStack; ReporterPreferences m_reporterPrefs; }; template char const* getLineOfChars() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; } return line; } struct TestEventListenerBase : StreamingReporterBase { TestEventListenerBase( ReporterConfig const& _config ) : StreamingReporterBase( _config ) {} virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { return false; } }; } // end namespace Catch // #included from: ../internal/catch_reporter_registrars.hpp #define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED namespace Catch { template class LegacyReporterRegistrar { class ReporterFactory : public IReporterFactory { virtual IStreamingReporter* create( ReporterConfig const& config ) const { return new LegacyReporterAdapter( new T( config ) ); } virtual std::string getDescription() const { return T::getDescription(); } }; public: LegacyReporterRegistrar( std::string const& name ) { getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); } }; template class ReporterRegistrar { class ReporterFactory : public SharedImpl { // *** Please Note ***: // - If you end up here looking at a compiler error because it's trying to register // your custom reporter class be aware that the native reporter interface has changed // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. // However please consider updating to the new interface as the old one is now // deprecated and will probably be removed quite soon! // Please contact me via github if you have any questions at all about this. // In fact, ideally, please contact me anyway to let me know you've hit this - as I have // no idea who is actually using custom reporters at all (possibly no-one!). // The new interface is designed to minimise exposure to interface changes in the future. virtual IStreamingReporter* create( ReporterConfig const& config ) const { return new T( config ); } virtual std::string getDescription() const { return T::getDescription(); } }; public: ReporterRegistrar( std::string const& name ) { getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); } }; template class ListenerRegistrar { class ListenerFactory : public SharedImpl { virtual IStreamingReporter* create( ReporterConfig const& config ) const { return new T( config ); } virtual std::string getDescription() const { return std::string(); } }; public: ListenerRegistrar() { getMutableRegistryHub().registerListener( new ListenerFactory() ); } }; } #define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } // Deprecated - use the form without INTERNAL_ #define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } #define CATCH_REGISTER_LISTENER( listenerType ) \ namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } // #included from: ../internal/catch_xmlwriter.hpp #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED #include #include #include #include namespace Catch { class XmlEncode { public: enum ForWhat { ForTextNodes, ForAttributes }; XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) : m_str( str ), m_forWhat( forWhat ) {} void encodeTo( std::ostream& os ) const { // Apostrophe escaping not necessary if we always use " to write attributes // (see: http://www.w3.org/TR/xml/#syntax) for( std::size_t i = 0; i < m_str.size(); ++ i ) { char c = m_str[i]; switch( c ) { case '<': os << "<"; break; case '&': os << "&"; break; case '>': // See: http://www.w3.org/TR/xml/#syntax if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) os << ">"; else os << c; break; case '\"': if( m_forWhat == ForAttributes ) os << """; else os << c; break; default: // Escape control chars - based on contribution by @espenalb in PR #465 and // by @mrpi PR #588 if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ); } else os << c; } } } friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { xmlEncode.encodeTo( os ); return os; } private: std::string m_str; ForWhat m_forWhat; }; class XmlWriter { public: class ScopedElement { public: ScopedElement( XmlWriter* writer ) : m_writer( writer ) {} ScopedElement( ScopedElement const& other ) : m_writer( other.m_writer ){ other.m_writer = CATCH_NULL; } ~ScopedElement() { if( m_writer ) m_writer->endElement(); } ScopedElement& writeText( std::string const& text, bool indent = true ) { m_writer->writeText( text, indent ); return *this; } template ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { m_writer->writeAttribute( name, attribute ); return *this; } private: mutable XmlWriter* m_writer; }; XmlWriter() : m_tagIsOpen( false ), m_needsNewline( false ), m_os( Catch::cout() ) { writeDeclaration(); } XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), m_os( os ) { writeDeclaration(); } ~XmlWriter() { while( !m_tags.empty() ) endElement(); } XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); m_os << m_indent << '<' << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; return *this; } ScopedElement scopedElement( std::string const& name ) { ScopedElement scoped( this ); startElement( name ); return scoped; } XmlWriter& endElement() { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { m_os << "/>"; m_tagIsOpen = false; } else { m_os << m_indent << ""; } m_os << std::endl; m_tags.pop_back(); return *this; } XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& writeAttribute( std::string const& name, bool attribute ) { m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; return *this; } template XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { std::ostringstream oss; oss << attribute; return writeAttribute( name, oss.str() ); } XmlWriter& writeText( std::string const& text, bool indent = true ) { if( !text.empty() ){ bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) m_os << m_indent; m_os << XmlEncode( text ); m_needsNewline = true; } return *this; } XmlWriter& writeComment( std::string const& text ) { ensureTagClosed(); m_os << m_indent << ""; m_needsNewline = true; return *this; } void writeStylesheetRef( std::string const& url ) { m_os << "\n"; } XmlWriter& writeBlankLine() { ensureTagClosed(); m_os << '\n'; return *this; } void ensureTagClosed() { if( m_tagIsOpen ) { m_os << ">" << std::endl; m_tagIsOpen = false; } } private: XmlWriter( XmlWriter const& ); void operator=( XmlWriter const& ); void writeDeclaration() { m_os << "\n"; } void newlineIfNecessary() { if( m_needsNewline ) { m_os << std::endl; m_needsNewline = false; } } bool m_tagIsOpen; bool m_needsNewline; std::vector m_tags; std::string m_indent; std::ostream& m_os; }; } namespace Catch { class XmlReporter : public StreamingReporterBase { public: XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), m_xml(_config.stream()), m_sectionDepth( 0 ) { m_reporterPrefs.shouldRedirectStdOut = true; } virtual ~XmlReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results as an XML document"; } virtual std::string getStylesheetRef() const { return std::string(); } void writeSourceInfo( SourceLineInfo const& sourceInfo ) { m_xml .writeAttribute( "filename", sourceInfo.file ) .writeAttribute( "line", sourceInfo.line ); } public: // StreamingReporterBase virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { StreamingReporterBase::noMatchingTestCases( s ); } virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); std::string stylesheetRef = getStylesheetRef(); if( !stylesheetRef.empty() ) m_xml.writeStylesheetRef( stylesheetRef ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); } virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { StreamingReporterBase::testGroupStarting( groupInfo ); m_xml.startElement( "Group" ) .writeAttribute( "name", groupInfo.name ); } virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); m_xml.startElement( "TestCase" ) .writeAttribute( "name", trim( testInfo.name ) ) .writeAttribute( "description", testInfo.description ) .writeAttribute( "tags", testInfo.tagsAsString ); writeSourceInfo( testInfo.lineInfo ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); m_xml.ensureTagClosed(); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { StreamingReporterBase::sectionStarting( sectionInfo ); if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) .writeAttribute( "name", trim( sectionInfo.name ) ) .writeAttribute( "description", sectionInfo.description ); writeSourceInfo( sectionInfo.lineInfo ); m_xml.ensureTagClosed(); } } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { AssertionResult const& result = assertionStats.assertionResult; bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); if( includeResults ) { // Print any info messages in tags. for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); it != itEnd; ++it ) { if( it->type == ResultWas::Info ) { m_xml.scopedElement( "Info" ) .writeText( it->message ); } else if ( it->type == ResultWas::Warning ) { m_xml.scopedElement( "Warning" ) .writeText( it->message ); } } } // Drop out if result was successful but we're not printing them. if( !includeResults && result.getResultType() != ResultWas::Warning ) return true; // Print the expression if there is one. if( result.hasExpression() ) { m_xml.startElement( "Expression" ) .writeAttribute( "success", result.succeeded() ) .writeAttribute( "type", result.getTestMacroName() ); writeSourceInfo( result.getSourceInfo() ); m_xml.scopedElement( "Original" ) .writeText( result.getExpression() ); m_xml.scopedElement( "Expanded" ) .writeText( result.getExpandedExpression() ); } // And... Print a result applicable to each result type. switch( result.getResultType() ) { case ResultWas::ThrewException: m_xml.startElement( "Exception" ); writeSourceInfo( result.getSourceInfo() ); m_xml.writeText( result.getMessage() ); m_xml.endElement(); break; case ResultWas::FatalErrorCondition: m_xml.startElement( "FatalErrorCondition" ); writeSourceInfo( result.getSourceInfo() ); m_xml.writeText( result.getMessage() ); m_xml.endElement(); break; case ResultWas::Info: m_xml.scopedElement( "Info" ) .writeText( result.getMessage() ); break; case ResultWas::Warning: // Warning will already have been written break; case ResultWas::ExplicitFailure: m_xml.startElement( "Failure" ); writeSourceInfo( result.getSourceInfo() ); m_xml.writeText( result.getMessage() ); m_xml.endElement(); break; default: break; } if( result.hasExpression() ) m_xml.endElement(); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { StreamingReporterBase::sectionEnded( sectionStats ); if( --m_sectionDepth > 0 ) { XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); e.writeAttribute( "successes", sectionStats.assertions.passed ); e.writeAttribute( "failures", sectionStats.assertions.failed ); e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); m_xml.endElement(); } } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { StreamingReporterBase::testCaseEnded( testCaseStats ); XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); if( !testCaseStats.stdOut.empty() ) m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); if( !testCaseStats.stdErr.empty() ) m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); m_xml.endElement(); } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { StreamingReporterBase::testGroupEnded( testGroupStats ); // TODO: Check testGroupStats.aborting and act accordingly. m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); m_xml.endElement(); } virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { StreamingReporterBase::testRunEnded( testRunStats ); m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testRunStats.totals.assertions.passed ) .writeAttribute( "failures", testRunStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); m_xml.endElement(); } private: Timer m_testCaseTimer; XmlWriter m_xml; int m_sectionDepth; }; INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_junit.hpp #define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED #include namespace Catch { namespace { std::string getCurrentTimestamp() { // Beware, this is not reentrant because of backward compatibility issues // Also, UTC only, again because of backward compatibility (%z is C++11) time_t rawtime; std::time(&rawtime); const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); #ifdef _MSC_VER std::tm timeInfo = {}; gmtime_s(&timeInfo, &rawtime); #else std::tm* timeInfo; timeInfo = std::gmtime(&rawtime); #endif char timeStamp[timeStampSize]; const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; #ifdef _MSC_VER std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif return std::string(timeStamp); } } class JunitReporter : public CumulativeReporterBase { public: JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), xml( _config.stream() ), m_okToFail( false ) { m_reporterPrefs.shouldRedirectStdOut = true; } virtual ~JunitReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results in an XML format that looks like Ant's junitreport target"; } virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { CumulativeReporterBase::testRunStarting( runInfo ); xml.startElement( "testsuites" ); } virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { suiteTimer.start(); stdOutForSuite.str(""); stdErrForSuite.str(""); unexpectedExceptions = 0; CumulativeReporterBase::testGroupStarting( groupInfo ); } virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE { m_okToFail = testCaseInfo.okToFail(); } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) unexpectedExceptions++; return CumulativeReporterBase::assertionEnded( assertionStats ); } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { stdOutForSuite << testCaseStats.stdOut; stdErrForSuite << testCaseStats.stdErr; CumulativeReporterBase::testCaseEnded( testCaseStats ); } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { double suiteTime = suiteTimer.getElapsedSeconds(); CumulativeReporterBase::testGroupEnded( testGroupStats ); writeGroup( *m_testGroups.back(), suiteTime ); } virtual void testRunEndedCumulative() CATCH_OVERRIDE { xml.endElement(); } void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); TestGroupStats const& stats = groupNode.value; xml.writeAttribute( "name", stats.groupInfo.name ); xml.writeAttribute( "errors", unexpectedExceptions ); xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); xml.writeAttribute( "tests", stats.totals.assertions.total() ); xml.writeAttribute( "hostname", "tbd" ); // !TBD if( m_config->showDurations() == ShowDurations::Never ) xml.writeAttribute( "time", "" ); else xml.writeAttribute( "time", suiteTime ); xml.writeAttribute( "timestamp", getCurrentTimestamp() ); // Write test cases for( TestGroupNode::ChildNodes::const_iterator it = groupNode.children.begin(), itEnd = groupNode.children.end(); it != itEnd; ++it ) writeTestCase( **it ); xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); } void writeTestCase( TestCaseNode const& testCaseNode ) { TestCaseStats const& stats = testCaseNode.value; // All test cases have exactly one section - which represents the // test case itself. That section may have 0-n nested sections assert( testCaseNode.children.size() == 1 ); SectionNode const& rootSection = *testCaseNode.children.front(); std::string className = stats.testInfo.className; if( className.empty() ) { if( rootSection.childSections.empty() ) className = "global"; } writeSection( className, "", rootSection ); } void writeSection( std::string const& className, std::string const& rootName, SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) name = rootName + '/' + name; if( !sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); if( className.empty() ) { xml.writeAttribute( "classname", name ); xml.writeAttribute( "name", "root" ); } else { xml.writeAttribute( "classname", className ); xml.writeAttribute( "name", name ); } xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); writeAssertions( sectionNode ); if( !sectionNode.stdOut.empty() ) xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); if( !sectionNode.stdErr.empty() ) xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); } for( SectionNode::ChildSections::const_iterator it = sectionNode.childSections.begin(), itEnd = sectionNode.childSections.end(); it != itEnd; ++it ) if( className.empty() ) writeSection( name, "", **it ); else writeSection( className, name, **it ); } void writeAssertions( SectionNode const& sectionNode ) { for( SectionNode::Assertions::const_iterator it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); it != itEnd; ++it ) writeAssertion( *it ); } void writeAssertion( AssertionStats const& stats ) { AssertionResult const& result = stats.assertionResult; if( !result.isOk() ) { std::string elementName; switch( result.getResultType() ) { case ResultWas::ThrewException: case ResultWas::FatalErrorCondition: elementName = "error"; break; case ResultWas::ExplicitFailure: elementName = "failure"; break; case ResultWas::ExpressionFailed: elementName = "failure"; break; case ResultWas::DidntThrowException: elementName = "failure"; break; // We should never see these here: case ResultWas::Info: case ResultWas::Warning: case ResultWas::Ok: case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: elementName = "internalError"; break; } XmlWriter::ScopedElement e = xml.scopedElement( elementName ); xml.writeAttribute( "message", result.getExpandedExpression() ); xml.writeAttribute( "type", result.getTestMacroName() ); std::ostringstream oss; if( !result.getMessage().empty() ) oss << result.getMessage() << '\n'; for( std::vector::const_iterator it = stats.infoMessages.begin(), itEnd = stats.infoMessages.end(); it != itEnd; ++it ) if( it->type == ResultWas::Info ) oss << it->message << '\n'; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); } } XmlWriter xml; Timer suiteTimer; std::ostringstream stdOutForSuite; std::ostringstream stdErrForSuite; unsigned int unexpectedExceptions; bool m_okToFail; }; INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_console.hpp #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED #include #include namespace Catch { struct ConsoleReporter : StreamingReporterBase { ConsoleReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), m_headerPrinted( false ) {} virtual ~ConsoleReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results as plain lines of text"; } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { stream << "No test cases matched '" << spec << '\'' << std::endl; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { AssertionResult const& result = _assertionStats.assertionResult; bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); // Drop out if result was successful but we're not printing them. if( !includeResults && result.getResultType() != ResultWas::Warning ) return false; lazyPrint(); AssertionPrinter printer( stream, _assertionStats, includeResults ); printer.print(); stream << std::endl; return true; } virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { m_headerPrinted = false; StreamingReporterBase::sectionStarting( _sectionInfo ); } virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { if( _sectionStats.missingAssertions ) { lazyPrint(); Colour colour( Colour::ResultError ); if( m_sectionStack.size() > 1 ) stream << "\nNo assertions in section"; else stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } if( m_config->showDurations() == ShowDurations::Always ) { stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } if( m_headerPrinted ) { m_headerPrinted = false; } StreamingReporterBase::sectionEnded( _sectionStats ); } virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { StreamingReporterBase::testCaseEnded( _testCaseStats ); m_headerPrinted = false; } virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { if( currentGroupInfo.used ) { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; printTotals( _testGroupStats.totals ); stream << '\n' << std::endl; } StreamingReporterBase::testGroupEnded( _testGroupStats ); } virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { printTotalsDivider( _testRunStats.totals ); printTotals( _testRunStats.totals ); stream << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: class AssertionPrinter { void operator= ( AssertionPrinter const& ); public: AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ), stats( _stats ), result( _stats.assertionResult ), colour( Colour::None ), message( result.getMessage() ), messages( _stats.infoMessages ), printInfoMessages( _printInfoMessages ) { switch( result.getResultType() ) { case ResultWas::Ok: colour = Colour::Success; passOrFail = "PASSED"; //if( result.hasMessage() ) if( _stats.infoMessages.size() == 1 ) messageLabel = "with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "with messages"; break; case ResultWas::ExpressionFailed: if( result.isOk() ) { colour = Colour::Success; passOrFail = "FAILED - but was ok"; } else { colour = Colour::Error; passOrFail = "FAILED"; } if( _stats.infoMessages.size() == 1 ) messageLabel = "with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "with messages"; break; case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to unexpected exception with "; if (_stats.infoMessages.size() == 1) messageLabel += "message"; if (_stats.infoMessages.size() > 1) messageLabel += "messages"; break; case ResultWas::FatalErrorCondition: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to a fatal error condition"; break; case ResultWas::DidntThrowException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "because no exception was thrown where one was expected"; break; case ResultWas::Info: messageLabel = "info"; break; case ResultWas::Warning: messageLabel = "warning"; break; case ResultWas::ExplicitFailure: passOrFail = "FAILED"; colour = Colour::Error; if( _stats.infoMessages.size() == 1 ) messageLabel = "explicitly with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "explicitly with messages"; break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: passOrFail = "** internal error **"; colour = Colour::Error; break; } } void print() const { printSourceInfo(); if( stats.totals.assertions.total() > 0 ) { if( result.isOk() ) stream << '\n'; printResultType(); printOriginalExpression(); printReconstructedExpression(); } else { stream << '\n'; } printMessage(); } private: void printResultType() const { if( !passOrFail.empty() ) { Colour colourGuard( colour ); stream << passOrFail << ":\n"; } } void printOriginalExpression() const { if( result.hasExpression() ) { Colour colourGuard( Colour::OriginalExpression ); stream << " "; stream << result.getExpressionInMacro(); stream << '\n'; } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; } } void printMessage() const { if( !messageLabel.empty() ) stream << messageLabel << ':' << '\n'; for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); it != itEnd; ++it ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || it->type != ResultWas::Info ) stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; } } void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ": "; } std::ostream& stream; AssertionStats const& stats; AssertionResult const& result; Colour::Code colour; std::string passOrFail; std::string messageLabel; std::string message; std::vector messages; bool printInfoMessages; }; void lazyPrint() { if( !currentTestRunInfo.used ) lazyPrintRunInfo(); if( !currentGroupInfo.used ) lazyPrintGroupInfo(); if( !m_headerPrinted ) { printTestCaseAndSectionHeader(); m_headerPrinted = true; } } void lazyPrintRunInfo() { stream << '\n' << getLineOfChars<'~'>() << '\n'; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name << " is a Catch v" << libraryVersion() << " host application.\n" << "Run with -? for options\n\n"; if( m_config->rngSeed() != 0 ) stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; currentTestRunInfo.used = true; } void lazyPrintGroupInfo() { if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { printClosedHeader( "Group: " + currentGroupInfo->name ); currentGroupInfo.used = true; } } void printTestCaseAndSectionHeader() { assert( !m_sectionStack.empty() ); printOpenHeader( currentTestCaseInfo->name ); if( m_sectionStack.size() > 1 ) { Colour colourGuard( Colour::Headers ); std::vector::const_iterator it = m_sectionStack.begin()+1, // Skip first section (test case) itEnd = m_sectionStack.end(); for( ; it != itEnd; ++it ) printHeaderString( it->name, 2 ); } SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; if( !lineInfo.empty() ){ stream << getLineOfChars<'-'>() << '\n'; Colour colourGuard( Colour::FileName ); stream << lineInfo << '\n'; } stream << getLineOfChars<'.'>() << '\n' << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); stream << getLineOfChars<'.'>() << '\n'; } void printOpenHeader( std::string const& _name ) { stream << getLineOfChars<'-'>() << '\n'; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); } } // if string has a : in first line will set indent to follow it on // subsequent lines void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { std::size_t i = _string.find( ": " ); if( i != std::string::npos ) i+=2; else i = 0; stream << Text( _string, TextAttributes() .setIndent( indent+i) .setInitialIndent( indent ) ) << '\n'; } struct SummaryColumn { SummaryColumn( std::string const& _label, Colour::Code _colour ) : label( _label ), colour( _colour ) {} SummaryColumn addRow( std::size_t count ) { std::ostringstream oss; oss << count; std::string row = oss.str(); for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { while( it->size() < row.size() ) *it = ' ' + *it; while( it->size() > row.size() ) row = ' ' + row; } rows.push_back( row ); return *this; } std::string label; Colour::Code colour; std::vector rows; }; void printTotals( Totals const& totals ) { if( totals.testCases.total() == 0 ) { stream << Colour( Colour::Warning ) << "No tests ran\n"; } else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " << pluralise( totals.testCases.passed, "test case" ) << ')' << '\n'; } else { std::vector columns; columns.push_back( SummaryColumn( "", Colour::None ) .addRow( totals.testCases.total() ) .addRow( totals.assertions.total() ) ); columns.push_back( SummaryColumn( "passed", Colour::Success ) .addRow( totals.testCases.passed ) .addRow( totals.assertions.passed ) ); columns.push_back( SummaryColumn( "failed", Colour::ResultError ) .addRow( totals.testCases.failed ) .addRow( totals.assertions.failed ) ); columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) .addRow( totals.testCases.failedButOk ) .addRow( totals.assertions.failedButOk ) ); printSummaryRow( "test cases", columns, 0 ); printSummaryRow( "assertions", columns, 1 ); } } void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { std::string value = it->rows[row]; if( it->label.empty() ) { stream << label << ": "; if( value != "0" ) stream << value; else stream << Colour( Colour::Warning ) << "- none -"; } else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; stream << Colour( it->colour ) << value << ' ' << it->label; } } stream << '\n'; } static std::size_t makeRatio( std::size_t number, std::size_t total ) { std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; return ( ratio == 0 && number > 0 ) ? 1 : ratio; } static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { if( i > j && i > k ) return i; else if( j > k ) return j; else return k; } void printTotalsDivider( Totals const& totals ) { if( totals.testCases.total() > 0 ) { std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) findMax( failedRatio, failedButOkRatio, passedRatio )++; while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) findMax( failedRatio, failedButOkRatio, passedRatio )--; stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); if( totals.testCases.allPassed() ) stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); else stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); } else { stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); } stream << '\n'; } void printSummaryDivider() { stream << getLineOfChars<'-'>() << '\n'; } private: bool m_headerPrinted; }; INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_compact.hpp #define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED namespace Catch { struct CompactReporter : StreamingReporterBase { CompactReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ) {} virtual ~CompactReporter(); static std::string getDescription() { return "Reports test results on a single line, suitable for IDEs"; } virtual ReporterPreferences getPreferences() const { ReporterPreferences prefs; prefs.shouldRedirectStdOut = false; return prefs; } virtual void noMatchingTestCases( std::string const& spec ) { stream << "No test cases matched '" << spec << '\'' << std::endl; } virtual void assertionStarting( AssertionInfo const& ) {} virtual bool assertionEnded( AssertionStats const& _assertionStats ) { AssertionResult const& result = _assertionStats.assertionResult; bool printInfoMessages = true; // Drop out if result was successful and we're not printing those if( !m_config->includeSuccessfulResults() && result.isOk() ) { if( result.getResultType() != ResultWas::Warning ) return false; printInfoMessages = false; } AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); printer.print(); stream << std::endl; return true; } virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { if (m_config->showDurations() == ShowDurations::Always) { stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } } virtual void testRunEnded( TestRunStats const& _testRunStats ) { printTotals( _testRunStats.totals ); stream << '\n' << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: class AssertionPrinter { void operator= ( AssertionPrinter const& ); public: AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ) , stats( _stats ) , result( _stats.assertionResult ) , messages( _stats.infoMessages ) , itMessage( _stats.infoMessages.begin() ) , printInfoMessages( _printInfoMessages ) {} void print() { printSourceInfo(); itMessage = messages.begin(); switch( result.getResultType() ) { case ResultWas::Ok: printResultType( Colour::ResultSuccess, passedString() ); printOriginalExpression(); printReconstructedExpression(); if ( ! result.hasExpression() ) printRemainingMessages( Colour::None ); else printRemainingMessages(); break; case ResultWas::ExpressionFailed: if( result.isOk() ) printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); else printResultType( Colour::Error, failedString() ); printOriginalExpression(); printReconstructedExpression(); printRemainingMessages(); break; case ResultWas::ThrewException: printResultType( Colour::Error, failedString() ); printIssue( "unexpected exception with message:" ); printMessage(); printExpressionWas(); printRemainingMessages(); break; case ResultWas::FatalErrorCondition: printResultType( Colour::Error, failedString() ); printIssue( "fatal error condition with message:" ); printMessage(); printExpressionWas(); printRemainingMessages(); break; case ResultWas::DidntThrowException: printResultType( Colour::Error, failedString() ); printIssue( "expected exception, got none" ); printExpressionWas(); printRemainingMessages(); break; case ResultWas::Info: printResultType( Colour::None, "info" ); printMessage(); printRemainingMessages(); break; case ResultWas::Warning: printResultType( Colour::None, "warning" ); printMessage(); printRemainingMessages(); break; case ResultWas::ExplicitFailure: printResultType( Colour::Error, failedString() ); printIssue( "explicitly" ); printRemainingMessages( Colour::None ); break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: printResultType( Colour::Error, "** internal error **" ); break; } } private: // Colour::LightGrey static Colour::Code dimColour() { return Colour::FileName; } #ifdef CATCH_PLATFORM_MAC static const char* failedString() { return "FAILED"; } static const char* passedString() { return "PASSED"; } #else static const char* failedString() { return "failed"; } static const char* passedString() { return "passed"; } #endif void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ':'; } void printResultType( Colour::Code colour, std::string const& passOrFail ) const { if( !passOrFail.empty() ) { { Colour colourGuard( colour ); stream << ' ' << passOrFail; } stream << ':'; } } void printIssue( std::string const& issue ) const { stream << ' ' << issue; } void printExpressionWas() { if( result.hasExpression() ) { stream << ';'; { Colour colour( dimColour() ); stream << " expression was:"; } printOriginalExpression(); } } void printOriginalExpression() const { if( result.hasExpression() ) { stream << ' ' << result.getExpression(); } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { { Colour colour( dimColour() ); stream << " for: "; } stream << result.getExpandedExpression(); } } void printMessage() { if ( itMessage != messages.end() ) { stream << " '" << itMessage->message << '\''; ++itMessage; } } void printRemainingMessages( Colour::Code colour = dimColour() ) { if ( itMessage == messages.end() ) return; // using messages.end() directly yields compilation error: std::vector::const_iterator itEnd = messages.end(); const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); { Colour colourGuard( colour ); stream << " with " << pluralise( N, "message" ) << ':'; } for(; itMessage != itEnd; ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || itMessage->type != ResultWas::Info ) { stream << " '" << itMessage->message << '\''; if ( ++itMessage != itEnd ) { Colour colourGuard( dimColour() ); stream << " and"; } } } } private: std::ostream& stream; AssertionStats const& stats; AssertionResult const& result; std::vector messages; std::vector::const_iterator itMessage; bool printInfoMessages; }; // Colour, message variants: // - white: No tests ran. // - red: Failed [both/all] N test cases, failed [both/all] M assertions. // - white: Passed [both/all] N test cases (no assertions). // - red: Failed N tests cases, failed M assertions. // - green: Passed [both/all] N tests cases with M assertions. std::string bothOrAll( std::size_t count ) const { return count == 1 ? std::string() : count == 2 ? "both " : "all " ; } void printTotals( const Totals& totals ) const { if( totals.testCases.total() == 0 ) { stream << "No tests ran."; } else if( totals.testCases.failed == totals.testCases.total() ) { Colour colour( Colour::ResultError ); const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? bothOrAll( totals.assertions.failed ) : std::string(); stream << "Failed " << bothOrAll( totals.testCases.failed ) << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << qualify_assertions_failed << pluralise( totals.assertions.failed, "assertion" ) << '.'; } else if( totals.assertions.total() == 0 ) { stream << "Passed " << bothOrAll( totals.testCases.total() ) << pluralise( totals.testCases.total(), "test case" ) << " (no assertions)."; } else if( totals.assertions.failed ) { Colour colour( Colour::ResultError ); stream << "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; } else { Colour colour( Colour::ResultSuccess ); stream << "Passed " << bothOrAll( totals.testCases.passed ) << pluralise( totals.testCases.passed, "test case" ) << " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; } } }; INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) } // end namespace Catch namespace Catch { // These are all here to avoid warnings about not having any out of line // virtual methods NonCopyable::~NonCopyable() {} IShared::~IShared() {} IStream::~IStream() CATCH_NOEXCEPT {} FileStream::~FileStream() CATCH_NOEXCEPT {} CoutStream::~CoutStream() CATCH_NOEXCEPT {} DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} IContext::~IContext() {} IResultCapture::~IResultCapture() {} ITestCase::~ITestCase() {} ITestCaseRegistry::~ITestCaseRegistry() {} IRegistryHub::~IRegistryHub() {} IMutableRegistryHub::~IMutableRegistryHub() {} IExceptionTranslator::~IExceptionTranslator() {} IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} IReporter::~IReporter() {} IReporterFactory::~IReporterFactory() {} IReporterRegistry::~IReporterRegistry() {} IStreamingReporter::~IStreamingReporter() {} AssertionStats::~AssertionStats() {} SectionStats::~SectionStats() {} TestCaseStats::~TestCaseStats() {} TestGroupStats::~TestGroupStats() {} TestRunStats::~TestRunStats() {} CumulativeReporterBase::SectionNode::~SectionNode() {} CumulativeReporterBase::~CumulativeReporterBase() {} StreamingReporterBase::~StreamingReporterBase() {} ConsoleReporter::~ConsoleReporter() {} CompactReporter::~CompactReporter() {} IRunner::~IRunner() {} IMutableContext::~IMutableContext() {} IConfig::~IConfig() {} XmlReporter::~XmlReporter() {} JunitReporter::~JunitReporter() {} TestRegistry::~TestRegistry() {} FreeFunctionTestCase::~FreeFunctionTestCase() {} IGeneratorInfo::~IGeneratorInfo() {} IGeneratorsForTest::~IGeneratorsForTest() {} WildcardPattern::~WildcardPattern() {} TestSpec::Pattern::~Pattern() {} TestSpec::NamePattern::~NamePattern() {} TestSpec::TagPattern::~TagPattern() {} TestSpec::ExcludedPattern::~ExcludedPattern() {} Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} void Config::dummy() {} namespace TestCaseTracking { ITracker::~ITracker() {} TrackerBase::~TrackerBase() {} SectionTracker::~SectionTracker() {} IndexTracker::~IndexTracker() {} } } #ifdef __clang__ # pragma clang diagnostic pop #endif #endif #ifdef CATCH_CONFIG_MAIN // #included from: internal/catch_default_main.hpp #define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED #ifndef __OBJC__ #if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) // Standard C/C++ Win32 Unicode wmain entry point extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { #else // Standard C/C++ main entry point int main (int argc, char * argv[]) { #endif int result = Catch::Session().run( argc, argv ); return ( result < 0xff ? result : 0xff ); } #else // __OBJC__ // Objective-C entry point int main (int argc, char * const argv[]) { #if !CATCH_ARC_ENABLED NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; #endif Catch::registerTestMethods(); int result = Catch::Session().run( argc, (char* const*)argv ); #if !CATCH_ARC_ENABLED [pool drain]; #endif return ( result < 0xff ? result : 0xff ); } #endif // __OBJC__ #endif #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED # undef CLARA_CONFIG_MAIN #endif ////// // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL #if defined(CATCH_CONFIG_FAST_COMPILE) #define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) #define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) #else #define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) #define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) #endif #define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) #define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) #define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) #define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) #define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) #define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) #define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) #define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) #define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #if defined(CATCH_CONFIG_FAST_COMPILE) #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) #else #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) #endif #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) #define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) #define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) #define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) #define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) #define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) #define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) #define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) #define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) #define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) #define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else #if defined(CATCH_CONFIG_FAST_COMPILE) #define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr ) #define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) #else #define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr ) #define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) #endif #define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) #define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) #define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) #define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) #define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) #define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) #define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) #define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) #define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) #define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) #define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #if defined(CATCH_CONFIG_FAST_COMPILE) #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) #else #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) #endif #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) #define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) #define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) #define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) #define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) #define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) #endif #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) #define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) #define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) #define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) #define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) #define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) using Catch::Detail::Approx; // #included from: internal/catch_reenable_warnings.h #define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(pop) # else # pragma clang diagnostic pop # endif #elif defined __GNUC__ # pragma GCC diagnostic pop #endif #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED isoband/src/init.cpp0000644000176200001440000000215313757763676014163 0ustar liggesusers#include #include #include // for NULL #include // Defined in clip-lines.cpp extern "C" SEXP clip_lines_impl(SEXP x, SEXP y, SEXP id, SEXP _p_mid_x, SEXP _p_mid_y, SEXP _width, SEXP _height, SEXP _theta, SEXP _asp); // Defined in isobands.cpp extern "C" SEXP isobands_impl(SEXP x, SEXP y, SEXP z, SEXP value_low, SEXP value_high); extern "C" SEXP isolines_impl(SEXP x, SEXP y, SEXP z, SEXP value); // Defined in separate-polygons.cpp extern "C" SEXP separate_polygons(SEXP x, SEXP y, SEXP id); // From testthat extern "C" SEXP run_testthat_tests(SEXP use_xml_sxp); static const R_CallMethodDef CallEntries[] = { {"clip_lines_impl_c", (DL_FUNC) &clip_lines_impl, 9}, {"isobands_impl_c", (DL_FUNC) &isobands_impl, 5}, {"isolines_impl_c", (DL_FUNC) &isolines_impl, 4}, {"separate_polygons_c", (DL_FUNC) &separate_polygons, 3}, {"run_testthat_tests", (DL_FUNC) &run_testthat_tests, 1}, {NULL, NULL, 0} }; extern "C" void R_init_isoband(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } isoband/src/utils.h0000644000176200001440000000326213673436547014016 0ustar liggesusers#ifndef UTILS_H #define UTILS_H // Turn off non-exported functionality #define R_NO_REMAP #include #include // Define a C++ try-catch macro to guard C++ calls #define BEGIN_CPP try { #define END_CPP \ } \ catch (std::exception & e) { \ Rf_error("C++ exception: %s", e.what()); \ } static void chkIntFn(void *dummy) { R_CheckUserInterrupt(); } // this will call the above in a top-level context so it won't longjmp-out of your context static inline bool checkInterrupt() { return (R_ToplevelExec(chkIntFn, NULL) == FALSE); } [[ noreturn ]] static inline void longjump_interrupt() { SEXP env = PROTECT(Rf_findVarInFrame(R_NamespaceRegistry, Rf_install("isoband"))); if (env == R_UnboundValue) { Rf_error("isoband namespace could not be found"); } SEXP call = PROTECT(Rf_lang1(Rf_install("rethrow_interrupt"))); Rf_eval(call, env); Rf_error("Interrupt failed to rethrow"); } class CollectorList { SEXP data_; R_xlen_t n_; public: CollectorList(R_xlen_t size = 1) : n_(0) { data_ = Rf_allocVector(VECSXP, size); R_PreserveObject(data_); } void push_back(SEXP x) { if (Rf_xlength(data_) == n_) { R_ReleaseObject(data_); data_ = Rf_lengthgets(data_, n_ * 2); R_PreserveObject(data_); } SET_VECTOR_ELT(data_, n_++, x); } operator SEXP() { if (Rf_xlength(data_) != n_) { SETLENGTH(data_, n_); } return data_; } ~CollectorList() { R_ReleaseObject(data_); } }; #endif isoband/src/separate-polygons.cpp0000644000176200001440000002614113673436547016666 0ustar liggesusers#define R_NO_REMAP #include #include #include #include #include #include using namespace std; #include "polygon.h" #include "separate-polygons.h" #include "utils.h" /* Calculate the number of times a ray extending from point P to the right * intersects with the line segment defined by p0, p1. This number is * 0 or 1. However, -1 is returned if the point lies exactly on the segment, * so intersection in indetermined. */ int ray_intersections(point P, point p0, point p1) { // simple cases if (p0.y < p1.y) { if ((P.y < p0.y) || (P.y > p1.y)) return 0; } else { if ((P.y > p0.y) || (P.y < p1.y)) return 0; } if ((P.x > p0.x) && (P.x > p1.x)) return 0; double dy = p1.y-p0.y; if (dy == 0) { if (P.y == p0.y) { // point is on the same y value, but does it lie inside the x interval? if ((P.x < p0.x) && (P.x < p1.x)) return 1; else return -1; } else return 0; // should never get here; handled by simple cases above } double t = (P.y - p0.y)/dy; double xint = p0.x + t*(p1.x - p0.x); //cout << "t = " << t << "; xint = " << xint << endl; if (xint < P.x) { return 0; } else if (xint == P.x) { return -1; } else return 1; } in_polygon_type point_in_polygon(const point &P, const polygon &poly) { int intersections = 0; int n = poly.size(); int istart = 0; while (poly[istart].y == P.y) { // algorithm doesn't work if we start with a line segment that starts at P.y istart++; if (istart == n-1) { // degenerate polygon; one horizontal line // find min and max x and test if P.x is in // that interval or not double xmin = poly[0].x; double xmax = poly[0].x; for (int i = 1; i < n-1; i++) { if (poly[i].x < xmin) { xmin = poly[i].x; } if (poly[i].x > xmax) { xmax = poly[i].x; } } if (P.x >= xmin && P.x <= xmax) { return undetermined; } else { return outside; } } } int i = istart; do { int itr = ray_intersections(P, poly[i], poly[i+1]); //cout << i << " " << itr << endl; if (itr < 0) { // undetermined case, so we're done return undetermined; } if (itr > 0 && poly[i+1].y == P.y) { // special case, intersection with exact line endpoint bool from_above = poly[i].y > poly[i+1].y; // did we enter from above bool wrap_around = false; int j = i+1; do { // find next line segment where we move away from that point if (j == n-1) { j = 0; } if (j == istart) { wrap_around = true; // should never get here, due to choice of istart } if (ray_intersections(P, poly[j], poly[j+1]) < 0) { // if the point lies exactly on any of these segments the case is undetermined return undetermined; } j++; } while (poly[j].y == poly[i+1].y); //cout << from_above << " " << i+1 << " " << j << " " << poly[i+1] << " " << poly[j] << endl; if ((!from_above && poly[j].y < poly[i+1].y) || (from_above && poly[j].y > poly[i+1].y)) { // incorrect intersection //cout << "incorrect intersection" << endl; itr = 0; } i = j; // fast forward if (wrap_around || i == istart) { //cout << "have wrapped around during fast forward" << endl; //cout << "increment intersections (wa) at " << i << " " << itr << " " << intersections << endl; intersections += itr; break; } i--; // decrement by one because it'll be incremented again below } //cout << "increment intersections (el) at " << i << " " << itr << " " << intersections << endl; intersections += itr; i++; if (i == n-1) i = 0; } while(i != istart); if (intersections % 2 == 1) return inside; return outside; } in_polygon_type polygon_in_polygon(const polygon &query, const polygon &reference, bool fast) { int ins = 0, out = 0, undet = 0; for (unsigned int i = 0; i < query.size()-1; i++) { switch(point_in_polygon(query[i], reference)) { case inside: ins += 1; break; case outside: out += 1; break; default: undet += 1; } // shortcut for faster classification: if at least one // non-ambiguous point is found, we know whether we're inside // or outside if (fast && (ins > 0 || out > 0)) break; } if (ins > 0 && out == 0) { return inside; } if (out > 0 && ins == 0) { return outside; } return undetermined; } class polygon_hierarchy { private: // for each polygon, contains a set of exterior polygons vector> ext_polygons; vector active_polygons; public: polygon_hierarchy(int n) { ext_polygons.resize(n); active_polygons.resize(n); // initially, all polygons are active for (auto it = active_polygons.begin(); it != active_polygons.end(); it++) { *it = true; } } void print() { for (unsigned int i = 0; i < ext_polygons.size(); i++) { cout << "polygon " << i << " (active = " << active_polygons[i] << ")" << endl; cout << " enclosing: "; for (auto it = ext_polygons[i].begin(); it != ext_polygons[i].end(); it++) { cout << (*it) << " "; } cout << endl; } } void set_exterior(int poly, int exterior) { ext_polygons[poly].insert(exterior); } void remove(int poly) { for (auto it = ext_polygons.begin(); it != ext_polygons.end(); it++) { it->erase(poly); } } // returns the next top level polygon found int top_level_poly() { unsigned int i = 0; do { if (active_polygons[i] && ext_polygons[i].size() == 0) { active_polygons[i] = false; break; } i++; } while (i < ext_polygons.size()); if (i == ext_polygons.size()) { // we have run out of top-level polygons, hence we're done i = -1; } return i; } // find all holes belonging to polygon, remove them and the parent // polygon from the hierarchy, and return set collect_holes(int poly) { set holes; unsigned int i = 0; do { if (active_polygons[i] && ext_polygons[i].size() == 1 && ext_polygons[i].count(poly) == 1) { holes.insert(i); active_polygons[i] = false; } i++; } while (i < ext_polygons.size()); for (auto it = holes.begin(); it != holes.end(); it++) { remove(*it); } remove(poly); return holes; } }; bool is_valid_ring(const polygon &poly) { if (poly.size() < 4) return false; // any polygon with fewer than four points is not a valid ring const point &p1 = poly.front(); auto it = poly.begin(); for (it++; it != poly.end(); it++) { if (!(p1 == *it)) { return true; // at least one point is different; we call it a valid ring } } return false; // degenerate polygon; we ignore it } SEXP polygon_as_matrix(polygon p, bool reverse = false) { int n = p.size(); SEXP m = PROTECT(Rf_allocMatrix(REALSXP, n, 2)); double* m_p = REAL(m); if (reverse) { for (int i = n; i > 0; i--) { m_p[n-i] = p[i-1].x; m_p[n-i+n] = p[i-1].y; } } else { for (int i = 0; i < n; i++) { m_p[i] = p[i].x; m_p[i+n] = p[i].y; } } UNPROTECT(1); return m; } extern "C" SEXP separate_polygons(SEXP x, SEXP y, SEXP id) { BEGIN_CPP SEXP out; // final result SEXP cl = PROTECT(Rf_allocVector(STRSXP, 3)); SET_STRING_ELT(cl, 0, Rf_mkChar("XY")); SET_STRING_ELT(cl, 1, Rf_mkChar("MULTIPOLYGON")); SET_STRING_ELT(cl, 2, Rf_mkChar("sfg")); int n = Rf_length(x); if (n == 0) { // set sf classes out = PROTECT(Rf_allocVector(VECSXP, 0)); Rf_classgets(out, cl); UNPROTECT(2); return out; } if (Rf_length(y) != n || Rf_length(id) != n) { Rf_error("Inputs x, y, and id must be of the same length."); } double* x_p = REAL(x); double* y_p = REAL(y); int* id_p = INTEGER(id); // create polygons from input data vector polys; int cur_id = id_p[0]; int cur_poly = 0; polys.push_back(polygon()); for (int i = 0; ifront() == it->back())) { it->push_back(it->front()); } } // set up polygon hierarchy polygon_hierarchy hi(polys.size()); for (unsigned int i = 0; i < polys.size(); i++) { if (checkInterrupt()) { longjump_interrupt(); } for (unsigned int j = 0; j < polys.size(); j++ ) { if (i == j) continue; in_polygon_type result = polygon_in_polygon(polys[i], polys[j]); //cout << "polygon " << i << " is " << result << " of polygon " << j << endl; if (result == inside) { hi.set_exterior(i, j); } else if (result == undetermined){ Rf_error("Found polygons without undefined interior/exterior relationship."); } } } int next_poly = hi.top_level_poly(); int i = 0; CollectorList all_rings; while(next_poly >= 0) { if (i % 1000 == 0 && checkInterrupt()) { longjump_interrupt(); } i++; // for simplicity, we collect the rings even if the polygon // is not valid; we just keep track of this and ignore it at // the end; this reduces the risk of bugs bool valid_poly = is_valid_ring(polys[next_poly]); // collect the holes, if any set holes = hi.collect_holes(next_poly); // record the polygon if valid if (valid_poly) { // collect all the rings belonging to this polygon SEXP rings = PROTECT(Rf_allocVector(VECSXP, holes.size() + 1)); // collect the outer ring SET_VECTOR_ELT(rings, 0, polygon_as_matrix(polys[next_poly])); int k = 1; for (auto it = holes.begin(); it != holes.end(); it++) { if (is_valid_ring(polys[*it])) { // we reverse holes so they run in the same direction as outer polygons SET_VECTOR_ELT(rings, k, polygon_as_matrix(polys[*it], true)); k++; } } // Shrink list to actual size because some holes may have been invalid rings = PROTECT(Rf_lengthgets(rings, k)); all_rings.push_back(rings); UNPROTECT(2); } next_poly = hi.top_level_poly(); } out = all_rings; Rf_classgets(out, cl); UNPROTECT(1); return(out); END_CPP } // testing code /*** R m <- matrix(c(0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) z <- isobands(1:6, 1:6, m, 0.5, 1.5) mp1 <- separate_polygons(z[[1]]$x, z[[1]]$y, z[[1]]$id) m <- matrix(c(0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) z <- isobands(1:6, 1:6, m, 0.5, 1.5) mp2 <- separate_polygons(z[[1]]$x, z[[1]]$y, z[[1]]$id) */ isoband/src/Makevars0000644000176200001440000000005214017470642014156 0ustar liggesusersCXX_STD = CXX11 PKG_CPPFLAGS = -Itestthat isoband/src/separate-polygons.h0000644000176200001440000000224513501223537016311 0ustar liggesusers#ifndef SEPARATE_POLYGONS_H #define SEPARATE_POLYGONS_H #include "polygon.h" /* Calculate the number of times a ray extending from point P to the right * intersects with the line segment defined by p0, p1. This number is * 0 or 1. However, -1 is returned if the point lies exactly on the segment, * so intersection in indetermined. */ int ray_intersections(point P, point p0, point p1); /* Test whether a point lies inside a polygon or not. Can return one of * three values, inside, outside, or undetermined. */ in_polygon_type point_in_polygon(const point &P, const polygon &poly); /* Test whether a polygon (the query) lies fully inside another polygon * (the reference). Undetermined points are ignored. If no clear determination * can be made, returns undetermined. * * The fast option determines whether we should call the outcome based on * only the first non-ambiguous point we find or on all points. */ in_polygon_type polygon_in_polygon(const polygon &query, const polygon &reference, bool fast = true); /* Test whether a polygon represents a valid ring (at least 4 points, * not all of which are the same). */ bool is_valid_ring(const polygon &poly); #endif isoband/src/Makevars.win0000644000176200001440000000005214017470642014752 0ustar liggesusersCXX_STD = CXX11 PKG_CPPFLAGS = -Itestthat isoband/src/polygon.cpp0000644000176200001440000000077213673436547014703 0ustar liggesusers#include using namespace std; #include "polygon.h" ostream & operator<<(ostream &out, const point &p) { out << "(" << p.x << ", " << p.y << ")"; return out; } bool operator==(const point &p1, const point &p2) { return (p1.x == p2.x) && (p1.y == p2.y); } ostream & operator<<(ostream &out, const in_polygon_type &t) { switch(t) { case inside: out << "inside"; break; case outside: out << "outside"; break; default: out << "undetermined"; } return out; } isoband/src/clip-lines.h0000644000176200001440000000354113673436547014715 0ustar liggesusers#ifndef CLIP_LINES_H #define CLIP_LINES_H #define R_NO_REMAP #include #include #include "polygon.h" enum segment_crop_type { none, // segment wasn't cropped complete, // entire segment is gone at_beginning, // beginning of segment is gone at_end, // end of segment is gone in_middle // middle of segment is gone }; // crops the line segment running from p1 to p2 to a unit box segment_crop_type crop_to_unit_box(const point &p1, const point &p2, point &crop1, point &crop2); // a class that can transform coordinates to and from a new coordinate system relative to a unit box class unitbox_transformer { protected: double m00, m01, m10, m11; // transformation matrix double mi00, mi01, mi10, mi11; // inverse transformation matrix point base; public: unitbox_transformer(const point &low_left, const point &low_right, const point &up_left) : base(low_left) { double x0 = low_right.x - low_left.x; double y0 = low_right.y - low_left.y; double x1 = up_left.x - low_left.x; double y1 = up_left.y - low_left.y; if ((x0 == 0 && y0 == 0) || (x1 == 0 && y1 == 0)) { Rf_error("singular transformation due to invalid box extent"); } double denominator = y0*x1 - y1*x0; if (denominator == 0) { Rf_error("singular transformation due to invalid box extent"); } m00 = -y1/denominator; m01 = x1/denominator; m10 = y0/denominator; m11 = -x0/denominator; mi00 = x0; mi01 = x1; mi10 = y0; mi11 = y1; } point transform(const point &p) { double x = p.x - base.x; double y = p.y - base.y; return point(m00*x + m01*y, m10*x + m11*y); } point inv_transform(const point &p) { double x = mi00*p.x + mi01*p.y; double y = mi10*p.x + mi11*p.y; return point(x + base.x, y + base.y); } }; #endif // CLIP_LINES_H isoband/src/test-runner.cpp0000644000176200001440000000042313757772465015500 0ustar liggesusers/* * Please do not edit this file -- it ensures that your package will export a * 'run_testthat_tests()' C routine that can be used to run the Catch unit tests * available in your package. */ #define CATCH_CONFIG_RUNNER #define TESTTHAT_TEST_RUNNER #include isoband/src/test-separate-polygons.cpp0000644000176200001440000002410413760131521017615 0ustar liggesusers#include #include "polygon.h" #include "separate-polygons.h" context("Point in polygon") { test_that("Simple square") { polygon poly = { point(0, 0), point(0, 1), point(1, 1), point(1, 0), point(0, 0) }; expect_true(point_in_polygon(point(0.5, 0.5), poly) == inside); expect_true(point_in_polygon(point(-0.5, 0.5), poly) == outside); expect_true(point_in_polygon(point(1.5, 0.5), poly) == outside); expect_true(point_in_polygon(point(0.5, -0.5), poly) == outside); expect_true(point_in_polygon(point(0.5, 1.5), poly) == outside); expect_true(point_in_polygon(point(-1, 1), poly) == outside); expect_true(point_in_polygon(point(2, 1), poly) == outside); expect_true(point_in_polygon(point(-1, 0), poly) == outside); expect_true(point_in_polygon(point(2, 0), poly) == outside); expect_true(point_in_polygon(point(0, 0), poly) == undetermined); expect_true(point_in_polygon(point(1, 0), poly) == undetermined); expect_true(point_in_polygon(point(0, 1), poly) == undetermined); expect_true(point_in_polygon(point(1, 1), poly) == undetermined); } test_that("Simple diamond") { polygon poly = { point(0, -.5), point(-.5, 0), point(0, .5), point(.5, 0), point(0, -.5) }; expect_true(point_in_polygon(point(0, 0), poly) == inside); expect_true(point_in_polygon(point(-1, 0), poly) == outside); expect_true(point_in_polygon(point(1, 0), poly) == outside); expect_true(point_in_polygon(point(-.3, -.3), poly) == outside); expect_true(point_in_polygon(point(-.3, .3), poly) == outside); expect_true(point_in_polygon(point(.3, .3), poly) == outside); expect_true(point_in_polygon(point(.3, -.3), poly) == outside); expect_true(point_in_polygon(point(-.2, -.2), poly) == inside); expect_true(point_in_polygon(point(-.2, .2), poly) == inside); expect_true(point_in_polygon(point(.2, .2), poly) == inside); expect_true(point_in_polygon(point(.2, -.2), poly) == inside); expect_true(point_in_polygon(point(0, -.5), poly) == undetermined); expect_true(point_in_polygon(point(-.5, 0), poly) == undetermined); expect_true(point_in_polygon(point(0, .5), poly) == undetermined); expect_true(point_in_polygon(point(.5, 0), poly) == undetermined); expect_true(point_in_polygon(point(-.25, -.25), poly) == undetermined); expect_true(point_in_polygon(point(-.25, .25), poly) == undetermined); expect_true(point_in_polygon(point(.25, .25), poly) == undetermined); expect_true(point_in_polygon(point(.25, -.25), poly) == undetermined); } test_that("Degenerate polygon: horizontal line") { polygon poly = { point(0, 0), point(1, 0), point(2, 0), point(0, 0) }; expect_true(point_in_polygon(point(-.5, 0), poly) == outside); expect_true(point_in_polygon(point(2.5, 0), poly) == outside); expect_true(point_in_polygon(point(0.5, 0), poly) == undetermined); expect_true(point_in_polygon(point(1.5, 0), poly) == undetermined); // alternative version polygon poly2 = { point(1, 0), point(2, 0), point(0, 0), point(1, 0) }; expect_true(point_in_polygon(point(-.5, 0), poly2) == outside); expect_true(point_in_polygon(point(2.5, 0), poly2) == outside); expect_true(point_in_polygon(point(0.5, 0), poly2) == undetermined); expect_true(point_in_polygon(point(1.5, 0), poly2) == undetermined); } test_that("Degenerate polygon: vertical line") { polygon poly = { point(.5, 2), point(.5, 1), point(.5, .5), point(.5, 2) }; expect_true(point_in_polygon(point(0, 2), poly) == outside); expect_true(point_in_polygon(point(0, 1.5), poly) == outside); expect_true(point_in_polygon(point(0, 1), poly) == outside); expect_true(point_in_polygon(point(0, .8), poly) == outside); expect_true(point_in_polygon(point(0, .5), poly) == outside); expect_true(point_in_polygon(point(0, .4), poly) == outside); expect_true(point_in_polygon(point(1, 1), poly) == outside); expect_true(point_in_polygon(point(.5, 2), poly) == undetermined); expect_true(point_in_polygon(point(.5, 1.5), poly) == undetermined); expect_true(point_in_polygon(point(.5, 1), poly) == undetermined); expect_true(point_in_polygon(point(.5, .5), poly) == undetermined); // alternative version polygon poly2 = { point(.5, 1), point(.5, .5), point(.5, 2), point(.5, 1) }; expect_true(point_in_polygon(point(0, 2), poly2) == outside); expect_true(point_in_polygon(point(0, 1.5), poly2) == outside); expect_true(point_in_polygon(point(0, 1), poly2) == outside); expect_true(point_in_polygon(point(0, .8), poly2) == outside); expect_true(point_in_polygon(point(0, .5), poly2) == outside); expect_true(point_in_polygon(point(0, .4), poly2) == outside); expect_true(point_in_polygon(point(1, 1), poly2) == outside); expect_true(point_in_polygon(point(.5, 2), poly2) == undetermined); expect_true(point_in_polygon(point(.5, 1.5), poly2) == undetermined); expect_true(point_in_polygon(point(.5, 1), poly2) == undetermined); expect_true(point_in_polygon(point(.5, .5), poly2) == undetermined); } test_that("Degenerate polygon: point") { polygon poly = { point(0, 0), point(0, 0) }; expect_true(point_in_polygon(point(-1, 0), poly) == outside); expect_true(point_in_polygon(point(1, 0), poly) == outside); expect_true(point_in_polygon(point(0, -1), poly) == outside); expect_true(point_in_polygon(point(0, 1), poly) == outside); expect_true(point_in_polygon(point(0.5, 0.5), poly) == outside); expect_true(point_in_polygon(point(0, 0), poly) == undetermined); } test_that("Multiple flat line segments") { polygon poly = { point(0, 2), point(1, 1), point(2, 1), point(3, 1), point(4, 1), point(4, 0), point(0, 0), point(0, 2) }; expect_true(point_in_polygon(point(-1, 1), poly) == outside); expect_true(point_in_polygon(point(5, 1), poly) == outside); expect_true(point_in_polygon(point(0.5, 1), poly) == inside); // alternative version polygon poly2 = { point(1, 1), point(2, 1), point(3, 1), point(4, 1), point(4, 0), point(0, 0), point(0, 2), point(1, 1) }; expect_true(point_in_polygon(point(-1, 1), poly2) == outside); expect_true(point_in_polygon(point(5, 1), poly2) == outside); expect_true(point_in_polygon(point(0.5, 1), poly2) == inside); // alternative version 2 polygon poly3 = { point(2, 1), point(3, 1), point(4, 1), point(4, 0), point(0, 0), point(0, 2), point(1, 1), point(2, 1) }; expect_true(point_in_polygon(point(-1, 1), poly3) == outside); expect_true(point_in_polygon(point(5, 1), poly3) == outside); expect_true(point_in_polygon(point(0.5, 1), poly3) == inside); // alternative version 3 polygon poly4 = { point(4, 1), point(4, 0), point(0, 0), point(0, 2), point(1, 1), point(2, 1), point(3, 1), point(4, 1) }; expect_true(point_in_polygon(point(-1, 1), poly4) == outside); expect_true(point_in_polygon(point(5, 1), poly4) == outside); expect_true(point_in_polygon(point(0.5, 1), poly4) == inside); } test_that("Zigzag 1") { polygon poly = { point(0, 2), point(1, 1), point(2, 1.5), point(3, 1), point(4, 1.5), point(5, 0), point(0, 0), point(0, 2) }; expect_true(point_in_polygon(point(-1, 1), poly) == outside); expect_true(point_in_polygon(point(5, 1), poly) == outside); expect_true(point_in_polygon(point(0.5, 1), poly) == inside); expect_true(point_in_polygon(point(3, 1), poly) == undetermined); } test_that("Zigzag 2") { polygon poly = { point(0, 2), point(1, 1), point(2, 1.5), point(3, 1), point(4, 1.5), point(4, 3), point(0, 3), point(0, 2) }; expect_true(point_in_polygon(point(-1, 1), poly) == outside); expect_true(point_in_polygon(point(5, 1), poly) == outside); expect_true(point_in_polygon(point(0.5, 1), poly) == outside); expect_true(point_in_polygon(point(1, 1.3), poly) == inside); expect_true(point_in_polygon(point(3, 1), poly) == undetermined); } } context("Polygon in polygon") { test_that("Basic relationships") { polygon p1 = { point(0, 0), point(0, 2), point(2, 2), point(2, 0), point(0, 0) }; polygon p2 = { point(0.5, 0.5), point(0.5, 1.5), point(1.5, 1.5), point(1.5, 0.5), point(0.5, 0.5) }; polygon p3 = { point(-1, -1), point(-1, 0), point(0, 0), point(0, -1), point(-1, -1) }; polygon p4 = { point(-1, -1), point(-1, 1), point(1, 1), point(1, -1), point(-1, -1) }; expect_true(polygon_in_polygon(p2, p1) == inside); expect_true(polygon_in_polygon(p1, p2) == outside); expect_true(polygon_in_polygon(p1, p3) == outside); expect_true(polygon_in_polygon(p3, p1) == outside); expect_true(polygon_in_polygon(p1, p4, false) == undetermined); expect_true(polygon_in_polygon(p4, p1, false) == undetermined); } test_that("Degenerate case") { polygon p1 = { point(0, 0), point(0, 2), point(2, 2), point(2, 0), point(0, 0) }; expect_true(polygon_in_polygon(p1, p1) == undetermined); } } context("is_valid_ring()") { test_that("valid ring") { point p(0, 0); polygon poly; expect_false(is_valid_ring(poly)); poly.push_back(p); expect_false(is_valid_ring(poly)); poly.push_back(p); expect_false(is_valid_ring(poly)); poly.push_back(p); expect_false(is_valid_ring(poly)); poly.push_back(p); expect_false(is_valid_ring(poly)); poly.push_back(point(1, 1)); expect_true(is_valid_ring(poly)); polygon poly2 = { point(0, 0), point(0, 2), point(2, 2), point(2, 0), point(0, 0) }; expect_true(is_valid_ring(poly2)); } } isoband/src/polygon.h0000644000176200001440000000111713501223537014321 0ustar liggesusers#ifndef POLYGON_H #define POLYGON_H #include #include #include using namespace std; // point in x-y space struct point { double x, y; // x and y coordinates point(double x_in = 0, double y_in = 0) : x(x_in), y(y_in) {} }; bool operator==(const point &p1, const point &p2); ostream & operator<<(ostream &out, const point &p); typedef vector polygon; enum in_polygon_type { inside, // point is inside a polygon outside, // point is outside a polygon undetermined // point lies right on the boundary }; #endif // POLYGON_H isoband/src/clip-lines.cpp0000644000176200001440000003217013673436547015250 0ustar liggesusers#define R_NO_REMAP #include #include #include #include #include // for size_t using namespace std; #include "clip-lines.h" #include "utils.h" // calculates the intersection point of a line segment and // the unit box, assuming p1 is outside and p2 is inside. // if the assumption isn't true, results are not reliable. point entry_intersection(const point &p1, const point &p2) { // p1 is to the left of box if (p1.x <= 0) { // intersection with left boundary double t = p1.x/(p1.x - p2.x); double yint = p1.y + t*(p2.y - p1.y); double xint = 0; if (yint < 0) { // actually need intersection with lower boundary t = p1.y/(p1.y - p2.y); xint = p1.x + t*(p2.x - p1.x); yint = 0; } else if (yint > 1) { // actually need intersection with upper boundary t = (1-p1.y)/(p2.y - p1.y); xint = p1.x + t*(p2.x - p1.x); yint = 1; } return point(xint, yint); } // p1 is to the right of box if (p1.x >= 1) { // intersection with right boundary double t = (1 - p1.x)/(p2.x - p1.x); double yint = p1.y + t*(p2.y - p1.y); double xint = 1; if (yint < 0) { // actually need intersection with lower boundary t = p1.y/(p1.y - p2.y); xint = p1.x + t*(p2.x - p1.x); yint = 0; } else if (yint > 1) { // actually need intersection with upper boundary t = (1-p1.y)/(p2.y - p1.y); xint = p1.x + t*(p2.x - p1.x); yint = 1; } return point(xint, yint); } // directly below if (p1.y <= 0) { // intersection with lower boundary double t = p1.y/(p1.y - p2.y); double xint = p1.x + t*(p2.x - p1.x); double yint = 0; return point(xint, yint); } // intersection with upper boundary double t = (1-p1.y)/(p2.y - p1.y); double xint = p1.x + t*(p2.x - p1.x); double yint = 1; return point(xint, yint); } // calculates the two intersection points ip1 and ip2 (if they exist) of a // line segment from p1 to p2 and the unit box, assuming both p1 and p2 // are outside the box. if the assumption isn't true, results are not reliable. // returns false if no intersection exists. bool double_intersection(const point &p1, const point &p2, point &ip1, point &ip2) { double dx = p2.x - p1.x; double dy = p2.y - p1.y; if (dx == 0) { if (dy == 0) return false; // degenerate case, should never get here // vertical line // trivial cases have been excluded by calling function, therefore this is easy ip1.x = p1.x; ip2.x = p1.x; if (p1.y >= 1) { ip1.y = 1; ip2.y = 0; } else { ip1.y = 0; ip2.y = 1; } return true; } else if (dy == 0) { // horizontal line // trivial cases have been exlcuded by calling function, therefore this is easy ip1.y = p1.y; ip2.y = p1.y; if (p1.x >= 1) { ip1.x = 1; ip2.x = 0; } else { ip1.x = 0; ip2.x = 1; } return true; } else { // in the general case, we need to calculate the intersection points with all four edges // we start at the top and go around clockwise, top, right, bottom, left double t[4]; // linear parameters defining intersection points int b[4]; // integer to keep track of boundaries t[0] = (1 - p1.y)/dy; // top b[0] = 0; t[1] = (1 - p1.x)/dx; // right b[1] = 1; t[2] = -1*p1.y/dy; // bottom b[2] = 2; t[3] = -1*p1.x/dx; // left b[3] = 3; // now we need to sort the t values, we don't need // a complex algorithm here since it's so few cases for (int i = 1; i < 4; i++) { // find minimum if (t[0] > t[i]) { double temp = t[0]; t[0] = t[i]; t[i] = temp; int btemp = b[0]; b[0] = b[i]; b[i] = btemp; } } for (int i = 2; i < 4; i++) { // find next larger value if (t[1] > t[i]) { double temp = t[1]; t[1] = t[i]; t[i] = temp; int btemp = b[1]; b[1] = b[i]; b[i] = btemp; } } for (int i = 3; i < 4; i++) { // find next larger value if (t[2] > t[i]) { double temp = t[1]; t[2] = t[i]; t[i] = temp; int btemp = b[2]; b[2] = b[i]; b[i] = btemp; } } // t[1] and t[2] are the two inner-most intersections, which // define the two clipping points bool result = true; switch(b[1]) { case 0: // top ip1 = point(p1.x + t[1]*dx, 1); if (ip1.x < -1e-10 || ip1.x > 1+1e-10) result = false; break; case 1: // right ip1 = point(1, p1.y + t[1]*dy); if (ip1.y < -1e-10 || ip1.y > 1+1e-10) result = false; break; case 2: // bottom ip1 = point(p1.x + t[1]*dx, 0); if (ip1.x < -1e-10 || ip1.x > 1+1e-10) result = false; break; case 3: // left ip1 = point(0, p1.y + t[1]*dy); if (ip1.y < -1e-10 || ip1.y > 1+1e-10) result = false; break; default: // should never go here result = false; } switch(b[2]) { case 0: // top ip2 = point(p1.x + t[2]*dx, 1); if (ip2.x < -1e-10 || ip2.x > 1+1e-10) result = false; break; case 1: // right ip2 = point(1, p1.y + t[2]*dy); if (ip2.y < -1e-10 || ip2.y > 1+1e-10) result = false; break; case 2: // bottom ip2 = point(p1.x + t[2]*dx, 0); if (ip2.x < -1e-10 || ip2.x > 1+1e-10) result = false; break; case 3: // left ip2 = point(0, p1.y + t[2]*dy); if (ip2.y < -1e-10 || ip2.y > 1+1e-10) result = false; break; default: // should never go here result = false; } return result; } } segment_crop_type crop_to_unit_box(const point &p1, const point &p2, point &crop1, point &crop2) { // trivial case 1: line segment trivially outside box if ((p1.x <= 0 && p2.x <= 0) || (p1.x >= 1 && p2.x >= 1) || (p1.y <= 0 && p2.y <= 0) || (p1.y >= 1 && p2.y >= 1)) { return none; } bool p1_inside = p1.x > 0 && p1.x < 1 && p1.y > 0 && p1.y < 1; bool p2_inside = p2.x > 0 && p2.x < 1 && p2.y > 0 && p2.y < 1; if (p1_inside) { // trivial case 2: line segment fully inside box if (p2_inside) { return complete; } // otherwise, simple case 1: crop at beginning crop1 = entry_intersection(p2, p1); return at_beginning; } if (p2_inside) { // simple case 2: crop at end crop1 = entry_intersection(p1, p2); return at_end; } // final case is double intersection in middle or no intersection at all bool crop = double_intersection(p1, p2, crop1, crop2); if (crop) return in_middle; return none; } // helper function for crop_lines(); checks whether a single point is inside the unit box bool in_unit_box(const point &p) { if (p.x > 0 && p.x < 1 && p.y > 0 && p.y < 1) return true; return false; } // helper function for crop_lines() void record_points(vector &x_out, vector &y_out, vector &id_out, const point &p1, const point &p2, int &cur_id_out, bool &p1_recorded, bool &p2_recorded, bool &new_line_segment) { if (new_line_segment) { // start a new line segment, but defer if nothing to record if (!p1_recorded || !p2_recorded) { cur_id_out++; new_line_segment = false; } } if (!p1_recorded) { x_out.push_back(p1.x); y_out.push_back(p1.y); id_out.push_back(cur_id_out); p1_recorded = true; } if (!p2_recorded) { x_out.push_back(p2.x); y_out.push_back(p2.y); id_out.push_back(cur_id_out); p2_recorded = true; } } // Clip lines to the outside of a box // // Clip lines to the outside of a box. The box is specified via midpoint, width, // height, and a rotation angle in radians. This is used to create space within // isolines for text labels or other annotations. // // @param x Numeric vector of x coordinates // @param y Numeric vector of y coordinates // @param id Integer vector of id numbers indicating which lines are connected // @param p_mid_x,p_mid_y Numeric values specifying the x and y position of the box midpoint // @param width Box width // @param height Box height // @param theta Box angle, in radians // @param asp Aspect ratio (width/height) of the target canvas. This is used to convert widths // to heights and vice versa for rotated boxes // @export extern "C" SEXP clip_lines_impl(SEXP x, SEXP y, SEXP id, SEXP _p_mid_x, SEXP _p_mid_y, SEXP _width, SEXP _height, SEXP _theta, SEXP _asp) { BEGIN_CPP // input int n = Rf_length(x); double* x_p = REAL(x); double* y_p = REAL(y); int* id_p = INTEGER(id); double p_mid_x = REAL(_p_mid_x)[0]; double p_mid_y = REAL(_p_mid_y)[0]; double width = REAL(_width)[0]; double height = REAL(_height)[0]; double theta = REAL(_theta)[0]; double asp = REAL(_asp)[0]; // output variable SEXP res = PROTECT(Rf_allocVector(VECSXP, 3)); SEXP names = PROTECT(Rf_allocVector(STRSXP, 3)); SET_STRING_ELT(names, 0, Rf_mkChar("x")); SET_STRING_ELT(names, 1, Rf_mkChar("y")); SET_STRING_ELT(names, 2, Rf_mkChar("id")); Rf_setAttrib(res, Rf_install("names"), names); vector x_out, y_out; vector id_out; // input checks if (n != Rf_length(y)) { Rf_error("Number of x and y coordinates must match."); } if (n != Rf_length(id)) { Rf_error("Number of x coordinates and id values must match."); } if (n == 0) { // empty input, return empty output SET_VECTOR_ELT(res, 0, Rf_allocVector(REALSXP, 0)); SET_VECTOR_ELT(res, 1, Rf_allocVector(REALSXP, 0)); SET_VECTOR_ELT(res, 2, Rf_allocVector(INTSXP, 0)); UNPROTECT(2); return res; } // set up transformation // lower left point of cropping rectangle point ll(p_mid_x - width*cos(theta)/2 + (height/asp)*sin(theta)/2, p_mid_y - asp*width*sin(theta)/2 - height*cos(theta)/2); // lower right point point lr(ll.x + width*cos(theta), ll.y + asp*width*sin(theta)); // upper left point point ul(ll.x - (height/asp)*sin(theta), ll.y + height*cos(theta)); unitbox_transformer t(ll, lr, ul); // crop int cur_id = id_p[0]; int cur_id_out = 0; // first output id - 1 point p1, p2, p1t, p2t; point crop1, crop2; p1 = point(x_p[0], y_p[0]); p1t = t.transform(p1); bool p1_recorded = in_unit_box(p1t); // record only if not in unit box, catches singlets bool p2_recorded = true; // when we first enter the loop, have only p1 unrecorded bool new_line_segment = true; int i = 1; while(i < n) { if (cur_id != id_p[i]) { // id mismatch means we are starting a new line segment // first record any points that haven't been recorded yet. catches singlets record_points(x_out, y_out, id_out, p1, p2, cur_id_out, p1_recorded, p2_recorded, new_line_segment); // now set up next line segment p1 = point(x_p[i], y_p[i]); p1t = t.transform(p1); cur_id = id_p[i]; p1_recorded = in_unit_box(p1t); // record only if not in unit box, catches singlets new_line_segment = true; i++; continue; } p2 = point(x_p[i], y_p[i]); p2t = t.transform(p2); p2_recorded = false; segment_crop_type result = crop_to_unit_box(p1t, p2t, crop1, crop2); switch(result) { case complete: // skip recording for this line segment p1_recorded = true; p2_recorded = true; // start new line segment with next point new_line_segment = true; break; case at_beginning: p1t = crop1; p1 = t.inv_transform(p1t); p1_recorded = false; new_line_segment = true; break; case at_end: p2_recorded = false; record_points(x_out, y_out, id_out, p1, t.inv_transform(crop1), cur_id_out, p1_recorded, p2_recorded, new_line_segment); new_line_segment = true; break; case in_middle: p2_recorded = false; record_points(x_out, y_out, id_out, p1, t.inv_transform(crop1), cur_id_out, p1_recorded, p2_recorded, new_line_segment); p1t = crop2; p1 = t.inv_transform(p1t); p1_recorded = false; p2_recorded = false; new_line_segment = true; break; default: // nothing to be done, record and move on break; } record_points(x_out, y_out, id_out, p1, p2, cur_id_out, p1_recorded, p2_recorded, new_line_segment); p1 = p2; p1t = p2t; i++; } // record any remaining points; catches singlets record_points(x_out, y_out, id_out, p1, p2, cur_id_out, p1_recorded, p2_recorded, new_line_segment); int final_size = x_out.size(); SEXP x_final = SET_VECTOR_ELT(res, 0, Rf_allocVector(REALSXP, final_size)); double* x_final_p = REAL(x_final); SEXP y_final = SET_VECTOR_ELT(res, 1, Rf_allocVector(REALSXP, final_size)); double* y_final_p = REAL(y_final); SEXP id_final = SET_VECTOR_ELT(res, 2, Rf_allocVector(INTSXP, final_size)); int* id_final_p = INTEGER(id_final); for (int i = 0; i < final_size; ++i) { x_final_p[i] = x_out[i]; y_final_p[i] = y_out[i]; id_final_p[i] = id_out[i]; } UNPROTECT(2); return res; END_CPP } /*** R x <- c(0, 0, 1, 1, 0, 2, 3, 2.5, 2) y <- c(0, 1, 1, 0, 0, 2, 2, 3, 2) id <- c(1, 1, 1, 1, 1, 2, 2, 2, 2) out <- clip_lines_impl(x, y, id, 1.5, 1.5, 2.5, 1, pi/4) grid.newpage() grid.polyline(x = out$x/5, y = out$y/5, id = out$id) */ isoband/vignettes/0000755000176200001440000000000014073124635013706 5ustar liggesusersisoband/vignettes/isoband1.Rmd0000644000176200001440000001113614070653241016051 0ustar liggesusers--- title: "Generating isolines and isobands" author: "Claus O. Wilke" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Generating isolines and isobands} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` The isoband package implements fast algorithms for generating isolines (lines of equal elevation) and isobands (ranges of elevation delimited by two isolines) from a matrix of elevation data. For both cases, the package employs the marching squares algorithms as described on [Wikipedia.](https://en.wikipedia.org/wiki/Marching_squares) Marching squares algorithms break down the elevation matrix into blocks of 2x2 elevation values. For each block, they then determine the appropriate isolines/isobands from a lookup table of all possible arrangements of isolines or isobands within a 2x2 block. There are 16 distinct possibilities for isolines and 81 for isobands. The implementation in the isoband package goes beyond the algorithm described on Wikipedia in that it merges the isolines or isobands from separate blocks into extended line traces or polygons. The package is meant as a low-level package with minimal required dependencies. Therefore, many of the functions provided may not immediately be useful to endusers, but they will enable developers of other packages to integrate isolines and isobands into their feature set. The two main functions of the package are called `isolines()` and `isobands()`, and they have similar user interfaces and return values. Both take a vector `x` specifying the x values corresponding to the columns of the elevation matrix, a vector `y` specifying the y values corresponding to the rows of the elevation matrix, and an elevation matrix `z`. The two functions differ in that `isolines()` takes a single argument `levels` specifying the elevation levels for which isolines should be calculated, whereas `isobands()` takes two arguments, `levels_low` and `levels_high`, specifying the lower and upper bounds for each isoband. The return value in both cases is a list of lists. The outer list contains one list element for each specified isolevel. The inner lists hold line or polygon data in the form `x`, `y`, `id` as used by `grid::polylineGrob()` or `grid::pathGrob()`. The format has been chosen for easy drawing of the resulting values via these two grid functions. ```{r} library(isoband) library(grid) m <- matrix( c(0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 1, 2, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0), 5, 5, byrow = TRUE ) lines <- isolines(x = 1:ncol(m)/6, y = nrow(m):1/6, z = m, levels = 0.5) lines grid.newpage() grid.draw(polylineGrob(lines[[1]]$x, lines[[1]]$y, lines[[1]]$id)) bands <- isobands(x = 1:ncol(m)/6, y = nrow(m):1/6, z = m, levels_low = 0.5, levels_high = 1.5) bands grid.newpage() grid.draw(pathGrob(bands[[1]]$x, bands[[1]]$y, bands[[1]]$id, gp = gpar(fill = "cornsilk"))) ``` A convenience function `plot_iso()` can be used to inspect a single isoband and corresponding isolines for an elevation matrix. This function is mostly meant for debugging and illustration purposes. It draws a grid of matrix points colored by whether each point is below, within, or above the isoband, as well as the isoband itself and the enclosing isolines. ```{r} plot_iso(m, 0.5, 1.5) ``` The isoband package handles `NA` values in the matrix by simply ignoring the respective grid points. ```{r} m <- matrix( c(NA, NA, NA, 0, 0, 0, NA, NA, NA, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE ) plot_iso(m, 0.5, 1.5) ``` Isobands can contain holes, as shown above, and they can also consist of multiple disconnected pieces. ```{r} m <- matrix( c(0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0.8, 0), 4, 4, byrow = TRUE ) plot_iso(m, 0.5, 1.5) ``` # Performance The code is written in C++ and performance is generally good. Isolining is about as fast as `grDevices::contourLines()`, isobanding is approximately 2.5 times slower. ```{r} # contouring with contourLines() from grDevices fn_contourLines <- function() { grDevices::contourLines(1:ncol(volcano), 1:nrow(volcano), volcano, levels = 10*(10:18)) } # contouring with isolines() fn_isolines <- function() { isolines(1:ncol(volcano), 1:nrow(volcano), volcano, 10*(10:18)) } # contouring with isobands() fn_isobands <- function() { isobands(1:ncol(volcano), 1:nrow(volcano), volcano, 10*(9:17), 10*(10:18)) } microbenchmark::microbenchmark(fn_contourLines(), fn_isolines(), fn_isobands()) ``` isoband/vignettes/isoband3.Rmd0000644000176200001440000000745714070653241016066 0ustar liggesusers--- title: "Labeled isolines" author: "Claus O. Wilke" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Labeled isolines} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` Labeled isolines can be drawn in the grid graphics system via the `isolines_grob()` function. ```{r} library(isoband) library(grid) x <- (0:(ncol(volcano) - 1))/(ncol(volcano) - 1) y <- ((nrow(volcano) - 1):0)/(nrow(volcano) - 1) lines <- isolines(x, y, volcano, 5*(19:38)) g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ) ) grid.newpage() grid.draw(g) ``` The function places labels at the isolines specified via the `breaks` argument, and it crops isolines around the labels so no lines run into the labels. It is possible to leave extra space around the labels using the `margin` argument. ```{r} g <- isolines_grob( lines, breaks = 20*(5:10), margin = unit(c(3, 5, 3, 5), "pt"), # margin specified as top, right, bottom, left gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ) ) grid.newpage() grid.draw(g) ``` Where the labels are drawn can be controlled via the `label_placer` argument. A few different label placement strategies are provided. The default, `label_placer_minmax()`, places labels at the maximum and minimum y value for each isoline. However, this label placer can be further customized. For example, labels can be placed at maximum and minimum x values and at a fixed rotation angle of 90 degrees. ```{r} g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ), label_placer = label_placer_minmax( placement = "rl", # place labels right and left, i.e., min and max x rot_adjuster = angle_fixed(pi/2) # set fixed angle of 90 degrees ) ) grid.newpage() grid.draw(g) ``` The label placer `label_placer_none()` doesn't place any labels. ```{r} g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ), label_placer = label_placer_none() ) grid.newpage() grid.draw(g) ``` We can also place labels entirely manually by using `label_placer_manual()`. This label placer takes as arguments the breaks that should be labeled and the corresponding coordinates and label angles. Breaks for which no coordinates are specified are ignored. If multiple coordinates are supplied for the same break then multiple labels are placed for that break. ```{r} g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 12, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ), label_col = "red", label_placer = label_placer_manual( breaks = c("120", "160", "160"), x = c(0.15, 0.5, 0.6), y = c(0.19, 0.51, 0.87), theta = 0 ) ) grid.newpage() grid.draw(g) ``` Because isolines are cropped around the labels, the labeling strategy works even if the isolines are drawn on top of a colored background. For example, we could draw filled isobands using `isobands_grob()` and then draw labeled lines on top. ```{r} viridis_pal <- colorRampPalette( c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"), space = "Lab" ) bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39)) b <- isobands_grob( bands, gp = gpar(col = NA, fill = viridis_pal(21), alpha = 0.4) ) l <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ) ) grid.newpage() grid.draw(b) grid.draw(l) ``` isoband/R/0000755000176200001440000000000014070653241012074 5ustar liggesusersisoband/R/catch-routine-registration.R0000644000176200001440000000044113757764407017515 0ustar liggesusers# This dummy function definition is included with the package to ensure that # 'tools::package_native_routine_registration_skeleton()' generates the required # registration info for the 'run_testthat_tests' symbol. (function() { .Call("run_testthat_tests", TRUE, PACKAGE = "isoband") }) isoband/R/isolines-grob.R0000644000176200001440000002314214064403653015000 0ustar liggesusers#' Render labeled isolines #' #' This function generates a grid grob that represents labeled isolines. #' #' @param lines Isolines, as produced by the [`isolines()`] function. #' @param gp Grid graphical parameters. Parameters applying to lines #' (such as `col`, `lwd`, `lty`, etc.) are recycled among the total #' number of lines drawn. Parameters applying only to labels (such #' as `fontfamily`, `fontsize`) are recycled among the specified #' breaks only. The two parameters `col` and `alpha` are also applied #' to labels, unless overridden (see `label_col` and `label_alpha`), #' but are matched to the corresponding lines. #' @param breaks Character vector specifying the isolines that should be #' labeled. If `NULL`, labels all isolines. #' @param labels Character vector specifying the labels for each break. #' If `NULL`, uses the breaks as labels. The number of labels provided #' must match the number of breaks provided. #' @param margin Unit object of length 4 specifying the top, right, bottom, #' and left margins around each text label. The same margins are applied #' to all labels. #' @param label_col Color applied to labels. Can be used to override the #' color provided in `gp`, in case labels and lines should have different #' colors. #' @param label_alpha Alpha applied to labels. Can be used to override the #' alpha value provided in `gp`, in case labels and lines should have #' different alpha values. #' @param label_placer Function that controls how labels are placed along #' the isolines. Uses [`label_placer_minmax()`] by default. #' @param units A character string specifying the units in which to #' interpret the isolines coordinates. Defaults to `"npc"`. #' @seealso #' See [`isobands_grob()`] for drawing of isobands. See [`label_placer_minmax()`] for #' label placement strategies. #' @examples #' library(grid) #' #' viridis_pal <- colorRampPalette( #' c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"), #' space = "Lab" #' ) #' #' x <- (1:ncol(volcano))/(ncol(volcano)+1) #' y <- (nrow(volcano):1)/(nrow(volcano)+1) #' lines <- isolines(x, y, volcano, 5*(19:38)) #' bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39)) #' #' b <- isobands_grob( #' bands, #' gp = gpar(col = NA, fill = viridis_pal(21), alpha = 0.4) #' ) #' l <- isolines_grob( #' lines, breaks = 20*(5:10), #' gp = gpar( #' lwd = c(.3, 1, .3, .3) #' ) #' ) #' #' grid.newpage() #' grid.draw(b) #' grid.draw(l) #' @export isolines_grob <- function(lines, gp = gpar(), breaks = NULL, labels = NULL, margin = unit(c(1, 1, 1, 1), "pt"), label_col = NULL, label_alpha = NULL, label_placer = label_placer_minmax(), units = "npc") { if (is.null(breaks)) { breaks <- names(lines) } else { breaks <- as.character(breaks) } if (is.null(labels)) { labels <- breaks } else if (length(labels) != length(breaks)) { stop("Number of labels must match the number of breaks.", call. = FALSE) } else { labels <- as.character(labels) } if (length(margin) != 4 || !is.unit(margin)) { stop("The `margin` parameter must be a unit object of length four.", call. = FALSE) } # first set up a data frame with all the label info labels_data <- data.frame( index = match(breaks, names(lines)), # index of labeled lines in original list of lines, for matching of graphical parameters break_index = 1:length(breaks), # index into original list of breaks, for matching graphical parameters break_id = breaks, # identifier for each break (corresponds to the name in the lines column) label = labels, # label for each break stringsAsFactors = FALSE ) # then calculate the position of all labels via the `label_placer()` function labels_data <- label_placer(lines, labels_data) gTree( lines = lines, breaks = breaks, labels = labels, labels_data = labels_data, gp_combined = gp, margin = margin, label_col = label_col, label_alpha = label_alpha, label_placer = label_placer, units = units, cl = "isolines_grob" ) } #' @export makeContext.isolines_grob <- function(x) { # store current graphics parameters for later x$gp_cur <- get.gpar() # if we have no labels then nothing else needs to be done if (nrow(x$labels_data) == 0) { return(x) } # we need to set up the gp slot for text labels, so font calculations work gp <- modifyList(x$gp_cur, x$gp_combined) # map graphical parameters to duplicated labels # we only handle font parameters here, to guarantee proper # calculation of label sizes n <- length(x$breaks) cex <- rep_len(gp$cex, n)[x$labels_data$break_index] lineheight <- rep_len(gp$lineheight, n)[x$labels_data$break_index] fontfamily <- rep_len(gp$fontfamily, n)[x$labels_data$break_index] fontsize <- rep_len(gp$fontsize, n)[x$labels_data$break_index] # fontface needs special treatment, since it can be NULL if font # is specified if (is.null(gp$fontface)) { # we work with font font <- rep_len(gp$font, n)[x$labels_data$break_index] x$gp <- gpar( cex = cex, fontsize = fontsize, lineheight = lineheight, fontfamily = fontfamily, font = font ) } else { # we work with fontface fontface <- rep_len(gp$fontface, n)[x$labels_data$break_index] x$gp <- gpar( cex = cex, fontsize = fontsize, lineheight = lineheight, fontfamily = fontfamily, fontface = fontface ) } x } #' @export makeContent.isolines_grob <- function(x) { labels_data <- x$labels_data if (nrow(labels_data) == 0) { # no labels to be drawn, nothing to be done return(isolines_grob_makeContent_nolabels(x)) } # calculate label widths and heights in npc units label_widths <- convertWidth(stringWidth(labels_data$label), x$units, valueOnly = TRUE) label_heights <- convertHeight( stringHeight(labels_data$label) + stringDescent(labels_data$label), x$units, valueOnly = TRUE ) # get viewport aspect ratio to correct clipping for rotated labels asp <- convertHeight(unit(1, "pt"), x$units, valueOnly = TRUE) / convertWidth(unit(1, "pt"), x$units, valueOnly = TRUE) #print(asp) # calculate margins in npc units margin_rl <- convertWidth(x$margin[c(2, 4)], x$units, valueOnly = TRUE) margin_tb <- convertHeight(x$margin[c(1, 3)], x$units, valueOnly = TRUE) margin_w <- sum(margin_rl) margin_h <- sum(margin_tb) margin_wdiff <- margin_rl[2] - margin_rl[1] margin_hdiff <- margin_rl[1] - margin_rl[2] # calculate the clip box for each label # xoff and yoff are needed to correct for uneven label margins xoff <- -margin_wdiff*cos(labels_data$theta)/2 + (margin_hdiff/asp)*sin(labels_data$theta)/2 yoff <- -margin_wdiff*sin(labels_data$theta)/2 - margin_hdiff*cos(labels_data$theta)/2 clip_boxes <- data.frame( x = labels_data$x + xoff, y = labels_data$y + yoff, width = label_widths + margin_w, height = label_heights + margin_h, theta = labels_data$theta ) make_lines_grobs <- function(data, col, alpha, lty, lwd, lex, lineend, linejoin, linemitre) { if (length(data$x) == 0) { return(NULL) } clipped <- clip_lines(data$x, data$y, data$id, clip_boxes, asp = asp) if (length(clipped$x) == 0) { return(NULL) } polylineGrob( clipped$x, clipped$y, clipped$id, default.units = x$units, gp = gpar( col = col, alpha = alpha, lty = lty, lwd = lwd, lex = lex, lineend = lineend, linejoin = linejoin, linemitre = linemitre ) ) } # merge current and grob-specific graphical parameters so we can redistribute among isolevels gp <- modifyList(x$gp_cur, x$gp_combined) n <- length(x$lines) lines_grobs <- mapply( make_lines_grobs, x$lines, rep_len(gp$col, n), rep_len(gp$alpha, n), rep_len(gp$lty, n), rep_len(gp$lwd, n), rep_len(gp$lex, n), rep_len(gp$lineend, n), rep_len(gp$linejoin, n), rep_len(gp$linemitre, n), SIMPLIFY = FALSE ) # calculate color and alpha for text labels if (is.null(x$label_col)) { col <- rep_len(gp$col, length(x$lines))[x$labels_data$index] } else { col <- rep_len(x$label_col, length(x$breaks))[x$labels_data$break_index] } if (is.null(x$label_alpha)) { # alpha is cumulative, so we don't take it from the current viewport alpha <- rep_len(x$gp_combined$alpha %||% 1, length(x$lines))[x$labels_data$index] } else { alpha <- rep_len(x$label_alpha, n)[x$labels_data$break_index] } labels_grob <- textGrob( labels_data$label, labels_data$x, labels_data$y, rot = 360*labels_data$theta/(2*pi), default.units = x$units, gp = gpar(col = col, alpha = alpha) ) children <- do.call(gList, c(lines_grobs, list(labels_grob))) setChildren(x, children) } isolines_grob_makeContent_nolabels <- function(x) { make_lines_grobs <- function(data, col, alpha, lty, lwd, lex, lineend, linejoin, linemitre) { if (length(data$x) == 0) { return(NULL) } polylineGrob( data$x, data$y, data$id, default.units = x$units, gp = gpar( col = col, alpha = alpha, lty = lty, lwd = lwd, lex = lex, lineend = lineend, linejoin = linejoin, linemitre = linemitre ) ) } # merge current and grob-specific graphical parameters so we can redistribute among isolevels gp <- modifyList(x$gp_cur, x$gp_combined) n <- length(x$lines) lines_grobs <- mapply( make_lines_grobs, x$lines, rep_len(gp$col, n), rep_len(gp$alpha, n), rep_len(gp$lty, n), rep_len(gp$lwd, n), rep_len(gp$lex, n), rep_len(gp$lineend, n), rep_len(gp$linejoin, n), rep_len(gp$linemitre, n), SIMPLIFY = FALSE ) children <- do.call(gList, lines_grobs) setChildren(x, children) } isoband/R/isobands-grob.R0000644000176200001440000000411313501223536014745 0ustar liggesusers#' Render isobands #' #' This function generates a grid grob that represents isobands. #' #' @param bands Isobands, as produced by the [`isobands()`] function. #' @param gp Grid graphical parameters. Parameters are recycled among #' the total number of bands drawn. #' @param units A character string specifying the units in which to #' interpret the isobands coordinates. Defaults to `"npc"`. #' @seealso #' See [`isolines_grob()`] for drawing of isolines. #' @examples #' library(grid) #' #' viridis_pal <- colorRampPalette( #' c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"), #' space = "Lab" #' ) #' #' x <- (1:ncol(volcano))/(ncol(volcano)+1) #' y <- (nrow(volcano):1)/(nrow(volcano)+1) #' bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39)) #' #' b <- isobands_grob( #' bands, #' gp = gpar(col = "black", fill = viridis_pal(21), alpha = 0.5) #' ) #' #' grid.newpage() #' grid.draw(b) #' @export isobands_grob <- function(bands, gp = gpar(), units = "npc") { gTree( bands = bands, gp_user = gp, units = units, cl = "isobands_grob" ) } #' @export makeContent.isobands_grob <- function(x) { make_bands_grobs <- function(data, col, fill, alpha, lty, lwd, lex, lineend, linejoin, linemitre) { if (length(data$x) == 0) { return(NULL) } pathGrob( data$x, data$y, data$id, default.units = x$units, gp = gpar( col = col, fill = fill, alpha = alpha, lty = lty, lwd = lwd, lex = lex, lineend = lineend, linejoin = linejoin, linemitre = linemitre ) ) } # merge current and grob-specific graphical parameters so we can redistribute among isolevels gp <- modifyList(get.gpar(), x$gp_user) n <- length(x$bands) bands_grobs <- mapply( make_bands_grobs, x$bands, rep_len(gp$col, n), rep_len(gp$fill, n), rep_len(gp$alpha, n), rep_len(gp$lty, n), rep_len(gp$lwd, n), rep_len(gp$lex, n), rep_len(gp$lineend, n), rep_len(gp$linejoin, n), rep_len(gp$linemitre, n), SIMPLIFY = FALSE ) children <- do.call(gList, bands_grobs) setChildren(x, children) } isoband/R/utilities.R0000644000176200001440000000064213673436547014254 0ustar liggesusers# convenience operator, don't want to import rlang just for this "%||%" <- function(x, y) { if (is.null(x)) { y } else { x } } # evaluates all arguments # (simpler than forcing each argument individually) force_all <- function(...) list(...) rethrow_interrupt <- function() { interrupt <- structure(list(), class = c("interrupt", "condition")) signalCondition(interrupt) invokeRestart("abort") } isoband/R/plot-iso.R0000644000176200001440000000430413501223536013764 0ustar liggesusers#' Visualize a single isoband #' #' This function visualizes a single isoband calculated from a matrix. It is mainly useful #' for debugging and visualizing the isobanding algorithm. See [`isobands()`] for more #' examples. #' #' @param m input matrix #' @param vlo lower cutoff for isobanding #' @param vhi higher cutoff for isobanding #' @param col_lo line color for lower cutoff #' @param col_hi line color for higher cutoff #' @param fill_lo fill color for points below the lower cutoff #' @param fill_mid fill color for points between the two cutoffs #' @param fill_hi fill color for points above the higher cutoff #' @param fill_band fill color for the isoband #' @param newpage boolean, indicating whether `grid.newpage()` should #' be called or not #' @examples #' m <- matrix(c(0, 0, 0, 0, 0, 0, #' 0, 2, 2, 2, 2, 0, #' 0, 2, 0, 0, 2, 0, #' 0, 2, 0, 0, 2, 0, #' 0, 2, 2, 2, 2, 0, #' 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) #' #' plot_iso(m, 0.5, 1.5) #' @export plot_iso <- function(m, vlo, vhi, fill_lo = "gray95", fill_mid = "gray50", fill_hi = "black", fill_band = "cornsilk", col_lo = "black", col_hi = "black", newpage = TRUE) { x <- 0.05 + 0.9*(0:(ncol(m)-1))/(ncol(m)-1) y <- 0.05 + 0.9*((nrow(m)-1):0)/(nrow(m)-1) df_bands <- isobands(x, y, m, vlo, vhi)[[1]] df_lines_lo <- isolines(x, y, m, vlo)[[1]] df_lines_hi <- isolines(x, y, m, vhi)[[1]] df_points <- expand.grid(y = y, x = x) pfill <- c(ifelse(m < vlo, fill_lo, ifelse(m < vhi, fill_mid, fill_hi))) pcol <- c(ifelse(m < vlo, "black", ifelse(m < vhi, fill_mid, "black"))) if (isTRUE(newpage)) grid.newpage() if (length(df_bands$x) > 0) grid.path(df_bands$x, df_bands$y, df_bands$id, gp = gpar(fill = fill_band, col = NA)) if (length(df_lines_lo$x) > 0) grid.polyline(df_lines_lo$x, df_lines_lo$y, df_lines_lo$id, gp = gpar(col = col_lo)) if (length(df_lines_hi$x) > 0) grid.polyline(df_lines_hi$x, df_lines_hi$y, df_lines_hi$id, gp = gpar(col = col_hi)) grid.points(df_points$x, df_points$y, default.units = "npc", pch = 21, size = unit(0.5, "char"), gp = gpar(fill = pfill, col = pcol)) } isoband/R/isobands.R0000644000176200001440000000775013673436547014052 0ustar liggesusers#' Efficient calculation of isolines and isobands from elevation grid #' #' @param x Numeric vector specifying the x locations of the grid points. #' @param y Numeric vector specifying the y locations of the grid points. #' @param z Numeric matrix specifying the elevation values for each grid point. #' @param levels_low,levels_high Numeric vectors of minimum/maximum z values #' for which isobands should be generated. Any z values that are exactly #' equal to a value in `levels_low` are considered part of the corresponding #' isoband, but any z values that are exactly equal to a value in `levels_high` #' are not considered part of the corresponding isoband. In other words, the #' intervals specifying isobands are closed at their lower boundary and open #' at their upper boundary. #' @seealso #' [`plot_iso`] #' @examples #' library(grid) #' #' #' # one simple connected shape #' m <- matrix(c(0, 0, 0, 0, 0, 0, #' 0, 0, 0, 1, 1, 0, #' 0, 0, 1, 1, 1, 0, #' 0, 1, 1, 0, 0, 0, #' 0, 0, 0, 1, 0, 0, #' 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) #' #' df_bands <- isobands((1:ncol(m))/(ncol(m)+1), (nrow(m):1)/(nrow(m)+1), m, 0.5, 1.5)[[1]] #' df_lines <- isolines((1:ncol(m))/(ncol(m)+1), (nrow(m):1)/(nrow(m)+1), m, 0.5)[[1]] #' g <- expand.grid(x = (1:ncol(m))/(ncol(m)+1), y = (nrow(m):1)/(nrow(m)+1)) #' grid.newpage() #' grid.points(g$x, g$y, default.units = "npc", pch = 19, size = unit(0.5, "char")) #' grid.path(df_bands$x, df_bands$y, df_bands$id, gp = gpar(fill = "cornsilk", col = NA)) #' grid.polyline(df_lines$x, df_lines$y, df_lines$id) #' #' # a similar plot can be generated with the plot_iso() function, #' # which is useful for exploring how the algorithm works #' plot_iso(m, 0.5, 1.5) #' #' # NAs are ignored #' m <- matrix(c(NA, NA, NA, 0, 0, 0, #' NA, NA, NA, 1, 1, 0, #' 0, 0, 1, 1, 1, 0, #' 0, 1, 1, 0, 0, 0, #' 0, 0, 0, 1, 0, 0, #' 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) #' plot_iso(m, 0.5, 1.5) #' #' # two separate shapes #' m <- matrix(c(0, 0, 1, 1, #' 0, 1, 1, 1, #' 1, 1, 0, 0, #' 0, 0, 0.8, 0), 4, 4, byrow = TRUE) #' plot_iso(m, 0.5, 1.5) #' #' # shape with hole #' m <- matrix(c(0, 0, 0, 0, 0, 0, #' 0, 1, 1, 1, 1, 0, #' 0, 1, 2, 2, 1, 0, #' 0, 1, 2, 2, 1, 0, #' 0, 1, 1, 1, 1, 0, #' 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE) #' plot_iso(m, 0.5, 1.5) #' @export isobands <- function(x, y, z, levels_low, levels_high) { nlow <- length(levels_low) nhigh <- length(levels_high) nmax <- max(nlow, nhigh) if ((nlow != nmax && nlow != 1) || (nhigh != nmax && nhigh != 1)) { stop("Vectors specifying isoband levels must be of equal length or of length 1", call. = FALSE) } levels_low <- rep_len(levels_low, nmax) levels_high <- rep_len(levels_high, nmax) # swap high and low levels when they're given in the wrong order idx <- levels_high < levels_low if (any(idx)) { levels_tmp <- levels_high levels_high[idx] <- levels_low[idx] levels_low[idx] <- levels_tmp[idx] } out <- isobands_impl(x, y, z, levels_low, levels_high) structure( stats::setNames(out, paste0(levels_low, ":", levels_high)), class = c("isobands", "iso") ) } #' @rdname isobands #' @param levels Numeric vector of z values for which isolines should be generated. #' @export isolines <- function(x, y, z, levels) { out <- isolines_impl(x, y, z, levels) structure( stats::setNames(out, levels), class = c("isolines", "iso") ) } isobands_impl <- function(x, y, z, value_low, value_high) { mode(z) <- "numeric" .Call( `isobands_impl_c`, as.numeric(x), as.numeric(y), z, as.numeric(value_low), as.numeric(value_high), PACKAGE = "isoband" ) } isolines_impl <- function(x, y, z, value) { mode(z) <- "numeric" .Call( `isolines_impl_c`, as.numeric(x), as.numeric(y), z, as.numeric(value), PACKAGE = "isoband" ) } isoband/R/iso-to-sfg.R0000644000176200001440000000750214070653525014217 0ustar liggesusers#' Convert isolines or isobands to sfg object #' #' Convert isolines or isobands to an sf geometry collection (`sfg`) object. Further downstream #' processing needs to happen via the sf package. #' #' The function `iso_to_sfg()` is a generic that takes an object created by either [`isolines()`] #' or [`isobands()`] and turns it into a simple features (sf) geometry collection. Importantly, #' the isobanding algorithm can produce polygons that do not represent valid simple features. This #' happens usually when the lower limit of an isoband is exactly equal to some data values (see #' examples for a demonstration). This can be worked around either by slightly shifting the data #' or band limits (e.g., round all data values and then shift them by a value smaller than the #' rounding error) or by fixing the geometries using the function `st_make_valid()`. #' @param x The object to convert. #' @examples #' if (requireNamespace("sf", quietly = TRUE)) { #' library(sf) #' library(ggplot2) #' #' # Example 1: simple 5x5 matrix #' m <- matrix(c(0, 2, 2, 2, 0, #' 0, 1, 0, 1, 0, #' 0, 1, 0, 0, 0, #' 0, 1, 0, 1, 0, #' 0, 0, 0, 0, 0), 5, 5, byrow = TRUE) #' #' z <- isolines(1:ncol(m), nrow(m):1, m, c(0.5, 1.5)) #' lines <- iso_to_sfg(z) #' x <- st_sf(level = names(lines), geometry = st_sfc(lines)) #' ggplot(x) + geom_sf(aes(color = level)) #' #' # Example 2: volcano dataset #' m <- volcano #' b <- isobands((1:ncol(m))/(ncol(m)+1), (nrow(m):1)/(nrow(m)+1), m, #' 10*9:19, 10*10:20) #' bands <- iso_to_sfg(b) #' x <- st_sf(level = as.numeric(sub(":.*", "", names(bands))), geometry = st_sfc(bands)) #' ggplot(x) + geom_sf(aes(color = level, fill = level)) #' #' # Example 3: invalid simple features #' m <- matrix(c(1.5, 1.5, 1.5, 1.5, 0.6, #' 0.5, 1.5, 1.5, 0, 0, #' 0, 1, 0, 1, 1, #' 0, 1, 0, 0.7, 0, #' 0.9, 1.3, 1.8, 1.4, 0.4), 5, 5, byrow = TRUE) #' #' raw <- isobands(1:5, 5:1, m, levels_low = 0:1, levels_high = 1:2) #' bands <- iso_to_sfg(raw) #' #' iso <- st_sf( #' id = factor(1:length(bands)), #' geometry = st_sfc(bands) #' ) #' #' # the geometries are not valid #' st_is_valid(iso, reason = TRUE) #' # this doesn't prevent us from plotting them #' ggplot(iso, aes(fill = id)) + geom_sf() #' #' # make all geometries valid, requires GEOS >= 3.8.0 #' if (sf_extSoftVersion()["GEOS"] >= "3.8.0") { #' iso2 <- st_make_valid(iso) #' st_is_valid(iso2, reason=TRUE) #' # the plot should be unchanged #' ggplot(iso2, aes(fill = id)) + geom_sf() #' } #' #' # alternatively, if we shift all data values by a tiny #' # amount (here, 1e-10) so they don't coincide with the band #' # limits, no invalid geometries are generated. #' raw <- isobands(1:5, 5:1, m + 1e-10, levels_low = 0:1, levels_high = 1:2) #' bands <- iso_to_sfg(raw) #' iso <- st_sf(id = factor(1:length(bands)), geometry = st_sfc(bands)) #' st_is_valid(iso, reason = TRUE) #' } #' @export iso_to_sfg <- function(x) { UseMethod("iso_to_sfg", x) } #' @export iso_to_sfg.default <- function(x) { stop( "Cannot convert objects of type ", paste(class(x), collapse = "/"), " to sf.", call. = FALSE ) } #' @export iso_to_sfg.isolines <- function(x) { mapply(multilinestring, x, SIMPLIFY = FALSE) } multilinestring <- function(object) { x <- split(object$x, object$id) y <- split(object$y, object$id) structure( unname(mapply(cbind, x, y, SIMPLIFY = FALSE)), class = c("XY", "MULTILINESTRING", "sfg") ) } #' @export iso_to_sfg.isobands <- function(x) { mapply(multipolygon, x, SIMPLIFY = FALSE) } multipolygon <- function(object) { separate_polygons(object$x, object$y, object$id) } separate_polygons <- function(x, y, id) { .Call( `separate_polygons_c`, as.numeric(x), as.numeric(y), as.integer(id), PACKAGE = "isoband" ) } isoband/R/clip-lines.R0000644000176200001440000000271113673436547014277 0ustar liggesusers#' Clip lines so they don't run into a set of boxes. #' #' Clip lines so they don't run into a set of boxes. Useful for labeling isolines, #' as it allows removal of line segments that would run into any text labels. #' @param x Numeric vector of x coordinates #' @param y Numeric vector of y coordinates #' @param id Integer vector of id numbers indicating which lines are connected #' @param clip_boxes Data frame specifying the locations of boxes to clip to. #' Should have five columns, named `x`, `y`, `width`, `height`, `theta`, which #' specify the x and y positions of each box midpoint, as well as the box width, #' box height, and box angle in radians. Each box is specified by one data #' row. #' @param asp Aspect ratio (width/height) of the target canvas. This is used to convert #' widths to heights and vice versa for rotated boxes #' @export clip_lines <- function(x, y, id, clip_boxes, asp = 1) { out = list(x = x, y = y, id = id) for (i in 1:nrow(clip_boxes)) { box <- clip_boxes[i, ] out <- clip_lines_impl( out$x, out$y, out$id, box$x, box$y, box$width, box$height, box$theta, asp ) } out } clip_lines_impl <- function(x, y, id, p_mid_x, p_mid_y, width, height, theta, asp = 1) { .Call( `clip_lines_impl_c`, as.numeric(x), as.numeric(y), as.integer(id), as.numeric(p_mid_x), as.numeric(p_mid_y), as.numeric(width), as.numeric(height), as.numeric(theta), as.numeric(asp), PACKAGE = "isoband" ) } isoband/R/label-placer.R0000644000176200001440000002222114064403653014544 0ustar liggesusers#' Generic label placement function #' #' The simple label placer processes separate isolines independently and places #' labels for each line using a placer function that does the actual placement work. #' This label placer is not meant to be used by end users, but rather facilitates the #' development of new label placers, such as [`label_placer_minmax()`]. #' @param lines Isolines object for which labels should be placed. #' @param labels_data A data frame containing information about which labels should #' be placed. #' @param placer_fun A function that takes an individual isoline plus its associated #' break id as input and returns a data frame specifying label positions. The data #' frame should have three columns called `x`, `y`, and `theta`. `x` and `y` specify #' the label position, and `theta` specifies the label angle in radians. The data #' frame can have multiple rows, which results in the same label being placed in #' multiple locations. #' @keywords internal #' @export label_placer_simple <- function(lines, labels_data, placer_fun) { # Calculate the label position for one set of isolines (one level). # # The line data is specified as a list of x, y, id. The parameters `index`, `break_index`, # `break_id`, and `label` are provided simply so they can be added to the resulting data # frame holding label positions place_labels_impl <- function(line_data, index, break_index, break_id, label, placer_fun) { # return empty row if either missing line data or missing label if (length(line_data$x) == 0 || is.na(label)) { return( data.frame( index = integer(0), break_index = integer(0), break_id = character(0), label = character(0), x = numeric(0), y = numeric(0), theta = numeric(0), stringsAsFactors = FALSE ) ) } # calculate label position pos <- placer_fun(line_data, break_id) # return results if (nrow(pos) > 0) { data.frame( index = index, break_index = break_index, break_id = break_id, label = label, x = pos$x, y = pos$y, theta = pos$theta, stringsAsFactors = FALSE ) } else { data.frame( index = integer(0), break_index = integer(0), break_id = character(0), label = character(0), x = numeric(0), y = numeric(0), theta = numeric(0), stringsAsFactors = FALSE ) } } rows <- mapply( place_labels_impl, lines[labels_data$index], labels_data$index, # index of labeled lines in original list of lines, for matching of graphical parameters labels_data$break_index, # index into original list of breaks, for matching graphical parameters labels_data$break_id, labels_data$label, MoreArgs = list(placer_fun = placer_fun), SIMPLIFY = FALSE ) Reduce(rbind, rows) } #' Set up a label placement strategy #' #' These functions set up various label placement strategies. #' #' `label_placer_minmax()` places labels at the horizontal or vertical minima or maxima of #' the respective isolines. #' #' `label_placer_none()` places no labels at all. #' #' `label_placer_manual()` places labels at manually defined locations. #' #' `label_placer_middle()` places labels at the middle of each isoline. #' #' @param placement String consisting of any combination of the letters #' "t", "r", "b", "l" indicating the placement of labels at the top, #' to the right, at the bottom, to the left of the isoline. #' @param rot_adjuster Function that standardizes the rotation angles of the labels. #' See e.g. [`angle_halfcircle_bottom()`]. #' @param n Size of the point neighborhood over which the rotation angle should be #' calculated. #' @rdname label_placer #' @export label_placer_minmax <- function(placement = "tb", rot_adjuster = angle_halfcircle_bottom(), n = 2) { force_all(placement, rot_adjuster, n) placer_fun <- function(line_data, ...) { # find location for labels idx <- stats::na.omit( c( which( # placement "top" isTRUE(grepl("t", placement, fixed = TRUE)) & line_data$y == max(line_data$y) )[1], which( # placement "bottom" isTRUE(grepl("b", placement, fixed = TRUE)) & line_data$y == min(line_data$y) )[1], which( # placement "left" isTRUE(grepl("l", placement, fixed = TRUE)) & line_data$x == min(line_data$x) )[1], which( # placement "right" isTRUE(grepl("r", placement, fixed = TRUE)) & line_data$x == max(line_data$x) )[1] ) ) out <- data.frame(x = numeric(0), y = numeric(0), theta = numeric(0)) for (i in seq_along(idx)) { out[i, ] <- minmax_impl(line_data, idx[i], n) } # standardize rotation angles for text labels out$theta <- rot_adjuster(out$theta) out } # final placer function function(lines, labels_data) { label_placer_simple(lines, labels_data, placer_fun) } } # function that does all the work for the minmax label placer. # requires a single index idx minmax_impl <- function(data, idx, n) { # set of indices belonging to this label idx_set <- which(data$id == data$id[idx]) idx_min <- min(idx_set) idx_max <- max(idx_set) # if the first and the last point are the same we wrap, otherwise we truncate if (data$x[idx_min] == data$x[idx_max] && data$y[idx_min] == data$y[idx_max]) { idx_range <- (idx_max - idx_min) i <- ((idx - n):(idx + n)-idx_min) %% idx_range + idx_min } else { i <- (max(idx - n, idx_min):min(idx + n, idx_max)) } x <- data$x[i] y <- data$y[i] xave <- mean(x) yave <- mean(y) m <- cbind(x - xave, y - yave) v <- svd(m)$v list(x = xave, y = yave, theta = atan2(v[2], v[1])) } #' @rdname label_placer #' @export label_placer_none <- function() { function(...) { data.frame( index = integer(0), break_index = integer(0), break_id = character(0), label = character(0), x = numeric(0), y = numeric(0), theta = numeric(0), stringsAsFactors = FALSE ) } } #' @param breaks Character vector specifying the isolines to be labeled, #' as in [`isolines_grob()`]. #' @param x,y,theta Numeric vectors specifying the x and y positions and #' angles (in radians) for each label corresponding to each break. #' @rdname label_placer #' @export label_placer_manual <- function(breaks, x, y, theta) { # recycle all inputs to the same length # also has the side effect of forcing them n <- max(length(breaks), length(x), length(y), length(theta)) breaks <- rep_len(breaks, n) x <- rep_len(x, n) y <- rep_len(y, n) theta <- rep_len(theta, n) placer_fun <- function(line_data, break_id) { idx <- (breaks == break_id) data.frame(x = x[idx], y = y[idx], theta = theta[idx]) } # final placer function function(lines, labels_data) { label_placer_simple(lines, labels_data, placer_fun) } } #' @rdname label_placer #' @export label_placer_middle <- function(rot_adjuster = angle_halfcircle_bottom()) { placer_fun <- function(line_data, ...) { out <- data.frame(x = numeric(0), y = numeric(0), theta = numeric(0)) # It identifies each isoline subdivision. For an individual isoline the id column identifies the number of subdivisions. line_sections <- unique(line_data$id) # Then the label is printed at the middle of each isoline subdivision. for (i in 1:length(line_sections)) { x <- line_data$x[line_data$id == i] y <- line_data$y[line_data$id == i] middle_index <- as.integer(length(x) / 2) x_mid <- x[middle_index] y_mid <- y[middle_index] xtheta <- c(x[middle_index - 1], x[middle_index], x[middle_index + 1]) ytheta <- c(y[middle_index - 1], y[middle_index], y[middle_index + 1]) m <- cbind(xtheta - mean(xtheta), ytheta - mean(ytheta)) v <- svd(m)$v out[i, ] <- list(x = x_mid, y = y_mid, theta = atan2(v[2], v[1])) } # standardize rotation angles for text labels out$theta <- rot_adjuster(out$theta) out } # final placer function function(lines, labels_data) { label_placer_simple(lines, labels_data, placer_fun) } } #' Standardize label angles #' #' Function factories that return functions to standardize rotation angles to specific angle ranges. #' #' `angle_halfcircle_bottom()` standardizes angles to (-pi/2, pi/2]. #' #' `angle_halfcircle_right()` standardizes angles to (0, pi]. #' #' `angle_fixed()` sets all angles to a fixed value (0 by default). #' #' `angle_identity()` does not modify any angles. #' @param theta Fixed angle, in radians. #' @export angle_halfcircle_bottom <- function() { function(theta) { ifelse( theta <= -pi/2, theta + pi, ifelse( theta > pi/2, theta - pi, theta ) ) } } #' @rdname angle_halfcircle_bottom #' @export angle_halfcircle_right <- function() { function(theta) { ifelse( theta <= 0, theta + pi, ifelse( theta > pi, theta - pi, theta ) ) } } #' @rdname angle_halfcircle_bottom #' @export angle_fixed <- function(theta = 0) { force(theta) function(x) { rep_len(theta, length(x)) } } #' @rdname angle_halfcircle_bottom #' @export angle_identity <- function() { function(x) { x } } isoband/R/isoband.R0000644000176200001440000000014713673436547013660 0ustar liggesusers#' @useDynLib isoband, .registration = TRUE #' @importFrom utils modifyList #' @import grid "_PACKAGE" isoband/NEWS.md0000644000176200001440000000207114073117525012774 0ustar liggesusersisoband 0.2.5 ---------------------------------------- - Add a new label placer function `label_placer_middle()` (#24, @jamarav). - The vendored testthat/catch code now uses a constant value for the stack size rather than relying on `SIGSTKSZ`. See: https://github.com/r-lib/testthat/issues/1373 isoband 0.2.4 ---------------------------------------- - Remove testthat compile-time dependency. isoband 0.2.3 ---------------------------------------- - Fix build for testthat 3.0. isoband 0.2.2 ---------------------------------------- - Remove Rcpp dependency (#11, @thomasp85). isoband 0.2.1 ---------------------------------------- - Improved clipping algorithm for `clip_lines()`, less likely to experience numerical instabilities. isoband 0.2.0 ---------------------------------------- - Added `isolines_grob()` for drawing labeled isolines via the grid graphics system. A companion function `isobands_grob()` is provided for convenience. - Numerous minor fixes and improvements. isoband 0.1.0 ---------------------------------------- First public release. isoband/MD50000644000176200001440000000653114073215605012211 0ustar liggesuserseec9540b87b604f5c14b23026aa985e7 *DESCRIPTION 8806d406c71a4230d8515037dd4748bd *LICENSE fb61add1195de9314f5090fc02acb36e *NAMESPACE 7a59c3689a2c6e57e6389809d80a8f65 *NEWS.md f8b7db7d324a909b346b4cab761d4f4b *R/catch-routine-registration.R 84d133ce3bec04dfcb71303ec9c0cd83 *R/clip-lines.R 816c40db3e55a6e26414f1111a355e8a *R/iso-to-sfg.R 075853c38e5226953f7389c3ec18f639 *R/isoband.R f173bf6d800d105afcb33ff330f4a566 *R/isobands-grob.R f9d148264133d26ef030e6a6d33fbdf8 *R/isobands.R 0139a701a34a2943bb06014eeed52232 *R/isolines-grob.R a5f7dc5d0ec584389af52d6a42f58dc6 *R/label-placer.R f4c4f7aba207d09f6cc32f1a8bee838f *R/plot-iso.R a0cb01ebd131f55f27d0e18b7cd2bfed *R/utilities.R 61685fa39ffa9004374acaef14e5375a *README.md 22ab88c64d65079b19c314f4e0089abf *build/vignette.rds d3566501ee164ef20dd4f68fe5fe69b4 *inst/doc/isoband1.R f28d414c1c3d815096531bc649a188f8 *inst/doc/isoband1.Rmd b4dc840854a7d44584e2166c3f975abb *inst/doc/isoband1.html 07c51400331f68bb2d7a3546e84ef56c *inst/doc/isoband3.R 40bb5c84837e16e19a854c24663f1e67 *inst/doc/isoband3.Rmd f0e0635e7febc510e8e3cf1072d06dd9 *inst/doc/isoband3.html 4982eca2b1c0f4dba2e771dbcdb217be *inst/extdata/ocean-cat.jpg 936436350c43cd60434d76af8dd4d0da *man/angle_halfcircle_bottom.Rd 7c0ed95c6408a942573c40950769a54d *man/clip_lines.Rd a9c291ed365e38e821fe06a380297d96 *man/figures/README-basic-example-plot-1.png 7daee4aba14ed5e106dd6e5c010e7229 *man/figures/README-volcano-1.png b1b0065382a9ba25acf3d9d848ddaced *man/figures/isoband-logo.png cdad55cede9fff4fb4c4b51256f5f05e *man/iso_to_sfg.Rd b513f9f9539cbfe76f9344887021c211 *man/isoband-package.Rd 2f749db681548986bea043265e946c3f *man/isobands.Rd e6414005fa788d49ef59732da41c0629 *man/isobands_grob.Rd b071066784f2475c1a42fb3dbf97ff82 *man/isolines_grob.Rd 40209cee102daeef99bcd40f6bf37bd6 *man/label_placer.Rd 51464d65197322829abb30b8fc3ff31e *man/label_placer_simple.Rd a7ea5c28ccb5b62c1a524cf71d5b9da9 *man/plot_iso.Rd 38e07fcab8bf86effb0590dcad4bee86 *src/Makevars 38e07fcab8bf86effb0590dcad4bee86 *src/Makevars.win 644d7cd8a617f3ba9eace94384652cb4 *src/clip-lines.cpp 4812cd4c95500d6a1fdbc19cae6bbe29 *src/clip-lines.h abda8913e0916d6583fdeaed86c1ed40 *src/init.cpp 5b76f6e29690b82d4881217706446e74 *src/isoband.cpp 406d1d0d8761c2df7a6a177cd9ae599a *src/polygon.cpp 008650b58bba804dfe60929ae3a6fbdc *src/polygon.h 76b7f808923d6f5e999726b679e7a1b6 *src/separate-polygons.cpp 9b1a510c4d9fbcbcd5c8fc0426543f07 *src/separate-polygons.h 0b38530f08746e3bc5db544222bce1b9 *src/test-clip-lines.cpp 5d6d391e1fead82c17e5cde71f450fcb *src/test-runner.cpp aa5ff3387794fbaaf7c1611d374d340b *src/test-separate-polygons.cpp 6fedbe5ace0d8e58a00b296d75b4c83f *src/testthat/testthat.h ed1eab7fc6a36a53aa5170c5b5923e2e *src/testthat/vendor/catch.h 08c27bee2993bfb461a08613c6c51388 *src/utils.h 84e266146c1615ea549d0616628803ed *tests/testthat.R e4ef8dc5b34c07edd040c77861d516a8 *tests/testthat/test-clip-lines.R 9f1d0f22319012799150049a20e68b1a *tests/testthat/test-cpp.R c84145723f2b5a0b25084e6c3f28a885 *tests/testthat/test-iso-to-sfg.R 6cdc6029ceec0b1241470406b00eee84 *tests/testthat/test-isobands.R c9fc2ed0b9e5f0fbebc5d8f3b9efaa70 *tests/testthat/test-isolines-grob.R 0ec6d27dc6276b45f2b08b135ae48708 *tests/testthat/test-isolines.R fdf9fa0281b6d53274e1cf29ef8eb3fe *tests/testthat/test-label-placer.R f28d414c1c3d815096531bc649a188f8 *vignettes/isoband1.Rmd 40bb5c84837e16e19a854c24663f1e67 *vignettes/isoband3.Rmd isoband/inst/0000755000176200001440000000000014073124635012653 5ustar liggesusersisoband/inst/doc/0000755000176200001440000000000014073124635013420 5ustar liggesusersisoband/inst/doc/isoband1.html0000644000176200001440000025406114073124634016015 0ustar liggesusers Generating isolines and isobands

Generating isolines and isobands

Claus O. Wilke

2021-07-12

The isoband package implements fast algorithms for generating isolines (lines of equal elevation) and isobands (ranges of elevation delimited by two isolines) from a matrix of elevation data. For both cases, the package employs the marching squares algorithms as described on Wikipedia. Marching squares algorithms break down the elevation matrix into blocks of 2x2 elevation values. For each block, they then determine the appropriate isolines/isobands from a lookup table of all possible arrangements of isolines or isobands within a 2x2 block. There are 16 distinct possibilities for isolines and 81 for isobands. The implementation in the isoband package goes beyond the algorithm described on Wikipedia in that it merges the isolines or isobands from separate blocks into extended line traces or polygons. The package is meant as a low-level package with minimal required dependencies. Therefore, many of the functions provided may not immediately be useful to endusers, but they will enable developers of other packages to integrate isolines and isobands into their feature set.

The two main functions of the package are called isolines() and isobands(), and they have similar user interfaces and return values. Both take a vector x specifying the x values corresponding to the columns of the elevation matrix, a vector y specifying the y values corresponding to the rows of the elevation matrix, and an elevation matrix z. The two functions differ in that isolines() takes a single argument levels specifying the elevation levels for which isolines should be calculated, whereas isobands() takes two arguments, levels_low and levels_high, specifying the lower and upper bounds for each isoband. The return value in both cases is a list of lists. The outer list contains one list element for each specified isolevel. The inner lists hold line or polygon data in the form x, y, id as used by grid::polylineGrob() or grid::pathGrob(). The format has been chosen for easy drawing of the resulting values via these two grid functions.

library(isoband)
library(grid)

m <- matrix(
  c(0, 0, 0, 0, 0,
    0, 1, 2, 1, 0,
    0, 1, 2, 0, 0,
    0, 1, 0, 1, 0,
    0, 0, 0, 0, 0),
  5, 5, byrow = TRUE
)

lines <- isolines(x = 1:ncol(m)/6, y = nrow(m):1/6, z = m, levels = 0.5)
lines
#> $`0.5`
#> $`0.5`$x
#>  [1] 0.6666667 0.5833333 0.5000000 0.4166667 0.3333333 0.2500000 0.2500000
#>  [8] 0.2500000 0.3333333 0.5000000 0.6666667 0.7500000 0.6666667 0.6250000
#> [15] 0.6666667 0.7500000 0.6666667
#> 
#> $`0.5`$y
#>  [1] 0.2500000 0.3333333 0.3750000 0.3333333 0.2500000 0.3333333 0.5000000
#>  [8] 0.6666667 0.7500000 0.7916667 0.7500000 0.6666667 0.5833333 0.5000000
#> [15] 0.4166667 0.3333333 0.2500000
#> 
#> $`0.5`$id
#>  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
#> 
#> 
#> attr(,"class")
#> [1] "isolines" "iso"
grid.newpage()
grid.draw(polylineGrob(lines[[1]]$x, lines[[1]]$y, lines[[1]]$id))


bands <- isobands(x = 1:ncol(m)/6, y = nrow(m):1/6, z = m, levels_low = 0.5, levels_high = 1.5)
bands
#> $`0.5:1.5`
#> $`0.5:1.5`$x
#>  [1] 0.4166667 0.3333333 0.2500000 0.2500000 0.2500000 0.3333333 0.5000000
#>  [8] 0.6666667 0.7500000 0.6666667 0.6250000 0.6666667 0.7500000 0.6666667
#> [15] 0.5833333 0.5000000 0.5000000 0.5416667 0.5833333 0.5000000 0.4166667
#> [22] 0.4166667
#> 
#> $`0.5:1.5`$y
#>  [1] 0.3333333 0.2500000 0.3333333 0.5000000 0.6666667 0.7500000 0.7916667
#>  [8] 0.7500000 0.6666667 0.5833333 0.5000000 0.4166667 0.3333333 0.2500000
#> [15] 0.3333333 0.3750000 0.4583333 0.5000000 0.6666667 0.7083333 0.6666667
#> [22] 0.5000000
#> 
#> $`0.5:1.5`$id
#>  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2
#> 
#> 
#> attr(,"class")
#> [1] "isobands" "iso"
grid.newpage()
grid.draw(pathGrob(bands[[1]]$x, bands[[1]]$y, bands[[1]]$id, gp = gpar(fill = "cornsilk")))

A convenience function plot_iso() can be used to inspect a single isoband and corresponding isolines for an elevation matrix. This function is mostly meant for debugging and illustration purposes. It draws a grid of matrix points colored by whether each point is below, within, or above the isoband, as well as the isoband itself and the enclosing isolines.

plot_iso(m, 0.5, 1.5)

The isoband package handles NA values in the matrix by simply ignoring the respective grid points.

m <- matrix(
  c(NA, NA, NA, 0, 0, 0,
    NA, NA, NA, 1, 1, 0,
     0,  0,  1, 1, 1, 0,
     0,  1,  1, 0, 0, 0,
     0,  0,  0, 1, 0, 0,
     0,  0,  0, 0, 0, 0),
  6, 6, byrow = TRUE
)
plot_iso(m, 0.5, 1.5)

Isobands can contain holes, as shown above, and they can also consist of multiple disconnected pieces.

m <- matrix(
  c(0, 0, 1, 1,
    0, 1, 1, 1,
    1, 1, 0, 0,
    0, 0, 0.8, 0),
  4, 4, byrow = TRUE
)
plot_iso(m, 0.5, 1.5)

Performance

The code is written in C++ and performance is generally good. Isolining is about as fast as grDevices::contourLines(), isobanding is approximately 2.5 times slower.

# contouring with contourLines() from grDevices
fn_contourLines <- function() {
  grDevices::contourLines(1:ncol(volcano), 1:nrow(volcano), volcano, levels = 10*(10:18))
}

# contouring with isolines()
fn_isolines <- function() {
  isolines(1:ncol(volcano), 1:nrow(volcano), volcano, 10*(10:18))
}

# contouring with isobands()
fn_isobands <- function() {
  isobands(1:ncol(volcano), 1:nrow(volcano), volcano, 10*(9:17), 10*(10:18))
}

microbenchmark::microbenchmark(fn_contourLines(), fn_isolines(), fn_isobands())
#> Unit: milliseconds
#>               expr      min       lq     mean   median       uq       max neval
#>  fn_contourLines() 1.684591 1.858066 2.407178 1.958155 2.451742 10.111227   100
#>      fn_isolines() 1.456334 1.534420 1.640146 1.599429 1.656649  5.420189   100
#>      fn_isobands() 3.986300 4.226374 4.476343 4.357670 4.496028  9.724201   100
#>  cld
#>   b 
#>  a  
#>    c
isoband/inst/doc/isoband1.R0000644000176200001440000000365214073124633015247 0ustar liggesusers## ----setup, include = FALSE--------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- library(isoband) library(grid) m <- matrix( c(0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 1, 2, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0), 5, 5, byrow = TRUE ) lines <- isolines(x = 1:ncol(m)/6, y = nrow(m):1/6, z = m, levels = 0.5) lines grid.newpage() grid.draw(polylineGrob(lines[[1]]$x, lines[[1]]$y, lines[[1]]$id)) bands <- isobands(x = 1:ncol(m)/6, y = nrow(m):1/6, z = m, levels_low = 0.5, levels_high = 1.5) bands grid.newpage() grid.draw(pathGrob(bands[[1]]$x, bands[[1]]$y, bands[[1]]$id, gp = gpar(fill = "cornsilk"))) ## ----------------------------------------------------------------------------- plot_iso(m, 0.5, 1.5) ## ----------------------------------------------------------------------------- m <- matrix( c(NA, NA, NA, 0, 0, 0, NA, NA, NA, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE ) plot_iso(m, 0.5, 1.5) ## ----------------------------------------------------------------------------- m <- matrix( c(0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0.8, 0), 4, 4, byrow = TRUE ) plot_iso(m, 0.5, 1.5) ## ----------------------------------------------------------------------------- # contouring with contourLines() from grDevices fn_contourLines <- function() { grDevices::contourLines(1:ncol(volcano), 1:nrow(volcano), volcano, levels = 10*(10:18)) } # contouring with isolines() fn_isolines <- function() { isolines(1:ncol(volcano), 1:nrow(volcano), volcano, 10*(10:18)) } # contouring with isobands() fn_isobands <- function() { isobands(1:ncol(volcano), 1:nrow(volcano), volcano, 10*(9:17), 10*(10:18)) } microbenchmark::microbenchmark(fn_contourLines(), fn_isolines(), fn_isobands()) isoband/inst/doc/isoband3.html0000644000176200001440000222161414073124634016017 0ustar liggesusers Labeled isolines

Labeled isolines

Claus O. Wilke

2021-07-12

Labeled isolines can be drawn in the grid graphics system via the isolines_grob() function.

library(isoband)
library(grid)

x <- (0:(ncol(volcano) - 1))/(ncol(volcano) - 1)
y <- ((nrow(volcano) - 1):0)/(nrow(volcano) - 1)
lines <- isolines(x, y, volcano, 5*(19:38))

g <- isolines_grob(
  lines, breaks = 20*(5:10),
  gp = gpar(
    fontsize = 10,
    lwd = c(1, 2, 1, 1),
    col = c("grey50", "grey20", "grey50", "grey50")
  )
)

grid.newpage()
grid.draw(g)

The function places labels at the isolines specified via the breaks argument, and it crops isolines around the labels so no lines run into the labels. It is possible to leave extra space around the labels using the margin argument.

g <- isolines_grob(
  lines, breaks = 20*(5:10),
  margin = unit(c(3, 5, 3, 5), "pt"), # margin specified as top, right, bottom, left
  gp = gpar(
    fontsize = 10,
    lwd = c(1, 2, 1, 1),
    col = c("grey50", "grey20", "grey50", "grey50")
  )
)

grid.newpage()
grid.draw(g)

Where the labels are drawn can be controlled via the label_placer argument. A few different label placement strategies are provided. The default, label_placer_minmax(), places labels at the maximum and minimum y value for each isoline. However, this label placer can be further customized. For example, labels can be placed at maximum and minimum x values and at a fixed rotation angle of 90 degrees.

g <- isolines_grob(
  lines, breaks = 20*(5:10),
  gp = gpar(
    fontsize = 10,
    lwd = c(1, 2, 1, 1),
    col = c("grey50", "grey20", "grey50", "grey50")
  ),
  label_placer = label_placer_minmax(
    placement = "rl", # place labels right and left, i.e., min and max x
    rot_adjuster = angle_fixed(pi/2) # set fixed angle of 90 degrees
  )
)

grid.newpage()
grid.draw(g)

The label placer label_placer_none() doesn’t place any labels.

g <- isolines_grob(
  lines, breaks = 20*(5:10),
  gp = gpar(
    lwd = c(1, 2, 1, 1),
    col = c("grey50", "grey20", "grey50", "grey50")
  ),
  label_placer = label_placer_none()
)

grid.newpage()
grid.draw(g)

We can also place labels entirely manually by using label_placer_manual(). This label placer takes as arguments the breaks that should be labeled and the corresponding coordinates and label angles. Breaks for which no coordinates are specified are ignored. If multiple coordinates are supplied for the same break then multiple labels are placed for that break.

g <- isolines_grob(
  lines, breaks = 20*(5:10),
  gp = gpar(
    fontsize = 12,
    lwd = c(1, 2, 1, 1),
    col = c("grey50", "grey20", "grey50", "grey50")
  ),
  label_col = "red",
  label_placer = label_placer_manual(
    breaks = c("120", "160", "160"),
    x = c(0.15, 0.5, 0.6),
    y = c(0.19, 0.51, 0.87),
    theta = 0
  )
)

grid.newpage()
grid.draw(g)

Because isolines are cropped around the labels, the labeling strategy works even if the isolines are drawn on top of a colored background. For example, we could draw filled isobands using isobands_grob() and then draw labeled lines on top.

viridis_pal <- colorRampPalette(
  c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"),
  space = "Lab"
)

bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39))

b <- isobands_grob(
  bands,
  gp = gpar(col = NA, fill = viridis_pal(21), alpha = 0.4)
)
l <- isolines_grob(
  lines, breaks = 20*(5:10),
  gp = gpar(
    fontsize = 10,
    lwd = c(1, 2, 1, 1),
    col = c("grey50", "grey20", "grey50", "grey50")
  )
)

grid.newpage()
grid.draw(b)
grid.draw(l)

isoband/inst/doc/isoband3.R0000644000176200001440000000522614073124634015251 0ustar liggesusers## ----setup, include = FALSE--------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- library(isoband) library(grid) x <- (0:(ncol(volcano) - 1))/(ncol(volcano) - 1) y <- ((nrow(volcano) - 1):0)/(nrow(volcano) - 1) lines <- isolines(x, y, volcano, 5*(19:38)) g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ) ) grid.newpage() grid.draw(g) ## ----------------------------------------------------------------------------- g <- isolines_grob( lines, breaks = 20*(5:10), margin = unit(c(3, 5, 3, 5), "pt"), # margin specified as top, right, bottom, left gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ) ) grid.newpage() grid.draw(g) ## ----------------------------------------------------------------------------- g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ), label_placer = label_placer_minmax( placement = "rl", # place labels right and left, i.e., min and max x rot_adjuster = angle_fixed(pi/2) # set fixed angle of 90 degrees ) ) grid.newpage() grid.draw(g) ## ----------------------------------------------------------------------------- g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ), label_placer = label_placer_none() ) grid.newpage() grid.draw(g) ## ----------------------------------------------------------------------------- g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 12, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ), label_col = "red", label_placer = label_placer_manual( breaks = c("120", "160", "160"), x = c(0.15, 0.5, 0.6), y = c(0.19, 0.51, 0.87), theta = 0 ) ) grid.newpage() grid.draw(g) ## ----------------------------------------------------------------------------- viridis_pal <- colorRampPalette( c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"), space = "Lab" ) bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39)) b <- isobands_grob( bands, gp = gpar(col = NA, fill = viridis_pal(21), alpha = 0.4) ) l <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ) ) grid.newpage() grid.draw(b) grid.draw(l) isoband/inst/doc/isoband1.Rmd0000644000176200001440000001113614070653241015563 0ustar liggesusers--- title: "Generating isolines and isobands" author: "Claus O. Wilke" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Generating isolines and isobands} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` The isoband package implements fast algorithms for generating isolines (lines of equal elevation) and isobands (ranges of elevation delimited by two isolines) from a matrix of elevation data. For both cases, the package employs the marching squares algorithms as described on [Wikipedia.](https://en.wikipedia.org/wiki/Marching_squares) Marching squares algorithms break down the elevation matrix into blocks of 2x2 elevation values. For each block, they then determine the appropriate isolines/isobands from a lookup table of all possible arrangements of isolines or isobands within a 2x2 block. There are 16 distinct possibilities for isolines and 81 for isobands. The implementation in the isoband package goes beyond the algorithm described on Wikipedia in that it merges the isolines or isobands from separate blocks into extended line traces or polygons. The package is meant as a low-level package with minimal required dependencies. Therefore, many of the functions provided may not immediately be useful to endusers, but they will enable developers of other packages to integrate isolines and isobands into their feature set. The two main functions of the package are called `isolines()` and `isobands()`, and they have similar user interfaces and return values. Both take a vector `x` specifying the x values corresponding to the columns of the elevation matrix, a vector `y` specifying the y values corresponding to the rows of the elevation matrix, and an elevation matrix `z`. The two functions differ in that `isolines()` takes a single argument `levels` specifying the elevation levels for which isolines should be calculated, whereas `isobands()` takes two arguments, `levels_low` and `levels_high`, specifying the lower and upper bounds for each isoband. The return value in both cases is a list of lists. The outer list contains one list element for each specified isolevel. The inner lists hold line or polygon data in the form `x`, `y`, `id` as used by `grid::polylineGrob()` or `grid::pathGrob()`. The format has been chosen for easy drawing of the resulting values via these two grid functions. ```{r} library(isoband) library(grid) m <- matrix( c(0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 1, 2, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0), 5, 5, byrow = TRUE ) lines <- isolines(x = 1:ncol(m)/6, y = nrow(m):1/6, z = m, levels = 0.5) lines grid.newpage() grid.draw(polylineGrob(lines[[1]]$x, lines[[1]]$y, lines[[1]]$id)) bands <- isobands(x = 1:ncol(m)/6, y = nrow(m):1/6, z = m, levels_low = 0.5, levels_high = 1.5) bands grid.newpage() grid.draw(pathGrob(bands[[1]]$x, bands[[1]]$y, bands[[1]]$id, gp = gpar(fill = "cornsilk"))) ``` A convenience function `plot_iso()` can be used to inspect a single isoband and corresponding isolines for an elevation matrix. This function is mostly meant for debugging and illustration purposes. It draws a grid of matrix points colored by whether each point is below, within, or above the isoband, as well as the isoband itself and the enclosing isolines. ```{r} plot_iso(m, 0.5, 1.5) ``` The isoband package handles `NA` values in the matrix by simply ignoring the respective grid points. ```{r} m <- matrix( c(NA, NA, NA, 0, 0, 0, NA, NA, NA, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0), 6, 6, byrow = TRUE ) plot_iso(m, 0.5, 1.5) ``` Isobands can contain holes, as shown above, and they can also consist of multiple disconnected pieces. ```{r} m <- matrix( c(0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0.8, 0), 4, 4, byrow = TRUE ) plot_iso(m, 0.5, 1.5) ``` # Performance The code is written in C++ and performance is generally good. Isolining is about as fast as `grDevices::contourLines()`, isobanding is approximately 2.5 times slower. ```{r} # contouring with contourLines() from grDevices fn_contourLines <- function() { grDevices::contourLines(1:ncol(volcano), 1:nrow(volcano), volcano, levels = 10*(10:18)) } # contouring with isolines() fn_isolines <- function() { isolines(1:ncol(volcano), 1:nrow(volcano), volcano, 10*(10:18)) } # contouring with isobands() fn_isobands <- function() { isobands(1:ncol(volcano), 1:nrow(volcano), volcano, 10*(9:17), 10*(10:18)) } microbenchmark::microbenchmark(fn_contourLines(), fn_isolines(), fn_isobands()) ``` isoband/inst/doc/isoband3.Rmd0000644000176200001440000000745714070653241015600 0ustar liggesusers--- title: "Labeled isolines" author: "Claus O. Wilke" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Labeled isolines} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` Labeled isolines can be drawn in the grid graphics system via the `isolines_grob()` function. ```{r} library(isoband) library(grid) x <- (0:(ncol(volcano) - 1))/(ncol(volcano) - 1) y <- ((nrow(volcano) - 1):0)/(nrow(volcano) - 1) lines <- isolines(x, y, volcano, 5*(19:38)) g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ) ) grid.newpage() grid.draw(g) ``` The function places labels at the isolines specified via the `breaks` argument, and it crops isolines around the labels so no lines run into the labels. It is possible to leave extra space around the labels using the `margin` argument. ```{r} g <- isolines_grob( lines, breaks = 20*(5:10), margin = unit(c(3, 5, 3, 5), "pt"), # margin specified as top, right, bottom, left gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ) ) grid.newpage() grid.draw(g) ``` Where the labels are drawn can be controlled via the `label_placer` argument. A few different label placement strategies are provided. The default, `label_placer_minmax()`, places labels at the maximum and minimum y value for each isoline. However, this label placer can be further customized. For example, labels can be placed at maximum and minimum x values and at a fixed rotation angle of 90 degrees. ```{r} g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ), label_placer = label_placer_minmax( placement = "rl", # place labels right and left, i.e., min and max x rot_adjuster = angle_fixed(pi/2) # set fixed angle of 90 degrees ) ) grid.newpage() grid.draw(g) ``` The label placer `label_placer_none()` doesn't place any labels. ```{r} g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ), label_placer = label_placer_none() ) grid.newpage() grid.draw(g) ``` We can also place labels entirely manually by using `label_placer_manual()`. This label placer takes as arguments the breaks that should be labeled and the corresponding coordinates and label angles. Breaks for which no coordinates are specified are ignored. If multiple coordinates are supplied for the same break then multiple labels are placed for that break. ```{r} g <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 12, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ), label_col = "red", label_placer = label_placer_manual( breaks = c("120", "160", "160"), x = c(0.15, 0.5, 0.6), y = c(0.19, 0.51, 0.87), theta = 0 ) ) grid.newpage() grid.draw(g) ``` Because isolines are cropped around the labels, the labeling strategy works even if the isolines are drawn on top of a colored background. For example, we could draw filled isobands using `isobands_grob()` and then draw labeled lines on top. ```{r} viridis_pal <- colorRampPalette( c("#440154", "#414487", "#2A788E", "#22A884", "#7AD151", "#FDE725"), space = "Lab" ) bands <- isobands(x, y, volcano, 5*(18:38), 5*(19:39)) b <- isobands_grob( bands, gp = gpar(col = NA, fill = viridis_pal(21), alpha = 0.4) ) l <- isolines_grob( lines, breaks = 20*(5:10), gp = gpar( fontsize = 10, lwd = c(1, 2, 1, 1), col = c("grey50", "grey20", "grey50", "grey50") ) ) grid.newpage() grid.draw(b) grid.draw(l) ``` isoband/inst/extdata/0000755000176200001440000000000013501223537014301 5ustar liggesusersisoband/inst/extdata/ocean-cat.jpg0000644000176200001440000257600113501223537016650 0ustar liggesusersJFIFHHExifMM*JR(iZHH02100100ܠhttp://ns.adobe.com/xap/1.0/ Top-left 72 72 Inch Exif Version 2.1 FlashPix Version 1.0 sRGB 2988 3547 C  !"$"$C |*p0* J AX(E& $!Q$P`1 (t @1A:!`*`0%@1!R9X Y)X,C`: !BѲsQIʬRJHՄc § S%,lpTaS$`0  8TR+  rI*AeR5`#TA`0 `0 p脬(% Č-q9D5T!D:@r($'*I),h @! ]Us9X5 AH `5H "J#ӄRDd`0RVuTϞ@_^<^sנ;OKKc|;{!Q{p,I) )DTC+`@ąՈ+DpՐ\c c(!d`PJ `(:R5  ^e4G,FcW5ϚjDvϚKzb]NqDUi%Ϡ2Ӛe//);ˎsw#P9K"R!2(*p(,j!)cByRR!  JP#A@ Z"@8jX,r00yYrWO/nrzJ}ϟ;Ǵ >A5gЬ.E@kV~n }\zr |]>~uQUMr!W)X $h:PD(jJ9RIAJ`V1""d!pX Z pr @F! DJ pY0*D%yĎYÎzg_cgeUq}"xzt<;zR1YQ&G CjtIRql|Ϥ^~fQ@ URȐ $bۛdX,!ƃYZ©T6Bcc2RpK2Y Sfn)퓷ו!A咰C`LcJy} -@bW.\$"Ci2șTjoN4Uk(ynْ X@JVH!ӈY [,dk(-dE:HX  CVJ)`ru˪j蔻l]~`V"H1JThԝZk5cC5qҙ/Lsj\}_8}t0@I@2J%, `5] 8$dA D! cR* +: UsugR3D^N~#"0%(0!%#T)`01ECu9ѝש錚[z((^:[  1ȌdD5`"`Y-9X`àc$6 ԆJ$1XD圾j_k1&mRTe+Wx2  rL@1,J!FHJhM9>ܲiZF+&B>1zؚ 4( 0Y!Hc 5ĥD$5$L $S B!*#/O4Yi4O-ٵK%\u:Z` `52C` 0H5,YK'.SéήaiYI9K5W4Y CDd!cG1@Idƨ ȣPIX!8u,Q!Y&I"@H_;\(]$nY r\һ9]'?y}cSj jףl-&mcAS,'{3p[=n%T}3*/H-%Z5tUX;G>v?Ab`0`8 2cQQF0Y"Pkd1DɁ"Ș ϛrmdf&˿6%ǍP5raNnn_뇸k]2YlHnͬc{u6t=DwˊʬTKOIwZxɨXZ".ugǡ+]CC`H `80(N[ H @He"c%գP1+$N&D"D?2o./n\孹}5gKsugn[9'^)G2ĽٯO󮴾gYҹ#Vw:b#Nx]Yz4,ε+*=jgX5y{z_>?>!DC2PQB:gǽg @Hd -$Tա+$8ՠ(pIe@M\HtHDU9f1#Ƿ Ν拙s\uUͷ:ٍiδjړ_G/3۞k)NsلIؚ\k[B)B8,r(t5PRC `Ƣ4zy0 ƳS)$#Q'"DJ9f9e t ጐ$5f=ܬoΫ1B͌엣-βG;M8֩x^=^J==xGjU.f~~oyVD||{k,ok1Vo׊댖tsI˦/Zf\tcy7=[@@ 0 )R0 ADI@52@0 tCpceW dB}?l_}(T A`: CV1@G<!!8#J @5LrhSC+$1Y/_xg5 .q5<\[W7iL[rd^rfzs5}y~Uw_{{14ܷErҵIjZߝw9v4e~gMuw H!D(10"J!"" `1 (1 BY*$A$J1 1Hr! 9۳8C4>鳞5Ehκt^:眾ͬy9>zY#k۬Ǖ>z՛Yzk tһc}=aZ*kם`fZfu6kT/Ƴo7i4]]+yKefo͸Uk4l˧cnsg]dq  f"#4Db$N 1#LNY8TAdAԑW JJ aR+bF ,oxwӟy1=iԫY.zvktUˮqxymMZN=Υ_9ۗYU(K""Å^{gB\ޮm&{%7&uWߞkos/ǩ95Oh: .=!ܱ#A7}C7tfzDz𷏢"nbsm3-]=>]n\[DE$@D12B$j!! Ɂa0,.<¥+"U]"qdB$`LHBIp8)"t1C NY|:Xٍ]9ӫ][V6u_qqscKϫ/'xהuu3pk87)ezN{:|6_7a=fuo^/<'6Bf=Q5ε2l&|:n;>7_SQ"BZDDdDYa`T Q]6J=!SQ"AY2#I $XYĂ8C "r%N*-K%01_~ Xz5fs; wzx8wӮ;sչk<.sZqYsᘵzj'Cr5Y|me:s-AZcxCONq۫"EԢF@$B"D&@"$wYU[R2uIYg,Vm : enWY:i%P%$Jro-YpnNnM.5(霺N<\{^ZǬf2k*޶/j^tcX: gƼLN]oLe\-bJW5=n7Vrk67LQ5xoߏOyܗXHY9gJ,&BJD pcC+.,!ߋ"VXM`+X֌I F"rM!QV [ #Hi"JFDDd˧ƹ_/c՛徯-vɓJuLC>y]9vNKuavs.#wZrԙjfe9QԚً^%lƪY2c?R'YI:sYYU(2F:ZH҄2`@ŵ0"L 00+ ߌVHX":!"DFL *Qr*&"2c QQ"Y.+U2H"D(Q"+,t +XD*  XtK  2lMl"H,쐂() "=D bI P@ k1$KecVHb"EpPJ q!S$@yߔ5s{:.o\g׍hKiܹ5Y;,:dy~yOk{K{s٬ԶtsY=nzgX+cW+DJ=eW=hιzyܺ8GLx-ʊ((DF@ia%@["ӋKQ)-IMed߈D̉#RW)]YRC@ 8F80bQ#,-&oz|a/SJ.-UZXum,ͩxpnkIĮnSqObqiN;Xߡu)=>֧-k<^1Y:ՍytXU JG/8vyotzsвCUB%q}jC"!FHCkHTPI(A&YA{V@5TVPV@DdAn$H@MZ!) XJDunmDl%I"j2θ:xi妋9,]^-Wzβٷ7>:;|?yu&oD/`N&orފφdzswyb^ߞ^<LJOurw9dȞӗNUu2ũ;w|On~S/EΧ'ys[3=[puè^n4R4 P"X@ ZdG+a;d "6U KC@d<~Si|2j*)ZR5|isfҵq^Yˢj"%eŶYVĥ &DA+&Y,R'Vino dӍvWD9kW{^Z{'s711 gtzwNһ/sG7/۞e;y_:UƲK:/oq7u˦oEkYg2{+ͶZ.{,ߠw\$"BQ %S+đ+ZUъ'QBTHɒ"% BHud,,J VZb뎂NZlћZ,YXfzEuKJԐ$1DIYi\]<=p&_vS]~9<-O;ە"٭ּk3YtTՖN9u}i-HjxG.v-z3nme9r2)/V_/ێog|wfu8ˏCˮ?7,ױ翥\e$D" EH-%dUR.-FBUAEF|ڎn mzϞƬѝB˕e2Ժbꐳ/k\a*d2|Wsl)Td"i5vN TtK2%|1ך^w\Q]Ou?ǕdYkǯKfuH]/K)|:c^k`n4VRa3~|+7jyN:wsr:l%^VlL];SP>__syϬt7|=bNͳ˧כח{/IKITH Y$`~kͪ^7Ontgj\U}t<~۸:gӞ]bkI@z\﷍os_D(lQ0$ "FLQTfPϛ.lV"Hʤ%!P9P@=|.\ܕYenCYtۍznZ7Ϗ=n}:RtgPwYIKS/#y[wu)W=sf;qgW|YGwӟw.G\b2/[5tӛr]\Lc':_Dw|8ێv㱾~Y*e"+RUD*erϼ_^|.}2Kxu+wkLvwǝ岋.k$l5ݝ]1 sdI XLJ$Knm XˬP@XLdmArʞY4˥*QY%f fu$:iÑx_溼ݼG[MъCyD7z9|-Nխؼmjru};Ϯu$ǖ\fK9&y~Ou3XYydr姸}-թ:c-:^\w>,3jR21Y)gRͻ5gY6KL>=~'9|??uޘin=f}<_gq^R.{n{/yӗwgVb%hȴ#cA-jj1E$$+ B 1+"kƸ^~kr:L]G<[S{bvuYJ=s7<磕ԭh:\9^._O冲نİϧOFįz|7ӖDwn+k>K<噾]ٽ^VtjBYVQ:2%˝k^Xצ/wҗ^/o5Nus~yYeY :u3(\/wŵtZi%-R Hh4 H#*Չ'SIU%ɌU1`*rL9˗7\jKLL^\:a;sNuשï|ylzͦo-w>uq7L"DJ,eOYӗ+yޜYdZy׫箭ϕ*@Z}=ǟEtgǧ.nϘVїMz{zL2p=<륝9hM^՝uV{cU-O?ZE^jzͬۢՔgtπ˜B[x>_y#fΜ/ntDF.zUNOa|ce2^*]5E9Ḳ;fbB?>mŊWzPYMWf~,h`\[SrږҊ4heZ\"R:RkO(_.g>Mzgx|תX_;q~d#;I }{cm7Zsc]zg}>{VcyiM/O#߹6:yt\YlRN|;<4)eмm-ӟ&Ό緛ׇۣk,yLD JW:sQ:R9z;|3t|ՕfiVāx߬Lߢ-坿<|׵Ykn.EY-KIKM:κ$Xe\HQLt )*[VY( I`$#,9to6C~5薊~\af~/t\M1tm|xix039K%ۍzzGcjkN?yzgx?WCoK1휻{k k*縧yκxk":2jΌNm{Nף:btY f#uqӍQ~:36n}_;˶ltY Tk6i4=@5o?S50۝lq^U`Tk<ۜ?z尉)"DՒ'SDĖ$eC$`UQ` ؕ8EdI,#|ןN~vK+k͠'ۛYƱf_[sp^kO+;r<<Ks+s<:9Dzzk;s7ɼs{_1ƶ]9zcuXG+W>:?M"16Kʬa]ױ۝ѝZ]Ei"9lsϣpHj;NU@bTofQ%䰄W.\4k4:6VFĖM$@HD2HYA`0,Led! $ S UDFn{|=RF%zx'b藯][/p=q>u⾇ەE_/yxm\^Lrz~\0$ۍ.;<每&s矿箎N-c41~_N\^78s9-L2\IS]}="3oFPv`!tK|\eTD$nhiK \T{_E4NUbtXi]s]o.fRg7YN>=oیS-SfQR3X%gۊbWfI( U DT"ŒTFĊNY+L X_MϦVmu vYķb.k(i1KFUɭغ}^ޙռ,R))*jYfl$!*c-Y_7n=gxjt1{ux18kAy﯋^؎-eo/Zo~=:Fmx7γ}ze'gαtś ͚s邺ݼs{pr[\fMg8uM󱮖ui$+2|]5Nu8ogrvsYMEV[ēkLYΜ%ȜzF'Y]ku+DYN 6 cS/_xs5ԫTjg^P˄"&REI HX @$$L`J3x~^ܮ=y>9tFڝN{\wkY˼gh%oC[R{C-&Ϯ+\tŝV6gb8;sk9<X z|}\[eИu4Ѯ霺+<7Kћqtiƺ7^nY5lC7F#xǼNxu{\VWOa÷3y'c|=-Vb5tN#QH.T"LV@e[}gǏ+5#{bF2Q]RZҖ0h4-B-"gumE-$H%i*HF .$Hb RBhie;as}<ϳ.4 7c^׹W1sjE(KэPݹӅܯxqzs3ЖQ{ w[~ w0fE-vve:7G.|թڢ&~)|[n[⯁Z9tkg~q=ze:[˥.=9Tz==Sy\sϹ˯{7(Y}jscKj&TAN#TIME53Yj9|e"LVdˬ̷PEDL6gQ4lt%i/dLf~5X2%bWd5p\rbP.ΑZTF2qb-GUB4.өR1lsH/zܤq[qmy99Қzvɝs]1`隍6zs= cBэ9rFm<|gY|~V_)_z< ,kϯCͬa3߅Ij</_Eiq뫏^}s۬ǯ}ԥ)"N 3V{JlJ7WhWHb*LZ\\ūYY,LEūV_j5QynRĬ|YdLV=C5MImX%lY=+VDJ&WK4gS缼:,s).-fr78]q6Y7Fu^o=* {(K]t8neoju#E_oӲ~W:q]3YxofwY8uΛ:g7ϵ5gWg\==DNw9GY ([\_.uQ #,àFDJLD,egBgd,*QeC,,TD ќqzVH^ͪWEZT@ / Ld9 aYX!m۠icY}j&u=*.3uŇj=^oL\_?ٕĚ]ۖ1s;XYϫPYIӟgLg^oo:YOOZXٖ͎}ܝ#jeT-*_-U屚نˍr$L^lJJ"K\#V Ze+=KZBEV,!SkdZʬYhe¤-+BY-KٖgI!˧ߐf: eflڷ[a@. ~t~ok7&Ҩ}H:Bs-^wpzzߝz]y[jDg#\in˩ח5dG^+:fnμy^g*MAkSV&J,:e%Ktl.*9Q1GF*)ȵsYΗ ,Z(iIӕ=˅rfWЮ C ]"2kJ0/ʹMYjf+-y~-ζuyZ:ZnvsYqB^7I8~?'ڗ)%-J=F]4֜o~5x˜nuUQ;cN{1,LY|>]!fGMәfj\c|t:ϼgtcgY1yoLD,LrekU%6f˓QY,]LٕIhź4KjLf*bQՉm] AkYij\RDBđZ$iR"k[b#db$h' k3Y9tKιVDt7-,˯ͮ7ƻsWy_o~8%SJ'/ι9+RZ]{ʝ˵]d{OPLU̕}zTf^o.~wN]`R{:oMp[1aw;f}Lw"r씷KD&Zs&,f, `A*!H,@$j(bKVXS ui(DN+YJlr̲VibZZ&H$)T\ι>OF|iu1f6UQ.4ʫdQX /#ugk: 2KҵߛF+NluKcutk6и-zikPXKMd:70QeeuB| J5YE.e]9p^^V]u^|}yG%DqUUXȲ.BrFs"Xj,Y2kaˤ+ 6PSA2E r"ULq:QK,HItDD*Q ȉcˋ^w-y^qo=hͪ.Z v;sfv<~e5YY"4K:g[nkԻN.:Ӭg\֨ x tꤨc%uԢZFpz;t9t9uQp/r^RzG;st7НREEFfZrS̤SWHU_AU[Ndt{w7}+:c=8QUV]2 @AĚ8 VFk4V5KikD2%NŵY]4D6iȅ1Ȫq%[lήUZYDؖQ $L8EbDV|sK$a:ζbݛKs{ܵ/7l۝zN:e3ە`8[:agZǧiq3fYƸ9:[؛*Ssuz.^#ט_7Lkan޻.ǯR"A+"5"H*%E$R)5#%e[2Tc@:TN 2&b*%G:*˛Dh[e,Y*C&""HQJ%<˓7TG*4n\V v[Z1y9}sN^z;3{ zXùǯcfy]ͧ/0k;8Wr43צΩFwAh WYUlg-ķ6KgSz>~=w:͖ȡ |B ReJPȮQ,QYjR(4-l%r] 21:b+˵kZh͑bgBhjȮ%" %v2D ldl@ӛ:ry?hs%9ZsqK6[^n>cXپ\\^-iyzIqk>X>3=<^y:gn_)x,Y\t#%I=67δjЮuiAQr'9eYBUz7^ϣ5R53$nlEMdVT"Q2! =L@RuVN6%yYT%clݕP,%@$dҷ.- U%uɒvJE3Vmfն,!P.\M\8|ן>^u?y7c]n{ݍWZW~#έؽܢk9bm[筺+u);N_^^kåMǙktcp\F^VoYxYg]mR_Y"%#篓%ypMtYǷϯO7Cu5u+ˣ7T D ]Tb .k(ٔKjeCtˊZɮlk4jz=gF4d[޳H=i3Dֈp1jdlԕk2۝X2d Y0Jx<:\v[wKяxZk*aXB>NNDҝ :9՛1rtxmf]p鋯:9{t15ۗVκbUƩ :iYX㞼^gL;s:]RkV~wM3-ּ.9.{g^g/0owq$=}=w-Q1袽gZeB{{N^5nzLu=7߆Ѷ=k|a3Vl2dl"rDC":dIDJ,BʹđUAE*E;R)lL[N f[Zk],,Y霉]@.-ʺYIJDVno?^-89B4RǝWU.f w<`^F]\9Ys]f?}?Y|tƬ˭QEf؍s,gQ:^U5eZVuשˮg۞gQgˡ.+=/kXK9YgϷk\}?zwϟ^虳uzx=~mnLJ tKl%Q UU#Ɏ:dUJ"HY?>}[db*db[:wM- jzk+"Vˑ3*'f6٦5KjQ T1ws̻m0bWKSfY׳0k:u8+LѬgOKtK9Ϛ=W.\͖N&yμ=ۏ9לvN!Ssys:發7ٶ^^WNΎn wgXRu]Lu2r뿯c}.`ϠƧcT˟.o7C.(KqWB*H1+"Z^E`*IleefP!_O +.iTER]a`&MƢd Ȥj1ZE&li6B9mͫUǥ͛-R^foX٢jgIn9&mLy)}])Zwtvvyuwr:cfwƘvyvasn}MtsvżdϼhDS`Dݎq9Ӛۮyyǩ;Nt瞻*}jb%)FmDD&NRfYIY]1$*Ș4ȑ,&kXUy 3TKf %V,&iiJKtC"D^g7r|S6(Ιμc6X81Sm3qo58zaY[36=cwU_[Ժ͝yG yr~l록rs󽸋n;~<?I_NuكXVdI.oϭ]N7/LK旕mycymI]2.X ))\[3㧝_st3UBC$_)UI#LL DVM*QY.|L2de:fJ,h7= w˧:Y+J"e1n񭹸cZ/Xx| [ e?Lém>]*ß<QsdQ²r'NvfҦ<]eYu:>n^]N/Lwϯ˧J\p:x=;2E^j%ՍBoN76Q^ǵl^=N"w$zc۟qũMI7ɘK|Ufii1gY-m)B6/VzȒ-"bӗsDX諌f3 Ω^!ʌQ.gIj kYBWd}jMꚲX$ 3S[эlSlF)iNB-9αW?xsǥwLF相&t竍rIӛӞMg=oM6gMi99qx=d%={ts8vam(7՝)NYٍ%ϧOI\XrzsVjssg~|ߗ./^yALVYWJV7ssDeQ5βHJY=4h.mNM:ε:.K9f;.[ͬ|uT"DFkMC))Zeɤ+3҈ٮ^=mD J8ߏ z>{kƺ#=0w^KYb{s-s[5F'<4gVܬWE4gTjs,r祝s.n]1::O zO?~.gLjι=9˼u#^zVӬu6s$ϾpY.]rxf_Q{swg}t^uߖ=g`Tff:u3vK+"Ve0ZfZ]{_v25"6"%&;0Y!Sm+Bgԙ|Ƴ:{- u-qz2#-U2л"&J&k5ˢjPP%d|;y].ZYdZY{3Kʹ3V cvtߎ=eY:,ߍ8ݝrzn}N75,#מr龍W=n=W+9g..~,mΥ+qԽ>[ן۳kbso]εb2OGptw^Ky"CZ- .X !DX,ƹ=7]15D3 lg[ zv(uЎSM(&%eƥқ t3SPLV#\ԣ-긥sKvnj]9FtDnjg#Ӟ]HXE_w|o fk<)$D[Y6/ȑd QQ]^e KMrۛ[Vhk5Wɮ6هRvd>&0K]8Mm۝FZ\ιY6,e5̗U+6Зf5{^Q68S68t.nԌ~]v׳'\u:g:g۝vAtfα8zs˩U/nw[eǬߝmƴfƫ 9wVvg]{ۍg.9קּ&qz׎-g^tzF%Ϧ8/?n7&Qr ^wbts9mg>"JK3YrYbgZ2T.$N[13u*HKzVeSSFr8rW޾u55׻K2)%+,˯:ZlqcsrK`Zk-4Kǭ|zc[YXu'LNeƬ9z=3(%HsY.-gˣ2Sd;\exsco:|.+y5YnεKYޜ5"룝 ՝_JNu~oKso:o^uo52EuRN[ *KtDN!BZ-01\i.Q9V̺> jk;?jȘ=r{r4(Qd&*sxͲ\:& .ӣq:cWqc5td+Q$Wf}M(jF[,6gM3ZԢ}.ԣSw}^X5LYrJ&2R`"$K JS=PRITl"E$UQ!YlYGj3qﳗVe*RK NV2$+25ҹpIItFh5WK֎](c3sgY#\=<Ӳ.vY3;nXLi1u>|:[Ke՛lk6VGSsY~g[W/9=9՛j4Ks=̥Ek eQ6hͬݝ[.s7Λ:t֫9K:sW2J2Ӫ緧2ʺP))]"@ fYc~nVca I;9ǗxTϷpf[^$%iqgAzÖg>VȉYUk$V$iY-y']w^;ڰyMc罚ϘӇ/r]i^I95&Ro{=Kf\O1茶fS;=]/.fsso1X&GNxmιy.Mr٧7&Y*n7؎:FMR:)׹ϦMgz.=/˹w.vn/Ͻ^}mYf7˷6ҊfHwJĭ(eTD Jj՛t˥|zb1#hM\>{թsֳ\E&x)1Fvw8ݹy "u~nMKLds|֓lΩCǧ<^zͩ9js8c5)teh2\Ѥ!kGsΝKz8 .׋jzkL/WDLdL to={yG.%ͩ3XY[u$l,"f;ݙ>9lA8=kӝzNzzϘ΄sSOG}]jWrJ@"B$HC8bcsӁN.$ꘗZyҳTVd^]`ʈ Zh]2'-5BUR.25RHB[M9J MdL͗.]#/B]5XksҪig9u<׍l&:˥cګ.O (Uٳ>fv[nUYg,,ˮ6QlnY܁]3Zbm˵[^wNWѧO.Ngn>=SY& =gB'.j{N;1fһ$DH*P$1F,%A\:Db"Zքo>_rsւޙs%,j&jfTtKlJ;*M V6eF+9JkYu]vSf\纱o"6vy9=sS5њ3UdlMI;%I5]lk=o.kځק˭M0k;*1et{qu}B:xկ_tj(t"zZ~=7gg^LVkƎP+D$bC'NQ`!  ˨GRϤ+z8A Bf(%Y6RU .bB*t]+g\]1\9zh4$qa|/y58=9]ybzQl߄aˇY5"F)Ul̀rZ]Rθy_uW__SY1k5Yfƥ.sy9dsyKڽc|7ӟznx׻K6sOaӜdGR@,@YDH1SSDE@"+$#[FMcJN<߳JeEgYI($tYi4-@I۝X8Er'==o\n#_xs3YMyEގ};ݛ::ɼz,H7䮹zkz|طz4ڷKr̶@r91Jfߵz,y1錚:,۝ש|Վ7ϟן+3gvy5^8湛Ƃf{|_ g5k7Yӫq5Ͷ"DE@"%Ed:!-@TH`1&YkI:"GZֲ1 JX2 I!s2a\%qt )Q:oG;o׏ǵq1<o?5Ό_Aͩyצn/'O;qUfUҭٱ-kMvon7my䶦-=?ǯV<^x?N|ؔ.ճэَޏ/*~_WO^^|NNe. z+YB\͹eq2$i" Q"Jd*ViR@dIPWA8,$[O<]WB-eeIEtQ-vnC+,4J2WfКۛ\\szsY ݳg^Q{۳=y*1HͦͳU7rl&q{ףjT.Z/Ws޼kۜLG_Otp]G#9>^MR:|vyuũyr.x]M3=wNys(%Vzn})٫:Ӟ]q:)?atnYq8jB) LlZVZI1%PcUU 'J$F-42D Fl5沋"eD[Fb|n7^?ovtsߍceg7sVnzRQ[\qϹG7z88Ҵݔi/Σ]=:pt ǯWn-ƺq;ukӞmg~5ܨgoN.yz{z/7:x=_Ny[ŦƴeY^vIKHJ^:oחv숋IRLD .YCg- HB$Hb!Rp}~]痑f%[WK9edDՒ,-)/ͲWdvu6b"IRcyiY67^WLr땝q:g~ǵ9pJ-'v~mιTY"ǯ_Lϝ>ޝo<;'*uDi;˯C:tϰvٍdyoW |QMa%uN7͜j :=lf1$%T1Ee1DT҅WDeU*UQ˥ dǐyuMLB a`Y̖Df.Z\㚖y: -Ӓ$k).飞5G?.3?_{\zwyjpMOK4T=}Nϓxjg%΅3UW-`L}.{p錺I5ךuCL1|޼ކ7:w{yuLYlnV۟+{rj֍gMB瑬_>/<ݍ=&{wzrѨb[H+$DH \1"*` !+Ĉe <όԛB'-9w7ձ"k]!UF]MF9cWCY% D@CN5і|7Ws# 4s6[SX:KY^k[\>vUyytZnBJtڸrlXPrk4 Hٓy:ٛRLJjrtKn|n<-}51o+. {9tjrqvzk>.NsԸcB2% i,CY"pBQh,N++ȗ/yY[ؗ-M+%U3\[eD%-^u67NoA5םޓ1ͼƃ˦껚z8ڜ;\;͕szc׊zČHdTGGOYJXVtɼpt6gTf=LkЎƳ{}7=3I{K#׎ JRٛt]['J:}3ǧӝNWlLCJȕL%Q4@"40)@pP(MR@H@>H%}zIlclL-L\mJj,S\N-+M%ݛ7E9kAef6zqqIoNub}VsszFk]9ĎwLc*d"R/?~fszNwNn]Zhlٍ9t( ԈfŁ߅ڗI_Lq鉙ӏѬٚM]\K%EmNLz.;CKI*doK@H @pauRFߛ8a4ʉYؖV-#ԝԲd FdU`ƩoΪ`jƽ=xuÍkr׎bΞtF79tƭcۏU8vb7srͬd .sïd9}17} lͮInu1pۋ:;~vRN~wm6fQsK)]\]O:^m7=(t_*Y%cLĪ[:TNU$DEibHDj̊d|yM5,~9˔ki87um:uƕqaїg^ǏKyk?[ԕaN9zcWgs2j+%s?5ߗO_Q+0Ɖ4c|lfmI÷z:j)L͓Y6y]^ι0$MZ최zyOnj.}e\3gۍ)eJgFˣo,$H@"(-ifuX%Io ALĒeՈ` &1j=q;,[uo:VLkWfTFX, JttNYF%ːFbAd:]gy듹e8҉}M[Z[sߝYusޜn}:iv6k;soWXѼ*Q925,"&%ĈNN垦R=v.⺙Xˈ(JkLHtțlYaYn^Μ{3MkR1+%blGr\SgkU9ldt%]-krŧL\c\c^k;|dy=N=zwy*7;PH[_WԬ2 й5,k&=18csg=/Yrw>16Nqu|4Z}qn5BG>rռ׬ݛ -g&{XYdDic Tb5(HTWo>WS=w=z]r5n{7(HDA3Ӗ) DuH%5MOEE|Y׍WC-AWFnf"q!B4jwe252B羦-\V.,oTgzY32t{sr[jf-r[Ws7Dw?V9Nn:z8y:7;/=㷋nBӬ|>ԷcwRƵcR͍8[UsJuMlY׬usoz@Yum@ZH"f&fj; 1=_az,[IilZyO=іDeVc6K$ Dper*z9nK4 c+/K׏Ao<DJ]٨-PnK'gyǠxbtϋE?՟Any.dW]}B劲k8u3]ws)μϫϋ9vtt/^5MbK4{_N~kõ˵)eΞ=cYgϳǧOOX^^yzc|umfiWP,ͯRmōMȚl+:G|tk*N~WEEeEUIBigso>W};}6uae.EId_oSfMNV."LJ\el|og Xgj6:5DcUcU{(?s[KnFM<nsgVfzo=֌k2=S\mMxO?1۟sg.oTlΰu:Z9:u"U(_G;:fzN۟p^OO'RU]Xt\yu.zfVsj7N>Γ"ٔiFul\̿6%ue^vo GNwk6٪miEeF2Iegcy˞>4U aXȖKӐ e5yJ5/m5h zOg]i~y5Y^7)|6`(ts-z㞺+N^vcۊkM):3s~}[r8/':'ߖxb,1o6YCBԈ2}&;xqsVdGS5s7.0Vnr.^;$o!|,LURX3w|]cezΉ:cDD׋߼یW=M㡬dFg*)9W|SqcR[$Jl]2NJɌU"r i|^sj}ՄQe2׬B4i5kscsuμ{jg|֥Vv3në-;٭2ѬНcz.~_>{yKԽ;qn ^{]>](r.[ǖHt/usO7[-<|,u-N/^x"j-w:|Z$ZUkcrڍǽösߝ:cnOvx\y>>vBwPI]gw=$s5SVz:Ž9Iq챮2.1D,`E&E|lHUV$s,ka%ג~g^]ilfO: Ke3[˦d6mY^/ί\r7qێ*#\u׍qpfN^=-sdtQӟ9V!(PU'#Z;}?=w$//W=QKNOLzsoyW/n/r.zNI.jf_Qkֹkx'ӹ:>Ws5;yPK$"aɩzQTX2K(vƧI+ADjg;tN F^oNλbdM'|1|/\bʤ7g^SrY [o<nt 록wzӿ<=<9umk]\mFyzcS[_LFQRP$i.ޗoY&f:;>̳Ui=<:uqӟ1vVS=1g_Au^;<:YڸY\^}.fi̕t^ëKR"IyϒYؾۖ4bk: F*@@1gΌUKHb*FIaeulyjdw1Y)}.5_5ⳙ"c/;N5䷞6,5GS%3zar#<\P3\zlk|yהqҾ7_'-(6'8uy`Np @ b I-1vt\upL:c>t12JJZ:ξu=7quEWjN]nmvW֒U^f5_\ncg~8,g:1uu<榈j%L$@"!D!b 8 `$2%Mn͵oNm~usdhƦ#x( ]9#]Y|y]FC75\_75ǥF9YeI^>)?myoZϹt\9]x5u *D0$(\H^>)gz\qSn|.te kКם[H'X n{ Qjzc'EϹϦ;]rܲEq-9f{xuq{Wy+7h$%IS #NTI  c1{ř7Yi - 6ڨq.9}3f vP30-voMegԧR=j{ &QR"$C+rps6k<^>}":yxsk<{C=ǯ6ntxwёhsԓN7ɬFw&j=utcݹN٦O_^fgy}qn*:סYkrEa"j "$BB)r'R uGfP K-vr%DdZ#IJgLCy52aĪXao}.=cs9nkYV1,(4X!j"HK)Q::ϧcëolyOWV5D,gSZsy]xG֜N:z:m&7מU,:Μo\V=c~?댕oץ.IfRF=<~:g 4Kחfź#RZbjJiK|Di$uE~8dʪq #U b dM22d̤%|W.*PYMc7LѼөMd+8U!DDkj҇Q ]g]z~^o&>y/g]vJXUO-aFo_z[3F\ˮvxMMUwivw˧F٪1Ax"!ssWr {k. bUe6 ' %f(Z8)BbH#jEQ>)0H*Q2P$Q iiQ"dHe4c~?>1<ϋϡd:#ei^F *I* "Bتqƻ<:9gםh׍lƧ.mFkvP]\x]({QnZ2XTnffly?u篟Nvͽywf;,JKJ-idAQY@)>/y˩(׹ޗ-I.rN e ,aZ`@@j@IztNNw\\zmgsle3#YճٮVu¡-=^5tVVu"td,Z(銊7TRKU qpN#R)*r%N9cb+bh6QP@`Hl9e/oy==]vk*[J53nmj=Lݸr\8^>=l٬hܚgIV@$IK|LE&Gz]IueűRښXXXP^AHbF$SW F=o[+K,3b,Su#-Iuikj;6ڞuor뫝QVҤdZË˝fΉ.[[-}L.Ey"]Fȓk9bIv6,C2%zNTX@:r7^ϟ[qձiGJ }q~5lܽ#epۏ:~7-ѹ\dռ˫vj"V"DY rYTAg+>SShqUi:{6M//!gDP[""U ᘦ3FdKlWd- Q˲$-gUﳍv1%(l,[Ee5&n|4,k6YVW]k2j PR#A!Ȓ',P$iS5jV]<<ϟEUUٟp.εr;%zf}~lOOKm1)Qrә+7E9FMWeDBX,+%YK"GID 7َbZXYitY^f DTBޟ9Vɡk%/:ZeW-l4f:Խ>zۍv%\TBKbIs9ku^u\jPqofklkHP%c' RY-ZfBHTӋeadR6JV:&`ixB(狯8j_~ޟ?:>o@nw"Zι:q\.:i"T_ IbWeKjy7tHH΍f.'ƅȓX .* gԍ<ϣV[c5rrM@Eƹl4CκOgZ aAB*aqUfӎ,=VKEz5^9k Fء &2"$@IgXrU %,6Nxudq{fi3tĨM86: ɾsKsQQ]KזV]c..Ib, LD"!S<~0UR.+7us78(aHa3%j\562͌)qcYiF[3]T+>U-z[("NX)X,lZ*: jTY4Fk6ׯ{.}F]r[-vYgN (뚺ۏ,[u}ΡeыOI^N%|]9twWLkvXA%tUYFYKRe" " ˳asyzbYfȧR^Ľ85,K*i|]L ҺiXꚂq**5賯A}<娬Y 罹'XRN(Pf]UL2ͮ\ޙ:̝3'+ZD-71SR!`IbP' )ccގ.=k9+k;D 7Ua*X 咫2bғ1MWe!-x0{5FiFؙY&:-$K'b:פz:|zbz18"Kn-ذҽIΒFGY^o]7St9m,z;qc[yϩ}..,^uDysy7l"tFJ}Bku땩9$B$!U*aUvfD:KB R\P!+I-e%, Yr8VptTCygDV E%5)fWWePAV@MJ RȦķd4d U)qΔ*rY+xSLf%mz͙NkgQݍvu:stüe o-S1fmԯyͮ|W/î};^?OKZ5:<~ZYo"9N}IV^T7pq=6g[35/ TyWϷ== fge\DR]*XVMcfY3Ej%eȦSEFȕֈ9t9=ytu˦UO,XDRrIE#HF+-.UЕiQ*'-TcYe\Fw3V=eS׍a&o5YjKKѓyKyržc?L蚯W?n^{F=_7w*01Im!K:]: %ϹRzg}<ގ7=5yˬf yqtMY٬uzUf[3RJƗUBvEb k- aIMi  uWDYSʏG;ILc""p+,Ȍ]icKK\EY-)Qipy ם`>.+X5#+(Jyz>5(c>qk?ruk4,&d៦fJ=^lӻz/]VS+y[{V"(Y=,3vJ[Ȥ qԖ2%T BYSQG-2ۛVmTr:kuYYMaCS>g]863̽y}6rԥ17h͜M-߇z;y?/Q6WY:Ie*Y[L}z4F.m4fߛN:ds;,Jvy^ٍhƵdԤjuw&nn+"=\R#(ʉ "HĔYtBQ&VYRo:=>]W$!"H L@Ԋ[զ2-6 RJi%DESlKa&fq͌ML]O?N.ܳ/3heM lQUiRV*s۟ߎn~6.^Yzogw|mLdi$ G;|KUؙ͘Gy32Ԇ^Ku:ۍΥ.}HOZZk]l/0YkUD,qCRY"-+"" Q 3D-fOW̵"$.Q#"EO/XdK 4H,XI+8ҵM.Bƹxé?LK5rY5[%qrש.N{|_Wߍٽ'u<-+<ܴf7(Y#u]:9t\f7ϧǦ]-=1e*FJj_bq_D$B"H|L 8dWr5M\@d%,lU-kDB\6]t7krטٹo=ѩVJ:~5k8{dZN7{9tpuRw-Y*#NY.]>욞jV6O-Xcabo~Yw,nn]'k1j,ϸY~XYuQΜqoFu~wZ#ӷ~hu|Dd,jZQQdBȨTHRcB"Gn:~Yo$h"Q#b +D!G?Q*,*L@BFUQhB"N2]kZsiӥs͖n~{~o=0Mal͂ S^uS8Ny{ܪp׷yy:obK4+&Xѝj筼sWE:Ys[ sMscJqd;q;WV+5r}jηs[rjoNM( L)i C UEή DX@AP! BA_BX,la,TX*j$rh&"PTJѩkbt%`7|k -Qr֬jfO::g=5K|r鷇oC^`LFmIJdIVŷ˓iv5\RgybgW>>VMQRL[w<~TK:םXN53ʲDu2@$*4v䳧5T $ @U/~DXE6k\$+r&B*!UIs٩:tBW(cβsgY1c;+ɷxR۳zL}~l=9:Y_YǯKӳ=sG0ͩvYzgg??Y\|an}s8ۛTU:\:mŖdVKRr{f:fM~u-'W+ԺU:YrVEsiu)]yЕE;Q-gL0 @bInMDRȉVDR6"$!$j)>sQV"Isͭ) ѩg}-2k方x񪱬l6yԓ^|gsC5G˯׿\{j+=SAio>0ًEՋEʬrU댽1q;&N24N;e)jO6j6XIu"gB[:׬#iU˯=~0Y%bl*sHBdeu]9xR2!QY)bF# DQ N)ZWj&\JlM,#Y%5uaIq \bugYq:H$V.0upӱ朾N]z'59֨6y%K5j#=ߋ*RfL1s}vp鷞¹s%Dus]N,BS&k f6iN鞍mBg,+9~n3bD/o:efFIEZ=~Ma++F 1I !b!Q Ȥil/E]VdҚe"J%fܛaQDLB9L#DKiβM{mH "BDB,T qZH5Db"g%k۞ՌZ"Q֓iHֲ1\q3X]r:sɼƺG.z^~cZr`Νg~k.rfnͲksߞw-N5r֌0ǦƮSO=ksg^-q6rypoN-ɪӍ8rYo"%}Wcz>=n鹦E0K*t:ƞ}-Φ6SZ6.ƬY@*מzY%rUzٝ|=iI%^[ͼrݖ[-93eFʵ/̳t,:24Y$:vdcc0tOFh edR$lXX"!Ab%,PDCH\:O).uEIͫI$-U9ן9j7c^_Avˠ^bX@W+U) 9:7<[b[-w:jx[tɏ'/G9<9zcNYsӱWcp\K~nQtk3͞.zyk}37OF ,*s!\F3j_fCb덼iYū!cDyO/I.ծcĒ";Gf" HdRBP DJ#"0@0$!H2V}ߣ-+ZKVĊRg'K8]1c=sߪ i[ LԶb9k1\22Zɧ.tъr]tΧڱu}O#k_Z\NuA+˯kL-gy_2ϩ^nM76uܴhj˹5t¨k4鿞k5ٚRN7lt"[͂DcxeDHJ("$P"(V Pt:HDL-`'>+Z+gk:<ћI$MfT:]59[Qͅn69jHήzR-Ϊ*,vg|˧oνN} v7co3$O=׬/Ι9hԻ)*-KYü:We:[j]z KeM)=w jInu=N(%T@ 华 *Hؒ$@ D PS#B1AIE#s#s$7VNj"WXsgw6_.nr*.ֶ72P坔gWqIK.܀D6KۢAqͲ:Ti_->߂D^7ԚRsړx5?~?K_}?K_P>zk}c7i]jyC6tIP}-u qHzޣ/9WW302zVmy_pOZC\+蹝Sgfά]+/T9]S??k}z#__?:n/IC/⪎?O>y6v9^[V3N:v?Hiu;:/Sd^c-:F-_b}~6ߌ/l]t~r~η;.?Tꙛg-oLŷ>21bbTP#tÝC"zcu^[>'L9]>tLW~& _ڬ [s'RHs~WWꯜzi"lPoM_-\/>B"OQ5A<;z%8Է}"3짥7q3uf6%w־HًrNC'OcX p3lȣ_>X'Lzu *0~8z~z_~."Y?OAo/H:~=YΕgޙzN%w~%Ϋ:; ?aU u/L{J_Oz O _;zxuONӽnOD>:zu,<.jfcu~_RSWV/dVW:KFunVvw]郪cnBtq2NC[q1Bzgg :3Su]>:_s_S`toi:oJ}Gނ(u_Tn}BON3{,ЯӟQ??D#'x]\7b`k}>Px菣P!Kӯ?>Gjk-=uCS5?_#?P?GC55Gӯ~z_Sצu5IAk:Ǧ~y-MMMMzk?uת}d~B?}7ӏ_GkM}Ezw|z_??S_>f[^ >}|z&75Z,~zǯaA_H<}DM~G_P1.OkGp}>x5#!zL= }:5}PE$ׯ3mAנSZ]}>!?קEѿ5_FMM O(ֿۏ#)9NS9NSsbosssssss~)w'9ܜ9noXIh3_o 'pNsBrܬ"{=ȃ%b'vwDNsss疜NÐ!CL3N[~wwCt"zwv wD7o;wL3` wgvw` aϖ`rzmIjFw_=у2{ْ̩3`; pg$ʲ91r" N)reZ#&;FnnyOg5o Gnr? ٛ3%X o󿙸:XTW+"۝ÏSstŴż1r`ɞ{ ȋN~h^5Cx%f5AvW_vZevnT#`z:@,@_%XLlq]wȐ|OO@E?7;&nn7x]ۋd 779Ns%ZL{,û N<]s V7-oaF[`@79Np;;ӻ;!~1R[uŀ[Z{`@f*n8f߮nnlspU$EK縇"{rLlƌw/P2;˓cRϖ8 bnZ;mҲGGŰ-~xCt6d,g)SErY۬N'ė?"lG:ɝ '77776`3sfnoשRu^(x{*[9.0~C-S>!DZ._xlN6ƶ5?#~$E2,ՇP&Զjх!v.<ҡ=wzo"jxO2e6~ eȵ!{՟\.+%?jdv?W_9P< -iHz2jE UmaA4cSw:w b9໛}77􉹿סz,^IJ(ȱJݝq`ziGn~,&ryU6D9NvYTQcc=J!^~}_l %3rn`JRz)n=3Ȫ+K.[+Ԁ$Si}Agq']p9 \z}N@'B AiMʻIy5jqv%zUWd# RtX!ti&jnUSNZ3 c+Pdpldqk)ltLjl/(RWZdd<6e@R1~-msYUFcM M>?V 5Pm`mEr!&tR(jm)Bۏĺ<ϳ]vT%kߛ?P2\- Mn{{ԁ*fT\⬁mX6V s'X+/U }ٴ+ɹޱ򾬁m185/:Kw2ղUr6b>ϔyj `]Vql aF(3,cb=}~ow>!\Tubn&lN6lN_՘{V|hes#uɢ}]m?:r/IMg:,EL7j[F?S_S-**f!+Qbtr[YK5k6IǦ֡6*ƪ+^Xsv'fa,6ǐpj{U4ZY8 ?珣ǩǮ7}ۺhe(cx!-uFT:ş[1T+V7Uȫ kjT%fgdYwO͌7;L>"3Mv/Ŕ]oe`膬5cX+{v-qgQ6w5S)9l&@Z-YUy(֡U0N9SԏNF@q#^xHܗ5<6~bwe]"k2ՋM2̤6v2(Lҿ]+".?ZR2#u-[xDi%vS(:Ȼ>K7;X);2[ɱe ]M}`nMASǶMʲCVV%W(1_!ӚeV>xS^^޸d>X MSnE{܊ke9 f&3(Zĭ;F nKǷY&2Crz~J1 wdJfV5gWZv[}Y]fUY9bg;ZQӍխ kݬ:۩VFKz|8e{G} 8"nbXMACߦܡ<~6.heB~K{{!UĮRdwlx,"lcvwK@sE|l}C.kkU"Oi}m]70cb7g&;'csZFḤ;'G.iǭtMetu*XLkDư,W,N5®CV)iUTQ{-_>23o,,7nXB}bѮX#v~/N]_,uγ<,LltzDzOC|yFEE;aⰱS'x= {wt12)r-\ìRGQjN}`ۑ\|+>f1㉍VWLȸ/bj0Vȼ@Se,,} 2˪Q-7#T#n7f<1ez0r1"u#=z])dzZ7*bݗruSEwOoMYӪ wIf^x U2򻓑xR6m2XaeWTϯ澦Id/muꝱsݿ~x ܫg޼u&Frpzٗs9,Uz[\;f.U ~=]1ݪ60lȠbu)'Rc]#K9NS9NS9S9s;;w  '9'rs;;wgtN;;;ܝ܂ߦ7noѹMf7777*WB zn-GiAaJdc8J)<խfZ^}ʛ7*ksg')c9bs_w&˩T:wQpź+n-]ޥxy ]]@=jRƺ)U. `ռb촂41YNB*ُ]k5JxSq2KTMXr*,g79M͙6f3ssssFٞfߦ)777777773779M͟P&k:&۩j-dY6_.>:Nzu͔=tzNS[de,ZlE]Tv*QgZN4zƬDש p"D+4}x0838M}q&juw鹹9GXWqU‡+VENm8wjܤvʲ8\'UYJcy׮kSS8N0}Fh&2 P,קGq!I@<3gsp]zk&54f^7\V:6?zQې~cMQTZqy3Lz}mF"_yf^f*W=PcQ6yVƫu"ֿtp;dguVȣ*{*͕YruUKL |K/~K("}c VޒZ{mNM ~5ﭭFMɣ,Zj@l6r3CClq_K踧xSSSSSS_NF WC77k}=jjk5 zMMMM}3D%g"r񱏚jg[* ъzj*˱r36;fKrW†ZTMy)ZLwj fuS>kS[ֽTq+=Z_cvpsOnyxMbt]Oz6)n4fEX2CuƢ>Ša(6 :pW{n/MKuٗ!ϧQF9sQ]LSS^w7'͙RfMzjqס55&׮uqgo ,9# [^>]Zqu46+r3cB8OQLV3}5/62j!y`۝6=ø=k̋3;snT2?.]&zfZL)ʸ:)=QҮ=hI\45=".=3bf27hbnk^d]8Uju:j_]6-p1\Zu/"Z7)u-*32GeZKӉuU2XZry!7 M#?~S>Os9x-9NP4؜(>׮ffOfir)r(^w +s 9s@EoSdԮuX1΋GvdWa*6e٨Y]g"l~2E^n-i7-6?&هfASY^'2˔N*T}(VPw# vM_+;mp1ۨr+Qn- 88MC55Xn7}D>g)]SނӔ'pO55805Nv8N8N35vIq'iagegdNqq~pXjX:-ޢCW9'ޛjl|Js_yjƧƪu]/K-̭ɡ8Ey2=ws˟:ů rqò)k,ZG;n9K{ /` 6Q8¥-pm-lWVN׿gTzv%/ "bm$]=;&c7^[.(~- ҎkF7 ߠ3FѹMPrfkn7<FL"g*E__P9NPߧq3q`ˑ_#*V'ɛJ㓅Cׇ]T;A[w鋕ܯ&r%q:h`rSwgm2誱J>-ڋz&)s9NScrk?^KC/RiIJXɽE㣶D>b[uY͈mc]W?ReF}_+ 1-әSƻ~Me'#QSSgcq-k9qUZ8?nąädYOI^%we+KR*t.\us1wu<<[HcRMMzjjDչsYzjjqg5555Lm06g)rÿMFr3V(X3sprSF9E-ɗMc;C} {Ք qut!6>Κ+:mm][l.éSO3Bj v' siF@w4;qQf=щ3=CظQ ~{\-rWU5u`ZY1ӳv gmP^qqTXr=?Z':ڋQ1n UÁ}H% A$NSߦI8rE|zr)o4߮(^sMMMMzjMMz5멩MN3 q8qg8N+838 ~TC Tպitij&=θx=IZK+W_mE@*U+\g ^Ns }-<6BS`nmkҰ*0ZXˋ ]x*td]u]U=ۣy{llwBܺV]20Wk\F.)OfiMz~3ssSP =u583S^Ж[Z qk7+ LͼZ~EqKyΩ8S)}ȶ`p˖ nk &[qh96g'.ɑVkj2 e;{Y^UFOMew /a{mVd\10T(۱8q_3/џFel ]_/e簢L?6]MwKd9-ERZߧmy^6.K[ӨZjR kk(Z8g9S9f0<كzkSѩp}83S^>58C³SS^M[Oyڙ7 ed)ΨRYedw}dGk] ! *(2ͩD`![ڽrUpu?@" '"%JsGUgf8@/f1n+pՙW~PɟC-uc(^θ?NrUw1ʤxlց*abM>zTl#ia`cتԝGn] 3c֥rGrNSp!7;S8jhf gl*SP]A76#0nzwgr)r8((Mxߦ)NP jku>zM}SP}aYe+d\@ً6Xb/70d8{c2޵FjGr 80tUj2vqoX{dU;4Ucd>wqż:^}14W7j޿xFΧUj2sÒyeVn駞wPgRDMWKq(b)2z$ACӋe@o9ʜZb8uig}~ݲLz}A3)n=G];vߦ jk^ߦqgu驡58MA[lY}nط%fŒo~`Z_oujNj7FoZkn#";t~x6=|41kY_ CǢ+rc.25.GP.1mynRsqêìNҵs9NS 󙁧(=7h9 6>a3s9Ns9MsSsp!aAr(r8^syܝ@rs9y0;֎zjjhby}w77 3{427Xo56ފUw6^ w5}7%q ݟ^ ԅʿ"ɾ_ت9WuYWin2_^N1-wz./HipK.J~Ga`n>dP[ EUPw-`6TQ-6d:P}uQcWCkJۧՙӹqf2:J*ۦE; :n)j+NbZm Vie(A&na}@ +8MN3צk Iq'q}u5fVpg p(BDೀDМV&& N0 838Nf[4u^V] U߆fhVuXZgbձ uM2/7B7R,/qyWVf3[zy8#ـZ-RKKΧK+MX mɢE;tsW7c]UX/OsGPSӲ$_q*랗EJA{8|.uK$Qݲ,d}+Ox^F=5)XRFBw'8^) fw7}i '1;  r'(!9@f!9 x&9 MH6 #^ Q2Cb;wD7ߝ;w,܂,r9/@9NS|Y .]/0)ʺ,TUfR3ԭvpˑVBY{ڬ"Vbj{˩LN*V "=k]\nًuLnVX.ŝ.ʑi䙽Aןܺ ^Knmyl~^]? &2-[ɜ5HiSSky=2d]r,N}-] i*r,ducf**^x}tњvݩڝ;FfvL3g8N5\3 MMMzh0Mz8 8yf0v7a_ 7ӻr{e`^KWf&-~a&%Yt˕tĹim wPmXҳSs/]WoL}X6nTx='ej unUjWS uu.:FN'iVd!7KE^{]5*^MK}̖liVHsSSjƧ_Ҭ\3'+U eqJ'c\5KRIbppBR_i^E*h뮆qjz23VS7P2,W=ࢾմ]O>WӱLZ3Ph }OS]S'fr9s6n|p>BD"@%!Q=,]6HdÔ!{1o.^7^NH%㵾{މ.O2D>F@t݌]漈r@L\NNss 779SaLk5p.UVOzɨ&FG\uq^ETpgU o>VCC>0z12+n~ wl%yL{WQ 7U59; \aP+}-N329%c5J2< BU u69 k._nT&' -a~ی:]8q,GT9y|Yדq毆6/"%檪bUƑWl<777 4>Y@rVSf 4Crfe"YԒ>qhOi3&RYi16D<#x?}yQ*$D]i`dPf8W5 4V340TҪ-Shv4Ũ8N3`S!^IyC8wmTFEmXO &eUFF5*䢻+Z SȞL$`aqbE@gZ GZWU&.mCU-:m!hgR)+6׷MBp&-4Yq3*:HjxƵeRV-]p>VM84vK\7z\egKV*]j]^3Ҋйǭ+:}qgq+' &3f[3s2˚s-'!9j)dNsHf|",. +>]z6WXFFq hq~\!=l8pbD(`c Dpq p'XkQ bUJhЦ{ZM>na;s" 8OWKVg\4kl-coqPkV SdJָ7![pms*Gz,l[aNZZ86J'lzV S̭K<ګ^0:~e]G*s/TRwnYVzb=Us5񉥕HEpbԙfYMPmB]ZGoQs:}|+jߍ/!$ 6~EOx8Q&G~Iu t ^\b9Yoܽ`ՏcөSh\:8t{kgt 4x[%6xiMEP"^&n;Bv3')< S'g5555;bvYqg58~uT 2eĭ;V[ojw aCe=P㛌]S,š-7qJ!BM択܋iƱUU[YzK!%Vs Ur"'{i:f]8Z^J4g-Y9ޭoX,݌%j5i6PR_nJ5Ul>5;ڌ`Z섢hd`l'ZSm+kp0vf9Bw a9\C`hf3bswDm-u+yyԴߜ%b)'lNܿ2zY$2jYgsܜ/;0l*. "h$554f"hzgf4'8 NӘ)r)r)P%aspm*ᤝ6(8n%fY10hݬ-WFf2 ٭ZĽmcv6]=HBvּZ sn% rf*{{8ill EUz6)JQ1a0YpXx3A{\I@g%]W[5$%W0{X p6yݡv(mEXZ'sfCu{3.m.jG郴qUOqM `s+:Y ͝.qSX"Qc ecW?56!i}55BLO3sњ04fp'k55jWׄ2U9 >ڤȨlfRӣ66:(+-5N]aSbr#䢎;8c1-V30|`FT؟drSҀ$IEj*-g(9rw`x?F=u >ͧߦhl,Iт/ߦe QF.J.Sݏn%<ЎGxɕlQU,)SF[dWۯd12șM4EFўf>O&/wȫ\J*XnŻ6VAxugLWC& xv[0c}Uuu WkJj"֕^f97cpUY^lbIc0[9z=N~sع-ɇ`e>;xu 5 DƬ(Hi)t5sX5;7K//̕'^^ wwsX'vsNb+=Vscܙe4\,AEZkQq1{]Ln. ,\vcV}C2jbű(X֡R21~YFl[M`!&=.cV{nĠܫL8>WcP.k/o"f ɉHA],)IƧt manl.ݎ~S)_ݜ yKtw3gznn jewvNbAg(TmkfnZҶ-Z׈rD|7G|Nl|Q-\Փg<7dɦExyprږMh f%'Ycy܂ܝӔ7 04-d58yVTeXJʬ+(ȶSdE84ⵡ\T.ƦP: `(lգVާ[Q*Xܵ;{jrLQa'h9Pc;#:YV.VkDwEVY9Tod4v̷rwlRNJƎ5kDZYbYw!Fjۗ-ij%=b|Pm&;fwNCYb;7a0*ж[rTf0`?GΣ+03^`yu:wcV4Ks([D,6Wry؝qg`ATi!CJTJIq!v@vk'8N!Q;bvj ۂہ'DN"^x&m֗`}A{ +YZK d" [R&P:&//KUX\ ͛q5c]gY6~+7r`RRVA)͚ ieseS0^,^H<*թ N )˭6졍W;+-n\c~]׋RM&9{Y[ed>:w6n>Z!;s)YEK%Znrx {,gJkD'ؕLg@, G=>:'$ߏ@a'үp^ϢFh}Chj֥ٟ#),MJ!UXnY6P宍奬s'8,g pgjqgצLMMM}SPe^+J< l5୑`Agڒȹv6K@24mb߆[nVTRjМs j`Nz,bU 2.caٮkug!t~&AbS,h2Iue썃s-dQ0KcjM|lc>+kc%iOQWTIk/3flyeDĴ_[lśϮ9M)ܭEW"Ox%bO''3ss~j nrǧMf~}sN,޳J[a١]rB\Rl{Xx @ !e6+;7b[[dLjIm6*01sqpd~1Krl~/=y-[+mUD NCZ7Vعj3X~vƞۆzF&8\f: J[j[W:Ƨ"{YxՕXncFuD/$ 8EOc@`VLâYNU @ X:6rYx%T~۝K4SjkFDEaK2Ai$~4tt9s79mQ-s9NS9mmY mNir8,NBno &2l^ÉhʀW[‡SłT:3 w ߮pa+Ƕ0%{'mU;5^ 2̝Ou]k|bq=ALصKUl}Ȏn!B Ck ݋a ;sg)s9F3&SmHDD% nC׈*~Kb {͗R܋ɏswTZ"ineY+'LeƷ>Ж{sU6|eçOU5VI.W} 8 w/u핗kR)i`:'-ef9,nOmM"cpuW$5webgCU(y(c^P^5^7>YI(K*l"BՉH`JE(x겡ɰqhSY}1;HsD{ "2|riwrabQpb\Ɯ^5dUNxa4558q&.5Z @ EjD:1F {I f41#T'f5gjv7;!ǃ @'LNLSN;Bpg׮*L8׹])Ֆ,b /lG)dz2Kc6 9Ak˙i[zCuutޝ*U#*(W )jRzdk,ҞPpca, TZ2mٍ_—ユfsp5{5o\SbUe^5pL9~7VS|rz !ƶcZ.|`&1-^>6͹]OMùU\ے\m Wi1]Oz'P'@Ks7 8NÖYKR *4d;5a=]Ÿgn[ Fw݊cnv@H@&vf 8cAkgPFmPN3Uqgq8N3S׮N3 8 pLȯ͊ةWX.4R+V.;CY,Ò3LnƮM̔gX,\XhZ?!t&>ꔵ&[ʺrs+5U95,˭*`WϢ+"pu]Χ}ΠJԜlLUˏ'&:rMSS^'qMzkN58znrS~R&A@,gX ]&)*4kx׺`gK`WEo iޮYg%@)b6OPZ[A=SҮ_B] R.%]n[6>>18`t[P\ d)$GFu13r0H1b(G"ʴO!ԩbV eÒCŽ٩)#*J񨭭P^{jWrFIC\ޡN\yYB D cڲ%rQYWYñ;cf*3k)F5ҼH2ZA܀ ]R+6ON{go 9%%U5LİWe ׯvY^/7WUMMvٍY̠8nYWlW| g@ɴc 3'nvgdCNŪD m4!ab51D|Eq$dģc:ux-pd޲aWh]2r]D 76!08`_nnr)Q55=w'}E;A&g j؍\Su(ƦUS-y+xjS6GL܆o+'#M~Sprm$oJ.ǪVfA^Q N@|YJZ`d(Tڷ{^Xb-VdVZrR.= c4O,Lʩ@VӢV8MȯMlj1-5G7\PE,kUUu4&@yA55b3|V'5CKqc<~XUCXf]Z|!""J)WV;v{rc%4Ze_1v&ҳ+`>9}{7779jL-M#S &؛L0)em[2Yimnp5UYYWܡlU`Jm{quNJX <ΝcSwN-,]^^agF]Ҝ%Id#C o%:Uƣ}Tev.>.E4}k~/urʻƣ}W|<ԦkWgJ͗&i36'4؛s.'r'xCtk&w9 5c_!a8̱KCQeBlk6%_pb?"x~Xy^:(ḩC9NBs9r(֑ұ8WeonZqXmi@0|uǝQE*ӀhlXh"va=粋6`v7qc87; Ŭ@,YN0#Oo0S;sj0} 59N;p+inlϙi3ۙ; N!bQ3 Q;p i3`؆!vrbʨ"&*Hs]^>+ũ0Zu~Q\L j2WQ7*(>mٖe>(M5d8Q]L-eަ`eUYgjo-%r-Eū%>>s=苰8°ʨcduvUdŹIm]:^e kX4 ֮ Oj,s$〘իEu*. e/ӻd&5UvWyn8+T˻aH b8\3f،4a5q|ϋXܫr%m(oBhB Y^Ob+9N[D=paQ8DY5F 'b%v4D KX@cü/9O3XnPXdGp&*SxUeճ9j+{4{*lPM*,Ǣe;0[k!#6&7ۧ2R..bae=H?t5ڛ-ZhVlַfZQd>%+mcԴٚǴ\P!D/LJ2rQ[_`1Xƨ]-yg1oEيT6qS&jkWveli/*>/5CW3x" $=N}ݹݹUca Vu.F[*'=;'|C#sF* ;ƱlgH[.ƌ"Ut>&jT"!'Iw D. wFXg|k,;|AOg9BN9@^p4&2qBڝNę& `Nؚ 78"qY9/S[amcbƢe\auV>/U_vNBv[Vea&Z}d⤻X7Qj) %Y&.2.ظU T|N'*恇.~4Ȯ+o+#k3Y{] )` տ}T׿o# UQgTW"N8)wxû+,^34ܡ-.Tb=%`YKY\'#*_Û2Zs"-#:,h)vl*X({a)Vf˚r-gid\ԶKc91N5m8qmZv OFp Ba 9CdN'z -FWT NNɂj)-0W8N3Zp<_MV޻`JUV+xH"lfN;dYBַaٻkn䨕}bԥyեwfڥEuc#_n?E "S]Ғ굪/v"1M;Wuϸt̶X2چ9]C*8tUzׇk b:O%긬 2|{qjԝtq1ʕ'g?!c^.^E"MvfEaQ٧b{T+W#M:N[cr ڂ؂ Ye+;k>7@'lCBǬ7OX;>,'0TkIX)7]5'q1CHN-9fgB!'FvZ APS`v'555g4& '"+je79$rM~WcJj{c]UJ+Q+[+P[l6hqJ[}u ΝScGQU6Б9aNM-u{vXiZo7QrJeۀ՝Ó^=e :GNj+u}:f[&V0{YRYa{Xm)ny3XXAYcERb})ƲNF6F3_feN1  〜}g3Bq5aIp9ۛ𾌛aƞ58} щ8Ǭٍ\k+nrw'1],yD"q(eD.b1*9Mnj*@+-UQQ%6n%#d#ybWoe%o}#bTr8&IK/{ryodՕ~M6>eX>Ȫb;ba{TǶ-i=os)&ce:5ҙ9c61h=1n9g.܏ਸ7d(x%'X Y1qVCgEM55(\NsL >@XmE >*Js\ݥ͕hArc†f &;F>>I~}Ƿ._\UxV\E^EM~]]*v[3i9R*اɺܺx y}QS1?:n&"5XʭF5WPec.ŀed`,ȯd "SU>t:`٪ol8Utd&`v[ ֶIks^Mx z| ѡYq0AsP6[s~Mzh Y8A\j^w c,e`V$Tb^A`k9fUb@>cYX݈ Ej>}R՚ĭ:2Si|2_Q8^,NgΊ<޶br:N3Nj7FlMChw'8Gf Pj>19,d9yQ+q77fN& X1hTEzn,עujkp>жAl9NQws&jv4.MDŭє~_GO%xs]`iںHⷖQGOPqWBdUkNV5v[ٺm)NX:|ܕX B){ kƶw<&¼WWLK]fJwS[Y Y aj:g_Scimp+7%+I5aN*TpRLMw冮2`dadabY"o!ޥSY~íW|(CLs a/@`oCD؁%C;@ɍ#GPI.Z׍=O{AsDF1L9L 1KG,eY,3^.cJ6܋fc7ߧgSboPn:@g# }8C\㩸w#AZ|4u"v{q=C$8+3,PD;k =OkNّ̳0L|-;~=L".$]Eڔ\"0X@At,9M#d-]yݜ&F b T199ѳ1s8s{+. rg% g]AQk2 (@XaZQW@ψ +HqueUB^ÿUIZ+;%e:W=l[b1Vruek~=מj^TdCqk,2;H9 9v \./J}=49bkn^}tSӇ$-\&=s2ە/VsH.D5;b#Pq 8N،jjD 83;b=l55`YpՉqaHhS਍QI-J3%Yb Mx%ۘUwi,8f*ǃBFmKl2ׇfqxQ83 JZI\K"c"gcŕJDRgU%Xo񃊩Q8,{-YS(8+Wk;<ȱK, ajVyJ/|\tήmwӅ~Rۖ1KjsvWV8,W_7l**n2p*(5v_kUGϨTp*eUbVkjt5eՑmrn?ӹϑILxn ,ZI3ie~6Fn=#3C_u-L63 Z]]\ 5uv s+hCD)8yZpxSnoǡ33SS+4f8zVp D ؖJ 9a]; [e>yJ4 azL(e>)hp`Ý5 ҼVD\"' ,!`xa&7f.uwmh1DWnu.+oiŷ9yNcXY)q9 -c;|n7N]i]cRծ-U2tZݬJb{ocf7.O_g*'C!@PM3+C+ɗv[L'ӜBy!hWsD'qg h-S۝ALyL``h9o@'pL(Ij&hBf6nPf*޻jKv3y8٘\re-8芉cd^dPUո))+=)Jd(KrllL~JX!oOh'8.Yxq%̬è ȖT M/ޅhPwYdbz-YO!ekk<]"^a-NpԪ8\Աiɯ!]KmJ9^+ꕐB՘'X2L!guaNxr-Q- _PSj^^De [ wPӻb0igiaNj aQ==4~P+ q.cϗ3V]R9 ƺjBܺ˴-uE,Ih5seV~,MEiq5URmWd4gBQxBIj"IE(o?_JxOܟPARvfnƿqVWYgaQ];8EЄ^Ԍbee] XA=^cV[ >PɫSm߷%>CXv^԰ vy"ew5/krp;VJ.%Qdua{QΥU#5QYSCCNADoab$n5&-fv6 / v7ǩF8ŧ4fz_ oqa ?2i9&d!q3F +1,ÞcqK-U[Sm#!_*, \Lu'jp ģqjQqn&SS@S p±Hkl㧱5N*qLSb-[bᏌ]i C3vɼTSR/\GIP٤uSN5X&a:̳&ڢ-㶇XWMEf>>c|X*S_;,+=!O!V|ƿ2ORa1:tȸK+p:v)ؖRSۉcFo1l+"bZ%X{a(4ADj qq!"a"6jKA8'j!ǖc>Å4^* x0 0U2ZƲh.VF;'w8N:AL6'1 Yzjj65lZ E*N^  bEcr-+MpT$նJ<Rƿ2iLj)N$:骻0q*QU~%\ELf}°eߒl&]l+@k)UR'$mkIt:_\$ox<emNY- `(i^M֝G,,uՅՏg';#kOJ9EMt(%^skjtn51oSآeMq2JQ-Mv%4 X|jipw88N s‚ &8ʳB!h;P#c|Pcر蝳ԉDw`xr؄xwS.DuZ c7SiGQ5% f3BqyTԱkrrfm-m0cZjY7PYcZ=U@tkFȍ`cŧNȴ,-Խx%q@2u+QVϦyZy&-8պueZL̵mVR)%5ߊOkbk{h&f>wuyynR櫞'l,Jv5)s9gS~wNPʸ7so3G_C{BZE![`="ģe r/=2=y Af5VX WCSV]6ژAb'56Nbs<&4xX154mo-5ȭzNYr F%Nr0q&@74bs圌5Xet.4lcHZYAe5Lq dn$^ vXK4*R78<Jk+j^2 Gͽ ^QGm1)X e5<ɉ_9^<: y՚3UU+LjI>uQ4FfFKf"*oy5[J- mAcی+'fMH\cJm6aȳIJG?X ~]((rƒ Vv{򱵚6͋2yq |~P*J8; 13m鸆ݡa9,^؁|XsTjK!SMh' هOjË=@kih+xxf=@D}p+*&[jLqd3Q LᕅbHvh\y?8j),>Iwf6Y,3Pg邝deh1ɃxAA crX]kBRU<+3.fcbpDϹ=ۋז_dt|!IVfE*,{t̾ 27ȩ$qh"e!If.xiF1Ǻ#rr,w'TܡYl~3+61-]5]?.m@6pW9a*v Bota?eN5Ѫ'r،l1^͵Pg8WԭqFjUh"0>aMс ;pW8@5&vcSgn5 p'k+ rz{kݞ!ՐjDŲ[mF1>{кux|h]D6,k5Fh*v|F}CkFk <+&{c knqQ'e]cXӎI,gv%j]/ܨ?!2ǡC=Ϳ#vު.˚ũU{^+_ kUɑCV=H¶V lҥ\4 CA:ٮm v[ߑV ɻn3E 2rV>(T+]I1a[j0wxi 7;sF5Qmo1j~a^%5~F»f%a`XhM mfǦA9 r kO"r Np<7 M4Jl1jQ< P<9NS9 g0Acp;,|2bD4 ܶYmܲn|rzuϙ} r;[lE67LQ5ܝ 7Zi@w6&)v)L@!ihڝD9 3zh<)N*'s"S h,w)vzG.<%s#٨Mַmm;([ۻ&QZ"%+\7@җQK"Xtrl&3x_bP;: *k dnUP+4ުsAǖ-*ͧ"$Ja_BvSO#J!kJ𜸜1K+Od 2e4)G2+dwj%ЭQ~u\#Dv|+`Ia*9nnnoLnw'wSYtו\omy~]C5-g7jIG;L{Xį:} ?V`Hv"V<օ ;J\xpP'\ 5ЫJ߻+# _$fNQ3m7l~I$bG)"wo/;"e~s,R6N/r_Hҫu*ȥyFLC0uc zpŏKy%z 7q3;5X6Ҳex@~څg1T4t`HƝw+cc5IEawՠܹr[fəc9+EPF`k~bJE,ȨdW|mq`!f>0$|LRwf=8.O۳e1ܗɳo@} u J~s,j \w+ٜ|$>r*d-&xGnut{d"o!Hj~j,]rfF6E[kbCJPCf>03 :>=u5A@ 0.2z'K[zONI.Jdj6ĩ.Ĭcӱ?e,TF+ Y;~E-@@5w,ZUP 9O;s'uc<.wXee1 _iM̎7mjx?,KmS+ SUDĔ]23@ؕ=6_`58\[<gGW._ |̛KiRZul|+ecט6۷ǁ>>UX@ĩRcYcUWΥZpU Uܚ*]F,S,S*h%1?Zl~Uν%=ms'8l &^vAlީ'N-y5n2ia*&P{KXvMiH1).ݞJb-TƫU&+Ѥ|%\+kOV9wm.[9QcuUPejIK* VJړhYӍbdabYն'-MzFAL7߮9-".D='hԑ PT!N҈MB ;'zXLnlg@qb9wLal̙"'kqqܳ(sci1ʽxh*q^PmbeE*dhñק$^ޕL읥#8dUsUڙ4(S!O Jjn;~n52,%o R+^OPT's:A*LO]kx e\(6&2Zi.JY&4 hfZ-3iton7+ .(jX^ٳ/+ae>Puj앎Ȭp?lJf1 XYz˟t[^ty?1r r𲿕x2^6_QkLÛ7Tl^Pk :p|OkL/MCo{R6O&}L~^nnM¾~Ht&jb?G66qok1n"ۂLǧ+^S`]|Zsa >lc ߃s>; ܮ0/_#QNmE1qGi3ĸo賐̏hVMeNޓB+W3 dӱ6Y]sk/-Z#6=-s?=ň e+U`= ,cjc$4#/.Ĉ`>>z]5fa\2WB&VAZnwɞ(쬚qC%Yf.t1ۚQyX8ء_Yx <3P#@Ewz'Q;t;xm>'Mj;ν8 k¿'d䷲iRze|6bXqktt?2-JV{\} $㽨^ XT}égGZPb8U @~0GwR'5 B5xpYEdNQ'qyFEEVzjy.Qw~}f=&p7@_7 m6Հ'5mGq@ 1qr׋gm1и]uk,/8.s-r,׷&H*ieѕ[< |$ܓQ8j5KGrxqE[Xa핻i]V+qxLu8DkT5BC_ v%gNæV)dzF3Uq~9cz׶q[f=x5WӔJ SʂŒ<_+-ͪGm>kT⽡8,ERws5>Sp*4#a|. 4ד| )2-@ͯ]z bsB*ZllidCsH~Mܱ`!6=>S>;g&h~܍gVIT 0r3^JMq?qfsaRDa'(sI[c^szck#[Y^5cn,7pw@*VUH캮cvFI zRࠍzjq3g/]@21Yݥ3%(ԯӻOkpbר*XwhAK3VWp51"N^aߧ.omDZV=fq+BfЫk,\όv!ͱf6vtLGvWLAڏm} 4KtcuUn;h0}g1$#s{b0,YofR8T=P3֖+v܆ dl{hElVyQSђ3(l溬7ŘY8U".Q{P1Ua,рy' kXcb=<겵yZ U_@ ZE aYPUzj5`>Fe U7 >bmY _?`а0faMƧqqOg1)^jm}-MhoN"g!CRŽflCl=ݐvNU$:5B+ wUڠ_&v,=zYR9= @w= lU;*^4vcj!6r"<dVia"&c}QUޔTwU|o:'N7P_h]Ua1Sm[ɫd'u:*h.*x-<4,#P~xgA+ܛMOqyp> 4xA[Hl#A*b9-=?}YCj,O"fe+][bl7 MrD"{>駼a2﬇9u۬vf&қGb]oeS(Op9BXͨ^t*}S.s\z, _.ʽsڼ5OO㶱L>S^ˆwy&5~E?w>#6fpD7e`^.5+){_YR-[m +KΫoqqhvg{fF#hWhs֐-%z1՛z-\kN::⎈ϊmDerV&xhN 9Wp!jd 0>9U8z2>;e '2^]E{9yс"vem=2-3guXo3q>}67_LuUAzPU6-`B6!qY>GڗA|@bk+FlnZ,ߍVNF#@u3+b xؿkvLn|Lt3?qPlYH^gאn)e[٧޵)ٯS]^=)e(ь=8c}{\c+zPpj{2iqaZoH /yYP']'KⶔXO IW~jeh+ 8qԱE;DFsBjqXО&`#B8Es6b@c&S1QU$K`Vmʺ*2jhuײWG)UŬK+mŮ7Om;MCq:@T)MHS' ?FըCO2־S[71U錱u}Ϣ89N N (=!/ƶqN&;vCQ+1<!'o ͔0SiXbc,{}Jk!c`7 kº]ڝ6kF|>AbVU315ڢ[%kH|G~LohL!eu)xZq*Hesra׫lz'TQB.8:YMuTw 1#g5vqv/=Ƿx'g8=mp.U ֈ 6" SQ ȧ w?Z_]e=q]"^ȼmt#y/xB&WX/Y?gX1<{)TT˒ft>[Xc7z.L6&p^@-FoMnn Ki[ JLUAպu\u+>^` H>= tljqjJ"Lk+3bV-ʏҰ+EOίeңEj- (Q;1Q멎'IJоKEdC̰j ư1SZ# /)PճZ-W ůa\17b~Kzfg :DZ»%ITF4߼z\Vt+lEȳVXN ;dn+fG)MWoTd5weuUv#ӝ31MI_MnȔ4^ V .^1+Hg%gHb}泜USu[9]4Bќl# Oi9WYáseܼ?t}6Xl5܄LއҲ^^l"QmI`4WȮ#+ރ<ss~7b2u]a,ZNr;ղ۪tFNf~6F9&W*7V[;1M9 ʘyꧨe]QW%95LzZAi9r(6uWVugY132+FP$L[ޯ`9{ɥJ_.NQwi& [XGjPr̰fH s'A*FfQf9=h D cV |UlU#QM`7WtMtݒW\Z~kƑ۪Q|V˴zElCeiuUV3>!.MUFD 0 /wàIUknYWeܮ, }uUMU]ug=Ch3ñ-RɷIX{S/`cęn̼q?u+?@Rqߢg l;s1l eOSMz~Tn0ڕ_&?h ltR̍mn ,q#)Kev,H/%}VWW"ʫ?)ӲYW+JzY4K3j^eW+}?1z k3ˢ*{z^E_N5ʲ-cߑ\v-AKUbeHeϾKl_c}EV|~k6jf/˷?HnTȮKvY/~J .!] Y7*M Ա9}No{&Sߙ{RYEB»y4FOTo:6/fdzLEOխ֙g&^ug/gfI3x N~^qVskٚg('VݖѲuk4kVڬ/o[.5\ 'ŌQ!CX2m5 f܌1?+'D+VbJֿp}~sW1l_Hyaߏ'^ӯ(8}-%b2l+mYe(ZqE[CҖX*Z0Kz>)^F}緇W|;{c;12ںfEJ4ٍFu=nINNR3 {`TW/f,z{ΟXXGPtoC&=tǵ'M4wZ7+WåbՉ kd:[kW_n-[6 ke2r~QP>3JlKX ZSCn&JǹE-2qwnY]}쮡2n\:ZnV_)S#ҟ3ٕiQr턷Y`z!ntH(b Յ`'zrN>LqT[R!{(*#7,fF,.ʱ'c̟J%W[s oF>(`.Ul '\( x>:WT'}6c|Oo|ZMnä"MySq/TN*gc6%|urJlBb*2Z2{;SkkdV1@Lr-~z^P>kX>d)k42dՋm=_ke3RV=?LK)L~>H\y12!FٸYs+!՘6)ʵyP]5vqdo::&8-g&cC}3OkaC9]g&U]2cdrij:ſµ'M%n-T;wj\UǼ9`bi!)U3:@Coͧk@]A8?6p`XuxEmw4Ҩ|u͠>8`Zܺ8u ə=K?Tɲbj-٧Ə<|G7-ŢLO,pP]-˪eŮced?sO]+a/iR|=[5V>$,N.`\a$ Z̼9Ww =(lZ jt 겛Eˍmw3acnqr[ԥzFd֛Ool;T :Mz q3K?..mrK[ gp(ox׃Eu5Ԭ"[֢k*ˋ,,+gP&gYoWt{mi3[k1}j\s^=O)NCԱ7enoǙl%vd3:IɟX#?b LQ \ꘘ-rye!Yh@vEe3>e2m5uZ{I*T&P{V=b@5\o7+.t̼uh]?eP֬{&oW4w11,b^0ѫ"2ⶻ!y#;h<*Q46\O_kaeQXhk]OIBՄư/M^툎r:7[ {n6󶫑*罡#2+KsR̜Jq ܲY'ʬ6NNfγC[݇u7K"{,-E-HiyFnl3 3{9zoQDrC@bhhw8Ӹ(.Usa_}Ui+=3.vm9uX|&8vF7DzJ*n=ssgx5VTCet̥g̞ef)rqż-b)OS/ WB:6YM^?Qdu?#7"&E4S-heVkV—Uܯ#[{J -ϿO3[JgO|['%#Mudd=FnlxUiqi@{ϑ̰[B, Si{6.SbtEJzVFA^ZdS6"$/x9Vn(Ii2 l8bw J*iPkaΣK &f_aޡm3+jQULĔ'#$|s!7[Y{-]n Y_ObX?(>ߦ_cKR^5~i.xƟMrfG`EjC"]6 ^^#t=: z .2= -N@Sɷ,ϾV.&1% vwȂK1-**n/imS,ư0B)ok6c),+!q2U\Tgp4=R~b> ooGc"(2q2W,GZ|\27'tx&2 Q':w,̅qn-Vڒ~*Ҕב׌;:8yr`t5~Fp;J_N:j/}f#4R?1sAͳVpݶ܎l-ۨ6ֻ+uvyNQ,;6Șuab*!͵KXĻ?3FXvk[nrZfKrg [ztl:Ejy;lkΦ^&&\6&n>fvW:հܫcx`rN[)rާ]Y"h 6YzBB3_I>=ML]#>_)D*l6-0^;F.NT=S$g'.J<~ğMO>}@k0}lNLc3v=1y%.wx[-GxO~ '1o'9 ~b'#UlT *^l?*:jV"Bw+3)o6­d鴬vIcK"czFB^U_. t@ xa:HwC0"! uKŦ79@ہu`nZf| AgtY{>QC,vـ Ͽ6@3sVDNN@&jO>}<Ϩ"xO'C<(~&&2+bZDܮұnӥ|\k8ۍ 4;UТ4ddXW0ŋuث=ݥmkؓ²DU1J]\`ҹ=S7!%t򞘓!sah JVn)%W#*`գK#G^SVt@dW^H;KL<"ߎ(EMCQEC)r ( i[51n+q`ߠsũ-V33O<{/-GN!WtQ}8 C-8=ka-|pS***d!؞SRrrA5tN%fhc܀}>:Ա< pUaߧ>0x}L|vb"3 @f&?SLS{?@$fU-1osӹ]xW5e.2nog}3fjq3^5M^n0@ mCxsΙvX 曦g@-bZi^Ve&I:\O%0> '0 $+|ϓsX6K '[M 55?##<7qر@|C?i[-,t!b ƆW]5='2WGE8CgX7 n<Dw5n,n@FD~ ~_@<@w 3x?G:uc`|HצAyyMO[v~Қ,KWWhEw=ڙ N3؜Ԛ-rirXYS*ȵݖbZ ŪS82/5yT"-N^+/.3y G *0 U լfN;܈;}p j7ޛ2?e-Ug%a&M]G؇cq9A \qJA-ZqPyyX´!0c2Mŧ%ƕ[l`Av ?&QNU:N?W9R\&ԒVhj9k/9aUqÈiHe)ZA@65SF3[F>Dw75ᓗ++z윧/?6,/RiNGq2OiFNVw@jȾ_RIMX6'rd9@{3`@L>: a7.+5 &b754g5f6]kŮ w#;Y=բkDoN@6~i`ZĮ-;|W 1+Mx!_O=5,`@cyaY6س<l]\"zq&x< ×p7.(Wuj!~RU)0nXs-Na!*U!l{ [xתRU{,?s7+.=Uh}-SQt5]VڗHc.熬Qh]ZVVsň|)`.t䡸]wP^vU X O]aǍ`HPOT,vF5VcӰY2#t: T %MYy 1x/Q\NS19\WNFrIQ0L, e5YdӔ=3@ʹ PVd6L4#kW=GG®AS4;ب,Wkp zj< PMj<! `[ vd9,QU&ûi Q! [O@<=.<a"l;7&$l&U"\ 3)gnp"ōVO%~*k Vq(+1?r1f:ܮ}M֎O QQ>[NLFUW ! PUΆj4;_5]oW7qT4``jU9)! ω ߗUx$->[("QCEv.{_>:U-8|Uu&oWNɴұ*,H&C!m #g'.?+/+8'/6@IcYY#ve;[̧"-@N[ܺ͝yxvܲnB+LrQqRv+8) {⬌L9ԛo-L܂fqB.G0 kmCaZ 31j+&""Q]r+X59 xl#E[΀YL"Q+م`Zt 2|nQ- 8>֤z`vMuTS¢Ɯ>ڲЕUn+W[Ԥze4{tѓ>6YeP]ٕRRZoMXO WŒ ۰vymC9ߧ-ع FưW[W>fVݍJkl`6M?&ȝ7JTVc%EXBdW;;C|7śXXR!ʽ,+rdӆB-Rqmy-wH," Z]">2[a\· 'SM`W+Q5X5g/1N^cj  j O[婳9y'@c+Z{@&nMzooڨ!ŷd-X,յ|Dkbophq-qb\umO>`h&HZ?<}tAei Zp[mBEHM } sV\ eFOykKߴ;,E GB:ҮHj[_7mȳ E[^cɰ)_(E`:;?zͽW"۞UuQMjVMq|l6Z25ec{lUm1^j<__w\bT.8uJQ.-E?8֥|.oſ hlg9=m-vV3bmp|WDm* -XeV$N% "bN*1qxq76?-9`-C6Vǫdc+>]}eK *8"̬kZ+)\V1A/:CW]+ԨgudfVc]βmeӃ#e12ϐֿ_ 'Eվ bx=Q2gb]Ztb,J * Yv=lnC ԒQKlt?.rl"Ym\1_dK6:]Y]|}tP YSVNOKWOGW]X~`q^BrE=ȉw%eڰ)VF XأzH TDҮ\`xcekjXnwY0k]SIV5c/طWn=;W#LaֹVA;| r j>=Gj8fY[P@ɋ}揧iDRX֩_$"1Q 3-x1;_$13ț޵3"SCY^&5a/m<~J9‰YBNٔ.(+v嬊nˮ~uYk^Cp%u*8ڮ9f|7lϝuUן qӐu[Pu mC WREeT#u-ȕ0+F--[Ĥg>ao1E+΄nmA=D l)o:9[EjחtXWv;";Y2uҜmv)vl00ZP :s0@xhoB|:&ljI鹹>ΌׂsOmlݻkN374' "m5RB=9iP|@VtRD$UTw5EMۍR&)`hZ5LDA 0M\yj":ݑUf̖ʫ!jN>7y'P*[j8Rc2f^J\*EPcD#(MB;;s%TG:oZ*K)7,&fyle0_GPZGZlo6k[[_: 7b #Za"&Eg*(91͖"vq5 Q+`]DˬjڗT2:幎Ѭf1QWcEYU 5[GcȅLV0U:dQ8m2(W?hr+=/\%Xڲ5ŵ5 U򲈩[.BW^˛kȭ HG$i=$tZG۪ī`U't:Vn !n VZ |y~ JР'տz'w,/=2@o^gbXNDtXW!j* l/[b)W[{pݸO Sy ;v;R#_c:oTV8" H^k n"rّ,:rPcԽ9}H>Z̩;UٌyUu*tw8"+wZoŹ*E&B^dUؕ7@NK6YX+[c'ml_ւ>+a;UT<["򆛵+K2ȥvڬl5Q5f]E5[nVeؔRo5XN*ɹUU2oMK{`yw1*B^wƫ9ܗRv@ǴxkA-ݪm6V)eo+¬hju)q,.B dfh=i6|qjne9 ;*+"Ye*6)9MMN]6g!S CV+o.dwe~~}|~>1q+[gЩ#@BV(aa5̖3l#63s6 6@i":qJچ\S]Mɇ0VXԯ5Ǚgx6䧧tsU`WW[ԋZ=jqs+,D6vI >f1ަȕ[hv-J'mZ1}%VYXΓU9*{wumP #KQJKE?\Z-eg |VM"2QtWdgRl(_qnJ9-ECMYŴzLbajF )@!M[ Lq"S}̬^3lY+1~80*JAߏA'sFv$&  q,k'"c0h>NQWhU'afnm g&̲%.&[f%' W@MXҫacin}Ybq^DiYVAkkCp,+PZR%P̜SɨcU'2@>G;g1߄kS}m.03e].-|VVD6=kA~[@ձDeQGC+l2%*sGJX׎_N榾[6&H=ƛ1q \Z"nrL9|IYݥ ;ph UM^j&Ԃڜ?{/%N/v4{'#1zWPʘ_ic`.lؗ+7t/Ĕ592l <+sREkW[-uکCWc㢭7ж2ZT+Y)ȱ&>g krXZ!Fk1z9ߊ,Xr]Z1`Wr,%w8 =z5wl[1Xˌ@@'m* E4#ڱ~'@°P-jQzc{2,0 Ymk;:0bwLde,YT]؜݈O>ס0~ѯz4ZlrDU}<ɍf<9n>"ZҚ͍nBRܩF#K,.Ϫ|EX>1? "l,\j8JnM>;/6P΁[SU!Jܖ,,03TRo~=~xp\0'w} {Ռۜ&g(3 v4. D=C"s|?6GMƁŷKrVCfUPV@ ʫ9ިȳf+7{[ aȅśR%IJ6 ,|~ߏ~!C#.[|q +T5E="ejſmq ) 9^ʭpƗrZ\nRZn%Ez~VQ!y,ܶ܄ s\Fھs)eyx@Ѫ{P#ֺGeYW0uu-@P6#o&@"yك0{#'=K]tmMz,ֵ73F*e Xwf^ؙMw\qXX0lAŠX~@ }|rӴ!hkmG"puN9Nj [j*plMKrX-kU}Gmb#Tm0 Y4q^d&gk,XcѓWV+͙KsȟUmz4ع;v~@h0lxc~>|N XئC'|Ip6"[*e.%CΩAA]'aT{,?AjSl61E6ra9hڕ;ާlYrΥ[_Y+NÏsm\:5Ҭܨ$]DҰa"ְg˃NEn .Hc']6[~f! g]K_"t [-? E@köycŮnZԈ"t*|Zͭr<^NJ{U|gj:jŞ'sNiU -,`m6,$ XJ*Rl:)L<9(B 7"ȠnVܸG_a4 Cy&CG.lj28KkG J|ְTXqx\ ̤ @gt VҠo!#5>᡻RBZ eX T Zdd# ፃZc1%[sFJXWʺkf0#SNL(eG C۟\10/?lFYO.f=#75r%nQʭyZ>.,&T 4 qߧ#9"r=[2Qc5EmY&w$Qah x% W*6$z+PFg5 ؎Ɩ|75|-4x__f)nZ6%fŵ?*[Q)ǹ<*NUwʬfR;r8rjߟ;׿CG/ !10@AQP`a"2qpBR?{5zu]נ5XΰBزz(!nxxYBڲW͗/ Ҳ~^W}~״3^zj/G^⿗Wkׯնk45v_=(((QEQE(a_ߥ^zV+W%b#eYe^,,QEW/U5xc_oYeY{k5쬿iE+ߨh1 /)(eEVN[XvqJ+(+d׶XH/qBKҗk.e.%~A+# \XOFDJ+Z,GXf$i!T&7(4QEQXWEQEW+*%~+4I E<,w袊(Q_[䍏ZHƆX؄/՗袿DGGgy 5^ _xlf[k+by1&4㾏p (iH"UڲŊoK3@[FI5BV+!m"&ŝaذ['+ ,5#PXebl(aDwk61[hd.oQeᗱ2x{"1cBDJ;|#Ue1H$rT<-xe eWsbfQe㣳x"<ͯ#ŋ}":[E+,bcڎ؟gIEѨ>ON47Ĥ5r+ME}ke%gce涢GPY!,L"Qgf'ő%!k?'X[,#ڶ#+"r΋lm%_f/d"#KʳX,yb&>F"7a fVˡ1//Ж[V$пQXk\ R8QR'([~p;~GGCgXHDe%țxuT.03Mt'x)S-SXhjRc9.U|-<,>6)ؚC4$pU_b4Wl7]b/-}Y>GQǏGx+(15QhrP #EbM{PbYj"#%|lH'p%}1B+4bK~N&4E5 /h^ZG"LX,D,K+xB/|YCVP2C?T!5Haȳ;UK ,Hw󅱏(]b]b=Bc/2b#Ƅv2.?F&,2k/:|2YR%IR"Ig 8b?l I/i bee؇Dżf2Kɼu| qMYm Ci E_hHF,ЛG,g94eCo".G8BBpBr2ʙi4JQEW[4QBՊHt] .ǗץX6ZJ%j"vJ5K%-DυEJ]/,Y~Vv8FBR8E/QVh,aLc… "]",&!% lb57UlPō+$-?+LMIHrla/ԞO;dU!5888ŗ(Qi(V(4QEb(g%PQXhVF3[55XfQFhE2/\IoD,(g4,6^(Pp$6Yec▖-&?9hYQ:!ƨōh\i4 'ZeSYơ,YxX;Ж/ea) " -pTN B+S$\Hxe)/j8uHxEXc~V+V+E#T2By\Qbh4ȩݑgŚ,c(5 p5fƆ|teXN1.OiCv4R#4$8b8Wa:+#zK_rYeTQ^&.vElʼnʺV5%eXbYXש^YLn(В&KQE,QfXBVt7 6(4id r(b%ОW"_n"# ?e_(HXj,Zal/J(>0+Q\J,q"YlXx^ʎxL{{/e/ӣIYeZ^^+nb,>N.J;bwP%IУ|"v(r4Ux5GڏC>Eq%\3hKԲeYeYe^~QEV,B1XU^^,#XbdMyb,!M炨EY|q]}㞊Ge!|c8"$5ȭ:d$?mEQE{eYfվQEVlFPqxNxH+6,bĝC!5_, CgUrjw()1| L.JpEЩ2EaTMpKmnQ^ʽlH];P"ElHefBe٤B/BC:;" '%(eP5ظ":3E>2C'Eo[,YIYeYe좊(HVǚ.)BxqdMjF;8đd!Hb<_[z㙦βНv76N5z+uQE,՚ej,"lǶ(Y|c$'#y(LF141vtqE4"aK _L+՝1"p6]4'c/ X/Y~tY_B,,Xňlb8OKğ%p..I dxbUE&#C/ 䬲ti+g8N+VtX,l!2ggH|P1!$XF!"h%I}lv^S͋b;"8c$G/սWlQpYj EpP%8$4Վ]c](|m&[!1g"bиy죲Q({lY{/6Ye&j5-eb^fH$XLX>vXtG%кb%Db*%xRBc9VˡC+~l~x^k/}lH[e6iILI#^PBl.E+ r}W8ruBdxv1#IbTt7v5gK/N9/Ab)E#2ƽDn*9P%<}$XMblL9e &I#t$r(e#YB(qKҬБEzV_EQX[/m{kŗV[-#UQ )BET:4HoXĺ"<",| " "!/x".gॆ,$o^ν_b˒/ggFiYiQХDo !H,jXE HćH_RF\EC_G%|cI1Qk-2KYV5PQEmW2) 3QEձl\,Yu_#-,XrK6Ybv][/X4C^u-_EVlQXXXoc*:ãPdz8Kb/bd| #,N|ⰰc5=~M"G(KWIEQ^{&e[9+ӳQ{4Ui4I&?53\t[%?A,G6! / eሾ1VE2)G"v2Qu,j/в:+7 e +آHqGc Փ]T7bi,b| befC+evh"Pj"y[/mQ{+ЯZGsb+%FV% ^CE^5cL f ;{bF"5%32DY~+eoH/VIfC|l\Bʡ2ʲ[ŗ΄Bc(X6QdG DJƜ3~+ecߧexY{hEzQQx E_f^LlGeQpɼ#{"#b=ѦE8Bgce?S +_B.6V8(oI ޮjXC1dUذ V{,(c#G_"\!(b썬Ki/Yeڈ 1XeJG"dV/ApG9#ؐ!HYxe |CDXݓ1tP3e]M s^XVXK5Y‚*b4hE&B$ |1 :>pE5erFpQEX'_'"u• CN= bd9x~ {$٢D(53ҍ| P$ Bb|Dz+XyDe_qfY>G_X9 "tj4Vk׼"%Cp%4Z%"'$bxy#XU)^;F!rJ(V#!ō+ӊ-Ezk؆^85}ЋE"ӄ%@xcxXOtX&AG4:<\D,vQ|E6ik/K^fʲ(cE lI $pi|ռv# RGEpCS1F>,m ?x奐54aQ. 4?c!]E!TR i2^atr6{#.uGGdDbh<;%,Yؑf]pAEBཟ9YXE1YO< E!! 5V EiaL,\$h4Ѡ~7\Ld"[,QK DcT]6w8{Mb!Ŀ4Q^nfxl(HD~V;ˑfȩ5dx."b/hė#XH| -PXb!O+բEzBviDՉQc,O|ӃbWɧ% G?סLt5E̸%[4'XxI;/mj/&sƑ7-a|x(~D`-(аj?Vi(v62/Wxg"PschM1ѤVy2,L";/ TieQ1,y~bWT^#8ʼUy\d5RƲdWxHӔ8cBkBX,58GdrЎNW8r+P$ИƸ_XgSE !ؙĄ·ȣe6j-QEi4Oe %2e8ą,&^)Z,e j= /fhCO&6IG#@$g qC49e;8)QYYfYxCFAV,8^.ȣQtjB4JǶuC$&Xr>D!+ %YCB.im EʼQcXUzTV/&54民[,vPŅG4<ŏerd=VĨ$Z;al ;.$'}Kb覐 >HIM'r%:Ea!Ƒ6XLJmEHz+٬!Vo5+f(eڱxBK:.D>GC2GK}GBGFVj#D!R4ĎJQ5c!xn+.S.&mE_{Жƶ(Ebǚeelqp5c ^+ff2W"VDT;\ʸd8eع i(jT)Ң2F&yFdPb 5#QLGEc_GB%d'*%D?Ȋ?ygi4i(YCoFXeS;BYIea )YLmD~4&^X^u;(<}ppsDapW9/E7$VXeiŗ^lX{-a (#,ckbh\+SYĨ]Bol%WF)E,Ѩn)IYiuq.^ nLfj/GB](cBD%56u>qCÕ ||C ri"mP࿢r,(,q{u#QR_LeH{.e=jǵ13Ģ,X3Wx{;<>! 3Hvi4UĎ 61.;D[;Ɩitj٤qި~Sl^6(QlR5,ob$^z-DX8Y}Ȟh64Hp4j,,R9YJeP~ۥF"HDykeL2G?U|NLL ,1C[4Qx )Ѩr%HS943J^e>GącX*DbDŊ|yQxtv7D Qec%%羍M>6HԤiۨ:X(P |.;(%FI2R5p<&&)fՕ;4r%GjHp·%䔰G%q+ebe#YemCw3?EncXc~ -2>$Kp 璊/4Q/;E(6G^%%;c7\S4_/u^,ӛ,Y,Hkml\#t7eWxVEdO((u蓢_CC%.wG55E߷UYx䲊K4@QlQXkEm{,|m=rj-4X*IJdx ?G/>y( ~J4e{xJMcR)2sEa"E DLEQVYؑȘ٭.~$ڋbJD?NE8tj/ce4^,E{[4I^l% ea+Ь%xԼj_.?9"~^FK[9tG>,K-b_G[z//Bi+۬YL4粇yVb79_˕?\!Q/*?uXrO>KiC#;CXl9EE⽽nM{Cr^,tF[DZ!>MV5b $#HBؑD?!dIMcr_,_9HoXOֿoOm⽿^p#dž_eUa9(CtQ*GZQE2*J]Ldv9W(MDԅG1l&+COvQLڽ1E(lҽ+)e䰊*ecNr6iD|OᑊC&ȾMXerc*.y˲>6O%Qd]H2P E ,wG(lsDWYH/E#hYCe2E"m~,Qec_9]B'甆Ql/x"+˲^N4)^$x\cT^l WjD #Y!'HQ(cX~V:D~%,e.V*%E\&Hc@pCc EQ ,D/0##xKc]] -&T9r.sKP'rO\&&jF(cx~DlȬ"˳N:LU d`(DbE!E#";ibK L^)uPQ裼4uM4j5ʡV8$KSB,RVQV|+?JiX;9eI5F-h,p*E8;:fKDQ&[#gG{+c1*J#Ic՚J;5,J6eIX_~9K^{mF] GƑhC{oE؆#'cx>H:;{ E٩DZS$)*Qgyce[-VSZ/z|eO)?M{~^ǣUʇcxQoxȼt!DC'gIOEVR .E>ѨR:/68}ʱĤC;9BثZo5DHEG%~H~=P/e{+Cfj"(H| 6/$d9Qh~=GVdX4>AGr)yp.ƚНiV5M1:;)GEL<>G(lQCcOJWeKҼ7G!b#Ebbt'F%fĨi>rd^5ʳIE,u5&Hi5ɪj;:,oWXF<>khk^ƋMBx׵4X?2DYq_kҲGm? TҐ՚%rQDlfB,BxMCB䟎$[T^g"D|&UXZhQeV( YФX5v)Il ZG[ԍcΆ/٥ m]4aHQX+ELğvHXhhT8%X>Ek bv2C\$9%Q.Bg%w[엖(l?J+կQ$'H^"*)MV!#x|Er5b?(Ɗ$:|R[Jj=O}+eJW"1eמ)⬢dy<#vFu٪/쌑x. "l\5E]b*ƳX5IG?#x/Ӳ_VorHϡɼ(r״hef5dcT&]e.ĨlLCEY;g9۩{(Ƥ1XRxxbxhP&Y/QYN/RxB{y4!1 A0Q"@Pa2`qBRp#br?;/?E^^Vkuܰ =kMvW_^7w_ؿEqiK_ke`//[5_ޯѿ6?ڿB;z=7EJ3eYE/,,,YfQ,jȿ,N,/(+j,Ii(i(((,,Q_`}} {Z bdbШJ4i46Y~4UzERsh#eYe+[jHEݖ),P5EYu+_wTQۼ%GBPm*쳓ȑ] bhܻ^HJj5}z0eV+Sb3z* bŊF^/k L8D܏T_nx".QYj5\g(o*4yCGNm-D0KJrPej,a:FQeYeYeVj5FQ%2cmcYJT|]Sy/M}XXYQfQY~j5mE^HǑo7&Ql|mYؤj5ByvQyǾ>)XcV,i=^R}s)bHV+ͮOFWr5-٤Ka[}v/l'Bn6G"x좍&IEQ]F"HE2]=By/l6!,2}2 #զUeCYbD$?圡D"Qk,]8 #4D$UX6^7cY̞ 좰m2GPB#_ݦTΔ5K(E BQDYXUDebl"H)P:G_:鋺FnnK$&EM%)k9ĩ#;e?,YB7vD$";% 1;c'Dx[ex(MfhMDWS>EWY((]QEf+QEQEQEb((g:XnM8'Љ~h!E ;"'%- 5bXF6X< FIecO$ttdEQEQEQEb(+QEQEQEz4QEbQEQXQEQEb(((((QEQEQY(hYv"5!HaJ Hvp1T.rƒh aT-(㶆>#[E$-(+| b4͊F]:1śj8(Oз{n%{1WjEEӡ Y|d#RG#č,Q}钋>}H_ݤO İCYT6)WoȢ-=/|8G$bG#Ŏ. VhX|;~2=hض=ΦJxbk/п%șMqv7JRļqMrj7璟r^ģq:kEl,V4Dw]pvşe5eS4R-yx&3 ;w.pLbq5Ĺ?>ƧG56%$:MR3c.(_b'C&l!iN;w=5"bgCkɩ. Zb{Hn~o;?ĆY]:G;⇊/4y8CU*1!2 ^/,GS>O'N^ (\|N%ؿeEmU!Lk#ؙyI˱*] bEQCtiFe_1$#2%hho/cPE'QԿӛ:rӰثa&%{'Dcܡ1;{c8e7'; d,!viijGdrKEYe Q BQ~Qj>ưlF~_n&?"^J#DF#Xo#$x8*(cip}RfaE=HO:{N1ԧܝrn~!{Q(7aan,4,u% )[9cئIТ3o!tr5()QEvPHc~ rkg(BtU{E9p5[j9+1i?lqvE.Of,X|GG}I$Ă^w."/V(Elɪ{ %tM.b5hksib({9BV͍xDW *+j5V$!#Ÿ,^+rؒ-EfH%C]oŹq+F>v#!=VFWڶ^j b͒EH\/~M8-&:GQK~ÍR2>bIGqݐJdD[L| mC,^%YTd9ĸc߁e:]sDK/"[tUJ;n_A{пO I/AJKamɤbn8{ei+{+o x$ObgV^q%p>,s T(7eTvd-8m+[ 3z+&.l:ّTl- ]u"Kas!,K#UnE~877ɡY&Z]Y!u[˾&6i4r'I=OZƂFLbn8pO8{+/RBř-řpp!cȳh=Lfqɲ1B%6S†jߧ^؏JƯ su U ,r;CZv4n1-q/b(:r+Xe̘ōS,Jxe3vDZղlU-X}QuI2M "Y杊W^D&Z-{x6jI~0#Y1'c4GĶ%*zF(O>u˲DNlQ+QY&$;6jܲ(HUrP<|'Oed,ybCg/bn;O69CfWʳKM!{ ƈĆKOݖ&Am-L苷o^xչ Kl1S4B#TJI"? Bd =-bƄcJ:ocIBn4I%2RGt6Ya| fQe6Yef^,qU"eX&|ƍGO[YFA&A$V~F&],U昡̲5T97F^YeYlv)&&jx]̢(ƒQ(YQذ,3q٨<b*a3]1ɶy} VƦ-Ж^ćlO| + 1UYeYfj5(U abFEi4iF#PB|,Q&Kq"eYcxŒf9.; #=dqW, Vhi$fD7690}b=a;[P(i4 BR4Є2I K#J1[u mh!2)iQ $POR*Ub .Շ7K;mj :FIqhShUE(΢SXYv.sQk#$C~xa>FOG~;<-.؝V2ǖCFFH/F ,NE#| *#٠pc"9CR\H-ǞjCf&#r'f#Xnectk+ a3S|ċ.Ef]DV,ҙ%\i-&h"nWqLqJulN.qJ.(x9$K]cQ(Y(Qe>3Gɱt%CPT0Τpj좊C9YO|0k5j5Ѭ,,E".f&&)ôEuWbcOBx/%ԑѭ1gX\^HΚ4!Xr0"erLmB{HIEvMZ%ft+{VK>Պ4TYfQfIl2g~~YxٰiH2Ź$LS'&QX6F+7/ 14f92&t•M{!b%(Q] N%eDX"BF,)c74@4 >Yi4M&AP++袊(Z(Q4h{rI<;%ǖ-COH[F^\LR5a&+%.)$%QsQ'9 bݐ,|y+ "p,i(bFK4J+#t>;=LEa^lHHB苤v8J'iDB5aг8"l~&'gҊ܏$Xx/xB]b)+S$H~a VCHزFIV+#~4LHCۂI4N(jXTlNGN263"8If!:9#AiO LTYfP?brGXYO)rDj~Y}9(D%~yv+dbЊd"vuXi+4QXJ+LHk%9{8.r,XQF-c^0DD쨛E6|Ut9Xi&l6rl"G),)cEQEbKѲ졢EvWeѤWuf»+dO1% 4M6(КE54k֙(|D})&Ƣ6UxK}%ER]CzUP?a 7xz P2;Wm(2/F*ή*Ⱥ!8J(5|JB~HhXxLs]f&jbt !b 5?&JFd-)] {А}^(jDZdvl,,BŖ_]_eVu2d1Ȓ(ybTQ-iLpf82oƢ,.MP'F) b!Й*5 nIW(j$Ʃ®D!fBe aaS9)]eYeYe,F2}. -"-PB D)P#H}OѪQ1tϗghx8)(ch5{Dei"-(Xƫ<%1Qȅ#8,ZMLHMrD#$9#Im1*R)BLjCص}>Z=(ccb⏖i(hbmik9,܌ؑذHd] bRq!A _HIh43C43AiE"FAi4J&IvjP;Xjl HEr>`P֏DMn}HtmPlЦ{CMf( Lv DpX<,ȢPd=:((+֢l/Ye#nBo6Yee7!;ڏ&/(?#C 6j؞\"x'fT)M%g"^ƹ>u#E[[I{t:lk"$,3IhEKQej5/ѼߥeYcX(QFD4Mi++:$41Ò5h8-E-YCVN6+ 41t%M&/| /$:Q +D_Ci#IB%QCcܬ2 _|QEe"(f^QEVh$<48b+HgH{ eXL4#B>j^-nPŹ$.謸Y)LBYBb "4+QEQEQi+:M&HEQ]+ӬQEi4(Ę핆I晶2ҷ#EY{ )#s|4ĨkFk5Ԋ%J?au٤=DDimVbzWY~TVkҲF,Y}yr,^{ei!նGdY_lpqg#$B,o r,P4:,q/sBc#hlMb%BcXyb[ !b^eYe_eYx*+,EeT7xChxm2j؇W]K7e$.Ds%d`|3CMbZF^EfYQCÃ!kVYFߡf/ԲԲ/蛼D Xn$3BJ\d$&^,5eWH9 ?&&)EQ^%Wu<1mC~r(^Y}/+oWeQFkeHfSc&MGI/E&5$&''Iܱ%nV+ܫƜQC^w.6r1B\vB[NFQekŗ7Be3tEHxℇ8#%2l"irO|_XxBUq_G#bd$-V-6(NEdKl7$pIV,>HD97DyUM}X3ΧVב?bp io?!~2;xߔ.vlQB~e1SFdn&'H:֊b(+fM%zUei(,٨T1X4Cث(b+ܑVVS vuQ;!p~'l{헾^{X}.w)_]lYX͡sEb4J((Q^ WH?C/4Vh}QXЮQXbEv4(%c 4j4k;$Jp4#'?Hػe~sCܬ=O,G|Yw;%t/f2'榸5eo͖Ye,B4},Ii(kR74g,,CdpntUUFRfcgO~K&E2<+lC$JO(ѷ8"ΠПQ'~2Bؔ|%I9o8i/(HvKr/Y~%zлc,}A·+Ñf4j#*%Ҷ.Pƫ䔝/IHw=B݈xXl*Qy؃Kb2^GSܚb9e5(l^,E$Q]Yy3UIŕc4&'$dB',Qݟ3Wt(}Kq,$=B#Kq#,<Xwnn5y:ѩٍQb*!+} ŖY}Yf(5xQIEWlH,~ae,49%G?I3dF-%v%l'bbc:rb2bQ'S{ ^P(RhIW}*&ݲr(k6<#l/*Ȣ,"?٥G-&x#FEQ vtОd{ k=6mI_Tk"LGsEQ_QE+ճQyWe^Y6^jV:G9JKaGآ1$1pqoal7| G,l[El21;"78EvPhcŖ6DH,b9nEYe:^,MCfw&:B\D#eyiee<:V ۓ*nYJGX%'" {?P~rWsD$pr8cvYy8bi(QX&CV(ハ_9[ؕxGFgˢEY4]/^,/4We$1(xxQkEpy>lCÿ$UŸ[3RF4.׊K/dPѷTSl'DI2c|(&E Vxx~.dIŚ/r^qhi2qcL,&FBIEzvYaYy;CQ_uXą!XF,#f!4I1} lؕwWV*e##=5dzbE8TR(V,jƙfa"/kWe_e} ,x,Q]lJĎMZW68A6HP]<[,mOr/,6pK1Xhkb+~FEp"Rwv5{S4%ay4{ԳQj5/7+(,좱E~J_c$oa%]m+d|n+r=kT8ߒ)aa^ELӆICDw#Ip" "Y^- uCCp*1vuYe_+ )bRFI^zv^kxQbX y%;#x>k|hi=XPI_bB+r(eeWcϞe d MI>Gt؟QWc]t9W~z +,懏&T9#b&I1^-9 ˢ[g;Fq;q}/ []$Y{ƎD4{8YԱO[W_ڲC/en(]) 6F2EȏOP5O{>Q/Ab#dIJȣXHq3+q<x^VJ% I!%R EXﹱ},KbXlyk O=#I/qLqB{I л$.n CLva!{V,<=bxhO *^UhR>h%!^,/ԢuXOhr_t>.(4']Y#q#8~ʤx6BWr89 OXg,1rΘH+{# 8jģd}#(ˑx/R,j+7ƻHw"{ŴC]qCu<89bwhCxG;#+{%M]a{EH1HxI!py8ܒ?|)ZƊ|Dc ~㢿ש~{$9VƯqn(ɷEDzeH>aȡ4#K"[vWkND8+ș^FXC{bv2J ƙ;V]S\BXi 5Bĉa~?Ч"ul.kr.ƽȴR]4qLR/|  ș b+Օ[3}J[rَ!BHLha &4hԞߡ]Yxd4Y^7DGqRBN^ m =K[7ÎB^8XK'%ؑTFШGjƍT) >DL/:ԉG VI%,% 5)bQeLei.Oq:9C hJc4a"/K5fx",_Ŝc-+^7ɶ+2M6|gM!t{ oDXL$gL'b4MY~&=(tVdaHM#cR HV!<26#:"4jHY$YeV==D֗B{"ԎQDH<1;(8qBěQt}Wd$Qec:XzأKb%$jسK/ z2(h){cSkp5!쬍>Mhk|uEQ)ؔ"BT&U "㒼6YHi% o^/ԲE^,VCeo}ȸ*f I{ -DY{3d]([w&"nU DLr!f–S^$%bDv,ȱ퍑&'/סEػ,/7EYlLQJHM.5JC[ Bll/NhaP?bܺLLsVkS>jl}H="2'؞X, Kk3e,OӢ좍%걒VU XtFE*YibeD+XALPNZ5!m Yej4Zs5oѰC(ȉbj#qt^(B+hYe+4QFHEIXQy"ȼtƎ,[vh*3d&:ro6)3CozjSQoaDD"[b#"DDUjsrJ[lBB5z6^lo/,cD  V <4$]AuHDGGv_VĨqR>]{lpKbQBbM^,K5LE$y4y(elr55 CE}V_k%/c.*-ΫB#g&{3[>iX}~xReF*[iIn46-D-H|VȔֻEf5WeE+c1Q,,he]6U+Nk:6:./ g)أo਌߃F$%$ڊBٚ؄mLlif-XO) ͱ{Q5uYXhjko1ᕘdDv55QViH^Y[%*l"Y1%zhWdJ2T?#RNDJ#4SHN%whRif.5b7bLF]Y^M/PXsH?vFs4]1;'c. 4зh6C,ccNCCXkLm9SضKTϥ};#cO zDirLY茄VN4LAUk 6(jO-&h8/ xOhg}ٸGH}ILQ PׁEӡGBD#DƜpDpE\0ҩM#VVǸѹP~K4ؐ4&QlQbe%$l)♥R+*ϖ.z)WkM,_e}A8"V!$E t!=Cޭȯ-.)8DeF٥7$5Fn*\|o \b}UC9^IM4,R|xO;%cBfņh4m#N['/>R7B"1-1B>q:q#|C8_6&P{Iٱئ~ʻ/쨢E fsx,ؘ[o ėĶ[{l[ܼjE,:G" RCD%xUvX-ZJH]Eq~w4QLEcQx+Dj>`}  v%bHッBkZ+QSiˁA}iQE" /^EN1I|D#E CDVWnSM1n[F̧+KRPscoe=*F1b+vQB.W$+Ide6PHmObܹ,]>h^1:3'gM~N}RfoC6g/v_m^l7xV1ů14";,{QLF"e-4t]KOˑ#e4)BX/Ԩ I2~a_oԏj5Xٱo䳞ՋŔ=>v%/K]n|ur})l&hM,qF򽏓叡D:JR)<\E=L_4&Vz-u=͟vRft9X"O|<,ܫkR]X_g1,X/ۛ#7{;h^ri'="61C;>[ĤpK⠶DCj {l%^1v)Ybu=Sz:E9!N,Ye!{| oVb9e bhV/x.(&&R\#ɣ)QRpBCe$z|bnFw7(iGUpWW&Ѻܽȿf%%'b+ԯxV5xM%MPr6)11\!,];Wg8N>< pjjK8]<#Up)c߁tg>G;bi^WF:=g ϜDWcfSЙI67ńYy)#'Wffk>`_kx}"BCD{=Oz#Qp>D)rF-.(WN)Ĵ95xoVDt..=DV1^/fwoVK25Xٍ<$>]ܢBbkj+)&eS)E.#4" Ppr.MHb߳Yo^/:DB(ՔlQ -/*|2*>|+{cO!u~HQ sUtNFFÊg4з+|&V(Wu2yt_lada2U5 4fatYeb.#Q]r+=7[ l(Ҳ[!ȿrOgK&GWAGK!e7 >Yx"t-=yEKsKBI- _RUaw]^}ExbdzBU^7FHy6DebО":EYHlԓ-Մy'oR QK͏.IFHSfz8CQ^EK$/ou>M _ĔE/R|Д?#iJw!׋졡GhXe6.mX4ѭRkj4TE!a!~8FFT'ԍr|Qd[͇$ش%k_, "N2_Qz9"-F*\E)'pq>&-n-ѤO.btj/ zwQUF_ܷ4"/sYdq bZ#mNS>S{E,b$f|{{)ON_ĹMױ܏R2Q̟$R!-8Oz8C)=ЖM #٫snQ /c7LjDHhF4.mX~v<_Y7~ݍYLMG"%^ C>p..hS MQ+{Gk5Q(, H5|~t !ьJ]~ygW}%e)Ez%CĒp#758.%\R->H#g(M#TYu/Ǧ^Y}כɼ(b<E(oqȥDGr54/qb6\q|'R_ȇ%c?$.s卧?O6>:}Gu"\5Fh'F4$eGHԙ?e'Ⱥuuoh/ HOstm#_Ԣ0lFub68{ xI-ut]MCAFCi#Dr)#rB_ ԑx,Y@Mx|J2"f^Gb,*ԫ $}K[44- ,LlKTEREo'_ć_ρ%~âE=qF+1'EQr>W'l"7D(ɏW߂ȒlHԙ74)p1."O_Ig=|CLWxrRNKs95mBU;DL툊~ .<e#E6NTG,R|; :b-F~|}VC(%<УiLґ)x$ȖTlFV|fIqlG)$:%$:/}E2^E:5k i(܌:ա-BiKd)EN*ͦi|-=iv4|YhOrOa\wE?ܯJd`/]+e$7FS*OK)aXݢ–Ž2Jd:|#܇M44=j^K3Z1*T?n:P+Dw?MHH+)x\bIѦʬk=oEFXҎE$W!tE6,G'Kbǡr~P#zu>ؽ5/$5PہOTiI-*ي/&hjrk$5QDMtTm'=VhDY5x:s"ᖍUE(˓EvɊ^k!?qԍ^bZ@HO4iPEI i"|7)jΟ^1+$))&qO7ԋUUYt]XHv&",Ϥ4.CCA#lMGi+^J4.#HPKc)×_[)S!58dV4QksU Pѧ䇹R?,It2=OqB)&Y؍H.E3&*V:l ,%)XT4)BEc+_u RB&QXFCb~*+ߩeXBK/NOPoʼn1ŋb;$*'cj^Pݫ!uh{-Y%i?q8 #>IHq48i#/k7F[1LD([>OG&1hN)VÅ&ْX%2~QH,jpwPB'EeE=&G_o:b_R(] c{ %Bcf4GW(fI7\[>rGoXiqtq+Q\Qg1I>I·B8]kF茷׆[6(4-¦&~Ɨ #&kq]*k1a+52ծ+b]+) BY}H]oaɾ{Z#Ս^Œ?؊$$\2!=Xip׹EĬg*q{"$Д:Eה)8EƩ5!5.M)ٛ7hwؽaq_䭨KNo]Hd$hE~1^ݶ^[HQEm}k*ǾOQ5{)$ݢ4b"^hF3Oؤu:~v[do&=2]:HpVYV $"q#=EonMxF{j++bXW\X_/6.%$QWc5⭉5VR+DثREϙhqcRu<2Ƃ<:dd;:Y4o E#$Ւ[Q*eCܡDd_OI'frUvGzI/ tC]F1alr4RKe&*C4;\yBtt"Z5XJfabLdhDQN؏Q5٤[6/#5b=Jt&uGa1T*#xM?/Jc$.縞8*q$3FWL$6fH_QtXhhtVQn/W#N;[ hZ\p:bhhc!XM]jڍH9"t7lJ5XM>#r5rF #}-=4~aEᢆtn ȭNTY?M'jR$NqJ^bcىj'li WHkc->q68LCT^ŗ*0r! _w6.Qok~oܣk7L،SFc݊7#!/P5qQ)QV8 EZ6gٰFDiƔ- cr{_r8wtbYmkb]eblb94%qDŽΔGM(hh{1%BfDZ?q6^ǁb%EDBvrUEܬE6pE$FO#kz>/ Y?X!1"AQ2aq#BR b03r$@PCS`cs4dp%D5T?#OĚ5ڍfKjكSQ\KjتFDH'm}G#~IχTY<49em4=잋dw!؞0 S{ݝm~I;_6:{:$~mٶ=[BFGEeF5- =Jg-#Lf,OB}ڍ8)ugpAM7<Pk'WgsĊT[VǶ>3|EZ6.Mֶ\W;]Mc{{jjݯOu'K9UVڪmO$ݯkSh汆S۪ou4'jhEhfN㢦.cB^vVh&3g_rl_w[C*.Yj-1}66 ubldUvnMWu}hvv:)MsK*j9*{Aڪ9*\KuWjQ?gqۨGhnQVDh-`빷F\Sn&mj>)#~څsB6 ӳ۲۪Rچꄗ903cjaD?z)ssAOgүê}c2h'ⶭhWu'?5SheMckzlNdMեRWfYͿٶ]05Ϋ\1 yG[E=֞jn΂oNU*56:F(lj67EWzNii/wh=d'RJ>T@TTJ}Qh[+iRXyd~jJ}0ϢCO,'/v"Z?3Z)9 }bv^I9~ϢsjcvFѳU,|ߑ_EٵڄH٨tpm:ժuP^jmߐ_HW5":FoݵWҿ7/kjXWN]\ 0KE[Jm-yie7 g/vUMCT1M 1cX)su~Ho6V4_rhܝwz f6t+^kSe/Qd{uj;]![[b]&S6j HwzMLCZ ͗kr {[7*t۫t+e]ft;*RCwvJT٩?GRͶv>]D[5Z)Ƨnmu6]7w۟t hG};ei'lCe0zf}9Uy GV.qԕTo7ﻻ]}nշUWeU-mXq6{_}?b.`m_]n;~}ʞߴm) tce nvE}^ӵC{n6q\㫊6Z-U*F2isWͱf4J.$ -1❴1kU".#1܊VE{Ol-?ܳzxMSMa2NJƟ:VUn^Vڭ~޿kU-P~=?aP5\tN@ĀLgVVvH"VK:|{ƀ-X>\Z9| { qgcUkٯj^{5Zߚ98ϒuVF?k҂*mvC92/4##{[@\ӠS#zEI%^he]R[K UcXmѻASiS;[>ORw9GKC vD*Uf͹61\MJ@:>>otc]jzUwFsHAZh N#f 42t ?V_k٪<맽;f?< lr&}Wn \#W-7hr,Orhb6Inqw(^޹lb8P;+YW5B-"^ۃB[ý ٯ^QS"׻ժ!3q m$F +r%k曩z+d s*T{|<*0H^Mo[S7^;kH@6|ׂ]^%FH pK[C_+.eW{(A'5Y+3k4RAN98x恋yX<E3:N|u3f?޹Mk} .ta6L65$n]h\圱']O\*H+XAϚ.{hOc**ZNI9P㪓0C̕Lf**l4%Ol+ D R805]RLUcmZ&;g>_habg?B*_$3@t_%di'\}`*lg1ni>\'j| ʒJJݴZQqqaq>܃\~ә8UWw@55/%YuR\Ö*'?5Hk'i;g%I?uzIu9<4i8F)@ >4u>e |W%C82/cF7ܮ}W$|4P , e}5)s#W\[Q+uݧJc5P5JF$GOXG*I?ٛ$ tw1qQmLQ{ yq<>^1l{P+A,J|VtGQ܀ת 0›Kjrs]k /ЄkReG^-;|UF|GCg(<g⩵οkp.E#HhPYMzY9\-a$=F+Q%1_2L\<5Lk ^VN 13ר$4=K+>/!g*[t<}G{n:*8&kKV[ssd;(1( Pj!Thr o]Ug8n=ӝS{c 6)^%wն:fRmVSOe` p iƻЮ g=k)Aܚ8l0c#7><9Z}MgӢI|#<0yG(2]lG a9w R#ejI M#2 ,o BPw %D08,A}5#AƤptǽnJeFN ILNxk\O;5t<_}KAoi$1dN2eS/|Zd'so}*wwiVScyjT:;0o9 ^Y9|p׷[jrYz).rҦr0rV0t{ g@Qw//?JOǒ& =i+7{< -NJ[LJrT 'F%9Ťi?z;o`-F_TfpӟyNu7DZ]ZH\8*x0@VCzs]x?$#P)틈S`@UlqH> UKoOꛐZOYQS.O2](b%cz&sNcbq lGUoy x6GD\ 'W /8r)\^֒gPQ Jm6Lǥ!ILMs޾zmE% Y⡍" T 6L$E7aYw$`!|QHr-thǴEq$OTȔi9͘wQg,' #@ > _0udUt4D:昄O;@'P\݌yBd"ܕ8QE;Qaj酞r =0Ћ:1[egIPɩ~%^ZnpT!|Wk <ۅhJmoeXXwo˙KX~]fed<aZʸGI\GtSky4_Si9ɗ|m]Cׂ;Tߖ8kp!Q.y3@ AҸ,"Rc_K5_Mh+wo+oi:eTq [꨹l}R#VtS4@ #~y"Grp1)N&$iNus7l#U`.-J&Y< 2t5ui$ctC H9V+v BH69tN/t`A$62<3i<1R51mzεC0rdݣhԧntBĎw7jC}-\ަ txahPQmA P?N"i햞a1BjS~qc5֌3 NYD:{u[6WyZf|\?5}]ݠ( HUZ6Զc?uC>벝Wh]YJ{ *=,\/H\ohл׫q7Tr)cY?Co!i2Hz7P& s!8asPAMk;Kc傷̀GzX鑯#ڑd6ɿ aU{0x`.-Nhk`1lZP׺g\,B]OA%vKXvѵcCJ;$*97.).J%yD.&{+5뉕~h~j]5S66ny*71z,խR\ގjʹ5pEtT}׏ew0MŠxf+1.k $*{3gқOHLmc`p|ٯf5~ U#B$wC=ܮuNyw/O*v SMLw/? 1]@m?w-1yaU繜Ee;sJjTY-- g{ϳr|q0|nZ YMi^>% QeٻRq-nePa5Q$wshT鑖26\#՞Fn֝ۻϢցZZt[tGkG>(gh :pȷO>ɐ%L']B!Ԫƴ.US k\i ƗBHD,:wc.GtG?(b?Po!S|Qsj3ztg\.EuXʮ>Cv[1DgfA vꛚ{H,ZTvU?񾪭69@g Hp@9o[RXݪ֓>s+>֏t7P m}"$ABOZ>8>m!${7MX:|${`V0ɿj425*-%J3->]r09U iR%_4t'AȉUJm<}7TpjRp ˏ w2ʯl0+:9vaRE0Sk=ѐzT\\P(2[U.eiAlG2Fbqc$g?>Zi(6yH@'mQU?()vQ.ңi2>D}\QsNCO:A]Q{h5sRC0/'=pgX)6pB\S{Te7pV0{ crs[E2e׹Q<X xX Bg3 摀_J3>ZU y94Ve?ITf :&ӧPZ"GcԂ=an??2l"=FI.:3PW>"4i!5gX: Ҹ1uI ngP:_0>+TĢ(EGV&s h!P常PMsxSin>ՠyߚv|4R;Y3ODNv4pBBmKMz֊{d8GEQ=p̪ӨƅlSڭaFDr-,B*Z5M]woǑ )zX)4V;wȵ5膑ݫDR> ΌcE˵s;`)/}P+wppyhf[37e]JhϡX'n+4.F.u۞&}[ q'O:t'j4|v@J.UjPoOFlML $b {yWP sPZiTb|]ݦб'<"0uc.tOܺi7ި:^F3V6b4\ AO^d9ԛ]6zG8h;'ڂjRGL>jS Jq~URݫ gO'/1T^ !qիMM~z)6iW?wȫGkkcծ*oAΡAR/lG;B F vo)Out[3a0֍H=P3$ Qu/KIx" OW]BIndP6\V[g}KR^wokOhlVZC MG WSm -:.?V<=cTZ3Tk.+8FC>UIol9m7Fѳ&S#M޳+v/sqCm{@1 م*7yF埂vϳ2jpF8ؔZjҦ?O-5:ӞU*<9ϲTN9A;O=irg J݇SRcip8h"2Z$Ap_XKhz~As5VS!0SRnGJ}:p6cw澕V8:Xn7-gI)s"BIϐ?TmH )dwZ@+"a: ta2@nn|9 h޸5U1sm >Hi+3V[{>hO U-S*h6`u+:nH5 4+\v7;pT}vt;i6jt~k}wB:B_Nl^GߒRnOM7?iZh:ҧe:o<4+OAm$QI}*[!Ft^ImY{p =]L;ζe@zAA{xiT ڍ履 [, O~?qTtRkKjTrǁVip[fl[Sfex9)l,ٛJ'4}6$#F|ൿ%3 rhĕDR~Un7 pfY:Gxg 0~0cѧE*K)QAA;vg?BaO">[䝳Xh[߽[ NlnǂnNV'jif`YSճ#т< vCNYS6@w}bYSc*G_hs1@-e1V|JQHx3s0U> Ck c T'I{ 5()A̫ ǿ\pDNSvsi?+O=Sd,um:ߠ \wut4U}*M4y>Nj^>WqVGj֓Ɯ2SXʀdoR;')to!\O'!KvQv]՚Q3Vm Lx!7;CPo>~돜 n<74' ݗg&'lk_%'kVӪ~Aڵo׶3MU6|,h:KyC B΍cM7MpOD*QGǒo{zhC:QfꙈMpwN>m|R:gU 85AJa4Up/x]lq^3N]N_-c[P9OM닿D"uMӒ4qOT|O!ݵ8U⭨~Us=- us|G"Ъ>\>هR~rsT7kdG>qwPu:$m{H[͆o_/>!8L*VfP^aG?%CfK}f*CpJc4Ӣx>l"FENa8L{SxRsIy{&|"}цH AL~IK?Pf2UFZWʢ݆}QugmIѣUfGfh+QL~h 9omX|a_%xaERA@Qa YO<Өllcn%;~5SF1\y w&^}ZHx~[ʕ釗Q_-gVqa3 AuQAh4m.]#/ &nK]N [m9iUziZ-LG׈q5gQ uYjknDvgisU(Vx)nk8?O_S{L=})SorԈJ?X_$ݞ+6=n9 :8ys_F,etUHݚϫ&> hc?y R=FN[8~ |nk]R~Z\VӨ1Y 7͓ ސ涝{vOaM5@Ҷ[K_H;I^kIx]F Ϩ&}4NE6#%SV]CyP8>:qjAQoI)K&cN/r\+o]9`w)}?DZM{ R}':MQ[yo8eS`(#[NN`!u$ LtcG6_*PZs/ |¾C 0[=Ug"5jŦt{Nc ڎhJ?uA"Fj3iS1S6rdV{zNeXke*9E5PUӘ$uS$9-9)q|9'iP(hwNg̪UhliI+:Yoz'*ӝh":3]PYg 68gWI[q9U>ûQCg vۖ?e2/ LvM {tTi:cOr/\పl -l {棶tQki!ſ.m#x2J}6w-njQ}19?ʼ-) fHl.׉PNPa sNtJhM|1JCƉձy?0`-|#oy 5Ͻg5#Ni>l85תy/>e*ba8Q&ki@kL5onΉhM] q K"`WhAhOUo0{wMIt*Z!ک|z.{DRvͲӟL;srt:-;0avyp6<=X^oSFۅe_|K _4mm0s(KGjx5}cx-\BTf0UTȂ|Q'Oy ]Gyx 4q:3V`7{N{^omd4,oDiyI3p@[V5Z`]CXHSZ?EU{FIoywg^NNzUEhn?ׯkݲs kxVuziC?0ײyqm{X+ u[gS  _Fu^"\Ԙ{R%DŽzePSt} "x)A~rW}')\+:z^UW|ꨗ;-pxqTc ϱ9T:wP/?D>,[m*eLݘ2uWo\|?[]OTov~BL2wB>~t8\BsFnIŢm\pSSͳL+E n\MN_vƌl4xx6tM5:5=Tu}wGؤ}_ {nIGlK~,vfyB6f4v2V~|eY/[c-D9kWf?=G\m%-0h>.g[w`GWMꁷLQ2瀶xwy*v>($~).+Uo?LgEJx\ݟhSkVϨԩhL|{x~8F4TqffVܥi3FfiYajWHNUV=Aj0o:?jTݒŦthOe _Vjm{EJL=@|Zˮ<9͔ P3g'שk2Sam& PkKr~ApA5>OGʵOh /5?2)6uQC*6]yve ͨo6vw7R>mO~6OϷRs^27^ke[ YX6^θeb Sn6c ] Z.m `u*gkwi?>ϊmc\R9xO xOq-k0UJ $#JJ[NFNY%s0`q5FvJ*S-%hOKlcJ7FC&5ӧ =fۃ gcig8*;rFKmwTqaFppt}6*&ت>q~kܩl\\b]k텠R֪;ϴTa/ui>dGh;]Wܚ )`ӤU6v5֛O:^F:{yN&?{#<-fmPRFѠ<&KT99B]¬jCF)_X緽l_aG=yЌiɡHnƯpTWkUt V$!&k[ӧL;O#=QI1C9C]Q@J\]p t& q @k]n2d-?xžu Tu# /(>zߪ@ݩIMzkكKizy+M]t *m0z:խZ[tfUJu7x惾u4[=GmwsUak=5_Yv͊bӴyTOeS<p?:2<6j/P\;Oب(Hǚˋq%2=)4S8ڑ'58)6zr͕ZϟV5+[lu_fh sO.?560cMv cJ8;K?S{uGh 5ri<[Z r9E0Ƙ"J(s bLFC|^!gy?٨}Rm̞ 3k|Q5RH{UhnOBjپ/NRSk1y\pSY#Paĕ[hhaNC}bl*kܟv%E7OuJ809@eOT1QuZ#NϑD&[Qm'Ĩ5 TYMFdi!'Eժ|U'iT)n4cm*ԋchlaP6rll[^/k]HLg4V;`VM*s>񏊬udQ>5#}WJ:Ҫ6trR'W_N[Mtnx[gup궪T%ӫl6gIg\8? E917|v˴u )ގ6\zpS(ǀ-i'P"XG涖Z=v:澓ꖹͥF,ڴw?M/rsB{hQ9^B)%UV;C LdL?P@>gN| m|09xc}>(;%ݍ$8{TU[*2줊}3 A[gYv5Ϩ[݂֩qmEԅ^qgܫ>uV{]~kcS9{eGU6vȇlu)08B~YTzSb}^_L7B^0IqBHM.;geclT{{/9e -fVxpj`r.Kg}{.>߰| ^KMryk}[3h{|'C-i n=u=Ц>*[H?jN[N,g=z95|q9Խ^?i Fe7iTiHIi;%?:y3iژsmOKo#ϫ8&#gjWkm[-7˧?ckG;FӴԍAq!hk[jpl=ǘ!X\u/-o?iQ7{&nݲT{K4QOjf{ӃЦN ܝʡ”ԡ2{E/O+۲ϡR@FOͪ(S0rm2џ5EW:v'<91OOɨONƵOsfm|UBs.!!R\wRsUiz!e6}zW:_Gh&ͣm-"y>ח@H tCgQytDl;Z~';qi1DӳѦ _HmmZm,&G6nuv:5J"3~KipЮfos9M`/y?%'&tDrPְEM6ZAtʷa*tXfCU}>*}pQg.pQoX&. kѮIwg2!Eթ7׆> Y31xi95h4U?ټlZײH+w86W֙Uq,G\r]36#i;mvG.⶝^X]IˀS-DqO{8i m݊uG#LbNFb~ Yk>Wq3rT$1֏('+kDս04a7MmWo_Mi"}cnA=Тg)Z$_% ޫwx)[_XkDYMxwR^}|\!؎ɨ~6j7@ +* [ր@^4 rO/5n8eVŃ(l΁:ZTzRL3Z|5_^ʥ\SiMZZ;-못iYVQ Shn{˿Z֚h54m5yA~mkV|cksݲۆԬ FmP"h^xzJ iORw.j״FQ@P8 U΢ްGqխMwW3o|uL5&@CTٶC*CA~rC1=Q+Q5vGBfNd4k;L.NRK\wu_LqgӒ[5zV~?К~0;6!`@ [-kbNvne^r"GBU/f3-='T{w ֲUZ%e8}Xv~@QE86}E֪G S?tiz*9-xV&<Qlo^ *#AF#'_H\^i  vw ><Ц: [F4eWy%}gZ $o{K쎁6`ͣY殪xA!K)VZ\@k_ W7wxϺJ6kH(3fi9:L+vƘe&A6Y^y@޸*7g..of#2q!eN : GV7s}G#8m8+*#W]]{U&Zpuȩ?ZX!Y=c,Pe:.>}Vt "U{D̞z[Jb}\iSls|%oim4K'1om a6YӫLL&;. |>Bo HT~p:Zz*yA9?fy0OQ6>M=v|WTquiOl; 0v9Dk?݆@C:-D\ IwB-^HJ4`-L @?5X^}pʸsشT8g+l^nݳx.I+:=kM-J<6hS5v} ٱǘ?\Xvo~Qt{E<(bAkNRlo굶ovGzTUwOh5yF wB ~{p))ջeB5k@nS(AY'v6Kjm`tV(0+78`vpN6'mBG5,+Zh]Y%>UiUQQ>Jlvl;>H 'P3K@Nݚ*wY54DS̛\(loiUcHq!i/##Dthx5R4~]NGZ>|8֫kgG#]|"χeو!HOQ{uB[UOUJeϔuT<݀Y$5j WJ}v<3UwSln:Uph:'wyW)BnLC<2gvrKc*_Tɾ%Eu{Xn)+wJս޳du7j(sW 1ZѣPڨ5>B1s9^UjLD\;u2>iN}* yI^Pf|lq%hkAT6 S8^l\u ~޿ZIV˂>W$+^޸7zF:5B :<<(vg j~kԸz-wGfhdEՃ`nmȷEZuǒwmgtSp |ٝ#)vuaw( ղF#׃Q"ڦ}|i}$91#ٍܷjP' M->k;뺓YS<;Vwnfk=նnPjBQV3=3xӾC3cq;zEϲӢ{*ݏfÏKcLM=,&Gf|TJ fKVqkV 4s?yT1(Щ]ǬvW;\<WSXM6 ЎjA{Ri. jyi-9'ʆ |mj޳Ϳ?YTY8'T,S @QN4R(KfN?CCҸ^I1L/Fװ-! y:i[FaZ4M[KԯIuNzxi#KRI1(= XH%#OZxjnGxe:6 wh,rkLnt^6g[,|پ<~*ylseJCq mi̵ֆ zMW9?W}ih$=K}kit|oIύNA?cx~ki:uwˊUg=:*4*x9VguFm$$)SO[`6>N)ǡލiDG܁5)qg':0cUU lj-UX9-i,kwhfI^$"]84gOX^cێO-u DjGͫhKZycZ$ pNJQ\׈Vŷyׇ. y¯Yѵc\}ci߂~};SәSSyˀZM 9ۂ~2mo)*T TǮ`[7 @6N1q}[Z:Cfu:t?L)fn 9 -eXm|N4_Ot%c\G0`\]>$o& u&?f|v5dj!x!^7L 5 >Z̭jZVUk:Eѓ"L({WOsKT6Sk^߽(#hdrIe=ŭ{g!?3skQm 5 ѬaO|kʀ|42Slr1s4> <?֤t\U$\fd 64Nwvήۋ~VԡB\}#+=*z+R.SjQ>3{BZczt#>ɶѮII3>L#U. D쵵8A+:i;ž&5mo&tVكvzM}eMxx0|R9faS$>MDR~ͽsH0jnʻ'G\Icjr-lk?=jƖO.>-55Ѱճl1?Mk@ {1Ѻ&.|jgEqЯqm(>d?|5Z~ΣUrg׷O,݁+$5w~KeT%)j87ބ-\5MַXFŠ-l<[(:ss(\t-J}gVeE q:yhy8Y?ïSqoTQvڞ #4\xi'ݗ"m62~V˄~jiyGcjtO>p>$U]}gkĕ'hcsUV|ӸÍ1.F.)s=ZkS#6 ;hla3O bT'UZU{ MNP sSeT]#8k cǬjWĔѼD?,.yY6`$1 e_PuMFI=7PsW5uN8Vyt+Ú1S55C^7X6gXx2]iVmtVAD"5zgPr}ll>8*j@v1/ݴQ2[WT,$!94 8IO\w!}%C__GnٟrZhvrXg/h#IY-gPxˉBhTp1)ͨ^umժ@;A^dqE-g6*~yc6zw\-iw#s}? ̍4^jGuM{<\Wzժj;|BTwTzvkzs;]t[Q쾠2=DlG]Ikrע,quU܃«Z;| }/V5U^|뢥Q6Ŏ+dj;QU"6[ֿ<ʘiz"iuqrJ,.k]vN씠iQp? AlԋgnTh?$Oo5 5Њ{J3Uk\Ofϒ5ÕłtNtD^V 1m wSMZMD7E)dhnPZcZ DO|# mb4io]MهDy*mi>:*ժV4o,y&m[iʏ`-_V.k̳୥B(4Ĕ iuP[s&mNheIv%c)fCB~?lУ '?%0~|lHF [̟4G N=n䨓es# uH/RPҦI&5%TBB S^w\L>uo~H3 kU{YuhE D|[#D *l #N)r*͞n#T\NJl( 5o7'd=s0WҧO[tjjQiSZ4I^ 9~Zkmp*GX9XӷUai4k.ZrVxC୵<Bv~joG?"5(8oFGfP 8HgB4"/kuW{zHx޵?V:>_Wp0)dO68j#NͮL} d.{MG]0<1 [lퟪ5C\Y;YM@h .&MF31AsV-o2ҩP駐N'T8CÏp痻LYi3X/<7t:_Fьշ-洲1QHWtqW/q_i-ͼLjQP9xkTmz*Nu cNmȴ c {<0=Ykeī8X⢘!AZ9—Te*~n]1"䦛u@9`O(\LUK+? S:+jZ5 ='+|Vj.> i%OfZE/h٢hi٧ {2J۵v n=BԄ$ѩ! gSwO9Yuf Z##s\"B;c?[7g05&\NcܜIaӢ޲^Ɯ?Qhk}4T(E-}oi 9s)MĢItT6}J\ˀ, Sr\0STL4xv6'[;bU6[> . ÝUjH71b:R[Jȓq[hhSkzPGmM?#\nǣCwѡʩYm}fk:ё'U٫\ݐH)&$31IwZe\EЭoZR}B}0~F-:.:t6 LIq*Z tNn]#q6SF >sX])Tn4l yOcke) dkҌ0 /+K3Y ,Lų0NsI7t_%aYS(&<;tON O64gUi|v͛e vҥQθ3XOg8 i(nVxEWvhԽ6Zl m*.N&O:6Lss/n7;[^GjiK:rֻS_G- ۟' x{%0tM~?`paZ Zi> s$@B>5!g $'2f@. -H7' ~->޿ZTиtZ-;'E "FOP!dk .tYyx.Cҭ50NxҢw仭H0+GyxtM;fz"\U2\`~2tԬ<̦y/ژ8Minci;c-}pK"ݯc}Vm͕{;4We[ +{d,!Voi}YÈR>+Src<,c4e[cUuŮԝI*-5j?ٶZe]kU;pujqbH"gJ2):1?26@{0?5otUX #gـ8R`kTӋlB6Tv`)\M- לBODp6fBԪUG/szB+V;e9p8u8}(<&Hw\-Vo2."09i*#% r8*Sz&-CsM H% 'l#a4D<'-Cp7 @15:BsH{> ;xm.*PeJj,qō1]a  mpTC $AoGIMFwd;N-3V_ 4o tLdLN5#F˵T`>*׊8N ࡭5\j8k\5Yzv<*m7]> 8܏*ΘA,]ЦSڪCdOxS2Fp$%R\2w 򕱼 :RA[C.UȪAah'PV{U 8p' ۟=R`uɵkt/=p椱za] ؅0:4.m7W+?,9JU:~׋EkvJiԕ޵(~..<>p,A78Е`ȉpW]ѱdi6׸aOQPD"*=WZ])#ef2y.ҮMp%:1Z>*M̮H){.kL 5b tnUIt)U$fD3AB[.UK/M ~>rlTm$A>c 1Qu]%KRB]+X-%CƒM GD\ĸ)[˰4aCL1i]sJcK_)OxhFZ3<EQ 5%}\ vf^|/몯L7^(N%T籅0l*G%wwat'PY}Yʼn'ihuFcgk2uDϊt1sR{z."xKJ`\{"F`^׷}QDԬƱ U]){Khp2e[d8]"p-Vc^Ӕ~࠻1Z>]\FP;$ On`쎝 _jYcElrC\U~ǒ:MOw[W~+XYreYk ioDԢ<*EH/@sPЧ gx#M"m&}vA@:ʛ\u\4<4%Ksȹb:uACWeI9>x*P ry]Ah/m[혃I*b4\C+.9,4 Ь!DۻTE= @V=S]o6ƞ.~iP~SMWQ؆۲CCL+{AGy j]p#T[>DNOV ٻX)*TΎV5=V4W mīg^H<axjZAT7c$ We}psq^צ[H\qk)g8TB}*t7op &S*Qhf'бQ2cO(gۻ.*)Г}9Ym,^ׇ=˅Ha~:ݚtf .;<+POKICţTK(u+7U\1LkܮpE:m3Ъ5 ? (>=k49wLH\wm%'䝴VpZ #oI^Zю=]c@ A 1D燽Eu8.{tsCukPKPo\(4h[U֫e1TTI.eXZda::tjzE㮨:\/x"ȇwҨ$ܚ5ֺ#fǷhG}X٭Gk0t_Əe*]>Z5xj>L`J6s_jT>2"|{fp^Ɵ'Q@P=ϬC(T^7AMԓ*Dx,I?VMe7ã.' ~gjh'*!b@:V9+,>)\o$;Upp񥣫eawDo]]oLtinٴUt x*RqP4)y dEsم;y Q4lv]ɰdmnTc=';LkɖRi+pGf%ahw-BC0P:CGtrNw1 co3R*bnt|R&<9.}Bb.).]N!lgeͩUٷJTyeME .'&Q,,Phy{OA@mĥֶ=A񎋋O kQ$IY`wF) *෴ w6d!Rʽ FQ5F?TI'0k'ǒß~J(m={ ;Jӂ:`J۲Ͷ[wQkn޿}'K"1.*8Xw[ w[[O"!S.͚ȑ|nv9w^JCih\ # tǝ=hBs=絨1qsjހ\\G$Z U6w~׊ xQTF\ GujIW룺*k(a9wf+ODFvx[ lCgk]UXUM7.?$U\cvvqWХL. ^̬.ς4{5ZEd5WUj~_k\/HZ,X.T;UzQ>n֌W{M˔bC lw-hUjC[͛fyw-\IR1qSݏiG3mJ)n+J^dpE@=8WG{ǣPχM  }:ܡ߼Ui5ҠR\epl3푔 FSզQкwNvsDTk}B?Nֹ%I.;!m=DP&dGtҚLB._Nݹ &*5mNWqO: kO qU3ɡ6`"Z!Np{2W ޚA4;Pv]|^[qRh wq\%\2'(@k\[_VJ4|[rܐKtx3sWFz .Ni1A_8_*ʔ7NZ ]ՖtXj.)ecZ.K_nv.k=z}E^+P|{uVcimu[hwZٶ:46{ [UH2tT]O(ioGd#_B9(:Udw*O[* uWt/>ݝƍm]fҤH Xs23\m$:<, Z0zʍiZZAc ]"'-:;OQ^V6}Ѓ*SStI;%n͠hTTX HGVӢZ/n׺f$! u& F~% ˜P6s3jnMߺ F!NUi=+UEF8 vZcPyVg#Ҥ^Qޝ_4(v6x-pvY.u6(ejM>`hcJ״}V;>}NOp认g\W 8úvgcNvggur-7"j0j~%z䥴SܥޏUmqΟIHPB e7=vʋV5_>Dh>J] ?3(ơ8Dz%sP 랧rBD+*Oq)'zMzQD;F(ӫD~=ijeQrAPGpԜkMr >ǟYwg*]Cv`U= ` ŹF |̫qM[{]췈JO~]3"ݞc_ޮ4jm2  wi?$]JJ=b$VGz>I$M[^u' -{~@? W}l`ghpi<ϬBxFy *Q;54?Fr\uE&Z)xyC5uD4v1K㋢g Bs_p䬫]dX}jUSÓ* 1*mr 6n&uv~;j2:ĨvB95mAD:]Uױ.G:Jq-Uk%8Gg{z0ڏsͺzO&÷EGQ|I)͢4#;s)[taO],)k v\U2ᜦ6 sLK8M*"B$'VB5(u,k]e$`6H Q㭼?o*"V moʾlwW ٩! !nQdHvŲ_mL?!6@{-s,vK+Gɣ_XZMOn6f4-}Sw4Q9$NuJcԟ,c:*03Ҫij5K-y0xa=V^qRdwG#y-x\ܭ/p Apq9acC FySr&z8}gVxݵ(A͢^%7qI2lch[DTpnH ^*5]\ދM|,wۢhI׳s*IY*%A˳@Y\8ZQuG2g mS/{C; Gg* .рfj@ EZU^&zKScgBԨʳ<(:ΣL4猬Q; yy#p聪&EwF2ykDz.h~.'2cxfzfU1\$g8qÄVs`#!k!$[v)| +IѦ%z79 4yUJͥj~'j쒴+N {4ZxjZBnU׷U-{yvٯgYXHVP E1 zDzpQlV*eUNNܥ*g+hm/ݏ֧Eo6_YO_y)6.T`;.Ql *=.pz:}-BagV6 >gVfDZc Fzo3. GM6'Ggԝjk&ʭ?Uh+zPkW~|ת{1QҠq`()0scY-tz #ܮPd4nYd7`yBox%D]N4= LęNk-<]{s)(#S&qrUwg4Sw@$D(׀Je Ӓ#8o+x*O @,cAA6%zC> S@LkOy]a\1(EyhoMX+`w5$xp?㭟!8=MWef  KtWT@PB*¹%IaP#V@}vS͜,ψV$A2sq:&x}^78H:j#aw4Vm']]]()iMhF1|,*0<Sc]а֏ 4\[;Ok|QiVYae`cEf٧nifOnEళۀVg+E]Q EJaOBiێ//s.9VE*loR.2Y`x.S1c{a(WtLE JlR3BS6qEenDD7 cLch 7mkS6zդ˜_ Ry$碱^6]Opz7K&UaϫTɦ} :HYKVT~aN2j=uߢjN%ܸt,^kKg䈣UcF} nw]u&Ԏށ܉/] O}#Dj {Bƪlib 5 mWt 1MuREoqi(5Z<`,r7*"97(({ iK~jjS}9Tr€eqKx<SO;WTLH<8*VoU׳#OjVjZ..VE4vIwW$Z.zPQuB\zB-Knqy.LdGN uFhwz$ehXFzax-I=IP0T5W.J2wɣfR0%ZͳJ~SWkkTgrQGR{'FP-4@ ]yu@>{֫vfF2Z#ބ2) 9qPi۶Npp ;N.@K*Uiy6!Z-smqMЍQjBvZ_Zh_ȫKdHYo)XW'y;ykӳh]Ҵ\֋CitZz.iṊ;4Z};4ӳEBy\Q<Y*a%[~=cGU} w!0yR-IMkSe10My( Ec=x^Gzmg'K?iSo{>Dz4?JMGg/c}PA](?ﵧh+UW{M>ٛPe*o~g_=ػ40gZQoƃ*Kd#*h?6/oU1N|Ԗom>v|<ԍ#,W? -/n(n=0WWtuv>|Wnҗ#_{Gj:1 hL|7ȞhS {.hS ֏X=hH$Qr2[ì4vw"\֠ HV^.)}TEY|sMT^ٺ1/X5K}.kI¸E;d{ѧV?H5榳EAjoU@FVv~=QQ9iӳ N>ސX EZ~EZrZ-"aF4G۟$1jVCDr t\ެ[LJ+ghZ+OĔwyġq `)ӧ惦|V2W첍7TwF_vv= 7F; pτ\5$h@V\~K9qԯISuAG Wx/rE!ϮmuJ6\VVwTsF鹹uzO29 anV`_k;l2B<_zh,;dl:OFE-uOH9ģGg7IFO 4Z*WwP<]ǺAiwvS,J IYYVB/Uaa Ȫ"^ݍ,\D0>r=QU{!6-Ѯ;b|{sm}aޞv&Lh 8+}Jv=֎OY"}o^?;4iۧ8¡?(^>JOݨx+lm!RVH V7<_8~HzΌ6Vr.[:usO_X껻w旱[Ih<ݴ.lbVV@4G뵍ji7~۱Oa؆[7Y3w~ߢ.w,LRz! h9(lgMeo>ef<HX͋giw&6gBV7<˰Ф. u=?'f5~Yznq$ˌzɽPNMM։$M& {kO:Oycm7$r`Rj.l-Oz* &L*0&NNYDBb.jmv8Je7]ɾ8@i⎋kQ>ZeZ OD03DU o ?]ꎨmXXQ-=/N^}.=ONvc;6$(tVy<^a=~9F ^ <4>?a1u8 yždOFw| ^<%waNC.pS^^}J ,0h}ghmG4&sjw06v[sFJTT4)[w辩R`Rʍi4#^F EkEk%Zԏ8Bws\ PYSZgCcuk5?yIϪ y#Qv<-??U36Ӥ6k|] f0]s> ꔜMK. oR6:T *ԃc?i ;FE'&)[5sg4oxU3=+'KuD[KDY1n91j xh|ӋMc٬qx"|a *t=ZǠ{< L$parKKX6i{zUF+l?S=B'zNvx3n`7~\r'ǒ uz;VKy:>GB~k =ghHLqSʐaq{ :(a东xpꦥқ jyٻkE-LSnyE}GN:sTZ;M!^)fTr 4;=0=&n:d&(c9l|P鋝py0-)#s4Ǵ]JHǿToGy=JÒ(ASnxsw&q'V uwbanzxUف7ɾ I͕ ΧAԦQiJh+{A6z>ԧySOd&SH[]'[D]ӞZX RT(/C bc.oH[]Ϝ&kVEoE@NJEY h3jOϵcŽlys5h{A7ibStϟ5k;MO)Χ:wω(e/Lw+lHmGo&4Gbm}Z9tݹ?#ޮ= Nɰfy@Gg)O.oQuA}3LAo.oi>͖t5ݻgl`Zc_peǯf7?k_lԢ~ lS:ߤqvFlv^Bl;^O=V4i4?cr++^Jڂ ?oO/$dkԴ8.DZWK9 9t^ꐐzj-v6OmKwp0=J~:٩P{W8iX_TF Nj$Qvzv'+iTSj=1WmsM )իVϊDp܃{M۪maTf:ktQS@mM Ojme5'N)f.]Dǹ28s=[sC'4omQE*qjRa6NpL*3Mw> h.hq~eSqkMi\}I6{dpUgqW,,9x\8a Cc>*9Ϫ39lw?k}?>gTte?el[.PP <@vmߟuZ?Ti⛱S.몫0Mھ&bt=<'/,(ի"sK#^]vpܒZ+yowgdFA<]Rm*>촏_lUWtCB4[ܞ)) ȣs<ʵpKc4 rg%ƨר0toQڟSmܦ8>nrf7<ٶgr3]{϶ Z g.kbKzEkϢDp'Tjj1P uJB\i[>ٳhmO}{cuinY-h֛/iȃ|rOW1H'Z^ݮ<<ƝֹԞFe;d{p!aNJ5+踱!qX^_碵fE-NgN;2;tS N;sUjZ~ƽ{5c +(c<:JBU%pi)w\d7o+>A~8 H9T -yh ф\z?W\R@ҥ4bO^EMgamUGLuEKpԃ?̅ϳSBºܯݠ#ޣWZ|щƆ<Hg6Hy04S s F@ՒQU廣M{<ЫU`<44@hJhnv:V7K:%k.'4j8ˌR:ii6 Uɱ=hy0cT| v([L5 )~6Ɲ|SXر3 qkKvՖjyFJeTI5v\/h;!?i{e!}g&[C]RLkqf׳Q9 ˒3n!bH嗸7z}h*XIam3qinT^}ni9??.oiN6q4PU?fn9*:s[VVcBbmQeOUoTk+Ei?a+ -+E׷U}v%fZvi٤gO+[Y"s{TGiC.TBl{ӟ$n3S9[CNٟ#? 25)m}9VvykEpOΙ_V3]uq4UoN-hyeZ>ayhfrB -rB:-?4k٪Z,VcUsd0G%1#0骹_C3Dﺷnz#<oO5kLyqt ysN7xH:1ހMs(z4i[N[A]YNL4quƒZ;A^O[qӪI>Scj2B=q~j.w7*Fm ip.3 A>.N>h$x@R^Qꑏ\L=}yb[_. *$'w]8@OA!5cqoz)G0Mkm2anSU]1\ ̫):>)}]]UԦyS-N5sFTYJl6x"HBԈ9Ftu&<ΐXAhSBn<;9 #V9&pTG`j-L9oq tkY= -էWR8Q09JNu-Ɗ淇%"?/M]s@@s䩺pC+ x!O # 3VGV=ɷ5}R;|By;uۢfpjci>xk%ccgPc%JHVaZHTq4y,jZc]ﵢhꂂ+Yd-{moIp,sE.ak iuMd*wJxmkLS%o"tZh!5R䷓B4+(>0ށ]75ѳ%UD+B/~&NJfp ^I̧WTkwLK]lL|d<$婖w,sr4 y(!i!\8JjFsVG5Ǫ? ]o": ׶AUMiI%iGi\ٖT״ڛ^(=$H(dvd*!5i?E<̖n[rNܿ{Gǹ5{wp![Mΰ4΅4U:뗣3`s~w0KJ jsWx.ZB׷Un^ajZ{9~E5ZEeGnO4Z-;4Z}v۞م/v:+h@,*쀮{hJ rFy :c.h7dOm{s!o2 i.?%Tr%A `BS!eQ" [@.\PKNAdZSe6ee6Z$e y*|GWs(kcQt w6i=PACy8*綬Lg)rV30or_J{Bm`\MǎFZAsVU"D09,:C{?UogjT=%Io(Vh.4 [S;o6z iDŽ(aowcA$v{%*|~5+WyjS%s]嬩}V Zg\З-gzjZhW?fVVb|%6xYz E[Tz.&(6eorZ| oW:6>!"ֿwW\XR-SH9)gکci٧f>΋N;p?a2T8`ci١\eqXjYYGwLgNZ~sQ,eO5Ԯ*¥Һ_% ‰Eeۏ5u&7´sV"Jx֓A(sV4qs9-z\=Fg 4)iw2G^爋rMuNyGV'UUF q6.=õ uB_q:iqOʛ~*cOC*Fʭ>J%uUS.&ƀkqB3-[(TǴbZU|LAFQkA ؎yZv^Z, +H.黡^% W5Va%d2 X.eك~J5jYUknoIRZfEӷE0Z- cEnVVEfEqr!~j]؏is+0*W<Š-!\|{!@ol:j!xu%vPfϲyAˠpUhV86LrV8-=2{5Zc=,-V>vI9P[3"a['>(I]{wn.w(Ac0Al(G`x o5|ek'7st[o˒7cs2a-<ù(%NcsC8x`D *T k:uAt~9)3V Zۜ:@8MO)q&%̐zjL.L`D]M6dRd140j"{VDJa2,ӢG$mv#vk-`r+*EF)Qj~7M(m^Z^_έef~z+r% >?aE0W5> ]@8 kUf%i jcۧgug3-A@u)Iܕ2ve[KuV#@6 熃ԕ kG!4(Uk94Uݎ  lDJ緡CJTBݲl=MP Vgp9+c8UB-/5`q,&0> $}1s$@d*{%9+E:UtYA8$%2rOleU9^SK 7Tr0o_48 N5RqI%q+UWOdsf{Lj8),Smp](ǂh!7F eoMJR?2ֽ->֝TnU>J3#Lyƶ|ʒx$wa,an/iH굅I~%sԭ\h,}`vsZ->Vm^-M淵AXnUVhWyH].珈P8HU֞F1N H0VUԟH000 ΠFVfOsL&?v#GO" G{MD]I`uΊJ'@qT&V)58BLSZ5:DqtJt ("= qK SSԅMU6Ԙ8H$ʕB8TT-W۪i٬IDXaeA:s (3=T_A0D>cP9 'vpp{EÚ k|}~ dqµ%5|D~q\Rrj⛇$]Nco!E5 xh6hk eL-g^;2ۢ;8l@QOz!R,BLyq"K;){AoPUqyA M_v g]=RIl/O9#V-w7{yjQsH>o][grcbrmD@Rp"q⋣Tڟw />]>%qe [J/wx _Mq9sMm!>P)N]-5 ޏTj6{SDFg%5%iPr AXMSpQ+->ҵIUsq׶aLao6x+a(Eܮ(E&AW۸|B 7TXwWaXkf82 \ms|;4Xr⡮+9HגײeGhLj~^{pfaW>Zvep{'SणHh*Z^+ZF%NMlw䷔ c XB64E3R#R==t-y%fZt$˾ ˮtO]6j>êOZ(j,)ͼ4l\/;%-M2IL&vp4u]8#ޞAiQf$EO:J:vO NJJAW )^K_%cF G`w欷hy&٨TjgUn҉p5{âl曾eß"o(T4\EgY0Bb^V V0y5~JpOfKA i G5݂ʌBw{׳Uϳ2=f~ӳW h+BlpU)U9j8"mX' I;e[cu'~g#Mev]FcG%iQS .!#딿t*Jg_+uS#Y$Xc Ԭ^ƐRݻ )6sķ32满٪ egRg=SX=Lӳ̭;40{q5 h"n[:uA~j"W"|4_r8c˼GE2kar%4^7Uң"WMt@&:+nv~ yZՠ~H-w[ b wUU*y=;yW4 28ֆ1n(֪DS2dN`ךXUd֠9(\a]OE64"@x.)SB*UsZП%?ge[Vp\ 4҉ٶRsEFKnQLG湭hGfg?gEkX?‰.j|yJNUQqTrfXާD+:XnjIkzTMjH3pRӎ4T6;i4Mr*ҫMTH#`۶ hZ$FV]V9>.4ݬzu_3FOm{]ݨÄ*l'[yOnS;x,G? ">cy\t\.!pVEiۀzw{9~;0;/nhy@4%Aւ;[j `n돫}!.JGmZP{A6a6 8T/zG"amDQ6 ~YXXy_z0(n GQnMĀ!6ʵ#XI]L"t=PZvcws'Bcv잎uamAөnK!oD5ǼU,pۉB_vsZ}BS19hGؐ"uXˌkӳ+!aݸ:مY?o_iycYW< ǏpjVy,?'i'AQ{M'Bt i4U|鬕^.syٙϚ/kN8n|.y_N5A$O%]@t"v]KWe7#%->{ *z·J0yً|SX N nNJWN8  ƀr t׽!Ө˧%Dq'ިZri0DV$ o/k yL Y5ą{4*ZOEA Z8Ou4e OĐIrX?c 8%hgNkn>vii`<)A>h 4K]^(h }W)K*և0lnJ2v<( /CENS;g-nukha5)NHׇqw.#;$bS^SSi N{"]!$.]P(\ 7OD5eЛJ;gGу0?;}# p)?;ƹ9x%XVZDOwku0L nBГV;G`Jo<9<->~XpUƝ:Qa{' >jg=SAptZC;߰L}E6*X+gc.M! Ußz⹧vQ̇;Լ1KLz-f (g- [F5bn~+5vl j%^g-;  %1V=H=rг5VJ%_|t绛Qװs\Oy'8vAz!w Z.Yrn=t5O$eBגYsh@ 3rJhav2eic\:ji=B<휪/v8j٘! UFkpNq*p{r*ڮ[#E1NtAςܼ<#Sླ߬EBa9S*֐p\"X9sW3it]{#!c:&k^vaJi:-aw.j] U^> rB{etZ P<, {%Of2dOB>Ɲeku׳U׳!b~ν¸tO%+V궟GaZrMށҦ Uا:pzW7˒.qpHԩe'0/I7W!9vp Y`?% !j%t\ YV[ Is%< W 8J-to9*8T|/24.iy(g7_HtN.ۛV 55 {gn@Ap #L*O&d|\t2R'VOnmm^PM?qFJt tLݣǗ{}dڮh5qV_x^;Qt.&ջq%-*\|Bu;cjVx^^\Ӆ ΋G_ \vqgol.vOROE2nٜb "O ԟKa6'0 Hk稜s]r0#L%wkEw.ryxvBήrsE4-S8M4g)l:.[LFuD=TP݇C|@`e.aTҵ ]pk+tY a3N3s86/9[V׍(]Rqͪݝ=趠5ꎨ\sG]!9%2U:~';fy9Wx\?4ʍHm3k``'X)ˈESvg<sӲgG%E]h[> !Gk,jiZ໥ךGCq cV( @%fN Zzv .w؉Rڮgָuo謽{*Z-lBiٟ`"B,բnV;#OlpQB>x\vE]a):4+xJqT6Ou-W\mzu> ۠my]G౯g%flLORpZHk>Kќ.6+C?c1hqjjx<) qh%\/gkݸgkۢf*>j^`,W9(NϪyoKj\w5z:p渘@[i\+e8{i귴a?OUtr(0}1 uE:Z]UAٯh%9*C<#VHOy"UrU[eǒsiM!9n`#7Î71| 醉rݰ4RL8>NvOG"͚Kr u( Y[<,.@HIW!"4Ew-tҩݕ.'⋱hi `+(*ׂ42~AP`裳\-;9L+t接[8WShSi>KH}G\x'="wp#RNqjq*ihɤu%I Pnc$[LJau,qtw`H? V6qbŠ]/8ltYQoviwP-0\~*OXh>5H\V G(KL39".r < >+h#'˗^ܔi*( ; 50; d(&{8'z*ˎVAp(ɻU"Heq1pTic xKkm^t8=uiNcK|>*NX-ZV%i~{43۪ tQ̒R;2h @hrg9w\<3,wν5(]J)[ ׳G|*S %EwH| # ͧ?%ʴN]$VC{J'k;!p++drC_|({\{0+`vx=(0c[ ʶ~k.ax<)496IpP|x+\qĠCJ$8î=)A9ڕ{||;"s٩. ʖ s *0.N݃SQsfQ9icў1*6W?̽' G`<39N;V~UwU*0b:uAIMnu2#Z5%1]JNpư6=Llp@x'I/+saTk W߬5ċ)PiŚ~iR}}`]( XZh ;]y..% .IR絽.RN_$ !N,zK!g'5O0G=;-}Oo$ͶX:76PsGW;ڦ vR֎R1jexgBԚǘ'梖ǎww 3i 4@'WhZ>Z[gNz1+%ZakvWUw!y`Z*\*6.οci-k`'T ӈ,Ѧ緜a&TE'Rtv9+Z|B`r,,-É6hi37FTiE`wϒt)/sQpۿ %%sjn^kH TJ6|zmoL~z#UĹveBٕLP&#EW QA[aN.ע2-@P1엘 Z!][j/hDҦ'z,jw=m[/ _Umg{![B)7AZ %4cvf3P᪚ͭ{[f';+$?XӨvW7p%nev WGGmsG FpUΤG8ʆ/rOqth 5zJ7n ,JNB֊e}roƢڛ5\.=[[@:%ټ>-[͏i ir)]8*X3\GvZy B|e7{Q9[<AM=nEsgV^'eYTS躭I_yhcی+Cǹ`_)G[L{)>Qw@JӒԎ<-\i+0 @N-YH>ی,ZגwU̮5'N2?4Js ȏ$FtqBֳUL(Eф|OU/u/WW{VNoL:vkԳ s=d?HGx3E6 'CubJm-@p)Y.~ezfYߢo۽Vom@-xMVeEZoviI΄MͫPXTX hc-<)\e5 sZjtN09!ɪX³Q'0fqjFVu@liK}],i=KI:(Tsl-7;2^x3\=IمIXsKK͸AO0UuH:}s}NOLk=ܵ%MF3goZE՟Wkp{S鰸]ِGg1 GPec+_#S;޴iN=/Y}kQ@7nsj1,A)u@?%sX"<׷D.hUƿ8: YQ=\Ac/И?NScXxV6q5MyTmj„Y F=HNd[ 斞Jh}W/N'sVҦ> D^kxWۋ0WMmzy>"l;v}n%]DeTԙNWsÇ28T(]= `#5Z} Nhh茾 _>*ehktP>J~|P 0PqI)_zqu StN|z FQiGMM|fte&- \gjA8ݎK%9̤;<0SqcdBOlZX\&aW{gHU_FTujӯ;pNlDA9CtP"띒\HS{1ʔ.2^Rn w\ns7VS/]v:dXRw[klJC<<}Gxi_ ShQ;CJ[o|46"a1M#GrSܛW{ k椺S<rNcp@;OΫk\Q{ROДmvrcҪ2uŸ⸃@-#Kȇ7exqnJOt9KiQEk;z&Rѕs3c媚SwE1C8{)^wO蹩@WÎ.jy.MpϹTJYP纋p>iԱ0΅x-ʹ1 HsGC"ڍ53%{BtqҳjNNUB4i;u)55V{Z AXRZE>YWzܑ6j.4KfKW\*Mk0T-OezmqRڎ8t[7qkjӲ59ST9a6q% CqGwY2(ʳnD{NjuO](ҭMx_>KNt8r W>cG.=^hq3v>j<ԏ.rYD;+> I3A EVLUabp»4oˋ0rc \PڛPպ! :GZ S]N4UIkl$LFle76iO]J"9mI]sic_ ^ˠWҥ] #Ȫw[%|G{e7^˪z.pٳ>:cH-',ͼkIauUik;c ٟLqť4kv OW0T\L괽/% -paK\Wt[€ X&&V ` E+z"j {cRI1 bB֓(YaqrW.Yy +tQǓhGPezJ(٨1%\ix1KIWSe?njvFmPejai*>%UJqeF<7[r cdc/3oyQ/{CA!M:rЎ_'X^ju vp炷ftmaaH =˼K5ejgԩj(gN< c]sWZu[y;56o>N;Uh4釷eƳP#5%!z?\Hl\ n:;^lt_٪H\.>*dXrvw`*ݺ-w늁Brc\& ֛LjRHjd1tEWkQ#-rꯪpBnXL'ո lQI.taTu. yiQi4j:(6cyp煱 >J^o:Sg  ;Lřkl)a]PՓv>k#x‹GM#ψ@nuo4dM'4c"^DlFX]Ix!Q.*si\ѼuBܴrdrDTw wNA)G$| }}X#ߢX](kԥV)t{.t!kϳ؅uJ .=(^bpmTHnM-ia\_ n3$s Ǵ\c?c+k٩VC+yug뜅"ݹ_Iluh~Wls^};>K|,om~kR6s2>,sj2kj*Tk[KHP֕ ӜͤM6yP_ DYv5+M?h\ ,uij5*Q5QIW߻Beч8\Z'OOgx܃_Z"\\~z{hП^L֨6-G2Ϝ煍egGoDS?c59ZcXikm~hέi!ͺjlf8h jlohmVTS:rE. gۙ!z*u-[R6~XVmVoGUԷ+ aY3'_qUJRְmak5mX{k^ߌ\_z"~ H* 0T{B-ڛ9fT} B¥8+u^kr4F&ؕ!9Y[*)o }AG[B#NiЀΐ[8JfAYV8&sW4"2FZOsRW? 7Uv=@E W91 'K2g^W;{ljRN"cKa^;6w4hm <^kunurWII(Hk\<jL]GE:aQa)q*ewBtm۟z7o s{Dޱh@TCZZƞ!Dc_P,vM s 'UdžRh KIP'1f9!"q"N e`!e࣊SSm547M~ek{+79wn&[xSB`O=҅,Q=,WZcHS?4X%qp>v@eYCb5O_+9s }k{1]TbEh""20Alvq|V5YkЎVT:|0G)q3CCR,XsO"$"NϸwDOIKS`}h?Ui.Pt* +l4`nVLZ¹4΄jmMQg[ly1ܲ;"$^uz[kn<ˑ; (6-VJzM>+yM0&͘(T$AUׂ<#;$,sz.99h8䥥A)`[$-rhztX1a ]TN$yG#vkutAj$S_+Usְaipk;DB;K7tƪZ]>%m`-M.Tr'хv's*&[>^OٌkNg Xp&pm][mM:odǟ11mnJ|\qFap@EKGt];uk X*ZWvw;&SVez!jEi ]ϷJ};BXQM2&Z5("R-w#ơ^t-'e{LYV9s'iP`jCii{5iTFQgEɶz4EE]SNDWȧM[lT@@Vڮw;L/sspj901Mh# kS@@D9uѪٞSNIaCp6\59yAvj`V-p|B;GWuC9ī |L.!tQ<;p. Ͷ@- ;w?g[>c{D֒灤WF.0Nŏ%OgOnc+T<&yٶtNvHJ7ő䡓 8iy1-E0C۴UT=- 0LS10ps3#+:dJiѻ~]t*ի.M ZZaQڇ s#E 0 qG>r#/gWLmRF_r߼Q1= u()\=֒\rϻ2?k 8"T9c@5N[xhSl[@sնڃI|plڣ) rԩ7+-+_|TCkoKH =v(m ޒ"G#E#\j7O/$qςM>`g䯠 _C_BY5XZ̝EKqlr4 s\d 0Gg??l*Z>J2`jVw+ \'?n-M=$knFߺ jZ[!vУdGg Q/^Qu+i"7"W+Qy.QKFo[Za>ꖁ*o>prGq.H9'$*` ǗD[r:Hٷ')<8"O,'9Vi 52ikpncy$I P\ya)/%܃P}* h&oS3.DHc31"z(limA>"}t &m0]a3"M۸ЋjvSVlT)l9E]ht7M}zͦӠ+"eCeU_ad(ԦN'_FIR83v:ߪf}?Gް4T纃;Ook+T jbKɃDnTeÞTS$tgN@Q|44ZtXj1 <9 :Crۯ]vsTʳ`iG\m5*xJ1 :wⷛ&aԁ/EQTS^'GDWٜԄ4þ~;s;akIϪ;ڥ)Lo{1B2v]BAE&VoQRp ƝŐ4~#\.ig.kÆ]p&;Y^bc\oe>Pp umL8VJ0 ? uVl}]Bk+Oh' D.Qĩ=pP^*<•39\Eg*r&y-ٍ6D~eM=ZG棠_ԻR<2!wH?{}ߒ6 q.eg*'^,#1ٯ׳ N\>Ɲ<86+Iss18w$^` Wk& =*=496"@hAш}jٜ;cۮӍs5Sr:@!4\\>]2$tVCsӴSx HOQu{)ŭvrhS]&L! :uMNq5W<ژ>`oē^s;v鹞p-t50O?ê1 =B|,>hФ p@jU>aڍ'k[5F,`SFTm;;_7n]|{aCtQ+\qo<YI=⦵:L7㏚./,iOih88>*[JTM6mno [tZ~lB2F=Ȑ97Xcm74'#$9p}ܾa`X^:;9>ԝV8]X>\Dx-/>*Q:5]@:X׳'UkJ.%Ҹ~ -{ w+s| 9k_otm 5*l%[YV)ьp'#$%4荒棠j ';_r,<ּiW.,m=E( %lxkh=Q 9!I:Mg9<1Ɖ̚E:jBmaϒ~@4ͧEquyyw.3D7F䤋$ܑcm/i{nFȏGU/qqJy@}G*ScEu |Z)ka4U$itթ e(ϯF}Do,s}5egXe0ZE˺t=5`'\<8s՝TlOOs9M1kP[H+*k<3:(){ڽvGpYS-<.cCEۤ?]s8o>;C]KLU~9x|u7Zt>Z˛!_I茒?kֻ\Y?5-qQCt!AzJocH {x'~Z$5Ǽρ2tTJxi[Qpl|a\sRϪWYwt1(ҩIG1"iT16úvAq#Ek^ބEػ>58=A.GYD :T0ԆAZ|Ēh4y{,p,]P}&.†Kc܉apD[)L;+6Ol)Od7oryk:> YZUVV/q6B:5"PݸL)+gS> s}}rd5˲'( 5y= uX쏚JΫ'UI\;3:[Ty1bu$%hlhj|bOQfGBzkd=p'r@Wlj| JJ \RN+$uXDco]cDϻVJL@ZBʑt.TT׳+ >Qc¶6%ʛ;y@f1 's^컪͸kE",$y)?P"d#p;ʱe6 {&y uGt8 @G%3GQ9`WO9͇\N&y)wvt!p;B :.K Eq 0+tܜA.|w C7ϧ]hW (Yޕ-,va3W6ʄn4;ƣ]Vl;ͧZ1 <VYš~3[O޲#ٕ49EEZAè@U'Sd5q$tw޶q\Z;u&?Usy/g4WS'k$~ady,#V;3޽v\'hq@wDx*^_DNt@POTI󓘎x/>&T4W+Gg仲gVg ]Z]& mu'K\BJkyo 2]J)˒|D.K>*^S`SdhW)pp>(|4.:hjTN (eDdN%ivxy)c]Vez69Ahxޚ-窲CAΎg !@ˏ."ܨ[ڸiXY49H4dyhH G94=,oP7B;R{AqoC7Ȕ ({p|2+||!|TN"&\$}R5QQc "ׁ4)9iVT`~9\4(Aǟfa^LNC426;TM»ODS3sE*|jݪƒ"g.ŧgtJU#޿ms8\ڤVtL}V\dHॅXQ٪DbVuX'i OzB{m^oq,z_\ǚPBⰵ沴AW Cp.p@:+OV%fQ"< SPMb#䬣E T.RDBt\ rk!C-@qtQS ާO\ػU%!UmZ4DFMY6](\L.:<֧8\ ŽWjUy7Q$RL+7[+> hvy)&V ̞wêp"U.h17bLqJGz&:7Kd0o{Xav-eai琕4 ^ .aGL8~hq7PZ q"pn:^L+lUq%)i7##T [F?zkiYM8$ʑRcAMyw jIY@D uW!-טy"sQ@ '' GT-w UiE}:oveI*հ~tm\ܼݟrIu{Ix,7B*qqGzSq,WxZD::1k|O5iSnPJT-Ϛ}FxQAnZʁ?W J#ŹךZ߼O_||G~46!AS9*EnzZcUnEF 8]mfߨ_ڶr\ uX t Ec\ǚ/g{"3j׶%E-zjkSoTI/bvgAwEÏ5aBvI#0瀚U7TCĈls]s]ZQv> h :VPIXhO5.!,.s5Y#$Li{qtI@lp߮׎i!]Wݣ%n[h9J8tb-z.$ю׏UlPk6`N5m9qD;F1F&鬑 _Zٸ5hl}j=IO68qLu2']UjyeF+eF59y;部O B!?Tr??vQֳ@?}ʖ8ui-ꦃ.=,?!_S8*uU1,!j}9!(* 4cGvOϒ^eEwIp}=_sGwORl DZUgsॶ)n:…Ю!]пU˨ʆU6zmRD nO\=Cr;<[4׬ nKaEHT=嬯CԨ=MZ>^y\@+hFӳsG |JGU FL,,@L-mƍ3$+h&by&VX@T9$%pж†FA`h,X)/p Uj R:H>KcI+xn'9+ (p`%c)S*bBV T!xk+乫@s jE:r8e, ,8`+`PdR8@-&c]GYϹLr.>H;Mi1 zypS=f|R+<(DR>j%Rh=TQlJa>汗u+$zBwMϹ@i]%qh#;aPas!X±.LWi) '桍m_k vv q'MY[5GiZCWÒUGsA)ϒ3wϪ\m+ 8PcPdbNM5E7?Q䠀ydOd:ΈϻQc˟vy..c"ʖsQk\j_P?B&:ÄZ6`fþ!d|n4w70Q{-z(?Yk˳1`H,#^JAm{=:I";nw[?06nm%袣ZE;{5pWS5Qy9Aqw&ݝVF<ն\Z %k9~VHcC|Lk4 xdRa 7 {ް.c|0N1=q# 8@_q9f"Wp㟒2] 4u )v:^ K*[yH -9΄\2JBv0GYz6t:`"Z} y+5| ^̮hY\?3"jQ ~<@A\W6HPwKcG̕˩026h|IR ~.? !>06er.BҀϵU>\YP jYR in`= (T~0*:ڮg2 -%Ylpi׈-Fby&\ =U9sTp>nQ(j{ AQtJNLQ LI9ZzH-Nh.zk#\r׷i1T4Ez`)9-Eέsk\D{7*8axy;HC%} (wz`'z˗5ŷ0+_C:hR7<̄:<ʚj0cͧD[:Jr-byȏ VCOPuEΖGP$gEׯ?DK gpÕƅjg |ekٯxv~h_%Ž Ui1N +]9[9[|cm#E溄EsݰUh[ .#99cd'_- w \)-kOTл\$52s ҵ@5ΞgRdM`92](r)rkN5c)_f5\k)zX]$~/zg%}CP);sK|4Z%bmrTliMj$4 h䰎P%w-'iT6&〧h;8p.(z*]!Jd݅p1 {< m\J:aw~+NzðnMLN"-wkTx~1 p%Cq\mdIP[g%(Lj Ȟk8xJNlVgp2?0 $)'G  F|kyo Dnoj4X{7[9HCH+9Bj s=~V>~j r-!p>)yi< +?0B-0v菭M Z^K{>!O|ych/ia6sdvnBe@[AC{K]%qhშ?!cht7"JV%dxHEև]5W5isGOS|[uM&P^KKam*TS1zCHY\JZ㎪ ws?5n_}? X9sj0Y?/t'ٻ~5ųΛ# BB4ncO;j..] ŇHך :u5W ,':u-ʐU]*u+B.k.֫ A-6:70 ,""\u\-`hR]r.'ON}ꏌڦt,kvIQUO: Za]s\<VA+nlUÌ }"W1C8Q(#ޜj=4.\7G.Vz U$?E\p>Z-)s/O%y KMԔä\nk. .7sMd\!] HAeWvD\sP{ȸį[L}4)StHpG.v]ÈO亃ok.PVK#ֵ4hs4wԪJm-w4pG^m` G vNkǘ8Ґ:j> {YtFC0"G 2Ӵ=_ ԓmQFPas^Ы*⋶Z-ܽ׶ِfc$Îh>a,G :"i]KeQp @QC:|WxqhN ͯg-anj6W߁֦{,%sT1g>(i4{.k.pk8k.{tJ-U\Ȉ>_Eg+aF?12U/J,{ZNoh>*u跀i_P(vz7Iʚ%8nEvc( &18NsOW6R$/Pǁ-!,({)Y%jiee߆~p>{žG" N~ 8 '춫IܯĩGQyvd~}C\n!^ޚ)'ܟT;- e#P-.JuBj e.g@D.%rZ"Ꮂ9&۴kU4.rhɅ3*udưpTf:B{jWum;P}}7g{QQh\$rgL8Bw5yd( B~1k>GT`[iM7=VNpzo+ TګXtQD\|kN,E&Bhx$Hx9?*0։͎|1eANO긭xoEkg&4s)k1rk(^dnP-$Oqk]n%5v{Fj7˽%uAL]՞rDpTzB<ݞn-XyCyh?w! )Si;; ;*SneckUp} R i=d/IE#|Q3pD;q ʒ_G%;S |-久[soE-}!qW O#gQ7T7~i`cUydz- ^W7p1O._=EKpVEߚZC*gD^r\oѠFp-iƍU"faZJ!LUT3wkΪڃx:r@`B#O%a3 v ])^ʖm曼ӪQݳګ­6T뺦 |Wzr2S-&俲2}C݃>%@(V@<#x3M"u3<0<Զ%;KQze RS]Z&t <5s8-vϾG c)X'NiUz<¦i-]껠B\y"/a7^qwTjSX-QP+o΅q[Vu_٪q(ͮw9Jtߞjڼ.@Xšo @ 0L(-> Xx8&bUyU5 v5.)W,<<7 B$x@\+dωEH)) }M[-{NARa ;%,wԫBi]T. a~n.h_:i=éTQn3̃0V<8\'8 ǿUeXpve|sHQϷ`dJ-V .Ӣ[VIADntr`ti gx{6`R!(*#uַeh]**Vvy7 \Q+٪dAwD)ts }nxx!AsrQhV܀cYI_Rt-BK+hh"'THNk*)`0jBN53@wwI.(FD\޹XX6liʐ.2<ˣ tkm@a-mFVh:WS p\\NE3炅c(9Ȯ<7o8:"M#!ҹ( !& fJ$3\cE!p#?%tGm:_^4E-`f l6oA@ij8t`kyx-ۈ\| ξˤuj"̇YzGөd*fv ޚV;la2]B 4\Px89W0Y% n&%Au0y'ĕ w}j4k 蟼AKcq%y aoKWUn_IAPY T7NJ\bT5Z'% ' Zj6 MeܹI Tr \=15\GkGs Dd꿴g:SOgqJhytZ'VarF[(-Fq]΃+ԮeEf$| k=e'e`LVU"O'#$M:p]M耾ɇv 0hkyzrnǸYYzgs[[#Zgv5D`o Tۉ몆z \Ct*ʖ C$r b}m HI- w!(CI:!ׅ onpHrlririgV7dz78=3uP>{+421Aͤ ݑ*lI5fϽDeEvuơˋeaM%kyLB <9(GY@FNuڹ\׹7(֖Vfr]aR-oyJ=~)3)sdzR踬s\N !ks pVB[]ȍ"Z|V00tL4Ϥ-*2-:An9߯fK(.p[ugF7~ ֤o*):-n]s xx8~V,9O媴.j$(.kzFyhT!`ǂ^hΓ0 Vqh%MT#9 !`'u桟%wef? &{$^\u ?h NCeC(nT1^.4yqrQ20T# %I#QݴZ5TӡLiO\ H1s+=E[BhV2G5}Wya{^!gĢwRUϨN( iT=59*EyЯw" sD ͟yEAіN}a8i\\M9ijGs΁$#9KкW '06wr0 Qr;Zmת-i9MdEӯ4.ڈyLwW9U<: g:+G2D> _L:wDu2GVB1weY^u.G{=ި w;#ESl\NJBK5ֿ ݶk7E:?ߏ'Sp;{LȨOD KI0m;aj+/[<{ xΏ9h Th+:R{-? c-sm% :N+34m-&X,,qޣ{U {Ԝ{E2[ *${Pc*Ɓ|0FtXȕ8YP]∭C' nar^)Z@Y 1P2 kp'z:MoB꼑Sp8h@SM5k@[Ds愾kr1]G+PGȜ,2W(|5|t@`u~m'DS:Fk*>{4#NJѧL`qKrȢcUH@m8wpW3t(O'-olj5h|bk^/gW >ʑ> fG8~ `FZ9z&+R8]".: ᷪO!l+5 1SjRljs|SL :]>H ʶI A;˃-is)+7E7ڢ|@MtB q8 {c]i5ԥSyLsYsW3ĎGXg⃘O:#GkEG7{Vmx+%]a&V4PuYv` qOWG.KXSqcO$+Lx+nZ{$\gV\S ÇெQnp6>5?nzjOEx gEU0^2Σ+= +ksa` ꤒ!>0VSFz(Ģk|$p=i(B7 o/U/%\ԘH42|QZ\qEpQEk&>-w{X[ON֎)ͦlrnY&":C}~|.cyx,ӚW`UOYP]ЯF$W[85t.<psFJ&mj;ncP*^B7Y O>J2>/`tYy5drC(inF_$.tM{o)a.'*yW<@Bu\Úy1gG'ܮ]')0Bkۻs$ofj+ŠZc,HVf+nv󏊷h ÈZ~H] Q{'HVAw(AV XTu;xh Z pR:EH9!{iՍ/l>G1AR,vM.>D@,iA :{IG8 -<q6̻+#r ՂrZeq,VH@Ӣޮ T]Nn\%i>k,2|еʙJ=܋ WN2Z:I5ka"k{4M!kqo= BBU;XAͳi &FaSE<`;\^TJy\ey'u;Ì(Cۇe [G9\]5:iӍdd#cYE}U@Cߵ dTL*{.)-?l8Q44|29E:s?0|Vybf\4F a70FBy'5 ֨{ /HQPINKpߙRRINh|o IY\eP-2Lx+:(rW`lG:em= odW>vϡsyyXIoVhBc qjtMwy8db18 я5ksIRp%pX\NbyQUu85 5k!-gD7Ɗٹw+*P:+w{aO0U$t@L5] i P8ʴ'JsR L%G+ӟ#khK~:_-waiنf Fq1m4R2b}ⱧֲO"٪Ys5>N>قA\NCY:A)k'ײ Gd :+H|{ݕVP1+DE#(^-k)Y'<Jlf%n˂j{< .+O \΋FI*p8 g渪|2Qu$d)U:TgފVGL(.l+QUA7uGuSvSs3% \׎ h^@#lx"@#(r'nEF!: xi.Dn-8%%[ͮIZ pCy$yNԸXpݼ1F (V > $%8O]r##泑ౕ38H(n\ V*S6R3u~By*K_VhUёkiݟWȯ8ɡQw({N9+x=-~u_./]A=G)Ɖ^칞ĭ{9vjN0Qs웯{ KmU J),:VP*2\${3BHCF ֻy=tIC)YQII Q渜M'~ [TCg,ZN7Q$σpa2ewtBA3P = S/ꦠ:c(M-&eéN47gɕǽweȟ5Z@[YIqx1^8jU& *N "5a<$`)TZOTúlH8K,'MN*W4+ڇ\Rt~+.p"1Ш?@80ZO8Rb'Āk\#6IW+֟pX djZUtF:.歕 8BQp?I9V5jOv 4 G0E>2enBʌ>=]k k9WI' YĘZ\< ·>W V4>$/L֎ )40x}ajCO59YTbT~ӹ [Y6* MJiZêyjeLqi3+: ?5U): hNRQ/~Qkgpz3WҪPn1A}r|8 jʡOkZfVA]EœĚ*]Pz O\3&[Bk|^bWNC>iPJiU$qON.ZBvmDpO @Z'Zz ޿Hi:`+j7Y]SGQ|ֵZẇ] dZQVmeVJ)צݸSNk x)]o0\GkynӳUS&TbUKZ;Ckp:]V Q@\6i=pꢝ"^{fWʆ ;;^'ƙ—< xFXq*ԑ$"T5yYiO1Ԭஹx TKrP5[ʍRTM]8TҊTmh&y}kj6V( A(< €Y&ߠ +_\ LvF'=]o<~pBT8-P1sM IL⬐Zt4uOTZO$\$A\A[L495#N &TZ 'J [5-=%qB1?RE'1qv12&0:TDKd2\Z[R5rSKQ{8LJ, ZعZFD؋k A0S9s\$9pGD)+cyw{uC-HϲD)mu<s}kzؑ䷇xOts\ŢW>*DЈr*3W $hTJ>=腏#^R?E{` v΃UmZ?:!/Iۇ"{ - YV?Ԣ>hsWQ7k- hq #!W"a[Eu-C<6L2@vis@uE;Qtx'X ڰGRsDiuS0aEƛ,iu @͠e梆˽Aĸ0tԢӚVE̕2 Dx5U}C9'(zӯ`I]S+o(A9Gx Z<,\!g1=:4դAvZ=! ȹd\kˢV\sjP4M6#20$aT;RȂ8NϣsE.x 3ϱƛt|4Y1­trS.u.De 8&~޹D7c!䷴:2'tktQӚT:\pzJNk\V{zʃ yAYi y(F+@Q#P!imxs{t+r],3' .g% ZZXWZ|,ęst//oGT|Eވr~1` 7oREݸg:ϰU]Q1%G{EshT'Wǹcް=q<ςae˚g?mrS_Q:|"qZY sMJ, 0H\)#Z]NJ,8N %BD8BӓD@>UmbNBޱ9T tW2xPaF [->< )"{ 1p'CqWqW6 0q7䌨sADE*9UͨȄjPo0pTVAϪ'M3(`r F*^ >a6yď[ែ? DC-':> {ęϫ(ON[o'SB}S2."]FtB:~hftDaQ*U$xuE=9\\MU1ȅmZLt';d.Z1 w6N=ȴÑ ^资;#ԣ{,J;kcFN8AsrZ_vjJd]]z"mj{cܸ)vL,𬟊0MVyY—ɒVEoyAKAJ C+[gwu2}Ȃ": q{r9.%crmwOwq309]1U1R $xHa:?1<phr :]M)#9] q,c::&p4NxC@'*uD0y8Q.n#Bg (pZV0 g*jp[O,|ȸ ʸv:[K9a]87EZ({5*lTa SPhoW\eiq\Þh7)-r>zDKLW tplZ!D -Maq"u[փy".^*^Ղ"y"~e9qTjaKUӦuY:iF$JhˡwS'T2:yJ2ֺz p1D-HZ# ӯTܮR_zk fAuRHp3N  |X h~jd@8DyX1:,HP -I$1iW7??UkK_r"ԥIR1 gE3z4m\?mROS˚4 sp'r4C#TL\RNʺ z9wFiA9(4šFfޤs@f|f PkͥYpuW+Ǐ"!F% wJ"J=3R[ #xקO+SC+7\µ=9] nW#Dr蟍uV$VU ѩL 97FSEr!FVǚ!-?@QPg=T>ӧЧüԈwT4 Y>J5PԮ=QR!ѓ8ACP8S4EQ_!1\;Spg"?Uc 4D!\q;ySu8*᪆꦳ʡ*- B,&%E*:$4GV`yW ,1N5.xnEIX[@BʘA.VF2>| 85j.] "V9tP>i쩇/$s}U aG2䍮F= q=Ja36]*~ u)>p2KA'Q !Ǡ1Ku!VOz|I[O!cNEVsc uâq:h$gmT J p+=Y {㒐QdJ vIvŮiAچ]0P .#Bwȵ Cg4ۀ+2 é`hZ&UkPlirFcȮ7p\. -8x-4?gQhYhSs2,v)Φg1Do,=SFA;mÛy+Ў^kݑ|P%hkYD-`% ]uAӒײZg]^gc|]5j- ;;oWꤝ;>XFDwEo:ĕ {#QgBi!d!eƈE\ N|a6U^9&Uh U?8䖅 9怒994U "p!4]q`H6wLYu+Vl=*|xK]"{>TZv٤n%rzu"qh ^L^C0F^J|24+I/:Ve<^,(Yv7Yat.ebYYMhAA&|b,ؤo30<F;ªUzǣ'QR5u,xi'KWI3FWJN%)!, jm/9}&KNjT.W_uXE~&zJ鞰+sEsS%vJJ{J;t9~8$dhU=htHr١OhPiɖ'Z_cYCYҦ@k?6x6 PXg1kzinDjM^>g-ς`/|# xm B{u1-~5k79it j:X6BYzY/]؊_$,q:H^! j +5p9̴'p:38+r *#-P[eNF]w7~-٢F`oKT=]tz`N{J& Lx;ˮ%OJ%v= 3zH0-ba)N9>{%^)@fw&E_@='ϭޡgֽ+&=*'iԬ%*TJP> v =KdUY! G x߼[w,8XMv'Yٽ7C{z)No_/j0*c2Pҗc/O73 5ثxJhӲ@p8Rx4[cp>Cu @+jG1)^,c+k+oH9{dޗNZ`2ϸ=SKz[nwz2:{^w(f- f25M1(vAMM7y~8+utq㤡Cs5ўs{tpCx0aUb$ٯULދz љ )f4FTO/Q>2}+0!v%OIU,* I_AB-솓Y^}=+]Ky3Q KPY:OCzgҽ+{zץq]='bgew:&a~Y uS ja(f/l?Wy]+ӟB71^Ҡܪ _V+Ϋվ3<=q(~:~23RT+hNFb[OQ-x^-z#2n?Y"R3%==*qJ*W̯J RxzaΥҽs MJ*q ԩᘋ2#OiqZKU"J-BTԂ}N==97Uz礩OWї7dW?̸~_4 }eWN+ҽ=J2 sCҠCTҧL>*ƾf?JJrDJ%ϭzcŒ =cp\ 7*}3 @,z&I׎:^ҹL= zWC+IRM7*='xMJ9Ӛܬz_J^>F&Wy3._ʅfXB]E et3+ f$V7P§iU83<T1.}TDsSĬ[W+k{J#R%s+z1OڮWFSħz:J|Cԥs(!_}*SnyOYX.nSkD 3l Tg/V.zt+3)}<{JO3ӹQSVfTށӳRL@zzo]h3>牞Z=Bs qSr&zOoC! Dp~= %ws\Wx`1-# Uu{Jr=:LKRRFR\=Cҥ>^18J&z?MGlUzHS2:J7)*ϩ}?O2uJZSuP. (\ꧯ^!D*UC0*W/pkҽhHxؕqLzl^DHQRUO9'~:OinQlgEjQP*fW觤RISW @~zO$OG *UܤG?]_ Vu Y@zr~S_O=jWi]=~1пT](%b)3rCWy2=$ ;OoQf213T#*Qsֶ|J2)8Q'&o^0a:LtKwq.;Ϩz{LyS0JeN&z^bi1*S*#g8>ތ{N?]2JjJJJ́^p3erMlaL!D?U2%zWJS~EJ1{J%b/,q3XF@8n[P}*^E~=]Y?_> B=JW2L kۏu*t]ByW^J:ץbuR 7zԣy؞{Lau3 2ʕ+?EvzJPr}L2@zcǧ1=LJ?EJzJz9=(1}BcYUTS^K=>c%z@*S_߯>!P<2pߦ?+ӷzV.W>2`J&&SPuOh .WY]ץf;C|R`t?JEcĬs̶bϟL>PG5SGF%NgjD?MJ%22+J3+֯{z3Uzb[*B;2D@z5+/"BLyDRT?VV}N2NeLL9sܤNWJ2a&zz?*S*qx ~zjW' Uܧ3[tJ=10bJ7WԻɕzܮޙaU[smf"3įՙ^S_*2S_JϦ;WUSz2TWOVQ^n+$=)=zpnWiF{O3Gϯ5*WUHx!WgS{W?~?K+Օ3*Txy_U+֥{P3xz_Co׎F:2/|1eRN}<=+>RJ_2*SRΓ)}|N=@?J53Q%~q%J=k kJqϩ^R~ AX뉋TRj[чL7ңA^J=(*T_u=?FT{~^E@SǦ:NeJWRJX o3Y1= =?S7(*~ҡr#:?Yy@y=*skҌ ]_驏V?MzJjRq PmFMzT>WJw3=jW?K~*WJ$2˪꛼ʄ?g7yJ;!+T3D R+LC_z_*SW)Q16$a*kSqľ=fOޓp=/ `W_EHuOo^ї|wfz㼯RRUz+ҥR}AhuD8}<j{z㿥yT]x'i]|}+sߦ%I9ԯLϘbs5*TzOJeKaҿ%~?F!uLI*)|̳P˘oS?=b|4T?@J%#ԩ~qOJ?G}UϦMʄ%OTBmsfY@U>+Д\gRAJgiP?:5ī+JƿV~ʕ~w+әRWz_hkQ+}kץ~=jJ1*qCq?JΥ7r̩^KsDW^+fWՙRO3P%z{~j׭J|*/w^%yznTS'2K3UzT?Y*WGֿx7?AϨzz&k=)SlJ~=.>G?/x9*cҥ@5 ^^wo~dTMJzڿ Cp9Jh15_%JQ\+rQ?ץJeJ? T̿JLzTRJ*bQ(O32BUʔJ/:J}5 ^k#wꙁ+ZR%zWϥw*WG2ѩJlT+mo#ʩj~{/*9wژ)x#ٝ <" @j=0Mم%K?ijg=yee2ޱYG0zWm-~t*ؑh~`Emc1+&9Bh)~No-_\>Z:2h14R:EגI)q+'-fX ->+PesShRGS(Ath-uc(\&Ꮘ Z"w y"-c36S& G=}`^E9x`:u))))\pSYYYIIEQ;\[r 4?>/AC/R` gG02b:&-NN:a$L`=OxN Gs2|b>ߡrF5AVb\K\R{^=NJ` T LUתzvvvGc c~R%l_Q[m[1lEUv4j?jS2\` $dgfS6h:27j{bG[yG 5߬BO;W.JθulåVdA\74o̿L˞e!fFw}"*AH72b^|y{u bw361KS@aBRwY؃*CP P,ω$2~f8QR}gӚTkEG$-,?t Օ'|SW B ̿"K,e!Jyj^ &߆3fuW%{[VNzpLX|(_QA}؋}A̹cv#.\L7~nq9.qaUs+]W>R>eq3C:\GvK@0gLux]s-3 o^vPry.Za_2+Ͽ;XdRwh|P)կWS,ݻT6#~s`lYu,*}`}Vi?ʠ كh<L],;J3:/8{/_B8d-)-_9,*ۜ._N=_FqzY̸ĬJ]r_0'b߯`u=j{Ѫߧz38@0.(Gmέf&\ K.\R޾nyo+KO Kx @v58L,x=O!hAIG(_ݖ =<%Ѹn"}S1)_qRb ^բ\r෹,LIrY_s5o/F35Mw›ĥU5a3t7SF;P D5%mr:rub]t+C|W7)U&e&RWu"_@R+,\*{1pi>VepI1/5֏{b)37Ÿc HECj'C(R W8>YR[/7As߶O4_9r`1:[ fZ-3m7KVP蘃\=vz*!uYm*֘ѯRXK}}.b,Cϡ~~?M%\{JOx 3mrlw&f9;[ԾC^Bs Nf/srr "5b057mJVռJ@}ir˰;á;?*k:sCWiET"Rk+$Sv=X|u|, 4wY8aTnW&K |Û{@Mg}pы@ O7S x'&[6، Urq`^T~O4-؆0" 7chVg X6~9 `q*O߅΍:nH,f'd.nׯ0a Xoq}5.[0aK%e AiNwbh$$пLGQW_[v:\ =||qҬU޼5GڿP@Keˬ~L]7M"-&HCNmg<楸VGFSxB.̵`b' aegk\GZ꿘W-8sg &z{ޒ6*10GGdPP=HEs*L/h}NmIe.˿vwԙA\+-eyfN]Gb6:GvL-|w\%n2Fֵj jق06{ת=F r^''ǃKӞ7$8J&P|ƃwx^8cgb}Z^&-/]u)j~ؽZ1UK?I]@y po6ax!w8OyGqT]˶W%/0˺|w.u~_ .}GX bhE}XW赺_J-E* ӞmS'2x/(Մ=|Ŭ1{ZnU`pS*V$*ŷX,D\5}b) =MEdg <]໿ X,mx;^}R?Ա3W isLFQ~[MZ"kt=\V(`=a HR! ǰxoaR< 7T{P V={h٫C,oͽ潠)׀(G!^zq~]oC r;}lUy^aT`rW\q;=*b ~`11OaJy;.h7la fS=>ysȾ(!ɷXeLdt)T d>9XxFzUyp׉Sgr`~b`C2׎垡 ܶoఈ(7^ơbWk|_mԦSy+̨L@C:zeܩQ7Zҡʜ*T0Ne8z!L¥wp|D^Vq6f5k-ۚ{XǼ!nxFC5uǼ/dkoT2?B*BEOLg< yчSA~@WhZV1DoQhu[&8䅄'hXhZ4 rҾ.عx[d0->i&hX~ju-`S,R8Ԡ.qJvޔ1(Z*rf۽C-ҜIGabS ߙ̢` l%p[j5Pn-Vw͞'njV:\PdP,W_< +1C>Tx*+m ƺQo?fd=z)]>&b1ٖL1 P㘅2#n[)uZptpӜigv qe)l-wiA.7*+~ԩGYR_ץ' 93yzD%a%*_)Bzק1WW1s_yl\Y1,B\Pq,SV;q\LUr~6Pq.[PX<IϚ{##AgOt"av̭]@̪ ωOŮ^&n=ᾱB!p FU95rRd$uپ}r^/3 /"w#[oX32ԫo5+!iAÜz>pյW+k㼔t[LU x"Ve!p|$tܸBTƅ)gmY)hd_Ќ~B7 \3m$u >Ѳc s+om`Xо /.}=\0㚰W쎐Z*snj- -)¿?n:sBs\ M~"EVbcS)jPb^X -U(LgMtkrV*Je90m Ep@SWBq [-T`U5{gnʢ^yz+#Y)y++SY^ޝfwcWG$ŸpވҰuejWX1NMoS0fff}335<-pe/.˗a-/s徹|#yO/NZ^[=2H$HC `(L>-rn ZL>뀐TR<жNbWzoOWx^byܱKR0գ娅 &UUi\yFA_v"Ќ]t3]ܣ,r%qC_c L A@|Ta]ꪧ3;50.ddWyܺV<9Al`/n K5^gˣ1 Lc RҽrJAO)\p{]zܢy( ^KRo (T(=D|VX3o0]\`W5踦d% ͼWHx}햇.UӍI|I-hX(:2S̟xY7We8FY*TBW-AZw'ztXn޳2ܖR_Ty9y~e<-+>u|_A^zupxp9 ]e*Moҳ~ĩD|m* +0LCǠJ@&zԧSM2fbTfhn6(k8Y~&|gbVd(<~!95xZ|3D U,~f:e}ߴ:9w!!P$?}`ӊۊUhZb_S/aW'MWW mdeS@;yh*.fE)2Ju= oR;'<{,7Bum_7}m5@\Q_(j+S#qfj\2r<#é#baC!8nК_4Q}օj%7z:m`bޱA {'\*4vU;T1X0boH:~o"IwoY}px~!rBpqL{Sڶ*E6,:0˄}S> 6 i2WVg%~fmu/.IT,@!iM%,mJ7&فpN̾qTZf=bF22eMǪm`+3u_ЫI< Mz|e|ʖ_YP+>*WJW(WoMJ^߂U{Ek.İ^Yqjm'bR:6}d= ۩hG$=`'dEM/\`Յ~ڥO$⡾T/-ݕkW6T,ƛF~zݴcbnٷO]q r7mUA<ژ>.ƣqugF҂H޳pl`~qBaʱ.pѸU5akSq_J;`BьvY; Z"`e` ]? dixdj\̲-R`MS,2ǹgH c^kN;t5Uo~xUv8:s<ըᶨ=aikks˼ާY{y~eKA>ՋU~bMMou b(hy]VTLw TIS*>"w&a@]u(uOE;/N~ 1d8AD))xTǣ\% +1^\¹J:JJC zSҥ@!Ss K_r\Y,V[mQ1DFL1 vœA`X[Pz<}Nf|8#l\brw;UUf=% c{$`k9+` /#ASU/GI6 {c ljj\85OpA@KsOwzeGg@-N<,3hmc0++&K@>T%KXHaPTCXP߈e$Ygsk!WUI~kQ?tJ(آYfhۃy 71$h)Obrz9 S;g Ow{ T-EYnb+[gH5 ʼs0:9,T1U2e׎+ 0 g*TV*ſo<5̸-ZiW6{FT{g2"؇Ĥ *T7(Ǿv.Y=Tϡz: +QB1P`vXA=+IX SiMjf={goJna~!RІtkXgШ=z+ѕs//:D.P8"S3BoF&T<Kz+IB̰ѻQ[E1vfv"iuϴ1naew a'qM*}[@_R?ҶbGj,5b+&T~8pQ*x V(y|8x{y*Q>#,Vg[g<$#R+R@vs )+T=RdDǦ#Q+#^z(L˲J"=akL̦PbWL=:*Tq]ȝ.mך)f7+c42Q.yae)ШYV+!ƾSޥEt#ñE/cAqeb7Ư.ifxf^}/Hc3 D|qmBo= M+/84b^lac J%7/|ٯ 9k%⌥C(%fz}s/((`yc5^N} *TQt`Zf!e\N* F_z> *oL\eʕ62<^mdRhY.!Ցhjfb׾Vn fo Fr9Z_u c]2D-7P >Q kzAPmVLV2[EԠֿx+/ElQ{l`|J3ڕI)vh蟴": m" }T^Xm^J 5CuVS|Qқ+I"e"Vb&#FԯA}*fD\ 3ƛnYu5ph)C'q3n/ϧE> O8ܹTUJD#=ATW}/\IDw)LˬR;A59HSxi—Ǭˍ4;sFm*U} )Nޅ"CC dtq9;+#lFY>nt$b}mX^t?0Հ1[kc\3}o0k T]Le0MA=}{cސK̲{D!ʛyrf?ܳ/t5o"YRl:;P] ℺UuMJx ?pl5pT}Ul"-eC5ټO{"I!PZP*JeDNKXDyv_Փ5O&Y[/> gUn=Ҿ`ƛ$yit"j'݇CFa00">/;+},TntyFxVLruSģu:p=0";SbvуQ'zA H:w' ܰ]V|LpBmiffm{Rv[?2HSVc)ڟr avF(m+ |܍&.u {N1M(y{=1AovNw {nحhU=egDOO>ʙa{ ޟtfS˕Ѐ%K{UNx{@>B%$Aǭz#$\jf&73^o<%#IV2"=:F9/xZQ> _8XeXIJaȒ8LQԽK.Έ(SRwV@fu߇FSA]_§b՟h8^E3S p7X|=6.L/z ĭV'2Bn"8~_wwʀ3Ey\-̓/v¯G%^j9Cl#6z @*ˊ&T=W.?! c֫0WG*0Pꗋ״j\110k=/ʺHϴ uWᑊ"رBРdVM̓7ѻSy"Y"?` J _ATwnW~Pjߠlf"ehl*ztN3+YtF.V _AA-;ĮlY;O>w `HwT5>@iXY7*,;`v`toc<SZTciD[^n,fwҎb-faIyKc|..[f{=- yKRƖ n#8Zo$b[C7f4[.w0~!׏z+/qM<[ ]jXVǘlU(ڿr`"H^޴u_!%VyG>xR548~{7kcؗ,q ]!h44^ S 88󂦴l^qq lj'9\KnZ,?)u$b/W^(- 2t`+ nz*V}{#~G6Ι^Vw4xvF/2 [a[}( =L->%VelEK|N'7z_ ?o `|JAx) ^Yub:Ep dC]s<"M&ۆzUv"Snba@u3=Vkl<8,JAvH>LPdt`UVG`NK P=f֝*ƹq^ ~kt+7TnfϚ +Ļì=+һI z1=;̧Ieete@.Q89w@zLz{OW y@̩w_2`z(u$.\Pf#fɪ"gy8e߲!)ª(eX?1[ſ>Qp>LK&*9T5Iq0XVI_⦙. >!Q+kL7E_-3ǗiEC*WX<#v wsQobA\X@Ҧq+f4G\8/(Uhb4kz6֪u5,!Q;%G< 0x/M9w:/uk7ݨٔ  K[#1]ey*yE6ISV+nfYl%wq9+_mO1T(ֱ77](-%٣Չ֮]N#(A3}|tw œW=Z?%.bnezKb EzM,EIx\yA Jg ĥ.>+Pw>ugdL< *xЮ} DZ No !*b9Wm+ϠΧB\5 Er}0^F>bO L<.2}g1j+4 ZQ1T{{l@_ųY+j4v9nf cEZn bh5u} y#q_8 [p yh,}IVA:*-S0BtqmiecV„{[g;wXY^ ӵծZ;5EѿUÈo U  [lE@1L_q[C d4 k L|D$WeJcYWBW:C(KV'dXP! 4(yC4Q 0>|+|M2ll?wZw (1WtO0u;UBr+-3/@!p>ɮFBzJ؛E&b*L.,=^}ecugjUjĢT؎O?P@CFh;#J`agxy)Q^5k_q1lWUj)pGN,j}WzyY0C۬6QFlJ~; ciDnF{U7|y { 9vG0-|J8 DOFx`LB.0L.̳c :aM ʲVe7So>_Gy+ ,4-z׈1s[EL&c-.Iˆ zn]g];[rsAK+{1r2Hoe@1pjo\ZGk濰 +X>X)|.<-{fzG})UxPLl*{"0TCJ:yuߩ ˫f [ς0EpG}ʿbSS,LC0K3Ce_ʿ&r݌G2]cyq~(gc;"[AG7:nEͥj,Ti8:ΰzp%ˀegӻB"znY :HӲvҽH%{DJt'WA:j1R P^)ij[hMŴN@X=*Hr=zJѹ^"_wB qRVyfc,!bǀ_ Er-My,\+WByF(T*-lO"~UOO\F3+H| _T ]W!6\rǜl-r%2yf&Ev U* QV;M+ƀu{ho؁eÎ% z/nf﷗㜄ɟtiWMi^~|~q}VG`~&;:JumnW)0s_KTEp`"#8gDc̐)U.F %KTz u}5-*^0pAP{ƻJ:vP%* B0O( D` >-׹RβҪq-8,i_,KĽ@eAt<92$%źLj4p^!%!|ެlQin%21|`V0b+Uycշ@ҕn%][wot8˜k.*0\W4'oywJ.xn[\nt#ivEЇtb&pk`\.#X_*KlE ;t ,Ylutkq G倛Ey_T:@g::|L`fK],8!虿 f8pi(T1%(竱/ՂaJ=CReEMD'5,w\v\AlB Wjaukm=jrWMU^*p緧#["%z*2p]Ιa3A] y\W7 C-b|1gZ8f@e×B+Einu*}bPWa$ C9&): ZVg]X4L}293Uq+bX*dK1tz4&})ue^3=gBW}R"Uz+ʿP(*˃-tPj~eH n*? 56`]UH9MlC؊.C9=s@vv:t7s܁= oũ,a84V^Ъ6?0,+Yf3"R/HbH0~R2GenQk1+S]hew@nxi6룇JbrtPGPbհq2xo(<,GЧ I[k %?0Ē.vblgPK(Tvag.'FJpjhg0a _?2 V?7%>TaI'[NOK&J)eAռ&ݞMmt/Dto@4|uye7Z+7HTha;eH:B5NXW#~TnyVeqc,ӼúAB J%TmQ~8P>:S,6HĪ#XKJ=:%@v%kuGYY^2)q*])k@V&) QBmIS69_A}IFރ A`D!X+z*i-SF"z .jS9]9utWLǠn=/=9K& ɇljnmLY,:4hlx'zMv_{CXҮ71-B* )̤B=Dh]A~٘[֚;J'H|zd,紭8(R R яes%НfȧU,ǹ1Niqiw@}/;A܄͛|ƳTKWJ1OW 04GE=xJ%0Dv CNC%2\GLy:@Mu+1bD9%F=)'Eq-[\ 1N1FWءȉ(As|rN9hK)jw#VE4k M} k,bد9p2qϼSHQҞ$ 5*r-[q1TB2A{] uvrb0S~4][)wx(dj)Aq+w|?jT pFD)C8֍k;u63%kK ˒o1 +N_Gf۲8sޓ0Rxh/'84e b{(̂ !;2f`׾ePY,.O0T}Gt{9ÙBQ*@)<؂ y%vrIX/ɚ<<$ٔ`*'{1Zbgylzw[{Rugid%KN$S2IPݍOA~jcaGK{o!'Wëlp+=hߘ?R'ܠc}m(+}¸i L .l /Ĵμ0z̅)|[;B{^fqY PZ x+ i:]Kcjo TĻ19#[;)r ߗ}ӽg[g:i5 "GV?̊[tRX(5#|e7Πs:Z,4ܳfZEI3 1:'U3}n5>@s7>fԷ#OJl&\h򛡷q^ |rt; /OshO렳|۪tq?&pC5y0K0ۂd6GGiat sH`?"5qn51Vop|͹ehzX.- 2j&/Iu%kp*6D܉ƒ:г|B3DNw^;鎐 ;lU-.ɋpozFQPS0'·_`@zʙX ɤO3#y*ig`Y:_ EJq+Xb m+8AX ŊHNlQ@"ީ=uX#w}i81]"-)8S0sZU+F}yup fmc0@;k @'"M*HJJZ״Nc}L<*,f+_/{Tş9[qB+D:WS7Q /?/w9bQRf0v,> *zEA56Ibx;w U/(^2,>#81*V-yU5TTGhYwt`Mt;F\@)ruJ`^ZnR8;Qfog"*yF™{2dA\qt4j ]s*S&%p9niJó=`wASgWcf;ғ;,JIT;'yeb[Ũ@w4]뼘Ct'= 6`"9lVv>|R Xʲjo&5s ^w~x{ǦZ;(˲fUFݯRkk fA)xbcbYEt^߷hsK[1>f#27ƬSp Ӧ 1칽޴|ܱ@_%@ݼ]-߁ Ťh  ,U nU }*t.g=ޓK!JFZN;;EA#'# (V{C*5)%GGlzvvE&= mt/WıJyR=4c/ҠyH0^q.q<5Z4C.1Wh]]\e;i`E1 jBSpjgl#Qmbmĥ=~ei #' x X 1;u#.-#ρ|Ck Ԍ~(^qR)1LF4yҎ-Rqd5k̹,Be2;9`4OJ)h)v:u\865 '&Rq"zWgr,sTs*F%g'&ߗW@jpg9R^ t t@?0h>q3)-;kb =J05yA<iLq^de;rQ00{V-uŬTĢO<B#0Xp%wRKݿJ']SكpQف iܠy`%kO7-9 K"ڒ$9mE2+_1q-UDUQVN_LME{֦aj&c} #UIR$X2ݥ CIsIFĚ2-_RpXÿс;k؏9x)5&oߙ:-Xk&Ɍa[i:u |i!{Rߦtxx~elNqsURܿ$R*TPX Q}J˻LwxI߇q %=T8 ycĥ`#rq hqTY-E9TeuFAg_4Թ :uR0;^`ρuVTf_x)V[yrU)< w]Cc2R{v%9A(G: c2bO@%0@/@R_!Cx8^-LiK@dZ.bb^>}4Q:pkYbg҈|G"M8%VHopu+:sC2;>w:|@pl_# UCBRuLxf(4mKg|h_KE%JpΓ}u+Y\,Zޢt6_^5W0vc+e9q xX!33Ʌ`202`l8 G "߈.{(VapQ}Hz\_3!: &)MTkwx.3.z?Xիs3sVұ,%}%,H-rwÐl{oǚ2SǢV,*]n/ gZ 'ȳY XcqTYջz@U\9|8GX9p=FFmOq.] 8 NΞ Y>kU2ŽhgWb^XG2jHq=df\PU,-J 4H [@/F9Srp7.G, ҳy[ݦ/S<#51 YiXjQQ1,O4G*g%up>-JpVl9t!l :s$:%b=qG\n0. 87̺:KmqwaP.p)x̱&1d=ʨLDT1Q`tJ֗+Dķ(]R1<[ÄY ZU'(Kt%!@:z)*qL0vtj!3^}EyF2Ít9WT?x^pF_ C:6fl[;Wk&9+ 1aT+7]#קfAPZ y:G% }Pqs*G| k*RT)|aUk~bnAzEktn ωN*` Pd/*r&{t|M+[A,Pt,N-rW08JTtC8eXUV] uv5jP)eueCE] LVBSQ4nJY)~QyE~tMVj\MzHX2vGe)YZBl`t $D:mW>NA%t0bs_)zt@!WdupH5̤Kf\xTa0We@76$Px*#:g|ï5-8A`QݞDL.w:8+vX!,p'b`lV#)&ܺR&PG"KMExk͝X:U&x 剶IuCs%bYrixȻɸms;OlBDƛ@-bnLe ;J9,K%P"TPS L HԸϠ9p!kM<}f,F%ʉ\ʾa aԢ^p. mɟ ԛ5 KkE_2ix f.WYd vM#˖5Bc\9 S  k)n-p#IQ5 ]CV0ÖYzC7Z!8B S]SQZjn$)@T~֔K#DZ9 W>NKՔcBL㘇j$WT_!^b*V  TIN-bX՟1:q NX`tLz* LJRB"YT2ЙGN+)75@PƊb 2SJR `X l_Lh\* ,1:-]]aɟw⢶7;~a SUc;NW)akT>"C]}J0XG #a3!|ήm N%"喭4 zg;/b.џ \嚻'tx%Esz/.jau=N+f!߽1uX.f+XÇV'' B*]js?f al#4 >R4x0A kCOnCdI{2 ZWgΐz&%,\+d!H fhi\TN2 mEmK`t-FH$#.A/$8G:EaK!]b|/b. вn+6Dkl=(ͿÛW`F!rŗLfݘ&K;g'dYZT FKct.P2YNݱ+c9x+1oh'3&&UB46KȚ+PT=ˑe#}ʮ)Ѱ  ' +|Wg/uNk_ljd:Rve%qwJ JX(X{pcC[%g,4!A 61ve E=TaWNe_|' R\%80T_0pbx!,Nm}ŔDJ= Ӈ70nqPr!2]=bb'g.sl_-\b<ږj_Io]¼L>#:ݸ[N J@9f7.t>e.b']Kv%&ǗH{u'xs ۹vn=~f%]$Xneo!fRdGRY|KPuň@ q)!; FF.,Cx ) m8e&G$g Kue/A_cV({{s 09z-%@6O |R/fa5P-J#LKTӽucܗA3z.9sS7rSf3/)K["_,ߓ_!oLhy\ŵ^\P e֊{gT5ն?P6.*.ZrW=f ~ 4{G!$EV&heno=w.μ@³+u r 4̅\b(;@ to2jgqE5 H 4ؙ̬:$yN5bSqM):As4(V/u/Љ6GKK=y)®CI_yVjWWS7>,:c#r:XA协CzbSϖ"GTTKƎfYLW\`Kjy&KqHCf8^ rϲJD8k1ͬ (#N715jˀp$YZ Q;ZfbZXc0y1Plk[UCihhpn M #W:UNYT6MiƇ2n7jwX֑N;rAo,ٔg MIH0f ~DK!5>mє}ƫfhvvc= #v^ 9As;5U̺eU7Py~eWJ837n +zd&Ɗ xq/]eמ:Տf s:h_/"C 0Gmi5: J\r=+~bI~5O4t 1Y0~G>\;KʻRDmm# &[.9X4 +AK*kl5Z _07 R@@c!]//X5$gή:.K|lKbse]v,FWA8:áXa8.C/Qŭ 7F=iTlFNveh\ŵ{óy״6|rc3W.+;WQBl7]ޱ`HO _s\B:Wy1m:xzEQ,Wє ZYvP|Ipu.*/OFb])_Q -Xl/C #Y;J:\z_*Q/^YZ|TvKBݔV;EQ'O~ީCǯb[xYCAP)ZCMkkҙw&e`kkf wt>hz9-!vVYs<}JވF)a9|jVgt|v>V0vie0{t(2c? ME199C{ppcqeq.  .G*Pj 1.i "FzkKUBT}:˖Fwwv+(ωan< Ho^_)X${q*=e]ѯ_SpyUrT{lA/4שfu](F <ܥ-55QxJvY]~u{SmNsѴlK"ŇǿS6ljC4MBhqeUspg^ XŸՍ\\x~@Ǒş@X2DӁ.݉HD9-de?Tٷ7GQ9-yrgU+Kᅵ%QkU݌V篚N ǘUzM,v]fnA_KU$|@W]uh3a帬 fx*f,w@~<>SO3->` bLLFր3% :Y( E3Bhv vGC↣"׳rUye~C[ `3c{# JjP>o,וePpxmJKM(eJzBUeJ3*f{seƽ*+1 rxbΡE/u.[rήE XXmo o2cOe)f +aS7n9y`^ҡ=sQuj]~"kW&aԿ@&0Qirc UuzK̋ mf0 {] :rAn\gd w^e-}zi{t'_gyԪ]csV3pF]/pFp# htzup_܃vMAi 1g^.}$J2 1rpeǭX~"' nrީf&dTG1.fׁ׳{t^.ļ҃5_xGs|Dkp:OdS,˃J%٬8+8_jQ?/rŜzq̤T8Ui/g-\xA umI+ȘSQy%L;sDj8.gI.]Mƣ4[4΄ /?1KӾe&/T3`i'X%z !Dd|9 :dRWY'BY<} X!+%x0$9̗{zYg %5и{k}4&:cj<4ZηT»~v|Ze=Z,/=m͋bP=K8D USU}cv8I`QHd&zFW>t+̶8^_0i`b?K ]V`{Y  ԫGud5ܨV(E8]cZ\̜) 3u-M,eoU4hZ3>KY"j;p g:{rN 7$Ee`UvP,]f'[̾"S#^ n5Y[uL` |/lWⓔr܍e@ǩ4&"sx#VS~rɷiCi#CT*ɸ\㚗3(BG5W%s+W`'[z sf! ׼3KGo[DF2Wu^y_G\ϴ\[UuH¥RuZyw3OGx^%cPbK="Ue6*d/55~}GmE⣿#N诹pv?2i^%#^]t5oDuƙ}=,VcFF(HWE\{`FU0 {,#*0WcFAP mqXtpֽд 3؆l&YÐF8QnS%Kʗ`l2#[Eur]me~8M , 忨ж~z}BMEK P)s 8 #d[ߘ'A :2IO\'"F)i 2uy|S-~K`MEVu PAgx⦩=1טb@zf5,\z`M0\84כ\._G5A.gXqpS0G&{2.HŐҥTYɂ 1An\Q`B \nQxq t1X nJ@hvTF5:b>-&C3Uyaq=/ׂ^ Ηz.Ow1񸉠CR.29WQ [W]nZ0O L繨9%4ÉȪo-i5`S-|1؋M!oT!Ams ) (]716wnW(M߼Vs{Cs+PQz^0F91cR՞ C0,;&-nq=e̚@pZ7ٹ癛ȣXAFZu.ZɱPg}dc(JɤxpW?*8AmmE!fhA?w:ц<&R 21YݙvYdd dU?Q_6s.WW&=̥OL "G;h9S9?gsI}e^<NjԱ.w"񊗬X՗j)&*PIcmU6{LFfd<F-Te sC`P^/L1v^Ҵ:Pm|Xd:ԈUK-:;/&f%*<p?b.fFLIz*%i*qNRb."ʃbm٢Lǯ;I/i' Ĭ!fJNR8GĭįHHxCPZxCDOI_Ax+@$:DFFyJvhTeo9{Þ)(CZZB5僮{h,B k/gL_ O݊H? {ĊRb̷Ss 'Ei8"d>eصaJa/M^_4g$ N{mJ''KZ' .B^u1iҚP>bP̦l.g2\1zţÉp1IݝjY@pL1tty~b+䜓 ^ϋSsȵƠyC*e9pTDR<Dq*KӴh/68uWљD֚+bA ‚Sr59mXi y82;~ `q*8.LqbsʚaܜmLjvE}LCюܒ-#Qex]ޓ,(h׊U ~!9ςYebgW9f)=`g-iVpS3u.kbAV`7+VTK﨎4U{h,C"uWUOwSI&f`$M1>%VogVAFPߙCp`W)CgҥZ:ًZ|p@*=Z%՞8a)fd/u*F$ssi8(A=,yN-;t6eF"u}TPsYW#1d9w]u^S  > 1@hRgBC°G1 m4^"Rī˫`*WYwDXJE !_ PNQݤŒrqVgp+%_>Һ`Wh'Z%2?$sOY+Y%;/3VPZp/{*TQ+C:~D;T50^o?~/:gae_:qeDN*Co쥸sʆFi#`k(X3gvٰVqqe@cusp5|M^`pHP,RZxA}-6qҠMLB b{#鐽"`'bQ1,BsP:nJ:&n*۳\*33ZPx䗬f/Z3kpbxD1jNWazBTԺr#o:vc1ʠZ-k+Xe| m5!8W89O^VGuin˶qݟrˡXo_*2q=ٙ ^N(chw+np~xܾRaǓؔ -j],L\as/Fєc I-? ɼ/kP&Li_$.~jتjnG6WN<=kߪ-J9̉{;x+97D]:d0JP+ޯ;.2Z_Cb]ae&Pv w:DK*bf;"lg4p;#z:gXS,! F۷X}7oz9 izO@25%%] `m\Բj3 8 p`u s%}";jX߲ʵqg"PЎir< %ҏu%U>4cJF#J2\)eMIJ.p0S(JUad>QM@'Eɖߢ״t(6H*N,uA4_lbT'pͷ?CuY]̠֏ D2}Eexx؎C쳩2dn+G]|Bvs_|A:j¡jRZl<9uPV3>`,2#tx-7}$|/ߙx䏸SH==TFw_]+3K0a˰W=A9e؇ Y1-znuG+cʁ={fާfJ`tfצ'YP|fƕ楴 e\UQ@0`3&,;;`:\WJT^G uÙMMh| 8K\M^C5U_glh8>eٮ Գt%9T&.ӟBڈZ74ʍ9;kafa]rK 7\A(3XY~]o9sfCɑpyf1Ne+7 0(G'tfπK!L7FXtnDu<NظhZ(n1hۂڢ7eQAh8 ѫdF]*-)0b ķ\>f, .W K >cFϙH)p9NcTv,OWP8҇!Iy(^?0Zq^W))V2ꦕ031_}>~3@a/x/;~ ,~*rqG\* +,DA=~ q-jDVWdZ{0c׆.Tp`|+HfCrE>!vUvF0~z4Hұ+1cArUOZ _c8W'>Gz?ETjjX`tQ^;ʌѵ?ҡTѪ_c—{@n߼:6sk8)k5!XPqW,t?y+8̵ f"٘-&+w̠ܬd.pstt :88r؜`SKO^in'EL(EP\D0k\usPTػN?ype+oiCc5|O~))UliyeJtbVQ,VVagw슚0*XcPwUK_QecoAYdL:Q_4T?3Ox ·(Jpܩ_eKv7*8h&K@ Z]#҃9fR1KXEfh}vtK_T4G77 wlK, ڀ6p9[(IJ0|C,wh]Ŕu>`"cfꟍv/uc?%nQwEBR[q!Ļ` \S[/n ҁnozd5Ź-Knc]qw0Z.;8*4X|K>йBX}@˳G/xyqcH6Z"K< M)(ybTCYJ;JRSRL,D-RLj-mm5de$tTJ@T9& BtB%=63 kywmo|ƙYy[&9 B^#oY1C:CfC GF7 EDvp3K?а9F&Rlb"YQVәҾF:e\ndUfynha)-ZS2SmVbՙm1[r;^ Úe슔:q CڽnJUU鷩1Q3F*dޱheqbVKz[j1\`99|u?9VŶ↲!23lbG?tqvXnǖ}!aUu^;8}D $=%h5\]4mb~ߔa>N CL\6BvjԀrn_`kqV|ǣTiY"|2qu~&ڶm׉k D4c 1 $W;#yq>xEqFWD:j ͢YV!iovxf3{@WNK0JG>pTK h1|T| kjT[)m۾vuvGm7#c_2,]kB-exnmd G ? CDj6:D#5t䁤}ZItjc`ehp/ղ?gK]Աq/3.5#l0A,e1)-:DJwqmK(hXL<|ND* 9NxG+WB4W*qg`K̥E֋{jjt5,dpNZ9`Flʾ yL i0Sǔ,5E))&O\d=n^.`*?}` cIafuXp5%[Di[,NjeQz%Lp&qfy3+ͻGL1=:8ߗ5#trcn45 MUMpT(Xj^b866OoCPJ5C]fAz?d3_Ԭ̰gr*G}`Da \Sq0UMC1,ܯTx1Etx⏸-qIC@#{\yBR,IPV(]Ӥ!NOg$ i<@NM偉ѯCCs$P:#!$Q܅.@1hEş`m>|TR̹d8~"[mL'#Waz* >IOs3X]!&{5-Aɘ[Vn{>&0KžEfYNiI~Xtr1u5fR?'%0+Eh`\Öp[N|3B$W,f mxձKyWDh+s e.x]L3rq?{K DVݷ.Ke%*UN̠ԧ]q3h'T ķE4 UmWW ND"\Oa|0e;9{b8ċ!"!J'[{MzQ!Z%Zx013fm<X7Fц۽FupaA:G}lj?gd/Lam#]k^D/֎wND>E0j`yZpN7*6vųsIY8la.j8ephZaP,WyS{|>n4&/GFAӟM#j0x;Io`8.oDZW6ف=?i m-w !پiO7mԸZ~rIN~`8x^S"i&. 1Bق`9DUMFs8/:[X{2~׈EZg~s)kX86+A~Hk =`cCuP;GWIB(61T⅏='.dz"A Dv2MMoV P@LJ9٤u)2Y˔#_Դ]>#qp k+UGl%-KZAq,0J+iF@Q64KWq(̴E-f3\3R&pvE[4 ( (37tE+ 4ȷ!ORIiX2r)f^([`遼#3)et(wp*݄;>8KY/V7bc5Wh!\'%BjCl$0JF50.b>0CpbYL.v\ |ݣ3f;0.P%pK^{ rB7Vהc2,;ZFfJA äDGq:C$Լ8:jgd >rܱJbA.qhj zޞ{-T)C!akn×.#JXCsSm![W@rӎCq 7bx(eVpUvNcduF"(o|cpgh)l4EQcj),;VfZ59 }+IѽK* h>8C+;pj{gNHh4cn1˾b\l/; N P˞5Da'x;rzU2 q.*\Gb`ڍ،%^@>6JVe&uˌ ,7tN,jU,q,bg@#0xAzQ-rUmEb]i`PIq`/mNW%PM=1q/ n 0\?k!Eimo+Pm=!5c> <=U>`" cw%lr3{߰ 9}}@DtLuG[m[smv ʣq{)1{c[Ke/ɦkp.8R??l&D8H gGn}GGNJ^/5-?.] U+ffCIl/a4=q,!Y6K5hݝj#(+x.eU%j16boa11u+ц L\% J1,Ⱦ}zK\ 7I{慝̾LD*ePLi,ܤ!E00 \b=Ft4m)?-a2ZZ _AZ! 9@=J: ޗsj[)lh`xU RUMQ=|bUop2QԼ#[%}iF#b%cP}e*ԞHQKjwF &38F`,z! ?3(5 -8v 56:h<±E4>b0 8aSkU4=h8N?OO5ˆg{ĥ@Qr~__b3"ޥUQ.HY7nGZ8;eCxXbsʱlFʙ! jJpA_nvbHN";xHoi QO`un_BKWXq%4:GڛuµYJfmtՆٻRʖDuVhdڱGG] I*/:C3d9qjHc1OO85MU{}j;#B-dwWD3;8댄_e 9! yԾD1J4{վIcBUoUw1)0B +z鰭)6TXQ1@Zwʽ૑O/{-j!8t7ί3T3(\P-q*{کin"&hIBk%'[yyJ6+AV5!lLl_$dI}ǺAӓ5U;WUuڀy1*߮P~~!yL>22[r1a1nS [wùk7 o--uR;bwHERdRS=x\KbJ3z֞%|^z{1J)s[!q<^% @]6д<3\Li!a3(y@-;WQ+YkϷ\j{Z7 eiK?~zp{j߳ jO^Хeu(qU06M<ō=ޓ"*bD{ͳ2lwq2E*-Ѐj'%wvpCK^[Q[,'k5hCs !9)gL3g eai~R)dkqcd8/1F]S ]KS8[ZatX&юK矞c1_i s -\gR1ݢ+al˾r0 d20vW ߙf*udseD,nZцzju.(W ߘ%:zJUԲu2--s=|)pɁ>3e6K9PZ#k] i@V]&@R*}J( &4s6;Cεx:ܱ8YzUBg)VtV9 < m{6h6Q`$BzOy,q@^XA*,s+r0D 5k,qTn6!B% @͠uGK~ {GDk9-J$jx EiuƔ?!q0@\9}'8\Y9O0vLLUjAuXLUUm/;B'h+^V&t7g1c+0t$UYɦ@*Uk$?A/VB6@́4h/2S n3QE7 Lzz G;i©Z;SJŧ~L:^9j>5oz*'DNBfd; @fpr~fY51g7*{Ar/Wҡu͑lx~P3%nGòEvtżLƼ.I[RPZ O%,evewqhTC,c-UI0)?f[*mbneD3[g/-߯A &aruems*zp.{ƱTv6 +)NzźWuU` A쒻n/?PQ0Wf9i+ӱ <=DʧB[aapWTx%wZ\(! ]^* 8px:/b[g,at7JcnaqWe4:~a\~i_Ie R7ojk꽻Lv6=S0G!8 s ]'[m3/z}(BrdjPF)<"B2sz#ІUR;!VMAǡ2HѢd2M'F!=1e&&-rT&m7%ҕGLju McpU_LG '$5D5+3Ig3zNarctJVр8i-d #/.+iu9( R@8& I8&fwn1r $]vS^eK2s,hiolدil~]U!v>qs:.`]Aܻ*aw-g+ڲYh~uHWe}3*8" Ȱ-ۏjB[KnW~wz>g9 r~~QT(s~%vUĂ*T< &!GOK Xt UcxfjxjE;(Y]5+3-<@Dl.V H@o&TDto8ąx0չVb ³c(Rܟ@V2 =iN` C@{sw KYz[ @c7zp#ׅv"?ӵ2%R,ɕnQ6\(akClg FjA!K8H#CRr[ KBxA8B8X*Oh$,KEYC1h<L]uؚkjNc#\ȅ߼c>e)XHըvI/. \)8t8 TߠW+F1uE/+و+eNLwF rfEne~7r 81*N| _Cyl=LJ2zLlE7CEk{h[~`/a`8t ~Ӊ[ {$ 1Rʍ0C7V#f ]H5aN. ;0)ᅫfRU#*2PZiΥe=_x g]r⡛wU24Mf&VEgxwyAw-T{n:Ѡ4MV"teig=^lu/k+,De,@j1@ĥK8`FuGG,qglF)Jg'o5X]ʛ&Ңא.QX7ó9QRcٗJ/4 u>Y{{s-wvYNLο], v>um0`+4FS۬X`=J'7Y1<7>;1MiݖUL,YceneT?0? ̻dF3=fR3M!.fK]871#iXosO4 ucvgMn_2 6 Z:StuW9ng[pz̸^zfkQ1z+^%JR#`ԭNgeTjVv_09?Ԁ`CRCLT;8`U:HKpVA $fI"t 37Ox>V:A]qb+Xd]!|?iutbb HQB]5HCPpjgY%5N I_AX5'~7W6 I`] X.!r`38ۭLE3̳XT+N;|,_?ʥ4?:1re57]k׉բ~qA (0U"JNSfq1-:^& >[k9G̯0~Ṵc22+ݘ.?6ާb|%$6~_ E#f1w3ıΦ+Tܷc3\FD@[=;2Π2b f[u;}&6wD4nUN,[Y:/iQ,8HxX6FbiʵHhtUge"QpZL&ϟ:_.\=%j÷T{Msu1Osʶq-DΪp%%eShԼa=X.j& [@1#,:/ f~G [}+ϴ3 \>!e*rEecz.8w*9M,EC@6 RS"&7 F{;Í,JtG@*?+bUopUk~fŞiVF`s㚁Kfۊ | h+h H.jα^˃:U:3Èu]ZX[vo:!B}fOaL8iwM'6ṈEsJa7;VwM)wU#F7In,gU jѫ|DC9ĹUCtY*/5u># SA#l) .Ն>"%05.+\ʵ cźܴi!-i&+ sb ,nc(>tפAUžج?ʱApq1s]a'}̕_Py}k^zgMrndKT63y,D:%Vm8*G2Q wS5P u(1b ̠,_Y3x`:bіSMG 756PNN7HY_"3qtB SƗ\\'yەcYI c<<0hw:`/hRC pN^̴U[̼;ҷ)`45NṇMr U:jWrj׌q)iDj2L̤Pą{+1X?pJ5-s- ST!t% `X%Z .^u /}"(LYLD"cp\K-lP3KW%561|kY]*|2r-h]ܨiiyu+dQ*EA7byFɞgĢLEY3X$0$d\XkT3 3.-W +c+mJV}\S lE>X gE*ޏ(!.XKfnc`7+q'M#3Oo;YviR[ "hfCsz׶y-hÌx%Vg`P6/!b)w0-kUܣPLYγ0ef"he&,ٍTeq29ausMfqk#7-YC.FXAUCy -x).rQ޻ÑXµ TrS*pt:=SX.+& B{r1;@"l?0a0_+\ĞZCTv#a`Y _,u@·G榐rbq{=<(bg[`MLAa=vT=k|".p:l1|KjXk93W2NW#{AQ!1CD'Btן,fQL1 m6[%_Vd2%جAl=ȭ8t0Wmj͑t9w/ΙR4֫Dͪr% 7 .,.[X.j8ô=c|؀X6b,j6JVEfbخ&_tZ"0ZǙY5jo0OM1 TaR0sAz"5c~`>4=vy+ c/H hxe R +.L{Uh;J)LzBjQx 3DHqE!& +W,p7/5ZfuHpk-&mɺ)+{ | ;HI5{aAtsf8;b2E\f".07Aןhb&YfVx\|lPhQjP;5ҷj ruU)|}+^fnkw7T; Lq For"~\,V3ʰṗW&hs)FU, e!^ɞC@[DGLuNxAyz./9`F&r LTMaUY4yZ:ł{5 QFzQ!"BKe)]L Ήۋa2b6o S6xvVv x&Z~qx1%3K- D ClT \^j1Ph&)RGlDNV_ ^ە`LMٺP]kt°(8}B:Oc-CF,yUoeZ,b;֢B9$[]tդg+HU~D& %*1(`,k04b:s<DSC2WWm)= vXu* 1cq'݌JT'l*Cq1x 3MrZ/7!`!_!97RQ1\ c}VAXD(zwڇ|E泙,0/aH9;%DxhEUuWxZj!A}69R|і̱t/W y^ff.eVE6~gC g#R~QKD-XR/0Ae{J`-{pNwy* L_N0P0G5|HXIU.(TB} %2O҈DHa#3n1y1*/46n O!ɮU?> iv,$yi@UkL' eݘ1 m.!؄aJ-Y4D3AGgWRrƛ|~gL~"V"t;DP(Ks*8u> cAg2 /w,P'W.&rƅnD$\dGi[5YIaAPf,嚞Xu0bZzNY =N/ҡ&sLɖ]8>t_sgg(MbCiwC$㴽9=ˁqe0]D! MtHI|c<=ef-ޕ;eS/BPb蠲ȎlwK[k]Gʏo_?1VJTJu6 aѾE)Z+6QlP;F?2R' Jۥ&gxtBr:dx|3ۖ(g?H_!#0sZ%I{l#( +؜yP;BH3 FqE̻DXɜ.eѥ} ygmP~cLcr>$Q7IocfnʰNd`a3zAr cN08?r  Q@ cɉx0D Mi5^mLLt&#x p-պf$°jvMܲ.Cm8 ]n1DfU>˽̵2{kK4$nmR8Bq]5y%LJ;j5XfbR9{ApwX:h6=*ffsB/L]l H]Qf(cXu+0Fa]]csHeUqBiuѝ#VRT(h6Bq%7Zvle=rߴ].e_a@&'RK-[X8bl|Hyynw0,&lQ=0V.˶ۗXLs:3rpZP&Ѡ ~tcR#q|3F({#6ZKX\LKb!Gme+XE.6bϖ|q˶f>E%ML".mobbRP30$!iKV-fo973V,޷1r]žeeSq3:q {`zq{mPfWp56+`o nSA*fYKh|ĶqczUUקx sQڴni̷5n-=s cXg/夀"^20CEn ƻˠ[D&ѱ_LVeTyWX7Ccˬ_+DTv&0͑¥]fWx6SW4t9y}$l AC]IKdW=Ь@` orY*` X z0mp@x !vR!,XHgY[gn<&fȣ M;fcƥ5mƋp&]No\_$^LT nXy"q־!.%trLb_Yy`/ u&l %w,MNL@Bݠn.!Lwx>Li'Ys N*,vcRJ/]`x%3VxRu( 8c`8^qgAF\ ̛e3Pڃ7-w} m3C5GHu6iPUWLRƱ 9ܭ/P|ALl#k-[B}{V5jSy"k#(CS \NHc4pGiGIk3ObVSIWGX7hq\U]ŘbL )o_ ۮ:e*9F;;bb0^=Z_z.ͦv*NyJI[h|HfuWh0^2P<&˶[NZ=9{@Y`G_ErJP#|J8 2˃Xr{KCW2.~)>Ircçjb$S!:{-Lōʍ-?ǫ65X:m~bQw|_t,S:J6DPo\飬ΥvFd<ʾkAG'( `1رUb\;K/Z[\qLs16$LmM›UV"Oq#Rv۔V0ǻ %_$o;94K%rY K r;6ru/ q]XF+D$9;čxZa!JHl!]`1q3bJD/0īg%/5}* !Or` ܶbfA\@Ci"c#1?xMJyKq\s*O4,18:tI= !mO8vVԯ,y!Mb!ic^+$>&cވ~!xf=% u Os?02st&wJXٲft4̬O愪j x9l,5ܦUDs Tݗehn, Ê%z-[?ay[_M&{4=@,^c*Rp'⒮,we޼~eC&@h1De Oi1&=GxC\rU4SqmTk ,}ѻig[aDv)efj&F4AZ4 Bu҄WQ>2rYkc]XY_2,|Ah+ѡh]gږUu-:k#c]a%*dk:7R o6[ˏE1S|q5GM*4P*):|éӬ`WK#+&3Cb%# E-3q$f/e{*ꃓU1s j˝u\*97jYo"9ΰ܀wY2u(jp LGEbQS+dVIWgc]triL.<z3.OM\Pfv)cE䔵e^ A`Y;LW q.~X̫Ks oJ `@X]gO1.QTR45YnQQŒb@ߦy]1Yv9>V?4_b]NfmQkݖծfo PxhwI\z^Gn!Uõ} Fe'J^aejUC y#R{}?KcK5s]80x05 .g8iA/l\,Ȕg1ut.xirU{RF@=܏%򳭝NaE ȃXz(Pfk1eqӀ~ my#`{/cܿhW M&Gvwz Q:1Fvi>u/+ o909S)kMCw50^:̆{Q{An(Lbb,`-7nDjgv5[ ڜ͵y+R]NיPa9xPq i0*q; +Q fQe<0KTmݭ{A0ۇ/'kr| 2/z.CE^iM:tp6ZG 5o$1"ba:i,7uvN!{3v~H7 y}ҹ&pne{C WJψNarxZ[L|L;BJΡNxatO7#Q (WjcI8$X&W#@nRKU^c`تYC[L 9!4Q^b C>dJ` Q|rONi4[ڼ V˂vnet? 1ھw(+TcWқI0^G/e3a!X0E0A=)ѷ[ )*0,Щ7KEA{̈́+ٖ`YJ8煦wS /g:A̠ڸ!,br=701Pe,N_r㗙OOk`Zcȭ`y,69NC ̽%TCU %E"1= 1pq5ѾM+)]9#NF 12B\1+p/ 5[3-]%K&l06#WޜMo1Z40mWykj^O@د6Z%-jԭg`ߴaiѫl,xuݘX7S3W/1mxwvbyhF.lp*a]"t\3kAExzb2XUΏ r|ũ v͝y3£+0B 1Goro]+?q4's/Sg(Q/#|s R3pNO˙m$P%Ne:L:n,;N8QA$P Bgߖ)o#(r''h1!+Ŋmw6/L Q;ju^fdgv`{l9D+_Ep9e`rėbah]n ,99sļ_f ssx5,x5c_WYQ* Waf&+~qשNht/rCdq ë/s Kz g]08#:^| -njˌ(q0Kd54 K)\brtBjYR[/U`LWLE%[31I\AB;XMԤ8q6f1/Om5GWl( йYD cpn!;N#j<l: UVpqA"bxZNM ߈:3L"u/ BOTp@\k,z5x^a@@sЄ@-y2#Xc6P,f$8r^~ʹ s}cߒhX1d"9"3&aC-n >ep%$ch'vY ax3!Q}C, LUS[z#eV"bϩ f?IP ї`f8:RkHnIp5b8)˙[׵"Ewn'Yy-Fb%_Ԥs%TkU?1% XnQw^& & Tʕg ܉kud"aϡo S%y7wU&ڿX+Dڽ]/[}gCҭQ``u 0:ꕸ^"^Ɇr+'a:%gk1a_~")= oi%bv)~- `TLGx{˖;Ԣ^KYnuqGwm{Qᄴ!8gBD Kd7,w/Se}0Cy]#d0 ,-\x^%*OAag}:4~&^"5OϖR\Pi7Dpfq{bo||D/hXl/%7 ڀqi`qal fPU^u%0_E!46x#b|TLr?҅ CWv@%r+( )p90@ a2!K:QY!XthC7P(94rkC<+F=<̪{W{W3r:Zd~&Ni{qx4HVRr G 7v¢d.{Z[2G:dH@D!+w0+~fZ˯dG;Jꤣ?a +CpU2ł88S~ہV ;t -ZܾE$utkh6#1s]l%ŐrМg8Jn(X@rL,,8"P!}n dsϳ0/_ ع/sJpsXR#i.b<z{F#pyW+v55G!n?u 7l%&\1 #|ޥ k9xi^Kbvf ݤn>Y_s6nƆ 0s\ */s>kEfǿLchf0JS=9u؅LM\Y V2j'XKwҎ6&]<"CFpm( «71 ֽvpqQ5p=,;F3o*/r )r[:߶GAl A30rϠ+Vg ̓,`̺ܱĹ˖m޺D<9v{ru"anjw%*yol<%KV:dKTONn"=tzZ4n@:Tޚy_2ۭCk_3{3INW8᮹o>,K^)LjI.gF1H7*rEڬDy:LmɁ<,|R%C+Z~Ή%\f_ /,4$pPZ%*u+cB}tr+pP@&B6b`,^pQP.,2* [ƕ:=siw]K|]xؖSbW67^:::#]a\ӽU&SpDvW{,*=B~H1c'>3dX*6,uxg\3(q_yjD`euf f*:Gq4<̡ 5NcPŕU\7:y=zLEQC;=!h[Mg=eBuL޹~X?s0do,~oRaNrl2;g 6J*,3yx$"H?71P=ni >bbp f8>bZǞ.2,g0cX%_/ s*d7W&TK6ٲ,MW5)\Cl g\f#xyu+wk<ϹJSRŇzV`K2ж]ژ\3qNxYLl[a@ZdcIŽ+qm+Q6WVQ|AaQ܇ٴ__CbZWX6kywwC pY|# aQjEu\"37t^~?|WV+UΈ#!N]CRxWwa͔5GC2AQֿIJE+%[*24]shyҸXx6/nsTHgr`Q l*b\M¢mqx KY~e҄4V?X2 ,CmE أL Q l;Olcyw +6XSaX}l`vvM}~QDXewA˴Nr YJY/2=_Vd٥~cY`-mhq1(vKue==PnZ trŊ3UP1|ݧ]ex\ ' * oy1mf̗^.ft&<5e:TLPn۔77tR)?6250EHiǝ3]fEªt*d嗬oˈUXD =[߉p, G0&[Kx*85@-E:CˈG |5 XD5@l&n=b͸p2k$r p_0+gIa +;m; t%B55enD\++aC#c)Τ7" ;xB͞j/o N0>#\3fs^Džpt`>n:T ap GG Ub 8{T9&p-Z w,ZVPjZJņVP i_f \_br/`RV0{[Ǘirm+4# ]F~(;E}+&CWn>")1õ&VGظh^;^!Ů|gN϶ "We˙P7.ɸ;ʴ6E/ d:B=IWôI.u*芹ͦq y{vf%5 LQﯖ"T|,XJ\D@" }"M:&Bܵ(=1CTe4Ytc>t2]s8s'^7yA V~ 61ȔYַu\̜KVUa^O >cx@> 4We1,G*b 0`[ԗ A7D-DK3{cu~Т3=*&~CU3 m[r26pC"`99usuw)\L@QM#}gY\H.".3y:2A2W7GeeDX2jO\0VM"PwO>a#-~`T-VȠ_0ꛈ:fS"49uE),zj~]#,6ldSg:undlc\Xj1Kc&-w V]a!h4Tn7PSa\)\۞sR1/Y)bQH:+([|DJ@8@n% ګWFժ7Ayl =Ãr%MpcIZ[jPX30M5x™RhWNPA~ºTu*JRŎ- \hV,>#5EglZhV&](^{Gi/^ fZ0UwtZ:Zjph3e+S,2qK6CE@="]Njec#H#M=?ac0_LP]pŽF5&qQ+, 1ZG`ib8-d|GlX. |KzAi%YeP9 nq,/pOĻpxW1 쀘' /u9wRPaesN9}@e6fecD6~Dq7F {b:T~mH[멓ĮhZ t }֕P޽Dypm|C6.q5jru"`"W/> 6_6B^q4^ ' lHq*M|p`(f(ui3,1ڣ+B/,YF[ Ǻ(‚=:rVj-pw*Ih*f9~V_l6s1\NLL UsUKJ%<> />A\\jb3 b<Ł@n .F f-:C8#{4K 䔂鑑ZfG,)nC@UC4VY{Jޱ*xS%up޹BL&,ɯAzx:ҩcEw%. (9PM9]KUuEľ/KqMoDtV*eA^X&r+0‡K/!7|eG#>&csor2WcUUyUuruxn/F;\<5:[ĸ[ZCc֓fnl,PS#fG+ TdS~y5q t~o0p(]Y?h%źRS[Xi܅os}@!@@xLK:\R}00Խ0~Dv0ՕY/W Xo,s"zy 'S./ӂv?ea ^ɹu,R.UBEd< o RR%?Pʄni5/N79q(FS#S9"9ؾez4ʘ'Jh- ݣoôMֳ:g|t4)P<'* S3!f؋rLJg\Ͷе2*V\sR8CK* ϴMԷ8 c6#!{cb[;nRϊ v5jݯ7ZZ5y6fk? (\BW=FN" i=+?uߴdx ]%0Qyt#ˣV F\Ư^Llmΰrh2i*_(1P: E[@E }^ooք@^2XZJ]]P˕yĻ;mZ/BNԲp2ɩ:sn;{i ]NbĴt n1@qNw }XgLefYޠhuL@SL>ҕ0}mMERpj;\@ Eg/N=&o=oާ˺s294+Y*1 rEDG Ĝ_>"״2j_NBS6 쿨\E ouY z3+f Bt̠Z/~(9cm]9RRZ[~׾t:@5|8!{zpX+iTZ̔Q)f[S 6 3n auDfr˙Ƣ&34t|Oɘ*. ѻ2?T{^=n P13f[N+\-,|Ί|v곫MQZEߘV(ysgiߒ>aqA,,fbvxJTJs)Uӿ+5@N݀z[5`:"i&YO%e3snhEtLܼ&F%eoA{〴+_/mc/X@6ėΎŇ t[y|61` 3W3R ;:ts{h|%rTj^e15sq:[U*T$]ua\ctE~j<0Dlqg(no>e6LEq/ Cc]x0͝][dl̬PRP1Gq31#JeI@`Gdh׫/|JV 9WhP^jb=B yku;a7(/;FaB]s-\"U^%ƌO||c>" ij# QtĽ.˲ (-7upc2U.t`0{]2h88;p߸U,(RC +%Nx7V~X. _p~RKJ@-9BVV ~wKE6 lfPuUiO :Yg պqA}1Po,Y|a-w2s2ǽWIl0`rpiTar 3 _*eAr~AWs>^_suZ[i}  Y~0f?Yr%8'upPlc;_ Ŋ)&kw lXz VWcYU2i\'EXO~\CIP$;lAT/b?qzqX:{p:?I]pR[jTN\:ܲ$W3 7Wc5lg!yW(jBCZqM@&e TIxuҸAɿkSd⫦b)Ae*국DqMS[C{*ÏܬZ{Gd/%$7a]nGH;n\39d\eg3@^ie*Z|*LV| [s"uMK4^/ n /eݾ" qsvYbnOqU_&˹k3u varU- i=u-<1A)YG=r)^J=6^֘cd&4ma,g0#/P-P[I`J/=WF]dqt#U__b{:? {5-ubRDQU~ g@7>R4=cIn[ί!@|b!쁫Z`d-ѦfODpc1W-L"N\[62A)EsYUFr^VFnW? 6Gȫ ~t1oyʯwvy;-(@1myc|ֺC1jl no]sSl<(#L5e0QtK{/mzLoKOߴ|2GOblѪRS1iLiX{*/"AVR . k~7,Pe壑~73Kf@=Zy{Rȧvw 7f4S>ecȏeBD>t~ 0`S*ja<>彦u=OEJU P,141F.A麭 *p,efLN/􊲶?wy2ѥ⨅w^e L7|w !OI\cL^pڋ`p T{Mc 1=a@&nicߗx!Q\Θr1sz1K84_VnJ0ݏ+t2QBh@ atkI|V[:@3 pqfYm ޖSPO ~>esU܌,6K*nd$<ɪ!cyĮҽ\g7ChZFh\eV:TsUՈr^6DU6j^`'yuE|J2>Y=/#k.E @?p?z񄢪V /KyK z } | 7oE=( D%øuJtF+^=_hVT\W) %7)1o_,kw݊]ɑFaR]+'"k t@M)niLuopǯ E9b]Kd7369:_4f=)܊ZUP/9ݣV|{sL5pty~SH:' _l=al%tX 'LUB J:E\ >j/ v1ۅK/mec5sWΘXL:=X%[Q jn)RO ]Y+ƫc͂3F+},0n4VeѢе0(([sg3YPUkPQ ^p:cyī,$_#w.{8˖Y-p.aa¿ cA[=kmx6@9٬1PI#?vhˠA5Fs8/SOp.R_pyv0-_DH|!%:d Jjk$0oۇkUthz]_?1\z ]jxR1B-&ШnlR!Ri]PMVd;&޵|A)KY/T>wIV/0h?\ DtWhh@-YV%:XXHXXZ4{#@9i5T[t ̄ZԹz:̚n&1m^C-9A.z^k3c)xx)XO|' ;!wwql;a1iUE@ ғcm+Uk543B\ɍ4akz$JZuRWPO^rު,09Ty,~*#CR|yuv?kz!B=m<_Sr_ij=J2/+}{{kp ,eg\4.*/N79:uz,f24`izPiϸad (6]V?8kOjL6<dFBc eF ,p5- c[0BĶ^sCcpϴ1K:%LwY$9A|'F:X _turaDKs9tM*3{Vo8%8fq6/39u4v̸ek0E%)IRwRiC'5D*K-]>vc6JEkWyhQ>K:ag<?1栈X%ȕGTqu[:Mah^x W\ L\?Y +0RR8߫ Ȇ P1hSxGhp]s/U t ΝKTߋICDs:̭!p뛩[2(Zf%MNu|@kDZ?i؞7AvTqwhe`Հ[](kǑ|ߚT1s+t8fD2C. Ʈik3l1f/,1K?)?PNPt)Ok \uDa1>В^X+dȝ:t^X>uᡟ^wAWY?N˽)p8AL4jzZ|r^_<ĽT)L~ȷs mL7 rf<JX3Q6yWhx|bq3b}a`%*nhPxbY{"2;,qUhǨZ4XM/$ԩ1?l̑6s A[9 0щytx`Kd;(/$̰k+hZq*?v `ukP_ܢ2 ̇SfpcPȚ;mF9e&lrt tgk+w[PPf$v$;ԳLp]ΰҩ^WYxx0*xK#LȢVf 'U%GMe1s ׻dǟCYʆb+WNꄃ˳ 0Ž綔 Xo0(dҽ!1 Q\a+P\R)Vi%_o6Į)]T @wj۪z4WcJ7헯xrA_fRQ!BΣ]bkzg6\졏q~2o P"iY/,۬ (W`aZ<|^.VYzMuK0uHrTjtМ:.&_2뉭\aٮsRω#+maL#|AM*$}]jݿ=ƸbdW9Y(g?p!wbd: HVbsujX09VNLY4NDCWx%X-{gɣ1"PiixIDtOHaq[-TV}ۖV ٱ{ѵ)RXS;/ĩ鐕XiUo |@T; ]o={au40&\t,שk}WE6Aͬk2ݏ<@SU0Jxpi5ql1- ,r{9L̾彷d,5&8W2"֦E%״7c Ɯ+sQXtBYel* uM3md4\LLuV JH&0LJHT%rgўԆ{t\b)dy!ܭǺ R[i)"+ZD Mp_-/84Џ4cc=S\+6ޜK-Ne]"?n*$Z53VL&8yTȗ\3e{L=hi+iZJݫpkV^XXjeTZrkD.3!X?v/GtWg`0PK'iqFC ]#BىqQ`wKhy/( @Lk89i66`nI!NLw*EuAbX>3ךSwMۀDEi'z:LrN L5]XVO3 Kc7"^jvه#@`l u!Mb|U*[uX= r]YYJ+hsna(n[0~&2-mP#S*XU0qfkXrUUq{" 5=g WV_-<1:7ϙbasfsg\ zHXRӈ/뿴ܠ`oxP)^%d]ZQbXِ#J=8/@ @~a)n >2yKhO_.~naR7Yx>wc-L%_ Qw/kt#X}Ȍ5X,=@ Kdu4a;tMnq*eb u7_iOvZrVf@Yj0(n#df`#7ұ,A)Ϻ+b1XY~)}v^ӳ,g%or)q(_yw)oḥRq q|@fo n<،/pK.ݓ v[kΌ9+8Ļئ/Ym0Uec+ ;JF)(=حZ訉pl+;׿ :vrY;uRw]yaS{rGbV3-W?goe|>!])7|bD;+W5+%7u>!וt.GU8%HqVbcskO-ZKdM0b#1ϴNŅp4ʢi].:l" BTEkr0:#\F Čb,R,N i^fpOԥFAۗ٭gߊc3ӚyjЫ+Bw,AH.̲c?-ON4MTV!Fz.s e[լ[QX" :EZԺ2ASI.XqTEwR(Gl_Wfb)Q(B e8k,%TnfKD ީNlH u wĠlF;K:5z;;BBbҼ̍݅PYQKQ*V&ܴ>v.{TY"@8x8J}E4^ok gSRN0=W~U.vJ`< HW>H my+c:o.QX@G1Bj!,wEvDy]n2"Nǘ0u!bvS ڣx/] (U`E8"K;8cᄬU:E->b*|ݕUxR,/b.{=;_18OQ)m}oh1qx-xyB`\y ]Vw> yϢݟ)nPϊu;X\9]c? -_fd + qALws0djR;#Cgc_U %rLK?WTGb35K4?SXkw(`r {DM_X8R%˜Y+C*f+%LK:0X BXG1Y"hsY]D3bTZryx~cbaeĪ`vgWe2U}o䥽b?:VYXvmrbQb 6;1 ff͋QvPl!\ZͺS1Y<-D2%>u_XO3I +Y[wN!JƆ1(GAJEZfN5eB[wyp+/-1+oEnWVf-Y, mh/E܁A9`R׍%`Į jψnpҗ#mG& .[6J\Jr֨x5~vxzxTOޣ Z?V1.W[qK)Yʳ] ȱMZMB\kL!_w*2ٷNu?(84x&V4-H!.rJ:9-C.4%i7o_LLvJ,}N++,qԫАUP}Taχ޹^8[HvJ%4%/7ˆ:IDKf$;TO?%FM^)^?$Š1W,8`gO_y)G:fL+%~;K ?/?SlelIX{tS Dkm|lѕi@&x-ЇXCm:ՕƩ8ZC8^U)ڞp@>D"R1Ҝe vEJaωBڡ{nc ԯVkX̸A:΅;U<ß5%_.V:JG5u]}=Ib: aۘ1u}q+fsJUp]c}vxk2ܺNVbKzIfx?*lu}ë?Sz+u wU@n8F]muR_:Or㟱88I,ψ.W؄?ѣ(wzz s=DQ|8ՌtV{Zf1]cc}0t)8Qp-v8 &R Seu,=У#`A/yXSDn:Ȃ:þ$F&ՈV2+C/7$2_yOe~Wi9{'w9;ZY-FG=F' Fy#UeN4μ̑k~ߘ%v U LJ;Ʒ0 9[r@,J';Փ do& E,8Z7t8Z ; OgzG uVA4F[ ]f,w?+Y"[k8dR-X <= gQSc7ZQ;8,6_^4ֲK"0)f0tA ^$Q,| >cOc*fQ.֘Y~~&ѮZlbҜJ,( Y+pU*||Ww?DQs*%SaOLjW-X{x}]=0{9*U{ֽy/t.pFkVQ:a{pc{ NBD# d9;p*7,woPU}{L_97N#-\?G%XѬߘ4pnh5L6%9oڈWe?᱈Bay^S3mmƀwf(ÈZwv˱|JiLXe˙*r ی`nKA[ qNxg4enڨRe#<j~QU_}SeTU*՘o"B zcFgClDtN2NZz-LtMVj:y"eTfȊ%ѡؕ2D&ͽ-Ļmsķ {˷jmiUR^%#:,7W [c^'ˤqUs!wJr𬹂ө]|*L b`(.w/[Н%3UX ýV+1efV{JD<ѓ E@F5 @iIμMNTEZieRg]mu:!F:-0k% n-BuToG*Ӎ/xtlBґ0h9a+JKrGX_, {xL[Q\0Zs`p߉V;=2bpҾ+̺ (^{/XK;'aR5^:&̷.Gs~@QCy19'KԈlBXouOToV7yYO$n[UX6kS(Mray]GIF\xjLIpS>ÇK6TJw2Le{9W06rJͭ츿(FÅ/ty~Ptzuhsxp"9`p',EK{R͌FlN1]!KI<|_?p(ԡE=^Z^Veێ1t~+cpMg3H}2]c|1-5KЅ*L[e!z~%K'E}DiThWb˩Krp* >)Ku68xLDP&٩~?s)uS]i~{gp`03U0eNh %ۗ-_cSR[1,kiJmGdiRȳ3g>/C/Jzÿ G/Z&F0eA8κdʥPنâyo8~ W-QMxb;j_Hː:̠NqW(MעdU',z^s=r/F6b_)Ja;+Y5V\i9ӱ Z $&~Zڿ7лRW#ۤ6euϱqO"k,/a帪cܸel}lIuY3P[Ѻ$Bí$P+Z(0.X QЌ qfhs\9V2o9+5Rt7ƛ`{jE^4 \[J+܄۶A4 7?hWT9Uyfe+];~%j[ [F?c aZ=tOhW̕d]Wp&\:[\g "~ ,X%&`/%I3&^7E[V0_Onm)XjQ-l[DFrR=w2Aٳ %e8 8.jwa5ޫ%4e.S@-"W[-SMn6 td94ʣGe֥F~Qy[\ +SkQx1@ 5|Mbj񏉥2L]s.3~h%̧dҵa9bd'FÈIQwūa k'p8ޑ1. ]L++'䖺ngÛkr!rB՛ӅgL/}Z8"uŽ|Tr!)O7ħy^T’Fjd_Lgc/6u>YEVA<` yxMQLUu* `.ݹZm7,/+6+F/&q|8k10ZMewkS#\gRMhgH ;]eoc/S-fo1`νuAm/2m釗/uUӈMYnO$g}vJ[=kWl^=]}O$`2&P {@g!a CYBir uA{s_dkbqq6@ևSs 5}@<F򛓀Z?4k-,iWU+e~@f`\=Sm/k^-R`&Wd0컆¯)kiVۤT`XUŒ_Z3h}`W7.<֣˨ehBq0A5G`&8]W/LH@6:t>%rj5,o ̳s WD1c%^X ͯLFb Xg.t`d/q1:06KƠpԪvdv]h+D2AMlcbx"</5Ro-Du;.Y3{o bmXVEnw U|g¤TᅑC+-586.07Yh*COsyѯ87[ܽ&7<7e#NW2Q K;ANX4K @ Lr{śaT@5RsU*-qa`U*0UNH`")LE1Wg25c1ͼ4:)aeԵ)8~!YX6@;j=HYw%HƗ)N^^%] h;Bmc@v4Z1zs_mYop\yrv_Q/@TQPw cmXDP|pKnS˛>' bfFt*,D'֮O`w<]c I#Tn|Gh.BqЄ*OD)&r# z)S-8<\ hDY\"q>`9 ШT&-Du1&|:~=*c j͐7CKU[=qm c x]K|𒝝a- A̝_.C}Hp6Ljj*/\FH/SOi+~.b #?Psa eC ^^ФjaNA:*z[2lC ŃCКHud޷!e^f?`ՙg=TR(uh1u jYځ G Ew_`" #wm(܅e)Ì-.d]aW̢VauEc 3<+ܥDpV cyĥF M@xP=S͙Z#LIJj_Htx@l%bx&y5c0U"ڎmĩ߈T`{LnlbωLj}Z8Z6V7PᩂU7E+nsc2>("káEģKtb\@w QxDk+x/;;Qp{UU"D= j8+6j$xo= J4WCp_ c P#j _+Sİ+c8! kV%|J\j5Ң+/[0Cx7BhN3X5\7m߅s9UPjpz߼SHFsY>_gheq y/maW0-uEoYl9Fy0J w7-Utu5K3*=pI @ 7gԧ#|]S[vEo OGXw>b9^,Yk~ϼVba}˻z w^?\tٕگĻ%gݘ[n3c VH XÈx;rVkQ6LXM 2̔_yJ8 >eÚw2Wq^fRܴ Sr\{/nv̼Go[N/iF3Mr91N_FxK/\:K;{:ĹY,e™ĺ1nLkޔl5uxr{3OwzսPenX څ4OW̆9 Ke%4ԣa)-=}ֹmiAN\*e. Iz0~"SԫgH?bX2-i  `Llw qj"%ECMݬV ,1Q, d6 аt_ IճK76KkdQw܌V=ܸ^yX&'R?2)hoL^In3})Eg2JRXE Yb;VTc"ԠX}uļO1-C=Qj29τفĶݷuE]P=+ SO_RYTdZ,RJ"md?̪+#_Y@)Z]`j:zR1aǙ.-dDU[H]Ed+z:D- *HwqPXSTo{EsQy)(H] cy.\1o{ZeF8tvwxJv6NXkK31¿T W?2W/r9^?,R95y_x9H=~.9(aQw|/ 7}aT5 /%Įq+>RT`)ݗ 7(;'C Wz>ƿc KQEN935i >/baoĺ 188{E]R"B5VKb0m9߉QV);03hV Ni |F0Z4;#.Ɯב&q>!S$\a;MВBQe3?{CGBюG C*Pk%ʹa1ĸS*KV4n^.-czBxmF_8Q> b:%yJs@&ݡ5]M]A|^Xtm̵̪>l:%ꕌ nrC{@d;&..Pp7Q/.=F(Q{Ae{tTWc Ph^`53Xayv/;Փ٘zy. _ICeU4+S 5#ڈ2C 4q x)<،<\nqB%Ukd=dNl -{5%H 欩^&6c0bZ)B~ ^QN廏CC6:1$w ($,.4Ȩ ehmxn"{Jewf#-#.(bl--pU3mXaՋj1tN ,]ft{ w¡{=-\j+Ob /Tx9'kx ²*AfRR#F "9r6/fivvg9sJ+d_fP<9< S46c0/\0>gQƍ%2ޏ{ߣ̵cwKEX(pqsb:f]45 9qaE}շǹ|,* ;5pP"Yy3iNdJ^P:\̺n\ MSUvl5bWrmBPY{wW= UlT<j9Ӆ qhp<5̵*vE4Vr)*U\U-yW寍JP/P~n"ҁxyT%l3$ `% : ALq ̥ܲtICcxI-35:DS-!Av#ϙʷ)/Ih Ͽ •á)ʔ1e8V*trH^ [$I+0XG%6[8*#!= 4upM$L"\9:D]ߵs.6CwBr"(@`Pَ dW8h: n i՝ JPǐn,v=Cu)b,/8b739{;"/n* XƋ4YG DŽLo,jϼX#,ًMaּVPM@HTc@q'i 88fY9ΗպI`1\~ sghK:=UE.n%oKkl822 _~x `~#+=9{VUi`n„ Q,Ld>A(dGN8̊6ܐM{?P42T&rd2y4kt_C=l9:oޱo؃^˸eڃGJWlJz 1|L5 "arݟ+s-ꌿrYF`?i`XM1Lq9ٲbuc/dT y=A#{$,L;ߌK%'( ۉa,uYJ {njֵS̾XOj$盷b!4*-]yJIMa(Jicc4ihL۸^_$ˡ#8` 4#C,ݓTm0R`7)UVwgQ8KRJ_=+ޛ4k*("`|KBeAjV&'KZ{td?A^}0s9A A{!6~S x~ew_ hx%VH 6MɴW*3J`^[.qYe;}S`׺QEv_lLRLU^(F)zEGJ`@3%]8gRH5< b-uxCy4O"ȅ'ZYFlqQ11y@yCIWdAT_o0=ۼڥf3U>Pkm|tj1MVNڙe2Aƒzgh,b?c1+{ޚ+z#{KL'=t_dp'JwSOŊ>ɟ^x@.xqL`o]Gilh&$vauspݑEtmYwXRU?SbR\EmX_Gavw;69΋"2"$)}F ꫙t^SZ~Vq[%`d͋ن/SR%ΥK]]Y=`r-]rՕ6Vq!(f*ll:Dܡh׾̬oÇ f^6~!K~2:+E1}.!z'7çqn./K_qֶn:NFj~w%݇J=I[E-F]/*qEͷdW[ovQv_쓹gpG4>n)N"añ|=hyy`.J0:B`3*hC]oo }{(|pFl 7/6˖MX-L 7C`5C&pBǽu)?1c*3efPk2(31ܼLyݭxZ=?ÅrO01Gt̪[p䶼RAiRc0yGع#pZѽb#\X桽9[SݘzNw .`W;/J8+C'-}%GS%MQؖ(G̥hH[xqc"WݔCK%e|~Z7Jhki/=bdҿ#(%WZ:GP_L҇FN=%1o1HmM1-4^DZH^AuVq ]Uuj8!yN!AF9K+o6:ʀ& Ĵpq76fr+` r z `+"4|zROfGv7{5C9ˍ" Pr[CJ7b_p45oG KWYZ 5XcA^!#wv%]n/h ?y!޸Ƶ52y1>&SKE wK&փ|3&TqScd0ȠPQ7F<(wLPh I,C{؞.\Gg-a# +ttyD]Hᆻ%p([v?dفղAvI@ND#w=֒J" *1Wr&vf#,m[ Pu:-/yB 38{!BOȷY%7P'VMXYPKqj;MPwU(~yiur~ T),A9dl/5[[Ci 4cjbK]t;]mbt_"g/Hh-ײ׉ug{?£eo*J*K # <,bO*(6Aұ3$Ub|K.x.t+g7m{}dPfV  Czk{Eԣ_(h潙X첯[!ϻKMQ܋L.YXUyu-Ƃ [骗|YQZو9JoA2)q#Mdv ZeZ|]㧴=qH.xۧʋC%hnv\JJ ј&A\ۋcA厄iu_n5G;*oqAJt2ԧЦ 37y|Cj+A :wĹ_%7 f6)z3wotx'qDV W.sf\.ҳ(G&/itr߄u;F/ۮV@d b0[34_Up a\к ]&]uժX^IH:D .ҋ&jއ_ A]69(tg=IhT4;h0pJH\*MCUeF:*Tӥ6V]<͇B>1S8dPrzϣk$]jQ>u.3ǷXx +5v$9p 8a[;Fdd_IF.j/BV_L" G̊b*8T?vZm@ƯpĵLMd :9+}!uao: hU$hbcMg1`{@dU;1NݧѪ}wEuk˰YV?uܕO䚄.L?0]c.t:~e +:WUw&n(zdD&pp^`h415>-o |J 58 6%l8U%L=τ^U5&Ѿ=e%Rt;lZ-̳dcj@f.ֳ; Rͅ LVY\.Yzs2guD)xS{kʢw NYN5/ قoBle"/l cv矸5.ʥMG}%Yșn‧LI^;7`3Փ F' ~" :l6NbwJ.&{ !)}UꝚ/w׈W5o.+)2_\B(չ- YfP$Y8[`>2u֨o- Z ëQ6!%%x#Lb G#1lYGjCH< /1YGWw ,}`Ff֜Mw WzjanQ5so6Ni`j l7k6^~/삩yʀs?1k /=} z]Axe},g͵0)X)0޺'yHE>z|Vqڬ&"Y)EUyF& e\;p^u%|ZN .^Lg/%5wڼoS.QP}J:_c>k-(8hኴ. ]kGl Y)E!B =K*+Zfll)i ́Vȝ #syW PfP읠K7.VlGAstOecQ[+ ժS/3` ;2f4yP5n:jac`] ӣ]' K|^25h/`h7VK&lfY PSGX+ڕ1ep_ؼT:T}q=1_ D~eg^&R1ŃZ]gRV78rd`pJwkR(F ah:t/vs8/W c9VncjP3PmMXw=+(+F^6`.7Ӓ2AǓWC!~"O^JMֿi_eR- ! ? %1UG7F+'-"9uS|2*Ν~b \~x+_ o77Td6ZlܜߟhWZ )FfF+>֎h(3ffL8E(sj] ~S5O5NԹ⤟"w>w昸Zd/I[O az8)]Mrҗe\?:8v=\B6cDǺ30,e^VIxRįcrW&f^֟iskf xn:~+g8=»g]1߱|*]F ',nV ~`V;הP'O K ݻIfs~5>|$|~7aXG" ۡL]W%-vKSþFuC?̺k5+ݵ,ox#Q+a\u 8!A\"SQA}ŏ!򾌽`mZ^e]p'*e)BBqxt.'`\f*ig;j9H1FX-o2 ݼƻos"¡+5,~bF}2q3-DiTzR56P0r&eG}]#dc]Ap&.0"VN`ݔ,&acy~oT]k&J[5ЬQr eǴ@bS5.&Ya+ٱ"b 4U2VAɷiJӅ59[tdWh6S鋂Xau{ >4gI_Qrw:@&b>bT:{XW5Gzho.^XEq2JXbf8PŚ\yT "HʬVպzaKCĢ(AZ*V<^5-f?م)tCNcӼ6890kz4@~3|u"!8}+:hnX;4!Ѝ )=#W P{GAvЗ%RM\H.q#-ReLoW[ UB,k*кF:-e.ۇOP}~3olHc3ZiyVnY%"׳Z~{Ũ5`2KV/( #\amWX]t%)4+T \="D\<ouwbZਸ਼ htJȈʠ-UM}F>8n\I;gfWP;e@#aKMf>;D {)(b}B0l!`R-Grٝ\f7Q~jl ݏ8$p_5ZE%k~*`.sbCߤ!M ̌-.a]x->$ˆ:²پ㳆:v`E(fp:Qm缴 lp)FQW }U!7o$(#0#B-q&kQU޷җ`AQN`NXh8K܎δF#G O"u4 oyD5|m;1g=3wڭ>73=ՐoIfTbҚ@i?ru)/ U?\AYz\8+&~ tJ[8cm7)C ZY Ks<'sk.@/ٕ B] EQ]K'l@SLfm^3F;*i аcli21w.&JTv(FGr1n-Exwz&fSx엡Ph8(؍_XU @Bo̲G.Hɭzs-k״FG@7xHG?yY_>qhzv4SN_VK3 .F=BR 3Z+܌b3у e YoFSg;{p<M/U+*B KB'>)p(y]UqG9f͕߲mZ˯Y|@c _&:}=𱩈Wz5r܃%5Zw^9]y\VNZY hP.cۆ 6L=aF{sr!z,f1aPYǶ.%yb! L.{Erߎ79_>6oiloejwR Eȧ >8:uXIK4=d,>N SyEA3^t|+i6j}عwmMU"٦t`Zݑ9<1'XЎr[auS/]ǟ]#/)by!ĢSuOdǬ qq #!WWKcTV>OmWXqU%ojU_YLA`(9pc]%] )RJLѾ"*{EyP ʇ$6E]^őnȖ]@\ h)UpՄ:/}F hQKΡ\rh4;KQJR,׈J7|02dDCJ%Tԥ>jgd6w˕m>*T^ eRd V̾#M+u9ZJtq;=@qZ%JD|&0YC߈Ut؎szPSf= v7D0g+ܷ { vͽ.C~er3ӻKN%y8Q \23o08H"ǫĪ4?NuEe̷Em<\]32h82x0 v9cS,2]O{dDM1n=TUO OO&iljU.-86ŗ !S pWlVWhrD[dO4w3nnGm`x]I@k16kM"`e4^[ClfPW~5Aۆb|(Pㇶ`cE&?c+6Z^:(P(ʼNe;2r2̹] ;eU=бe}aqF ӂ+*c؊-uߴ:N 4qNŊ"d?s[ۗ}ŭ,N/P,D5LYjH5G5X܋&X-Ӄ$ l<xa! `3ND-J%hL{,Tٌ(]\'\ZOgt LL_:c3@#l&7F! xKrx^1LD7E<_h!xm\U8k*43]*7ƥ[b]5/-u܎3[W4k7ek+`AWQ[Z p@]G?%p&ַ%}-`"kZeg1TTWUo XUb`{:`V58Ad0b4lYܟŎ:E᭑\ӣ\@ X/B L,F &)sfG$D4x)_ LqT-u*I"{QSgijj[P]auUrU`7Izd ~-W!K'sd:3dW WA W#>ePP& \}¼NSZF:YcVY6T6A,4AKY dXV1OfDZv#M:Fe|!@)ixBIԻ0;Lw[v"`缸g5t9ϴC\>v\CR]s6-Ʒ"' kDb.k1YP^J>?PHD us?n8ѬXq0Ƴ%0u;J .N(B錄߭-hpx-(3N\:/?\5c%._S4~Tb{_32 Tb{~n{s`5Uݓj4/بQb>bw*p=03̢XgۛxW N0 DH0]h`: 6Ck,,:hJ> C6[tam]ߧQxFX>#u%чoS3r*>ތu-/2ޮRj" h 2لZ aTvJ&an@#e:trų% J3Eu~mkcumf▷v5-{Ugų70#|X&oz?d:Uf+UFSx^6l|U|@, r,y(BuPo[N& :ra~At|OL=s_VDJ,S8]@N I:;f#3BQz0(^~$|_#0OeE>0e}.Fg0#FኼxN߬6(6Y! Mc5_'~2>755e/l{˱op#kfpk}Qq?ܹdθ:|~dkvu1u9Ec2jtR4j(3z9@Yu@ŷq˜aT{7|D)3Fea\34+] ZF^gUGf~2 s K;j0LbXFCȫ,K8%]d)-4QF3+aKb+>`$x&[S^h8PMAnue`ɛ+=D8"N֢%y:s,a ƁMdtSGZ{ߴEq+HD ieG D+yp-w5.(M f|b?q&B$y kk 5`:N9:@Y:ϒ+ȮC^Z T8lMs7#toF*\0`V<]S=1% = 7e0V آ߉`NG]:2gC2iCZSGeZKl/ [VIN85+6 ="ܹkƣlVg''&XB7y+᪸ƻJӥV}Ljo//X 4.͗^#"^↹V,Y XrgxgpPw"x%>O@yp;=_x40ykYȔEoe=: վe(դN1k`x '*c}%LwP[Z= *PNjsqFѳ{İǶ[wJ:zU%Q{'%(ͰϿIY=;LjyrQ#SgKUEkL8ZĶʑ;~M Lt ;Y-[~n p`?wt!X4L*Z3&U1kx3 #f;p2EN`Gލe[qn*sjZvs R,Afl`E*OqV6E7{s+Zck-˺c7h_/JS̻c@/Et#w 6ϗ}ˎ-Mx)sZYP<: y@TApk,z!Ɉ=^% lVD-2FAVM$Əޘ.XϔHHN蕋"b[aQ 3|q yh^cU4(tPw&y {d 1j3oqWJgз 8qRk]H\`w>jK,e|A[;2KWeP! ^f"CnzĀ [Qz3Bnѧgn`Bl M㳩1ԕ/F+cRwo0z)CQ2怺X nu/A)w`ҽ"ڮ`kINM%7dRĹ̲\1mMCa]'Js2`JD-vcFZ=koX CngĿX.h>emᦝtGu qc'.@bvah374mb:,_yyߠ`H{Ծ%Z.;m>]g18Œ{ AS+Y0}ܾPG6k0]:GG36*WN3vUL(}^Q6;"uҪZNFr[H$N=z.HTԯ_daU/\J#R\ A7JL[w9Vٻ1v3&r=dS_ƻV~eX5]&X70/q\+b5HiATV;y_r.ie+&R. '_̯ ]-b:~Ԡ ]2f@ D+0mQV\ Q]Rr$c@P rk@6᳤$)E4ʧ}7yHiZ/+-Bkſjdl*&. XuzT!rgnua l1Lه:\BN7vo3*TTP&oqGzzB ^or[p/Qb 㙑dЦK>GIu&K/`әH.\L\K1%0>т #϶>ޔnf(ihjR=3N )/Y[7K,}[f:4eFWbv>w6 Qʌf;f7* .mmVca `,)vFos3fv!k&-fMj/lJCQCדys@wb`t):2TۙwDf[{pQ/יtí*a._ymz[/ļ]7suM[!3xۉm1`.oݻ]r2f7E&Z]P[4h[Fg 4A/ T_6䬰? Cc+'+.*m<ۓ:71rg9(u܍\W]@dv [mUh!$*1SkRm?l64cf.&Wrlxr|ܨi ̅`S[ܷhȲ,5j2>bM?~t.uG ]Gf\88y}¡Av?se20=51uֈ!lY۩Q>Bh~SU:cܥp`tLete*2,7ջneO;:dJez,U]0e3>ٟtjZ/зR j-Ǥ\Է,f#FjfeEsRYU3gO[ >c}1(-ߘQPp=K㛢X.1V| 1b\ˑ 4[?ļ<8!H >[ ^+0-JYq8TdPJ״Zժ!faxhJfZ̉ 5E{DIؼ 0d-{-ŴSHXJw]Ρ)w,ڔѻddf:8tiSGtbtY덱<{%P RV뗙+a^^#،0,ns9wť0k;1_`|qR[1/zה,Akˋ mx?RՏM@ƐZvm1Y(4c+g>!5xäa"اŅ3y?⣨\`Ac3Unr3dJ9ֽ1>p\*0Ye.q,m:ʑ&&dXZVqq Fvg7dj5֙BMGu\K3XsNA|!m)y~~;`vrHj\66rw*ۚ*|ܻ\Ʀdީ0P&}R]kۈ8^뀾cV=E[2NKKV8cY /!xn-C=*Q3{T.Cel^/i4| hs.-(t@Xe: O `^zF@tz3#wtf}/ KD:܍^ì6=9t4uά%Qbv@ij޺7)u10]@9-Epbx2qBS=V,Tӿ)y3V\ db4d% ? -aRˤCJD4NHWrʮpm?E,Y|A͌97kI/pKm[x0/Um ǐ'auv**VP F[}7O%jëh:Mty3 G q/hWS 5ob*bs"(ļft Vڵ-(znPaP^'qmlg?mz,~"9g2WC%Pm\қQl/Iwx+)b|)e^\Vx"60hƴ\3|ac:D+ ?/$1^v֦[[K tj[cse\wR=W|[pJn.$:qS0.ge6.~TkA []SN՞!(f7߾aslFk,+{t%;ek!Mc.wm棛дT6r]ʡ 8q4q<>=^J;4,O(y\w ĩXC8vġf\KNBۭM ubܠ8 L-l+[; nN.Z|}j_-'^ʊ.z^< t^5#}ojHhbo 2{!+\dVL$)e2G91emq vGn0 Hkn\?Cc5-̇ldsQG'OrV vZq2rZDbxW~e:-T^HL⹗AkG(yPTvPe"")zw,u_Jx:o1/[ZRe3Szyk|M2sBƽXq;rXgYfi)V\nxM H•iHT/l{5 '0ZpLBVɆFMܬO!<[nUJS5,5ȧ,0}B?iO@)d0.dn«YPÛk2 _YE+y]z&*naR7l^lGC "_݀~&U`Jh tey%+ 5UU3T4jy`9Ea,\RitYEqu_14I]T`pPgew_ha*DZ)cH oEDEt){!i? ]93n^ OMJ-59Up{\u;5'RYS2ʻylZVhuOYx.h{k1ꕟh0s oﻈx>L ,8Ai\pkaae5U[RV\şةɚ˘8ǼrF[݄Bs&_yoGdcJnyW%"?`'R]NOwqXTsqDDP:,f]K*=wSIu*W1UaGdɞ}*rUL\Xghŗe{'`՗|%s{6D-JAf Q0%9dUZ|b0 ɝ \3fLkh"qҰ18P،L=uQm/8J»iw[T^6*{/Xy_6R⁙mзC:=.fڢ יV]bg#>ӘCT*:SRJE1PLǾRϸ)#m p'SDtt$&Pա ƥgrIC 퉖+q3(A\4e:K P^oK <`81oCn 5 Wbztu]/yUqlj 9/Uy)U6<˘b vR[ܸ EDuA]e~uGL\6uD#!m`S |=Myyp7 ceNpq"1/lJWۮFOah T^B/]qPWnZ#(/7t?+e0G]|MJ>|ewi!* [(I=Ra'OVUs16,Hծ}1!GlsѓP_pc[!1+lQ2s3(nMtWI=u Oaقl5CD$~(Y4wcŨUs7ܳa…qyw)WN,/cG.[CE_ tk[@ |J|K[ Vs k2+.MwyhWHpV-*919T1 隖 Cy=协esLt{""c&1çn)@4W` 4Él% T+xEMMՏ{M&Yט eT92Bm,!8E@,+`cSeKrLf]d عXt?bSjpB-Yv,ڔ^Nuyy@ \fV0 ng-ʧ*iI\ސ׿$U2hY$YV)2U[_yk[e0GXށtXGu`}Bd@ `5zd @T`*TW/}Hf6 +RwM*N35Y,Y"tWTAq&.Y?XU0ͲmϪD5P{_oIdm{/hEBKCqbw56 $lUe&h)*+"rui_-IT&i*KC eЫ^'mb< HBHfQiUX4֜dI}Dvdav!);kD5$hM~YaVĴk C4)o9#Y`ͬy| ߃oOR mQ+&$3:-_ɢ ~g NyAaukd7iY@c\|#PH xw2p2=vN3v'*Xl$Ia\FD o)Y؀9|oyoꙥbN]8 ɉ%^fR(ϲy*Va aזiEɥqgi]knz& UHI W`G QnT-PJh[M$Iu[=_y(%d#@7S^{dE$l Y'M"q(Klvg}]y [}LH\Yf SMPlD0B4T-*nlmtIw6]t xLA 6 [Vח]w d0ݩ޾%em7Km|Q7if/(%ZH@,m_6Ee@ n4 AIy$ o;M&I/mi425^% i H 4P+S&z?%4I^(C%NdKZK-m,y4gr 6C[t " `d i&dl' 8I"I j# 2 'd Y@4 u c^V `$oDeEBMM(I$i P4 (0RI M),66%bVCb.ܖY+M_K=iCh4mdI$eGX q6 A )"L$hH,{h41ԗlL-6\d4mI$Hi -A$R ;]\NR܆96mVtO`O6@$IO;DA$>hAK48&YVBY ;|_˼^۪*u-$ m ŋmq&#XK-<-@J@H2U}XAK,kHH߲l$I%%@H:LWLkO9piR$FؗNYeaQK$2Z@ 2 B $IIQIA.ty5g)S^4i^MH29[Ԁ[da%@}I $\ ah[,iY57$ ($KE:Ʌ,T%IHf@-H2I o+P- A#k~ j23;XD *Ŷ`$%_$lg}뉤J (Yj"4Cp*1%jddTwR ܋/ V7r_.@@ p)slG>:KqCIg`I2`}5 X)m 7͐YHWn9vCi,#Y0!T nWZ&0(:$2dBWNxH 3| ( 8:/ER+hm+#@PjVh7  + A X S A&K@7)HtKuôd:&{Q`J @IIA2d %'e&A& 'ݶO_sMuBZZvA Β )I$A0MfړIa4aW(],8eJ7h &D(I@"$UK bm4e` "f ѝqGsiwDQg0LjU4A  (+  U M}M:` =00ܥc%HQ$gܐJ#&ɬI,XN˚WIPsc rz#UID" nM $6O&&"N>,|'QG^$mRXgpD!MsI!CH&)Јq6y.91 @XYk{ fk.u758%$(H &?<H$Dif 6DAs_HщؽmX]bNXM \x$䴖u$Po $5f[30Qd\%y.L-Ń jX2k-]\\$cmHXޭ)m`#,RIRgeXӵOn-9M[#0fxa•n{*vKvm}Y˸u̢  "±8[z,3Kݢv\DIi%jB_kw:#lV.&kt:njcx&߯8k%Q$T8[MI ANY g:<7x*%iFvʧ^6 T%E1c2>$]c̄v)6iz4.L XX;kKIcF,{,!I,I0Km}*iMq4_?jqF!ߚ2[0ɶ-ml6e~ߵY2~SD‚JK0!`V&Q)Q%,$l[dIgDA@ZL~m)z3o?lMi`7[nM.#z)඙$h,%I@oi%g~wa eo~3 7' O~ R&4Jd,H MASTDçjPv]FӖYV:.M6;1yG`([4r pd;F'Dzu%e$m2&eeX|}&,kb-)ELnnL`݉2_UE4e$0@?e(ާD0o7i" lސ-v>:y'w^2gS?g3K/Ѐ(lBTMqd֛N4T|V)$%{y1oS"cnȰ$l )pYZRm4[Yeh&Yem/rS"4Mɼ dIЂY5$C_c͞E7rY>XE$B. J1jK mўSKBQCV)4Iez &0I_dH e}T&"-;+pF0$f4ш/ .ǎ+3#mt={wcTIaү4+X-M-|,;),adtf; ".nd-y~"^\ޟ"6? #:Ĕl+ `2G}4h@cpR۔(D C3?>rQWu4a/I@ H@mYMca9NW.DFҎE.8?[4x[@i1xm`p-GDR*n$q}1Q͢X<'k5v8~_7,gX'Ӟ!E\qd&m K~,9ȋe`iq ^"=; LH bۖ'UHk 6͈Ũd5 ఌT] Ki];nN=D-Жڞ0䔚A4*5~֥I-b,Ju94c833ЖĈlF9+Il R4Oƕ̹nq[!oE\A{>$?ǡ&(wlnH~[8|fk]P!(6[DoA}P8AKY~'e5tP) tPJs{|<UI?<HT2ETpeQQ He.[!-}qU b Z>[d|۲(0$>]a|`Q45PլGm0mLA"v\(q)+djaStpDQ7%b0м+AS$z\ 8N&p0㈂. cd"\+yvכF\ܸv gaѶ,%ҨyG-25DK݆HM%&&cOrAĴI "iЫ՚v/g$i " e,h;wkН#T&'v\ e=l̺Zc "$X]Q5[ Z9a8&BFe1R 9NyaY4ǒMɅɥ‡Wavש&Iw<*UqB|1T-$Ff#"J?g-o72sP a L0ʽ^|RF*M.b8ij=1h7Y Q:DN"J>WR`pg)OK^pKݹpK>)?2r$u8%}oNb2q\p<9ˬs:=ޔpJ㱠0~2H MT,WxFY0Ha, ,ytY{#[̑"uQ 3H7 `6@l@3e%cO ]1ki.SpmQ=sX v//}z=<JyHI!$%m@6B$o2R!>wb <Ꝿ'%!齾j"ñAmDYL}YPNn`-ދiѹR"?GJe+Ϡ48$KFTNZS I[6Y%#.~ N;e4kSCY5:;/5Vrw\ d *E3k f}Ue ԽwI2,ɛW&*OhP7XYC=0PA 3Ko-˓.l#&?A&&OdkۛZۑYh857vA L* [Π:YZn yV%My:cX4.!/|Kh k9~CfƦd@~oެSAbboܪ| ^Y)% B*;@1KG2 օSm {MTƐWeOI7@HQi'Hk D 'iY ESV$鰸zX nv w>ߐ ;dRW iU._}agiBg|ߥKmuۻaO="4$DLDD&XVN4\B> tkx°˺wH^jH!)RͼEiUnuhHaQ:x4v;^f`h")/)aXW?k.-|d{rp?u09J'w4z94Fl$-LQٗxГ4)9 [~Aop|U-Ֆsc$s|p'C+Dq;*XP4 V̔Hhy ]a,4-ƃg-@ihy\(W],s62{ m9~sGC5Ev}#P_0$*_g 4糫o?3@M3EϮv s7yfuYH^J<.h;4MjIQo1s|Gsq'fQtnBb] 3n0xNBp )-^YC }ȺJlc6?^zmt7t*%}П%9z8MXrVu'!H]ƸPf)=UX=%?niv)LsU'  (lE P(eZ-On~60d#6k)>R]8VF?^~%#@~?5ʽ%@ 9DoLŏGpe* FXvwC cW}le“e(ۙs᫵cޥK2z'ޣB~{ n!cHר^回XpQKs!7kl0&^4)eo]RٔK뛞0Flt8L+a,)|u Saʈ/':bmy x0G2/dd6FqJ>-"g`NIbMw1Y®;ÈET@1G]DzFW"ZQ3?|a6ëkއ_(sGwC6UQXl!$[.YֻZHĜ#:W/fT kR,ϺA7nd}% xayS@M~(+YUk#giRpS$U gLȠ8TC!3F::2Bh2ml"tgθl<07V,miq%7^c\ YfVcKMdYd4e(u(+kUBϜJL̳f%Ĉ*78E7^5-4ʒUH%&m1j{_Fr#tet#ѣ?BE}m<.5N91$Z!"+B? 9Qp%>q64)MN8,ҩ5\/LII\9Q9ے-IdmKU8W6v !آb{#wbNȎFIK\AFjKOU*z=ғi ʥQN85Ckբ!GVuݷ-YHa,mS|]b E3x p ^A\*dT&B h TUDQ0|DsglAc6d΢: U\$7Q™ENbe&euZA$ |ѸVNb]6ᐗ e=R֣$@I@$ddӊd?X Cgý0C i ][uwb0.#jĀHh]I݆L'C>"UtU]>*qO}ϯAͷG]-(LىV#([l ͛- PD7@̬˄**?N5&k޶)|H3nm6{z)47!`DhhT4.o<f l_vw݉Ubӱ`PǏ 6@rh>[hj,z`h $;l&Ч+ GxF>+W ҁꡛj< "$"bI~fxd1 VaN?8o>i (Uӧ`5>tЕr)-*`>!N&Ft#=ps? 1q'SQhU9)>k{o[= &%wEZ7|GxKD o!f49j5;Hf-c+>`|lІ8kqCDOk)k컔oK{¬WY Rz'n1z(T٣(ŐV,KL,Cv(Ĭʔcػw3Ya\C>Aa/cH3 #B"Op5jDiyKQ"=Qe1[:EːWYy3h0@u80OV&8i& !-A~Z- u{;TbK/mxXN:OR1hߴRI$t#2l)DAMad oyl4X(+T3>|hc "A)ZF8ө2HD 6-k DEE"%a}V_sV}a13pmfs!m0-ey'$#)&ŰO&L.mT\} Vb*!L AE܅nhE+7NM8y[vĴ$K+XyGes~!(a&s[0?MNK2o>U'ipHauց\-«ȑ}uzNϜ077~JqD"}\PGQ\7r3 8[1o]!2\@Ұy-_Tw`3]ТO)L:6M<&>5Vձj.޾v{~-ÕLxT 0wᮯ{k2X5ry-{:HyMeyBԫĖw/`ٍW5oؿ,פ|]!Q¬sk| -x}Rm ΑOi\Jy@VܑrLY=ǝ?j 2,#XtSk 55 Cs)-/agE>i,@ak1b!w(w48:OR@IF,XI$ym*LL̐'%DG}d)զ-rKR1+{oٖwE֗T`y6}gep?aGU"Ԉ '73RX1nwe9ʑF8HlB@?u~tw%_Oe$K-p ie i#bĐP{⺂_LI(fK=YrIn p(FYf4Y_ ܛY&6 v#b?@p4@M;V +a7~9s=AIT!,WgbZ2 RD̺ y}ŃN,h.wQ[v=Ia H}6qc!DXrfK|a/XK՟Op'E48O5~PTpԫruiyPy#YƿTsg҃XvE/6H&Tit0$۬8"K`Z]GRAS'nXKأV}{y@'} l I],SpX _1%aÍ1((Բ>MU%?Tv1z"(oI$eJ-쬃 mBWɰLhmmca'o7 v);o _N〲3w{eǼg..R7>=e,~,s,~3ORK[c?-$a,m7q/9|DcYgۼ+pOqer=e[eo'cφY3.mKɑl-x`8ܞ~om&u9g]84,ޠ|p{dYu6{o3߆~E{5K9[wcSHK2zoۍadL3.~Y3rp69H3,G7MAeC~9g8','?żg#ټLu,$A8ω3,<0ngżdd!Aÿ-o8qOpIfq 9߀~ 9xώ}g9?deY3x,1Y6iyf{e>9'w넷~ oG;n3glYe?w-?6Npm>> y6,929co&'/SngC96ټd$f$,qe|2ώYqg>e|&A6&: 9>ɜlIyx`>ɂYdO8//`m99wg8l8?,>;fq|Xs,9`>6lYg6q,crφYg,ɲ3uegI'y /8,g_͂N/뜳el,2s,?fo9dNYe2˩"',,m x,y#?A|rxxoqY|r329xv2~\p~LOlp|2ͳ3useYxſ TdN2ӄwy|3g6[Id,Ǯm;? wdr,3Y8ß93㿋,eϙL rϑ2ϖ圜ov~ 989K&{'Ƀ$,g9Y<}|67 0p^? g9g?s ~Gy,|6~\,o+,/0}|ed~ 6>LYsg,Iy>9qYIgQ2ቷ6Yeg&;3r8@8m88lK<f%oMZݖqe<2͂͏_|e .Y3g?{6_58d8'?~K oK;8y>>Ƕm{a:eb i7ܟxy&?|,rɜ ,彎;,~gg'2g9qɟodmݖ6p1cYYeYe01er?,eYYeIuueޡYyo~m,,.,.ՒYe8 6ťՅweYeYag 2,LYv:"/Y x^8㭬06>`eY% M89,7K~Z,, 僻Qo}g7M+e2Y%<,G_G;f{v,,˹s2ɖ18Ͼ #~l66m, ,(p|2בխ_e=2x3Ye,YЛQ>9>!66x~yg;k+=sx߀z0~;4oϛmY,8^r5kx|pǻѿۿ\m66I˗WVpqt-~f7WGl7'KO~>$fK, ?.sWT埾sd9'yߚq]Iz؋ϻC*ˣ8!3+l[mm2c1\?vߎp~ a-Yd^~ 8HbYn9[P/Ps܉(6뜳mytX!L:|x / VmXڱ,'=Ֆ>|umYeYݿW[dA[~u9cŰw&?c}~a? ů##YS .seIeH}Ya< ǂ 2 Ln~/7 ,9KuǷ!xK.{~| ݖB[/}Aa6XO?lMw9rɲ,v60ai?O  u 4@О&=3'nwnp-gl:gsvG7g?ϯ'?1? ,$_'w},6\ԣC,w-$Pö$1{/G}CA%xv&-%lTߎw2yEyl6|8b/PZS>X׏lmIQ9;6z@;ˡ,>ݜ]?rM7ȀoV1vF[gCƏna?hrS_ CFFtD,9#oLy$պru)vˬ GWpp}B=C`wa#>Gy?|ȋ9)&K`Vr@l,vƋtp^cpܲ;[wgsGgh㻻5I ;k3g~!{Ym s՟1>- x|A8ww}G/DȒli(XD92ne"Kգܾx=]غ^ lv<9ߞ1wloț zIb^Iϖoj,Bb;}_q yMy #Իթvlp{B8I-/r?`&vacto# ]g,,42~gl{u={Z_rc=K>B~6MuY6,bS"{o^K\P^+-n$R wۧZ:n_;/`ǮY.2{'l{<~%;$qݷWVm]f7|98 սO?r;m}#3l;lK LB͘0%v{l lv~#S y+܈PrʟIdG?՜wlCg.wPՁi-kwk #=~îڏF|lܜ]CgeֱvN؞]]}g=6oR[wfavSr[iS:M -e%_n̴`@>COķuχvg#/^NK~iiim ڛFȉԟVRqJ=;<acvae ۴ޣ DlKHFtqAJLoDdb-ϫRl1tm`> , l6immmmmmmmm6ض j[\5u|9?uu}zc^c׶P'v̙aՇVy=pyb1< ^,^3gCA.' ;،w}~m2[KKaimmwo}/ xm\6_c wSMp&G%r1?fGv]=1ڣc~`;6&61>쵆~Hz^Pϲ{vdZCm]}ݲf1/zXKigm[amnxcmmmmmma[[[mmmZ۶v mlW:M B>DK;wfz?n^lM/!^]ܷWn>j.Ka7I՟ :cY D8,,lx$eq9>8Bgg=, >a֖,M`O0@3.Zԃ .bgz?-%4Mczm]XvPPFlqzqqel>GfB7۳[w|mae)g Ydq9$p#gհY'>M|Kyx~-l^噌2̓ՇYԗ᷎! cs7^Kxݫc{mx&3nAZVXl 6jZ;#=ǩl,2LBceYpfgyo',gϞrw˼ ly ,^1G9K ԺY{ ;u&p}ڶOVG錍8@ Y=?%{>Bn2%)ar: MYiILӨN fÿ%de,1)eng8Դ2,/vd8 vRH:u/?I|6սy8W{\v%MIf+քwuʽBX/ N| Oşg,, K,,,[o8n8xcsu:Cڎ%qd<2='^-aOm~/c}R'nwx=mc*\/;1گV2Omxl98\9H 9,3'Yeq8 CÚ!H$i/L;,zlx~pXɚAa v{g !.$>?iu><9WQ8X-9eElgAi geYeq&Ƀ ] |K>B=[yuc|χbػ[lL':z!wOmz;YRV;IS$$wۿj2O0:` Q/=x;պK%Kp> '"P孟<~c5]9xNmem5_r0]wl~V%;xm+:Q%ov#dO8;k 0$ e ve/#u[vsMnݶcvw񱋾#N am~~A8,-msuul r6ΦGe'nтσLPά廫z7B~`YK>@ 7n̲I1aR Nd< Cِ^h n}qX}iO`g83՟m~9g~'o| $:n~mPho}B̿9;1AH'q3v%#/}Cxtc}rK_qc[vD~,d1lz.ZLm6mgg~Ag9 >g&YeYY՛eY%YYݖIdyeꎥN>J{Bc?io4ձ}>}<6 |3؈IAX:OBZd0 /z'&<[ƶoGFmJ凶3g,,|3|se|r3[82ُvM}OJIY۷>}!=Z{|CK myO[8C€|]DvaO]zpaݻ`;`F>ޮO'PIY9˶|~|6? ϙs,gm#%Km:aQs峔 >u oF ,'IwjF@X8_dɅݖwg, e'HXEN.ϹIOρee>;-g_ß g28>a 7~ZCW?}X]gC.riaZK ǒlZ~Qԋcf0AnjKl]<͢y QGEPILo[#{ad'8[/8?kko9 A3K$2meYId}F F>${>/KY5gZ &l區eەy?I~aſ :|C[_ww2,lq"/l, ,9,mʜ`w3/DJeX;dR1 96~'V~-u=5/KDΣl3+Y$?V-mH~巌^q3 󅅖Y7kңw38K&-aNv-e0vdmA0>[q:,\7㯾Ig˨Oe#9-fvm͙,ZunwmՇOm>;opr =qD@z%kC$z{vQ!F{"묃; Kub{{m{x-%D. }͓,9rlYe>Z?կ\l7fNFuoVxs;l[ylZ/SoK|m% a @Yէ݇Sǫ6eu(&]O|`%>Ϲ˂ vhuݰ`0 3cMĴl YeTvKI`w3 =M}Y8Ⱥbθ_@y"5dèIӬH7sa ckl<{/Wߒ?OtKldW[CC9N ";reDm9ỲL3Vs>Y{;߆/<Y%9, ,_zCd Č</x;v23egrW]xlq<}Z RM䗲;HT:m˲ f;m!dq#4=cR[o fC5& tHw.{'?k9 k2r~{o dz6$mtKt6wٕ3%ݤ޴}B͒vC:a%[xgBz zo^fDJw,0/VA $uԟ#'Lx8Ig֔ L .w!g[QZsRڕ5N{+!>9veYeYs,82φ0xKuۮ *!XB@=YIgt#W%CO` ,&dOAv=N:6 zK,K } Nr|sX!Nk ]=ѐ`3Gs.Igre$q#0zX퐐 Ճ tK 6'7OwϞI %IxymQ-g8!FnE7'|p9#$, k݆Ifկ!c %KcdnξAD+vsMڿ,2?pwx[Y7哩f > A4+,v՟{/{!y!(B30ƍf8xtt!ѳBx2,}>8,uq]d?/c!pˬ,<]#,co,Q;!ՃսPؙ,0&ٰJy]\y xN2lK68, .QSbleeamXH;G,'zlg|N:{y#,~C]HM-0نX C=l:,o 鱁 KW?t'|XǴ}j+cY{b4xĝNw&ߤ2 37NZIL,+ś,[^|ٛbemmmaaC^ݬgջ E^=f] w!0s}w~<.zYnz;ӫ+u6V;7JCXˡx [0}{c":w=Wc`ez8x,8MawA9=-g,  31KĀè$s6Vmmmm'[k,g\KI2Cxiiacr~t䌻ߩe GswGL,e7-gq1)oVn[kǹwownIl13~3sgrKOd k-34 rC,Hv8& M)`}nX,Nm ~LG˰m%ۯv-g"G@]]Džu#ki/$۵_he@=]YݞmۤpŃՏY&KlgVoD<&[OsmGԇs_a;m- q|7Yx{:̀qEjW{,=0/>YeYg92o~' ;-~0Ox|cv˶Dѻ"ib[&GmY'r=Hy\:K//Б41ίNnC1z<f!zΒ']6By :X,Or=?,[m\_K̲D9φqAeYelN$φ|r D#Zó =6-==}pCe^vJg}a䧖VBoSO$: ga_HpO~B` gFXycƍORX"ex^{?V9ݒmOar\(ve#Oq:mp32͙;فgRϋf  I|rH3|CqLu,v~-@: KKc~赅w0:vH|e}O_AjV@5:eڝ1!DjԍS!cQc2 Ll_u:g$I݃@AaY8m, e}]|3B&5Qݳ-a;pl7Q-N;wv/mFxm[I2`EOC^ϹIGP\{jV'b/ߢB_s}H:NgI&g _ׂYՄcp1˒{ؽcu@XKH vgf,-ܭm |1$H2:Ye 1cq6o?P>mexmmmm~[՗E>qtie,~+^h?M"3W :Li`eܐw^Cc6d.E:5gbT;cXĕ]^]:_w%wgP &9v!:a8%9~mٓmelbͲ Yx8I܄6amm9{ƶ{'Cm|"2uynڶ7HoYoq pI@]×R[O}$^St?vKajN#MzMr ܷz| j7?%;Zg2PAc,e daLXHllcv x qlymr2, y,{. dS>>J,a} E ۹!cG{X}*Wlufa$б26oGS.n/@m~f)tt~|:D܌gS3K0Ӗ`,Y# Ս9te^2\|^smD]}Xm?PeWs=sCΛ/;"| B\3c{n?& v,رĞ ]%6ǵx㧁g!:^G!@0pge8lk٧[ ۫'} F#>X{ndPCۍܷwOs0w7u۶/.]Nyi;Xq\Zڶ"ɶ]N!vym%~oo96Xm[ Qm[Yccjae8;dk6A@O@;b̉eE &o O`[lׂ|~G/ MGM$rR\e2wKzIxdne-wj6Xo_Psvb%r j9foXp ,Y&pݖs lmOr, a8uzw{j5?pqz, d87ՏAw=4,oi+`k%!#2f'og"cI.6ίVư=xde "covl덋,T.5Pe1X_bݘC'[]h?Qn-ߖ_]!܁jN;6eWЌu0:͟,-~ ͛32φdY'!o8am ,^[o9Yg&Ye?R, ͹efvN{A$;m]7)N2ߣ#V}~]YvpMAwC={jBK> p{rA|dR{A@8 89/$ m,,?LYdDK!l~8l 9ߒpqx g}B _K,S#݋gۻ >a♽0h{ݯ?We| ,7G\f,zaz[o/Wu/Wwvm-ϐ9,>eYodL,&Aݛ57$ԁm ~$aaj['E yn=b ! $!mm6X${fe6lYc#,߻]/$:qwOcσ²իvcchn]RBt:;`-ԳH۲C{g$8':j(y)݃^FK -lfx6ut ՄK.cFP0kimR^5!KK,&2DeQڝ1 ,Rw.`0vq>Ye>_VYe<&'g[YŻ6g-!m]qAr;׻X$˒p3ѶAіa-lKwYDK&=oa܀ (͚Ekg@[:]6a}%=G{`f=Y$ݵdOww(m.NԆ>$=].p&YgYe>g,%e[YgPY'.c 6 {c2Jfb)\ە,=lp]֒b&d 6-ؐ2?d}.66o,rzO2id% ,0{0gk6 -zxȦK:7SHb0|y>i$x1Sj ~w}ݹ~ "׹AdYedYe~lg/!eXؙgJMD0~,],m>Ev]X &K$OQisxI_P&&GݴgBK݀ K,"rpel v[&w=%oX=oŀ=0vIn ~>/zZB>q=EYg-A? MYem/0/Yg INNԈ~w_Ջ6:N]+;`΄;G]^Ԃ wi)tZ!}3KD2c/t)nkvQyBRx x{,Q2GuaamώYga/76mYƖwglw$|% !AYq˷ b"tճwgطgoq`uvzڮN;=-N55M\/|iUt@/k7gH]KtǩRV׶X ݗ-a,.,,?s$eaeY'wY9m-[26,ya#v^g_1L2ɜx^B~3_DD`1iB |wmx4}D-,1>ctwwiB)HݴNPܪN$ߐGr]dY|PIOL瓣BElKr[xбm 3E*C4Izl3,0-/|9W p͖yۣ˦%t _n>G~,;n$ eމy!=oºu X=(]B< Yk臂Fcc_ةL0lYϢN>ə}#Mݽ_']B,'ߒnsSY0:pqcr99-ېMm䭨Nsm,ropd[gG[ 8='zN_ri#K忨aT']oDF]ݡ;an#pb:7,f,C!:d0';])3v/P`5Ń/z:Z}-_tA`>B{`#NpD-[S8a~ am7PyYX;, 7[jl$Ɉouc2k;f0]=@+m p`vS57Ht]!zlBl) &Iqm5Yg MŲD 82ݼ ZHwf !QdrW$ݝV}w;,tuif<1ڸ%uCmϹ0|ed`۝-6O! oL6G2.{ q.kԇ}/=;VOݻ1}!}:c8plٳ`,89^ y6Zǯ Z˯. f[m^AdeE6,3B[(@ _#,RsKF wԅ{lqlْ$3>m1d_:-oq'5^Տv;o {lkrWD۾˹x> g ahl:7Na :w RIYeYdYAe, 8,9es䰐}[@m3-VX,,d^69lwսpز] ۧOm untH޲, XՃԎϦHK`xb܃[w qèME@JLKM][ d3^IDڒ\V-=V1]^d} !e~domEe`5 il^[AmrVg@yo:dc b{moXs`t 'rtذ{٘a1mmm ,vlZp9vN26]p&f %8,n98 svFL !6C3ei2=]~xGdH?Vr{w-gkKT5xZe8K-3Kt:DL螑I,zOdho ߦr[etrogX߱jKg#s&\ l'3<m8`Zpj+D{dqe-A0An䌱w>ߑ魣%_o_{78vV {:fe08wd7GeODqv8ɷ$d s.ِ'Ocgn,I>u;L 埃ՇeђWN$A$L^; v{ |]pɱ%YvOgY S֗Vx=$n:!ޠta ܟ{{m,g=-qlǨ['[v=_E\D'NKLձn!7&}IBQoԅܩ tq[]dBG mfXI meb\;Rwfz~/mHt$Q'M0R8}˼5#uaԺzmL:{[t.w}e C5M2CHn_tFq>C&'So3o՛nH{(KnvǓ0`}&m̓r^N㍶[9k{0Gԙ~|bY4d=H8a3&hc{mݚؐ@a#e"JvXFJ;h^G[yJ`w='8|nż%Mm5l ,-m-ݵr ywm.AOuN;L^N/wW|V%qdGCɒwq">Ndenx"gDl1cwDa SO},])6P;&^]OTɟɿmgYe#X-!ᇄ[Vݰl|-x]ޣvVia\tk `=b8J~%\/#8_; Y:άttY؎ Lyg^0f^&ZOa?P+DǫF2oRo"I>y,m~6q:Xwx3]@6YVeՈo9 6I e#qO9huhu/atbtzJe/|g$Kݷ}e^RD!wh tZ] X ,^D贛J!Y+h:cR6I<-Sly/һoSo H߁b_Y8D2%X?˧t }2G[Q/;ofwpkv9,]"uOݽ%llBŷغ^s&. ҄ \nGedf2C%fr'| 1;f/i :_^{t&MӸt堘Awݓ'-Kv gq|gp}Sn2ٱƖ0^}qX?<B$%50#q>eb!m5li:qf{$Nzi` i?~&_ լ +\D6"\/ 7c|4`>{:7LLt 8} A ͐:ab^%L%gl2>n~M13S|o%W|6Lw®3eYgVA| $;.Ӷ>csŏ}~d gLoVH~F56/d>,UBC;Zodo%p RGiQĆtC]2!@͛6ώ>}r$lrGY V- -dDݗ"jr"KnN[ȋ{`5;>HOFOdnaZdݧ1adcBsK˽$x<;x'v/遑 `20g]gdYab'^>[r;f/emaqXx66>DD*Z$3`qc?{#9 3#caul{~֘p7.Kԋ$beY3lwcNuB^7}Y`~ݡӀMf鶟`ժIgMQV,$]YdۖmgcaFcve$-˵bHԫcp]-~[;uoP9c-c7Χ]'^wX(iů$ldlC\wut( lWreHwײ">#0]feV^Jl.m8.~;fl4&NrܖYrgYdE9^{缶[fE8ߚq5S?nݍqeB'|ABیgfK:$vb/#R˫mia8a;.eabKtwR&n"#,}, \"!2={?E/[b{4yoYhwmLx5uL<^dû+Z~gJli]N<:XgwO1Uh_w:ch2)Ol ~-s%"YIflqiuYEL|_If7cVZH<}8v,R4-_ly$f8Y$qa:l6=Dp6YgpJ[իßyWPx[ Wdc<N@ye#a4BGA=VCj쾖2;[<@\tiu}Itu'׃iWi2vׂW6%xڞ?/2m[z v$%m3fZڶg N`ۿy#Sz gsؽon0G.w'#$a>u%D$Km߉}綛S z$Tu/3 '@%zta6o L v7|kCz-ө5[elX0-EӀPV^r>;'+Pmϗ|<6_v->IzOv/L:"si}+ nö6l]/LY{'N`wx}%˫O$}$4U#.:d}_.20Ipھk嵝RC`w# o;IlNJ׶ab7>f0SuC>Wu2߷qddIKola& 坷߆ƗGLb6 k%{$Yoe<Ёf vԵ 6 w_m}6i#dHVӸKsV!N]OGۉSv^6%y :w$ly`,݈=ezmsa jX{>y!;S,떘NF$Mk}râ$ |9d~̡77 d -_:BWGmАxƻ/hZ m2q xo{lKp$x ^J^Xcuhʼեݤ;I՗5w>X}-Iu<0^dj]X'O C>ٶI{D? lԽpV/gD ׂwwzE6$[<gVnYu YޤGtm^BD5y\`]wV}@]=_m_m7sy[=-,[䥧ԡtNgVg)rߖ߆u>BÐm2so蔊j<vFt؞JHW<ײoYib}D 'aezJ{4>uƬ6֣nĒ= Cޘ9khy~Ƕ hi'_>HFvF'32z\ .DfO=q?ɭ8~D:l`y/˳mo0죆A#8,]om,lgb .r.ujjK#n<Di-{,F3gcHvm'1 6 M 32}$]ۧ M`w27ʾ! } f -C f߉o9x.-Z]~Cm[5a=RP$[uWe0w|gnrg ios}eKd̀:gsf d/~Uc~`I{bkc|~CTAܽi &ɎF] b7֗8{K.H2Y|7m]/x-9o^:9 }Y 61pf~3m.b>8>9I݇28w;$`] lשǻX:>Ls(h}:VF6)_pQ]]w[<.O 7q9hm-,94(߆}|>]pY{ż_vhnɛe?d-ݕ zv 8 6~: ? 8ͺ'c/br1Ӭ-6.׸7/Xv[]NޒIDGcӻA,=vnājzc/,cϵ,'W񥥵;BKsɞ <}Ax&pib;86@ nKkHJZ%lu؟my6d}p{˰䇗~]:N=w }ND?W{BIlS5^'xDT Vp6nw]ovgpoPLtgm㯿Yß-L)n1 d6ڒ.B/n~y6×M!c4\pܺ !8M5k&f`BwYVom$yd#g,˷w6'7~{ݞ:'ٳWUXսcyx 6$}}l[,6m:W-Lr5ȼ-o-7=pƲڰX~덷xox885 ^:O3}Oc`y7.˻Z9n]lAۄ~,lP2H,=ږv>⫶/śa|_/sHtwM-لX穂so)l g^x' m}$xf|ZdOu/Cb=eˮ7o`>ZCN8>d|}ly}L׶~N>\]OfX]=G9:z}߫2Oai}o#pw6O[P!&)6a?[5!YcI/Qlg#<6،zyG]H,$c$ώO㻿pYAgB|՘%awu 2HKԬ:u $G wp͝Ko䖬- 'ݷ.]XgvlsO$c#2qHA Mu=vB!u쓫F❒ \ (dz#Ixqe|k+ߣ3l^/.yӇ>2:#D>Z,Z&Yf~o5߀B d8?p $%egW7mc, um_$rY =eb{s ] dzuR`c7Gݓs`$/6f}+ :##Hd` X,zӖ ;śF a`z] rm!8 ߻OŇ-x8Ü/'eܶlt(vy 1x'vImo!Ɩ,',YdavxȺ;.=H;ɻӳX4>XM1`l@2ROX]Bql>wX].Xw%>t@ECAԏPWEz`!}>'ݦ$;R2nN;%dwkmn{a!a|_eGg",,ObS?qR^"qR׸AݿahOG݅ӻt}lC7v~o`dAY}]f-Z:2ӫ38J]c݌B}y.ehLFcc:]'"[ܫ"K%-$9ev[mmn2~ ae3nYg )fyqay$ٶq'̆x!cM tɦ]re>]`Lz'O'V{{,KR&m9%AëaԢjΠ}@L8D>;*j]Aԝ-?]YctK'+/)v.,f,;e + [puv>GRev-]oP2Ft6}WP'5`Eש*kvDcvwa6Q\ aOV0Eڌk_o&?HjihC=YKZ۱`b=%܊.z˿|σdgF;woFGGHGLcNCXsgH6̾e"ԻgY ׸oI:"e+(u!tX=dV_ uЏt}:$"wanеLoRip6dΠ}I;|oVBސvuQ/H7ձ윲qxۧMmpvUp~%l3oVx"F8v5t]ug^6wmOI;d6DEO-gρ `|x㷜 >#lw[V ӿg}&;a=w/g `m跸}Yk;͈`Q3ػ.7f9m]]ҼoU!x2A`Zai|ujbt'y =!OmS|>=Zv] WTȟ]5ۧLt}&66_LjX'xߞvsD8uxlʱovb쵋3 m8ey7\{dNsmM|1Gл[.!lʼ{#~tYwGRg^Ejo}蟺GWxǩ 2NIں<{369m1,C VRz/a>$~ÞFZȜd:qs;xwZ^=ym y;vKld|gC0Ilul,@~hFn6RNlu:;owr.~ؿRձcvӨ~WbąӰ&YI/ vFu=X,Θy]x<2YD{_Rp&{#'N&1$C>drw=s؜$}{gˢ9~-ݿNsyOl2ݷ sv;w1'Z[WXX#%Md$m /e;Б޻w l :{#lh^vM8Elq|v ,7R~g0y?HkeK|zZS~YNza 'K}nǨy<,ygeC}&q|oo7kϏ|>Z[s,w,pvG u{7yC!LGo'ޭ>N&O~=7{{Gfi}v%zɱ%9 &3ru!bTv]U @;O{ #>nԓAuԈ̵vu:>Sˤ='lX.}/:^_RO|Zeφs~;Y g-`㫣XBI|? u@L?H|_ pYA::H^K7dkP2HvyE#~ߛm ^git:a_^%I-;(ʶțbal/!!oM5/y+=; ;]OdԖ,SVp)ώv^Cۣ{~Ζ߷oWkvespSKxȻ}{yr8lX峉#K6v]HM,=C>Occ^)0N Mǖ}]|/Vbo`wo%ݾ}%ޡ-`yj:.#3|ϭ!x`: u'N˪>&HW.Ʋ,H0Osx?ܞt-8ߎYqef[orXxupxT-xb8L7KVtrH.Nݶ@hk]}[YR~Y;;>sa& ĺ]NLtۍq:ϒgvIWQ׫F;͘mI#n3`@tweX7""7{9y:.F-W`f,_!7]2oxؿtm<>0mo ^+OؙG:g"F;E(:ubA { {ժun,uEixFO#OigDǫ7N2m~/ݝܼlўn=gpC^[aIdc^38/l˶WÞ8ρٌw%. Xg @z%{Q6[>iɌc;gp 2``X7HqR_#ېufFΞ@oMQv2y=lOFdՙcHp>úr ԙc,~BI>H ϓg vY{+_Sz,#vIe E|o#dw:8l=_Շ7lrm<I~Yĉux&@Ydgrgg:@[2}U_r/rtR_;=Д~~8G3p=~6'MP({wŗ;KoӬPgpd %kz'N[} '{O,}CeI:SX{ex߆շc|<#qg25f2w e{׀╔fp6Gx/&*kwy6u^{$m=Ok:5_O8,x?ݯ:۱F;]шPV0kg'P=/d4oYH_KgoTV]7<$J;()cܿIDyA$]^/m屋c =[8̶6߀L9͖sO/͙8=e@q0,#n0ԭ'l.+r`Yv=hƞgELB7&-mR, i,$O-"nR"}u 'V| %:[I};/vgcܽç6j1:c$7׹LMCX6`ݜc0Hݜیxmԣ˥Hدo&8\Ze;]8Moy-ӓe-cȿ82,7Fav^ [ԙegA `xyk?PR&l[.NtK(oѭIF ]Ytsxu,gG?Wi/SIى>\ns&Gkϖ3|F{>e%me䏧w@>]3oce>ut{k6wg n386FCdy6]LէKx^\Opln_rIh"ԑ.g6dwtz=z;GmltO{Ct~#Ŧy"_}cL wf{an^2#{$>A-z'zxI{Bf ײ6l=1@侥Z8ýx$֒5_Q]Y3zdfnK[w{>ﱇkP:dptjuo; '\'|.eh{lY=l0ed=NN`79:x(Gvv){F=Na4:;0\d!ԷgVl0; "tIF({uz:?S ܵHN$&+w~Pyϸ_oPF䟫^˼|ۥchxg9&p^35{X6%`KdMK:W{io+g|Yx`-ol $]onK8m3ʷx<h.&CMnQY>{y-[mwz̝v@{g܃lmn0#/Ck2rլp?Vou>B`_${#xBoc=Iz8, Rݽ΋=GL{%hK_b迩A] 5u$O-wk;Dc0{~%: ad_8ui۰FY.ZAK[7 dH!I俨vAG峎Ig8K/!,ƶlٖYuOS,]`yu Y}+6s7]ő{3ݏ#%2/{۶}N# ̳azle I= I3oвGܺ)VttfyB\ tKvu8q2ө12ģ7ufkru@Zۜ1;,_K|6o\=jdCVgd rφ|s~{g|XKRvW{+dI{ϱvFY}@mq]Yy1B6$M ߨ5oK]a_a_Axῤ;fB~>Qo8{&{bHZm"mCۨ<)Yf2#Y_R__p1sExu#pYrl>:Wܞ#n1lv| 7EpG\7k;<>";az`2KuׁW-6}]wqzKft trz,ߵhe哳zs-fp1 4hwZ-:t:[eٳvJC{iFm]59Д'{7{[Raٿ"ru; .F3ݻ=^8#O*!1 A0Qa@qP`?I8 2HἍd[,2m=[=RQܷmٺ,m$:8K,Id/n߆W?m^v^ /^`[i+w,=[eOǗ_Ip̒u-29s6;23?v\+Bn>]a6yemyp{l>[pԙߖYmwmfA/%x mO7X'~;eLu{KwnRN9 wmosaKݧm{ooo+Ǘ[O9Xo!zhv_sݹ0ݍ7It^0ym[ Kz奶Yomߋly^?ŷE{n~g~_|emgrVopL˞^y߃ ρ%8sg6.]˼/cg-eovfOä߇qS;fmF<=mowwmx߃--ms-὞vc~,g-m,ofqMОb^ymo~Y|3m? ' xm$ŷo+߆As=[mg~ ?2 e폆LK63~9xxyvg~KӍymgq;-Ye6n[m\Owx7 ,Oo;xWZp-ݍ)<[l69? ٶmx˩a4%|7xK?˶6I#,Pog dgmFwi^YX^ܓ:Gɟ,D<ͧ~/"$ᅖ<1 ͳa'!! yui8wďEh켏cg;Mtmea:ij+;;аum}>YAş,ſK$&8,9m!l}k@Y͘-.=]Zlgŷ'>86me"g,l:'}ZtsL;mm?qȶr,l/,l|85}ٙ{܋>郷Fa\9Z,,>9gC>;nr[QZmn% z?տmI%~ [cmaYwfd`^>AE~sb{d𱃍Hr|Rv[g>gMy뜳!'K?lBnomgQxv9t{(>/ ,xDuwՙ䚟 8pߨGaYf͛<bѺ~ csf b|Ky͔e|6m*{vm/PXza-]3u >vv6 I]|z'd]-X$ݢe[X[P?nSwvsf(2grww|8K Y$?+8xV_,a0'[Vvl dwltl>YYwe~6uc]r=>  |mimw!we.,bKLC۶DIR!uug>YgfMk.džVHu=>/ba>K.e ?l3៏3O97 oV_s S룫NpBF`)ܱ>DNjśP;0vrR}Iuxk):6*:=/X;Ӓ WSu?>9˸?s$9~Wlko8KNm/->CW mMn1DD6Y_cb ٨{t::w8--ݩӲ&]O,.w ,|w7Vp}Yv@C#?i߹?W׶p}-O..H3Xw3en9w=×Op鑌?9BRޯgWdddkS;'"& &'N|uKx-1gyσ|r$?haߝCw$xv6mړ:GIf88_=a< 3~hwy2Zp? wh匏wJ*m[:;o=^.D6x[~3e|uxvX V_m$v[_rǮND1`v;8NY.C,< ǩa9`;:xH S3mg/g8ْ?!ߞm~m<f5 $l8&6O&]667{N'y{Ϭ=6'O'z./J%Ր+6v89߇-w0춄-PCIԎͤb[?_cAF^9Mx9ϖ~=1ռ?$? :Ϗq22rß #-D7Z>O9Jx>ax$Dt6ZXQ.o ~%t}cgCKѶټ}Evq?!';g{]^a|m,ܼ6#N4lT~ g'7w le,&w͏Ę|2K !&PVe~oA?"ע{'qޭ/r`m,#4=aWMp6K!{DۧV޶v\6Y#vYwlw/ztsvG^pv?ClxcfY\eY%p>`jծ,Yj իVgӐadALt_a;ū,vsw%5?:w{1=>Nwwa.tN7XZ`@|ud;hro .ӲLk^Ynڐ?N.džIg իVj5vmZ*&Y ԫVj&eYeY0ńA_nq -p~#~`NA^,ne!X]olDd?h=p}>k.e_y܇^":zXc=yɞaI=b"O"uKܻcKu:a_ؽ݇$aGE3683,,, l,,댲,,,YeYe $qAaW\eeYcdY,s$%yj=l@n{fu=GO`O2Wo k]a{`*gn6;L OIw~VuD?R./^̀ :}JԬӹ0Bz)7yw->#Y˟߇~}xr/8ٷ mxƝdޢ['BbYYܙڟuHuY{+zb m5:9 5햟Pp8,4F#`/"7[;^G]tR{L&+m/̴u|@>Jσߎolg^۷ ~}q-j͉F! {]M?":)uv+ 2=kD2lZ 'C-,iɝd/pg2! u쥟P{,gܓY9uk'_y_po[[mݥm[mx$mxw6mzcF9hKkXb7$8.j2yX=sclXeI\2)|Kޯ|@C&!= )sP'aRMՀ{IJ#ܿ]0,_w{6c[6g-o/>;my!ն6}E]a0X610w=H=wvȴyJuNAE, p>ROa兯)>^[&F]Okk^UYnr0Xaz[]xZr7M#ݱk/O? 8v߆Ygmm=5cw;6$N1^z}%%L"|z֦@hK#G|sERtL>ί6^?o Yj[wV7%Gb˸p3aY߷-KKώ,,e?I|7=8xߎ1հo&[ajȐI~ж{u<0c30cᾬ2S0k2?s yalAf?sLJH͎B4N pƯE\0vfLqw/^F;r87|mpesgg96pfweu!0{y{$}X:t?Vϥ1/YK\>}q'Bq.XZ&uVvJ޸|`r0RlI<@RSܶmn~"K? ,$23Yg9mkɰ'PvVV姧/b=]Š ӟp~أ^C.$vûpT;Z^qI6~- P0t{{;{}WP\as&m'6zH 26,' '[_7HcNz0;?K`>W9(EDz6fN _p gIf9y?L X;bS^4ĮJT0񷽮gg,t@]κ`,6.%p]䛷R],&dmxm"[m~;/mom:.uc;4=~am!.otn',g?>Yߖxmc6[m QWu?HuUeBͩ+^Hv0:Nʧ#w<,6pCfwfun~1GCd7wh\i˫eKt{kOOl݈~Ԕ'N`ݷL| spYYeA%1 XLPѼ?@f{2~}v!yld#1dz=G%1fB,c~qZy#2u_ԖF-WdR欗b^o%g,eRzm$,D-mm~?xaX~om׌v&%d;v jKmѧ,Oݣ-=0 Y<ɬKVVåAw~ԍ$#Hz6{;'S̋KIg:RKYݘNC^;)uZ !e{|w᷶l [[_oĘm) 6ۿ3y\.|M^=Wĸ\GSh{ﴏNL<2@n#}8{wun;}׵v+ȸsZBSXTz6fũE3_]ڽAa=a{ܶ&-m~cmol.m?w~i6稓2Z[{"V}dyle>/x$@ȟi`=18lk ClAZGIY%fqcY߹r&]. tx'6ӇZ9yÝammmdZ &=;-]ԚcnH'i~NلrCщ/U;~#_6^rttyjKgy _\8 CnluaPLh{z^7dw;I+G->/;aymexlĜmbc؞=&,[kn[ln9c,^ ̄]'{듡!h^df }A>i3@7xQ|7'S7kXxqҋkؔ%WLwkL$A_:=6gf]Z}m03i6dz}":]Xq5?=,,<#Y>9solkmfo 8YƭBkkO&}eט";,߫vOJfr2p{ow?_u$%AWp7Z<Q۩m2ˢԩoCoqlqImm#b[Yv[e[l Ol[q66foaٓba.9t}L;Z=EB{oZ00 ro{&J>W0N3cy 8gpuiMmvYvX;-AQlc1%!Y蔳e>'? oyσSs6$.,NV{`2lԉiK'O׆) Ao;kzr-Āޒ}˻`-.g'0lN,}Y 鑜Ff]<^a5S[wg1L=V};}EcgV}].` lO #zǤ[sի{5h.6;kp˴pZù,)V(6XλH{9, ዒ[@[vYx Y[lz'yeIǀ'n#e/7h쯄E#4/vcS o.pczˮSdL$~S$ᶝW2 ,8S3+wa:C8 K<1[vpOH _,Kwj?xfA 'O.doSՈ 1cc$wmH[ i;'@ S0 Ֆi!!0w:l,2<vH-pdԎ '1 'Y : mޭnݭ/url,d|%BO_rF(]d?HA!^-ɰ]ue9erYd,AIep簅V[VGmO Ub5nL^ ܐv30cMqaG۴iè;.'(Y+q/[􃍰^Ǿ[y}_'ttE/}y[m۴o oj薽Z^dWWhqzAcpõ&O9g)Ye|^295mm'ȏH 埤N˿W 8%@ϣ&M}ǶN7d0/L ?]VrM{Ӆ˦,ԶNɪ_Vfo%eEӂ߻f2 _glqqd&ɒ lo@ˆϔRmB&]I u !;] N$̿%d|g ,'dv{=:JD=%X'SKCaop1ݎq ȶh}]]pFV|}DO`y%ߩkax%'X@i߾cd܊G@v=]- {8^g^e@k$.m8VBh6#@ ;(7pB22(v 3->{x6vxw,:ɖy,> Quy-utj(FpX1}gGgaYC{?W~&_E%s߆|)u|,= 6Y?_Q-{Y.HLuvc}sD|]`~OյY^ Q;I q nv^2 ݻ0dAiCrH2>۾GH8-o?Fg|!K'!w,vLwY r5f91m^K~@.*WpmiΥMjܻ\6a~YmD/}ˡո9.N >}ZeLz,- Oߧ'OYx[$ ^_[7>D,9{I3/ߐsVwF{OLzW0c>!gpy-D72 Cum3a8 o8#{DQH?VM \:BAFx6lx,N&dYe=893-?vFF$}Ze:`B=@xγCk?}6%/D: (?Vo 3;u&VGVm~vp $wȗsvSǑ0s,6_ߨ;d#(4@0e-ym;Ap-l G^Vےxm{Y(6^$OK~b,,,ώYYgYdd 8φ,6 -z&/ .Q["ަ퓅=莹:mWڥ䋩I ^2{<]Io oܰ mHwDAif:A6Aј{!&%b+ ee2B],<03lȵlG"-b1f2d2Ov2s, mWě}Ze;ڃ5.ԗ(v -r{.d[/:ůR~.Jy0Q=QEu;tj=-G N,-c ߢ,H[vLA:2NͲ -͖-'{Jfw{ eAg Sy8m,V*D&'9$݂Pnu {lSwYN]Yh ]*9:jhz1/iVu{܆Ij2 ܁^} NF >{fd].ר垬 Y'#,덃$_]Re.],{'N3['xlرdpI=y7KebL(XO͞&?0{-8XFP#</ zzvp6$4O س!OH`>dF%MoK_o페n`~6~`u7ܰNK;?P`tlG. ͛Z,c{&)g >l[l.LŤד{-Z%S[%1\MmmuZ\#m` &!;eAw࿃)a{1]B7ש;/#CbCIųm!OՃEgݣ 6$>w ]m!e󌓩nvGYM-x!IsLGv6N lrm$I |YLfaަ[eܿvKZ8waKf1>8/,;,-JqϸDtnoG`@tϨ@GWFLvݑwuLވb޳О$P$};'b%^wvig\vR,e@6zt:HeZ;EѰ ް>DYNX^peCg#2x8&Km-jq%?vY d[/\g Yek,a|rφr,eIeYge߳e[-1_St7rްdOlm/щwoW'S@Hؗ`Fv|"Cr^vgwd, ;˵˷k}Dk$NS=lNҰ*;Pm-,rdeݥxKrM=M daCbѲ>loax z^z_b&uxgr)rz$w@ҕ6{_b w}Ia :eOL/औlUp'~Hv߾uʖc\ buO?\Nn8-eB+m!ja7g^-y8m8 9-{;,[×Sۥݺz.lcs;OϮg%!&L?sKx}]w%Mr6e,rg+A]c mC3vGml6^LK ż|`<.Fm^S䳏!99 fg,|v^Kp#XHLwt&i;vZDZ,D&~q }vKF|7ؾvw-hN/GS^˧Kqދ^ߓ x1c`qFNϑ,$ ?AZL^3!ugrugvqx8; 22&&L/|k-}!]u$K=:r{:[f.u2_jXn>vyiXyh^oxAMЙx-.ÿ.x % NR#mnGl]+(oRo{w̎fL>/Gef^KYfWvś7 c UlL1l``90^G6m[o HSYia#-4ȼs7ڄ&YOgʾtGk`nB+O!#ȗ*5$>g>8'͇ RGV^JC]-M쾠aԖoop6'r #GlGYC gI/ܽ)˲< kO@ 莖s ؿVFe1~$Gv߲̕g $y-nX^*Hޡ`Y݋$J8 k;T2 jQ95KVZYjL[e]NB[{,A{37YAG;K8ׁlΥƽ %tBwѶGyuzɽYqIui2 okz8}k=hRܫ=.ő՗1ɉշȫ'Lp?-xmVH߰'Sݼ񗑍fae CM1 iuڶ #'8sKl%YYfm mċ ݩwoeVd MmFJh_^X;%zEv-lv_FgsԎv|dv`FgPO&ia,N-m{ 7&nq_\9 춴e%ublqHK ݒ#Iը\,umڵ iiovdg/[ c^˵6իmmy 8VC7ru%r~$-ll=P=>rWü%z-P}1^f- `qjcWA| :E!܈jVGVa 6ųn|em,^q m-fmڄl-gmg;YYYdj l)VaQmD@=ep/l<$q.d Q{mf [RGp|fM#Չ~%Xxm}3nIݿrNC5Ѱߊ^[=- 8d3o^m 3vɟ^Cycm[rpwoՍaf?r=m7K\b'=^6 gqugq_l7CtK32=ԏ!-f|G!6Ŗ3zxl%M]kQ ٛݜmae",Z!B5,pM|!Ydt[mm6ZAlՅoxܵ~;x3xb|2 ~) C`^iDAοp"emgu<Żn݃HlgVL"~>]6>0dVak$Or^O#Xg-ml6rc%;{>Am>Cmeqme~|3e~km,+2NLc7,`1ԛ>K dC,.j[7.&-f?툚kv+kڇiL0,ܘ]GIM.{GQՌRJ;'qE`wQK:>|IggA Clwmm6x6mj1 2XY>g8ȷ6#qʶ]C3ۻRuЂsЀΎ21fIk4^_I /V !odV=c -}{|!zb]xV Ӄ p"==߻wG~ÜpIy s L Ice2K>$532 $ >-#j+\X9,mI fb\ō|v`}Il͖\wy?.y[mKdgW K=ڇ oo*~$ e7"2ru+C.SzuvwK)9 B=-2e?3= !d.]k:t=J|Wdl鲋DlHt/K ~IFX㤘-Zp IۥePfKu:&]O.'u%:Sf{.mԐn >ۭe%G찶,rYfl)l9d;c'8 K eͼ<%Ј՛ xVl-IՏ.c]dtc0_[Iz;{et%уM_RLVwc:2*]&%ڝ/Jؘno`2N#gcNdw6d|CjsIsm`[eon7xRӇ{oemm@,fOVvNpGml r+vj߃1eI» t{,{g%e,O!vPUq}zۻ`gN-/Qv 2wcR4I|^w{ M{Zu=Y$~Ї8ђ,F} L m7axxnaݥݲ)<6Cvuw^[<6'Rei.KOp nc]J"=,=-V.Ƀմ_Yh-R7NB:YbZ{+ε:nw&j]Zl :WZ6Nvߐ0%Wcb엍^6f~G~{m,lgp8! ŞZ``8mnf[ S$#&Fp<a/v wUX٬_K ìGJ% a-?y#1w2-ᶦ1unc5#t8-BZ@ݑ܇WR|mpWCaDwzՖO ,$xmmՑ1gm88N2R|ծrI,s_6e$[ iY>Ļl]qu d3 0H^!>=(#Gߨ`/,Xka #=8Ib6`:&0{,nF;݃=dbYG;;Z;ʹwi?Lgϼ!>!'lXaȶ-,Om [my%Ys,Ŭ,dK-,޿1 fH]:I@jyB~́ݛOQׯ~OS_V l0w$ɭ>- Yr12l;C*=N8+rNnxw!8 `@cf΢ pU>,YFufa7?yX2W^K`XAaXvLXWQԪ,>>/^ yy;:ct]mfveO=Iv]ĥc&{]=D<רs;L6lװBMdqyo0lnzi !8,B 'PY$Ȟw,ȷfNv6 P{r{ga w{QսWs^AؘWoa-pn̓`aYFw5y,WcKY-gvDHC^qy cӅztߣ { z?i X~g[u,cd½eeEEe6;g9YuuQ<KkcleqYg,,n쾬xɳ.ldv`W80 zCo^݃{} 2Y2&ZF=J寜}G]lHcYvO 'y.;5vk_m퇒2ӂ=,lYt2,nb`wwocwr0ntGĕC&pa bd .-욓"]8kq̳w"7{F3CF% OՃGNJv,eYc,Y&,xQe$I1Ξp]/ef7C9dNZ mvn[y,8gruC.H/Bg&gBuD/ }Ì~tz^#q_mAz/c蘻;f[B:cng/S]v m{& `e [m|Oo;o%m`l̓E!Yu@ډڲ8X%eűVL.yn=X^tˣdcZGR kM#-E`vi6grWo!˾]X2!>V~,n͟aci:ZC [[lq=]ؾƖ&v$=ҒSmme߂|ۥd~ir\奃$,%.,"bXKN [Yo/ANuެOE7`$NǦ胎_2n.ܹ{h--C g,6:^]q[a6A=.}ߤ~ų:= wvkm{rcû[FAoG\o l`fx> -zNR7K4CISDNS{gg=wxɓz`'T w{ed/˖lzN .^ I'QoiZ8`V'Nn܈-_R-, ÖgzpAtݖlF}X 3c/[6n3VXY,̐ :n`Bެ=d=,d_eAdgd݂ɋ?K>ަ:޼<ԝ(szaCC"Tw!m l=!M,F 9vdyo- xL#:n۽tjF[x=:2g{q6 ^ˈWOnLt;- MI;EܮͧeQk˜VAC,Fz8mrjɁt/]Kx-_FdӸ o\eW]M~>cYvLa" ggl;%.GQs8~qmdY&e [jծL2 ˩ሷ6^ LۺPxV!Mwk_A$XnXqnY}X},zAܐˎ]wpԖ>YeGl1.a=H d!ޯՐ䎉;[wV]nb,k?T^cN0 OCD.öɏqe cv7,mնط)d6,/l'ET#ݏװ|%q#i7H`3nc]Hͻw'Yn WLl6 QF|esp 'K/k<6I=ԍ4{?Q-6Wq3IG܃v5wclMsǜ8ț,ܳΰM#G\fp\q%J-F, #FT ԧq ;:KC%ޯY/GC Ҟ5a mf0I=FnY&J /L3u_ڢݞG0]'˧$81߻3˼gBC9wo,aY<#~ ob/9Ódpry'8xua=Elw7ٯO[[=NݽYa+ݬ>;XXBd H}n?wgfЏztɇ}wtޯiԣK][o?S˓m$߃<ݒAua?&gp9m,#si`q,,r}yd׽'FM}MYAC pǖηc:0v}`ɉՐ I n'f}ERG%IķdO.Nߴ sHz1/Hϻ%>#_%هvgm -Epx w9ѶYbo_׆Bxrgפb|â LH߻06_UymX{#$;YDճ͓ ˲ uJAPGbXYOE=:^;Il%Nw1t^ܜ!}#c c6pYu8ss3-lb1R?Spr6>g#d(l&R=7-zі $`>wϸ:,:ȶ%aQw!A.;PBN,cKlGn cm!X{ K;t;-<7o#&[ |:[8ߖO;>[6y]bKlIVڂ2.ӈ'\?I2,#pDz_}-e2v=,=npRե<5۾Ccw`د쓜ϻOCir`=tO%_l.>$N,doq3qmpA7Yclռۜo  wo-2|XF 4vd\il_E2WOwKB  u?vg˷lXAgm݋7!:gx-$7!m,aMnؐ{gv_v1 ɚmMcmwlkݦ߾ %a-K r|cwlqrU!t9=}1P/ G#{aO&;!zɳW.F=˗ J`^|7NM1`ٍDvo]]`5kͳ/x]}~{/f^BlgY86͜F/y ?&7OQ@m$e0Gvsq>[sk6 ԟnZ}m]Xь/wc엀.@Z:lզ/MucdfGH=ǩ[1kH 1۸ݻQ!ݺɵua7,lp~,mg\lk!}u=#Cc &٧Vd<'i&`PϨ_#e|40p ^w峖6Gèdn1ݺ=GbXƞ6hda:Æ+N -MtE6"lǦ~u7lzu;33aWGs`ؔts˳$i7{A~o]s۳c=!}-.$R;pm:30 [y[Ƽne'w|w[>XI2]䧈F/taPK.smBE5j7YΡ;Ml͗]2H Y~8 pgܨG ޷dS,}=FV1,xc , %M~#"$<*4~˻C7Rˤaя[~&oH!',~.vǬz+亻pm_IމNvuշx ]$`ʝL@t;##m8&pϑlֳ_/ӥko,r6 aitXI}XՌo!au&eޖpǑ'\ǩt@u8yuݻ ;-sv2r{$HQ;5`^83$4vgloh{g~B;FPQG{䳿le$gԗf 90ODW vlht~AY1g{X^-#w>N׾&Q:F<$-7~ѼuA>h|'{d2*2X2K0zICloz{,cs?nOi~`R]q)V~^l8}Y#..]{rݣ 4ol ]B?Hl= >Hzr8@v!$}]=n#!!1Ȥ񺌗vvs^0MOxGNVvq _a?fk#ɮ+zeubwԧ_4?侍Ooc/g偷=lw!~@  -[.l6!7' 7ŏMiz}?vp඲g>݌ݓ|}FOi@tG$C!ZC=wbyv8,2ͷ,>wu oPfeԄ[I^c>Ֆ+bF@݀ yݼGb>A> Wk;_e:yvz%.gmciukAeǃ:07Nmʻd~'v:X_lrlX7K67I^8 aϓeC2w;ȇa$F<]skk=[|ZqhZaw+:; a0r.>\Gs=qg=Xf˝ݥ^Ñ].@]efA:bdnKF1|촞/2B(ԏ^ {@ +Kt:k&q }LH}yaN{y? =YvIb̒B,:'98,L !|=Lcܘ.#O-L }_>!); vƐ% Gy`|8ykH51yv6[x}ۤKYݽŔ~deGGHvK&;pug׌ >L} g -Ᵹx;ʝ \}{.⭱_/ X[-:,}qL%߫8g>mVg8$8cFNr2geIVoKAPdR0-8r=-?P [4~ =uuߎL6=7sGV^A' KacNN ;.AØbNdt2~G`F |ðmͲ={t8͋u/5+lY{a'e:KB z,-88YeՙnNK3o>qlgl`lK~0]GٳdAlh,r̶`ʭ ./B 8QWyc{pg0noRLX;vC\zmBŏ,;' X161ϻ#ʼnbL|~%ncy#fJ $da@l6 Ζkds{a;'3dK[-hdu_z[+m'\'rp C 1Dwd>^Aٍ3NYt]sYy3G;1$ձ1m:8<%ZEө/TFoc F{tu!$ǹ?۫3;.\D_qv.^y\ K!I4ݟΣٴ\;G;ه<6R^M J vdMY%wtw:aZMH@^du忻[wumc67wg_plql>ɛdC1t,G^pVʹ{tnf &;?+ezr] lfp){ ptahXl6v{uز@ab]%}^_=i d43[:5 BwsD.ᬿl?fehBē]r9k[g H`ؐ?ԇdOg`w=6[h[o!g "#dl!-ܗR%r˸8$;?_o3Rׁtv~aՏ_qmYRz-'Im8/ou ro2~!]8oCqXF2ZaհO1}^KK..>܂è,y;HnbYON;-FK}q0Y-1,%xKaaϼ{kyVDh{n@p4BՄ:q5vzn|vՒ^qm~]l]EEwsJޗ!/~av}ݦrev:ILh`{D^+?rݭuϰ6,kP l 8؝K8xWrul$섁[kucvXՏ#'nH_QVݺe:or4oŻky.v8L Ե ݽ]>^6eދ^z(nwiv׫ ^CdC=՛fa=zjEl ]C:V#:/z7NDt@mG'SܟϏVL,X-=ſN-mٶAp;gSId/ݎݲ>읝MI6}G͆g6s [ܗrzͰ8v|Rc0-XncGSFmElfW>rtO$%>\}kk}8;c a}=@t1+pC =CgL1̰][6$>KqwW 2G}0E?n:=va `KL0ZvGoNGĞܝr}=%oCt1X~CH/?Guwo|xb%8f6i̝HyIV}g9Erl^Yy2r_1q'&( ܳ'DzGeԱ^{`K߲GmvHSZk(}Ks3$v;D/8׸HLgXy=7~f%F_ۏ j}7\ [:3ՇՇM~]γl7;M#Avi3pr5фGqӷ\-pL3 ~1m ,a<ѐ}$jDbu,쁙X'yw3lq(^xx v&Ggwd #[vh0D~?;VO͉ùL<ܻA e 5d0!-e}CO"`}, UĶw%X1P?Ὃ'6X=S`u=RXK%eDCK !ݤp`w`>f?두yo9t]qn{ov?xy,h|":1.E^Ր}l+r*"Ŏ)Gp2|7Btwj!Gl`}JX knk.eg-@? wm.X]Aav?I g9e/:/#;auy.;~%t Gӧ.]oˏZ^)c;6X1[(!Gn6l!쥿`@ Z/3%äȝݼvu0y$x3jogK?˯ hu&.{7f&[9cccիPtzftu5l[Ba% C K0(}2ns tp6~|': ,8,ڳlɉ`Kg᭬/ܠ|9I0vaZ]Zf>Zv-aOr=JPф,05vܪDy'fMѴcУF}Pp eՐ;Wkc-͓+Զu1kstAջlX =䬿lEάN?ID$EGj6GYabΝI@ gH!̶;>ȓv˫~8"} s?/չjY;uv88uo 읗.lIx'fuum Oai?E4T[tl3{òIeo:]6AnܦtDXA܃,N]GZ6v'Vfݼ;vzbSW|K`%uOV6w_䃠^#[+N^ e[2-/ !z-wܯEa1[kϾ:uvtO2O_o u߶dIleH}$I ={h_L:^KclQKqն:x˻^8m"xS?v nCg;E?wŜ\6&J'#4Dijw HÿXs~\@3ԋ'%w+;i+`<Z,S۝XoCܽ]@/2kn3O F FzNIJcl#HׯqqW^~I糝Fwu;Q0w-][8 ȇ%I9r߯~zF냻-h m85# o}~ 9X۲zda ,H / k 釡kz$NyC731g܁mm ;N5~H^] V?v{t܁۝bH>^nR>nI {F3^ 4` @#hp.L#caFB#iH%ǻ.>{-f[o9g9lm/RFLL7+ [ݟN>[v'$'Gݞz<҆naMedt`$F#mu}ڥ5 iŗ;r4]6nG3 Q2F 7o}4#},6~$CnpX綒:a:y8@4C&m/QCau!,0@.܎uv|=$ukw mԹ 軧Xxϙ߁'"ğAhyφ &Y!9ٞz,@72g ]cFOc`t0Qȝ=IIXJ{+cz|.Z9^˥ d6BSerI݇ר.;6%x . IO}XwzF ̇d,}]h4[|nClg\g6X:ee9,8ɛ&݋qt]-0͟-Ael%e,;'CYD[w͍ $>f2gV>TSg}ϒkOUv L`t#߲OI72GNul@IQb^I˶Kp w=47~%zl;ON…'L3ɘdD;LvǶgp~-v[?323bK/8 1lIHMmmG`;{uHk3qil_K^10mrdn6ǻwlNH" mv^> TXC]W*2y-݄(ݥj_9o>Ctu#Ա6[f;?Rlј;v>e GNeJϲ˸1$'wGbYdursm'_||-Nu䷍φĹu+{[K,-}=<-YdmIqO{|͌Un@årl#>Ҥو:F.€NHKZ=E,N1;`1;uc:λZ޺"/ I3;dO.]cĄ{;#Cw> u>nmrj0>.H)2A/aKYwY#+vs->9g%8̋m&'`8Cղ/ ˥)3xvTBwzu\ax:^#;t =In`6OI%/ ‡Jy쓸;>em~?z4LߒZ`a{N`]@ggXYwaKt\CNG%8^ɛ{#zs?޿ű|y' z_˘3 u E >9ZF3`}V<glse?v IrʢW^xY8K) lܯd7-^m!mps:) 􌁉`2t6+6tMF$ դ?sؗa"kW?6L_WJv)z9mԼ'ȘK^$YA\`K?{ p=]XGGqOԧ" ݺzLI,V<CO>%9sKyN#>ym㤻Ǒ˼ul>%KQ,{o:m$X'I;c|QLcW{N&e#ez{vS0w.meewwڿfOKxa-r}QHWè!KKWnEH:y:{qx>F:^c/!GHĒ^$%6Y3prY@K3w۱1}9[a\#-`>mou[͋Rët"u+Xr۵,;h^Eg~촑ՙ<_o<mZw$vl>dWW&3 N>׹~y,uӨ=pwov+}}-/*噬g[?^Z;i/9m_@.uF糶e,I/!`1]F1'՜oþ
    6?&^Bݙ퐑-tp'q7܈#4 ,oݜf uݷeam>- ȹo\KD^{{- ]}qfzd#^Wmck!׳gC䦸&&Iu;n6@N9i”v`P⚎#wRvzw7l4Ruϫ!xoC _]oϓkz l Uubwݙ7wN"X=Fumqegɶ:f6N3p|K#6v&7e%{6ὖxE'e%덡ԬOٺvF8r6;zd,=dIToǝCfYVvC|q@zwe9 ~ {}'O=)]OXW!k3uFVOc':,X%ܿ~_ϫrc^Ξ^'w>:}|7Yx%ϘYm\f,3ËbPR}Xao '<+^KnE>6r0ɉo݀b$bz'p]#@Ǥ@l7[h"5 ܡ}E艇Q}Xiw ?qΡ":ԧLFq u 1B o P !FpߋO6Gc7NB:o/G $Izq/ɝ޷wL C!yS{y 8y8f~K[1iXZ9;{wG/}}|H̅c#b6:->pg՗㤽{@gա@GB##!2mw4F0ysM w l `wYelKO>;#"=ő NǒNl b`e:Kb1t_Kowlll3T(wÎE@`-<{0tl 8 O˯6~lm!c_pʰŀ ^L^8dߴ6˾ @> 3Vd_hΤߤg-r:6@rq)tD"}K%:v>e u)m2AF#b:GF^t e 8gm&]2פbdCN1 .G%MBhXeg~Sg;)!1AQaq 0@?JR܆)blyZbh.0x.to 6h_O#t9Gw P6)7#fy_+a.z6ସ!ڮD ->crDR䁔.X%FXZӚ VA&= 9'Y@ Fx2X^:b MOHxCyf b=pP} (f˜'yu}fUOخz]etcnrffihf멮2PMEnVwV"rL Wf7u' #$ &q8'q^XZ|_yhDQ.r}S&Y1oO?>޿ IS8W8q>6 7L=O`q&{<$x1kHyc$!`ozw"nZy@M:IƧ.C^O#g"<^Y˝yƍ LfD 5׭a>~f2Ksm^p ; nSGx3!&p:XQM`/E2ofEְ_c-.GXi SD0T;`4Zz~S8:/W4) Ç|eV~p8|0g[b?x>qZjwFΛH7d31F8"L d,{X7 s#+N{ȯ:usN?l|爡5OR ɲ9übX8xa5T׌t>Xb)}oRrkt\mx;qQJw1لcκPy)yЄ`#cf5+y60f7ː8!tqrɧÙ4\_yɾ-F| z#oykAb)1 l)M:W^e {CXp 3<∅Г6 '3F 2ߜ8iE 8nN16Va] 8lW-R[ƋUN9 &WT|L2xc4a9yB>5I Q`8>HQ_sd-Y5D.9k!P v¸=+!Vfq";9hV9]c@!QcY)țKlKN/?v lup iӔHCs8gG%4׌4<ޝ)?ޅ {4~9ҫPN&|A>Smx+cB@ZB9!˜I8?$ȞI 1@P" ~ zWԢ$n 9yOG<=59>*.!MqYyq~wqGe`wt.߼XkD2jX') |* usk)RW a.fX,(9ɣWXnXc^s5wAyjylOY顇 ņWne0Y;(SQ'nM ><,x~pwpOy6z1]cɧd1k& `.p&01bڙŸM&HhYKJel* bws&7 2txX$bq`h-(oe^? ~&0bްPHƜ7 R۵*E{AxҼŤp(9}1JLP⏥9(W,7Ti-0'jQiQnBJaz%u )lWI׌FnDIEh uRl(QMF:iЛ~/,X =+eBp-OF8&p% {ri QPd CVXD.k nRk 'h^{DzɝbM; Qӧ9HVEPn EE4 Dܨ=:A1=l>p  4 d5;tqrhsH^rtB|xµAJzB v4*%AG] LxdsjPB@< N2a"€P|X*ǹD&T^bJuBT9MHGXTy}d}~0K(矜!L!zdz>048J8ؿ"6549#ό)Qް.¿6`8 nfpq'f~4 "3٬Nu,=?\WXhć~ m0apK"t}gt?8A qwa.< s^ +-K&AY4~0(#LL q44SDT0[w3V.Uٸ/&I县Xxɬb :j^nJs:0h1+7oloŬկK1/ʪk%LX63շ't/0Soo^F%2I5/O LKrʰoRT5±ۉyhG_\}aRW?FY׉|@] 鰎a^$'vh{jgfJvJ0Hu,V<H+ a"X4hjU05zP 5]hl oZd i;v Q6pݤW̏p!?3hk$[D F)H66Auq/mxpJLdH7&!ZyŒ{~`(=5U}7$Zˈ8V>M "QRcLDndep{a:|xm}p:*>Z־nFG9",b$2M9N>0_NCJ?X.9686yC)jd2g8U4~D=<񝩼l(ǵEWNh9}dw FQfX>7fxM9򾲪q\.E03zsA~ucD7yAlA$_Dr \MS =?YO4}GTp})Nspl|2 0R\ֹBpϯ9/ypk x['8 E|LrL|bcP[Ov/džÏys4[]AjV1ІZ=EHSu 'h W UEh+ UmXpթ_ 8okohY҃~mbE7PC^q#Irr5m ;[ZV&:Z.̠tND J0*eHQN G$/ˌG~ݥZMm\ίAC|_H"P5&H U[@ma8etח8qJ͋ӓ23.#R}'vXY}`)J']C`@KA2CZ|qHӋC.zm!WNU@MAkH%l"0Æ^a[?@@bgY_ *SCZd4)'x5N}3q&9n`^"ᣇĎ?y>dd]Z;/³u^p&.߬!0!q7f IP>f+^,*TfMS ZI޿9y0?Y0xRiO9'VeM[&dֲ&}eg,Yf^q!8ܸ–1'ƀ3j߬l?8ĸ8&ٚYi77?9+M\OftÌ12.85b*3yS9ݯc|{2}]9]8 C&a%<7L0~Sp. B} f"xm uIiYP4wqOxBbLf-4 İsbVQ r}015fߌH wi婣7ė)WN.b+aRb塞Lpq=g8_7x{θt9dqMpzZ8#A9Wbq0nwACNaK8zū:0=c7[f `kX)J!qLX#斘񎋛ۇ 9|dόLscOذ9U@Í٧iHo3utƎnW'9^.9ǚgWX^X0V`Czr}bbq:o:#>y.|yp|csa{t>qlx?8 *q>.Gu=*xW^Z[m.qoGaOz1Azyr1 z74gXpɛr7}<OaJ%ra =lihx$~A.'`o uC&mL¹Oxl.+!3Ծum`-n7$#*DBgXJ[۶v7,#!:,\c=YX6LRo}{M:Fxɳg ^Oyw8jS"uMoE0C97w0ciX;\k[ipBjsp5țȸ@ޜo!]g7񁆞 fbq\[W"ŘZON1awfɍ¼$ɹ?X'?X,q7pιsu0)3G8s}ً:u|LhĆyɉy5 W5O:C4 %1ѭ=c 6Lg`I{[`}15O/VCF83šې+.MwoZ٬ N69p2t ?F fYǜ{:٩!7?YG&a`^o(o *u9_+ˑ4Szp!\bYgY }\dxq<4-ð?yZ:$WW|ƁMMK)95}>/YSkxu{ƚ/!8g|Y<# x`h:e3ov'rh4=03xnSvK^&Mixq` ̚5>2@xf7&Cq^pZuM>S]BPQ8{ eqLF;H<+rp#c K> bC'JE6X_8 +6Y2?0A& q+KWA.HI`=`kNj{p9o?Ʈ|f0d͙74\n 7usMc k.v 6xӈq fE嵂Lgb#oFvY Ug#h^0M;Y3 6`bwk &]̇+T"&n]`p8 >G%nw0Q4.3/&s+8O&O!Ykn5|g{xNyOL9 1]`TkNxQ/wMlSePl`k;lLәo(;9fa$\`!9 DכmGlW`]>2>NJmvb=q9oxwPxW}<1c!xtnR{\vpC-^*q1mva \/xSn>Ip!x:ʢ8Gp.hP7x>3>?<8yf`lE=0+.;08(޲77x> fγKs^n?8{ΰ.r>;oYeuN\roZ-3IC=d(A%ϼ(5|E5pL0ͦ˼oXn1+<8D>r/4gea#3Cu5qUixύ+>sPLY͋&3SSh1,|aۼ hH-À{)dqykb8 hp\slW `nwKXhRΰ6D$xa^ />i^׬'DdWdMk.>ee`\ 2]3@d8p:ȹ'Yx&:p p7$gOXsƱD kpP∓ F8|7`Yy Ü6m5;dXq.0W"W}fyWK}]~X"K1+<)!96fxZe;ԯb3>']fSL(MwH9+!nKÂt8'fYQ9!PͿxRny F=3LYz^'f3b ֲ%Mf5 p&T"CUpcqv?XC>0n"&ӼzÖg|`k"qق7!фoYɯx'8Yn'J LtLxyu\'g+]dx.x߼HLokӈMx'.6dWo\cA5 i06-Z)Xz˖)_G0#=hepGB|w=9Rg~/y2c =fR`H[*GsLMS=@b Ƃ 8_S(„U6d O!dƅ㜔5=Ɋ+28]Cw1Ü$|d\'z4+rA+M~p6xshW>SP 5`6q;gͬU4#Ɂs9xȼq/)X`ڼ9L|.$pnM}@>3q[L]NHy!`CH L$+ό [Mȕ _Y!ٚ.i`༸NGzg<KY,"[N3nGqp8`E]. W >p&ib.o|%M9 ~3N.+7SSW"K7$rG`xp뉕 Yfo&"2LȴQf8Gl*r8fsƑN+pm;N/!չ)rUG;p]B-ln -qd{qƳi@_Y䘙{f7>%aWf\?QT&~0}7x@IdȮ)4r >r|-q?W 5#~1ѻ0ː&#q13Y~XɆ3󏫒 }k'y]&?NSr9 Wq~r<exόp&I r<m\GY3Ly 0j2/O.G%~sdS3v`q5y?qa;1Μt zG>4F%uP6f_#W%KO.in8`xb‘ q522^0U;0#ade-52ˈucɌ Qu#pCNi0?ŝ|aLuR\!єco7pDWF:?8~'X|ZbAn:\r/oÝgq c3> -u\V&,hNCOyR4֦C\̧NWn ư#I:zs.EN5Z.NIlq5ްa3ysxo\`Y1JF=lβ g?ɭ>ÌBcy5<0<{8)Ү'7<<>u{9 ļk-6\Xpǜxap:0v;ºZ5!X/Ϸ64rcO |G:>|R%7 #z0oV{{n6C7-6G ^M`97|3Y0ό zBO7$fi_OoN7.k6d񻎿;ϛ(u^1<`h=x&C),gs̓Y:13 0ca)6Mb1~hX6BaSfUǎ#\&mnF强g?"̘kq+,\=dqwX3#pz5q~-~e=0&(߼Ecdi~|fr3eo6#͝c0fC%߼jS#YnL{gbN[$AVc ,8:Ⱦr^ i>Cb'1x|br@['/D8 ¥~q{iy&o8&pWX/xw0Aph͚ڸ߆lio͹`C`xd0\>6Np1ywr+͢mu5pUsDd%F'9w=y^OYxy'Y0Ma\,BpWyuMhy7IsXI}r\r73f] )⇼#'7njk0I/N q5+R(:ǫל%ۉ= <"bvᬀ{y}G7qo75~s y'0@&#Iq'NqOpoFa㼓_rNx1V5uuYVF\1ϑ2{0XiN0<كq p~P5ۄM-Ȟw8vci4X\¹L4:*vy:8ۼE _ Va!w]VhX^;02oa/&>lJA^0ivI,/8 \ C!C;|dS IM=i:_DxM͛ΰϼ 7`GxsxeԙFk>L p۲d\ְ: 0 sOW,NĹST)SÌ3nQ2P93gj̧LZ'Q~g|biz0Nry&fslٔ6fŸϜx0s u!hMi/p(scqa%x/GB>ryo3; xm#&DϬxkT )39k_d A5N3OdsN5FDzğ w=`'qyy~p1 8L;ÌIS o9r#;yO7AXNzGk0?ƳXBXz_X` uNv`p';Y?\co$^sN\ q T\S`Ml?8 tcnqq<uL@>3s\i~1YbwS.7/[2X -=.7сhzZr/8f^ekMu FHv\`7r^2\c [ٽaɚ\{g~q&}88o dʃ( GN (0w18a54bt Yp=bznr>0pddr&Nr83wɀG/>LR 3YDC ^ *l2G/чېf㌅Ӆg9Xq&?8;qXvߌ2dI8?9OSsW78վk6ݏYDyC2f+Xoi2 x of0r%Y2z/5~q0߬O.q<{X-`ngX'9LO_oN|9@TL\gXǬ OzygGXOX1/;Xەf`NC|̦cx305&29, d:O۝$1_Nxrs[ x3eHo17L ;ߌrkN=}d_Y^r ]$.5s@"-hLoMCAI.@*ߜK@xϬ" < YɚrbFB{]aq&Vˀ4VdW)f89q3*nEv Xʞ_;\Dt}y(l_wGJ'kǜMaɩ@loO1x bCrYb|`z6{y7CzLg.r2fdr0?0ɬNq&F`<9w#qw6|U:|~p@@ 2>x\d;+2s3~;⪵WN&off#0ZzNqXY pÏzɞ35 YƓ!8`}`7uĸqyrdn_b`0_zpun~ǬkO]L.|`50]Df|F?#؁ː8mƟx`wp`n'SYhbVaxll X}p\acwL W2oSS".8v`?X@.9p/Ʀa :7W71Gx?x<`dks7fL?Y'xnFss?!M`8badn!?8L&a{/OYӔM1" >OR_F VÑ8[v]["dsR{NɱZw2>0=`z8rnWfOw22n<oG'1;Y."dr p%{;Ǔ+_|pɅ r`_?xRD:\%y2C9-"xuA6;B\?/XjZ `Go0;Kg|=`7n]cpk#x&53 LnLL&~r. >_C:qB$㶰Ym~1 9#ƏIe 8 f|dk0NG\Im0sHsXs?wb`ypPӌ'Ӈy7½|gY>o3w\mSVnP ~_v.48~p#hb956x.W{.6s;?]/X[bd'ϧo%'zďJ?YlH $].ˎbp3^2dqis8pwLĚkpY0#5d ys?9;:r^"\ nCG{L?Ï?'#J:Ìq _c|˃:bkYu8<w`lNp1qfm(n:8p}b%ڸ86a.XEn& lp(69Zfny=.iל EZ;3r`M!B&ۉ[f@7&w9ʘmk[X|=. #wq6dL)< pz_ߟquX65nC s8;pbLnox!Ɏeb'8Ǐ >.s׼6[rMO|uÆKM36cZMsxɸ=[×#q!78yYlrL gn^EPnac&>!Iwr>qֲ,ioso :wrfkSܘ^l{a83w,{8d²BSYO2hɌ3w}gx'{t\\37'=`{M 3c١a^0p 6dz5f28kY{]`G8=\Xv9`7WsʎZ]aY2`)nF|'G.G rnr'.BiG(c'YWdLG w7`nGp!yzx6` S%% fC.*U04g1=p royMp ;k­6TWwy5Ƨy]I\ v}aC'LÜϜ +8 ɼ'\rf6挸o>G75n8>0&784bL7MadޮG9 ?/&LfW6d:0[:\ 2??x,`kXpӛu__! C.{q4p xÞ9ڻ[^03C/83lL/y? 89\XsKz_7sfdpIwę)ɏYs:ɽ`/ĿgxuX[w'<#kOs0Ș7lۓ=o%"bqId\ZG򷆴8Y׼>3J0/}b.#)SqݽaLɝd?L?˼g|ya3YaLr>0=r<`=܁8%xɓ7b7 qyqI'nC!wٟs&}r^O8h.q6s!sk7s6N2aϯTfW"97kX h7yGY~0\6%3N&G&L8w:;nя9rY05s?8ka{~0Uxz 5;զAb/9 FvŹ:ɑ܏/njd9Gɚr.= `;>iwT>ZÍx<ېuCXy'y8̞qNqّ&yǞrǜK`^roOagk :o\x0Uo  !y͹<1ɧcPz`x8W c4\9Գ'fLxɓX?3';8X"uM8{ǜiunV:uWɆp9nC0K ׎?Xsspt/9!qw|<. SX 6ϼz6_FyS9hT xS/ÄӅܸru9yw{k$5 &͎Hܕbq`aAz9(θn yx)<D/. mo?Yb|+'=Ta-C0Vͥ ؄0, "߼Ӂ0o뉁R +9VPwr5e ܳ>Iy L 7^sLf`|[Xcfo. .?9 m=., h rp !,% u> r 4C5*aNrVl=^u+Νg9ֿw?|c?ư5FR 4[1m͒O'R[5NgggmhKSPg#W]ʒsWaiy˞hk.y_9Wdo nJ).7Z&ix\~psR{.T>Lۃ>ѻ(ie`]|by:͠*cV<|C* f &Ga g>w\p%vn))) 6~xXV=hV$Hy=#2y q^ f缹Euk]gh)vÀ.l ;o U/Y8jɍ9#S!Xgf ᴹ9r/g@ g2_Q7ASr׃!qMmNҡP45çI:FddT*Jzu0 @y*SAv_,'3~ ό?9c!(js6^] Gf`/X+y͘3^?ʣ i}LY0M9s7b 7xJ!Zk:Z)xbfEXZ ۷x+*o ΰxo#9GgT9rcZ 9g_1ۥ2m|Yό9w=L k/֦ ?h7=O9[k<"fO -GB@B lKjDFa Mf0E4h0 )}=h4(Rci3gy&C?߷(O-Pל@m[5a_'0] (`3EwpzϜƼx i3YC&sn1m9<ٳ1?IV",{a}}p $JNu@n Nśq@k [汐xoub`(#l8I!LdY13y'*x#4lg0/-wsA;3F4`K}aA'b{ɼMqo\3CxhiA&puJ0D.)׌0<qyP{É\d`".%\9p5deXXeEPZP81eo#\̯_F-1(iF%#} ,ofhApD{gM X&-6@MB#x "w6X7'V^ jތd"wyև<ʱw=ID1YJ`%)Ĵ-`Wvs/xx LGp=927ypsL a˭23ga',a3L-{1G\2Wc ChLwpsNr,xD0T4)wڍbBum =8 fv/90Pof[mM$L:J٧oui x[}ΰڬ')qjEإSA`G`ws2kfb1 yCSN}9䍝`cX C5K 7(-a-$8aS(~"Wu g{0#b@&9?dp POK!H5ߌggx yf8!5UiӐ("7kEK?X)-Хnokq9Ӟ㝘W~0X2@9:p@$ToC= 3n@1cFk=z~1xPI_r'|pbiVʒ$ q5F< 5<)&Uxգ&xuXfvhxD8 q7ε, d;9=]فߌ@ hg!;18q\alxWa)?X`wCJ]⼧ÊaN2]PKCOJ yE4x$ y8,ȸA;:/MYf [Z>?&|Su82N[yTy=D9tf%!F篼`lO9xgJٸnA:uԶ!AZ!ibXXI®j ki ԛ3##yV`EoDA·Nዴ"S^Uv<`7DWeDF wqe׎2/11ƵX|85X)3U?X8.xʼtV4(臼%-BOѶ9Ol˾D{آ˜~|NX_>1V~p!\{0XPt7~߶0<|ǩ{(AlxlZvھp9_xy44]wW 1eyLB|F+֝yq:z{45l O&/Fiy|&o D̮Bh y~YHqZ}bk}?]dXhK{{95q.:?\N\>x2.m~1]d=8NK!$[y1;C 7 547/&X@'L84CME%>Fo#T;sN^(p5 1/dm8YmA' gc[y$0҈|dhUk*J"1g_8"i4޴㵣E-.(kW00Ms P/LYY;0#c&Kq[L=<Ը+N~099dLj^y(ѵYoSbu0o s |3E-yKcm|/8w([_Rپn=emu8$X xO 95ο6wu0"}:eSk y`X)o἖؏#|-,~s@ź 'p(ᘃcO%]T@*CS +D  ۔p fOdePGj0,Muù_h7ƌP:U˦<0MYi{`,)BPErZ'H@\s`*ZT6 ߬y2=<&s:lݣFB\Jۓ!NpțLXˬMqwX]z8ƒEp7!!p5p_cg g`{]uqPD J&LjJjCw >2<쏜Db0;NnPRDB=g dEyM]0^4 AX7=Ba oڎ(uWDŠ路f(bO9hzi5 &lj w*x/{O "]/c {G~.r9vbm ٌwx9e(5L$rc `M'OʹOh{72kd_s>L(5Ǵ&()I~ҭMM ְMnnvp:OY_nݴch ʆ5v>q9~?YΩ{'pSff0f+w c_*j2x$텘$^MocHLow1ik ؿeI,٬tywTq KzG{ZGݿVGnt^- u`pmťMDѠ 4_ !6aSR1)(bÃ&XIL4ôTr$ -٪Ek6^zHtܼ4/xQ[WQm',<-L!*D#ic}Aی3΅ΰ& /xzu˂a]w4`XR8aJ۔J:iኯ7 #d=1WjxHb<`=9/X7h5A) / j C_3[Ao@/diOx +ညmUFi"n9UI)4q ij^GJQ}`pm.7vm n NpIlT<vw E!m5=7W-x[]61nq]dlX:6!4z3&2{w7εp8!p& #+#Xsf88f9qq@fuߌQ^sdtU/֮jyqg|c tuCF]Oxuγs\y zĒX4oXsJ;NT#oTFтbC+̏[ѥ !|`7ECܳP|?xO>1QP͍Nw58sW|ɚ54ag_LfِJY ÚUz $unH,h;UHًWPQNP]~pl|x4ߨVXp)UւǬ9:v$^}d5@ڄf.) w"tdiDw :<T0#KA /'8-EЌ *HBƊ2]bE0[6I1GLMbVE$ N&vXG#w d^_9=9N/?y3 A?8T۳?,Y 蹝mX׆nH'T5 '\ mwEpH>B:!|&pÂ)`ٶRkpC܀Euӥic>WKI(`4x7Ɓ U<=&Ɔ&Ū$ʅMx!mhu?^<_bj|tV~qY .?|oJێO9&q,τ %52`՞2f i&jo|L[usmަU#s"N!SYP 5'/;z^aX(w sg,kyxcͻC-zH/pe3KyЧpŹ|b~02>iLF0u;=:z9^6Lb rc,ˬ[cs@~3aJwy 5K!}`|;Ćb5`kf%781]91U}k!3bޭX'(yr8o>2b }QkO[m+Z|-DT3(g]fO+ҍOp| F;-ل8'B&5]x\K#ad ĥ}{" "SlCaxDqEֈw+qEǢdoO .pygYHi4x۫i9kT;1fr#`izَ%}dsX˿kn'^&e,'x>\Z4W .yrWK>1ZFx5|Nqj'X5z\6t]8W]r:k}a*s>CQFA ;O/8  /ulޱ]MJ88t> {W*7co.~\Z^aeIv:pNe/NMɄ`Я4l[yK~0KZA4Wxoˏsdw A g41wW#^#wu' bDHwγgZXz\@=(Uvzx8zf0ݡ@~y=g7F; ̎h {Ԫ;1d qHsJ A#wnQGzB)B "Jw+o?_9o+;øsssKK ~@qtaǼ"Rk7dݝ@N.E$;:u#c-qƦ,1vu^ބi~Q`2| vwZi)I jQ݂u%k\jrYG|íIkw.Q&ndzÊU!7У1iJ֚ZP Tlyv`' M-4ʚ(JSWˁ8bM82U,r~c`qzhvDv}umyM7 @3p4nntÙWpEcu<ޚ:rH=/&1tJod{s rHQQ5Bۄ▹8DF]_tDaRq1r/-p5ZTToY3O {Xk RJ &.z24s5oa.' Fix=e `C-UcSv~บO[M1)%GfR,j-5G¬գ$l㼨.o>  6oRYQ#A8N/+SWt2%]y*:PqDi8h|aˍHx 'Kf H (ğN!qB7TwDU0Ms/v;WB5#""P<.m|P?WJj5TBBPL+fE scջ %=#{ʆTԺM8oGvmZmAi~pO`8+E*tkwWpt]lxTZu5;" ^UV:N5h"R /8eDZ9.0.nm^2an$*;J--`P@k'kU-t#/GW\b. 7hS*9z. /G{0^rx M_k88F#ކq dxu:ɞNٯ\a(མJ&}a>S}a?XmZY>!V.@5|\JroʻYfT t`%aTa/pqVǞsDUNx_ST1*\{793ƻqF"@PYnt޾ aHS|We82ikD 5)jA u` |oʾmG 3KLoqwiӛ;-P ?7"[ai}7!E WsC>bZ 9nw@$cp*c.`L^Wуۑia !/FX\"MVkHMs"x*8Z5Jׇb*z+K${8%]vپwo/~h:ъ[\be Vb!@)˨m5D >0 !r 5`m  G4h@DR&_lΒlpsw]]94)yz˃֍\w<ޚc(Mۣ9je+Ї8C}GII(:RZm"phj:tIRq!HtRo~`{'zv?<|o*v;$% ýxZZrCФ4vfv#e:Gxs[u'@.8\[8+AWS&iPbyNrӸ SOv3B C:S/!yzn"Yl:on~S"7ajVpAP2Shk {` v|'&~#oUQ8 f%, \IPjDhi"6ssarRQO8.#0*.:Y?#'X zNnI3W2{pwq & "eE q LUae&g6L$r?Yoo:˜W63`oHxѓm0y\fj9aM;`I:&/S'zDE X6oSԡ>gk'MNüLHۄ-eq!8vdp`o1mÏD]~r}Ntz]xx~ubIsvAuwS2g0VXL-p ñ`)/cN0FsNPLjO1w Ή+B 8/4j!ǠFxmĕy G~zWua^ҭ u6 ҍ6z(T; t ]4B*LS@UΨ8>mU7f/ 8 šNİvQa!R2T/^B송S:yx~288bu(jGm,4WE /7Ōت4҇7Q#g!oj4\mb~C R@&ijdJwSS_O `|yvO^\vbayt| J[a즔5ÓFdi5.47K bzwaͭyko"ʼno B܉Oh`Ma)Fddv.87*D\a ﹄U;4n1ըAyO&l60UlqqNچ nخo0!y)(G!. a1^[:V5x5S/7^H+WRk|(JnW٢ksWG6(E7眫j4k!IwD39#]F[TİعB^KZwFcHxcY79r~s[19vVm͊Uyp?hbylM` _Da9M59g(7 α"LO/zއ"LߜYK.wsdw`Z 6Fư.M= wѯ?}`f w;1qd7^\ٰvqbqBe}9Q<O2`)Y^pe^^pxߎ}c&9 \:XdGKi`C|-Cgkfp)( WvL wX{do/`Ewn(H:]y@j@ݯ'SxRlqDVXEz aЀmy*"1^IIWZ}`T&hj]sSe]"9φkϪg~cÀϐdSpL=6 Oyбȁψ@2fBK(yhTZ%6` 8(8 7x;n2r]+̬V.;,N q!_gAY&:y.dwvvēa0 좔71.@Нpg&dμ2 ާ/W e#IݴMa`Uy]7u[rqz{ ` Fk@``EC>9O`do07߿\H7=sYۑfn)T=MR4A(#r#WBǏpT}d}{R4&b* OXxɁwΰ )ɈKs| փˀ:2g9v3s  ;oM#7ß'nopӞɬ(iG.ed|}Xdrk4?2/2,^|sDZ*o過oBNXJX]1l#1#=^8(+9sv:e yIc@ W!-M`  -43H~MpL[LᒊfPx9h?wqSo,6#M'`A2Ù;'Yrɯ37:ΰ$2? :>V.|C﫝 9"hp@*V^J4 |w6"X7k亥AHg]T.Hӫj4j%xbx L dɗjXK6:zqG$<*mXsZ\4q~p8ĄoDR 7/rM M߼"7tb|cB`wWv[:ANv gj.Xyj i`4j{7G44S*m"@xž?=.E"֩#{kZ5E%.chڒ{qf2w /w` 낛U\FƪO1-rGm`5OW0E嚸_)GO9x !fV;b.i;͘S[ 4G1Zbg:B tțT̜.w@9n]c ׳wXs~U 6F0J AW~qpLJmؤ}05eEvu xp}m s3co<ZPfcG{x}^-4sබ'(@u5mI*02$?p4'7! ]ۄ>5C:# }zsoTbw\̸i [kfPjxo"=H~2K2UoMbE~Np޼Sv!Ǚkfӽ:ve]X7SoyFLH&>@bDtrنM]B<و[C/o#o Oc8W3b|eT~yM~PT &Iiri8(>$l8^{SXc,P {Jqb gG3g+ WuD1\h5 sHz"ˀy^wTPTm*kXP ٶ :qHix/ FVx'(X@[b,e*pM,YŽZ>6=K5588hj F63(+^jGji6r_N*TmnןE'<9^H&hGޫiE'|cTH9AӉd_g%wow k#΋=⩢@(Ҏw"k{VN$3ȆuF?g)ZTq$:C}y:V0z.H(d=OayuJ?byx(-0(2RA~wR ȕոp ֹ0N{F7\'rE(sNC@Ӂ1zXWΛksq1D/fTF۴nĝ,J/7 2 ijzEqٳ]&azX;1ȧx32gNr4o+uKw0?'끹%9rUc=`+1` >2 șo ZaA3W>qnzq[L(? RkA'#:XiZQRxS4Hy~2K k isְWO8xI{aֱi/4kC8Mra 7dE5)ɁD_xly۾0 2+قx2Xx]L8w KՀxY0vk7{¶MJtL~rYÂe0()/F̫n hUS5%J^Mᱮ^=J$.ZƔ.P.#h׎.{9\Iɉ{8w\e0;u)4_s@S hxvBju:8 Ǫk hz[tvzʠu7q9 pwC}dyJIVSnj]0DGw{_4&"uɔok9ofbSZL!]5ޱ 措r6<Ep]u&isAn_]8#Ð㿌gI445W>O?&kל(hi=s_|Tj ?4z\- D$zQ9&?`,:~a)U8?SE qmP &u8A/eaIGWVi֪'rord؁Nz&uji+h'(8.c\TKhat ?8KzJTJl3r4:?*"j9i;vxG X2^0Pvp7dPS`?L XY6. Ͱ(*pf6}Hm܈TK`^vEރY|*?8* Mٮ8D=wz3 :s>h%clB| I.qF{aj;9@@ )(P;m:2+THGeR#|PF0ɦ hjp9ڦ;+PߓQŒxMLӸ|IsS! F Yf1cq3\ϼN) ɓ|3]cƖv.fy~7p> qLiϼ+ӄ pۦ)p|#dk |I㻊 :K t(j[2#N( p+7ͱ]bDT]/ق[&޴r/(?X{z4Zykp>L ~6k׭GDz&@͏JB,ֽa-QA6([p ʔ.ԨcDVNd8hʩˆ:Nb[N^qh,6QiaT}6߼\g+')VeEBqv,l>f$pm[ҽ!`v+>]Y4T^Z$XF|v};D ER4 )|;s!wp0Hqi~G?X:Ę77\woh-@Ķqb}\ ݊2Y]IaX0MtYx?a(/6x_zfbX"}bwﯜӠ\+(Exe8o#L^k96s٬nιQG7Wi1u+b`w5ŧ0ao{,c=a-(&_qwbZ/ %we߬SRች ]q06%>2Y}L]oD E#hWyw參e:_sՀwl(-J_bqBH}>`‘^scFX<=eZegK@@ltVזΖHB!p;ž8]0HZp&ST7wyZi ''0iIJQo\Yv!SRՈri'5Jh:q2#P;P܇wvhV HMƸd9"`aq8IpQl!t9f3l 3ic1uCc) 9ŸMy<@*..CoN޼p~1x!׆';.|5 vLsml7Vl'3\p6"k*oƺA"|NA<)^qk*hCS{0ėEd6B?XDT(*^iқrS $9Y _SxdZ5g8^"!G7FSxi6g仐DU({Z1- Hx%e@&ݒF]gPMgU o(yz]K|e^Kw&션aXh OnO3kNTMus/ ;5D@: lDRv/%$BVv7ʳ5 )<̩Pel)T>MV x;~#]8㩋x2߬`6}ar |jgTް&E]s߼5D_o[)q܀ˆ`qz缜ZS$nӼkCis(8a$t~Lb]FEyu3m p96.POY(L~<>F'&LJ"݀|õmX" m`:';$I+1F, T=4z1+0y >ÙC,jhy(X9LRM5*9j fƔ`ގsSv]qkNѴ7|rl{䞻)u"``ΐ Yf $5·rx},R?:5R%"&cX\zF$ÇRk[]Ʌxa#p.=[2?>t3R3|m!] /mjmaRt)~w^1 PYnsz T6$zC;$  ͝+ηd#8 {NIfs8)(rT=L/K;[x5PF]Tɑk4*Wy~(~iJ[$ξ1皙|/$+S'URִchUmURzz648qؤ"2pԻp9c$_pxċA !n0 ! hƅ^GwMƏ CN4 w 00kqqx&L "@lekr`jzےnl\ EPCS?x!@1=0"/c?%mX :29{L4H[&4xTۋ:[rppV 9 rO(Y9'W&bsE0ŻһśXcMqȋOY+鬚,p9{k@V`nqrvr/\6=<0/) @r^!שׂE!Ð"Ua>3׋9|g34r S7q^fr;8r穿8gXǖN[7XTg Q 1 w猏[{/C_& ;Q>2? Q+sTl('7x 7 +kflfu{>. Vp+ל nJhcfz'NH\/)hM8w6*s`^LWz&簍vߋ3 @7큷i$,pҶ:WeJl3h_fXbjgC5+TAED T@D*(Ӕ;sz9HրfTIv^r(<R˿\uV/O r#5ӟ{J"uZ~CYz@-OG< ]+e&n' i{sQQ΋xdX!hcW ܳ܄er|EK֏}+pr->1AV 0 "`etb‹< &T\C6비բ>Dxp>dZTQWICZUrjIl'+j#I" iew/h]Q@PC\|,vc7x${H1z7Jb'˸P]\lik.\R@ Kɱe`~;,Ô-.*0ucaӄ',}e5yʈ)$A\>ifo[o4x0b(IdE5@yk*{P[FFa xOa?FSE:s_{dM%[{M_ܳ^p?sXey^:uF naF0z20'LAC{ k? V8Smp:h.n;:= *>5($sp~. njPـe+3s" ŶbPwe9ln(؋61q?ξ0w5[BB7S@PY@)Mo0}0"c4sэ_T1-p&xAl4E# 5E~:1N(6 ~ywH0NAÇцQ+~C{JSdZøJQ(D,tx,%CDJ%7 "ޕQ*e°T8xRњ74hBKJ1#U؞hvv$NY |Zh`b)x*08>=ARy ~q 9HPEi٭!Ia`'G褠& y.Dz@H0ZDŽxf!0G)M #XʸpVA 㾝]q`.#]\ seu(x=\H9~dBZ<@Gѽ'|BֽSc{rƶǭx"oxZrӬ &LHφ*_G\Өu- Be{+kZ;DL6(k^Ls&,R8:ʩ6#/IQ<"1#3OyBx1S:%|! 5mę(G AG"S4wr )$pqwcDVNrm|uԙC,.0L X`;']ѧ)ɿ!5 }g0Z0#?S.Xor83dMX;O}[w͇_-㥉|h ^G j/qMm%i󕭴cf0  9<=ԴY6(2N/0xЭs|`4Ac̉[7SlgGY5qkj#J+ȎVpywL/Ynq缾yLtM c>FX\O_14?x׬ ٚ9.y=$ Gm0y(p t|n ōk6<<8Y'Xef[oo Kbv͜2 fɁz̫7ytN)gp>1x C$1|¦]#v |L q޼卖z3 A C?fރAX+' Py!x-WhwowhJ"!pQArDRÚvXl 1ap(Z ?c\{1^ Cyp녁nB&À=*A@"%`(Q%VJ(&`.GRh _a_,9L7(i"`4I[!X74=)Ёn3Vy4h&HĬ,#U4Z5o4"'*"sl<`Hk\~I,LIG+zpS=2ㆰc9Y1Sӕ{.] Uʒ@=c]@[* /1*~!P9~p C nqpDLP:AEw< _x2W'0]%މT}k 3xvE^&A,$|m19 "Z;GG5A**RI͑#BK lVM\M.UZt" !0\"%SC|̀"AU908KxrU·D#wF닀аbh kY1b|0h)fr*]&0853T8_ w{d4с*,(0@+6"DI Yn w93U۬.,1oAl !NL~~%޲&|b=bT\ـ05c?Yf|pW?0(hI`6f!>2F-y'Ӟ!Far9Ga]o'#l“)|189s?g;#/ Xd?IK= tiʺ_3Y6:Q)kε~/իk<1k!;fAtNܕ]7aG pB<'AG!T07)';Oq 9ް @j z d0"0#uOU)lEQ4Z׊M&x'Rb ܋mMx$h:2 Y!QR0 Ko(@-]FR=]cU" eINJ]XoHPIۡ+hdEI';׀*KOEKF<$؅kɪ^*I]5\h0|0yŎgMP4) e8d, ji`Ma %i ȢkS GOe--][]JM57\uu 4U= _fZ>;Wrcv?ن}!/c ;4c#tAqtUh{bFcx{(q}W9 -A @yw*"]"w^fBиkBEd "b6 vaCCBHhxM~5-M*Vjo_ tCˇO<\b1#e/bj\NEcρF7HX1ͪB6vxmw4V$/#T @@ GƑ9Al`fItY8t4 h)u3Vc^E<89?R|1tx9Ggưe`cn"''xnII`J`+p. rai>ޫ7}dUfVd6 93E^&@}ay0>XI Fuй"2T<ݟYwwm6A:q+bm :zp+>21Vzb!Cw]y1&9^b 4o,V΁LPo݂r<*%0$u!ۄ"F;~1\X qYr1Fό~f vGiMfOx=8ٚbg `@{rn68Iihױʒ)FM􎔍cJ<%i[.3?qr_8w TAp4G m- FWIþ3N~a`ZIx6:aauX6JЍMQ8@`&@͠!5"6jz^.Hr9 ZdV_U|aiX2 Sgq1>ZAx[ hz)/.|xM7Dm#5:+X@Ӱj\ӇѩFWp:'Ah(/ma BY@dAQ s@ u#Ƃ73s5M3yk %"\km¹"5pOa8xʚ㻌&ic9x"2M{ɮ-WGq+_xp}GYirXz![2% 78i&\PW~pnL28T({uq~YN"uH_8+#10"xxI>seN<|e{t{=1_naL uXQ˜=dq YK{sge‹^//ۻ urLx&~l3w/X.8(< Ʊ]86=4xu΀Ms(㍫.dJoO8n]8,Xƾ/z3'V{7,(OӲc!3}U 4X(ڇ"R!i1Zbt5HŠj]j!(cZl>6!䏔`:1(lMRlWkNg N&:|G ĭL>0Ca"48,f:j|eN`l0ְs 7rx b)z`K0PD~sM[=`(3a:_y6¥nrٿX`j~LpbY%ܡNX35`usBK -l0~1 1'?yJ)SqYlsἣ! "9B$"5~0f4;(b41 T>1#`"<=`%ȏe}u| lQ vcD0Jk;i͐ĸwƤ̾4&Iυ11Ǎw?Ӌi04IS{~<ȼ65SqY\"0ZPnpە v6XMq(ࣟсlp6ie YW6ZN"C:7 I#)W[` !u1ZB(х4vלp['l| #rZP8IaCi!հ=xwI FKόW#D L܂Qn 9LSPXiT"̺&\pet G-gJ'uC`E-yTfb%XBᱡk,JBؗ G1VSۆz䦰mzriDt]7>壣wyWZxXTk%<MS@xۼg&i`b^}7c :Nep]C%Sc#gMs0fF!*1)# =`CxL#@(^#yb:4Ph {̣k8CX侯5)C|@ס$If޺.ѱ!;@<&iXk QsGj7+\IJ0J8a (Sr#Mp$&M>`[UT6=h(y(;r21cƵ-)In`X%!0 )kk]5%: N?f_L@'dOκ_"g#MPf5";1O̓nĺ_tr=[''5/nX> ߜy9 `L^_QL~). mLK㶟Š_h>Ae}nfK89G1C]˾u} :ɋ7 q\/6:ݚUOwx΁N /*NkS>vhH\۬sىi!.*x^XRMSPPrFh%bFImh{ e5PleQ`83FMb]*7@qxk5Nx$ ]ۦnpiRLr/]O5Xjp һ!:gpuoy *Qxl^XQ(Bg7hs[@]5noy'x;fGV$3* h!C"ah=0w〛Y{hBW]|`7Cm;{XbGK]&Ghk;9v%窖צCǥ¶cE)C‘),|iY ݚY|q0#PimcQ0,.c&0 r@+0SZł1l* ]! d'3 ѻ $AAt؈ z#PTqoZiE!xt~$ĄBgZaOp_Gh% 3%k f5 %Qi&MʽMf3b0)g;F9ZZZP 8] ֳ[@GӅl⍾L 0 q2&Ҁ{h˥G-eپu× Nɬ%E6qǎ:"~#Qcxx #Eb)PU>D#AJkk$5[hQtc(瘟4%H7k2cAɂ4;W8=o6aq9W`9amYőÓۊ4K&En.Ĉ]'tع<`YO{̊ol-*O#Oe}]chb#^gƌ fxǶEI\\n`!*gwC6k(dY Af(8q[&A3󃱬WXoq&'aL f)rcNMLP]⸈7b{iUbwVkA #fXōkn $F:=~NG 5T#c6/&s\M@lُ^琢{ ڶ#FUE?5&CyhmY^)PnX{69 )a`0\.hɇK@XATM\Wg8ӱٓkDkIӑZCPh #xQ0t)A  DP([ս=DQLhP EF8jPg6w$CNNn0bAygCgM6m/5 B6ܮYj˔x&A޸7~bv -Z&{ 3%HhUs@c vB!ɡTMP G(RDΆ,i ~ !Q:Z@.R9캧]ݏ|^{obd2o޲aK:ߍa9rL1N؂Kӆ=(E[,ZDv;4QBSD<8@)cנea >hfX;<Ѝ)Wvr >"\,v-hp(H?DqxWvvqf8B,ֳnvfROC͡J_C`" aȒCySHSk]ᕒvJѮ-\"0`B<wÒCєs߼Lѿk0Oߌѷɜ7sh_9@fjx5֜}44%8 m|b70[wseA '7b#aOxxg792æ`p7_۔oW!q5"kQ9\a%4aׅ#rOyOxFx"U<5o\5 :xkɬ.㙂ۜ7Oicvxo4 ӊۙe5~?n@S^p܏-׽c]sx.?#9nӬWG&wy|1<'SXAˏ-M*Š$8={˦RB4RkPHklH PR[g+vLG@qd}hkqXyl#nNMHA8$8N0riUYfסҹS9,&8rrjm!'4NQYA@QX+jO:Hy3ӻOF=dzߎ( llq w$fxK4hj{\Vchmqa`_Qxf9:KBT4r~TCQxeR·X:kHW⼄A%_j]_rwZZ) h$ʄ#Fh=455W"*G@t,?+nj4=E #ALw=.6 5uDb=Vbx^(gߌ`Z0$ @+i5O2 v$r#@jP i1!UN=]1MxZ o61e@ꜺHb,%)űqb96nѰ5Nhd1}hN_%-:jy&HI! \uo.@AZ.h,vr h!S` Y;="v0k8@-p[HDP}!2M;Pb=rMS󖚷ч3h90\EkA07S9Mx_8s)jVE\ 6s9›ƛ[023Y1;us`3 _8Y9oόH̦}I?SqFox'qT2u 5׌'8B飔M\Ok뛯X+P)D㜛yEv;~8 }o9I&kơw1kY6t:ugoX>LscOf-YOX|τv> 1t"<],{ tzi~LC%[ 8]ců-reT(˚EY\|ap:1)M+zrwzX[һ.=&rX9̓] U9}4G[a9;ĕHWs HkT=G: yk@ v'<=9'%nvlhMإ~8<#(l7׶.=Lqxkx;3ړD 4'-kLX: #TS. eάnnFĮ $m iwT '6Xq# YPN!?D^Gp½׳ vʦ^3|b!dM Y,K\ 5Ca4KtECupޅ)͓b8^r 8_7ƙ,pe*O`\n&*촩nr `@M?N9V[VkX͒/<$EOCuMyI{C =W 6ht CDKןYgܠpØ/e m pb6\*-{(ܔ leٕޫwq@Ҡv:iqЉ#QK \1.P%7U"NtQ@-BGlXT֤&a Fɽ-h RϪ.@v #{3nҴaOH4@IhGmw0^=~ PaTӈ64?,,6&^MA rbCdŸ%flO3U8X lsh?;CY2k$Կ9_Er1, H&pj}g([|Qۈ; b&1)W -LjkPf4s|ܡ֌}w++}eJNYo9N\C)ؿE4>r µN˛;q.ro 4H!K | !QS \Ldt *)䄘B~nTj"$D"аwY“K&U"$AZ EaNA81D`W%F.`g@Rc)K[&ӇkNXoB$wT %] ڲ[cNjkCҁ0hsFTr7'o5*$CMͅ޸*Ѥ9!9V}p( ". BHH#PSH<&~,-^YXE{tm;8uB&X>[E@r»sDJD7Zkv[.kEe(6!+RG&AhrZR ~ TqD+sTE:Cz@&FxHB}C!fY6& G@d  u/q!ʭv@ 5 Q(vޱ9cWa0T ih,T~f+"o B h+X,}jW^ЀhVa:nSj^X %Z %~.&lit;åhy0u/hk_*!C([X >qfQ?@֜qw 5.x(ʼnxurfݺqyE'wn%6^-u v0'yFx 0#g0i0h:jzx&&b٩b 'W߄Q 8ӬŚ7­;tڜcԇ#c?8Xe ƙuf+f1nc/^C80R-@)1$_(71mroP`MpKp6*k4{ę],xD@VbJѓ-j!ɂ^O(u& ȘgUl?\N"4?8|I g@= H׺my㼿i]GhPHDr?8lP~y֑QFvi(^&YoM1̋AjJ4kA9ۺNqyHUMT'Sb5V)ymi:}\6 ؈'RN03V4NʇCE S8Oר1HNQF; Jl3Oc&9 lg$d-$FJO 'ppF0s6\\f*zc6{g4e$k &b^+xc"5 ,kPCËG|HUxPޘIVwGO> ׮LIA=&&Z# dkF8]DuiIy8but)C|mԂUCv`EE(* 58l]N-PC^PGE"Y Dx@N/fW%Ҹ/a8()ÍwRpK'YD-W(jna<{2(ڏJ`sscaث"5hvuIRV(d;dŏs7f iDۄ0Me1P}޷dS[ 8&s& _9xBi>q>? rͧ% uRHQhQ\f9?Ys~Lh2+d`e\ Dr ؏y-r}be%=y_'xyhjaa>19c.T9frˏ؝d k!%8@ :bNcCoXP;Er9O5q<. q[QNyhB/kF=nkDSTN")2׌BHg@8rQ!YN2P):'9 T޸6X.0b HA#3 !Un1& UvM#Dh(;,C kb*hHg4LF(AMB :FӄE-/Z+`cR < ZOAPkD8kSBs ])pBg=1!,B'aPq$#Cc;8O?nyや ,Dv&Z4]bT&oo@_7i,!G\\Yi\18RkEijP3MGk QhJ&w;KSofHLK߶u$֔C8ybMT2SdK7 &)ո?UhYd^tPuybYH(J('X $6@WP!Tv`s*τd Zk%)kFAC_񱉾l T8bpҋ$oC^MZZaϣ\Aq Mc@ &Ǝ5tqp^fsA$@ߗx7N@I1IdlgCuϴ 0OX h ۾cZq1rxujYx˛1pV+lzˢĺF` 1v?]Y0;w2EF`5+usV1/1 -zõ%0Ҟ3OY>p * <{&6c7r)ړ`7*CDom Ƀ >{y;zM +al5kPe sf@)Όb>{8)PpSt\I q;ѬckS?y{xxgU>qJ8407دyWWI4cL[T~0~ vgl,p}co*'d|QN YaTu"r%lO4e՝#<͕%r9/?hq N=3 d? !Gh)ZӜ%GmR4u丿ZMVhS&z߼M5u.!$"r#Tƞp&:ylI*&Ij8T; IH'l䯭lc˜B1XQ%UhpFhϐ7rs7*ʕ$06Ѿfťlb^ ){Q3+ bu6ݭ.`2Xo"рu&Y):x٥H/ ষ6_ :T wu@28Bq5-!W},V=|`Js3Iɠab+J't달si(c Қ9y` phf }yKDf:.U`mJuMT6E 2"tKDÐppn6K:7uXP`Ee>wD6E.\GpE4*[p+e݈/0J,@E]ϗah3nЊjI|1u:yӾwgDI6tp^v6lwn@ 5_Y  ܁POyܡb*(C  j4SXC x.0P*ȁBkgQǧ'guzG-8ifȧp _A~pϑ%8q7COVvo,YV:$0Q"ט `z!!~`&O/b'х3rfX U\Cلv'b=0;6(7AZRz0 A(ۼn2}d]v`;N<9kЃL1ºفt)>pw觼MvzB.xlJ+}JNvA<c@V-z 8tNɂ.z&АiE 2 ðh7`{/ʫi b)S6"N~ѣ Jt ؤXĬYIW6v6PuF_  PmhL i5s4| E؟6)Yf9 Pp{[ ɬ"--&3:,4: ) ;R}djNR` 4:7~A A6^M ;dp'gA');υhxoHhwfZImI$:4h5k5)* ,xoPvr{ZOM=p?(ai ث'}E?b9∬N_<WbhT'{?11) [tw}k]e`Emb8P;EV6NbǶؼ1?P)]JK]-o$%@m`tz @Ʌ{'Ck󛖊40:=Yn%QHխ.!t1@g4sG2Xy-V 7ϓƍP6h\Sҍ —b%0q@֖qLv%lLu@w %1;s޽ۺuԟ2)~ y?8ȚGqzͧ*ZXx |s" &rvtό1ph{ $}Ca_d2{ZȵGnusnrz ^m ql' ~1{~L=oM6'om{sC->Mb6'lQyjЮj߼X'!fn9;ɉ=~ף7$QYg*}bhL 8M8 L|g] qp^rˀ)i9'Phف1c;5VL}J8ˇ<<ѡRiw%mL-uq-8d?kk'%`!k-6f( hQj0d<Ao&C :92hRk^2Td`2]˿9"DqZ zZV*)QexYŸDއzxɚ.{yi4J {'%Ã8yP\M]l.'䪖́FHa$̀ XP2s%p,\6{mx@0V!JJ@r#)HXCKqIDeR }D 6 ] g8M`|p,l9:IC]mZ6Ae 7]#rf,a'$u( t7DM(7Mx)a6ý ڭ^c< wݑB/>r /j 5W:t]RE K뼣]ݾ+*r ܺFko#y1+6Tov!@Ht'8Q kg#sE -s$}LZ񕷔nNQ :oNAM+No/ssx*m(6u)@p#8g0@^RL&TnM]wG>*=ܽCP FٜE؝t "fy/*r`4%NhT6H)3fMvf\b:/F2NQPFN*O׃ ( I> #( G`խ5 010QX4x2 (P7y\F \>pQSve H~LwÌȻD's*z~'+?sg1r \0wr{kf CL`K!$b!z\yIT3:51|$DXF>sygN hrlCMs ce,7p8P[exY>)oe TS-eD=gд ~{ a5& ͡t*f@IpBWD 4D4w8ͦnSq[0VAoL;fF@C@l9wAL*TSV;ʿ+P[W4[SMG|xd=+fdR$UȨ D49 B 8joJĕ 8rN :3 A"Byp|BjTܸCxt}7D 6SBnFcZk*̗-*:1ThoK`@%<wxI0HÍt!]Fi&̝Z9MD¥Aִj\ ecQ յ vg/HozBZ X,I}=Qwim;Qs #@ 1+ˬx 5Ao@q@U7Ct[ 8i$I>qŏ#@2VBqNB R=QdUgq %xU9f3gEVh,'Jp9"XMsWɆ(f,{LA+24~f7M8y7 { -lʿV nekqAyVXέN`-b EsQږx*~r{Ctxa?xn\Q>1 Eo>q &8?8rC0a͓`2q(.; !R~3_{zM`O7όZooiyIh/9';f=-x\W˱7 *mw>iہ=!㌵/rtQ8<WYI0^~3zY^d6>qHUݸ?xΞf cdgh=!/ Y?d੏y*pu`l5|&[Ҏ;U!Sؗ|8$ JCX<%$ (٧nvr`fMPжu|`M ;/&XN;6 &E׫'k1AIbRrď쎈2&%%(e)Xa|QvZ+S,K2;.dˎK17h6xCA  kJlgXQvqwq&)6!WD\_ `=%MmxfEED]T0]Q^PTyjz[Ƴ` 6sZueKA0LUK "lbEmjoR)O/ AAB0z1H8u݊u&tBH^514dd:!@ZWb2&w lēcTՋ §Lr ϑ;5HXh]ȵB%zp/:MǩVOHYhZ7w\>1K v5gS8ObEޮB<=>RE0?]1c#fHϬb*/_)!2k\zn[pVfo7^.ޱz2k,}cEt`Bq%ӛ8e@&=o0,<_Ǯ Co ;'F&1JnMshX4]q%}]:M !鯡9?? eMNIe~(汵 aNn{)=#.WM1t: `/xѰX _'ƴx2を<{fk+SbQ;[+ ?8ظ $\Qw(#h<x Q#A&9?s%SX3a߼xpG> 1ꟜwWȹ5K8<5!\, Ağ209np(97+L#>r-!üBԘg%A`!ΰܚ 2)O9'O~2mAqrϜꮦ(پ3dT] LϒAh\w,o-GqK=CyQ+7+t"[gDD6yܘ8gĝUM$\bd]eZG&%7COYu *H?cTikOcޅHh2ac5.?٦w)Ė.J>_#^T`/by#h>2'ћթx~ (ҿ7aذ7 4Ȧ8.GS! z>zn!iPЕ Ћ%n{pȖ>5x7HݨZ3@>&ϗ 9Ũx968)@~#2">?_zY#z@34T$L ,jc_V*zfuaMV(!R}bz s^'tx&z]k8*zcGNtV3f|`QNQii xDqD1<0+UnA֝޲= ~f3(Jt~wJt+k@94֔.9Ie.R89a/jrMNO|ᡋBJ< Yhjy1`q`D6$"CA#TZӉ! 逊C+(r$Q[4G88 H-HR$m D+QQTL [HbmPH#{(V7!AŦ@9Aeg9X%nLfƸ IJ|xTU5%q0+Td,cmADrpg+&yyk3A5 n0Hci^nzW/nҗݻmIpo&H< tG7?WEƽu9ħ{p"H_Y!WlGHS LRLfq/@2 ^3PfD)}0;e|M>my)|6|thÈ 9Clj8 ^G.Ͳ}ecS~Ds۫ wL M}1pѡHar[cO;d}3'bH.Q{X|?ƊD2 VnyAQшsr4֯b;o-[`uDވ~wQ??$VNi~eYnv_f|y"@024*Y-zʰ< t1mȜF '\\ `'NAϚ VV3ѿPO7mt f\jk缵[|f'\:}3j#㌧ QRxp3\eUmɏ>1qJK=,BvU{$L0!;"yRVI1܋8hBt'8QQr]}Ž' #fmoa AjH[4Y,VYi4 ~$U|:Hmp/xk84 <(lC!q&1cǬu&فtǯ8[Ldh'8%8v 1wMAzq;v̒So#}~~<nzip]y~8Bqv|b>γV~2$m&]£aP6rrN("}usO6\.(C3 Lv^.> ֝9u!,NlEJ 5~2םb, o%Auy8og1)hr0pVsϜF 蝵}gkFvGx>yI~u8SI #IINr7 l:,<5 ]k9&4" xFS_ RB!`2s"9ڐx #0 iCuτe<`]a]7'0x@F5|He.Qѷ>1UO&QyspsRkjմB{u_,/yQCWCG+8%EiĝZOH省5 q3K"?)y)VQsƍ(S 79S "V\nj i"ػUBZmz `+ZvR#{ѐ @݃NOd8uri4>Qُƈ}׌}Ȟ>8P BS6(恽NU߃7dͼA/Ymf ;fs榄gfV W_CF@<ØWxc*( 2r΀^_n\Y^ :0n`^E<1ҳxܦ F:8 y<|s"E󄆭ԑ>0<~lfBA~v +Poڠt"K ._B* Zj p1r$9'Sg.C+8v8PQox\Z NLhO8MXӿH6?8:7}|~xIT+Y0aF:ܼnا=9Y'kf ]J3sLޚCʋVWvHZ*0ңw=z\up\ږQ0f񅀤TFќ lu4ù ?qG Tk1ԛpP:ؠG7Ӆ:mrY5t[SxW,%(WѮlh>*rي};7z2Le FnlUræhYsMz:}2Xy=m27hx v Lmm#S m8N2Pz8?0({MaPai[1Z1rgvFh|~P:5r3 ϜÕ')~sm:XqوVz4`qpePx ȩ6cƸʧۑbξ鋭 vbOwn ?(ZLV t151Hk xaXe3O6 5h6/X^*W^{ _`=K3c#I7M#{8ZG@0H$cۖ"~w~/S%VzxH9w5Co$x4p1z`YwqkY?x=\C8w-`*)!@>0@0ɨ,x EB89|kk1u\pЏ ϼ%`:˂ .#m?'ƱXaN x%0ױ<s^\$ARD|Ɛ:,Б/\` AC 6E1٤Q :%a72.&j34ipC(J "lb<3T^t;4Vw*P(ӣrHnT=L`*1w)<|PB2'ǜn$]N;"I7f@3K)7mTT|S@P7/YVƳ}N&X]͡6d#xf#bqg6n\61ND&NIW9"({="E;ֱ/; gfj _;‘;2* M\6jGl9Z't}   % H}`kq/bEQz$ [YTRNXGxE?-Û |DHe@<ݜ8nWY cq@,V|όA@4VBAwf̥lOXp\W842 "h77883)?L(L()aQl󊻇!S\q$53x :q$uSă ۯ8mMuX79f!dJas5.h# P ''*~r&wSQ r#r9Yys߿0f~Lک5rx(2;;)lx`GDT}+D f{u$(98cB:N^j|.]:g|b[BIYoY#M<7`4{ؘ"]E6wI*nY D ooщ;b)|O>qrb͓ Ihh_XjZw H 65DRw(,_%shClwH 8]EqQʀ_!۽Y77j GR1.w&]q]٣+8/c@ Nɔ*9(0M<xu:1܃ݬƱsbRvlDZ@NT4z>ѽFsyټ(u[U4sI:A|'g1H$#VkX%Q8LqLCS4\ew@8$(B1͖C )6s9ul21U{H6C^X'2xpK=KR)/S 9G~1z8u!7z?8} 8_8 ?(%LZa 4@O;MpP1͞HnGr `=,RX9*"1L`=5>r> ĸ7Sͮ;s6C=&`vx3UtyO Gq/Fpȧ5h>z?x7FrC85&Ml ˌO01 7kd- *vL ;/`}8tT.^E8BS'>UV-C.:SN4bhA i5v9xDl;ٿ_Xĭw,m"ΚŕD2696c#+V^5t~:x 5#tR4`k5ÂivQ8D'76L`EI6n᫦;{DzDŽIx$Ti) c]"1&Jrt5^1#w=0j! r!q8<0eWK|ƀo>005vM7o;:v^(8@aY,]R2NyN/ִ^ ʱ'+1-HWs@ i!1-y.X [Hui!@yfGjPM/E#@J5՜,2yHTB ]U⏺7lh#\x!^V񔨅~3aHSF ɨkcz_b"$Ew-C`֪MiN AؠoxK\ub {e@ c@˳b!L-7CkN[]+`i4Amq6Q۠e .x?9 ,,Qtc.)8j+6ppGsc!Zy aq*;vL A_ү@X A@0Uz2!QDjT|"m7%4H@R_ư 7Nw7vnv`D)Gx<zHY@m4~\6:DW)P(\xt^ PL7?Jnzqx 9th"A< ݎm }|5 i}b8RwDoP ݟBI>S9%tkqM~g 52A_^@01\E4Y7-!~D'qĝRE'ч1hO m'X8G"?RLryLH=cm`mB:1|b8qYW& ư |s(o J3^:1př@@dI#RuB7z. K2I)qx XAM6kLA 4vo$ph:9A8h-, DY(1/DKck`9q*MePCM21C9sHx}!?{ ը{ BMUf8OW )sxG8EWEm#'yEJMcyؼM:ʨxg}#]nwi +šn[b^|̈́r6 :q4Wpĉ0y nC_~q|tOa, j$p+ FbvG~9šBGA1 C|ҠQrD(_~4v#X+ѭT/9@D4LN7DŽjэ2Yk 1i74DLCo5'[ <z(#x\bR.oэ. ]rȻvAӳ_4ªқl.{DMS2RblcYX*Sj59 \Rp=h#b+&ߜlE0Rw2DY˧WHs?=aB`mz| U<{X+#G. l uŹ@޶ 9$bnht;ٯ`dۆ!iwPi|4&4so޾1E#ƨ`#ф@g,'9_I~YlIFQCoXC(7y4}`kh8R b)x8K8O/"/Eq?lcrK;bNSHjL y5)X P\**G4D6PۊIQRT8fG*/"AфQ、P)fa2.D;ibdijŚKtS1QTGzA5$Kńgk YLpm <\i`ytLE$[GFώ]JFէo,6TrÀ;=*dUG2nU5 `cNLH<S !oWO^U""ޣ3BsΗ":o972Ҏ6WOF!@M؎uQ˹j= 1Lh}/{]E&B/0W 8SU4*[妪`)elIIVa 3hMإF5j˪b64pDaR U0Hlyqy+}paׂ؇hYX( , eѥSg$z[6p≨kWPZ:ËMV9*9WqFيձx|'bp , c7V#7:?b`c֞'7nYb*;ex7qP's*'[9PGYj+:,O@zq>VqcP8ksd %$Hd4m9GczȠ ?*j_P9Gv=y sD}ǐyN0+\ ϠƒM9 6es֞W!Ԙӂ4{W 8d7)xkq"7b:`E;ばi Ը^^5mHC- >"xD3ʡ&>E.&08~q"z/l.J}.[]${ȑR2Tw0 MO ﻮ{yU0O.N5(wcၣz 5⽣jzM Ô Dۣ G{Ig2hsF%5gZ^sP a]5 {7\s'ǀ&ᚅbb"wn-qp#+oR(~sFP:pK O``jX)ǚkkt)qS ir{ %gY ZEα\ ~3uPF3_ /FTW]WK yn9rRR&ZĮ0^o]D*vw;ZJ@8=R;ϮEhޛ ύRA,A<̦ z,ƯF &E8O$X{[6Ɲ9va{J"G '觷$1l2[g8.Nv Cq4Jptd4E%TٻAVedy#)ax=D<<yQ7`!d/'#d:{CQ4 j37ɣa8,t6_cGm/E6SyĚBaox a+c^?0%9&P/~3RZXXrh`l8' }5|[[ |[Mx780tf3(aZ_2;&||+3Y{xOx6x&Tb:?8ZSk >p:q.|x;[78.G=ct4/|\M`Y_]1bC,"ocPC:@Qx=u6qmqV4*Iig^_Gf(Ѐp $-%# .>%Fͩ>Sq(]q8iI'w lD &/xdfK]CF#aI|`%8ї@u"7SzT6`z XmScPÛ.T|{g*rX TA] 1-4nHQ#ZNTx\FԒ!-V? Ni+854 e1=0+=](NSbM6ve\+,T߅Uyc@B5@pPb(̈́8g6W*h/*pgq"L[ 6*uqRs6(mvepyn4Ƞ 6U\dYJrU3YA7=^#<H=*<@ڄ [%wwNf0.ߌ,61c Y*sν-&dh 6Ӱxeq?Nȡ|NTP:ԩX7]Y"4l Q& PE{k$04/e)Xlߜ l!Z`4L]iW5!tn>#vk Q;<]`gtI42;:[1ǐhYy3G[H'S&4^T^YcJ/kZ2xEx{8tkhNƿ=6DR,s$TW\b}d6 BO9!^:,pH=S"jᰠM`S0 'o wt Ùz%_4t$2j. `rodJ]98%z4. <"CT\РlG'b?g0FQ&.[xU{@!ڗpPD$ӈ^Z眹H5:3qch6ܸBp81NBzMTkXHBI?n5%i\Oq N0[7"5\enxNW\qb DYųqD y+"#ʅTv l }qU2nWE;!0. ]it[URÆT'í{mR%"w.p,B ?Ok()$cѿRota.4Z{Ǩ@]ik7\FxFć}QAVS̔A@jap- ax5bУ*Wׯ8B>\h.I?;ŒDvQAt%.CtqV1N+}3r +{#AO5E5o*u5[v:#xsEDVwԱIx.^l 6?ZH`$4 mQOx_ &m!y,(EA<t:T;P?8b}?|o qUxBnŴ$\+Ǚ*îgMq; 0OzքzK+R m|aY*hlt#!h8 UxXt oF;+\CCvxYMqmv,*2=ch\wU1[q|Y^qzѼjH>mA6nu pq1=SaO8f8кRun\\:1cM̴swH3l KˑhXFs9B$UO Sw~4G#^Y7ljw;H`҂AbS}չeK6cN2f W';'`REWR5h-@lܧpr8zG9f;4Oc='O`CaB.K{I$2l7_x=d\' (.81 .[LX.}'$1xX9(9^QN,-co M`kq4Р>9. ׼h &&sはܦ a/i]4X\ bdix 8❛G> lE1TX`}G)&vpaPد_vpWM Yx%sG:d"[ˌN|, 4Od?,wmk dj)tM GAׅ݇eV.Gl2f.㩖 ud  Mx0ox݉u# W\{o T!4rX%!E+굋DuHIU= w:x$O9lVkk;-$.wU>.\FMaEͶ7MCea J /-:e4g5S q]9yt h߬cD *(AVWo ܅ɔP#yST!AdHyWHB]A. -#[׌iT,G0oD P5hoxaQ\_QUyśګ”1;XO{`n4هqT !"3Tʫ!6<>]Nnrgp,b98o^$@H|`ŧB_Xs4닚:>޼8 nbq$x1-bR;ٖAO#f炘 jeLxS4%{,?90H759wrza]NrHdžlzqip#(W\_9;1&0l6`Fx͑K! Vb 8)9ɴy{êD] :`sK*8)y; ߞqm/XH7g_xn"?Dն q KqltI;_?7fYop3 ί_8лPoZ:!+mfP6H.1֣P(o&5D?6Zƛ.m+cyl}a,$NT߰.T aE2!Pϫ$^~p0TexN 618U.һ+P`;[ZMҐS9w*CNG3| 7.۝'Yj*v6|pkʦ\QzNK k*C_xi P WhYX-]M" Vi1nzN"^+QQiXcǓo&PXm U:x$,eh _Mj[Q kC+ UwiAYƱR"_r1KdGT9i(,7PE׼B ͷXQGu*n$mu5dt\`7,-ql@Ey.0P3 8顬ҢڱaQ*`[Qu.=z-D9$KriKjmqL@'x ޓSB}D/{ ;"xZlVnj1wgWy?K{OJ>B;ōeoDJPzy~tNOUƱsˇ/y+|axt3<]r%XiQqټ"Ai3@TuJr䗎3x!/8! A~U(黾xX;~q\B_>2m9t`FC~q]b:$Ko񐎅*fI _{x]IQgp-?RiCv䡑kPA?}\epv Wk@pm.&#K ]a; \}E/b zv[q;ԞAT9i j[4;>q};jri5 s߼C*RvT*;[`?<n\EeN'?xZ#^;2" <#>0kZiTW(vw.}bE*x)ͮYTy߉ir ;7{01O_ܬ|-\Bsg2^7,D P_tIҶQY<"䜿3 {&65wʱDԷh~ *,+kܦK2. S bL]+:}4*k?##l]}atO 4#PwX9RĚe"EE) pmVJ  OĎi a[KnW)F7 ck$ݑZH(K]p~`Rj~$H|dz-ҫ)nc- (X-|7y{hp-rk"A C?"ih [l",s(0uw[ =>p2jYnz˰BSP [EWJԂ*@?X|Tpuvv9f"A4pV/EcN9%يfX2 7$Mo1A6ٵo "Xd&d]s.TiDh$z AãVaP,#jkLg&հCu5,d jWn\ t]GCq]r3S# ιFЅ/N!H}I;AnuGzζ UkrIiUNI:M&!,ār,B A냬;ڪ,؉_ZC**؜΢3⡭Y[t@"#p;gGdx`&{.jb<B2bנ =|+!TsW wuVx\ް1'4dz,J/L Bxh c|ĤVS9 RyDIߍcnqxp!@pFC7u1: Zj8'WS])Pe場y&v:#pNmm-rU Z< jNh9` *:iba 9t'X^KD58 5SHw+Ӏng!7}d<EywJ4|E]W;g2>Q[8{,tʹsUNxwB<$:p/GXMPDkeUf5%FϷy,kpΒok44 ]i;7D/>19Lk5s~=2pgswj;Csƿ!kJlSk6*_rRMsVK~]{[Yj(n3''5NfVGE~,F-2 'R5jeg{y>a:'i}Ex2r xSxeR2oRNƼe]xqV8-I^FLK΍ /{-!θc7P)@kP՜|1&q'sXw2cU?(YXPWF4UI{Dk]P8NC,(ɬ֛~!ԦS{$򭱡<`A 1Rhd*)MG?Xi_@Gߌ%-CAU~oRQ~Z.= Gh=fywׇ/di1^_x1*$ dz%f4@_2M_9_y3L4 JaMQ|?\ehq՛LVǔs(wwʝk {q@;v Zk?yV l{!nT!_ף啍OAV׏xQݐ%[~ʽ~XyޜDŽ^n +k}u ANx_/')U:,Ox 5Fam|J8ɛ?Y#5x- GfF8/s2d?$И `[psUFy^}ap~b [ŚjJ @``>rIߜ08O#^lX]Hd  TᎎQlHt_y#8s~:i|vɀӤ3LAÄZuK/߅ !Bx¢k=t^3_IUV<&?mvy/io~ ЊVq`O?]p&f0X=xN8Myr@.; PYBfoXAMs|- ԭ!G4k,E' {1 r2-*Ƿ\B6yd(FN`ZSi-y|!@MwGωFmG-],4ۿk Jٽw*/6aVy$޸vPm+ȺR`PJcEݶ(lʴZhS{ {@X!-h-rbWe:T47l;2e/hmzP=8`&F8'cj>A^dk`ߝc%]ye &5 q^ xxN؇]^ aUGr`&8B*2)_L:Ɖ-gqݜV *@ 2Pޱg@@$S??XTf;P6c15;7/.H>{ Y+?DG58ߧOT*x_g8$ɛ yAƜ`{?8P~e:V^ì!`~ 0#(ny)~=➇YTT~\)rf0tn" SGXAX(MiJ[e , L74 ]뎲[>! KX#CJݷF8hގ~uܵ-e_C>ymaf5e?40.Q-Pt&g=KQs\}R*aYI]Mw¯A;NXY.WOqؼ9*F6s>nJۑi:uz@Aǜ!eC"*p{u#l.T%O;񏑬RI.k; to C{2?X]s4s%"r 3:#'Hk*u  co8-2q`1([j(0rE'WnH }1'2dXzˍi? *Yq&ۼxfǜ݋',^YFd;2=_Y8&Q5.3K}?9km[OWb1y(5q+}m88=8oǼSzC%4hu{ƁC9t*wV*~`vd8Vr9NjJj . |:/~Bky 4T^"ߞ34NK/l1ϵ Cl>n $˨=ƲDJNgA{or<2T$rvY@K`i=r.7Hqx}@D'POc'; o 7s@jc_JA~\`;{O H| p9@At&>&KtvA{eO5nXIĤ*9N)ofV 5/ JpKK2ISDunZD6 䁻9%z]MR kԠHx x| ђMprr&  w_,ھUpI(-Zr:ig3&ـ٧ k߇qXE4>z~8He`H ICKüX *ppPNQajf`8Tw5 ~pe,Eq"PnK>pR<ޗAVysp_}| ї C_cWUǞFȼxY)Qf3LI u)(k!n^`,"w)0*3(7qT4_Ga$x_ܘPoya]d,N=9W.~u=.P>7QlMeEb]tmmU}dNKkc~0f)܀Mة)րjHG|$Mlc@?N#L2 x3Uvd*( f(hڦ$Qz^̎tq6x Z#1tPmvu"TJ?1b \cX،Dotn<@$\ ۭch"rhHZP$&E(>G(ccd#T'z.Jń"m vrߜH b]6wp)'V'rn|;>OF #gfmېG?Cl̳@YW{rba#xsoF08kY82ו,:LS A_jwϯ8UA:`tPkŇ|=&T}?ƕ`+wIH:ps>Ϊąi0 8+8 "tGr|6uZYLJ^1N;~ji?UxAG ;nc[r|¸;'(c #mǜ=~1;ʽ'X2wyS?O8_鼬 /[0ªxcg<ĵ9Ń u&&9AR'3DD;3V*xc`G15g@yy?ix491A7l~,1UH"<7{?ִ3J^ /F/(nk3[1GܦT-kZQOEɀqټXLg JRb*_nyQ=׌#BTkiY u0]j* H;_*7%j 2e5mV=NIP//VHuV돾y`2v@Ydf`=Uw>KYdAVʀ61)d^EU6R7Ԧ}R:[1@[bM;_#B6 @NY3tM nj:ty]{$IjP :))ٱӊsl ̠baѿ[tX h:{4QyZ†Ր@_x^msZbh|DFtZ tkY*Rf /1mX5Njz^`dLJtzgM=F .9# )^;5"C ), XoҒw !C4ywtFØ;CsS6#`]:A5QN |KE5)_'NzT;=&Ed[MP5x`-pq(bAwu8p4U{|fFZ4?xe݇%{D8Kw`Z:/iE(4&7>̄*,wM~I,^uJ0H V&]9埘r|yss @߶7WKg9*_dnqS5(_NO.RXa,y@ Dslw>$NϬ5(!` p`cEn3WZb6"$Bzm tx%8[7To# C›xP|&|tN٢:0T({j89Ŋ\tfAPq#l&ww^ |5Opp0!pIPYF!V !qS'lGۃcxv{Ik?]<7(Vu ڥbґׁr^YUF|]o/A.yᅩ tô#p(2J`D#UuGLsͲʹ -G'/:7_y zqmKj7jh$y}03[J \Oȅd0@I2*l^ys7b^P1KlXW1CũV/2x6yy08u>?~9-gHqKҚjtxV5Sr?;mTJo㨀$|/pO~- 0R*-;pR8׌GE6XUYj&N1܂@B0Aҍ@`bQP?\f fR4kʰ|X@DI,70dbnk[Nm({ oW'n Lo0X5Iqcb (KՁ4^ ל1<`HK9e4_W_y!G@C!iPAJJ#n A"MSX "/i]ң: i/ QNpG,14҃jq_xj'g5@ Mk|D+ ;.n-tXP@6O-/|/~xʅ Լj%skn3Ў!.gFHXys~gOfq]}c+Mwq'8!X?Ma"[Lj`wy68%sH)`6\H[>pt-ZHplX@)9R4tY[E>7xqUr67y|>՞탮;\(lEw{ w?KXeMkBā 9-n8|,u8uǡL݄K̋LR8aB#. ،@+j9aBӇqFoyV6Sx.9qsqn*kN󯇌HsO [Z#@۟+PXXǼMmՇUkd!:Xf>x"&Xjf9ʦuk8WV+E:FIA$u{rpL/:gw~'ISaC_a"cJ<^n>GZ?m4>p#Cʚ~05K?a! ; ηڔ8+h^2Dh,WD;tWkBBkGX}= cxQяf pc |\!(l9x]%y`xDJv(I*ܸmi,jh}AzBs|A;} JyCڳE{3DkD?8x%*9a67yE~G"g(_Rth}=K &XRvlMjB}}E8M `4Oc"q@ȢDдx,U雝yWb% ޷l+{xDi]|b 0Evq=?Cwn#9RPrk{SQ ]3rީmMtbS>4}0m-n#|VT全'ӗ ZVgK%PA >\F<'Jx Lx٥WGBD H4=w"ơ2awfIϠH*=K3-fҚ? \LꯑZ-^T+^.V݄vu.܉V `c{みi`!9FJN ]chJߧ ]3X捶 @GBvj#BzGyΡ62rrpUO"PNt9(Hz'$w9糬Orא,b5qL,ݺ'Mrp߇*uv&te;Mprߜׇ X_j+8 08Euμ֒8># =o ~5Hj:27> Td Kw;j?W{n-6K~B|Liwe/ulm8I댹yrx*oˏB<6PS񃄓Vqob}ڕr>3Gs4 D75Y6zYqdt? `B9 sN 2_8}G&;@C(͇re\q5q t۴c?46UтX`x#޿xKpn&X>`8s͏;XIX M(|q%5v7Tp4mGϼb=GK Ki.ZhNBcl;PiT84:JIPS~1.i7@n|L A55=? o,Sv~tx?0yZO{ĨYz  iMHu׃xȼRk/^(dONmsf٪1O:|^<{Lͧ )"e&xF0fćMg9k17 .$$9xK\ 8bdL_D wp{5iAzIJ:Xeuw1W}CE66",Ng˿]S:!vh^]kW*R/W{ݐjOwvJkaS@ a.` 1bZi^p 8GfjPZV+zG6UP,B!GHxG/|N +^/!F;|;*U"[-I]~v7͜~*3@O]bxv}|D/0G޲>4@\jYYqM${ Gˆv_6pY͸/LJ\YFM.{, C=WHj3X8<2Hk{w>4֌5@@3]kAeM Yä;)͸ r$QOqf;͟XjMCQw)6oI$@Ujp;[HZwS2eh\]bF&qQ^9[ZH@!Fя ,08čA14n<`M5'jP~piYÅ@AF\|qS9CTncI.X )j isjই,6'gY@`oBtp˃)rP%(|ɟ9$x2-^2ʡkqA|K?0##-ytwa6UB"8Gf (yT_Mf>LQ:8$LޅOxN'|d=IO Xukh d'R.X]*fp-%q8! 2 $bt=N&nküv|YQ,:ʗB{'聉|.W ;Q=]bIu]Jb0YmdBDypԠ VgzuUJ- S SzB4D'pc*Ĝ 2خRhnTvI\`Q|V~f&`bl6E8&:& ׬"OxZOawFB.u;U<2j)v8"V8a8. txG&3"hRlq雉5ȎYGAH˶Da٣ 7J8Zѯǜ1 'Oqu,.*z·O/  VmLL $XAGw$nț' fsY64 Q9VM]Hs kd 'IY0EW#2$W26u(B x`:+&<@\etu0qE+ˀ{,)CDyȟxn{`k6qJ <6:r;3rMW\h{x,ݮȦyB}`-i6nfmqH<"|+ێsY,;V`"]umrV,{?"p\vfhY@_'˄`Q25Cxet2+ 7mM\@6bh)n~hlKC"3S$Fr0bCX&HUBmq>6"(8H/ThT8/bZUHQ - Z1#A_H!d7k!@K( zʤMU0Zq!8YbN~2`ER`;J8aղ}}}qre<x;u/b 0Y˝aıK`pN2WS@6mxBTȣRkZ-&m+22hWkHBwnmc"3uۅQ'vi2g^?xv  6EP܀(pAZ#uaJvtNQ{mw55p~mXЈj2h`x);˳3zS)NI;<C9(/7 <\Zo!.޸Ϊ$f\blFQx7w0L&,ƥ4^ nD?:@~3v8z̯gQp@2[<`l#N.Uo6c1ayWcl͍\ٱ9#kca(Q@ b4S?F>p#CLJ˶DGx}5 iLƱ p>O S K1̖8^4w/pX*dZ,RQ?A8] dp_Y1gaU81=xVc}1rqDZѐG3o;q'c/1>0 }B׌.u(?w(qXBHX|\*#o93YrmN '1T֍k#q)P^EΒp\*ӕWpJ/6eW`( ^+ǞL Xz~NOFL*lwf8T! >EgL)}ج bΡ;Y` ~ArÓ)q.q#6ET N3N=t5y Oμ'UyB#@޿12PWXA16Cˬ@JmA9#% ET4bt*5ۘ685PTɖx0z cFk!J2콸5)|gj֙?]&U5;ZB H̢鼮!'۔ߺ6j>_6l"%%38Հ֕k{/+qp(`_KX(LK=^nNBoK_PȕZ`kGd׍񯿻Z)z]hJh2>)G],W]s$~R  B67\BdC.Zenqַiux:83@|I,!nFTQiب *j i;$-j+iavʨks*ZV.== Ӂtж58^m%oj2Q5Ilj=&MuxH0uk iQϜ/\ tx4kN(S䲱y[o9֚i\}f9 *<1٫*׿-B*<⬝r~|*(ys !kk[XN4kX@xKH6nFs#8c:I[кmt0X¹}ЅTMӐvE^82%Ԫvl4-o9`;9nT2/}rR4"ۿh/S#hJ*BW )u!KB׼ןe_yu_ؑ% (4s U*Ʃ8lp,|rМad([T/(T0Rl%8'}h/C$'K\M'ӌ5$ha6\YXX(3PBSgVnva AˤЩ^ ņmHxQhJJ\Qcae[flؘ(:"- iE΃*MpmK5BmHYôR4-h0Ƣ'o'md O++0N(Ck__x x |}BU<& 7 Mا9bekCqzEPA,60i&^]:av(ڍ5nzșN*>=Ea6^JlP]r L|Zk'<༈PRk|2ēA ƭE9bN=awS apeӏ@[O $o$ 98fo1C.#AKu^D~0ҹ1q{ A`yE[cU)^Z $!]@W<On<@X[%"59B24w \@Vh?(( `j0 J;uuǩ<5c@!]nZ&5*A@H HTAWg2eboxZ(PO}uMyՇe -K=cY)ZSOmb" sw6Jc{dSRv5E@orVH :8AR",t@G%Z@w߬Gvg,N Up f3U$T%T^}a9ȅ=!-"0knMZk]! 8Z9 [cKÖNblуz@JgW)N կa(dēfs'5D+Crv&Y5^D5J5@d^ղyY3wc[@Ӿ?;!LL_ˊbq.ba u9ʡrl5o98XܒPD zD"40L/ hcDr:\N/" \lx,R6pu'/T::)h8J3QBap2gu0,\8p{L8G,wHMbTu=ANt@3@Lauz!{2΃x>YM.-&h8NLm8ҡ=7xÎ1J))xuUӉ+1@ǫA~pxFhq{.W]a#T]6.YXҘyfT1fA#!uQtCQVxJ G\q nЛ7|q|Q1RH)K<'}bk"UW(월LIH {DD8d H^^VF*[~ !K,ޏ"@\@-xIR`GF5n$ 98f>n]s@{yA4kca'S|@W^uԀoꫵ`z! J8+ނ|U55G*q4\(1K))6J:0K mOxAp*F om3s(/5w!AyFZsi$xot+BqQjYvHOUt-7(`96;CVK.g^\XQa7hi~su?y"I J5Shޓ#6(_=_怵.Ay7:hUNw&k<(E';<`/IZYsZia?;w#K/{y.Bwք ~ N^w7OazwpurSJXJwX:|&e[ȁ*Dn>ly͕A}T4([p`d,t$M`{kq|@piNG0\Ijs86&@lijnAhlGyg>|tHEf5UɅB8\I9TYйkc9z,博<Jxîc($]o*ً7xupNMG5V?y$rbl VagjְvM̈ rvWc:創 ~dM?r +~?`^q4IYFH1vU> ;و9~aVflՎl>]?I1 ՞ph>sR&yp pKS9Ý|`.Dv ^9Ƈ_ =攻n ,P8͂4pxse]{Cyφg3X`wJh x&̆J&z7VB>{CI ħug%n}]Fez*e[\@9pŶn|P-$r0ڗY˃F(&$dƕP%](:.:~@^oCNu\:{]۷(D1l$COަK&&F"V$;6fW"w]sk 6\`/XuJ"9x%<\5 ׌\BRu3HňUd+`GxD'PR1pL1&W7^wG]G8ʸv( K^:74M<;ʐhk0QGjikH"e+$X ,rwqUƱ1` ˟#A)6G cƁnd5OO SezHF1ZpSqˉHFxۍAZ%4=kFW>-vwBi5p^ Gm^@@Z-B6C݅ \1o-Qj]3ABY6ImEvZ@ mdHr3%|JR&i"to 﫞A(){ J#N]XY}<&fWIOmLx8C0ߜuGf u_IZ͑~\A?`K5_Ęu/EyNX^ϼYeb*{8vca meb T7@`)su^1/t99w|bW98ZiXvΤ!o:p n]~B?/XTHy4iЊQь TOJρb~$ywDMThۈ ry|Xb(cSneg!h+ ZLു} 35tyZ;Wz?8| &y[hTq66AX'R>yNnRGx6<R? cAg(mNXF84?YW*3&NmfQ!Mxa4'Ϯ>ឫw Aį8JoFՑVyq#v뵻m-JRxƑS iJ;{Xi<٭'\$D$2hO\,ƿ9'C):םlX|Ysp;MD'_vE`_ה)hqcf_l|hrb74zS;B`4 2å0b,ZaBL7+u @p)}V}F[1F8KYjӮ`۝Gӗj,g5C@.s+#b(ssH\W`T΢4х+OT5xTR) |eζU: jp~nh,JןxHZ!6p7V~QLqW=+pY_-} S)}c m[yߊ6&56 '/F~1"wBzaR}X!D ;0ٚ'yӬJg6ᆷsW[T'ƈֿ(Ɋܐ]:ĉ$@-2уV9לQ q\ZϤMՔ ̅asO[5/JAjaXVQuz:{D>L4}9ŞCUJuΝax({:ԍ6cGa.hvC>9'LZZn4q DRՑA m4@uaѾ23Nspko]5ɺiҸ#KC~,{jⲠsmT):5Rʊy ?S (7oql)T&p!]T m&7'-ܤ@P pWN \?Sβ|$ 8*(*Dޅ>&!"͟"xP ~WwUX'f%rIgVRomw|FLRRa>Ś@ocʂیK6]22,EHum] w"P@E9?(UkΙxQht(F]G/&<oF9aa.M|cu\@OxW =Gy979 L⑟X4GGugvY&0>=bPop"*fqveK\s 7&gBm:-o<ЂEZJ .V]a%}0] srS?= ޓ?zš!I#r:s}sMqvq!2 q)!F'ݔ 'w.@;@ :ekzp!/#w1!^HBPn{~ȃFzI "!]D#PMsC7u`x۠ƹ|:3ejStBs aiy"c:9_o-F/8o]wHoź*f󿎰R@ %mr)45XQB/gaYOb`Xx_8Z"Er{R7H ʓf:z&L ۻuJ)  WŽ̆ҴN?U"&gcx^g# zǥ eJ0~R̨J"kFΦ%۠F#@k@tdծD}dF8<6 'y,:VIWŴ@N+RXEX$ )"i7_"VGG0 TUtMB(W<{J J]o9n]5kV=)QR9 ޙ] Dm 0HgK(~]lJN;;O:$,|)/E!y4E솿p#Naj\ x ;p7ÜIxp_Ex;\X(u91 W5ַV󆔏n5_`AMn@ jw^w4'8S{@ܙoH5nרl 8='|fMe^BA5OϽe/(Uп_Jν.=Vֲ(ls3׋ ߮2\Y*5cO9ٓbnܗ2 T{!+뇜VwW= huQ R ҈!q< [8H d5$:uI~HUjI@97St,u2hу ]` Tb7rHʱ{ңn6ܸog'.Őj.u6"PI`i!U&M/3FaK{s|e#&z۠,iV@!Q6\R)N )vO,#Pd pIJP^[LYS^0}C.{9+#ygwAfzn3{n[_Kc Ϝ ip4w AHzE!cDWk!Mxז~0 2]|`͂<'9/䱧 [wӢ,qǼ$hOBA #66.a6?xZp̈́}Id iygY1 4Fe5U'8U^:vs:M308֧E^A&ũZ+a<$B]?[Gž c2=eL:~)F7w:Đ9~qIwLXsp82=<-c9sV; x7GX?,GI 'v$p xaS`Xs :Nk췣1ƉM3Xí<&ū0ܜaNPw.(Q֞imL4!C\X&Ḑb5$jb 7pIE92îwWzƄeXN9,!AS6~f+-y1ʝ&mY@:d*Y Xw*to8B&8ާt_ء1h#֟Z[n\~l@ r[=/c}܋*|0TPMcpGT-Rv#~pI;4<:Ҕt>9 <d)R>GCrXƅ> {DU*ҟh ˗>==o rFVÿAQZ_-Xv$s O`lwjl,1HDJB!Se#;` oqOL*8>HF]@!i/wF >rZ*]w@ eM;!Phg {余ϻK}„itһ5Q]yz8>MsjBG 1߿0)XD<SҺ o1Ϩxƣ  Xߙe]1t +HyLdABS;}>q %6V)(rxnE ؉Z|҄l ג!*DB󇷅vU4`ec YDw6FFk,5D޼` FG4+]?r&:!:>r)qe9o5ىs pYD?@p))1oo: )bֳ`B_܌9^w8$n@mIzG$ARx%;f LX|xFn&R*=~\Kz޳NruL߷]g+Lph\#gA~=s  mp`joL`眂]rZ ۗGaM Ko45sv<1+)rϿ.]zu);U{kyf? _>0Z4ʑH$50P`mKdx N02:a8Xw8a7-SSwIpH?")*ׯ遀V1f»w]o%m'Uȕ} oؠ!u/vHtn\jxw5.)cnbmFz6mv7ts؀r!MD&ο}>Gj{bZtJƹxB GGތ}$n_8B '@ObMߌda(hઠAeT$It*ְBH7o["i+i9-ttP*u۞GihUy6{񁣫IAw9uCޒ^MEWAֱBQL!0%wtoR?HG.HWFӎhw߷~p@rv*}[Mwa!NWdū*w@(!'0pмz*G&Wy)^~QfwgKQq4 j`{@x ^k>N6Z`F3B";yNC}'Y%|1ǁ w9+_l&|` ;{2(2ϭ^4uMPp` p r9Mb@,)Z"}k4{E.WM<%BG>g7t!4c9q]|gƌ9N.n"a`R=Tv?`",T)Iܾ "9'K'{|`EnX+H:֪[^Tw5 9J5y 3mS(~.;Ƹ `w^C  G15kڕH):&4fYR,ٺsˑ ¥cYhO=ި+v|KChRÉN0*}L|{K+C'`VVJNZ1GF4t, |ƶPq$G gwCnU(X?F3jSoj't=ް*|cM@^F3׬ՓuЦ_0Geo"zyP#P|_F vr_"6%=kV:T$7 ; -KJ=dQc`Hi91xOOPVW@ח6v˯sj{+A܀\otENsaJ7tpU CV ՟Xȭٮ<8ۼֲY$ݼ7?Ɨ% ߜuqʠ e{,I|zyӧip[:矌 5M`muy~4_1*<8s mـ^TO#Ӌc'$wk&9ee\85qX 83d=u RǟXr&6 #jv8hܥQ)Bb0_DӇ*(γJɋyONRˀu<&*=/d'c QDĔˈ><j-rUDDb5 sҾHJBVEĪ5-xJf뎍QÁRo┞U^Wb߼SqqHw>cj0Fuh]|--54qf@0щOz3p"xud50hyt(1,*:۟\cFB[>4=?%F _뙍s*9ɡx>Uy`B_g !XDdauί۵ӎg^]DX/3G_ ȜݹXlk $hNگd^3Z:x9q*<6&f$i%񃻶pbM7Xpih?>xL|vF]x1ԼU] D4Fi(V8k:91, ]!+O搵AG`̋u/.yp<f*;/.%tz# ls8-JN{wqF n''n5}%sWpS 2 Po& 3bHy536xR|wZQG^D*[vs,тǫ{GP$G}]CM焚jt^ѡ]ɕݎ(Igq@|p NXMȊoQ,Ǒ2}qBhC"#i}oW@ej#n7}cKV9s2]]upG5o1cSl7I$'" ަ 3 !"L\z%w?3HRG vhDXwAAT]h&;$6՛7] M);{ZR`E0Ji EɅϫeo\ᯪ:Az,<)}ۉ{1vJ.4';kJٜ9c[s/e' 8EflXri_ ч n v=8Z=ey:Op4k@ Z0Dx=b L^pT9#qyNX"Q )Ž& XG&S8P mzrv/@ueKvo~/:{x!pŏHكRLu V=eW#1p R}M&_# IA83q%m^އr~&vX,ɷ퉥kwŧ#?)|@l;Ĕ88X\OY3A ^?;{.6׃3dF( Ի?yd76g4P\dPp 7[ Sfr|Q .+}exRߞ0{.+[G#FLuч>>rᆴ}f|rc3 Kd± w2 :%ka 6JB]1 w}`;7HeNvdl?ea Y ^2{x~Ьt,ű{6;7iMb)S'T%J}ЛAu5X Ҹ ?9˘hrDhcͱ]R׉Qt⨗}?pk gE턫' = ]k6t?"^`c-Eq5wWn@$*\VpVB$itK :FE'0j!akx6uՈ_ylͼ;1H5t ,lѫD";,:'gˬ`@l0_d,)6k֎7Xkna3bvBNah{+2T˸D(Uf 7LdbM151%H-M5.`*=t]qhCDt]3cf~x}xŜ9Dg{=sRq'\W)5D{g{qjFwcԦ]nZAʏk(at&:Zw+]O7 Ӎkjy!:^U72<tSr4R%.]z3QRӊcc/Nh@4֛w7~Ύybʄ{&-SbaM\hl`F CS{~ptI% cXEC*uACv~JkPs> bAaU TEwO=&0=U E mN qEmll{ tn<8+]#N'1XhD\Hy|=qcU#_Uk`L0_yB֮q \Ym:Q.㌕*׏YlDƎWu0Ѫ ]踊[OMW eHPE.YWۖβ )+?k!N8CH'/]P~^߼kXNuy\hbo(\;DY xŀP>&ߠMLXgz3VA8$+ ֿXǥ\O(F qBSL(H$f<ˊԝt9:w`k^7+9޳T y  ,<-9@l;D_9F59gY]BAaۇ"~}p0p7]`\j2yWqSr,Ăg=5qx"ˋ(QGZϢ}rn_\Z ,߭Lȶ|m.4m)md {_~  #V_ww"*<7kAO8ϱĺP+a'jR"m҆<t(l^')ԶM<C ;Co}]p// r|ShxAMUUBgNXMhz6[9./YޯU?E=w (J)l]~pRH޼ߜBTT_׼x* _Y2&b GU2NJ>7'fs" @4>󈵫կF\^Ge5LjK츧z Rh/Y4Qx˃@ɇ(Tp /Ts9S8ގu0%6Y%HJr,jwcYEeچ? A-{ļ4|"NN18,4q88f |N N);PsyߩzWP"]n:hW{}S/Yt}`)RJsXbi篍YC N]OI0iW3S$s1iu1'$a?&]Ɔq5n}`/ NpbAww>pR}d(^ xa?9}>n\N;kYoF8/bn?88~ aAÃі+L> f+C\pFܹ{Qe|s)o80~sa+֥׃#a$ o5\_.[mh>Xrv4ˆZ4hпW9Jv8&igyے4|@Az19w؟Mō߻ >m#? pWHt -qw)[w!Hңj 4G=!MƈX< +$}blj4V I6u&eu?8 Gmx vUYˈЭPAdiZFEYCZuŷ 7_45; h T۲p 6e˳0H11}*3gu5ʑ l/ASzCN|xyp 5ؠ|ep5Ï6X6M1t{ҳsC~ZĭXT;#3Z wJ-f4T)'7k(o(V%6y5Mج> c}X`0Po\5/{ `oa߇PerM֭oo7z̀u.K1#'o lD hmmW7x,ݼIC{p]KNxIpPpl.qQؑQ!%T:x]p`e]yHٹ !(& t֛Wxqn ĺټv켱]`c>x#Ble>J ٖZ]J&01\'lz C,U߇uB{"aw<J'2 LT@4pa4= )c5Fʨ-|∓t#;,?Wqi@l Xw1 pgÓi=ɀ(2Xh^)8PvA`;/ANQ,1rqI$xg'U ҹ5<[Eؑ&p9 i߹HԜ*i̯yElt,m9PwCQ^5 mMo!eƚx{K⫭~2x0pqZq4rm)? G1 vhC*~õz48̪mQ!-0aN?lo -S5u8Ʉe,aΡޔBO\i \@WC6-ѣ tg q㒪E]Iv(8CȢkxi#E A} QlD>'":ό,]i$ן9j*oЀ#îqge` ۈ(v٣&y#DGL#[l:=s=V K B:cVd9*֔z40~]\Xh=.c>ܪ5Դ~0OPpn]^d6zD Q,Dj1[vJLtG,F|b=3~-'r[ֈJ'`QHHQXAK{kƢMl+`Bz'K,A)QXMp46xWfΏ*4gDy5& IJfK66|'2 a%r`4W(ޛ7x#]X )r;{D ??I Sc}=!'S˹3tC6W"|؇/ @0Ei}cXj? Z Xt<ڃY#I0l_vsζ}a 86`V 1]F\eTg;<yx"mxO7K7]RE=Rfһ98I#nP֋$^93\ .Rjo"I&:nq&Tle9O"UӌOR|-d:t u +q *uqKUU#L\L6❞H흓Y{*kAÓ%hswX,AKP9^ҒαHtaRrO?8$x$b X1;t2Jkn1ɣ:he3$mw֓sXE1,+CqucII*ړо+uPN !Dy<ߌ8"[pW|?OE*z`4o`&| JA7GkQzstnj5*>_Y)P΃%9}OjhAG&ַ׬(lFjHx݅M76)N>W]~^g'B,*|kH5ҥQ[5WzAab^rE*>Ayb2tgQLگ]^`V0 mW^o*!?$klvM!ұ^@S=N~CBJTPCG`z,BBO_tzM$*ÅΡ{oNżYv /0$QTGD͂ G+' :mHH":w߬bѽhNGAH)Ua\94l%iՎߌo+hq ̡P㍵Wܙ"&2ɾf@rX MfkVC^C\DD0poຽ_ I}]V3kB';ĉF)hjp;2hݗk8;ƖdWo88 5), vhl%+HWY9]CQ44{E{T&:#٬oIS>AH}q[\&JFl .i eތ!v:`^OTOc>&];'#@q|Ev5~0kXҚrN8Pm0A"1H2;FwH5(xfn<p1siwa:93:u'-n ]AD_HͿ`I6<hN*ݛgV^o27"*DaޓX"isi *9T x,Ij8._;KkFIQXźxqiz`;j)i/ ,Sn[g a*ͼ-i4~`PnDX^ NV=7^ܥh4;Z E<6#D]WS ~>fFBZ4)qε4ZSdsaΆQC߂G9u`deiuyQD=! [_T?c%fI=)^X'as>0 <3_X5?870Q{mp ~F!Q/&6G{h`irvE9bUF.; 0J7׃_[  /v撱LŵMHDhE~F0TvY_7.BP/i&" 4k0(h0$54+^1Y" }shC"4#Çy|r-n\ܳ$jFkX"$ 2zG ]M#wB_+-pH7DRz$tMXe' 03azբ;*>Ypotw[rẍ́o6}?Ep8v8a<G᜔~CnjfS?G׼C/H`Տ4K(|xOF˺͊xO= ߲P r>r NaY%ӼP2 8@h+>Fq5&5}PrD=mі49i9hQJ(˽XڗW>,θbt.̉ xp9g'_߬s;c&o /Hzɡ}|Z*3+YX޳ghT3d{+ x;5~H]NR:n4a:0p[޼L} JM #4m7dǣ5.{ly)QM> 4?H (tyRQwxc ν2r tqJ|VDTӌ6HuEOfY O8~b JǕFMpk5~1(L(q#R'it]Wn,.+,q(|s8QkmOO;b ~-I55Jg%ktZHr" ZOU " ' n`D4*5aʆ{^=\@Py0YI#T),+{J }Ύ9 9C~0i $[:Ģbd*:ͻeBfD6_өL*nDNlT%e!:´'e[iN܄ㆼVb)dëCUC[91^Y<6K;JZth@ %Цw˛(l5s1|^=ph.3qyU< 7eJe*Ya**V| _j^L_(8t%8J䇧68dV`R(1{sZ&[gPO. fe `p@x `chHv?Iyzv^ v^uozcӓ * ~ C~|aCZsͫ~I׭xɅz(Iwy.SqBo\^2֨2ʐxۏ'E6/4_LSM}*r|⸵XN]|~qDGc䬡N X!x"g,Cf@cdgj=Vx;o7maX܀ݏ~l-oG)[fXK}M}eNi^7`P?xtM%Vj6 hN5wҏpЖ~6΂*xяA9Te>r0yYǑ]S_xrl\T^0J_X*pjD=w-IƛuϮ2鑾 9rHP+x&Mѝc#앧_8(U"qoH|cU|õ3l!ұ*T@60M*hh7#vy>|"ީA|#@Um[pbA]ކ(!=rgraDøpt%yܼ:7чvxW}t˴Bͬ?*Ա_ iܑyF*N8S-QpEl;ĉ5O.Ir}jP%e17&Wn٣f %G'Go!OH)ߛ^M[cq~/ όBęTNW8Q/N\Q b9$ =-z:˺f{}h'W-aqC HaģqUe:ŋx ֱ#\;ft pI Lq2/AXk,muWrI"|Hcg;3TמŅҡXGΰGmsoxQd{|@| :Gj֞̌J[n0ߦsaŷ50A[֞g2kÜ'd虠Hy2q5I s5AѮrcEy7BZb[6ǏD9†һrX|u~1p6 ӖmTp B;x| Gn1`/xpzlg 'NVp؛WL\_ vy 66f98+p$fr #Fc nu W' 879M<mq7Fp0ŤBC6>D5-&mGevLZQM3^PMq8_6}eU\؞P~ <ޱ_yíc' !픽nEdt9i\%UOK;GGy:(>y1䫰vҳɀ|=Sj/)C@P=} TuI{upakes a5aD. %  p4 F%1c{^">ɾ]h笄=9*ӑu{ |Ó֣L736xXFro)ch=ІG -فZb;^r=OSSz0x ;bU7ŜᅱdH(hƾ0Wɧ~f"M󄨔ϟT`j[w_6Ln1}Rs3 1 a/ P`8ӎ0(o6s~'ɷ l,<` ̼# ax4F׬j!/=A) }k\byEʢb ɏ !WF>_V(g=sl^t"a^p/XyG{6#lobi@U|J 2'TTWh6^2^U]@oSRl_]stssQmh8]k֔''sy;Ew,x,8M6[("(Q^CБSh73Q8{%0sG Re51-T *;!| |c$"N]Ye^GO8d˨3+@Ȫ:^zƆ؀% h$# 8,tLˑV5qpWh.xjIy9߼Y8B,я;}f@#dJyrgbmczy98~~0Gx<2 ]/>2=dM?xvҗf/Ԫ [|$}6*w6OXZI~0#2d9lǝBWzR/9w5O1hRo9 lߜ"\<H8AoE[@suǠ|ԏnbP[ 1{Lfq+)pzdQluO'C)xyu%M>IŢWaw6d)^ AdOs ~KTO7lJ2}WQ*~5M@~Dn@\vH!k=d/8>UpgXFHy(xa޳/pL z1P i1 ѡ6dx3XM{\pjgaqkտ7r w*__XI-N S!:nkTD4ygCi᭠U|kŇ{4o:~<5 } 'W2ʃqڨ*F"qGEU$< Z.B: }g w5~uJ }4.KdžUYdAK['&8+Hx}2yE(;o(V"譁.-QG4I2AӚ#=|dȈ-_4C+E"m~;{T4j> )*27_ <9ԡsq""Ʃ [{S^3M<+QWy.l:FhN< 9Ktz.PO%pCMcUD.6k[~ykBBg&}`u1@pO>g>cx@`ow͗"mڅ8C]cOױ 5H?zHTtAlP |˄ y8 &81 _ 7>DB\;"=tXGÃcY֛0l~7BzŪq4lۭϳJVCM QoD'r H{Bak󎘔^׌nY^Zנ-q0DbDl82L)LUpXJnWar6]KQ}[Y4bל`=5xX+zd.h)=L0l:1ǜ;2=\Foό t(IyrpB7VGIQ<Uo6)̂]pPŮr~-K/WtW8wʰ>:DVJK&ֿx~m{;wfvY IDϜi]<ڤK?);ybg\F &ȳȆ)cQ~of;Q+ƛX Sg)Q] TTU`=#15SH@$oaRnknGNBd-2+pT#(?P/2]Q;(fU-xw? njK L.\1Ukr!j\Ƚ8|9h7r^+g;0E|z׍}%֍JXr/p!qA0Bc.@>o2H0EJT8yz}b@ М|q" ]xX 鹯$%ެY\ _hdٯ -<hq`M]޾1׆"-p) VCC o8tDy@EگO\U&kYo{ȸAGyj rj}ʍFnR8ٯmZXx` ت[ѳw+򦋒i78/xxӭx'&:Bv=XNj@ (Nj8dxY6'p w&|ǷC+;X8\'.|\ray0$&ư׃o0)?H_(8fK8_ǼH~JMZ#=bʟa@{Zx4};H#f\HUA55~"J('AjoV+BV-9Ev{vYg:\d^~e#o8i{yN1Cp!l]b};4q# t) —*wb[mֲ tz4n!=ɱKPiqb=Lq}Z\ yS1T T|\5]S2<.@*qy$(18LINm³%ϔ UA2Aü.~җ xKl~CGȠmRE b❰eER0  a:SSo F}hJEK0[^N+P["wwz)i5ϟXj<-iZĘߧ+]u2E&{mb;`_^  :0]û/ooYd7vc2ky7D5\ps:07{\"!~o\ow.@l'"ٌ7I $rI룏"9뮾(:p <Cn:tm6χQL;wttwZ~O/\7%f;|o7ÉjӦs5:3M@x2h6ċuXJ*{6!DSs.xN5W)Fww>>iCkE_+DXm ث|e/d*p3vo/V[%`nx-"+b糣Jtk ܟN| -(*C^)A"g#DSu0 +!=NPR8zh&GVz5m쓺(֭0dzIeD7n$Z*bu^8ٜ8qZ=\[3GpD3+=qp"<8nt9@yB#NUF t:63J;8 m;{={+'hỻ^y*Qo5wPFoH$p!rhCvOc*509 pvO l)p{Y&QD9tF9@6uQOc8s͒)JfROOdQ{*#6ؿ1|bQ2&iuk>O߳Fp!ۇH¨(*hh7LP;4.]eO˖2O ލx dE]dp| o ~' |~Ln% E-Ǭq^N i˛GpRէW&+u ~7Z-D_!u:J~ل$)ϜiP :S~8q2~ egNGk0'RW0O8OORvaa"w|a|O0OL_k}84G9Gλ_ƚ e /˕t))-hC']1< luɽhft:JB/1#OPpT䠗!1FX8#!MaP.29@jE[LDPXգzSy `|H7<%W**$"WP5׳%46 ΂7/- y6ߜivy-\OX@MO{%.`MqDx ?n \BPn&#S7vgG @ZIOD@Η0hux(nePTӉ@@iY{%yYUݳhkJKAyMylFĐPB0i ̌(KuAl<3wdV*ֆg|A1BJ޸N|} s&^mu48<B"bHB]΂!^ O&?6/fq.,kѕ8M!h 7tOø,kxp#yXJqFho&SI٬iߊv)5H)7K" iF=*Pr 9hoFLh"7INWL18_X@ivQq9 0! ,b,Іxw|nai?2-t9t!BqSes3:kAO5qK bo%hG0<0t{r -^{0ʄbEi yȍPNnDyw.#w GjfHk*Fnq+yy |aSk厥|eYX h$/PݭC]#hNJ-r_'d_o\=/N' o7#\O"y>wV҂b X9vE|^V󐝺7YӞxz; b)B㡘Yx^06_sN0F`1V*!;VȻ5*j&SBAvNn4XMrȊlJY#M?9'J]LwIm@Aw' XK^؇X;k]0M.MMh1oÇ@ 1^7~+sX> n>Ex*;\D>DGr-O<~/ ? w)5S'] SWjW>ʸ**] UcA=I j: %&{)(T#\j1#d-X>^qlsCTuRiǸ^ %$7J0%rү3 DtK(-kke`c#M4gVѐ^}` ]̄I޲RvTGᴫOa/9 2#2EE#^&npFiȞL5B/-݆^^wZUr!@Ck=:6CS|GE-W&&w0טGd1!ވ_HQ 40Қӑ]Yp 24˚ J֛HR%tuqxHG -u_o [*hG?l@i`R?@0Ā;qTPR'DWF n0Zu5xm.dRT<&Jpşs1䢈v|焓5(ۛ3pzZk?@7 ?x C}8Jm򔣯Y~;/8xE*+7Wh cb8߬e7JpRU{h5㞙`%&_T6`deT+6gdn9}WCI#cNr W;6oӐ)>&e(LB :Ѡ _ 4r>ySw&zð?D8/n:"K ?f1ENBR=ڼfŅy~21pEŀ =BIۮH0Mw`ἨV/Bu(֟g eɏ?o3{-O傶 <;3wA$0k$"7*k.^pﶷȇz(GjĐ_XMh|q +!ԎQ@&(b@Mp=Je)>?,w_y7ۅ Sj\ AQw8Lij!%y;&АJ Kzj7My19$FxgJ%;}fPl=U%!uX0-ؾ5nD:4h*^58PkT׼[`Bz;tû15>-0Y@ÛKv6%5Njr8f.|y S ,%z6YPOܸdaA٣6ECf宀\6kZ׿i4A}5+4tQBGθ SW,=wIIQk3%+恢oىpg _2qqv߃$p)b@p\yFᗋ͏#ljCᕯ[ ^ZQ0 8 R%\Ds,J*a ȬCϜLڟ=%z 7s$N $xŨbjN|[LODO4F|D*&SByy>3Ȑ'\Rv#p1?'h{s_qz-/b8@'XkF#uߢiz[2eфO<;ʀvY0LAu0.ć^=ćo)'D)ad0إojxpN~sp7󁲎kS^:llqYJb@PWB5$ݯfkX*IY]ߧX8 r#L7X$Qc BWC#.{f!2# l_;хhGU-aM m'/mRxWYEKWЮ\;7 x!*Q R5VcƱ5FNV&r_.rvkJ@Ce)mSa !p8.<'> 9f-fr$7iMu` c=*(Qy¬!{$DnrQ99ƶfњwk!{wEvq,]6/bH!^ˮo-#Z__ KZ@;@OhpᣩPE *qrT)$D66񣼖6%~0ޡ>qt-k<}ci(d=|V e#ԱaP;E e/ FnBJS C4M6 IvadKJ3IHht.Jӓ'^8;qB\QaNurCJ*k0j3Rѽn.@H_z N76aS1طuZ`KQ $]W&WS3]+n aw#AHu o!wi&`^nNK>Hf+hk~$]O)|DCsH2q: _Y#D8b՚?#!xG{#ƅJ\-ڡ|1n.79L`: W"$;؃w%;p9 'HUVPW^? ;Ma"h鼊.e`KJX9:j0mڌ!;ƪz3A)@z kpZf` *IJAI<[ǞK)JX렋xӈ4k0Dj(?|W.Araф @mí_SfuP7Qpew41D&k;A'Ju|F r焣y ' 8JmTŝ7*.5xI;89n$,|`lw]dp>JlWN1ЃN?& S=9ŤM{q՚[\)A Whr"a5U?p4Nm$8@ktvw*% W ?/S|y !0E컫0h>%A\;?[yp lebP}.''8+l |)QZpE^2* ws@"?3M)Fp4aLIkPxM2M;I[=< }XW&vE>>rA_)v˅Cbtrkac%4^uO%}=L-B$.(D!(ڌ.5cqƻZlA>1T}etogcX/KC{%@dUăe+&lT@wy뮼޳|e8uuOXT6L,f2E'F,v ;{ {񍀖 M&ba4Gf*e,zp]ƥGxǜBcpxl!u:avw T7γ`Nģ7} _ }~àUWq5ɺi:އEB6`vُX7ѤY!A>JqK /׌+A3j+eC G 0)p6 XVǚm]d;5{^1'LXm1C:Ww׷گ+7O )^2:ɭwËX:0(58Wb_"r0#/1/@UCrp$qNڒ_ vx{^/&U#3o5xz`Ktz^64BomʲWN-?{@Lۥ/k WѮe1u6d2;h7njhFz1}\SEP U;j) 3(tRVxRC3zhK% y^ZhMR1ZJu!JEBO\uxV ]4@XHtl~;rip=a)Vٮ7SF:b6FCcM5z_Pn&8aT (# Ykd$e=}(#޷5~ΜF4x#DS K:bkǼ" N1$h)3j Ԩz<@^k ~rvlJjA+i?ʊ}c"r=̊ cNG_xm(>y@(_vʂ^.J=㏠4C4#mb+J8~^1mD/zIcj iNŒVɐQԨ o,"7R7+K5#5  {7k@z 9zs9AQG%W r5:OdaA9y8* S;;;#}zӾ[ m8}g"9;z1i_?K\\.ߜz kN_$ӄA:!PxQ`'.wK3)]AqW: 8a%99Z:\ukԧ߬ (!d(+M' pBMrW03m?bͥSE@8@ wTF2p 0:TZ. *li-* Wu[> 6V d xR1TW`?ް΀Ju"cTu-)PaD6d>|Zlï) quU8Fss鍛-i7}-tkNGimFEPWyD @ZP (yloY@ TO. wǷH#%!}óDwJ҆CQrvZ@f G)=7O .PAӃvLuk^GilvSl18g<ݠ(7,p3t#h"*48]!DAJ Ґ::LZA&sv-iGs@](9i&9^ƯZ^ɂ"Vpی [ټQCb5%Ʌ0i+fLWcGX(bbQb7:D&85pF4$r *X v#{,QRxwxZIo(ZOhSez90ږ|xӑp rl/9؄ O3dotkz D|XF(A7qJqQ5/8pԿ')*78b7杘< ˵c3hXw[ƚvA{Gq.t!X)aWmX_&(6!>x^4g?Y[s!{:Xmn^=c:qrޞ7 iIR[ߌeK5w)|?#]J]vy ly92xv8; hߌ|o+,*uhm(ž#Sc˫MOh9?IROìnNȹXx`j9m`kl;~PG\ FGNyLgs?$ DBirP A"N'!4ɼanoW5v ˧pܶ(ן͉#bH.67phmqapj:IwD;U*Ջ$CK<$¯[Tl8x?_v7W_455zlm[xʩ]L>у 5Ng~yZu]h-6y:ڤJг*j+V(b!xs^\4W}5 pc 4SAH4rTH׾c@%\TSIWޑ CȢXd C޵Sl7:K`Vgl.wܶ[7T D_n31h([C6oR ,d+IGF(Pvoӈ o):, 3j|@ทys@gN*֦tkx-oЋt́BTo~.gh$H(lE*j=ҕg)6" y pjP UWK#4X:H  ?uؓHg 6*qZGyZ-F< CNqw[6Pz"1ׁY\/jfv/Fq]{n# F=]X%<`h0{CX],RHT ::IRV| ~sҊ OYv Z%/BGxa>,iGz㾸1Nbˋ IN|n*zUTx3Lo00"8FzL0V4dF:GTdbi|O9j8P[@tq87qx - 2lKO:p@=^y (ᄚs廢<#.aR貼܉6Mb^r(wppwc kTSraRl:A-|B =F^4!u4\Ѯ [07.K _2sCbasWx%Ƨd& c|@^DU P8X0C/4w J[WU;6hQF vy~ c&&HxXX3Id72z.8MpTMI/bGl+'xKGDm䣂?Tl]]ƥ<`l/<+q/$GP2/rIJ^@1j Dt;7jήJv4nilN!?s^ȃfn7??|<-|]`1%6Ewx6@K(1ܳfWAv%3(aӒipjU>̪qI:ӷ<5@S :5ː-rw?i8BzJ!r@P,!ҡ;:dC?{?,9h iCn4j.Ǽ gRIsySڊwμZm~1L lۢL(Ѡw6*N0UF*EqɁ0CSGUdR5:=ˣGcNx (mfcxn0t04K0\_%bbpQD8^9UE }cM;u93ۄi׬(8 pO)x{0ha8 .LybeN +8n&xjakʥi˯8&0b=\Hz+~=`r`\M 8]ù]qiʅDpDMa"Bɋ<0B4<) ˈ yXSk6z)OycO "wG~𣄐׿"x4#i;bm-c|rcYq_pȴc}`@]8N-Μ2rߌ8s'e5KN΂Npa]Л }p.HqeRP_>.>`;77~ !h<UX"QU?m6xQg[<-DbW}U'uڟ}1kΛIG'˻UɄNۛXzM"OXO뙒T_aBV&:mm9%5>0v_sG=fz5E ߃-īDIUy]ꅸQRiJTxޫ&mT&d <8 uF-fߜ*xA@=hp,ga/ARw}"hޛŠ]bkM;'#}`7޲z'@bقA P7͍h,0['ܮZ!$a@>}By!;lB_ }xqUf{IR_x@ :E;T' !-2bHvןp&X6!D- j߬PE{ÙDvJ޳e2<8v!L򭇜yV:C 1pS5OfΑJHP8,eM':T*m?pv; ^ 7dL4yR·E߬ԀtM9C]cψ]W逰"[75H: &"tVrf*FةGh;9 70 {4jM}a4S  g\'0nAI"Ժ| ^ )b,'לh>obi(!s)"A£(#FbXeWe0YaCh .:4z3mxhnTǤnϯiR}f$_hL&W] D*$ y:u?-r46N`,/XoIE/謡L44ڿ8N@*@gZ/ W:" px#Xu٦/;o =,W~^*Jm>:0@Kdq. sM%e:ʅ;g;wnCNs2H=1ESn̢Z;RZlQ~yo4mo8&DXɒCOjJNrİ!k]o,Q}[']2ȕ7I0&??)!C@s|~C@M$,o8ל0SD! @xp-ut[/8r lȜyƖ&_rhhfƉfdC)6tP˕1XFnl#wA]5!Tf@xxcKjH.ӝ,w$˹%fRɀɾ#W;${qs%NPm dF‚Ky:ծ{%J:QrҎj oA+j4o8M쎁Pnq][%oG{ہ; 'yxpˎNqXpǔ^1t5zdž/PBP`63e|xipM;=JW5=uCa8P%}_YDNSn҃[z98S81;Άt57^<U A4d "hl(>g<})$!4 &~=UtywP5wt l'xH$YR8G~9 $70afE:]o$P'ubOhd&-nwzY<F0-}f#+`?<9`@M&Xt|bVS^M(@B@j`cmڄ/;z+$Vo$>pU=zE-cz]|u@X rS"TAIl#Z*Y[X{/0 [& sB ;-[}O(GJn[\Iw mkd+TЎb|4Tlg*Ar{'&UwbbR^H!i# k8KZT4uϳh#JayC{7=q%8g)0:S)SJ_dcLZRjMcc!ľsUPB%9܇ p=0cBYoO |o|+Gzv4S|Rź@"iKtp0[JxʔYpN* \$Bg; ~W h`3w5&Lky;Ž-_gEGTU]3o_Q3=~14 S*I~Kho<8a"“wy Ӆm^3RY$ @B~:C璥pQ41޴(8bٵyBخ5_±,g@>|(Q4T*! {с{Dӈ.C~/dBY% Wq)]{J~WkY7PlC|Cȱ`%4٭ah4S8j[俜@Rmg}U %<|(q ;W˴/.ctAv^#f$H@x!IFoN(m~3X*"b' ᳽o*${hHyg3t;ﯼMA8K@+D<yǁ0_q9{PлlL0b*AYZ*z%]hLWj&>NW; 1fX>fA']:}3x=Wk^47 0h.=lhHb[bR6-]x0E3J'OTuEmGmMQ_A:}d. E]Ԓ 1p`pC)Ԟ¥i} iĐ?8K~Du1aLz"~ߌ]˧ZG4r+Ɉu˄0N7dh t>\Q=Nu(*HœG`u =ˆ!zLE8RR=;ǧ=Ʃ:л ڮ{{$ ;g|{5 jUosDoRQറں`# ߍt'0 'htqfZ WzzN}CR~+Vϓr/Wx+:6jRC=f-j={ p}T\b-Z-t_ X=Ξo6S_tԚRA$q" )pjpd}_x3;h87^`OTK^VQ1˰]x_tޡs(_+#ȵpqpf)SL>xI@p>pU3D sCP"F3-8FmfA@<T#)NMқ_8`RhD UNG> ;tyپ^3OczM; wY5DCowbh;5F]?CU;Gzێ! BpyǞATcAThuw0#^7)QwWBhdMC텠4}.LjR/#@VTdE<-p0@iO^U>#i7, ZQ󮝴lo0,Srtx<8ĥxĝ^}/TUMaՃgtF R| LAH%DZ>~`lSgk(@Pphf(Ø` c vم:2@[;]^nA8%؝ Ws!411'$IG(6]ow4̧t/iR =Ql'3Ӵ&FcC.%9V(\Y Ad NJ?cSӡ; xLQg:wq.&P#N@VϬBMw?L Ewc!(Dpt* uFӿ8c: =A:*4a1g=:zvUjl)zqc/ *.ǫDVD5Ϝ[k>\)Ѕ IM"ࠨ2~`.=z\zBWE;^qoTtF*[{îSXxL`N(m VG+üahufژZPxqɒ5akGB;!qsCwUۻ#PNa_@l? G!ւpH6[0 Yr^>x6-E<ٸR OHm1Ǐ\*DuG"MW=0p@cL9!f.Mxu.-FT ##$Bv\7rqt)#tPT(%cNQK(sAw B<EP{ӝV$X9a͸˻a"G[/23bt8iNc6]|z 0RwxC2<)󴈝cPO :Qh_F6|6xѻgZ^:d\=Sȃw^C h71 yanNDSJܙ՝6/MN^=[J \9*I $;}8 \n1j9щ3ZaP+?X=KFf腧*4W~xͶT7:epVw|bWOu@: !?yzQ@˄($M o:[I9G7\I/f *)\ |@2ґ$#ɊCD102YGA9LZ"*ib6"v;MyBiQBy|M>)hN t"W`t)4ZƝx= Edyc Bg%!|dsJ/8Ӣo?jһ͹+Fþ{yǗH]j["A=#,4q4 ,zňko JwƄ9rmv|OqR͡l[oߌ.z\џӚ_6N4H'<\R$,]RW]<~7 "zsxbtbN3ȺÝ/u[t@_EЂ7LP4AaA|~1f ruM̟X!׼&ˬw󅚛i^mz_|^['gqw].9W7syPA؊n1#2 ؂$"C~g)Gw|*Pg)SIsJ]M ̘i^>Du5`=@yg8:|GilQYOcQ| 0z>x*Mvg=oо2]QV\ {7b*Z<01 9g _D) CtP$i+аnaٰ3E"yoȚ*&`]U L鷱 &HB+ .0+L6,!s)>f4 ;/mc\xXʝ {KBC96mwݷʆVѸ 58c{/*4jNVl뒕X^fNuxA.D7% -S 5v $ ![:O5RGYf6 @~&QF\(n-@%έo0 (9 sצZr;FF@. )nufn[EQrB,Y8xAAZ1\erJ6t6>bsRwÀ!B %@K2A6IH ;us_q-W@eUbJp0Lz[4H`@rfPbX(N4z7@:Za Ʋ3@Qʮo? ~8 W,e%vZ> Ua0($;t:%\ ;3{r.(AVJ ^7TʿRazG2TM<6%|(گ@pGp&=h:o@̂^ƟHnvs.+x7bIjDN5); MAfi׌nQο#5`T,ꊠ:mMY4Ʃ1 3B4=D`A8.*MYp$dt\+,jk]haԱFg_m˽j I`SȿېEhy Q[aK7U/9*f[J]"B傽'!k މ&D`qAGm F~ԺGOofVGu_Ggu$F?C**]/xJ2[Awy&wYeEkz̠S4x$e<ߟ3YӑϑýqEXrưX\MTccMc!ExeOP.u_H(AT` k X5ټ_ SH1lgPB6!lОj-K@%RiumԁS_`‰C:t \x" z5FצB-Ox=^G*Y^%x2~xN{A<#6Z0"ZYVZ-@qq0q #Hq%r.&RD&"biC+te-[i i@ϼ<ג(ۃ ,x˦7/XL l'IPX`/Yv`U r9B_KtS(0@ػ1x"?p'.9B|!/%*d z0hlKg$)N zn sѠd=иA"ԥ%T0( A$'< B;u5X`yԜ!q7vq{8\ ;JkR5aq)Q&V* i.@s[R.p^E]ZԔUtQ@jpʢ [Siwu8'NgeTI@!taXG[|M^0oUL~~ zuῇ4_gy^90J ręO0r\Ym;^7˖BXp&msM\.sX彞/ ^ufctD=*V -w=7xgO ˂ i4+LD 0UF گ:L)4lpy9 n祇g`l5l .€^#h.( N7;7#`aUuu1aYwjkl WZiMVIל Jy( "3F@3\vOCC`q3jh^^)Oc #o7TqW,ɀ4(@V|nÀl=vr7$@ 4dur; ;]ib% X?,@ ^N97iەݩ{. #h4nq{VPaJG]%yx`F-4q!/9`K MuWTNE~miiֲ &NF`U(wj[97)VnRQ6:S*HfG!6x*=JЄ<n+ԪT`dJHlzpeæMO}i ' \1,KZYww+4# 'ֿx<`f)"* 4LbwpFxpUXVKM8N wx[[{ n80zq.7b~1.~Jk=`6 eg~0T`-߼wEyd*,R"0d+DZE*Arsg,5@Nu~17'-߻L ;sF4Ϝܸ!: o4zW^NZX6CpD~h( }( "T:8zv?YS RxxplsnD ѿ58;M{1TF&zoZwTNޱJO kze|&p6q5W+T [}Q BDn\ R8pp i}fSc"_fH[z\%4mL  G<IZe_{r%1x5\q'Q 5 N%ڔ/0tQqF:Z48gC"lahB7e:ZUmָvķ1>ѶI\i놞Ҽ\Axc^<߳Xߚ窹O)sE^ =n6yc) mxQioq O&ͪj"(6b7J׼nx"α$i^_)HC7&@*! .k#s6)jȜ:^7\4<y:jNʔ M,NJw)wC7p{o0Mplo^[ji7κt8%^QK*Jrđǧxt)$׹lfHR+jd4CmLZ[C{ɯ!)CRS[^d/>{!-#@- 2ӣM[_6y(H4wf]Lr ԧSθf#·g}{ϒk1R˿4>f4Y(."++/ 7x˭zd8Dً@NHϜm)ex1,+A)P"ЎxcCJ`,KAI7htPE:aVm::Pu`jOŠ"`vD,U!ŖW( H[=p ܐrQ9rۇa\8>э5::>zD4pUqX)%9PƆZJ`VXFv=xԷN[=eEQqE+OdEnJF)-a qsq<+ѫ~LM_(OM'j~ = U ʠGIkRz⮤y}w󶄦M7 آxd@> F_06׎fPSضNlB}u(α)b czmbp*^Z"^t iEսWE]P˃Pl%'+"8Yڔ\98w '` 1"a @B~9wi NGKA- 45nuR4teO܄ߌH =6,v x,.DŽ]ytX"ל(L5wfF~۟qGIH.|XDNQHhcsur>EO Z ־yuK*P#}{ܲ(R*+;S e{&uGN1v];SQxR8\v;R2!tn.p?>qW1w|8oo/j#d]b)E IP o1W< hN&QgiҢ3{b Sq@#̼G^\Ǭ`SOϼMi.q "x~> R*u_G Rl#k&ĩGM"B d1W&2CƤ\iyeCc;=aMy|- bݻa.f߬)tit j|?!pN9xۻo@52A,:IKg.X-" ]i SɅx"uMi9[uϣrZhq4тE6<_ UЋEx4.ţFt4 tvS%4(k߬"֢5}ɹj i+U|pnb  @^Zbixׇ$.  JGZJh q]'ˏ`G-)q'bh MtNv,.t)Nox!Nu6  <[I/'(AlMWo@*J ~/!"lGbvc' oLYM8N7 N9=&^`]_LT1;~0rµQ0l%ކZ!Su\ N,ẋDwY3ĘGF jD&*bX6\dz x~;j^$ZܳP1$< H'C$br7&9phy;+MuJEBqu2jAx)QjY]Pa:5"MSH&ʈ/ @ OT;hguG$wA(T_yA=v43zfUtw*]3hK0y~GiAHs SO"w`РsC`#A7tC.?uu8*9tP#hi6He 'E6 4͙S!E_bZ!Q)Ьh䭸V X¶tu;ѽ[+)XMVZR6: Z9+E(i%bt`k) )}`Bx91ՔQL iEI abM yGnX+ygZ$o/8 ]P433H*y~ 4Pr* Vk VK˸a(~)|XPOA2O +N,+ѻVB!9]ɉ'ؾ?@mvW_,ݸZb##ަ 'aw 6`lp*moL g0:e>%=dJTs_هt$ޥ?iއ= z Sh|_بh|"Г%=34|227~_8 <@VvXov}W4: IvA&(v= sRp!]_' >x٤R8V8 tch'[r^'S V;^"}_\L$g3 .k$PkB;q/Gu:k(4sY2UDPgm9BFؠnYNMwуf۩S%yl?0+AxcS7 ; siVU;$qv7&W3M`z=a|1e&G" E&H7( 9/AWAZ`HB8; @9hv@Q?zA4-G"M{#dו\G+@>`* v {N \H֕3oHyE; `:v19SyXk#VE""uDU ;e42s *|`[>0KpwL`phi LlAPAݩ^,pv%OO$`[рGHj^8: on.X*+ ve❜AIwj0(<& Ni]ԁ4UWN(P $VϏh8@@8E "*>V!%0RnG֍b(5"z͐YH(Tmާ8Myy#&6=a9ix(oo0gy!GE0= SG3Twt>+ybUj!Vp{MX/3 9Op+.0fioL݈,ʹP+z*z1- # (t4w m6=T~#\SNEEww8'z[ Snٶs^v Cuu 9h$}k;/m^l j4>mY-4y?oP$瓓#u(~$@='-@fDDӅw])N)KCb(ES&-LRF 3CN8sAY!!)ϼpG.9d 7ytU]>p%݅9#imj HYYJ ixX7Quǧ+zRN8~.AFW9ɯ+㜊;_8ДM>VD \A;Ĥ,u((D~3ȅ!{; UqKwQ~ƄADɜ=  U ":;nFdd:&`3@b0&T>< hh! em#J_yqTn7rN1Hv Y=h:սSjn%Lc>N8] 6y=|0%~u8H?ߌuu|cGSgOXHS_/~? TS}FA@RFنB pR(|SP&O<`U]1RD˙n-D!}ؚ<Vm6e8Vu+ˎ]C=dZ!OƽLB CI)FW,KFٌ.mNJyvޗqS-6rYGú&& /7 RCDf4K;f ? lfDKx͐pRrԙe ,Cш|sҔYLTӈB;yJ/EA?;H?Szb`0:6sX_N_[H-j_Dx%^]hZz,9zgx ?[#BN@_2@R)zmٽX6-z1NQk}\X~~P45,o[Э@Fl.=XTaMts7`G#]SX H͝S+vM-DE+B_^9SV!Q}>4v t]Í\ vJmS[4x?Bx 4BrQ)h8^oY4RNh* O[qq/$s8b"D eJ﷞i Jֵ79Ń8\6xtP>N~sh[\8`U Jq+'|)і2*^dO#>pW2k+M$EN9V4XZEX A*tv<"6(M8E6ygX`Ѐ*\cSo-ZCӬKȥ|wV YrO86|x9Cu.|?r{xћҊeL!(i?rIMX* 1lsr<6*JGи@{UdK`9~pP:9oGe B(;x:6T8;ҵC,֒|q7R*7o$ h2Ç ;C×E|/V1 yۯB 8N{VA SƐ+808;j2~- рx D# 9.LVl+u%h;7CE0TaPƊ? wqpclpg [Z\=:+q4 }q`MКwJ(Mt_ȭŽ; MVowtP7_{1D3E\.ڠ襞LM&<{DEOቨ y9ibԶv]ѯ] %"nLd{<™IƿLAt:̋3! &=oYSOsUrHBA_rB*x;z<;Ȯ' }X Nu ^p SfaeӭA֍blGbEUY]_]ۻܮ21)Oia+9(tߏ99:ڠ^vajx& 6RVkRQ x4B'X T`] Aj !S7_=tAmo,z~ok¼Gַ_J`  zzr9^ u-%Jw5ʎB;A4CP(shҦr9e(Pt\uv`@ioX1FZIR3J1"öI؟T UQ@i?C(ty@(0Irb yٓB@}4+iXnyjml^&W qY0 q\' <G7 Ij p]٧1Av 뎲Љ~p ܸ+D}5S3T8v}YhBDw˨7MAۅ)Fdd48GN1肔vBy91yˢ0!L=C#=bcLR|hOcak{ַ`pM@ rxp w_0I;y!vMuۀ ȍCd$޿4G}1 SGrD$h5a]8V3.is~OQ٤7H!Ŝrm>@m.cmZ]0jv^Yƽք@'/ayk?8$jAKKz>q֢ ;$P 9إ)-(;iBӁ ɶp!$^a >EWoXvMV`1GK٣ww6y;4s6xK!47X[N>up&\u[Ȑ umֵeJR7W AW mPx4xC#Pr|䩡G@h^ ޘȐ4oY0CA4Ml\ҿf/YeTЫ}/A4 []'8nl|I;°.  EYw"aE+G{ɱ !hZkIqѫcBEQvP<ػO+Thm;Wn3ig`0&z5@`n7Z$4v`&-4nF޳Y[UMb۬P'#NWoa32qqF2.sjFj $٦kQ3b{[kK:m᝿v<9X=$i%م5j4lXCHXۍeMlw2 v^x 7Z/:EἦI$ [!HY{?Ձ*4M\Zx3g0z @:@PMwf^~ BǝW^z0޹TR3޶.x\bZ'B׎C`<|oē6s0i"敚 |;[)A*(ebu*(5)93ր* 1m2KH5!) m7qYқ%V7<brˤ.V#)6ݾpmq#{E7`X'Dy,Bp`ɥ9QN_#ۃ 044lA,Oi")$dbjEiuζ&'証 4šoϳ&߯Ckgq)6c!Vy/?іAD}K+bh}sQ}aбP:BXN1 37#hX4SK99ٱy0+ĵι=DR][.ܒlŻf Ҋo) .rw3 yn5tF++ %|p'h6 LNutp8!jLJXV4aA),9w0#_EJ]cEr;X.N !0g&z5@۶4RӮx>z~7xm8m}ML 8׾;/05jz]7pgp~t:@貴&ƔI3rNA(jG\$/; 4-ɑip-\vq)P} t/B1מ M`tK[t 7[ÍՠVD,*@*D CW ;Ɒαuc% mq%%Uz߬} 빈"1mw׿0$Y@Zr2B3T@u|b3z"k+АZЏ"eĝ(<3srcB`I)t Jюҗ4АDlx y!T Wk3*L[ 2$pR,7XPv-Qr8PXF/7[t j/ccś )RmuEb1SA*Ң{sP*񉸍Iё솿w=66r8 j{A?y#}YE untm~J~1) k񫊅\? Z+- h%=y-q1~_P8yqӌ̊/ֹPLG.ϒO*}3FlAg~7IE/\H.y&j\?H?.MFw178F=% a )Sw!VM@쮦XK{ !:fNp綐8x75A]GVIM^?/xjqjh~<2b3U_xu +"tɠbucH tC]#lu ư\C}= @TӾ*. !pmj@P2/`aIWnwnoiSG>qhiNOˏd)P3tۚ xҨdpnI X7Xw\b!v~1bqBwІ%>mX"%f1r;b/'oRwv˿7fDw?$RC9?87=k~2Yֽh!KTKY¥ pA RK8 zZq(x W&&R]a1]:NSJ(UȑO$^PYxCV9>9#,^+mTEv$Hל$ 3i/O\c(MT7]ɏh# 9y ])^B%|8M%;;-PD%-qh7 U&[R[}aS-pb!ŃDbUSDR_Ne@Å9X0$Ƽj͞QB+UhyqrPmtQh- ^Xw{Š=&:yFT$s_ⴲH;ۅH: +K-9bPhΌAۈ֖*4k •y^1buҊ] ܬ5CK@op(7Q;4 >!i]ean0l!CqUM5;/@!,X1&϶9 4QLAx.+ 鮡pVc0=MYfc2Pk}}Z;ŨsI~eXF'~VO>Y:x`&O_ BK?>%yg3=4@qFb_m"! *%-po*Rq1@%cnpKȅ>75ۦlǜ45M{([W.U0Mmz)v}HA|dW<Sv'bhJ)Ϛ6hAɱMhZ|DU0-k$ts!B ?I qqfwΦ="O)i3y po< {^XH1$J}b1 h؟Vix*>2)D.d3"/"Xv]CGn&"i3:U/%x`e0_$ۅ ,+  Dbiq0#b\.DpktVbU$(Iym~[@* l+޼MLMO/O?piL+]t!N\LXR$wCZ2B4Sѫȑ \jꚖMcsE&„ЖB>Xrtge!X^jlNf:g"2fo (@Qom@@}ᤚ&{! fi<ξ20Ui!bgMu7]GZ眀:HuN^H<.Vr-ے 9]tVQd/rKȋ+z.QCWPPs[U\0DƧJ_!axymM=f΢MA~q+A[ָsɈ.v$8PG\ JpLC41`߭7 u +Xzkqq@iβη.!]A,[( ت[=E=:1 H'U@rD#q HNh[ [jWmhGBC`M'* lKd0 +</0<v%>@h;ZFO&$ov7XL`CQOo `"|2S`?JY%6x7 V'e\_b%65A9_8h9Mtq#Vn\9C!" ɮOmACA-(pd.%G֘bI=Fz-r>H,|؟` Zǿ< ('tv; R77V]E+(gXcj7^lG-wCv+hx#vP̲ZuU6X Yd)Q 'dIx5k_H|= ]嚕6ޖX;bhgg(1hZEcM".\!.Q&#9 k-?K1;%8XUM /#o { dwF."h0&x ,C$XUt!P.ZBիAhӎxPdD]k7d.]N#X9Lh+)v:bvs118S lWw`r9:2E_&>SRP+IMƨ(*/Dw(Xj 1sνb yU6|>ym^&n-G#%`fwX0ŕ rݻdSm_%ĨFU"ڢze k/zHύ,D!^[ķI.'= r`wQrZ*s_2h+xKα F-KѮf\-*lYL [Pj൚Pw|1sj o+kQ|Ut:,b'Y!\q+wPjMpy#JM[X.G}%g"R*9N M WG/Z96kU;J An6mw;ٕ \c'K|M6PbXNϬ`NTD5"< < :-z|)E}|7 rD۩mگKjq<w ~wFy҃{a": 4gLIǂC`f1W\ַ?0H[۹Nf^1POZǥ:/'%ѮO@Q xYN~:SP=\nB :8=iH:vX1D|kz)Kw"V#c,0 .nսPŏnU'7ha괺Aֹ QZ?.V '[Yp(I:7ѠFppbfoBb<Exo5MN+^l{f2;4o~mYn8GTv[ka [o rʲ%k߬]0BYf<*]C U&3ghPnґ/x)54sлGk=L6BEExzmQ8Tg\mö譧[αSx7u"i{»$uyM nDwPS<up@ huW8xWHM :#Zr/9M Pg01vr=\sPz̡U[]4Nɬ C‰Lp'l^_A+_s|WEa3C?E|((`B] :'3%h!Rw#A|a+µ s*X ޾QL 2YV wzÓIIBXu5Neѹiv{L[W]1#29=Xvi~Hg/ KG$gRFvQX4@Z6^[)0 ZEgtW缚Y?8wT$*C %w$)^nI&IÁZHp{}c@<:tJVu-me۬}-8ml$TZ\Cӭ *'#{wcCFt~q~Euh uLDmTx9ČW~ U@yYY2uUr!I8 O*%( H\V"&8ŨMMk_ ~ $*^"~y|ySzˇz΃1bU M7!yº͝ṓy~3fh) 7ӝN|omEk\5η WJOMxMA)T^r #~t~#U/LsYMJlCrg\,k]ӳ x'))+ L$#-7<n!-R ߎ8p !G&Kᇼ!ltӾްRlhfe|ȜWSҨoy>4]/_ AJ6ZDr-PI4%!un$&N{/p =pq6Y73&:ۋzHT`܌SJtC4}&t WPFq GT`bZ-)-bkZ(֤TwAxHEO"lZ18yO@a9,z,U}[5<"UoO!&b!_£cP #q hx QHf3ܒzEb4h7b(ǟk-IaXw1#M}~6U׶Z"2]*ƝqU_z  bK.h|Pl:٥Y.By(zR7` *e SasS$q`A15KGܚYv^:s(rG9.Uh{]ܴe!/(v_$1l;_;WHzVǗﮑWi'l`(?Ɨ IRs~<4EDOdDu6~2p`ڒx G*DŽ6O7njׁACzɎ(9V뎲jh?>k;JbYJ y[+_F@40(~p u}G(?96η4+(Rq3Cm TK:!I,$t*zdkfOpGDTS*Ρzxf%KB4MLv W{t>aPHڗdpyhTѰ{ߐ!R-39N 84&\68[Z802ywo$kg?/"sqW6S>jەk hu'Y}bOe{Xzs7kȘa$[hlj^)3c(J|Y$ )Nz2/ spM3Mp]1 ӄN!嵇PA+vq{H `% 7RH! ^*P,[uTn>{\:m p> $%w+^T㫑*;0[lfr/WP4ACW^sO|ݦB “4fK"m56vqRz H m]|2 H7u L;^%4qz2,Zbqx[F)p4u'Fo%GmA؄G5a]њ>.(WaoS!Q[b nYI04sL A]sc(x/\/"gj V~!\ I;x?fsp!p*i:NknqiHl⿰(HN8{ RlL:\&ʿ)|o Fqy@{Ţ 5}' rST<.Fvjָ9*.km+v,YC۫ n.=F8 F[<|?՗k / zyOP缽z(+(F0+(S;*\Roa,KF:Xa-|oed~%qLz_HcOW9RA8%Ʀw+mj}2&E)KvHr'o tri-cp^}"Gl҂ j=|{zqݧ a$(6\|(uGDl~Ic8?8$5 :NJ굱oϜMdaxA Ixݜ+k8s]SDMe7I:zj~rQ]]yhPu^w yțRzx&M7W)W)楅BNPYl7\$(@Ml<Z @8 C*@q5 sPѾM Zt{?Õ*3O@ɧN/&"] أ9y "TZQi0=B_qq4I+s~qdix޼I폽 =58\:4@8,p>AsHSaQt0SZ5QAkCPx x ;|Bk`IE#@/q3o1ҡ8!}6c7,NSS!p\09[\ˣ|*<ЈtEQ]2~bn[:WUNe`(U^`¥UU葁ޔ'E|NO!zk~y;Ҕ8Ca$8M.!%h]s0X 3H?뢸u;4Hg6)͆6Oc(7^g3L94M`>\M`=c.LѾ%Mc(q󋥸I)H6ݩQk]Dߣ-gezl`l@z8U7NęQWP8&s2 zBQwd{5R$T*ph :R:]$-eF%fQӜ;G3䇇*Jh";!޴I4CZ!BRBýuL@ p`JBPm wqjBxM,4@. < H,:{q&{.EwFxNJSmyDL:༐L: Sl(Bl>lW`F1j6\em)HlkdG1Kp_ʶAmF6C ByBÜFNT7iN!gsSp0 $UZef(t5P X 10v\^e_O(x]VvE/Ρ@yi'KlÝ-!oMZ89pX(]Ȅ]=7PT}Qpq˂: gxQAJ4t ;c1rB8r*3rr=lPw'01ӯ;Kfb3s}cFZQ;I/qP0źC%dPB!!@OuPiNbEU眲fi~} @Z;6 k2@"Bt8R@ ƺ05`x! sY*^8JʾrA{<[C%l%`=qʆwěM&:3U9;ޮ.TC5"X71q@Sh( k2l./#vw[j|a5JYsi)di75 V9xjP/:r>֕EM2#ᅈzY)= yu=kͼ.' oBTc\˴ zl2d`6n}`4hWhֱ9y0HJg!Մ yAe[<|$Ԏ8m%9Cό7AwCK1#?6Onއ$ BZ|l~H5=]-Q^ ?I /^v},!ҵdyP@qÁ?^L!yQP=rDKx`[^ciΪDxqQ<.29ĩGtӠD"1,-Jv EF| ުAcaykp:FPpk3 h> rroB>͍P:;;p lBt,"ʶxy1:nA}&)$GԚzɚ' kPmA;̂szܐ-\CfXer{֦4kvg {u多\4BJ w>p:*{qNQз+P)U*"Ixo* eQӥβ $`.2h 蓿6:fC+bońΈg0R^1H 3#Hia54𴤁wB- po B/fhBa)Ѫ4MMxH 2誀p ɅHѦ(bJqt#Bm(@ݰJG|0]=ֆS;FN.{6jSKg&fIJB½G{l. T jƈG'8We>sI KazH"M@vl֨a+w_ mS) C|~8Jp%,91>~0: / }OL<}!!"׫2Dl6^JE zT VbƱ(ꗋ񌝭7=oigxGф54ďHOl:%T^0!j02WhD{6АE7f=pδrcǟnlpafQhB?(Mu) >pӡ=gwUk4}a&*'z&xqbBDQ[8>U515IqqX] 0Y@OU$c@f$k]bCD\_ vx*(#LB@0~Hv;;Y b'hMiƉ4Pvn5(0<<BM,=*MCr' u&YO3y%FTȫ@t&gev4w?X˰w F#y8gIݲo 76/>Pͅx\P"@7HB.PwN\$X!$ٮὖ4ܓh#O62Z&\yfB>`+}``C_MV Kv|sF5YS]| U5Tۋ@tWZ@<y#sLkps˃lDo#7m$'q59}=!hK7Mk_S m|`bKSCdv}㔚NMr@ͫ0%i{i[ZR@3&(Jڠwى%X ְB126VC?5v 8J-q fڽu,<;4 @Ow!!@NՇ myQI-4,!_-xc‹(*]3dfU[_ȜGc%GiXm]0"`~,rGBNK*k1LEfHzzmُJoABX?9ܘn?lRce,;&GFn^1f'Pǔ 75>6UچIu0};NJdMLvDK+;vLM\IF?8Hth7tuCD,ɧ8;<1G A t`vϼT+owh`~]h H񛣊tf7D&m\<8Ʉ|@(HKIYv4+}onPZwm7u* ]-!oY/::WTq5PQv2nPc/+vr,}z" 70hShW)ƟǗ>1NN/ CSfu4!c#;_*%=c^VZ 1gP@ҦyqyqEH^xРȅ4Fz$8LHVYӋ ER0H!rQFGfr<মQX*˾-ھeg?xN]coSE3'{_mǪY ؍;5c|,{ްc/PjPe8vFF7q҄ 3(M߼d&4[S{µC, ?x4a,L`J؞{rr؉r[N52Vw#F\<E/RwQ`%/) nuN"~ɜHƉq< X4|N=( 4tkgr!x}.BM|;$4p4'-+P['rw(]O[U[Y >:IpcI7mi*.?LloМ'\a@ZhD1lӭ;k+:r[ȑ@Hkn+@S|񝑌q[ N@R<|!6o.ΕisMkS‡ |=89 BuQd<B&$V`bXO" 5Q$̳a]!m5.m-knImWQ ,zJ'fnװM!cf "%l+9¢t 5kGfSkQBi\c cn*ҁ!waA(E<Ñ h_^h.se6t#7qE]R7+<H\qk}rh & %qLSÌ2iMʜVo[wXTh`ę7 5PoJʗp @v ]|tA燨 :Ml_8FF1c͌ 55fZrk{q9f.89JX]L ֫yf1hTl36y4pr<{4\A /|s Uĕ?%H6Mۉ3p2 *u/D()|uP۾m/m&n8 P_<y$oP=lv1~tЫ_gTyrQ D-(l4z(`={?8HN'>$kXx5p/WN_;3du{|劸r77*M5;}EETf3vo[,Eq4u/(<>;M(X?x<ā@] :q Н3Y6.х:HWߦŪ!^kjN`ȏ6w!`gS݊4 {BU8׼_ B$:n;0E6 j"iP@ 6j>P5XuC)N+gr(KT] on Sٰǵk sgv|]{] wq;r %_ĩJ:tی0gߓ5C>PdÛd#n{yoUm-SRGo;X-|_Xsł Օ+6uޭGegƒ֜OǬ3O _sSuKΩmHNonz:1vtw0uqȻk4Uƃl8JaX WrFv.Q& Zσ fgW`6ՍreT (iHs t=sL nQ~=B&_] i;! =ۀ%b:oɵcDcϗQ`4ap:29Ecԝ &#PEx\#{E]]i쀤 :; ԯ.M"m~<(:l=mDZy$\C̿xpe܋qZލdoJ$ yɇdQͿU>mjlZ9'.{'i+N&ze, kt&G6<W Ў\* j ;DԢ8@ no,Utq/ꙷu\ 8 7>bml_0h@jb,IhMVt]6 (b-%>:5>I_֯]y+a>$lkM]…$w-m$͸ ʑ H R»:`ddZl'ҖгJE[ێJ1`o!< yo`U"';ƹ:6]% vD%R&cNFBl0 ;T4ZGj&Ẃ y`$։ށ%(t`$U~Sٻ\eRc7ƾ8 P&v6D \deLm iP樅:*ylxY[Ot [+/u{,Q9&!Ha'ɱ"@A$гz î__Xhr*ۥBltM<˸@y;[[3)*p(=XyuUxT,DP Ůd|cx<w@>I xm7Mug є_c1el*-Of\fvFv7ɤ&b+f'HyRg']%~`ja,Db rd)S0Z,7 Ʀ~h× {Z†Kn uyAۃĂVz66!olw t;* K𗎲]c>W$QwZA掹E(~4`58K)]߁iv,"kQىCIJz[p9:vͰC6}$ /H}~: b1g#77)~$,! TƇh^*OqOͩ ⟋@]pA! Y%D p6/ _J7M6u(E)#N:_HO8UY"dL͊P:2I uQG(JqOU#ҨN?xjl).QgZz%\p\x-8RN 4־Ikw&"bsďqʆOkA$4x;&[7θJQi'r2Ga[gHmԆ>4y5=}ykã`/5ˎ"-NQ_.,-z; /'Sni0_,64|f"rJY7z}q(lhlyͩ^E]8S7 eO_}a⚦R"zWa˿PDŽ}YԸrnթynjyA"<"_rVJ\XN p;^8$UBl WBej[ ^Fą@!.>񻧣BBP> Ry5ó714PURVFi<>zCW9I.dž(tl2),㝏6.k4I̒{qI@+/_@, 4&kDl֞p,UH{Ds}Mb)#SN/¯ ՅԊYcU3b_%p dI9Kޖ΍T7lf2PDN!c-sn:mrd Nʏ㉅i{o}]aVA5OsbjCw߄H6Iۣ'!510QgRrd(cFfF3ɦnc$  $0ӶQF7>jH6%t5?Ɯif<6npdAܛ2X\yȧ}T<2fိM}bUZnwpD(E\ @ >{Ф(X" 3PhY\k$*iSc0kxgu˒1nD66< [.򤨓ua 6ki>~B9eFn]Gfz@5c2QPuv`v!d&ܺڨ jZx8U@9Etױ>;45W[wӕ>uϹ< { ۽:0ȃ.iyhqX]MI *XhCg|,+arnZcTskUTj%,TzrpQՅHHV: 1#6On1X@~[z@ rR#̼n'7yY<6]nU!1tx:8-N iJI'ZAl7߷4' |s8Spr(凪eR:xYnj7Jsc m9 &TX(E~MxXB"_fHh|96TkP_ IX 6 t Ӛ@y["A86+kUhbj7U ף$,B<ۉv6]57zO"TEFQQ֟w tW*XpBH i}\ Vv [;'dahjvb|*mq6W=0=r1\Mpt@xO-2vh'/)1["K)gzhQ8?.WN[㷂I4<*6k-^JZ0]i~* E |wCm곕k"8Dz,|OA%Do#& 5- o7Biz}fu[yd8H(ō?ˍ[+&$@ i9 xcWg2k&.Xu`ӥ֞=ҤΆ/qS|M'0nvA6aiH G((Q<~̝'|4l;Xbxy1H*@6,M@Gwl7ly Р|q%Ro1MDkt0 R;rh`|]b6@?6BO6MC6\yH<3AOݚZڟR @Ӆ&nCJ,8Pn5@t'Q@ Þ80%n~TX+P72AvճpdPt,w>?-"R<*6]l^̆;`4t8,bph Zu;4@*?8Ȏ [KЁ|lů&U]8%xA@@Iؚ5zqNmvH䁹/>ԪeNAy'wb?']Їtt>qP~[ڿEQ6&P = H5u=]#t܉`4O+Jb%kŠA=d[֩Sj'bX C[W΀Cs`q-ٛMjQWl8 ʼni8Ay<[JS_x ءk: jv֋Ǿ 5@(^G})"53M,N_<-1nQaǏ]⪻L:&Sx1n >g w:M%ld*.&@*?DJUTq V-fm'PXFw戔pӷ $#NH=dPʄg3pC%^>5XKiv@z=c~$(KxDqmvkf\/}~3X+ʄ/"fk"SWLKW\AIk ֊c$^]Rû 3Bhk(Am1ё 9*;WyxƆ%o|뾷0mx1NŋtNtHu=-e;9|ݶÇ. @x;zmu#` m:Tm"ֱI@vxTr8`B1b :h@)э&*ۚ9 siMobPr#9D瞖| '4"7)|EuyB iݢh16AmbZ͈D_;Բ""&W 7 #e[*6&FDQA{V  ^1VA htz;Ǩ 4/CјtBPfRN[P;!bs{HTrWGo~@Q~"mδx[.;KEf <c6_1Կ,P:@jК_xds"'P5z%B>ƒ(Lއm "}W6Tt߳am; 1t"ni۝6>V⊌g<nj.-xS%㟌D,U֌kvY 0")~1[. o׮0($MTDa+ =9YK‚Y fn1R,/!J;kR PBqLwbfT {J@]o]ԟ^Ex|aPoG}^2C7vTznpxj|Fe`'3hUO5⭔6-&0@=:JF  \GNVEq͆ g8-+2[gz"%3 ~  jݡ(< }N?>CEM9s>p9 IM=@,9zleA֨h﹂ )RZ2u0U|~:hY|b/dnUtkɠh8ِ;)81"NMcgu l x+[x4 :|hɨCg<.&|`{-i$vgfR%Ai8KnTg)-c&$p&]RP,"tqWho@'| Ƣ "bkzŮ$A0kTDK fɣ߬)YeU:2P^NMlpݕc4H6޸p"!/tbsDm6{rLDBU}`P9W/'lR4n字6UIuv0{oDDk^[9Mk~:] FO`ì$xVi=aw8ShI8J(FxM'b/4@qb)DwQJCN!0_,9Ɔ9P4;"_xkA*6i׋w,tҮ-zǥB;9'%jPlQ.: '+[igyݵŬ"}BĔM8PT8f )1EyCт6'8Hp#ry|DKw[TCM.7zzď_HqRHq^S)eL{v;+҈z r{͎7˛#=-9 [=(|7 lzbGNZdͯkK*,O j 킓G26?G Am$iJN83\p}l5 Ǐ#Q;;hlw QxC`'XCx(Z{ 7bȼ5 mi5#{"⑙]O$i3xW\0gXsy=x3`{xX wHSx(#N4Y,,&v% AF}`8יAB! $u6*hw-ЍsKߠmE&vbB 3Q97oKoΠnSr>0J^>NQ[vzāzpJvmC ֯5yaDe'wV#bNj')i}^EԞrbGgh)5˲Tvm_\ ]Y>0P's!!vs{r&{ |zbX؂/Oso hQ,!mˑH?yh?3 < .9vXPKްkjaRi[ {Pw{#h)˯XxCl_W<4|8^Nnlr&5}oyJLtQ] 23 iMdvh17^plTXHf4 rzɈ<@jRSWo !.ކ(x1Mqå9so>{I5A$xxC?8pGNmw^׉:aK}( un^Csx 7BPk!bGz&r:D\xh؁MDix[SU!|߮;=ӭ $'D7qUڂ|ﷸeX |6nӌsA8$,B[FD@5._咧8!IH *nӟbI,yqIƍJ Oh)H9S BP ̿x%&Íb +Eθp1A?ye"We˿ybě\Qy|bčoKs~1 :w9Dih*{00Ǩ^^<$޳AG/_UJ9!V$6n ɛH#0KC%y޴lZU޷0L WQo R4޲(Rid &Қ4:i\rr$ [6 颤ZzscXuQ%"}cNp}lnv*r5ڣ^ E1Q^4`k1S=34iݠ^°5rh8m:mGe6?D'l7(f,[3PD[][6]~ tHwvփ +>fmT|:X]8[-xX*D4"Ҷ$ؑ﻽>$SbC\Q%S}S`nmОx84gx-U[X!.6^Jݯ̛.&'/{7bF "9|튅ȹ3 T*g%vЀjZF\AJCާ͏!Eq:v"Ɗjyȷ #[2b56xA(Åh[Y, +cr_'C{Y"$*/% xX!z +E} GsC6-o_(w/KqT'v+8:G;sZɭ4a kjkoIo-5RS~v>)G9v7(#kM80q;Fu͐Nβu@x0{0).hN5Z;& |l:'ٜbߌa(i7'Cl<#^ULMs xLVY3GgᒌűG̓(|a(Zx QrŐ`%aˬMD e_X`Uph:oG X[>kPkBstP?4SW%=$:bgxc-.ln'a%s24z!^wMױz8m-̙/s1ߗ'X.St|ui~y)E!;{Br3\臇AP8#3z⹀A_"OE2 /A1B-Ξo#A{3/N%:K_ɒ@hO^@@T '& yJRw_#h :s.4FX,ƶ<6I ;Vn]֩/;q j(r"ZwkˏtV5 0UVNUWW%d:!KK y$nH{o$ߡǰM, ۈqlNp)ȩ ~IE֘ȁÐӬ6S{cRrCzezʍm0y ^XP>nU/mM +~mӊ;ՉZI>7^\Zzg!r>]7$2cA7x sc=&И?x B|\ezC ԯb|R F^f'ްf>IR#`Ɣ0'`;?7*^Yc٫j4KjҥFD~ p'X W{[E@$+ĺrIE䭎hJZ"CH0Mx |uޜ+p{_5-Phs^|߂>T4,r2L6;|8%-D{2PԢgǜq-& pn:C65%EWAޱgNKך"@G()ik@m%An ^Uŝ~^6W*wWv*5"CJݵ;@$BJ?;͜~U+׼.Hè]8#ho0.|$gUtW)n5v'ɹu8[^RmrpL&8+X-O hd >AםWʂ&wKoZ1v6p_'mۀ?yb}“R)Z?86>0P7D!ikk)SOwXl*x)6&Ge.߃Y:+)*I޳fu !""5>g+C8pu&7JpUH$ؒ-yzۮGEnlֵGzj]1 [&qD_y5E#Qx ua t%\:wWy^4hwvpsw#mq%j?85IrTn8?z1؃rM P*6 +QxeP·~:9ҁGO:l0bh%psVFlqG`UJ@[bA|qV MyW6%] t5S v ɷcrl`#[]Iu\ONA!WS`]aqHs->,^99͇-%o7yɗE/OcXٷYZRlc j{#$%StD}x' Bׁf̒h֛-KPx2! НPN/|y@Rh^M>p@O* ܌R(,'A8VV.eB t iF fwpՠ,zG˸(4PZ~ *(&^b -|ݍ,d< 7-=AU;71wnPܰ/CS:-;`L+DVx *V)Y%vq (46q&g('E:![L&#X7MXGol<8!;a(DD.EJaXB_8)w4rI(6pDRׇ#Pb!ԎǖNDiTDg5t$p%oWWFCٯXXhl]?F"'ǯ9*#`Һ iWGS(8}i3H@!@ jVкWyP9ɪGt]±¹H;ÛX.w[lf74.CqMU׾.I7yZpjD5 ]+4Lv4MA0뇡޺õyɡK;8'Eq5>t`F㶍;xx~}CK6\] MxQ}c4Tzk2z4UE^[BhD7rr3֟{ U+ٱ>3^&Ìl*Z|⑷w:0QAW1iRߌ)1E(O/yAPooJb0R̜kԋϕ2EpwKGB^|`yE]u!2;7actXvXnڻ7/F]뭟{@kq=¤xa~ lX26 @(NYY 89neѡ$\8懗kpu}o)&xjpAYiZ,@9;]養 {0$(Sࠎ˼{ٸ2웯FAF:>\4_N @_`طj+(W28xy!(ݮU$ QE7?x]Ao1WHi^x繚@]=ـA#w8xBT{$W<;q@&x`l@zrv(b TXp"n_c)@g$4h[Ӈškmn>ΆUYeF4TPтAb2&u;yLи&QǛ@P`ZU;SG OЎ,J;9f5r N'i@l9oy$09'-a {%<{C h,DC񉀃ZgO *h{$?CN.N]XQ]83n=eְ{̝fst*8p &K吓]r :o U8+^\ H]R 3"1Mo.|el7mLRyW̢*&8L}9)v1| <Tz(%%Uf5Mm*F ]]LqTkP 硵V'&x}\do>p0@ne 2O~~qMex mr}lB6.ʧ\ӽ!u5砋0|h-^G"PtM9!uJyy0(8|߬w s28v-^Cl`KN9^^]JpWzK鷬:~&5:­w] t!3 .hWU穹)Eu 1T ][1ggɂ|e;/G+ŁDѼ֣l_ydia ΦxGΊ *#b2v.AнOXv}hU9Dj@yxT//n0Ƹ{F E _/*&]KM( yQH>vDM8MfϜO(~7F}f !'-& 6ι0CtB#*>Y7d#6a, fktEq1rjGxpz: ⅦT(4"%o :Ť~99`$#Q$^S|W s7Ðݵ}~yej:Y55;ØPmN˪`mX}c*"УpVRzP>OErThA`(Zg]Bsmb vy(CW{ a9bq5-C/H4.u'Z  [0] >NJ ۢޱay@(u( BD8ă޷ Ӏr8DF/tgw \R:#>XX@MkFjQKoR%˯kYH!y#UOY:n.UH}&64;J{󼕏ADouw<8~+|e3JӣXF%FǍ|quN Tۍnf|4]NqCM.2yk+DZ7xcƓ\8h!4ӓoj4M&A:73K" է*q@ ߅4s*`\,i(X`x oiK) }Ν :0CBi yUvq.YSTḋc.ͺk0R5!'bg7#(NK86AT0T*i )dF00 r:5aИ ;EǛRh:j x=-Zxi5LHZi@'U<7(Dt$Z)P4]fE]-#l )F˧/u#hNF|2l(ނc-fy*IjWnZP:r N wg,ǽj+ȑLf>pX!s4rZ 'pfC§ "`ԀU8oFǠ؋ (@cmRr [C91c W(J](D@_)15H35c 0tg]q`l6^5a6q1K ڜmO?':xY9Ҕl|A.RHv{OfnUx+X}o(kI ~uz5LĎY }^Ik'XH4'< Ɩ +a* kNd)/ )|愊m( 8ћAy͠~5 [d%&}񀼽V Rpl5{B@䂎$I{8 ް@!Du5@Hy[N nkMNw@!@/:޹%# [V*_8#KXB-']oT(朐NWP|;IR^Z:HZB7I ]tC}o7 7nҼ! j CZ3iyq *Mn%9Ϭ6@oQ:4\&6mtm=` JOo~@Ț~pDZrUǢb ÅFlVPV%TWz>\P*rWFgUwϗ+(+I {mZzY"`h%iqql Dee א!GOM;3ό]:C.ۢm^%{iC%]~wo)6< TG{dŴ8TbR'wQ׉/2 v7ӲuΈ'5+Dv@wzd&ϴ~9(@^cY {k'@v*qOITGoPinUm{n~ qJ.g "eyfO匴#wdҔMe vnÔ6AcE^˷ŰWf?zeQ&́R| (t |cա~!]tB!SB1q `weFUD7T_:.!JF~.0& Ɛ]؟Sy ^W>_*& B]f9w;#x:ˀ@X|eRI8s|X5uƹbaYNgc Hufl_Y<*'m]^ x@V|Evi~pUW%@962d{mُg8Rk1Wt:(ق2@oi蹧$ PM84oWwi?۽?Yu)o6QɉrS Rp(#;-:< 9B0X ߙ: x%*?L(:ʨvь54ЪwO [6&C9H菫Dp׌:Gzttr]#Eڡgz8b$lI9l2ѬHyшm@h/ӜFC^*vsa瘛x4ak`2 pēBG`1DxWezrm 4cJAgVO]`4ke:*4b f48x_8%|ȱeӱ}7JkP]Uy,=V,_FW_|U5և"^=u24!AZ"ޑc`kt 1.dK٣`qZa_2% ~ (Rs`l k|pf_'8m׼x3H_fq׮>x5㽺< " ,鴅C8sZv23Bأ +&$?T֕/d: RLR\ߣ[ q8A A-|_\ j2t5 (t,߯7 GM;4"1G5€v>k)S" =`kL)ku,ޱK^zva J,"%H f s/X( BG{ Raن ZZ<ɎiG4:C;))w:VdЅiǣlqIӶ 9JT 4o}8$yb:.ﻳRHv`m[ח8aJ.;m*zV 02O1#Ud޵q&DcƬ Ow( vuJ;ps[6-uRus,9x+Hr*JXviaJyԌΞ0naȧØa֨kTv~XzqMжqaE?ݷm}ԎR~qr QMOhՀfqۗy_EoM_4ypRx:[HT,u[GoCM0@]S x,x{(]a5\M>'".8c]a%`2K^rP HhmwQ*wC*p(^S*g[ZKSpxYf\#6o?D_BwXN:Il[h&U ͅC\s|$ ??8`myXͪ~2 ЬZ̆ɉ0)׍阗Tv`hqJ} dX לAEFi(|͋o`VC%!:sgL ]l*Gz E ck4EBpoMV(HK;rNn ݮ;Wr6g:!4>rPDvN8 .Zӧ@4H,W)cmG[^M#`x@$qD 7\t" ]&,ۥ/! pUPfgyHfd婚s<y$kdB*Н;ɱ[缑h'gLHL&RDu@8M {I'v4 C[9VRZQvF4k'º䇼 hu!45PJJGl.lЋ) ]qOF2MoX$J.x܋5%nℐ]"]mEWáÂۇD'd*2( ئ}MJP@97Ӡ:i4O8ܕF笤D]};9 odBNx^گiwK?r#m!`vܠyFN_8^30$(5"ыC\`0z9F`t<'490splx ,y=_lm#njP r㈮c,ȩ1Z }e.ǻ">U 4M2{#e% -Z{!;8:zq#_1SI6y}uA~}6vr2=B^q7 y0#^_ZwMrXE!QFD| кE Q%lèOf=7M~o˱|_?9QpmW1܉GK $6QۋH3 cj뎣AD㤺ogz^N*Ź;k[eɻĬy4e(.RQ{ +[|`D4 yM w>tػ_%&a.a 3*s~(%jַX@#4}b)AN o,2+\i0H!+[Eiy ({A^o!a&: 9%Bu{bn`Z@&37E`A߃<o^0ï%ښm< !pq0?7!J eaXq4KY[IE=akP7뛨G%ž(?K(Cxy4pkg'^2fR &¾ DP46 3P5;1B36L? ~sdc`I7:q W!d1VѧW 8# N:O^ /%KH&ƊӼ1)] 9BPiww.MVMq;LcPG1ɗI$Ǟ$ETn] B< S$/Zp=Ě 6HlԘ|R(/-F>!%hzRǣ7%ulX)H"}!>.RnT]9 >hAABm;9M )/((|&6FqYbL佃v,&\`"H5~v3CBk?xzĥigh+ytRTIXZ7<۪(<`[(!Zi;$hnӽHX$'?xz1$phN"FZoD* 9:M[1˥\ jwg5UāF*WfsZb5}!}AAd˸]/ΰBN_՝}snFBM =?$vV2.!,h[iuNxUQI5 Ie94GL]Wr4ɝu4ؘsHuo _n{r}iqVX@ꜵͅ|4(x#ǪUvQZPjN <)9`Ktt0x[*P!\ *h8@@= rExG o/r! +/lDӣ"*;VK+ ʯE<S*#ju4 }:sX^3 ;Šch|oat '|M~>Hm<3TڛOz)D343D*Bɳy Qacas iғ9:QԌCc`U;E†|JP??,ҭL*A 랆1<psx1[7(x&)}pkd[% `*vu`-GoێD.یj_yM[fOLHN}xX y] iw@z6Kw 8UhΜi"l#%E-H3@-x*#ETӔA9q*?j0.t07< v7EJJVQrIvawY5İ8+Xk*تt^~zqAMSy!@6UMM1%ɴpMNpw?Y4ԃ|%7.ƇnrI]n``]<~]P\DpAG#5dĞϜA"&x2'f; x뼸:^M@ЖK鸸U0ym$xq=Us\bPEPAq6<<sXdZT^,/*05C-]cv &.)38N!!;u*wXy|8jMAbocG*BEmXqAyVƁc9"꾻QtJy5?*ct;\ 9k,ߌaCTﯿ5:%B_ F }S?~P8~)Tt2!Nۭ NL+?%r*(ּ71-w )d |1`!;Dލg%ymX|´`6 wl:Sty6P%64uDbt)N8-D3~*TR-!r4YBWZ-xUD^/#j|7>\0aXU$w,؀|摢"'w͡׼wr=raqA&ZmFfsGƳbM $ G WvQE(Tj]~v0*O"&?uyԐy`*n<-ڎRd1MaWAZSچP1eV!w?6+yoY)˯hH(Bmkʫ5ό:ACnKG-#笨NP cöP44ד'6?63h^Ti\b7G؄wv^8vs ֪,OQ+z" F ى@d"Uu.Y.v fN()G\1Q=%b_!7Cn(6"U܃h"!+FN|Ȥ`M=FZW|蝜.+> MS'p (g;^>qvB1$/'~u Xl=QG8 NBk6+嫻 !^Wgs,Y|Ouj&fDAW:Zqy@#vf`v9a1M.GP(/$1[wֻ.:G߅ى77z|{1v|,>0|kRB:%k&=AH~TLxpour\ҌɏsZN0[H:G(򈛐>C6:*_PAi1B^ЄXu?Xe %i{(RAyGHShnוknj]+ H,:JXfr R'u5tN nK<|;.yF[nNݵfRVZ+|܆kqfTa zms73N upwWNQЂcm㛛 $٨$58Cw~V=ס[$pfn[NB9D:ǭ!&000B(:` =dt1 xzA(]7GN-!!nZ}샽Uk `i~qyhQ:Hj^ٯYljx9› l .0 ƜOaQ"j@hI'W3 4WEtU; 3xb.UT:yy/5(LٕⰃEjˢ!p4=dAZx(uH]m< @0FC?